寄存器概念
寄存器是CPU中的主要部件,是CPU中程序员可以读写的部件。程序员通过改变各种寄存器中的内容来实现对CPU的控制。
不同的CPU,寄存器的个数、结构是不同的。8086CPU有14个寄存器,每个寄存器有一个名称。这些寄存器是:AX、BX、CX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。
通用寄存器
8086CPU的所有寄存器都是16位的,可以存放两个字节。AX、AB、CX、DX这4个寄存器通常用来存放一般性的数据,被称为通用寄存器。
一个16位寄存器可以储存一个16位的数据。
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的低8位(0位~7位)构成了AL寄存器,高8位(8位~15位)构成了AH寄存器。AH和AL寄存器是可以独立使用的8位寄存器。
字在寄存器中的存储
出于对兼容性的考虑。8086CPU可以一次性处理以下两种尺寸的数据。
- 字节:记为byte,一个字节由8个bit组成,可以存在8位寄存器中。
- 字:记为word,一个字由两个字节组成,这两个字节分别成为这个字的高位字节和低位字节。
一个字可以存在一个16位寄存器中。这个字的高位字节和低位字节自然就存在 这个寄存器的高8位寄存器和低8位寄存器中,如图2.4,一个字型数据20000,存在AX寄存器中,在AH中存储了它的低8位。AH和AL中的数据,即可以看成是一个字型数据的高8位和低8位,这个字型数据的大小是20000,也可以看成是两个独立的字节型数据,它们的大小分别是78和32。
几条汇编指令
汇编指令 | 控制CPU完成的操作 | 用高级语言的语法描述 |
---|---|---|
mov ax,18 | 将18送入寄存器AX | AX=18 |
mov ah,78 | 将78送入寄存器AH | AH=78 |
add ax,8 | 将寄存器AX中的数值加上8 | AX=AX+8 |
mov ax,bx | 将寄存器BX中的数据送入寄存器AX | AX=BX |
add ax,bx | 将AX和BX中的数值相加,结果存在AX中 | AX=AX+BX |
注意,为了使具有高级语言基础的读者更好的理解指令的含义,有时会用文字描述和高级语言描述这两种方式来描绘一条汇编指令的含义。在写一条汇编指令或一个寄存器的名称时不区分大小写。如:mov ax,18和MOV AX,18的含义相同;bx和BX的含义相同。
PS:如果数据大小超过寄存器大小,则最高位数据将不能被储存。如果将16位的寄存器AX看作两个8位的寄存器AH和AL,那么在进行运算时,就会将AL或AH视为1个独立的8位寄存器,在进行add运算时数据大小超出8位,则最高位丢失(这里的丢失,指的是进位值不能在8位寄存器中保存,但时CPU并不是真的丢弃这个进位值。)
在进行数据传送或运算时,要注意指令的两个操作对象的位数应当是一致的。
地址加法器
地址加法器采用
物理地址=段地址x16+偏移地址
的方法用段地址和偏移地址合成物理地址。例如,8086CPU要访问地址为123C8H的内存单元,此时,地址加法器的工作如图2.7所示:
“段地址x16”有一个更常用的说法是左移4位。“左移4位”中的位,指的是二进制位。
左移位数 | 二进制 | 十六进制 | 十进制 |
0 | 10B | 2H | 2 |
1 | 100B | 4H | 4 |
2 | 1000B | 8H | 8 |
3 | 10000B | 10H | 16 |
4 | 100000B | 20H | 32 |
- 一个数据的二进制形式左移1位,相当于该数据乘以2;
- 一个数据的二进制形式左移N位,相当于该数据乘以2的N次方;
- 地址段x16的运算,就是将以二进制形式存放的地址段左移4位。
进一步,我们不难得出一个数据的十六己内酯形式左移一位,相当于乘以16;一个数据的十进制形式左移一位,相当于乘以10;一个X进制的数据左移一位,相当于乘以X。
“段地址x16+偏移地址=物理地址”的本质含义
注意,这里讨论的是8086CPU段地址和偏移地址的本质含义。
“段地址x16+偏移地址=物理地址”本质含义是:CPU在访问内存时,用一个基础地址(段地址x16)和一个相对基础地址偏移地址相加,给出内存单元物理地址。更一般的说,这种寻址功能是“基础地址+偏移地址=物理地址”。
为什么是乘以16呢?
用16是应为:8086/8088CPU使用的是20位物理地址,而8086/8088CPU内部的ALU只能进行16位的运算,因此,8086/8088所使用的20物理地址,是由相应的段地址(16位)加上偏移地址(4位)组成的。
段的概念
“段地址”这个名称中包含这“段”的概念。这种说法可能对一些学习者产生了误导,使人误以为内存被划分成一个一个的段,每一个段有一个段的地址。
其实,内存并没有分段,段的划分来自于CPU,由于8086CPU用“基础地址+偏移地址=物理地址”的方式给出内存单元的物理地址,使得我们可以用分段的方式来管理内存。
以后,在编程时可以根据需要,将若干个地址连续的内存看作一个段,用段地址x16定位段地址的起始地址(基础地质),用偏移地址定位段中的内存单元。有两点需要注意:段地址下16必然是16的倍数,所以一个段的起始地址也一定是16的倍数;偏移地址为16位,16位地址的寻址能力为64KB,所以一个段地址的长度最大为64KB。
注:CPU可以用不同的段地址和偏移地址形成同一个物理地址。
物理地址 | 段地址 | 偏移地址 |
21F60H | 2000H | 1F60H |
2100H | 0F60H | |
21F0H | 0060H | |
21F6H | 0000H | |
1F00H |
2F60H |
PS:“数据在21F60H内存单元中。”这句话对于8086PC机一般不这样讲,而是使用另外两种类似的说法:1.数据存在内存20000:1F60单元中;2.数据存在内存的2000H段中的1F60H单元中。
CS和IP
CS和IP是8086CPU中两个最关键的寄存器,他们指示了CPU当前要读取指令的地址。CS为代码段寄存器,IP为指令指针寄存器,从名称上我们可以看出他们和指令的关系。
在8086PC机中,任意时刻,设CS中的内容为M,IP中的内容为N,8086CPU将从内存Mx16+N单元开始,读取一条指令并执行。也可以说:8086机中,任意时刻。CPU将CS:IP指向的内容当作指令执行。
现在,我们更清楚了解了CS和IP的重要性,它们的内容提供了CPU要执行指令的地址。
在内存中,指令和数据没有任何区别,都是二进制信息,CPU在工作时把有些信息看作指令,有些信息看作数据,现在,提出一个问题:CPU根据什么将内存中的信息看作指令?我们可以说,CPU将CS:IP指向的内存单元中的内容看作指令,因为,在任何时候,CPU将CS、IP中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码,执行。如果说,内存中的一段信息曾经被CPU执行过的话,那么它所在的内存单元必然被CS:IP指向过。
修改CS、IP的指令
在CPU中,程序员能够指令读写的部件只有寄存器,程序员可以通过改变寄存器中的内容实现对CPU的控制。CPU从何处执行指令是由CS、IP中的内容决定的,所以程序员可以通过改变CS。IP中的内容来控制CPU执行目标指令。
我们如何改变CS、IP的值呢?显然,8086CPU必须提供相应的指令。我们如何修改AX中的值?可以用mov指令,如mov ax,123将ax中的值设为123,显然,我们也可以用同样的方式设置其他寄存器的值,如mov bx,123,mov cx,123,mov dx,123等。其实,8086CPU大部分寄存器的值,都可以用mov指令来改变,mov指令被称为传送指令。
但是,mov指令不能用于设置CS、IP的值,原因很简单,应为8086CPU没有提供这样的功能。8086CPU为CS、IP提供了另外的指令来改变他们的值。能够改变CS、IP的内容指令被统称为转移指令。
而若想修改CS、IP的内容,可用形如“jmp段地址:偏移地址”的指令完成,如
jmp 2AE3:3,执行后:CS=2AE3H,IP=0003H,CPU将从2AE33H处读取指令。 |
jmp 3:0B16,执行后:CS=0003H,IP=0B16H,CPU将从00B46H处读取指令。 |
“jmp 段地址:偏移地址”指令的功能为:用指令的功能为:用指令中给出的段地址修改CS,偏移地址修改IP。
若想仅修改IP的内容,可用形如“jmp某一合法寄存器”的指令完成,如
jmp ax,指令执行前:ax=1000H,CS=2000H,IP=0003H |
指令执行后:ax=1000H,CS=2000H,IP=1000H |
jmp bx,指令执行前:bx=0B16H,CS=2000H,IP=0003H |
指令执行后:bx=0B16H,CS=2000H,IP=0B16H |
“jmp 某一合法寄存器“指令的功能为:用寄存器中的值修改IP。
jmp ax,,在含以上好似:mov IP,ax。但是并非真的存在mov IP,ax这样的指令。
代码段
对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。我们可以将程度为N(N<=64KB)的一组代码,存在一组地址连续、其实地址为16的倍数的内存单元中,我们可以认为,这段内存是用来存放代码的,从而定义了一个代码段。
而将一段内存当作代码段,仅仅是我们在编程时的一种安排,CPU并不会由于这种安排,就自动地将我们定义的代码段中的指令当作指令来执行。CPU只认CS:IP指向所定义的代码段中的指令。所以,要让CPU执行我们放在代码段中的指令,必须要将CS:IP只想所定义的代码段中的第一条指令的首地址。