x86实模式中的段大小

时间:2013-07-22 11:24:48

标签: x86 memory-segmentation real-mode

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

2 个答案:

答案 0 :(得分:2)

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

physical address = segment * 16 + offset

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

没有表格可以保存某个细分受众群所在的位置。 问题是您必须设置段寄存器和偏移寄存器才能访问任何地址。所以你可以通过一个简单的循环来访问最多64k的RAM字节,这个循环只会增加偏移量寄存器,这使得对较大缓冲区的存储器访问不如平面模型那么舒适。

答案 1 :(得分:1)

实模式中的段限制为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上。否则,它们会延伸超过1MiB,以获得类似FFFF:FFFF seg:off = 0x10ffef线性的地址。见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链接。

根据当前pop标记,

push / call / ret / SS:ESP使用SS:SPB堆栈段描述符;地址大小前缀仅影响push word [eax]push word [si]等内容。

在实模式下向段寄存器写入值时,将忽略GDT / LDT。该值直接用于设置缓存的段基础,而不是用作选择器。

(每个段是分开的;虚幻模式不是实际模式,如受保护与真实; CPU处于实模式。例如,写入FS寄存器会使该段恢复正常的实模式行为但是并没有改变其他的。它只是一个名称,在实模式下具有较大限制的缓存段描述符,所以你可以使用32位地址大小来获得更大的平面地址空间。 base = 0且limit = 4G)

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

有关有意或无意地从虚幻模式中删除片段的更多详细信息,请参阅此答案的评论。

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