AX、AH、AL如何映射到EAX?

58
我的理解是,x86寄存器中的每个寄存器都可以被整个32位代码访问,并且它被分成多个可访问的寄存器。
在这个例子中,32位寄存器EAX,如果我们调用AX,它应该返回前16位,如果我们调用AH或AL,它应该返回16位后的下一个8位,而AL应该返回最后的8位。
所以我的问题是,因为我不完全相信它是如何操作的。如果我们存储32位值即EAX存储:
0000 0100 0000 1000 0110 0000 0000 0111

所以如果我们访问AX,它应该返回
0000 0100 0000 1000

如果我们读取AH,它应该返回。
0000 0100

当我们读取 AL 时应返回

0000 0111

这是否正确?如果是,AH 真正持有什么值?


不,你的AX值是错误的。截掉最后16位。AH仅返回EAX的第8到15位。 - Hans Passant
5
请将文本从英语翻译成中文。仅返回已翻译的文本:请在您的问题背景下,用更易于理解的内容替换位模式,例如:0000 0001 0010 0011 ... - Micha Wiedenmann
相关:汇编语言 - 为什么字符以小端方式存储在寄存器中? 关于内存映射。 - Peter Cordes
7个回答

108

不,那不是完全正确的。

  • EAX 是 32 位完整值
  • AX 是低 16 位
  • AL 是低 8 位
  • AH 是位于 8 到 15 位(从零开始)的高半部分,即 AX 的顶半部分

因此,AX 由 AH:AL 两个半部分组成,并且本身是 EAX 的低半部分。(不直接访问 EAX 的上半部分作为 16 位寄存器;如果需要访问它,可以进行移位或旋转 EAX。)

x86-64 CPU 将整数寄存器扩展到 64 位:

  • RAX 是 64 位完整值,其中 EAX 及其子部件映射到低 32 位。 64 位寄存器的上半部分仅在 64 位模式下可访问,而 32 位寄存器可以在支持它们的任何模式下使用。
所有这些也适用于EBX/RBX、ECX/RCX和EDX/RDX。其他寄存器如EDI/RDI具有低16位部分寄存器DI,但没有高8位部分,而低8位DIL仅在64位模式下可访问:64位架构中的汇编寄存器

写入AL、AH或AX会使其他字节在完整的AX/EAX/RAX中保持不变,这是出于历史原因。例如,它必须将新的AL合并到完整的RAX中。(在32位或64位代码中,如果您不特别需要此合并,请优先使用movzx eax,byte [mem]movzx eax,word [mem]加载:为什么GCC不使用部分寄存器?

写入EAX会将其零扩展为RAX。(为什么在32位寄存器上执行的x86-64指令会将完整的64位寄存器的上半部分清零?

同样的规则适用于所有寄存器,而不仅仅是RAX。例如,写入DI或DIL会合并到旧的RDI中,写入EDI会零扩展并覆盖整个RDI。对于R10B或R10W的写入也是如此,写入R10D会使R10独立于旧的R10值。


谢谢您的帮助,这解决了很多问题。这是一个愚蠢的错误。 - Randy
你如何称呼高16位和高32位?是否有EAXH或AXH? - user97662
1
@user97662:不,无法仅访问寄存器的上半部分 - 您必须读取整个寄存器并根据需要进行移位。 - 500 - Internal Server Error
1
@wintermute:请看上面的评论,除非我误解了你。 - 500 - Internal Server Error
1
@wintermute: 为什么没有一个包含EAX高位字节的寄存器? 解释了原因。在汇编中访问寄存器的高位字节 解决了该问题,特别是那里的评论提供了一些高效的建议。 - Peter Cordes
显示剩余2条评论

47
| 0000 0001 0010 0011 0100 0101 0110 0111 | ------> EAX

|                     0100 0101 0110 0111 | ------> AX

|                               0110 0111 | ------> AL

|                     0100 0101           | ------> AH

44

AX是EAX的低16位。AH是AX的8个高位(即EAX的位8-15),AL是EAX的最低有效字节(位0-7)以及AX。

示例(十六进制数字):

EAX: 12 34 56 78
AX: 56 78
AH: 56
AL: 78

7

你的答案是错误的

Al和Ah的选择来自AX而不是EAX

例如

EAX=0000 0000 0000 0000 0000 0000 0000 0111

所以如果我们调用 AX,它应该返回什么。
0000 0000 0000 0111

如果我们调用AH,它应该返回什么?
0000 0000

当我们调用AL时,它应该返回什么。
0000 0111

例子二

EAX: 22 33 55 77
AX: 55 77
AH: 55    
AL: 77

example 3

EAX: 1111 0000 0000 0000 0000 0000 0000 0111    
AX= 0000 0000 0000 0111
AH= 0000 0000
AL= 0000 0111  

1
关于字节序的问题?如果我使用_GAS_汇编执行movl $0x01 %eax,那么%ax%al的值将是什么?是一还是零? - Frozen Flame
@FrozenFlame:不,字节序只适用于内存(包括如何编码mov $imm32, %eax指令,如opcode 01 00 00 00)。%al中的值将为1。如果您在寄存器内将MSB放在左侧,LSB放在右侧,则左移%eax有效。(对于矢量寄存器,这可能会变得棘手,请参见https://dev59.com/IZ3ha4cB1Zd3GeqPYbIl) - Peter Cordes

4
以下代码片段使用GDB检查EAX。
    (gdb) info register eax
    eax            0xaa55   43605
    (gdb) info register ax
    ax             0xaa55   -21931
    (gdb) info register ah
    ah             0xaa -86
    (gdb) info register al
    al             0x55 85
  1. EAX - 完整的32位数值
  2. AX - 低16位数值
  3. AH - 从8到15位的比特位
  4. AL - EAX / AX的低8位

2
你可以使用p /x $eax(或省略/x以获取十进制)在gdb中打印寄存器。并使用set $eax = 0xdeadbeef修改它们,如果我没记错的话。有关汇编的一些gdb提示,请参见x86标签wiki底部。 - Peter Cordes

4

不对--AL是AX的8个最低有效位。 AX是EAX的16个最低有效位。

也许我们从eax中的04030201h开始处理会更容易。在这种情况下,AX将包含0201h,AH将包含02h,而AL将包含01h。


0
这是一个访问寄存器的地图: 访问寄存器 然后你可以了解到 EAXAXAHAL 寄存器。
| 00000000 11111111 11111111 00000000 | | EAX |
|                   11111111 00000000 | | AX  |
|                   11111111          | | AH  |
|                            00000000 | | AL  |

另一个例子:
| 10110101 11010101 10100110 00001111 | | EAX |
|                   10100110 00001111 | | AX  |
|                   10100110          | | AH  |
|                            00001111 | | AL  |

因为从x86 Assembly/x86 Architecture中得知:
最低有效字节(LSB),或低半部分,通过将'X'替换为'L'来标识。最高有效字节(MSB),或高半部分,则使用'H'来表示。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接