我正在学习x86保护模式,刚刚了解了GDT。之前我学过,在进入保护模式(即:使用所有32位地址线)之前,必须启用A20门。所以,启用A20的代码必须在16位中,对吗? 最近当我浏览wiki.osdev网站时,发现启用A20的代码是用x86汇编语言编写的。X86汇编产生的32位操作码不能在16位模式下加载,对吗? 如有可能,请解释一下。 谢谢。
8086寻址模型包括一个16位段和一个16位偏移量,结合起来表示为segment * 16 + offset。 最小地址是000000h,最大地址是10ffefh。 虽然后者实际上是21位值,但CPU只有20位地址总线,因此可访问的最大地址是0fffffh1。超过0fffffh的地址将被简单地包装成2,因此10ffefh是0ffefh的别名。 一些程序开始依赖这一点。当80286推出时,它有24位地址总线。 像10ffefh这样的地址不再简单地回绕了。 在当时,模拟旧行为需要太多的晶体管(10ffefh无法使用AND屏蔽),因此引入了A20掩码。顾名思义,地址线20即第21位与8055/8042芯片中特定寄存器的特定位进行了AND运算。 BIOS会在启动时清除该位,从而强制第21位为零,以模拟旧的行为。如果不启用A20,则每个物理地址的第21位始终为零。 在保护模式下,可以使用平面模型启用A20,这是最接近“32位模式”的东西,但需要小心地放置代码位置。x86汇编语言可通过告诉汇编器目标大小而同样用于生成16位或32位代码。 1例如,给出段0f000h和偏移0ffffh。 2第21位被简单丢弃。3 简单来说,如果你正在编写16或32位代码。
问题在于如果A20门关闭,第20位地址位将被设置为零。例如,如果您想访问地址0x000100ab,则实际上访问的是0x000000ab,或者如果您想访问0x06570021,则实际上访问的是0x0656021,无论您是处于保护模式还是实模式。原因是在实模式下,您可以使用段:有效地址组合FFFF:00bb生成占用超过20位的物理地址,例如0x001000ab,但8086会将其截断为0x000ab,而80286或更新版本将在A20门打开时访问0x001000ab,这对于依赖该“特性”进行包装的程序(如某些BIOS代码)非常不利。当然,关闭A20门会完全干扰您在保护模式下超过20位地址的操作,这就是为什么在切换到保护模式时必须确保它已打开的原因。