【问题标题】:Segment size in x86 real modex86 实模式下的段大小
【发布时间】:2013-07-21 02:47:09
【问题描述】:

我对实模式下段的大小有一个疑问,因为它们不能超过 64K,但可以是less than that. 我的问题是这些段大小和基地址是如何初始化的?就像 GDT 和 LDT 处于保护模式一样。Real mode segments can also overlapped,disjoint or adjacent.像 BIOS 有一些保留区域用于特定的东西,如引导代码、视频缓冲区等,汇编程序需要做这样的事情吗?

【问题讨论】:

标签: x86 memory-segmentation real-mode


【解决方案1】:

在实模式下,分段地址被硬连线到内存中。要获取物理地址,您可以使用以下等式:

physical address = segment * 16 + offset

段地址和偏移地址都是 16 位的。通过使用这个公式,您可以创建一个 20 位地址并毫无问题地访问低 640kB 的 RAM。

没有表可以保存某些段所在的位置。 问题是您必须同时设置段寄存器和偏移寄存器才能访问任何地址。因此,您可以通过一个简单的循环访问最多 64k 的 RAM 字节,该循环只增加偏移寄存器,这使得对较大缓冲区的内存访问不如平面模型中舒适。

【讨论】:

  • 感谢 nio 的回答。那么决定段的基地址和段的大小是汇编程序员的工作吗?如果是这种情况,段可以重叠,而其他重叠的段可以被其他程序使用,从而破坏数据?假设两个 MSDOS 程序正在运行,如果没有保护,将如何分配内存?
  • 我不确定 DOS 中的内存管理是如何工作的,但这里有一些内存地址表:webpages.charter.net/danrollins/techhelp/0094.HTM 如果你正在制作一个 DOS 程序,你必须小心不要覆盖其他一些 .com驱动程序或 TSR 例程。通常同时运行一个 DOS 程序。
  • @nio:如果将对象排列为段落对齐,则只需加载段寄存器即可访问存储在对象内已知偏移量处的内容。我不知道有任何编译语言可以利用这一点,但这是汇编代码中的一个常见技巧。
【解决方案2】:

实模式下的段限制为 64k,即使在 386 或更高版本的 CPU 上,您也可以通过前缀使用 32 位地址大小。例如mov ax, [edx + ecx*4] 在实模式下仍然限制为 64 kiB 的偏移量。

如果超出此限制,则会引发#GP 异常。 (或者 #SS 如果段是 SS)。

16 位地址大小不能超过 64k 段限制,因为像 [bx + si] 这样的寻址模式以 16 位包装。因此,只有在实模式下使用0x67 地址大小前缀(在 386 中添加)的代码才会遇到段限制。 8086 不必检查限制,只需将Sreg << 4 添加到寻址模式的偏移量,使限制隐式为 64k。

在最高可能地址的 64k 内开始的段在 8086 上以 1MiB 环绕,如果 A20 被禁用,则在更高版本的 CPU 上环绕。否则,对于FFFF:FFFF seg:off = 0x10ffef 线性这样的地址,它们会超过 1MiB。见What are Segments and how can they be addressed in 8086 mode?


如果切换到保护模式并设置段寄存器,CPU 会在内部缓存段描述(基址 + 限制),即使切换回 16 位实模式也是如此。这种情况称为unreal mode

在 16 位模式下写入段寄存器只会将段基址设置为 value << 4 而不会更改限制,因此 unreal 模式对于 CS 以外的段来说有些持久。 CS:EIP 是特殊的,特别是如果您需要避免在从中断或其他情况返回时将 EIP 截断为 16 位。请参阅该 osdev wiki 链接。

push/pop/call/ret 根据当前堆栈段描述符中的B 标志使用SS:ESPSS:SP;地址大小前缀仅影响 push word [eax]push word [si] 之类的内容。

在实模式下将值写入段寄存器时会忽略 GDT / LDT。该值直接用于设置缓存段基数,根本不作为选择器。

(每个段都是独立的;虚幻模式不是像受保护与真实那样的实际模式;CPU 处于实模式。例如,写入 FS 寄存器会使该段恢复正常的实模式行为,但是不会改变其他。它只是在实模式下使用具有更大限制的缓存段描述符的名称,因此您可以将 32 位地址大小用于更大的平面地址空间。通常使用 base=0 和 limit=4G )

AFAIK,在实模式下无法查询段的内部限制值。 lsl 直接从内存中 GDT / LDT 中的描述符加载段限制值,而不是从内部值(所以这不是您想要的),并且无论如何它在实模式下都不可用。

有关有意或无意将片段脱离虚幻模式的更多详细信息,请参阅此答案上的 cmets。

286 和 386 CPU 支持 a LOADALL instruction,这可以从实模式设置段限制,但后来的 CPU 没有它。评论者说,SMM(系统管理模式)或许能够在现代 x86 上做类似的事情。

【讨论】:

  • 不太正确。如果您处于“虚幻模式”并且您修改了段寄存器,则描述符缓存基数将相应更改,但描述符缓存限制将保持不变。虚幻模式应保持原位,直到您下次切换到保护模式并更改相关段寄存器的段限制和基址。
  • 在实模式(包括虚模式)下还有另一种机制可以更改它们,即通过 LOADALL 指令,但该指令在大多数处理器上不可用。 LOADALL 指令在 Intel 386 和 286 上很有用,因为您可以在根本不切换到保护模式的情况下有效地获得虚幻模式。Ona 286 是一个好处,因为从保护模式切换回实模式需要很高的性能成本。跨度>
  • 并且记录在案,在 80 年代末 90 年代初有一些不寻常的 BIOS)在使用某些 BIOS 中断(驱动器访问等)时悄悄地切换到保护模式(可能重置虚幻模式)。
  • @HadiBrais :可能不止这些。在早期的 386(带有某些 BIOS)上调用 Int 6h(无效的操作码)是为了在没有功能齐全的 286 LOADALL 指令的情况下模拟 LOADALL。这后来在某些系统上通过 SMM 进行了模拟。
  • @HadiBrais 关于 SMM 和 Int 6h,需要阅读 RSM。它具有类似于 LOADALL 的执行状态,并且 SMM 可以在返回之前的 CPU 模式之前修改状态,其效果是模拟大部分 LOADALL 而无需将 int 6h 更改为保护模式(以设置虚幻模式):asm.inightmare.org/opcodelst/index.php?op=RSM
猜你喜欢
  • 2011-06-28
  • 1970-01-01
  • 2016-08-15
  • 1970-01-01
  • 2018-09-25
  • 1970-01-01
  • 2013-11-22
  • 2019-11-17
相关资源
最近更新 更多