我正在尝试了解GAS的.code16行为。 从手册中可以看出,在16位部分中,对于32位操作数或指令,将为指令编码生成66H操作数覆盖前缀。这是否意味着
.code16
movw %eax, %ebx
在这种模式下合法吗?那么代码不能在16位处理器上运行?
答案 0 :(得分:11)
这些是80386+的法律说明。 从80386开始,我们可以使用operandsize-和addresssize- override前缀。 这些前缀可以与16位地址模式和32位地址模式结合使用。另外,它可以与真实地址模式一起使用,也可以与受保护模式和虚拟86模式一起使用。这些前缀反转了代码段中一条指令的默认操作数大小和/或地址大小。默认的operandsize和addresssize由代码段描述符中的D标志指定(或者如果没有GDT / LDT,那么我们将在完成BIOS的POST过程后成为16位地址模式。)
如果我们想使用32位操作数和/或32位地址,使用16位地址模式我们必须添加这些前缀。如果没有这些前缀,我们只能在16位地址模式中使用16位地址/操作数。
如果我们想使用32位操作数和/或32位地址,使用32位地址模式,我们必须从代码中省略这些前缀。如果我们将这些前缀添加到代码中,那么我们可以在32位地址模式中使用16位地址/操作数。
块引用 英特尔:
指令前缀可用于覆盖代码段的默认操作数大小和地址大小。这些前缀可用于实地址模式以及受保护模式和虚拟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位寄存器的值,处理器填充两个高位字节 带零的寄存器。
块引用 AMD:
3.3.2。 32位与16位地址和操作数大小 处理器可配置为32位或16位地址和操作数大小。有32位 地址和操作数大小,最大线性地址或段偏移量为FFFFFFFFH (2 ^ 32-1),并且操作数大小通常是8位或32位。使用16位地址和操作数大小, 最大线性地址或段偏移量是FFFFH(2 ^ 16-1),并且操作数大小通常是 8位或16位。 使用32位寻址时,逻辑地址(或远指针)由16位段组成 选择器和32位偏移;当使用16位寻址时,它由16位段选择器组成 和一个16位的偏移量。 指令前缀允许临时覆盖默认地址和/或操作数大小 在一个程序中。 在受保护模式下操作时,当前正在执行的代码的段描述符 segment定义默认地址和操作数大小。段描述符是系统数据 应用程序代码通常不可见的结构。汇编程序指令允许默认值 为程序选择的寻址和操作数大小。然后设置汇编器和其他工具 适当地增加代码段的段描述符。 在实地址模式下操作时,默认寻址和操作数大小为16位。一个 地址大小覆盖可用于实地址模式以启用32位寻址;然而 最大允许32位线性地址仍为000FFFFFH(2 ^ 20-1)。
3.6。操作尺寸和地址尺寸属性 当处理器在保护模式下执行时,每个代码段都有一个默认的操作数大小 attribute和address-size属性。使用D(默认大小)选择这些属性 代码段的段描述符中的标志(请参阅第3章,保护模式内存 管理,参见英特尔架构软件开发人员手册,第3卷)。当D 如果设置了标志,则选择32位操作数大小和地址大小属性;当国旗清晰时, 选择16位大小属性。当处理器在实地址模式下执行时, virtual-8086模式或SMM(系统管理模式),默认的操作数大小和地址大小属性始终为16 位。 operand-size属性选择指令操作的操作数的大小。当。。。的时候 16位操作数大小属性有效,操作数通常可以是8位或16位,和 当32位操作数大小属性有效时,操作数通常可以是8位或32位。 address-size属性选择用于寻址内存的地址大小:16位或32位 位。当16位地址大小属性生效时,段偏移和位移为16 位。此限制限制了可以寻址为64 KB的段的大小。当。。。的时候 32位地址大小属性有效,段偏移和位移是32位,允许 最多可达4 GB的细分市场。 可以覆盖特定的默认操作数大小属性和/或地址大小属性 通过在指令中添加操作数大小和/或地址大小前缀来指令(参见 英特尔架构软件开发人员手册第2章中的“指令前缀”, 第3卷)。此前缀的效果仅适用于它所附加的指令。 表3-1显示了有效的操作数大小和地址大小(在保护模式下执行时) 取决于D标志的设置以及操作数大小和地址大小前缀。
德克