计组与微机控制
6.1 8086和最小模式最大模式
6.1 8086和最小模式最大模式
32 位处理器在实模式、保护模式下怎么形成地址。8086/8088 CPU 引脚功能,尤其是地址/数据复用、BHE 和 A0、NMI/INTR、RD、CLK。
这是本章节要解决的两个问题
实模式下的地址形成
80386/80486 把 8086/8088 的 IP 从 16 位扩展成了 32 位 EIP。
但注意:
在 实地址模式 下,只能使用 EIP 的低 16 位,也就是 IP。
在 保护模式 下,才能真正使用 32 位 EIP。
1. 实模式下为什么还只用 IP 的低 16 位?
因为实模式本质上是为了兼容 8086。
8086 的取指地址是:CS:IP
其中:CS:16 位段地址 IP:16 位偏移地址
到了 80386/80486,虽然 EIP 是 32 位,但如果 CPU 工作在实模式,为了保持和 8086 程序兼容,就仍然按 8086 那套规则来:
段地址左移 4 位 + 16 位偏移量
所以实模式下,EIP 的高 16 位不能随便用。
寄存器虽然扩展成 32 位了,但实模式下仍然受 8086 兼容规则限制。
二、实模式下地址形成
在实模式下,80386/80486 的地址形成方式基本上和 8086/8088 相同:
物理地址 = 段基址左移 4 位 + 偏移量
也就是:物理地址 = 段寄存器 × 10H + 偏移量
但有两点要注意。
1. 偏移地址不能大于 0FFFFH
虽然 80386/80486 有 32 位寄存器,比如 EAX、EBX、ESI、EDI、EBP、ESP,但是在实模式下,偏移地址仍然不能超过:0FFFFH
也就是 64KB 范围。为什么?
因为实模式的逻辑段仍然按 8086 的方式理解:
一个段最大 64KB 偏移量范围:0000H ~ FFFFH
所以即使你有 32 位寄存器,如果在实模式里用一个大于 FFFFH 的偏移量访问内存,也会导致寻址错误。
可以这样记:实模式下,寄存器可以是 32 位,但段内偏移仍然按 16 位限制。
2. 80386/80486 实模式寻址范围比 8086 多一点
这一句有点绕,课本说:80386/80486 在实模式下的寻址范围是:
1MB + 64KB - 16B也就是:
0 ~ 10FFEFH而不是 8086/8088 的:0 ~ 0FFFFFH
3. 为什么最大能到 10FFEFH?
因为实模式地址公式仍然是:物理地址 = 段地址 × 10H + 偏移地址
如果段地址最大:FFFFH
那么段基址是:FFFFH × 10H = FFFF0H
如果偏移量也最大:FFFFH
那么最大物理地址是:
FFFF0H + FFFFH = 10FFEFH
所以理论上可以访问到:10FFEFH
也就是比 1MB 多出接近 64KB 的范围。
4. 为什么 8086 不行?
8086 只有 20 根地址线。
20 位最大地址是:FFFFFH
如果计算结果超过 FFFFFH,就会发生回绕。
例如课本举的:0FFFBH:0FFFFH
计算:
0FFFBH × 10H + 0FFFFH= 0FFFB0H + 0FFFFH= 10FFAFH
这个结果超过了 20 位地址范围。
8086 只能输出低 20 位,所以高出来的那一位会被丢掉,变成:0FFAFH
这就叫地址回绕。
5. 为什么 80386/80486 不回绕?
因为 80386/80486 的地址线更多。
80386 是 32 位地址总线,理论物理地址空间可达 4GB。
所以在实模式下,如果计算出 10FFEFH 这种超过 1MB 的地址,80386/80486 的硬件是有能力送出这个地址的。
课本说,在 DOS 中:100000H ~ 10FFEFH
这部分空间叫 上位内存空间。
8086 因为只有 20 位地址线,超过 1MB 会绕回低地址。80386/80486 地址线更多,所以实模式下可以访问 1MB 上面一点点的空间。
保护模式下的 32 位微处理器编程结构
接下来进入:保护模式下的 32 位微处理器的编程结构
保护模式比实模式复杂很多,这里课本只是简介。
一、保护模式下新增了什么寄存器?
课本说:除了和实模式相同的通用寄存器、段寄存器、指令寄存器、标志寄存器外,还新增:4 个控制寄存器 4 个存储管理寄存器
在 80386/80486 中:控制寄存器:CR0、CR1、CR2、CR3
到 Pentium 又增加:CR4
存储管理寄存器主要是:
GDTRLDTRIDTRTR
这几个是保护模式的核心。
二、控制寄存器 CR0 ~ CR3
控制寄存器用来保存 CPU 的全局控制状态。
这些寄存器不是普通应用程序能随便读写的,通常只有操作系统内核能操作。
课本说它们很重要,所以只有最高特权级,也就是特权级 0 的系统程序可以访问。
1. CR0:控制处理器工作方式
CR0 里面有很多控制位,用来控制 CPU 的基本工作方式。比如:
是否进入保护模式。是否启用分页。是否启用某些协处理器相关功能。当前处理器状态如何。
你可以先简单记:
CR0 决定 CPU 的重要工作模式
例如从实模式进入保护模式,关键就和 CR0 中的保护模式使能位有关。
2. CR1:保留
课本说 CR1 留作扩充用。
也就是当时没有实际使用,预留给以后扩展。
3. CR2:页故障线性地址
CR2 用在分页机制中。
如果发生页故障,也就是访问某个页面出错,CPU 会把引起错误的线性地址保存到 CR2 中。这样操作系统处理页故障时,就可以知道:
刚才到底访问了哪个地址导致出错?所以:CR2 保存页故障地址
4. CR3:页目录基址寄存器
CR3 和分页机制密切相关。
分页时,CPU 要通过页目录、页表把线性地址转换成物理地址。
CR3 保存页目录的基地址。
所以:
CR3 指向当前页目录
操作系统切换进程时,常常会切换 CR3,因为不同进程有不同的地址空间。
5. CR4:Pentium 增加的控制寄存器
CR4 是 Pentium 后加入的,用来控制一些扩展功能。
课本这里不深入,
CR4 用来开启或关闭一些新的系统扩展功能
第 30 页:图 2.18 保护模式下寄存器结构
图 2.18 把保护模式下寄存器画出来了。

