8086 中生成物理地址的方式
8086 的 CPU
8086 是一个十六位结构的 CPU,一个十六位结构具备以下这几方面的特性
- 运算器一次最多可以处理 16 位的数据;
- 寄存器的最大宽度为 16 位;
- 寄存器和运算器之间的通路为 16 位。
物理地址=段地址 X 16 + 偏移地址
然而,8086 的地址总线有 20 位宽可以寻址 1M 的内存,CPU 的 16 位只能寻址 64 KB。CPU 是如何把 16 位的数据变成 20 位呢?8086 使用内部两个 16 位地址合成 20 位地址。
当 CPU 需要寻址时
- CPU 中的相关部件提供两个 16 位的地址,一个称为段地址,另一个称为偏移地址
- 段地址和偏移地址通过内部总线送入一个称为地址加法器的部件
- 地址加法器将两个 16 位的地址合成一个 20 位的物理地址
- 地址加法器通过内部总线将 20 位的物理地址送入输入输出控制电路
- 输入输出控制电路将 20 位的物理地址送上地址总线
地址加法器采用 物理地址=段地址 X 16 + 偏移地址 的方法用段地址和偏移地址合成物理地址,段地址 X 16 通过左移 4 位完成,站在 16 进制上看就是左移一位。
”物理地址=段地址 X 16 + 偏移地址“ 的本质含义
王爽的《汇编语言》写得很好就是在这些地方体现,他的描述让我明白了为什么。我对此的理解就是 CPU 的设计妥协艺术。
CPU 设计时要考虑内部、外部的情况,内部设计为 16 位结构而不是 32 位结构是因为这是 8086 时代所限。
那外部为什么没设计成 16 位地址总线呢?如果是 16 位的地址总线,寻址能力 64 KB,这就不是大放异彩的 8086 了。
为了强大的寻址范围,同时受限于内部的结构采用了这么一种方案。用既有的能力合成出更大的能力。
段的概念和段寄存器
首先说段的概念,并不是将内存划分成了几个段。内存是连续的,在需要的时候将若干地址连续的内存单元看做一个段,用段地址 X 16 定位段的起始地址(基础地址),用便宜地址定位段中的内存单元。所以段的起始地址一定是 16 的倍数,十六进制最后一位一定是 0,二进制最后四位一定是 0。偏移地址为 16 位,16 位只能寻址 64 KB,所以一个段的长度最大为 64 KB
CS 和 IP 是 8086 中最关键的寄存器,之前我就知道 IP 是程序计数器,永远指向下一条指令地址。
在 8086 中,任意时刻设 CS 中的内容为 M,IP 中的内容为 N,8086 从内存位置 M X 16 + N 单元开始,读取一条指令并执行。
在任意时刻 CPU 将 CS:IP 指向的内容当做指令执行。
每执行一条指令,IP 都会加上该指令的长度以指向下一条指令位置。
8086 在加电启动或者复位时 CS=0xF000 IP=0xFFFF
修改 CS、IP 的指令
对于普通寄存器如 AX,可以使用传送命令修改值
mov ax, 123 # 将 ax 中的值设置为 123
但是不能使用 mov 直接改变 CS、IP 的值,8086 没有提供这样的功能,因为提供了另外的指令来改变值。能改变 CS、IP 的内容的指令被统称为转移指令如 jmp 指令。
jmp 段地址:偏移地址
jmp 0x2AE3:3 #执行后 CS=0x2AE3 IP=0x0003
如果只想修改 IP 的内容,使用 ”jmp 某一合法寄存器“ 来完成
jmp ax, 指令执行前 ax=0x1000 CS=0x2000 IP=0x0003
指令执行后 ax=0x1000 CS=0x2000 IP=0x1000