A20地址线困惑我了很久,这篇文章终于揭开了这个谜团。详细阐述之前,我先来总结一把—— 8086/8088和80286地址转换方式一样,寻址范围不一样 。
8086/8088和80286中“逻辑地址-->线性地址”的转换方式是一样的:segment:offset-->segment<<4+offset,而且segment和offset均只有16bit. 只不过因为8086/8088是20位地址总线,而80286是24位地址总线,故地址范围有点不一样:
(1)8086/8088寻址范围是0~1M(在8086/8088中1M以上的extend memory地址回回绕到1M内的模值);
(2)80286寻址范围是0~1M~10FFEFh -->注:虽然80286有24根地址线,但实际上只用了21根,浪费了3根,这是由于寻址方式的牵制。
1 先看看 real mode 的寻址方法
8086/8088 的地址线有 20 条:A0 ~ A19,意味着 processor 可以将 20 位地址放上这 20 条地址线上,它的寻址能力是 1M (00000 ~ FFFFF),它的寻址方法是:segment:offset ,这是一种被称为 logic address (逻辑地址)表示法,它需要转化为 processor 的 linear address(线性地址)表示:
segment:offset ---------> segment << 4 + offset |
如:F000:FFFF = F0000 + FFFF = FFFFF ,这是 8086/8088 所能访问的最高地址。这种表示方法是 Intel 为了在 16 位 real mode 下能够访问 20 位地址空间 所想设计出来的计算方式。
因此,8086/8088 的寻址范围是可以表示为:从 0000:0000 ~ 0000:FFFF 开始到 F000:0000 ~ F000:FFFF
2 访问 extended memory——1M及其以上地址
在后续的 80286 上,Intel 实现了 24 位的 Address bus,那么在 real mode 下 80286 能够访问到的最高地址是 10FFEF,这个地址值是由下面的方法而来:
FFFF:FFFF = FFFF0 + FFFF = 10FFEFh |
这已经是 logic address 所能表达的极限范围了。100000h 以上的内存被称为 extend memory,从 100000h ~ 10FFEFh 这片内存区域在 DOS 下被称为 High Memory(高端内存)。高端内存是 80286 在 real mode 所能访问到的区域,而 8086/8088 所不能访问到的。
3 wraparound 现象——只有A0~A19上的信号有效
当在 8086/8088 下执行 FFFF:FFFF 这个内存寻址时,会产生什么结果呢?
19
|
18
|
17
|
16
|
15
|
14
|
13
|
12
|
11
|
10
|
9
|
8
|
7
|
6
|
5
|
4
|
3
|
2
|
1
|
0
| ||||
0
|
0
|
0
|
1
|
0
|
0
|
0
|
0
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
0
|
1
|
1
|
1
|
1
|
结果很明显:由于 8086/8088 只有 20 条 address bus,地址 10FFEF 的高 4 位会被抛弃,实际上送上 address bus 的只有 0FFEFh 值,所以访问 FFFF:FFFF 地址结果只能访问到 1M 以内的地址。这就是 wraparound 现象:访问 1M 以上地址都会回绕到 1M 内的模值。
那么,当 80286 下访问 FFFF:FFFF 地址时,又会产生什么果呢? 由于 80286 具有 24 条 address bus,对于 FFFF:FFFF 地址的访问,会正确得到访问。
SO,访问 FFFF:FFFF 内存,使得 8086/8088 下产生 wraparound 现象,变相访问 0FFEF 地址内存。而在 80286 下得到正确的的 10FFEF 地址,不存在 wraparound 现象。因此:wraparound 现象在 8086/8088 才会产生。这样产生的问题是:访问高端内存时,80286 在 real mode 下和 8086/8088 的行为不一致!
4 引入 A20 Gate
为了使用 80286 和 8086/8088 在 real mode 下的行为一致,即:在 80286 下也产生 wraparound 现象 。IBM 想出了古怪方法:当 80286 运行在 real mode 时,将 A20 地址线(第 21 条 address bus)置为 0 ,这样使得 80286 在 real mode 下第 21 条 address line 无效,从而人为造成了 wraparound 现象。
具体实现方法是:
设立一个 AND Gate (与门电路),AND gate 的 IN 输入端中一端接 A20 line 上,另一端接在 keyboard control 8042 上,而 AND gate 的 OUT 输出端接在 A20 line 上。只有两个 IN 端都为 1 时,OUT 端才为 1 |
A20 line 一直处于 1 状态(High 电平),而 8042 内的 A20 gate 一直处于 0(Low 电平),因此:必须使 Keyboard controller 8042 内的 A20 Gate 处于 high 时,A20 line 输出才有效。 A20 gate 也被称为 A20 MASK#。
SO,Keyboard Controller 8042 增加了一组命令去控制 A20 Gate 的开/关,给 8042 发送命令 0xDF 置 A20 gate 有效,给 8042 送命令 0xDD 置 A20 gate 无效。
现在的 system 中,南桥芯片的 A20 MASK# 缺省都是 MASK 状态,即:A20 gate 缺省都是开的。
5 打开 A20 gate
在 OS 的 boot 阶段一般都要做打开 A20 gate 操作,虽然现在 A20 gate 缺省为开的。
打开 A20 gate 的方法最原始的是给 keyboard controller 8042 发送 A20 gate enable 命令字,就是上面所说的 0xDF 命令。
下面是我在 mouseOS 操作系统项目中 keyboard 驱动里的一小段代码(编译器是 nasm):
;-----------------------------------
|
这个代码实现很简单,向 0x64(I8402 write 端口)发送 0xDF 命令,这是控制 keyboard controller I8402 A20 gate 的其中一种方法。
另一种方法是:通过写 I8402 的 output 端口,直接置 I8402 的 pin 21 (对应于 A20 gate line),同样来自 mouseOS 中的代码(nasm 编译器)
;----------------------------------- ;-----------------------------------
;------------------------------------------------------
;----------------------------------------- ; 2: read byte from 8408 (0x60) ; 3: send write_output_port command to 8402 (0x64) ; 4: set A20 gate line ; 5: send to data port (0x60) |
在 __enable_a20() 代码中,首先向 8402 发送 read output port 命令,然后从 0x60(8408 的数据端口)读出 output port 里的数据,将其它的 bit1 置为 1。再向 8402 发出 write output port 命令,将该数据写向 0x60(8408 的 data port)。
上面的方法都是基于 keyboard controller 来实现的,这是比较正统的实现方法。一个新的 fast A20 gate 方法,但是这种方法不是通用的方法。在一些系统可能得不到正确的结果
;---------------------------------------- |
6 打开 A20 gate 的必要性
打开 A20 gate 是为了在 80286/286+ 以后的 processor 上使用 protected mode 来访问完全的 24/24+ 位地址空间,如:在 32 位 protected mode 下,在不打开 A20 gate 的情况下,Bit20 为 0,导致 Bit20 留下一个空位。
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
0
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
1
|
转载自:http://www.mouseos.com/arch/002.html
所有权限 mik 所有,转载请注明出处!