← 返回 计组与微机控制

计组与微机控制

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 把保护模式下寄存器画出来了。

6.1 8086和最小模式最大模式 图 1

上面仍然是熟悉的:

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 画得很关键,过程可以分成两步:

6.1 8086和最小模式最大模式 图 2

逻辑地址 → 线性地址 → 物理地址

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 引脚芯片。

括号内是最大模式下的引脚名。

这一节会逐个讲引脚。

6.1 8086和最小模式最大模式 图 3

一、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 复用一定要配合地址锁存。