二、寄存器与简单汇编指令
二、寄存器与简单汇编指令
1.* 简单汇编指令
1.1 mov,add和sub指令
mov指令(传输指令)
用C语言可理解为赋值语句,如”mov ax,bx”
可理解成”ax=bx”。
使用形式如下:
mov 寄存器,数据 比如: mov ax,8
mov 寄存器,寄存器 比如: mov ax,bx
mov 寄存器,内存单元 比如: mov ax,[0]
mov 内存单元,寄存器 比如: mov [0],ax
mov 段寄存器,寄存器 比如: mov ds,ax
mov 寄存器,段寄存器 比如: mov ax,ds
mov 内存单元,段寄存器 比如:mov [0],ds
mov 段寄存器,内存单元 比如:mov ds,[0]
add和sub指令
类似C语言中”+=”和”-=”这两个运算符号,如”add ax,8”和”sub ax,8”可分别翻译成”ax+=8”和”ax-=8”这两种运算。
使用形式可参照上述方式用debug自行实验,不再赘述。
1.2 jmp指令(转移指令)
用于修改CS和IP的值来控制CPU执行目标指令。
若想同时修改CS、IP的值,可用
“jmp 段地址:偏移地址”的指令完成。如:
jmp 2AE3:3,执行后,CS=2AE3H,IP=0003H,CPU将从2AE33H处读取指令。
jmp 3:0B16,执行后,CS=0003H,IP=0B16H,CPU将从00B46H处读取指令。
若只想修改IP的值,可用“jmp 某一合法寄存器”完成。如:
jmp ax,在含义上好似“mov IP,ax”意思是用寄存器的值修改IP。
1.3 push和pop指令(入栈、出栈指令)
push和pop指令是可以在寄存器和内存(栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间。)之间传送数据的。
push和pop指令的格式可以是如下形式:
push 寄存器;将一个奇存器中的数据入栈
pop 寄存器;出栈,用一个寄存器接收出栈的数据
(PS:用什么类型数据入栈,就用什么类型数据出栈。)
指令执行时,CPU要知道内存单元的地址,可以在push、pop 指令中只给出内存单元的偏移地址,段地址在指令执行时,CPU 从ds 中取得。
- 用栈来暂存以后需要恢复的寄存器中的内容时,出栈的顺序要和入栈的顺序相反,因为最后入栈的寄存器的内容在栈顶,所以在恢复时,要最先出栈。
- 将寄存器清零,可以用
mov ax,0
sub ax,ax
xor ax,ax
2. 寄存器
一个典型的CPU由运算器、控制器、寄存器(CPU工作原理)等器件构成,这些器件靠内部总线相连。其中寄存器进行信息存储。程序员正是通过改变各种寄存器中的内容来实现对CPU的控制。
每一代CPU都是向下兼容的。
8086CPU 的上一代CPU中的寄存器都是8位的,为了保证兼容,使原来基于上代CPU编写的程序稍加修改就可以运行在8086之上,8086CPU的AX、BX、CX、DX这4个寄存器都可分为两个可独立使用的8位寄存器来用
- AX可分为AH和AL;
- BX 可分为BH 和BL;
- CX 可分为CH 和CL;
- DX可分为DH和DL。
以 AX 为例,8086CPU的16位寄存器分为两个8位寄存器的情况如图2. 3所示。
2.1 字在寄存器中的存储
8086CPU可以一次性处理以下两种尺寸的数据。
- 字节: 记为byte,一个字节由8个bit组成,可以存在8位寄存器中。
- 宇:记为word,一个字由两个字节组成,这两个字节分别称为这个字的高位字节和低位字节,一个字可以存在一个16位寄存器中,这个字的高位字节和低位字节自然就存在这个寄存器的高8位寄存器和低8位寄存器中。一个字型数据20000,存在AX寄存器中,在AH中存储它的高8位,在AL中存储了它的低8位。AH和AL中的数据,既可以看成是一个字型数据的高8位和低8位,这个字型数据的大小是20000:又可以看成是两个独立的字节型数据,它们的大小分别是78和32。
- 高位存储高地址,低位存储低地址。
- 一个字要用两个地址连续的内存单元来存放
- 任何两个地址连续的内存单元,N号单元和 N+1号单元,可以将它们看成两个内存单元,也可看成一个地址为N的字单元中的高位字节单元和低位字节单元。
字节型数据:该地址存放数据,是一个字节。
字型数据:整体所指数据,是一个字。
2.2 物理地址与偏移地址
物理地址: CPU访问内存单元时,要给出内存单元的地址。所有的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有唯一的地址,我们将这个唯一的地址称为物理地址。
CPU 通过地址总线送入存储器的,必须是一个内存单元的物理地址。在 CPU 向地址总线上发出物理地址之前,必须要在内部先形成这个物理地址。不同的CPU可以有不同的形成物理地址的方式。
16位CPU(8086CPU):
- 运算器一次最多可以处理16位的数据
- 寄存器的最大宽度为16位
- 寄存器和运算器之间的通路为16位
8086CPU有20位地址总线,可以传送20位地址,达到IMB寻址能力。8086CPU又是16位结构,在内部一次性处理、传输、暂时存储的地址为16位。从8086CPU的内部结构来看,如果将地址从内部简单地发出,那么它只能送出16位的地址,表现出的寻址能力只有64KB。8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址。
(1)CPU中的相关部件提供两个16位的地址,一个称为段地址,另一个称为偏移地址;
(2)段地址和偏移地址通过内部总线送入一个称为地址加法器的部件;
(3)地址加法器将两个16位地址合成为一个20位的物理地址;
(4)地址加法器通过内部总线将20位物理地址送入输入输出控制电路;
(5)输入输出控制电路将20位物理地址送上地址总线;
(6)20位物理地址被地址总线传送到存储器。
地址加法器采用“物理地址=段地址x16+偏移地址”的方法用段地址和偏移地址合成物理地址。
“段地址x16”的含义:
(1)一个数据的二进制形式左移1位,相当于该数据乘以2:
(2)一个数据的二进制形式左移N位,相当于该数据乘以2的N次方;
(3)地址加法器完成段地址×16的运算就是将以二进制形式存放的段地址左移4位。
(4)进一步可知一个X进制的数据左移1位,相当于乘以X。
“物理地址=段地址x16+偏移地址”的本质含义
CPU在访问内存时,用一个基础地址(段地址×16)和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址。
更一般地说,8086CPU的这种寻址功能是 “基础地址+偏移地址=物理地址”寻址模式的一种具体实现方案。8086CPU中,段地址×16可看作是基础地址。
2.3 段
(1) 基本概念
其实,内存并没有分段,段的划分来自于CPU我们可以用分段的方式来管理内存。(人为规定的)
将若干地址连续的内存单元看作一个段,用段地址×16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。
段地址x16必然是16的倍数,所以一个段的起始地址也一定是16的倍数:偏移地址为16位,16位地址的寻址能力为64KB,所以给定一个段地址,仅通过变化偏移地址来进行寻址,偏移地址16位,变化范围为0- FFFFH,仅用偏移地址来寻址最多可寻64KB个内存单元。
PS:
- CPU 可以用不同的段地址和偏移地址形成同一个物理地址
- “数据在21F60H内存单元中。”有两种类似的说法:
1.数据存在内存2000:1F60单元中;
2.数据存在内存的2000H段中的1F60H单元中。
(2) 段寄存器
8086CPU有4个段寄存器:CS、DS、SS、ES。当8086CPU要访问内存时由这4个段寄存器提供内存单元的段地址。
(3) CS和IP
CS和IP是8086CPU中两个最关键的寄存器,它们指示了CPU当前要读取指令的地址。CS 为代码段寄存器,IP 为指令指针寄存器,从名称上我们可以看出它们和指令的关系。
在8086PC机中,任意时刻,设CS中的内容为M,IP 中的内容为N,8086CPU将从内存Mx16+N 单元开始,读取一条指令并执行。
也可以这样表述:8086机中,任意时刻,CPU将CS:IP 指向的内容当作指令执行。
如图可知,将数据存放到寄存器中需要三个字节,对两个寄存器进行操作需要两个字节。
8086CPU的工作过程可简要描述如下:
(1)从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器;
(2)IP=IP+所读取指令的长度,从而指向下一条指令;
(3)执行指令。转到步骤(1),重复这个过程。
CPU将CS: IP指向的内存单元中的内容看作指令,因为,在任何时候,CPU将CS、IP 中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行。
(4)代码段
可以根据需要,将一组内存单元定义为一个段。我们可以将长度为N(N≤64KB)的一组代码,存在一组地址连续、起始地址为16的倍数的内存单元中,我们可以认为,这段内存是用来存放代码的,从而定义了 一个代码段。(同样是人为规定的,CPU不知道,它只负责读写指令,执行)
(5)数据段
我们可以将一组长度为 N( ≤ 64KB) 、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段用DS存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。(同上)
(6) DS和[address]
内存地址由段地址和偏移地址组成。8086CPU 中有DS寄存器,通常用来存放要访问数据的段地址。
“[…]”表示一个内存单元,“[…]”中的0表示内存单元的偏移地址。
“mov al,[0]”指令执行时,8086CPU自动取DS中的数据为内存单元的段地址。
10000H 用段地址和偏移地址表示为1000:0,我们先将段地址1000H放入ds,然后用”mov al,[0]”完成传送。mov指令中的[0]说明操作对象是一个内存单元,[0]中的”0”说明这个内存单元的偏移地址是0,它的段地址默认放在DS中,指令执行时,8086CPU会自动从DS中取出。
“数据——>通用寄存器——>段寄存器”
mov bx, 1000H
mov ds,bx
是给段寄存器赋值的常用操作。
下面是字传送的详细操作: