44

My understanding of x86 registers say that each register can be accessed by the entire 32 bit code and it is broken into multiple accessible registers.

In this example EAX being a 32 bit register, if we call AX it should return the first 16 bits, and if we call AH or AL it should return the next 8 bits after the 16 bits and AL should return the last 8 bits.

So my question, because I don't truly believe is this is how it operates. If we store the 32 bit value aka EAX storing:

0000 0100 0000 1000 0110 0000 0000 0111

So if we access AX it should return

0000 0100 0000 1000

if we read AH it should return

0000 0100

and when we read AL it should return

0000 0111

Is this correct? and if it is what value does AH truly hold?

Community
  • 1
  • 1
Randy
  • 1,111
  • 2
  • 9
  • 28

6 Answers6

79

No, that's not quite right.

EAX is the full 32-bit value
AX is the lower 16-bits
AL is the lower 8 bits
AH is the bits 8 through 15 (zero-based)

So AX is composed of AH:AL halves, and is itself the low half of EAX. (The upper half of EAX isn't directly accessible as a 16-bit register; you can shift or rotate EAX if you want to get at it.)

For completeness, in addition to the above, which was based on a 32-bit CPU, 64-bit Intel/AMD CPUs have

RAX, which hold a 64-bit value, and where EAX is mapped to the lower 32 bits.

All of this also applies to EBX/RBX, ECX/RCX, and EDX/RDX. The other registers like EDI/RDI have a DI low 16-bit partial register, but no high-8 part, and the low-8 DIL is only accessible in 64-bit mode: Assembly registers in 64-bit architecture


Writing AL, AH, or AX leaves other bytes unmodified in the full AX/EAX/RAX, for historical reasons. i.e. it has to merge a new AL into the full RAX, for example. (In 32 or 64-bit code, prefer a movzx eax, byte [mem] or movzx eax, word [mem] load if you don't specifically want this merging: Why doesn't GCC use partial registers?)

Writing EAX zero-extends into RAX. (Why do x86-64 instructions on 32-bit registers zero the upper part of the full 64-bit register?)

Again, all of this applies to every register, not just RAX. e.g. writing DI or DIL merges into the old RDI, writing EDI zero-extends and overwrites the full RDI. Same for R10B or R10W writes merging, writing R10D leaving R10 independent of the old R10 value.

Peter Cordes
  • 245,674
  • 35
  • 423
  • 606
  • So would you say my misunderstanding would come from Ax being the top 16, but instead a call to a value containing both Al and Ah? – Randy Mar 03 '13 at 21:42
  • Thanks for the help, this cleared a lot up. Such a silly error – Randy Mar 03 '13 at 21:51
  • how d you call the higher 16-bits, and higher 32bits? Is there a EAXH or AXH? – user97662 Jun 26 '17 at 19:39
  • @user97662: No, there's no way to access only the upper parts of the registers - you must read the entire register and shift as needed. – 500 - Internal Server Error Jun 26 '17 at 19:54
  • When I was in school (in Russia), we were primarily studying the 16-bit assembly language in DOS. I do remember that general purpose registers adhered to the little endian system, e.g. AX was |AL|AH|. Although I had a hard time at extrapolating this concept onto 32-bit registers. Would something like this be legit for EAX? |AL|AH|EAL|EAH| Or is it just |AL|AH| EAX | ? – wintermute Dec 04 '17 at 06:09
  • @wintermute: Please see the comment above, unless I am misunderstanding you. – 500 - Internal Server Error Dec 04 '17 at 11:21
  • 1
    Thanks to Peter Cordes for heavily augmenting this answer. – 500 - Internal Server Error Apr 01 '21 at 15:59
  • My pleasure; it makes it work as a canonical duplicate for more different questions (especially about writing one part modifying other parts or not). – Peter Cordes Apr 05 '21 at 00:59
43

AX is the 16 lower bits of EAX. AH is the 8 high bits of AX (i.e. the bits 8-15 of EAX) and AL is the least significant byte (bits 0-7) of EAX as well as AX.

Example (Hexadecimal digits):

EAX: 12 34 56 78
AX: 56 78
AH: 56
AL: 78
phuclv
  • 27,258
  • 11
  • 104
  • 360
pascalhein
  • 5,372
  • 4
  • 29
  • 41
32
| 0000 0001 0010 0011 0100 0101 0110 0111 | ------> EAX

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

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

|                     0100 0101           | ------> AH
Sohcahtoa82
  • 549
  • 2
  • 13
Miku Ghoul
  • 469
  • 5
  • 7
  • 6
    You should remove the manual spaces (` `), and just format this entire thing as a code block (Indent each line with 4 spaces, and it will take on mono-space formatting, and preserve spacing) – Goodbye StackExchange Oct 19 '18 at 12:52
7

no your ans is Wrong

Selection of Al and Ah is from AX not from EAX

e.g

EAX=0000 0000 0000 0000 0000 0000 0000 0111

So if we call AX it should return

0000 0000 0000 0111

if we call AH it should return

0000 0000

and when we call AL it should return

0000 0111

Example number 2

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  
Tomas Pastircak
  • 2,829
  • 14
  • 28
  • 1
    Any thing to do with endian? If I do `movl $0x01 %eax` assembling with _GAS_, what will the values of `%ax` and `%al` be? One or zero? – Frozen Flame Aug 27 '14 at 03:47
  • @FrozenFlame: No, endianness only applies to memory (including how the `mov $imm32, %eax` instruction is encoded, as `opcode 01 00 00 00`.). The value in %al will be `1`. Left-shifting %eax works if you think of the MSB at the left, LSB at the right within the register. (This can get tricky for vector registers, see https://stackoverflow.com/questions/41351087/convention-for-displaying-vector-registers) – Peter Cordes Sep 19 '17 at 03:46
4

No -- AL is the 8 least significant bits of AX. AX is the 16 least significant bits of EAX.

Perhaps it's easiest to deal with if we start with 04030201h in eax. In this case, AX will contain 0201h, AH wil contain 02h and AL will contain 01h.

Jerry Coffin
  • 437,173
  • 71
  • 570
  • 1,035
4

The below snippet examines EAX using GDB.

    (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 - Full 32 bit value
  2. AX - lower 16 bit value
  3. AH - Bits from 8 to 15
  4. AL - lower 8 bits of EAX/AX
scanjee
  • 301
  • 3
  • 5
  • 1
    you can also print registers in gdb with `p /x $eax` (or omit the `/x` for decimal). And modify them with `set $eax = 0xdeadbeef`, IIRC. See also the bottom of the [x86 tag wiki](https://stackoverflow.com/tags/x86/info) for some gdb tips for asm. – Peter Cordes Sep 19 '17 at 03:41