[从 0 开始写一个操作系统] A20 Gate
A20 Gate
作者:解琛
时间:2020 年 9 月 4 日
Intel 早期的 8086 CPU 提供了 20 根地址线,可寻址空间范围即 (00000H ~ FFFFFH) 的 1MB 内存空间。
但 8086 的数据处理位宽位 16 位,无法直接寻址 1MB 内存空间,所以 8086 提供了段地址加偏移地址的地址转换机制。
PC 机的寻址结构是 segment:offset,segment 和 offset 都是 16 位的寄存器,最大值是 0ffffh。
换算成物理地址的计算方法是把 segment 左移 4 位,再加上 offset,所以 segment:offset 所能表达的寻址空间最大应为 0ffff0h + 0ffffh = 10ffefh。
前面的 0ffffh 是 segment = 0ffffh 并向左移动 4 位的结果,后面的 0ffffh 是可能的最大 offset。
这个计算出的 10ffefh 是多大呢?大约是 1088KB,就是说,segment:offset 的地址表示能力,超过了 20 位地址线的物理寻址能力。
所以当寻址到超过 1MB 的内存时,会发生“回卷”(不会发生异常)。
下一代的基于 Intel 80286 CPU 的 PC AT 计算机系统提供了 24 根地址线,这样 CPU 的寻址范围变为 ,同时也提供了保护模式,可以访问到 1MB 以上的内存了,此时如果遇到“寻址超过 1MB”的情况,系统不会再“回卷”了,这就造成了向下不兼容。
为了保持完全的向下兼容性,IBM 决定在 PC AT 计算机系统上加个硬件逻辑,来模仿以上的回绕特征,于是出现了 A20 Gate。
他们的方法就是把 A20 地址线控制和键盘控制器的一个输出进行 AND 操作,这样来控制 A20 地址线的打开(使能)和关闭(屏蔽\禁止)。
一开始时 A20 地址线控制是被屏蔽的(总为 0),直到系统软件通过一定的 IO 操作去打开它。
在实模式下要访问高端内存区,这个开关必须打开。
在保护模式下,由于使用 32 位地址线,如果 A20 恒等于0,那么系统只能访问奇数兆的内存,即只能访问 0-1M、2-3M、4-5M…,这显然是不行的,所以在保护模式下,这个开关也必须打开。
当 A20 地址线控制禁止时,则程序就像在 8086 中运行,1MB 以上的地址是不可访问的。
在保护模式下 A20 地址线控制是要打开的。
为了使能所有地址位的寻址能力,必须向键盘控制器 8042 发送一个命令。
键盘控制器 8042 将会将它的的某个输出引脚的输出置高电平,作为 A20 地址线控制的输入。
一旦设置成功之后,内存将不会再被绕回(memory wrapping),这样我们就可以寻址整个 286 的 16M 内存,或者是寻址 80386 级别机器的所有 4G 内存了。
键盘控制器 8042 的逻辑结构图如下所示。
早期的 PC 机,控制键盘有一个单独的单片机 8042,现如今这个芯片已经给集成到了其它大片子中,但其功能和使用方法还是一样。
当 PC 机刚刚出现 A20 Gate 的时候,为节省硬件设计成本等考虑,工程师使用这个 8042 键盘控制器来控制 A20 Gate,但 A20 Gate 与键盘管理没有一点关系。
8042 键盘控制器的 IO 端口是 0x60~0x6f,实际上 IBM PC/AT 使用的只有 0x60 和 0x64 两个端口(0x61、0x62 和 0x63 用于与 XT 兼容目的)。
8042 通过这些端口给键盘控制器或键盘发送命令或读取状态。
输出端口 P2 用于特定目的。
位 0(P20引脚)用于实现 CPU 复位操作,位 1(P21 引脚)用户控制 A20 信号线的开启与否。
系统向输入缓冲(端口 0x64)写入一个字节,即发送一个键盘控制器命令,可以带一个参数。
参数是通过 0x60 端口发送的。 命令的返回值也从端口 0x60 去读。
8042 有 4 个寄存器:
- 1 个 8-bit 长的 Input buffer,Write-Only;
- 1 个 8-bit 长的 Output buffer,Read-Only;
- 1 个 8-bit 长的 Status Register,Read-Only;
- 1 个 8-bit 长的 Control Register,Read/Write。
有两个端口地址,60h 和 64h,有关对它们的读写操作描述如下:
- 读 60h 端口,读 output buffer;
- 写 60h 端口,写 input buffer;
- 读 64h 端口,读 Status Register;
- 操作 Control Register。
- 要向 64h 端口写一个命令(20h 为读命令,60h 为写命令);
- 根据命令从 60h 端口读出 Control Register 的数据;
- 或向 60h 端口写入 Control Register 的数据(64h 端口还可以接受许多其它的命令)。
Status Register 的定义(要用 bit 0 和 bit 1):
bit | meaning |
---|---|
0 | output register (60h) 中有数据 |
1 | input register (60h/64h) 有数据 |
2 | 系统标志(上电复位后被置为0) |
3 | data in input register is command (1) or data (0) |
4 | 1=keyboard enabled, 0=keyboard disabled (via switch) |
5 | 1=transmit timeout (data transmit not complete) |
6 | 1=receive timeout (data transmit not complete) |
7 | 1=even parity rec’d, 0=odd parity rec’d (should be odd) |
8042 还有 3 个内部端口:Input Port、Outport Port 和 Test Port。
这三个端口的操作都是通过向 64h 发送命令,然后在 60h 进行读写的方式完成,其中本文要操作的 A20 Gate 被定义在 Output Port 的 bit 1 上,所以有必要对 Outport Port 的操作及端口定义做一个说明。
- 读 Output Port:向 64h 发送 0d0h 命令,然后从 60h 读取 Output Port 的内容;
- 写 Output Port:向 64h 发送 0d1h 命令,然后向 60h 写入 Output Port 的数据;
- 禁止键盘操作命令:向 64h 发送 0adh;
- 打开键盘操作命令:向 64h 发送 0aeh。
有了这些命令和知识,就可以实现操作 A20 Gate 来从实模式切换到保护模式了。
理论上讲,我们只要操作 8042 芯片的输出端口(64h)的bit 1,就可以控制 A20 Gate。
实际上,当你准备向 8042 的输入缓冲区里写数据时,可能里面还有其它数据没有处理,所以,我们要首先禁止键盘操作,同时等待数据缓冲区中没有数据以后,才能真正地去操作 8042 打开或者关闭 A20 Gate。
打开 A20 Gate 的具体步骤大致如下:
- 等待 8042 Input buffer 为空;
- 发送 Write 8042 Output Port (P2)命令到8042 Input buffer;
- 等待 8042 Input buffer 为空;
- 将 8042 Output Port(P2)得到字节的第 2 位置 1,然后写入 8042 Input buffer。