超级用户上这个问题的最高答案给出了一个让我满意的解释,为什么重置向量不在地址 0 (后来,我意识到这不是为什么不能放置 RAM 的末尾在 0xFFFFFFFF 然后向下增长)。但是,0xFFFFFFF0 是这样一个奇怪的地址。为什么 x86 在 32 位地址空间顶部之前的 16 个字节处开始执行?这 16 个字节是否用于特殊用途?
x86 重置向量位于 0xFFFFFFF0 而不是 0xFFFFFFFF 的原因
x86 指令通常占用超过一个字节,一个合理的复位例程几乎肯定会指向多条指令。
如果复位向量指向 0xFFFF_FFFF,那么只有单字节指令适合该内存映射;因此,几乎任何有用的复位功能都需要跨越 0xFFFF_FFFF/0xFFFF_0000 线性地址边界的指令(因为代码段设置为基址 0xFFFF_0000)
通过将复位代码放置在此地址,可以适应一些指令(包括跳转),而不需要 0xFFFF_0000 处的有效内存。
它在内存顶部以下 16 个字节的原因来自于在复位期间如何加载原始 8086 CPU 寄存器。甚至这可能与旧 8085 CPU 的兼容性有关。而这种兼容性一直延续到后来的芯片,比如80286、80386等。
当然,他们可以选择任何值,但是由于中断向量固定在打算有 RAM 的内存区域的底部,因此程序 ROM 打算放在内存区域的顶部。
虽然可以选择存储器顶部的任何地址,但尽可能靠近最后的存储器地址是有意义的,这样它就不会规定 ROM 区域必须有多大,并且您可以使用尽可能小的 ROM你可以。
而且它也不能太靠近最后一个内存地址,以便为指令提供足够的空间,至少可以跳转到其他地方执行代码。短跳转操作码是两个字节,近跳转是三个字节,远跳转需要 5 个字节,因此为区域保留至少 5 个字节是有意义的。
由于 8086 有一个 20 位(或 1 兆字节)的地址空间,设计人员选择它使用分段内存方法,其中您有一个 16 位段和 16 位偏移量来指向一个线性 20 位地址。这意味着每个段寄存器可以选择一个基地址为 16 位偏移,基地址粒度为 16 字节。基本上,16 位 8085 的寻址只是通过添加段寄存器来扩展。
因此,在硬件复位期间,程序计数器 (IP) 寄存器设置为 0x0000,就像在 8085 CPU 上一样。为了到达内存的末尾,代码段 (CS) 寄存器设置为 0xFFFF。这使得 CPU 从内存末尾的 16 个字节开始。
有许多有效的 CS:IP 组合加起来为 0xFFFF0 的线性地址,但这很可能是最简单的方法,因为寄存器中的所有位都加载相同的值,IP 为零位,CS使用一位,因此无需将特殊的位组合加载到任一寄存器。
然而,后来的 CPU 已经在一定程度上破坏了这种兼容性,但在某种程度上它并不重要。
例如,80286 用 0xFFF0 加载 IP 寄存器。并且由于它使用 24 位内存总线访问 16 MB 的内存,而且 ROM 必须仍然在 16 MB 可寻址内存的末尾,所以 CS 寄存器值被重置为 0xF000,这样实模式 CS:IP 指向0xFFFF0,并将CS选择器基址设置为0xFF0000的基地址设置物理内存基址,使得物理地址为0xFFFFF0。这样,如果您只是将旧的 8086 系统重新设计为具有 80286,则可以使其兼容以启动原始 ROM。
当它扩展到 32 位 80386 时,它还会将 32 位 IP 寄存器加载为 0x0000FFF0,将 CS 寄存器加载为 0xF000,并将 CS 选择器基址加载为 0xFFFF0000,因此物理地址为 0xFFFFFFF0。因此,如果将 80286 系统重新设计为采用 80386SX CPU,则可以使其兼容以启动原始 ROM。