为什么x86的bootloader首先使用16位代码?

时间:2014-10-24 00:15:49

标签: linux assembly x86 kernel bootloader

我读了一些x86的bootloader代码。这些引导加载程序的第一部分通常使用gas编写。他们使用" .code16"告诉气体生成16位代码。

但是,我发现32位代码在REAL MODE中是可执行的。他们为什么使用16位代码?是" .code16"必要?

3 个答案:

答案 0 :(得分:3)

您可以使用32位代码,但仍受实模式分段和BIOS服务的约束。然而,.code16是必需的,因为cpu以16位模式运行,因此要使用32位指令,您需要使用操作数和地址大小覆盖前缀。该指令指示汇编程序为您添加这些。

如果您在没有mov edx, eax的情况下编写.code16,汇编程序将生成机器代码89 C2。但是,当cpu处于16位模式时,它会将其解释为mov dx, ax。如果您真的想要在32位上运行,则需要66前缀,即66 89 C2。使用.code16汇编程序就可以做到这一点。 66前缀基本上切换到"其他"模式,即在16位模式下它将使用32位,反之亦然。这也意味着如果您在32位代码中编写mov dx, ax,您将获得66 89 C2,当以16位模式运行时,mov edx, eax.code16。因此,您需要告诉汇编程序您的代码要运行的模式。

TL; DR:{{1}}确保汇编程序生成的代码在16位模式下由cpu执行时将执行您的预期。

答案 1 :(得分:1)

英特尔:

指令前缀可用于覆盖代码段的默认操作数大小和地址大小。这些前缀可用于实地址模式以及受保护模式和虚拟8086模式。操作数大小或地址大小前缀仅更改指令持续时间的大小。

以下两个指令前缀允许在一个段内混合32位和16位操作: •操作数大小前缀(66H) •地址大小前缀(67H)

这些前缀反转了代码段描述符中D标志选择的默认大小。例如,处理器可以通过以下四种方式解释(MOV mem,reg)指令: •在32位代码段中: - 使用32位有效地址从32位寄存器移动32位到存储器。 - 如果在操作数大小前缀之前,则使用32位有效地址将16位寄存器中的16位移至存储器。 - 如果前面有地址大小的前缀,则使用16位有效地址将32位从32位寄存器移到存储器。 - 如果前面有地址大小前缀和操作数大小前缀,则使用16位有效地址将16位寄存器中的16位移入存储器。

•在16位代码段中: - 使用16位有效地址从16位寄存器移动16位到存储器。 - 如果在操作数大小前缀之前,则使用16位有效地址将32位从32位寄存器移至存储器。 - 如果前面有地址大小前缀,则使用32位有效地址将16位寄存器中的16位移入存储器。 - 如果前面有地址大小前缀和操作数大小前缀,则使用32位有效地址将32位从32位寄存器移到存储器。

前面的示例表明,无论指令是在16位还是32位段,任何指令都可以生成操作数大小和地址大小的任意组合。代码段的16位或32位默认值的选择通常基于以下标准: •性能 - 尽可能使用32位代码段。它们的运行速度比P6系列处理器上的16位代码段快得多,而且在早期的IA-32处理器上运行得更快。 •代码段将运行的操作系统 - 如果操作系统是16位操作系统,则可能不支持32位程序模块。 •操作模式 - 如果代码段设计为在实地址模式,虚拟8086模式或SMM中运行,则它必须是16位代码段。 •向后兼容早期的IA-32处理器 - 如果代码段必须能够在Intel 8086或Intel 286处理器上运行,则它必须是16位代码段。

代码段描述符中的D标志确定代码段指令的默认操作数大小和地址大小。 (在实地址模式和虚拟8086模式下,不使用段描述符,默认为16位。)设置了D标志的代码段是32位段;其D标志清除的代码段是16位段。

可执行代码段。该标志称为D标志,它指示段中指令引用的有效地址和操作数的默认长度。如果设置了标志,则假定32位地址和32位或8位操作数;如果清楚,则假设16位地址和16位或8位操作数。 指令前缀66H可用于选择默认值以外的操作数大小,可以使用前缀67H选择默认值以外的地址大小。

32位操作数前缀可用于实地址模式程序,以执行32位形式的指令。此前缀还允许实地址模式程序使用处理器的32位通用寄存器。 32位地址前缀可用于实地址模式程序,允许32位偏移。

从Intel386处理器开始的IA-32处理器可以使用地址覆盖前缀生成32位偏移量;但是,在实地址模式下,32位偏移的值不能超过FFFFH而不会导致异常。

汇编程序用法: 如果定义了将以实地址模式运行的代码段,则必须将其设置为USE 16属性。如果在此代码段的指令中使用32位操作数(例如,MOV EAX,EBX),则汇编器会自动为强制处理器执行32位操作的指令生成操作数前缀,即使其默认代码段属性是16位。

32位操作数前缀允许实地址模式程序使用32位通用寄存器(EAX,EBX,ECX,EDX,ESP,EBP,ESI和EDI)。

在段寄存器和32位通用之间以32位模式移动数据时 注册时,Pentium Pro处理器不需要使用16位操作数大小的前缀; 但是,有些汇编程序确实需要这个前缀。处理器假设16个最不重要 通用寄存器的位是目标或源操作数。搬家的时候 从段选择器到32位寄存器的值,处理器填充两个高位字节 带零的寄存器。

答案 2 :(得分:0)

当制造出第一批英特尔处理器时,它们都使用了16位代码,未使用32位代码。当386处理器生成32位时,英特尔指定它的默认启动状态为16位,以便与旧处理器向后兼容。然后,OS或Bootloader开发人员可以使用16位指令来访问受保护(32位)模式。英特尔似乎喜欢它们的向后兼容性,迄今为止它已经停留在所有x86处理器上,因此它们可以保持与旧软件的兼容性。