Linux_关于A20 Gate

本文详细解析了PC机内存结构中的A20 Gate原理及其控制方法,探讨了不同内存区域如常规内存、高端内存等的概念,并介绍了通过8042芯片和BIOS中断控制A20 Gate的具体实现。
本来想直接写一篇关于保护模式的文章,因为有一位读者不断地问我这个问题,随着问题的深入,在评论上回答这个问题实在是太困难了,动起笔来,发现涉及的事情太多,免不了又是长篇大论惹人烦,而且要写很长时间,不知道我能不能把它写完,所以干脆把一些问题分离出来写,或许还可以坚持写出来。

    在许多PC的CMOS设置里,都有一项叫做“A20 Enable“的设置,不知道大家是否就此设置困惑过,这个A20是什么呢?

    说A20则不得不说PC机的内存,我尽量用简短的语言说明白PC机内存的一些独有的术语,在我刚接触PC机的时候(1985年前后),如果你的机器有一块10M或者20M的硬盘,那已经是一台不错的机器,如果你的机器有256K的内存,那绝对是高配置,那时候还没有3.5"的软盘,都是使用5"的容量仅360K的软盘,所以那个时候,设计IBM PC的IBM公司非常自信地认为,1M内存是一个根本达不到的天文数字,我想由于这种思想的作怪(我猜的),IBM非常愚蠢地把PC机1M存储空间的最上面的384K用作了ROM和系统设备,这种设计给现在的PC机的内存结构埋下了麻烦的伏笔,如果当初IBM把PC机的1M存储空间的最下面的384K用作ROM和系统设备,可能现在就会少好多麻烦,这个后面说。

    写到这里的时候,我想起了许多事,大家是否知道我国的第一台电子计算机是什么型号?是什么样子?是1958年生产的103机,后来又有了全晶体管的电子计算机,很荣幸的是,在我工作过的单位里,曾经有过这么一台全晶体管的电子计算机,就是那种使用穿孔纸带输入计算机程序,占地好几百平方米,一旦开动需要几十人进行维护的家伙,我到这个单位的时候,这台机器早已不再运转的,80年代的时候,我有幸参加了这台机器的拆解工作,给我印象最深的是,我终于看到了当时在教科书里说的“磁鼓”(估计现在的教科书里也没有了),一种看上去像筛子一样的东西,那就是当年的存储器,用于在纸带上打孔进行程序输入的穿孔机我用过,样子很像老式的英文打字机。还有那种8英寸的软盘,估计现在也很少有人见过了。

    好了言归正传,大家都知道,8088/8086只有20位地址线,按理它的寻址空间是2^20,应该是1024KB,但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位地址线的物理寻址能力,你说这是不是有点麻烦。在早先,由于所有的机器都没有那么大的内存,加上地址线只有20位,所以当你用segment:offset的方式企图寻址100000h这个地址时,由于没有实际的第21位地址线,你实际寻址的内存是00000h的位置,如果你企图寻址100001h这个地址时,你实际得到的内容是地址00001h上的内容,所以这个事对实际使用几乎没有任何影响,但是后来就不行了,出现了80286,地址线达到了24位,使segment:offset寻址100000h--10ffefh这将近64K的存储器成为可能,为了保持向下兼容,于是出现了A20 Gate,这是后话,我们后面再细说。

    我们可能经常听到一些只有在PC机上才有的一些关于存储器的专有名词,包括:常规内存(Conventional Memory)、上位内存区(Upper Memory Area)、高端内存区(High Memory Area)和扩展内存(Extended Memory),我尽量把这几个东东说明白,这需要下面这张著名的图。

关于A20 Gate - whowin - DOS编程技术

    这张图很清楚地说明了问题,大家都知道,DOS下的“常规内存”只有640K,这640K就是从0--A0000H这段地址空间;所谓“上位内存区”,指的就是20位地址线所能寻址到的1M地址空间的上面384K空间,就是从A0001H--100000H这段地址空间,也就是我们说的用于ROM和系统设备的地址区域,这384K空间和常规内存的640K空间加起来就是20位地址线所能寻址的完整空间1024KB;由于80286和80386的出现使PC机的地址线从20位变成24位又变成32位,寻址能力极大地增加,1M以上的内存寻址空间,我们统称为“扩展内存”;这里面绝大部分内存区域只能在保护模式下才能寻址到,但有一部分既可以在保护模式下,也可以在实模式下寻址,这就是我们前面提到过的地址100000h--10ffefh之间的这块内存,为了表明其特殊性,我们把这块有趣的内存区叫做“高端内存”。

    前面我们提过由于IBM的愚蠢设计给PC机的内存结构埋下了麻烦的伏笔,现在我们来说说这个麻烦。我们都见过PC机上的内存条,但是由于上位内存区的存在,这个内存条上的地址居然不能连续,就是说,这个内存条上要有0--A0000H的地址空间,还要有100000h--最大内存容量的地址空间,中间的384K地址空间必须留出来给ROM用,在现如今一个芯片就好几兆的情况下,你说这个内存条应该怎么做,当然我相信一定是可以做出来的,但肯定很麻烦,如果当初IBM把这个“上位内存区”放在地址低端,就是0--6000h这一部分,岂不是这个麻烦就没有了?!

    但是,实际的内存条上地址都是连续的,并没有人把这段地址空间留出来给ROM使用,原因很简单,采用技术手段把这段地址空间空出来,比浪费这384K内存的成本还要高,所以在这个地址区域就出现了很奇怪的现象,ROM和RAM的地址重叠。 实际上,往往ROM并不能完全覆盖整个384K区域,这样就会有一些地址没有被ROM占用,那么这部分地址上的RAM仍然是可以使用的。实际上,和ROM重叠的这384K RAM一般也不会被浪费,说到这里,不得不说所谓的ROM Shadowing了,RAM和ROM的性能是有很大差异的,RAM的存取速度要远远大于ROM,而且RAM可以32位存取,ROM通常只能16位,所以目前的PC机对这块RAM和ROM重叠的区域的处理采用一种ROM Shadowing的技术方式,当机器加电后,先让ROM有效,RAM无效,然后读出ROM内容,再让ROM无效,RAM有效,把读出的ROM内容放到相同地址的RAM中,并把相应位置的RAM设定为只读,这样就把ROM搬到了RAM中,地址完全一样,只是性能比使用ROM要高些,这块RAM就好像ROM的Shadow一样。

    回到我们的主题A20 Gate,出现80286以后,为了保持和8086的兼容,PC机在设计上在第21条地址线(也就是A20)上做了一个开关,当这个开关打开时,这条地址线和其它地址线一样可以使用,当这个开关关闭时,第21条地址线(A20)恒为0,这个开关就叫做A20 Gate,很显然,在实模式下要访问高端内存区,这个开关必须打开,在保护模式下,由于使用32位地址线,如果A20恒等于0,那么系统只能访问奇数兆的内存,即只能访问0--1M、2-3M、4-5M......,这显然是不行的,所以在保护模式下,这个开关也必须打开。

    下面我们来看一下PC机是怎么实现A20 Gate的。

     早期的PC机,控制键盘有一个单独的单片机8042,现如今这个芯片已经给集成到了其它大片子中,但其功能和使用方法还是一样,当PC机刚刚出现A20 Gate的时候,估计实在找不到控制它的地方了,同时为这点小事也不值得增加芯片,于是工程师使用这个8042键盘控制器来控制A20 Gate,但A20 Gate真的和键盘一点关系也没有,我们先从软件的角度简单介绍一下8042这个芯片。

    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芯片更详细的资料,请参考该芯片的Data Sheet。

     如何打开和关闭A20 Gate。

    理论上讲,我们只要操作8042芯片的输出端口(64h)的bit 1,就可以控制A20 Gate,但实际上,当你准备向8042的输入缓冲区里写数据时,可能里面还有其它数据没有处理,所以,我们要首先禁止键盘操作,同时等待数据缓冲区中没有数据以后,才能真正地去操作8042打开或者关闭A20 Gate。打开A20 Gate的具体步骤大致如下:

    1.关闭中断;
    2.等待8042 Input buffer为空;
    3.发送禁止键盘操作命令;
    4.等待8042 Input buffer为空;
    5.发送读取8042 Output Port命令;
    6.等待8042 Output buffer有数据;
    7.读取8042 Output buffer,并保存得到的字节;
    8.等待8042 Input buffer为空;
    9.发送Write 8042 Output Port命令到8042 Input buffer;
    10.等待8042 Input buffer为空;
    11.将从8042 Output Port得到的字节的第2位置1(或清0),然后写入8042 Input buffer;
    12.等待,直到8042 Input buffer为空为止;
    13.发送允许键盘操作命令到8042 Input buffer;
    14. 打开中断。

    下面是完成打开A20 Gate的代码:

    A20Enable:
                cli                     ;1.关闭中断
                call WaitInbufEmpty    ;2.等待8042 Input buffer为空;
                mov al, 0adh
                mov dx, 64h
                out dx, al              ;3.发送禁止键盘操作命令
                call WaitInbufEmpty    ;4.等待8042 Input buffer为空;
                mov al, 0d0h
                mov dx, 64h
                out dx, al              ;5.发送读取8042 Output Port命令;
                call WaitOutbufFull     ;6.等待8042 Output buffer有数据;
                mov dx, 60h
                in al, dx               ;7.读取8042 Output buffer
                push ax                 ;保存读取的数据
                call WaitInbufEmpty     ;8.等待8042 Input buffer为空;
                mov al, 0d1h
                mov dx, 64h
                out dx, al              ;9.发送写 8042 Output Port命令
                call WaitInbufEmpty     ;10.等待8042 Input buffer为空
                pop ax
                or al, 02h              ;11.将从8042 Output Port得到的字节的bit 1置1
                mov dx, 60h
                out dx, al              ;写入Output Port
                call WaitInbufEmpty    ;12.等待8042 Input buffer为空
                mov al, 0aeh
                mov dx, 64h
                out dx, al              ;13.发送允许键盘操作命令
                sti                     ;开中断
                ret

    WaitInbufEmpty:
                mov dx, 64h
                in al, dx               ;读取Status Register
                test al, 02h
                jnz WaitInbufEmpty
                ret

