查看“A20 Line”的源代码
←
A20 Line
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
A20地址线是任何内存访问的第21位 (从0开始计数到数字20) 物理表示。(译者注:A20 Address Line启用设置是一种为了兼容而保留的技术,一般使用需要设置1启用) 当IBM-AT(Intel 286)推出时,它能够访问多达16兆字节的内存(而不是8086的1兆字节)。 但为了保持与8086的兼容性,必须在AT中复制8086体系结构中的一个怪癖(内存地址折回-memory wraparound)。 为此,默认情况下,地址总线上的A20线被禁用。 这是因为8086只能访问1兆字节的内存, 但由于分段内存模型,它可以有效地寻址高达1M字节和64K字节(减去16字节)。 因为8086上有20个地址线 (A0到A19),所以1兆字节标记以上的任何地址都折回为零。 出于某种原因,一些目光短浅的程序员会编写实际使用这种折回行为的程序 (而不是在内存底部的正常位置直接寻址内存)。 因此,为了在新处理器上支持这些8086时代的程序,必须在IBM AT及其兼容性上模拟这种折回; 这最初是通过锁存器(latch)实现的,默认情况下,锁存器将A20线设置为零。(译者注:也就是保持和原来旧CPU的内存地址溢出后的归零折回一样的特性) 后来,486将逻辑添加到处理器中,并引入了A20M引脚来控制它。 对于操作系统开发人员 (或 [[Bootloader]] 开发人员),这意味着必须启用A20线路,以便可以访问所有内存。 这一开始只是一个简单的黑客技巧,但最后又加入更简单的方法来应对它, 导致编写肯定会启用它的代码变得更加困难,而编写肯定会禁用它的代码也变得更加困难。 == 键盘控制器 == A20线使能的传统方法是直接探测键盘控制器。 原因是英特尔的[["8042" PS/2 Controller|8042键盘控制器]]有一个备用引脚,他们决定通过该引脚控制A20线路。 考虑到它们(内存和键盘)的设计目的毫无关系,这在现在看来似乎是愚蠢的,但是当时计算机还没有那么标准化。 键盘控制器通常是[http://www.diakom.ru/el/elfirms/datashts/Smsc/42w11.pdf 8042]芯片。 通过对该芯片进行精确编程,你可以启用或禁用地址总线上的位#20。 当你的PC启动时,A20 gate通常被禁用,但是一些BIOS (和仿真器,如QEMU) 确实为你启用了它,一些高内存管理器 (HIMEM.SYS) 或引导加载器 ([[GRUB]]) 也会这么做。 ==测试A20线== 在使用下面介绍的任何方法启用A20之前,最好测试A20地址线是否已由[[BIOS]]启用。 这可以通过将位于地址0000:7DFE处的引导扇区标识符 (0xAA55) 与位于地址FFFF:7E0E处的值更高的1MIB进行比较来实现。 当两个值不同时,这意味着A20已启用,否则如果值相同,则必须排除看看这是不是偶然。 因此,需要更改引导扇区标识符,例如通过将其向左旋转8位,并再次与FFFF:7E0E处的16位字进行比较。 当它们仍然相同时,A20地址线是被禁用的,否则是被启用的。 下面的代码执行检查(不像上面描述的那样——更直接)。 <SOURCE lang=“ASM”> ; 以下代码是公共领域许可的 [bits 16] ; Function: check_a20 ; ; 目的:以完全独立的状态保存方式检查a20线路的状态。 ; 如果不需要完全的自包含(self-containment),可以根据需要对该函数进行修改 ; 删除开头的push和结尾的pop。 ; ; 如果a20线被禁用(内存出现折回),则返回: ax中的0 ; 如果启用了A20线路,则为AX中的1(内存不折回) check_a20: pushf push ds push es push di push si cli xor ax, ax ; ax = 0 mov es, ax not ax ; ax = 0xFFFF mov ds, ax mov di, 0x0500 mov si, 0x0510 mov al, byte [es:di] push ax mov al, byte [ds:si] push ax mov byte [es:di], 0x00 mov byte [ds:si], 0xFF cmp byte [es:di], 0xFF pop ax mov byte [ds:si], al pop ax mov byte [es:di], al mov ax, 0 je check_a20__exit mov ax, 1 check_a20__exit: pop si pop di pop es pop ds popf ret </source> === 从保护模式测试A20线路 === 在受保护模式下,测试A20更容易,因为你可以使用任何奇数兆字节地址访问A20的设置内存地址,并将其与偶数兆字节的邻居进行比较。 <source lang="asm"> [bits 32] ; 检查A20线路设置 ; 如果A20 gate被清除0,则返回给调用者。 ; 如果设置了A20行,则继续到A20_on运行。 ; 作者Elad Ashkcenazi is_A20_on?: pushad mov edi,0x112345 ;odd megabyte address. mov esi,0x012345 ;even megabyte address. mov [esi],esi ;确保两个地址都包含不同的值。 mov [edi],edi ;( 如果清除了A20线路设置为0,则两个指针将指向包含0x112345 (edi) 的地址0x012345) cmpsd ;比较地址以查看它们是否相同。 popad jne A20_on ;如果不等效,则设置A20行。 ret ;如果等效,A20线路已被清除为0。 A20_on: ;*此处是你的代码* </source> ==启用== 有几个使能A20的信号源,通常每一个输入被或运算在一起以形成A20使能信号。 这意味着使用一种方法 (如果芯片组支持) 就足以启用A20。 如果要禁用A20,可能必须禁用所有现有源。 始终通过如上所述测试线路来确保A20具有请求的状态。 === 键盘控制器 === 对于最初启用A20线路的方法,需要一些使用键盘控制器芯片(8042芯片)的硬件IO。 <source lang="c"> void init_A20(void) { uint8_t a; disable_ints(); kyb_wait_until_done(); kyb_send_command(0xAD); // disable keyboard kyb_wait_until_done(); a=kyb_get_data(); kyb_wait_until_done(); kyb_send_command(0xD1); // Write to output kyb_wait_until_done(); kyb_send_data(a|2); kyb_wait_until_done(); kyb_send_command(0xAE); // enable keyboard enable_ints(); } </source> or in [[assembly]] <source lang="asm"> ;; ;; NASM 32bit assembler ;; [bits 32] [section .text] enable_A20: cli call a20wait mov al,0xAD out 0x64,al call a20wait mov al,0xD0 out 0x64,al call a20wait2 in al,0x60 push eax call a20wait mov al,0xD1 out 0x64,al call a20wait pop eax or al,2 out 0x60,al call a20wait mov al,0xAE out 0x64,al call a20wait sti ret a20wait: in al,0x64 test al,2 jnz a20wait ret a20wait2: in al,0x64 test al,1 jz a20wait2 ret </source> ===Fast A20 Gate=== 在大多数以IBM PS/2开始的较新计算机上,芯片组有一个FAST A20选项,可以快速启用A20线路设置。 如果以这种方式启用A20,则不需要延迟循环或轮询,只需3条简单的指令。 <source lang="asm"> in al, 0x92 or al, 2 out 0x92, al </source> 正如在 [http://www.win.tue.nl/~ aeb/linux/kbd/A20.html 参见站点]中提到的,最好仅在必要时进行写入,并确保位0为0,因为它用于快速重置。 下面是一个例子: <source lang="asm"> in al, 0x92 test al, 2 jnz after or al, 2 and al, 0xFE out 0x92, al after: </source> 然而,并非所有地方都支持Fast A20方法,而且没有可靠的方法来判断它是否会对给定的系统产生一些影响。 更糟糕的是,在某些系统上,它实际上可能会做其他事情,例如清空屏幕,因此只有在 [[BIOS]] 报告FAST A20可用后才应使用它。 还需要为缺乏FAST A20支持的系统编写代码,因此不鼓励只依赖这种方法。 此外,在某些芯片组上,你可能需要在BIOS配置屏幕中启用Fast A20支持。 ===INT 15=== 另一种方法是使用BIOS。 <source lang="asm"> ;FASM use16 mov ax,2403h ;--- A20-Gate Support --- int 15h jb a20_ns ;INT 15h is not supported cmp ah,0 jnz a20_ns ;INT 15h is not supported mov ax,2402h ;--- A20-Gate Status --- int 15h jb a20_failed ;couldn't get status cmp ah,0 jnz a20_failed ;couldn't get status cmp al,1 jz a20_activated ;A20 is already activated mov ax,2401h ;--- A20-Gate Activate --- int 15h jb a20_failed ;couldn't activate the gate cmp ah,0 jnz a20_failed ;couldn't activate the gate a20_activated: ;go on </source> 如果有一个中断失败,你将不得不使用另一种方法。(见下文。) ===访问0xee=== 在某些系统上,读取IO端口 0xee启用A20,而写入则禁用A20。 (或者,有时,此操作仅在启用IO端口 0xee时发生。) 类似的情况也适用于IO端口 0xef和Reset(写入会导致重置)。 来自i386SL/i486SL文档: 以下端口仅在启用时可见,对这些端口的任何写入都会导致对应名称的操作。 <pre>Name of Register Address Default Value Where placed Size FAST CPU RESET EFh N/A 82360SL 8 FAST A20 GATE EEh N/A 82360SL 8 </pre>启用A20: <source lang="asm"> in al,0xee </source> 禁用A20: <source lang="asm"> out 0xee,al </source> '''注意''' 写入时AL包含什么并不重要,读取时AL未定义(端口0xee) === 推荐方法 === 因为有几种不同的方法可能被支持,也可能不被支持,而且其中一些方法会在某些计算机上引发问题; 推荐的方法是按“风险最小”的顺序尝试所有这些方法,直到其中一种方法起作用。 本质上: * 测试A20是否已启用——如果已启用,则根本不需要执行任何操作 * 尝试使用BIOS功能。 忽略返回状态。 * 测试A20是否启用(查看BIOS功能是否实际工作) * 尝试键盘控制器方法。 * 测试是否在循环中启用了A20并具有超时等待-(因为键盘控制器方法可能工作缓慢) * 最后尝试Fast A20方法 * 测试A20是否在超时循环中启用(因为FAST A20方法可能工作缓慢) * 如果以上都不起作用,请放弃 ==另见== ===外部链接=== *http://www.win.tue.nl/~aeb/linux/kbd/A20.html [[Category:X86]] [[de:A20-Gate]]
返回至“
A20 Line
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
变体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息