概述
本文来介绍最基础的8086汇编,将通过关键点的突出以及个人理解,而不会将原理做重点介绍,争取最快的速度来学会汇编并且使用,我们平时接触的C/C++/JAVA等语言都是高级语言,而计算机只能运行机器语言也就是0和1,高级语言通过编译成汇编语言再编译成机器语言运行到计算机上。高级语言、汇编语言、机器语言特性如下:
- 汇编语言不区分大小写
- 汇编语言与机器语言一一对应,每一条机器语言都有一条汇编语言对应
- 汇编语言可以通过编译得到机器语言,机器语言可以通过反汇编得到汇编语言
- 高级语言可以通过编译得到汇编语言或者机器语言,但汇编语言或者机器语言几乎不可能还原成高级语言
程序执行流程:程序被装载进内存,CPU会对内存进行读和写,根据读取到的指令也会控制硬件设备比如显示器,音响,话筒等。我们在学习汇编过程中绝大部分指令都是跟CPU和内存相关的。
总线
总线(Bus)是计算机各种功能部件之间传送信息的公共通信干线,它是由导线组成的传输线束, 按照计算机所传输的信息种类,计算机的总线可以划分为数据总线、地址总线和控制总线,分别用来传输数据、数据地址和控制信号。总线是一种内部结构,它是CPU、内存、输入、输出设备传递信息的公用通道,主机的各个部件通过总线相连接,外部设备通过相应的接口电路再与总线相连接,从而形成了计算机硬件系统。在计算机系统中,各个部件之间传送信息的公共通路叫总线,微型计算机是以总线结构来连接各个功能部件的。
- 地址总线:它的宽度决定了CPU的寻址能力,8086的地址总线宽度是20,它的寻址能力是2^20 =1M,32位CPU寻址能力则是2^32=4G, 64位CPU寻址能力是2^64 = 4G * 4G,64位CPU提供足够大的内存地址,目前实际没有用到那么大的地址。
- 数据总线:它的宽度决定了CPU的单次数据传送量,也就是数据传送速度,8086的数据总线宽度是16,所以单次最大传递2个字节的数据,比如8086需要传送数据89D8H,89D8H占两个字节第一次传送DB,第二次传送89。
- 控制总线:它的宽度决定了CPU对其他器件的控制能力、能有多少种控制。
内存
所有的内存单元都有唯一的地址,叫做物理地址,各类存储器的物理地址情况,内存地址空间的大小受CPU地址总线宽度的限制。8086的地址总线宽度为20,可以定位2^20个不同的内存单元(内存地址范围0x00000~0xFFFFF)所以8086的内存空间大小为1MB
- 0x00000~0x9FFFF:主存储器。可读可写
- 0xA0000~0xBFFFF:向显存中写入数据,这些数据会被显卡输出到显示器,可读可写
- 0xC0000~0xFFFFF:存储各种硬件、系统信息。只读
寻址方式
8086CPU是16位的,但是地址总线是20位的,按理说CPU寻址能力只有2^16=64KB,那如何输送地址20位呢,这是8086的特殊性,它内部采用了地址加法器2个16位地址合成的方法生成1个20位的地址,一个地址称为段地址,一个称为偏移地址,地址加法器采用物理地址=段地址x16+偏移地址,比如8086要访问123C8H的内存单元,此时8086地址加法器进行合成,其步骤如下:
1.相关部件提供段地址1230和偏移地址00C8给地址加法器。
2.段地址1230和偏移地址00C8送入地址加法器。
3.地址加法器合成屋里地址:1230x16+00C8 = 123C8H。
4.输出物理地址。
在8086中,一般段地址又称为这个段的起始地址,偏移地址决定了这个段的最大长度:2^16=64KB。
CPU
CPU典型构成:寄存器,运算器,控制器。寄存器负责信息存储,运算器负责信息处理,控制器负责控制其它器件的工作。对我们目前学到的汇编主要是跟寄存器打交道,8086有14个16位寄存器,可以存放2个字节,8086寄存器如下所示:
1.CPU首先会将红色内存空间的值放到AX寄存器中:mov ax,地址1
2.然后让AX寄存器与1相加:add ax,1
3.最后将值赋值给内存空间:mov 地址2,ax
段寄存器
我们这里再重点介绍下段寄存器,前面我们说了相关部件提供段地址和偏移地址给地址加法器进行合成地址,到底是什么提供的的呢,就是段寄存器,包括:代码段寄存器(CS)、数据段寄存器(DS)、堆栈寄存器(SS)、附加段寄存器(ES)。
- CS和IP
CS为代码段寄存器,IP为指令寄存器,它们指示了CPU当前要读取指令的地址,任意时刻8086都会将CS:IP指向的指令作为下一条要需要取出执行的指令,也就是说将要被执行的指令都是从CS:IP取出地址合成的。指令执行流程总结如下:
1.从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器
2.IP=IP+所读取指令的长度,从而指向下一条指令
3.执行步骤1,重复这个过程
CPU从何处执行指令是由CS、IP中的内容决定的,我们可以通过改变CS、IP的内容来控制CPU执行目标指令,MOV能修改大部分寄存器中的值,但是不能用于修改CS:IP的值,JMP等转移指令能修改CS:IP中的值。比如:
jmp 2AE3:3,执行后:CS=2AE3H,IP=0003H,CPU将从2AE33H处读取指令。
jmp 3:0B16,执行后:CS=003H,IP=0B16H,CPU将从00B46H处读取指令。
综合上述,“jmp 段地址:偏移地址”指令的功能为:用指令中给出的段地址修改CS,偏移地址修改IP。jmp 也可以直接jmp寄存器来修改IP的值,比如:jmp ax,就是设置IP的值为寄存器里面的值。 - DS和[address]
CPU要读写一个内存单元时,必须要先给出这个内存单元的地址,在8086中,内存地址由段地址和偏移地址组成,8086中有一个DS段寄存器,通常用来存放要访问数据的段地址,比如:
mov bx,1000h //将地址放到bx中
mov ds,bx //将bx中的值赋值到ds数据段寄存器,段地址就是1000h
mov al,[0] //将10000H(1000:0)中的内存数据赋值到al寄存器中
mov al,[address]的意思将DS:address中的内存数据赋值到al寄存器中,由于al是8位寄存器,所以是将一个字节的数据赋值给al寄存器,8086不支持将数据直接送入段寄存器中,mov ds,1000H是错误的。这里在补充一个知识点大小端,大段模式:数据的高字节存保存在内存的低地址中,而数据的低字节保存在内存的高字节中,小段模式刚好相反。
基础指令
mov指令
mov 寄存器, 数据 比如:mov ax,8 ;将十进制8传送到ax
mov 寄存器, 寄存器 比如: mov ax,bx ;将bx寄存器中的数据传送到ax
mov 寄存器,内存单元 比如:mov ax,[2000h] ;将地址2000H单元的内容传送到AL寄存器
mov 内存单元,寄存器 比如:mov[2000h],ax ;将ax传送到地址2000h中
mov 段寄存器,寄存器 比如:mov ds,ax ;将ax中的数据传入到ds中
mov 寄存器,段寄存器 比如 mov ax ,ds ;将ds中的数据传入到ax中
注意事项如下:
- 两个存储单元之间不能直接传送数据,即:MOV指令只允许一个操作数在存储器中。MOV [SI],[2000H];这是错误的
- MOV指令中立即数不能直接传送给段寄存器(CS、DS、SS、ES)和IP;段寄存器之间不能直接传送。MOV IP,2000H ;这是错误的
- CS和IP不能作为目的操作数。MOV CS,AX ;这是错误的
- MOV指令中立即数不能作目标操作数。MOV 2000H,[SI] ;这是错误的
- MOV指令中的源操作数绝对不能是立即数和代码段CS寄存器
- MOV指令中绝对不允许在两个存储单元之间直接传送数据
- MOV指令中绝对不允许在两个段寄存器之间直接传送数据
- MOV指令不会影响标志位
add指令和sub指令都跟mov一样,都有两个操作对象,注意事项也是一样,这里就不列举,详细了解请自行查看官方文档。
栈
函数、方法,我们平时开发是使用非常频繁,我们看看函数最简单的的结构:函数名,函数参数,返回值,平时也在说函数调用栈,其实函数调用其底层就是对栈的操作,我们想彻底搞懂函数调用,我们必须先来认识栈,栈:是一种具有特殊的访问方式的存储空间(后进先出,Last In Out First,LIFO)。
- 8086会将CS作为代码段的段地址,将CS:IP指向的指令作为下一条需要取出执行的指令
- 8086会将DS作为数据段的段地址,mov ax,[address]就是取出DS:address的内存数据放到ax寄存器中
- 8086会将SS作为栈段的段地址,任意时刻,SS:SP指向栈顶元素
- 8086提供了PUSH(入栈)和POP (出栈)指令来操作栈段的数据。
比如push ax是将ax的数据入栈,如下所示:
总结下,本篇文章介绍了编程语言的发展,以及汇编与高级语言的关系,通过对CPU、内存、寄存器、指令以及栈的学习,希望你能抓住关键点快速入门,下一篇将正式搭建环境编写汇编代码。敬请期待,祝学习快乐!