WaitOutbufFull:
                mov dx, 64h
                in al, dx
                test al, 01             ;读取Status Register
                jz WaitOutbufFull
                ret

    后来,由于感觉使用8042控制A20运行太慢了(确实,那么长的代码,中间还要若干次的wait),所以后来又出现了所谓的Fast A20,实际上,现在的大多数机器都是Fast A20,Fast A20使用92h端口控制A20,同时BIOS里提供了一个软中断来控制A20:

    入口:ah=24h
          al=0    关闭A20
             1    打开A20
             2    读取A20状态
          int 15h

    返回:如果BIOS支持此功能,CF=0,否则CF=1
          CF=0时,AX返回当前A20状态,1=打开,0=关闭

    像8042中的Output Port中的定义一样,92h端口的bit 1控制着A20,为1时打开,为0时关闭,从92h中读一个byte可以看a20的当前状态,所以对92h的操作如下:

  • 读A20状态
    mov dx, 92h
    in al, dx
    如果al的bit 1为1表示a20打开,否则为0
  • 打开A20
    mov dx, 92h
    mov al, 02
    out dx, al
  • 关闭A20
    mov dx, 92h
    mov al, 0
    out dx, al

    特别要注意的是,大家从这篇文章的文字中可能也能感觉到,A20 Gate的设计本身就让人感觉很别扭,不是那么流畅,所以和A20有关的事情就难免也会有相同的感觉,很奇怪的是,上面介绍的三种方法并不是在每台机器上都适用,所以如果你要做一个商业软件其中要操作A20,那一定要三种方式联合使用才比较稳妥,否则会有意想不到的结果,LINUX公开的启动代码中就是这么做的

    在DOS下有时我们会在config.sys中写上一句:dos=high,这句就会把驻留的DOS放到高端内存区域去,怎么放的呢?关键点就是打开A20,然后把DOS从常规内存搬到100000h起始的区域去,并把在常规内存中占用的内存释放掉,当然说起来容易,实际做的时候还有很多细节要处理。

    说到DOS=HIGH,就不得不提醒大家另一件事,如果在config.sys中有dos=high这一句,那么恐怕92h的方法和BIOS的方法都会不完全灵验(至于操作8042的方法灵不灵我没有试),这是DOS做了手脚,因为DOS被放到了高端内存中,为了保证DOS能正常运行,它不允许你把A20给关掉,遇到这种情况不要惊慌,不是我写得不对,确实是DOS太狡猾了。

    还有最后一点要特别注意,92h的bit 0是给机器发复位信号的(8042 Output Port的bit 0也是),所以在向92h写数据时,千万不要让bit 0为1,否则机器会重新启动,如果你的应用程序需要重新启动机器,这也是方法之一,比jmp 0ffffh:0来的还要干脆。

    在其它我们介绍保护模式的文章中,我们会用到上面提到的打开A20的方法,届时可能就不会做更多的解释了。 

    另外,涉及操作A20的资料其实很少,有些资料我手里也很缺乏,比如92h除bit 0和bit 1以外的定义是什么我至今也不知道,包括本文中的一些内容也是摸索所得,并没有资料予以佐证,所以万一有不对的地方,不要见怪,并恳请指出。

### A20 Gate in Computer Architecture The A20 line plays a crucial role in addressing memory beyond 1 MiB on IBM PCs and compatible systems. Initially, the Intel 8086 processor used only 20 address lines (A0-A19), allowing it to access up to 1 MiB of physical RAM. However, when the IBM PC/AT was introduced with an upgraded 80286 CPU using 24 address lines (A0-A23)[^1], this allowed for accessing up to 16 MiB of memory. To maintain compatibility between older software designed for earlier CPUs like the 8086 and newer processors that could handle more than 1 MiB of RAM, manufacturers implemented what became known as the "A20 gate." This mechanism controls whether the system uses the 21st address line (A20). By default, the A20 line remains disabled through hardware control via the keyboard controller chip during boot-up; thus, emulating behavior similar to early 8086-based machines where any attempt at higher addresses would wrap around back into lower regions due to fewer available bits being utilized by those initial designs. When operating systems or applications require full utilization of extended memory above 1 MB—or wish to switch their environment from real mode into protected modes—they must enable the A20 signal before performing these operations. Enabling involves sending specific commands directly to the keyboard controller which manages switching states of the A20 pin within its circuitry design. Once enabled, programs gain unrestricted linear access across all installed DRAM without encountering wrapping issues present under legacy conditions imposed upon them prior to enabling this feature. ```assembly mov al, 0xd1 ; Command byte to write output port out 0x64, al ; Send command to keyboard controller's status register xor al, al ; Data byte value zero out 0x60, al ; Write data byte to disable A20 gate ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值