`

(第三章 4)A20地址线

阅读更多

     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 gateIN 输入端中一端接 A20 line 上,另一端接在 keyboard control 8042 上,而 AND gate 的 OUT 输出端接在 A20 line 上。只有两个 IN 端都为 1 时,OUT 端才为 1

A20 Gate

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):

;-----------------------------------
; macro: WAIT_STATUS_FOR_WRITE
;-----------------------------------
%macro WAIT_STATUS_FOR_WRITE 0
%%wait_status_for_write_loop:
        IO_DELAY
        in al, I8402_STATUS_PORT
        bt rax, 1
        jc %%wait_status_for_write_loop
%endmacro


;-------------------------------------------------------
; macro: WRITE_BYTE_TO_I8402
; description:
;               send byte to keyboard contrroler (I8402)
;-------------------------------------------------------
%macro WRITE_BYTE_TO_I8402 1
        WAIT_STATUS_FOR_WRITE
        mov al, %1
        out I8402_COMMAND_PORT, al
%endmacro

 


;-----------------------------------------
; keyboard_enable_a20()
;-----------------------------------------
__keyboard_enable_a20:
        WRITE_BYTE_TO_I8402 I8402_ENABLE_A20_CMD       ; send 0xDF to I8402
        ret

这个代码实现很简单,向 0x64(I8402 write 端口)发送 0xDF 命令,这是控制 keyboard controller I8402 A20 gate 的其中一种方法。

另一种方法是:通过写 I8402 的 output 端口,直接置 I8402 的 pin 21 (对应于 A20 gate line),同样来自 mouseOS 中的代码(nasm 编译器)

;-----------------------------------
; macro: WAIT_STATUS_FOR_WRITE
;-----------------------------------
%macro WAIT_STATUS_FOR_WRITE 0
%%wait_status_for_write_loop:
        IO_DELAY
        in al, I8402_STATUS_PORT
        bt rax, 1
        jc %%wait_status_for_write_loop
%endmacro

;-----------------------------------
; macro: WAIT_STATUS_FOR_READ
;-----------------------------------
%macro WAIT_STATUS_FOR_READ 0
%%wait_status_for_read_loop:
        IO_DELAY
        in al, I8402_STATUS_PORT
        bt rax, 0
        jnc %%wait_status_for_read_loop
%endmacro


;-------------------------------------------------------
; macro: WRITE_BYTE_TO_I8402
; description:
;               send byte to keyboard contrroler (I8402)
;-------------------------------------------------------
%macro WRITE_BYTE_TO_I8402 1
        WAIT_STATUS_FOR_WRITE
        mov al, %1
        out I8402_COMMAND_PORT, al
%endmacro


;------------------------------------------------------
; macro: WRITE_BYTE_TO_I8408
; description:
;               send byte to keyboard encoder (I8408)
;------------------------------------------------------
%macro WRITE_BYTE_TO_I8408 1
        WAIT_STATUS_FOR_WRITE
        mov al, %1
        out I8408_COMMAND_PORT, al
%endmacro

;------------------------------------------------------
; macro: READ_BYTE_FROM_I8408
; description:
;               read byte from keyboard encoder (I8408)
;------------------------------------------------------
%macro READ_BYTE_FROM_I8408 0
        WAIT_STATUS_FOR_READ
        in I8408_DATA_PORT
%endmacro

 

;-----------------------------------------
; __enable_a20() with 0xD0/0xD1
;-----------------------------------------
__enable_a20:
; 1: send read_output_port command to 8402 (0x64)
        WRITE_BYTE_TO_I8402 I8402_READ_OUTPUT_CMD

; 2: read byte from 8408 (0x60)
        READ_BYTE_FROM_I8408
        mov ah, al

; 3: send write_output_port command to 8402 (0x64)
        WRITE_BYTE_TO_I8402 I8402_WRITE_OUTPUT_CMD

; 4: set A20 gate line
        mov al, ah
        or al, 2

; 5: send to data port (0x60)
        WRITE_BYTE_TO_I8408 al
        ret

在 __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 方法,但是这种方法不是通用的方法。在一些系统可能得不到正确的结果

;----------------------------------------
; macro: A20_ENABLE
; descriptor:
;            Fast A20 gate enable
;----------------------------------------
%macro A20_ENABLE 0
        in al, SYSTEM_CONTROL_PORT          ; port - 0x92
        or al, 0x02
        out SYSTEM_CONTROL_PORT, al       
%endmacro

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 所有,转载请注明出处!

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics