我将尝试解释GAS在使用.code16
时的行为。
根据手册,当处于16位模式下使用32位操作数或指令时,指令编码会产生一个66H操作数前缀。这是否意味着
.code16
movw %eax, %ebx
这种模式是否合法?那么这段代码不能在16位处理器上运行吗?
我将尝试解释GAS在使用.code16
时的行为。
根据手册,当处于16位模式下使用32位操作数或指令时,指令编码会产生一个66H操作数前缀。这是否意味着
.code16
movw %eax, %ebx
从80386开始,我们可以使用
operandsize-
和addresssize-
覆盖前缀。这些前缀可以与16位地址模式和32位地址模式配合使用。此外,它还可以用于实地址模式、保护模式和虚拟86模式。这些前缀会反转代码段中一个指令的默认操作数大小和/或地址大小。默认的操作数大小和地址大小由代码段描述符中的D标志指定(如果没有GDT / LDT,则在BIOS的POST过程完成后我们变成16位地址模式)。
在16位地址模式下,如果我们想要使用32位操作数和/或32位地址,则必须添加这些前缀。如果没有这些前缀,我们只能在16位地址模式下使用16位地址/操作数。
在32位地址模式下,如果我们想要使用32位操作数和/或32位地址,则必须从代码中省略这些前缀。如果我们向我们的代码中添加这些前缀,则可以在32位地址模式下使用16位地址/操作数。
Intel:
指令前缀可用于覆盖代码段的默认操作数大小和地址大小。这些前缀可在实地址模式、保护模式和虚拟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位有效地址将32位寄存器中的内容移动到内存中。
- 如果有地址大小前缀,则使用32位有效地址将16位寄存器中的内容移动到内存中。
- 如果有地址大小前缀和操作数大小前缀,则使用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位寄存器传输值时,处理器将寄存器的两个高位字节填充为零。
处理器可配置为32位或16位地址和操作数大小。在32位地址和操作数大小下,最大线性地址或段偏移量为FFFFFFFFH(2^32-1),操作数大小通常为8位或32位。在16位地址和操作数大小下,最大线性地址或段偏移量为FFFFH(2^16-1),操作数大小通常为8位或16位。
在使用32位寻址时,逻辑地址(或far指针)由16位段选择器和32位偏移量组成;在使用16位寻址时,它由16位段选择器和16位偏移量组成。指令前缀允许程序内临时覆盖默认的地址和/或操作数大小。
在保护模式下运行时,当前执行代码段的段描述符定义默认地址和操作数大小。段描述符是一个系统数据结构,通常对应用程序代码不可见。汇编指令允许为程序选择默认寻址和操作数大小。然后,汇编器和其他工具适当地设置代码段的段描述符。
在实地址模式下运行时,默认的寻址和操作数大小为16位。可以在实地址模式中使用地址大小覆盖来启用32位寻址;但是,最大允许的32位线性地址仍为000FFFFFH(2^20-1)。
w
后缀表示 2 字节移动。movl %eax,%eax
是合法的。 - fuz