上面仍然是熟悉的:
EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESPCS、DS、SS、ES、FS、GSEIPEFLAGS
下面多了:
GDTRIDTRLDTRT RCR0 ~ CR3
这说明保护模式的重点不是普通运算寄存器,而是:
内存保护 任务管理中断管理 分页管理
这些都需要专门的寄存器支持。
三、存储管理寄存器
接下来讲 4 个存储管理寄存器。
1. GDTR:全局描述符表寄存器
GDTR 全称:Global Descriptor Table Register
意思是:全局描述符表寄存器
它保存的是 GDT 的信息。
GDT 是 Global Descriptor Table,全局描述符表。
GDTR 里面包含两部分:
GDT 的 32 位基地址 GDT 的 16 位界限
也就是告诉 CPU:GDT 从哪里开始? GDT 有多大?
2. GDT 是干什么的?
保护模式下,段寄存器里不再直接保存段基址。
它保存的是一个 段选择符 selector。
段选择符会去 GDT 或 LDT 中找到一个 段描述符 descriptor。
段描述符里才保存:
段基址段界限段属性访问权限段类型特权级
所以 GDT 可以理解成一张“段信息表”。
每一项描述一个段。
3. LDTR:局部描述符表寄存器
LDTR 全称:
Local Descriptor Table Register
意思是局部描述符表寄存器。
LDT 是 Local Descriptor Table,局部描述符表。
GDT 是全局的,系统中所有任务都可以共用某些描述符。
LDT 更偏向某个任务自己的描述符表。
LDT 包含了某一任务私用的描述符。
也就是说:GDT:全局共享 LDT:任务私有
4. IDTR:中断描述符表寄存器
IDTR 全称:
Interrupt Descriptor Table Register
意思是中断描述符表寄存器。
IDT 是 Interrupt Descriptor Table,中断描述符表。
在 8086 里,中断向量表保存中断服务程序入口。
到了 80386/80486 保护模式下,变成用 IDT 管理中断和异常。
IDTR 保存:IDT 的基地址 IDT 的界限
也就是告诉 CPU:中断描述符表在哪里? 表有多大?
5. 中断和异常的区别
页脚里有一句注释:
在 80386/80486 中,把 8086/8088 的中断细分为:
中断异常
一般理解:
中断通常来自外部设备,比如键盘、定时器、外设请求。
异常通常来自 CPU 内部执行指令时出现的问题,比如除法错误、页故障、非法指令等。
你可以简单记:
中断:多来自外部异常:多来自内部执行错误或特殊事件
6. TR:任务寄存器
TR 是 Task Register,任务寄存器。
保护模式支持任务切换,每个任务有一个任务状态段:
TSS:Task State Segment
TSS 里面保存一个任务的运行状态。
比如:
寄存器状态堆栈信息段选择符任务切换所需信息
TR 中保存当前任务的 TSS 选择符。
也就是说:
TR 指向当前任务的 TSS
CPU 需要任务切换时,就可以根据 TR 找到当前任务状态,并切换到另一个任务。
第 31 页:保护模式下的地址形成
这一页非常重要。
课本开始讲:
保护模式下的地址形成。
在实模式下:
逻辑地址 = 段地址:偏移量物理地址 = 段地址 × 16 + 偏移量
但在保护模式下,不再这么简单。
一、保护模式下访问存储器都通过描述符表
课本说:
在保护模式下,所有的存储器访问都是通过描述符表进行的。
描述符表里的每一项叫描述符。
每个描述符 8 个字节。
段描述符用来描述一个段,包括:
段基址段长度访问权限段类型特权级
所以保护模式不是直接相信程序给出的地址,而是先检查这个地址是否合法。
1. 为什么保护模式要这么麻烦?
因为保护模式的目标是保护系统。
比如一个用户程序想访问操作系统内核内存,CPU 必须阻止它。
一个程序访问超过自己段界限的地址,CPU 也要阻止。
一个低特权级程序执行高特权指令,CPU 也要阻止。
所以保护模式下,地址访问不仅要算地址,还要检查权限。
二、保护模式下的逻辑地址组成
课本说,保护模式下一个逻辑地址包括:
16 位段选择符 + 32 位偏移量
也就是:Selector : Offset
注意,这里不再说“段地址”,而是说“段选择符”。
这是和实模式最大的区别之一。
1. 段选择符是什么?
段选择符不是段基址。
它更像一个“索引”。它告诉 CPU:
去 GDT 或 LDT 中找哪个段描述符
段描述符里才真正保存这个段的基址、界限、属性等。
所以保护模式下段寄存器里表面上装的是段选择符。
2. 偏移量是什么?
偏移量是段内地址。
在 32 位保护模式下,偏移量通常可以是 32 位。
所以一个段最大理论上可以达到:4GB
这和 8086 每段最大 64KB 完全不同。
三、保护模式地址形成过程
图 2.19 画得很关键,过程可以分成两步:

