汇编(三)
8086的寻址方式
- CPU访问内存单元时,要给出内存单元的地址,所有的内存单元都有唯一的地址,叫做物理地址
- 8086有20根地址总线,可以传送20位的地址,具有1M(2^20)的寻址能力
- 但是同时,8086又是16位结构的CPU,它内部能够一次性处理、传输、暂存的地址为16位。如果将地址从内部简单地发出,那么它只能送出16位的地址,表现出来的寻址能力,只有64K(2^16)
- 因此,8086采用一种在内部使用2个16位地址合成的方法,来生成1个20位物理地址
段地址和偏移地址有多种组合方式
物理地址 | 段地址 | 偏移地址 |
---|---|---|
21F60H | 2000H | 1F60H |
21F60H | 2100H | 0F60H |
21F60H | 21F0H | 0060H |
21F60H | 21F6H | 0000H |
21F60H | 1F00H | 2F60H |
结论:CPU可以用不同的段地址和偏移地址形成同一个物理地址
比如,CPU要访问21F60H内存单元,则他给出的段地址SA和偏移地址EA,满足 SA x 16 + EA = 21F60H即可(x16即进一位,即左移一位)
内存分段管理
- 8086是用基地址(段地址 x 16)+ 偏移地址 = 物理地址的方式给出物理地址
- 为了开发方便,我们可以采取分段的方法来管理内存,比如:
(1)地址10000H ~ 100FFH的内存单元组成1个段,该段的起始地址(基地址)为10000H,段地址为1000H,大小为100H
(2)地址10000H ~ 1007FH、10080 ~ 100FF的内存单元组成2个段,它们的起始地址(基地址)分别为10000H和10080H,段地址为1000H和1008H,大小为80H - 在编程时可以根据需要,将若干连续地址的内存单元看做一个段,用段地址 x 16定为段的起始地址(基地址),用偏移地址定位段中的内存单元
- 段地址 x 16必然是16的倍数,所以 一个段的起始地址(基地址)也一定是16的倍数
- 偏移地址为16位,16位地址的寻址能力为64K,所以一个段的长度最大为64K
Question: 如果给定一个段地址,仅通过变化偏移地址来进行寻址,最多可定位多少个内存单元?
Answer: 偏移地址16位,变化范围为 0000H ~ FFFFH,仅用偏移地址来寻址最多可寻64K个内存单元。
在8086PC机中,存储单元的地址用两个元素来描述,即段地址和偏移地址。
“数据在 21F60H 内存单元中” 这句话对于8086PC机一般不这么讲,取而代之的是两种类似的说法:
1.数据存在内存 2000:1F60 单元中
2.数据存在内存的 2000H 段中的 1F60H 单元中
这两种描述都表示"数据在 21F60H 内存单元中"
段寄存器
- 8086在访问内存时要由相关部件提供内存单元的段地址和偏移地址,送入地址加法器合成物理地址
- 是什么部件提供段地址?-- 段地址在8086的段寄存器中存放
- 8086有4个段寄存器:CS、DS、SS、ES,当CPU需要访问内存时由这4个段寄存器提供内存单元的段地址
- CS (Code Segment):代码段寄存器
- DS (Data Segment):数据段寄存器
- SS (Stack Segment):堆栈段寄存器
- ES (Extra Segment):附加段寄存器
代码段CS和IP
- CS为代码段寄存器,IP为指令指针寄存器,它们指示了CPU当前要读取指令的地址
- 任意时刻,8086CPU都会将CS:IP指向的指令作为下一条需要取出执行的指令
(1)8086CPU当前状态:CS中的内容为 2000H,IP中的内容为 0000H;
(2)内存 20000H ~ 20009H 单元中存放着可执行的机器码;
(3)内存 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
指令的执行过程
通过上面的过程展示,8086CPU 的工作过程可以简要描述如下:
(1)从CS:IP,指向的内存单元读取指令,读取的指令进入指令缓冲器;
(2)IP=IP+所读取指令的长度,从而指向下一条指令;
(3)执行指令。转到步骤(1),重复这个过程。
在 8086CPU 加电启动或者复位后(即 CPU 刚开始工作时) CS 和 IP 被设置为 CS=FFFFH,IP=0000H,即在 8086PC机 刚启动时, CPU 从内存 FFFF0H 单元中读取指令执行, FFFF0H 单元中的指令是 8086PC机 开机后执行的第一条指令。
指令和数据的本质
- 在内存或者磁盘上,指令和数据没有任何区别,都是二进制数据
- CPU在工作的时候把有的信息看做指令,有的信息看做数据,为同样的信息赋予了不同的意义
例如:内存中的二进制信息 1000 1001 1101 1000,计算机可以把它看做大小为 89D8H 的数据来处理,也可以将其看做指令 mov ax,bx 来执行。
1000 1001 1101 1000 -> 89D8H(数据)
1000 1001 1101 1000 -> mov ax,bx(程序)
Question: CPU根据什么将内存中的信息看做指令?
Answer:
CPU将CS:IP指向的内存单元的内容看做指令
如果内存中的某段内容曾被CPU执行过,那么它所在的内存单元必然被CS:IP指向过
jmp指令
-
CPU从何处执行指令是由CS、IP中的内容决定的,我们可以通过改变CS、IP的内容来控制CPU执行目标指令
-
8086提供了一个mov指令(传送指令),可以用来修改大部分寄存器的值 ,比如:
mov ax,10、mov bx,20、mov cx,30、mov dx,40 -
但是,mov指令不能用于设置CS、IP的值,8086没有提供这样的功能
-
8086提供了另外的指令来修改CS、IP的值,这些指令统称为转移指令,最简单的是jmp指令
"jmp 段地址 : 偏移地址"指令的功能为:用指令中给出的段地址修改CS,偏移地址修改IP。
"jmp 某一合法寄存器"指令的功能为:用寄存器中的值修改IP,CS中的值保持不变。 jmp ax,在含义上好似:mov IP, ax
(1)若想同时修改CS,IP的内容,可用形如 “jmp 地址段 : 偏移地址” 的指令完成,如:
jmp 2AE3:3,执行后:CS=2AE3H,IP=0003H,CPU将从2AE33H处读取指令。
jum 3:0B16,执行后:CS=0003H,IP=0B16H,CPU将从00B46H处读取指令。
(2)若想仅修改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指令练习
(1)mov ax, 6622H
(2)jmp 1000H:0003H
(3)mov ax, 0000H
(4)mov bx, ax
(5)jmp bx
(6)mov ax, 0123H
(7)转到第(3)步执行
(1)mov ax, bx:IP的值第一次被修改
(2)sub ax, ax:IP的值第二次被修改,ax的值为0H
(3)jmp ax:IP的值第三次被修改
(4)执行jmp ax,IP的值第四次被修改,修改为ax中的值,为0H
代码段
前面讲过,对于 8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。我们可以将长度为 N(N <= 64K)的一组代码,存在一组地址连续,起始地址为16的倍数的内存单元中,我们可以认为,这段内存是用来存放代码的,从而定义了一个代码段。比如,将:
mov ax, 0000H (B8 00 00)
add ax, 0123H (05 23 01)
mov bx, ax (8B D8)
jmp bx (FF E3)
这段长度为10个字节的指令,存放在 123B0H ~ 123B9H 的一组内存单元中,我们就可以认为, 123B0H ~ 123B9H这段内存是用来存放代码的,是一个代码段,它的段地址为 123BH,长度为10个字节。
如何使得代码段中的指令被执行呢?将一段内存当做代码段,仅仅是我们在编程时的一种安排,CPU并不会由于这种安排,就自动地将我们定义的代码段中的内容当做指令来执行。 CPU只认被CS:IP指向的内存单元中的内容为指令。所以,要让CPU执行我们放在代码段中的指令,必须要将 CS:IP 指向所定义的代码段中的第一条指令的首地址。对于上面的例子,我们将一段代码存放在 123B0H ~ 123B9H 内存单元中,将其定义为代码段,如果要让这段代码得到执行,可设 CS=123BH,IP=0000H。