第2章 寄存器
一个典型的CPU(此处讨论的不是某一具体的CPU)由运算器、控制器、寄存器等器件构成,这些器件靠内部总线相连。前一章所说的总线,相对于CPU内部来说是外部总线。内部总线实现CPU内部各个器件之间的联系,外部总线实现CPU和主板上其他器件的联系。简单地说,在CPU中:
- 运算器进行信息处理;
- 寄存器进行信息存储;
- 控制器控制各种器件进行工作;
- 内部总线连接各种器件,在它们之间进行数据的传送。
对于一个汇编程序员来说,CPU中的主要部件是寄存器。寄存器是CPU中程序员可以用指令读写的部件。程序员通过改变各种寄存器中的内容来实现对CPU的控制。
不同的CPU,寄存器的个数、结构是不相同的。8086CPU有14个寄存器,每个寄存器有一个名称。这些寄存器是:AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。我们不对这些寄存器进行一次性的介绍,在课程的进行中,需要用到哪些寄存器,再介绍哪些寄存器。
2.1 通用寄存器
8086CPU的所有寄存器都是16位的,可以存放两个字节。AX、BX、CX、DX这4个寄存器通常用来存放一般性的数据,被称为通用寄存器。
以AX为例,寄存器的逻辑结构如图2.1所示。
一个16位寄存器可以存储一个16位的数据,数据在寄存器中的存放情况如图2.2所示。想一想,一个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。
数据存储示例
- 数据:18
- 二进制表示:10010
- 在寄存器AX中的存储:
AX
15
0
14
0
13 12
0
11 10
0
9
0
8
0
7
6
0
5
0
4
1
3
0
2
1
1
0
0
- 数据:20000
- 二进制表示:100111000100000
- 在寄存器AX中的存储:
AX
15
1
14
0
13
0
12
0
11
1
10
1
9
1
8
0
7
0
6
1
5
0
4
0
3
0
2
0
1
0
0
0
图2.2 16位数据在寄存器中的存储情况
以AX为例,8086CPU的16位寄存器分为两个8位寄存器的情况如图2.3所示。
AX的低8位(0位-7位)构成了AL寄存器,高8位(8位-15位)构成了AH寄存器。AH和AL寄存器是可以独立使用的8位寄存器。图2.4展示了16位寄存器及它所分成的两个8位寄存器的数据存储的情况。
数据存储示例
- 数据:20000
- 二进制表示:100111000100000
- 在寄存器AX中的存储:
AX
15 14
1 0
13 12
0 0
11 10
1 1
9 8
1 0
7 6
0 0
5 4
1 0
3 2
0 0
1 0
0 0
- AH:
7 6
1 0
5 4
0 0
3 2
1 1
1 0
0 0
- AL:
7 6
0 0
5 4
1 0
3 2
0 0
1 0
0 0
图2.4 16位寄存器及所分成的两个8位寄存器的数据存储情况
2.2 字在寄存器中的存储
出于对兼容性的考虑,8086CPU可以一次性处理以下两种尺寸的数据。
- 字节:记为byte,一个字节由8个bit组成,可以存在8位寄存器中。
- 字:记为word,一个字由两个字节组成,这两个字节分别称为这个字的高位字节和低位字节,如图2.5所示。
一个字可以存在一个16位寄存器中,这个字的高位字节和低位字节自然就存在这个寄存器的高8位寄存器和低8位寄存器中。如图2.4所示,一个字型数据20000,存在AX寄存器中,在AH中存储了它的高8位,在AL中存储了它的低8位。AH和AL中的数据,既可以看成是一个字型数据的高8位和低8位,这个字型数据的大小是20000;又可以看成是两个独立的字节型数据,它们的大小分别是78和32。
数制的讨论
任何数据,到了计算机中都是以二进制的形式存放的。为了描述不同的问题,又经常将它们用其他的进制来表示。例如,图2.4中寄存器AX中的数据是0100100010000,这是AX中的信息本身,可以用不同的逻辑意义来看待它。可以将它看作一个数值,大小是20000。当然,二进制数0100111000100000本身也可表示一个数值的大小,但人类习惯的是十进制,用十进制20000表示可以使我们直观地感受到这个数值的大小。
十六进制数的一位相当于二进制数的四位,如0100111000100000可表示成:4E20H。
由于一个内存单元可存放8位数据,CPU中的寄存器又可存放多个8位的数据。也就是说,计算机中的数据大多是由1-N个8位数据构成的。很多时候,需要直观地看出组成数据的各个字节数据的值,用十六进制来表示数据可以直观地看出这个数据是由哪些8位数据构成的。例如,20000写成4E20就可以直观地看出,这个数据是由4E和20两个字节数据构成的。如果AX中存放4E20,则AH里是4E,AL里是20。这种表示方法便于许多问题的直观分析。在以后的课程中,我们多用十六进制来表示一个数据。
在以后的课程中,为了区分不同的进制,在十六进制表示的数据的后面加H,在二进制表示的数据后面加B,十进制表示的数据后面什么也不加。如:可用3种不同的进制表示图2.4中AX里的数据,十进制:20000,十六进制:4E20H,二进制:0100111000100000。
2.3 几条汇编指令
通过汇编指令控制CPU进行工作,看一下表2.1中的几条指令。
汇编指令 | 控制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
的含义相同。
接下来看一下CPU执行表2.2中所列的程序段中的每条指令后,对寄存器中的数据进行的改变。
程序段中指令的执行情况之一
程序段中的指令 | 指令执行后AX中的数据 | 指令执行后BX中的数据 |
---|---|---|
mov ax, 4E20H | 4E20H | 0000H |
add ax, 1406H | 6226H | 0000H |
问题2.1:指令执行后AX中的数据为多少?思考后看分析。
分析:程序段中的最后一条指令add ax, bx
,在执行前AX和BX中的数据都为8226H,相加后所得的值为:1044CH,但是AX为16位寄存器,只能存放4位十六进制的数据,所以最高位的1不能在AX中保存,AX中的数据为:044CH。
程序段中指令的执行情况之二
程序段中的指令 | 指令执行后AX中的数据 | 指令执行后BX中的数据 |
---|---|---|
mov ax, 001AH | 001AH | 0000H |
mov bh, 0026H | 0040H | 0026H |
add al, 85H | 2640H | 4026H |
add ah, 93H | 2640H | 4028H |
问题2.2:指令执行后AX中的数据为多少?思考后看分析。
分析:程序段中的最后一条指令add al, 93H
,在执行前,AL中的数据为C5H,相加后所得的值为:158H,但是AL为8位寄存器,只能存放两位十六进制的数据,所以最高位的1丢失,AX中的数据为:0058H。
注意,此时AL是作为一个独立的8位寄存器来使用的,和AH没有关系,CPU在执行这条指令时认为AH和AL是两个不相关的寄存器。不要错误地认为,诸如add al, 93H
的指令产生的进位会存储在AH中,add al, 93H
进行的是8位运算。如果执行add ax, 93H
,低8位的进位会存储在AH中,CPU在执行这条指令时认为只有一个16位寄存器AX,进行的是16位运算。指令add ax, 93H
执行后,AX中的值为:0158H。此时,使用的寄存器是16位寄存器AX,add ax, 93H
相当于将AX中的16位数据00051和另一个16位数据0093H相加,结果是16位的0158H。
在进行数据传送或运算时,要注意指令的两个操作对象的位数应当是一致的,例如:
mov ax, bx
mov bx, cx
mov ax, 18H
mov al, 18
add ax, bx
add ax, 20000
这些都是正确的指令,而以下指令是错误的:
mov ax, bl
(在8位寄存器和16位寄存器之间传送数据)mov bx, ch
(在16位寄存器和8位寄存器之间传送数据)mov al, 20000
(8位寄存器最大可存放值为255的数据)add al, 100H
(将一个高于8位的数据加到一个8位寄存器中)
错误的原因是指令的两个操作对象的位数不一致。
检测点2.1
- 写出每条汇编指令执行后相关寄存器中的值:
mov ax, 6262H
AX = _____mov ah, 31H
AX = _____mov al, 23H
AX = _____add ax, 30H
AX = _____mov bx, 826CH
BX = _____mov ax, ax
AX = _____mov ax, bx
AX = _____add ax, bx
AX = _____mov al, bh
AX = _____add ah, ah
AX = _____
2.4 物理地址
我们知道,CPU访问内存单元时,要给出内存单元的地址。所有的内存单元构成的存储空间是一个一维的线性空间,每一个内存单元在这个空间中都有唯一的地址,我们将这个唯一的地址称为物理地址。
CPU通过地址总线送入存储器的,必须是一个内存单元的物理地址。在CPU向地址总线上发出物理地址之前,必须要在内部先形成这个物理地址。不同的CPU可以有不同的形成物理地址的方式。我们现在讨论8086CPU是如何在内部形成内存单元的物理地址的。
2.5 16位结构的CPU
我们说8086CPU的上一代CPU(8080、8085)等是8位机,而8086是16位机,也可以说8086是16位结构的CPU。那么什么是16位结构的CPU呢?
概括地讲,16位结构(16位机、字长为16位等常见说法,与16位结构的含义相同)描述了一个CPU具有下面几方面的结构特性:
- 运算器一次最多可以处理16位的数据;
- 寄存器的最大宽度为16位;
- 寄存器和运算器之间的通路为16位。
8086是16位结构的CPU,这也就是说,在8086内部,能够一次性处理、传输、暂时存储的信息的最大长度是16位的。内存单元的地址在送上地址总线之前,必须在CPU中处理、传输、暂时存放,对于16位CPU,能一次性处理、传输、暂时存储16位的地址。
2.6 8086CPU给出物理地址的方法
8086CPU有20位地址总线,可以传送20位地址,达到1MB寻址能力。8086CPU又是16位结构,在内部一次性处理、传输、暂时存储的地址为16位。从8086CPU的内部结构来看,如果将地址从内部简单地发出,那么它只能送出16位的地址,表现出的寻址能力只有64KB。
8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址。8086CPU相关部件的逻辑结构如图2.6所示。
如图2.6所示,当8086CPU要读写内存时:
- CPU中的相关部件提供两个16位的地址,一个称为段地址,另一个称为偏移地址;
- 段地址和偏移地址通过内部总线送入一个称为地址加法器的部件;
- 地址加法器将两个16位地址合成为一个20位的物理地址;
- 地址加法器通过内部总线将20位物理地址送入输入输出控制电路;
- 输入输出控制电路将20位物理地址送上地址总线;
- 20位物理地址被地址总线传送到存储器。
地址加法器采用物理地址 = 段地址 x 16 + 偏移地址的方法用段地址和偏移地址合成物理地址。例如,8086CPU要访问地址为123C8H的内存单元,此时,地址加法器的工作过程如图2.7所示(图中数据皆为十六进制表示)。
由段地址 x 16 引发的讨论
“段地址 x 16”有一个更为常用的说法是左移4位。计算机中的所有信息都是以二进制的形式存储的,段地址当然也不例外。机器只能处理二进制信息,“左移4位”中的位,指的是二进制位。
我们看一个例子。一个数据为2H,二进制形式为10B,对其进行左移运算。
左移位数 | 二进制 | 十六进制 | 十进制 |
---|---|---|---|
0 | 10B | 2H | 2 |
1 | 100B | 4H | 4 |
2 | 1000B | 8H | 8 |
3 | 10000B | 10H | 16 |
观察上面移位次数和各种形式数据的关系,我们可以发现:
- 一个数据的二进制形式左移1位,相当于该数据乘以2;
- 一个数据的二进制形式左移N位,相当于该数据乘以2的N次方;
- 地址加法器如何完成段地址x16的运算?就是将以二进制形式存放的段地址左移4位。
进一步思考,我们可看出:一个数据的十六进制形式左移1位,相当于乘以16;一个数据的十进制形式左移1位,相当于乘以10;一个X进制的数据左移1位,相当于乘以X。