逻辑地址 → 线性地址 → 物理地址
1. 第一步:逻辑地址变线性地址
逻辑地址是:段选择符 : 偏移量
CPU 根据段选择符去 GDT 或 LDT 找到段描述符。
段描述符里有段基址。
然后:线性地址 = 段基址 + 偏移量
这一部分叫 分段机制。
2. 第二步:线性地址变物理地址
如果没有开启分页,那么线性地址可以直接当物理地址。
如果开启分页,那么 CPU 还要通过页目录、页表进行地址转换:
线性地址 → 物理地址这一部分叫 分页机制。
所以保护模式完整地址形成是:
逻辑地址= 段选择符 + 偏移量通过 GDT/LDT 找段描述符得到段基址段基址 + 偏移量= 线性地址如果启用分页线性地址经页表转换= 物理地址
3. 图 2.19 怎么读?
逻辑地址 = 选择符 + 偏移量
选择符去 GDT/LDT 中查找段描述符。
段描述符给出段基址。
段基址和偏移量相加,形成线性地址。
然后右边:线性地址经过页地址转换,变成物理地址。
这就是保护模式的地址转换过程。
4. 和实模式对比
项目 | 实模式 | 保护模式
段寄存器内容 | 段地址 | 段选择符
段基址来源 | 段寄存器左移 4 位 | 描述符表中的段描述符
偏移量 | 通常 16 位 | 可为 32 位
段大小 | 最大 64KB | 可大到 4GB
保护检查 | 基本没有 | 有界限、权限、特权级检查
地址过程 | 段地址×16+偏移 | 选择符查描述符→线性地址→分页→物理地址可以记:
实模式的段寄存器装的是段地址;保护模式的段寄存器装的是选择符。
8086/8088 CPU 的引脚功能:(之前讲过一部分)
接下来进入 8086/8088 CPU 的引脚功能。
这一节开始偏硬件。
地址总线数据总线控制总线时钟中断读写信号最小/最大工作模式
一、最小模式和最大模式
课本说,在讲引脚前,要先介绍最小工作模式和最大工作模式。
因为同一个引脚在不同模式下可能功能不同。
1. 最小模式
最小模式指系统中只有一个 8086/8088 微处理器。
在这种系统里,总线控制信号直接由 8086/8088 自己产生。
系统结构比较简单。
所以:最小模式 = 单处理器系统
总线控制逻辑最少。
2. 最大模式
最大模式指系统中包含两个或多个处理器。
其中一个主处理器是 8086/8088。
其他处理器称为协处理器。课本举了两个:
8087:数值运算协处理器8089:输入/输出协处理器
最大模式下,因为系统更复杂,总线控制信号不完全由 CPU 直接给出,而是要配合总线控制器等外部逻辑。
所以:最大模式 = 多处理器/协处理器系统
3. 8087 是干什么的?
8087 是数值运算协处理器。它专门处理复杂数值运算,比如:
浮点运算 高精度整数运算三角函数 对数函数
这些如果完全靠软件算会比较慢。有 8087 后,可以用硬件加速,提高数值运算速度。
4. 8089 是干什么的?
8089 是输入/输出协处理器。课本说,计算机中大量时间花在 I/O 和数据格式转换上。如果让 8086/8088 全部亲自处理,会降低主 CPU 效率。
8089 可以专门负责 I/O 操作,使主处理器不必承担大量输入输出工作。
可以理解成:
8087 帮 CPU 算数学8089 帮 CPU 管 I/O
二、总线周期 T1 ~ T4
8086/8088 对存储器或 I/O 端口的读写通过系统总线进行。
总线周期一般至少由:T1、T2、T3、T4组成。
如果连接慢速存储器或外设,还可以插入等待周期:Tw
1. T1 做什么?
T1 通常用于输出地址。
也就是 CPU 告诉外部:
我要访问哪个内存单元或 I/O 端口?
2. T2、T3、T4 做什么?
后面的状态一般用于:传输数据 产生读/写控制信号等待外部设备响应 完成总线周期
具体读操作和写操作时序会略有不同。
T1:给地址T2~T4:传数据和控制Tw:慢设备需要等待时插入
8086/8088 引脚图
图 2.20 画的是 8086 和 8088 的引脚信号。
两者都是 40 引脚芯片。
括号内是最大模式下的引脚名。
这一节会逐个讲引脚。

