实模式下的段限制为 64K,即使在 386 或更高版本的 CPU 上,您可以通过前缀使用 32 位地址大小. e.g. mov ax, [edx + ecx*4]
在实模式下仍然限制为 64 KiB 的偏移量。
如果超过此限制,则会在 286+ 上引发 #GP 异常。 (或者#SS
如果该段是 SS)。
8086 没有 #SS 或 #GP 例外,它没有一般保护或其他保护,只是使用Sreg << 4
添加到偏移量以形成线性地址。
16 位地址大小可以通过字或更宽的访问超过 64K 段限制seg:FFFF
。在8086上,高字节来自seg:0000
(在计算第二个内存总线事务的新线性地址之前,将逻辑地址中的偏移量包装起来,而不是访问该段的 64K 线性范围之外)。
在 286 及之后,#GP
or #SS
在这种情况下也适用于数据和指令。https://www.os2museum.com/wp/does-eip-wrap-around-in-16-bit-segments/ https://www.os2museum.com/wp/does-eip-wrap-around-in-16-bit-segments/
一般来说,寻址模式如[bx + si + 1]
以 16 位换行。 (和push word
SP=0 则换行至 SP=FFFEh,只要堆栈对齐就没有问题)。所以只有使用代码0x67
地址大小前缀(在 386 中添加)用于寻址模式,例如[eax]
可以超出实模式下的段限制,但段末尾的字或更宽的访问除外。
在 8086 上,在最高可能地址的 64K 范围内开始的段在 1MiB 处环绕,如果禁用 A20,则在更高版本的 CPU 上。否则,它们会扩展超过 1MiB 的地址,例如FFFF:FFFF
段:关闭=0x10ffef
线性。看什么是段以及如何在 8086 模式下寻址它们? https://stackoverflow.com/questions/42861524/what-are-segments-and-how-can-they-be-addressed-in-8086-mode/42864071#42864071
虚幻模式:386实模式的平面内存模型
如果切换到保护模式并设置段寄存器,CPU 会在内部缓存段描述(基址 + 限制),甚至切换回 16 位实模式。这种情况称为虚幻模式 https://wiki.osdev.org/Unreal_Mode.
在 16 位模式下写入段寄存器仅将段基址设置为value << 4
不改变限制,因此虚幻模式对于CS以外的细分市场来说有些持久。 CS:EIP 很特殊,特别是当您需要避免在从中断或其他情况返回时将 EIP 截断为 16 位时。请参阅前面链接的 osdev wiki。
push http://felixcloutier.com/x86/PUSH.html/pop
/call
/ret
use SS:ESP
or SS:SP
根据B
当前堆栈段描述符中的标志;地址大小前缀仅影响诸如push word [eax]
vs. push word [si]
.
当您在实模式下将值写入段寄存器时,GDT / LDT 将被忽略。该值直接用于设置缓存的段基址,根本不作为选择器。
(每个段都是独立的;虚幻模式不是像受保护模式与实模式那样的实际模式;CPU 处于实模式。例如,写入 FS 寄存器,会将该段恢复到正常的实模式行为(除了其限制),但不会改变其他的。它只是处于实模式下的名称,具有更大限制的缓存段描述符,因此您可以使用 32 位地址大小来获得更大的平面地址空间。通常使用 base=0 和 limit= 4G)
AFAIK,没有办法在实模式下查询段的内部限制值。lsl http://felixcloutier.com/x86/LSL.html直接从内存中的 GDT / LDT 中的描述符加载段限制值,而不是从内部值加载(所以这不是您想要的),并且无论如何它在实模式下不可用。
有关有意或无意地将片段从虚幻模式中取出的更多详细信息,请参阅对此答案的评论。
支持 286 和 386 CPUa LOADALL操作说明 https://en.wikipedia.org/wiki/LOADALL它可以设置实模式下的段限制,但后来的 CPU 没有它。评论者表示,SMM(系统管理模式)也许能够在现代 x86 上做类似的事情。