CPU中的主要部件是寄存器,寄存器是CPU中程序猿可以用指令读写的部件,程序猿通过改变各种寄存器中的内容来实现对CPU的控制。
8086CPU有14个寄存器:AX, BX, CX, DX, SI, DI, SP, BP, IP, CS, SS, DS, ES, PSW,分类如下。关于每种寄存器的功能,点击这里查看
一、通用寄存器
1.1 整个寄存器
8086CPU的所有寄存器都是16位(Bit),可以存放两个字节。
以AX为例,通用寄存器的逻辑结构如图2.1所示
- 现在有一个十进制数据:18,转换为二进制后为:10010,在寄存器AX中的存储如下
- 现在有一个十进制数据:20000,其二进制表示:100111000100000,在寄存器AX中的存储:
1.2 低+高
8086CPU的上一代CPU寄存器是8位的,为了保证兼容,使得原来基于上代CPU编写的程序能够运行在8086之上,8086CPU的AX,BX,CX,DX都可以分为两个独立的8位寄存器来使用。
以AX为例,8086CPU的16位寄存器分为两个8位寄存器的情况如下图所示
AX的低8位(0位-7位)构成了AL寄存器,高8位(8位-15位)构成了AH寄存器。AH和AL寄存器都是可以独立使用的8位寄存器
现在,将一个16位数据拆成两个8位数据后,数据的变化如下
可以看到其中的16位数据可以拆为低8位和高8位数据
二、字在寄存器中的存储
1个16位寄存器存储1个字,两个字节,这两个字节分为高8位字节和低8位字节
三、汇编指令
通过汇编指令控制CPU进行工作,看一下表2.1中的几条汇编指令
在写一条汇编指令或一个寄存器的名称时不区分大小写
// 以下两条指令意义一样
mov ax, 18
MOV AX, 18
3.1 16位寄存器数据改变
将CPU执行下表中所列的程序段中的每条指令后,对寄存器中的数据改变如下
程序段中的指令 | 指令执行后AX中的数据 | 指令执行后BX中的数据 |
---|---|---|
mov ax, 4E20 H | 4E20 H | 0000 H |
add ax, 1406 H | 6226 H | 0000 H |
mov bx, 2000 H | 6226 H | 2000 H |
add ax , bx | 8226 H | 2000 H |
mov bx, ax | 8226 H | 8226 H |
add ax, bx | ? | 8226 H |
上面最后一条语句结果是什么?
对于上面那个只能保存4位16进制的解释,这是因为1个16进制数据换算成二进制数据,就需要4bit来表示,所以一个寄存器有16bit,最终为一个寄存器保存4个16进制数据
3.2 分为高低位寄存器数据改变
程序段中的指令 | 指令执行后AX中的数据 | 指令执行后BX中的数据 |
---|---|---|
mov ax, 001A H | 001A H | 0000 H |
mov bx, 0026 H | 001A H | 0026 H |
add al, bl | 0040 H | 0026 H |
add ah , bl | 2640 H | 0026 H |
add bh, al | 2640 H | 4026 H |
mov ah, 0 | 0040 H | 4026 H |
add al, 85H | 00C5 H | 4026 H |
add al, 93H | ? | 4026 H |
四、物理地址
CPU访问内存单元时,要给出内存单元的地址。所有内存单元构成的储存空间是一个一维的线性空间,每一个内存单元在这个空间中都有唯一的地址,这个唯一的地址称为物理地址。
CPU通过地址总线送入存储器的,必须是一个内存单元的物理地址。在CPU向地址总线上发出物理地址之前,必须要在内部先形成这个物理地址。
不同的CPU可以有不同的物理地址形成方式。现在讨论8086CPU是如何在内部形成内存单元的物理地址。
五、16位结构的CPU
8086CPU的上一代CPU(8080、8085)等是8位机,而8086是16位机,也可以说8086是16位结构的CPU。那么什么是16位结构的CPU呢?
还记得吗,最一开头说的CPU是由运算器、控制器、寄存器组成的,所以CPU的结构特性和它们之间是离不开关系的。
简单来讲:16位结构描述了一个CPU具有以下几方面的结构特性
8086是16位结构的CPU,也就是说,在8086内部,能够一次性处理、传输、暂时存储的信息的最大长度是16位的。
内存单元的地址在送上地址总线之前,必须在CPU中处理、传输、暂时存放,对于16位CPU,能够一次性处理、传输、暂时存储16位的地址。
六、8086CPU给出物理地址的方法
8086CPU有20位地址总线,可以传送20位地址,达到寻址能力。8086CPU又是16位结构,在内部一次性处理、传输、暂时存储的地址为16位(只能找到个内存的数据)。从8086CPU的内部结构来看,如果将地址从内部简单发出,那么它只能送出16位的地址,表现出的寻址能力只有。
8086CPU采用一种在内部用两个16位地址合成的方法来形成一个20位的物理地址。
8086CPU相关部件的逻辑部件结构如下图2.6所示
如图2.6所示,当8086CPU要读写内存时
- CPU中相关部件提供两个16位的地址,一个称为段地址,另一个称为偏移地址
- 段地址和偏移地址通过内部总线送入一个称为地址加法器的部件
- 地址加法器将两个16位地址合成为一个20位的物理地址
- 地址加法器通过内部总线将20位物理地址送入输入输出控制电路
- 输入输出控制电路将20位物理地址送上地址总线
- 20位物理地址被地址总线传送到存储器
地址加法器采用公式(1)来合成物理地址
例如,8086CPU要访问地址为123C8 H的内存单元时,地址加法器的工作过程如图2.7所示
七、段地址x16+偏移地址=物理地址的本质含义
“段地址x16+偏移地址=物理地址“的本质含义:CPU在访问内存时,用一个基础地址(段地址x16)和一个相对于基础地址的偏移地址相加,给出内存单元的物理地址。
下面,用一个例子做一个比喻。
7.1 例子1
比如说,学校、体育馆、图书馆在一条笔直的单行路上,学校位于路的起点,如下图所示
现在你要去图书馆,想知道那里的地址,现在有两种可以告诉你图书馆的地址
- 从学校走2826m到达图书馆,这个2828m可以认为是图书馆的物理地址
- 从学校走2000m到体育馆,从体育馆再走826m到图书馆。第一个距离2000m,适当对于起点的基础地质,第二个距离826m是相对于基础地质的偏移地址。
7.2 例子2
给上面的例子加一个限制条件,你问我图书馆的地址我只能将它写在纸上告诉你。对于2827这个数据,我只能用4位数据的纸条才能写下。
但是,现在只有很多3位数据的纸条,这样就只能以下面这种方式告诉你这个数据
在第一张图写上200(段地址),在第二张纸上写上826(偏移地址),这样物理地址就为
%5Ctimes%2010%20%2B%20826(%5Ctext%7B%E5%81%8F%E7%A7%BB%E5%9C%B0%E5%9D%80%7D)%3D2826(%5Ctext%7B%E7%89%A9%E7%90%86%E5%9C%B0%E5%9D%80%7D)%0A#card=math&code=200%28%5Ctext%7B%E6%AE%B5%E5%9C%B0%E5%9D%80%7D%29%5Ctimes%2010%20%2B%20826%28%5Ctext%7B%E5%81%8F%E7%A7%BB%E5%9C%B0%E5%9D%80%7D%29%3D2826%28%5Ctext%7B%E7%89%A9%E7%90%86%E5%9C%B0%E5%9D%80%7D%29%0A&id=HZvEe)
8086CPU就是一个只能提供两张3位数据纸条的CPU
八、段地址并不分段
之前使用公式(1)计算物理地址的时候,提到过一个段地址。”段地址“这个名称中包含着段的概念,这种说法会对学习者产生舞蹈,使人误以为内存被划分为了一个一个的段,每一个段都有一个段地址 。
其实,内存并没分段,段的划分来自于CPU ,由于8086CPU用 的方式给出内存单元的物理地址,使得我们可以用分段的方式来管理内存。
也就是说:可以使用内存划分段的方式帮助记忆,但是真实的分段其实是来自于CPU。
如图2.9所示,我们可以认为(虽然结果并不是这是):地址10000H~100FFH的内存单元组成一个段,该段的起始地址(基础地质)为10000H,段地址为1000H,大小为100H.
我们也可以认为地址10000H~1007FH, 10080H~100FFH的内存单元组成两个段,它们的起始地址(基础地址)为:10000H和10080H,段地址为:1000H和1008H,大小都为80H.
为了编程时的方便,可以将若干地址连续的内存单元看作一个段,用定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。
有两点需要注意:
- 必然是16的倍数,所以一个段的起始地址也一定是16的倍数
- 偏移地址为16位,16位地址的寻址能力为,所以一个段的长度最大为
九、段寄存器
8086CPU在访问内存时需要由相关部件提供内存单元的段地址和偏移地址,送入地址加法器合成物理地址。
这里,需要知道,是什么部件提供段地址。段地址在8086CPU的段寄存器中存放。8086有4个段寄存器:CS, DS, SS, ES。当8086CPU要访问内存时由这4个段寄存器提供内存单元的段地址。
十、CS和IP
CS和IP是8086CPU中最关键的寄存器,它们指示了CPU当前要读取指令的地址。CS为代码段寄存器,IP为指针指针寄存器。
在8086PC机中,任意时刻,设CS中的内容为M,IP中的内容为N,8086CPU将从内存单元开始,读取一条指令并执行。
或者表述为:8086PC机,任意时刻,CPU将CS : IP指向的内容当作指令执行。
下面这张图2.10就展示了8086CPU读取、执行指令的工作原理
可以看到,在2.10中,CS中存放的段地址:2000H,而IP中的内容为0000H,所以要取得地址为:20000H,取到的指令为mov ax, 0123H
。
内存20000H~20009H单元中存放着可执行的机器码,这些机器码对应的汇编指令如下:
- 地址:
20000H~20002H
,内容:B8 23 01
,长度:3Byte
,对应汇编指令:mov ax,0123H
- 地址:
20003H~20005H
,内容:BB 03 00
,长度:3Byte
,对应汇编指令:mov bx, 0003H
- 地址:
20006H~20007H
,内容:89 D8
,长度:2Byte
,对应汇编指令:mov ax, bx
- 地址:20008H~20009H,内容:
01 D8
,长度:2Byte
,对应汇编指令:add ax, bx
下面,用一个动态图来展示CPU怎么读取指令,并且执行指令的过程
通过上面的演示过程,可以知道8086CPU的工作过程可以简要描述如下:
- 从CS : IP指向的内存单元读取指令,读取的指令进入指令缓冲区
- IP=IP+所读取指令的长度,从而指向下一条指令
- 执行指令。转到步骤1,重复这个过程
在8086CPU加电启动或复位后(即CPU刚开始工作时),CS和IP被设置为CS=FFFFH,IP=0000H,即在8086PC机刚启动时,CPU从内存FFFF0H单元中读取指令执行,FFFF0H单元中的指令是8086机开机后执行的第一条指令。
思考:什么时候把二进制信息看作指令,什么时候看作数据?
之前学习过,在内存中,指令和数据没有任何区别,都是二进制信息。CPU在工作的时候把有的信息看作指令,有的信息看作数据。所以什么将内存中的信息看作指令?
答案:CPU将CS : IP指向的内存单元中的内容看作为指令。因为在任何时候,CPU将CS,IP中的内容当作指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令码执行。
十一、修改CS、IP的指令
在CPU中,程序员能够用指令读写的部件只有寄存器,程序员可以通过改变寄存器中的内容实现对CPU的控制。CPU从何处执行指令是由CS、IP中的内容决定的,程序猿可以通过改变CS、IP中的内容来控制CPU执行目标指令。
对于一般的通用寄存器,我们可以使用指令mov
来改变AX中的值
; 使用mov指令改变AX寄存器中的值
mov ax, 123 ; 将AX中的值设为123
8086CPU大部分寄存器的值都可以使用mov
指令来改变,**mov**
指令被称为传送指令。
但是,**mov**
指令不能设置CS、IP中的值,因为8086CPU没有提供这样的功能。能够改变CS、IP的内容的指令被统称为转移指令,一个最简单的可以修改CS、IP的指令:jmp
指令。
; 使用jmp跳转指令修改CS和IP中的值
; 1.如果想要同时修改CS和IP的内容,使用 jmp 段地址:偏移地址的指令完成
jmp 2AE3: 3 ; 执行后:CS=2AE3H, IP=0003H,CPU将从2AE33H处读取指令
jmp 3:0B16 ; 执行后,CS=0003H, IP=0B16H,CPU将从00B46H处读取指令
; 2.仅仅想要修改IP中的内容,使用jmp 寄存器就可完成
jmp ax ; 执行前:ax=1000H, CS=2000H, IP=0003H 功能类似于 mov IP, ax
; 执行后:ax=1000H, CS=2000H, IP=1000H
jmp bx ; 执行前:bx=0B16H, CS=2000H, IP=0003H
; 执行后:bx=0B16H, CS=2000H, IP=0B16H
可以看P45问题3来帮助理解这个执行的过程
十二、代码段
前面(八、段地址并不分段)说过,对于8086PC机,可以将一组内存单元理解为一个段。
将长度为#card=math&code=%5Ctext%7BN%7D%28%5Ctext%7BN%7D%5Cleqslant%2064%5Ctext%7BKB%7D%29&id=GfdKS)的一组代码,存放在一组地址连续、起始地址为16倍数的内存单元中,可以认为这段内存是用来存放代码的,从而定义了一个代码段。
拿一个例子来说
mov ax, 0000 ; (B8 00 00) mov指令对应的二进制为B8
add ax, 0123H ; (05 23 01) add指令对应的二进制位05
mov bx, ax ; (8B D8)
jmp bx ; (FF E3)
可以看到上面这段指令一共10个字节(B8 00 00 05 23 … FF E3)
将上述的10个字节的指令,存放在123B0H~123B9H的一组内存单元中,这段内存就是一个内存段,段地址为123BH,长度为10个字节
既然代码段中存放的是代码,所以这个是绝对不可以当作普通的二进制信息被执行,必须成为指令才可以,这个时候用到了上面的CS和IP,CS和IP所指向的内存单元就应该是代码段的物理地址。就像上面的123B0 H
,就需要将它的物理地址放到CS
和IP
中,CS=123BH, IP=0000H
.
检测点
监测点2.1
1.写出每条汇编指令执行后相关寄存器中的值
2.只能使用目前学过的汇编指令,最多使用4条指令,编程计算2的4次方
mov ax,2
add ax,ax
add ax,ax
add ax,ax
add
指令 在汇编中表示两个数相加,而在数学中 两个一样的数相加的值刚好等于这个数乘以2。但是这道题仅仅针对2的次方 如果是其他数的次方 4条或者更多指令只使用add
和mov
指令是计算不出来结果的。
监测点2.2
1.给定段地址0001H,仅通过变化偏移地址寻址,CPU的寻址范围为 00010 H 到 1FFFF H 。
2.有一数据存放在内存20000H单元中,现给定段地址为SA,若想用偏移地址寻到此单元。则SA应满足的条件是:最小为 1001H,最大为 2000H。
提示,反过来思考一下,当段地址给定为多少,CPU无论怎么变化偏移地址都无法寻到20000H单元?
监测点2.3
下面的3条指令执行后,CPU修改几次IP?都是在什么时候?最后IP中的值是多少?
; 第一条指令
mov ax, bx
; 第二条指令
sub ax, ax
; 第三条指令
jmp ax
只在最后的jmp ax
中修改了一次IP,最后的IP值为0000 H.
实验部分
关于实验部分,因为WIN10系统不能进入8086虚拟环境,所以这里使用emu8086来做实验
assume cs:code
data segment
db 'Welcome to asm','$'
data ends
code segment
start:
mov ax,4E20H
add ax,1416H
mov bx,2000H
add ax,bx
mov ax,001AH
mov bx,0026H
add al,bl
add ah,bl
add bh,al
mov ah,0
add al,bl
add al,9CH
code ends
end start
通过F8单步调试后,可以看到对应的寄存器在不断的变化数据。