一、AD15 ~ AD0:地址/数据复用引脚
1. 什么叫地址/数据复用?
同一组引脚有时传地址,有时传数据。
在一个总线周期中:T1 状态:传地址 T2、T3、T4 状态:传数据
这样可以节省引脚数量。如果地址线和数据线完全分开,CPU 需要更多引脚。
但 8086/8088 只有 40 个引脚,所以采用复用技术。
2. 8086 的 AD15 ~ AD0 8086 有 16 位外部数据总线。
所以 AD15 ~ AD0 在不同状态下作用是:
T1:输出低 16 位地址 A15 ~ A0T2~T4:传输 16 位数据 D15 ~ D0
注意,8086 共有 20 位地址线。
低 16 位地址由 AD15 ~ AD0 在 T1 输出。
高 4 位地址由 A19/S6 ~ A16/S3 输出。
3. 8088 的 AD7 ~ AD0 8088 外部数据总线只有 8 位。
所以 8088 中:
AD7 ~ AD0 是地址/数据复用A15 ~ A8 是单纯地址输出
也就是:8088 的低 8 位地址和数据复用。高一些的地址线不是数据线。
这也解释了为什么 8088 外部传数据比 8086 慢:
8086 一次外部可传 16 位8088 一次外部只传 8 位
4. 为什么需要锁存器?
因为 AD 线在 T1 是地址,到了 T2 以后就变成数据。
但外部存储器在整个读写过程中都需要知道地址。
所以 T1 期间输出的地址必须被外部锁存器保存下来。
这就要用到 ALE 信号。
简单说:
T1:CPU 在 AD 线上放地址ALE 有效:外部锁存器把地址锁住T2~T4:AD 线改用来传数据
所以 AD 复用一定要配合地址锁存。
