6

In x86 assembly language, is there any way to obtain the upper half of the EAX register? I know that the AX register already contains the lower half of the EAX register, but I don't yet know of any way to obtain the upper half.

I know that mov bx, ax would move the lower half of eax into bx, but I want to know how to move the upper half of eax into bx as well.

nrz
  • 9,959
  • 4
  • 36
  • 69
Anderson Green
  • 25,996
  • 59
  • 164
  • 297
  • 4
    Just shift it down by 16 bits. – Mysticial Mar 05 '13 at 17:28
  • Either do `shr eax, 16` followed by the move. (which destroys `eax`), or do `mov ebx, eax` and `shr ebx, 16` (which zeros the upper half of `ebx`) I'm not sure if doing operations on `bx` will automatically zero the upper half of `ebx` anyway. So if that's the case, you might as well go with the latter method. – Mysticial Mar 05 '13 at 17:32
  • 3
    `ror eax,16` `mov bx,ax` `ror eax,16` if you want to leave eax/the upper part of ebx untouched – user786653 Mar 05 '13 at 17:44
  • 2
    @Mysticial Doing operations on `bx` does not automatically zero upper half of `ebx`. However, in x86-64 doing operations with `ebx` (but not `bx`, `bl` or `bh`) as dest zeroes the top 32 bits of `rbx`. – nrz Mar 05 '13 at 20:23
  • @nrz Ah. That's good to know. I'm aware of the zeroing behavior on x64. I just wasn't sure if that behavior already existed during the earlier days. – Mysticial Mar 05 '13 at 20:25

4 Answers4

13

If you want to preserve EAX and the upper half of EBX:

rol eax, 16
mov bx, ax
rol eax, 16

If have a scratch register available, this is more efficient (and doesn't introduce extra latency for later instructions that read EAX):

mov ecx, eax
shr ecx, 16
mov  bx, cx

If you don't need either of those, mov ebx, eax / shr ebx, 16 is the obvious way and avoids any partial-register stalls or false dependencies.

Peter Cordes
  • 245,674
  • 35
  • 423
  • 606
Alexey Frunze
  • 58,178
  • 10
  • 71
  • 163
8

If you don't mind shifting the original value of bx (low 16 bits of ebx) to high 16 bits of ebx, you need only 1 instruction:

shld ebx,eax,16

This does not modify eax.

nrz
  • 9,959
  • 4
  • 36
  • 69
  • Or with BMI2, [`rorx ebx, eax, 16`](http://felixcloutier.com/x86/RORX.html) to set `ebx = word_swap(eax)`. Unlike `shld`, this has no dependency on the old value of EBX, and is a single uop with 1c latency on all CPUs that support it. (http://agner.org/optimize/) – Peter Cordes May 15 '18 at 17:35
3

I would do it like this:

mov ebx,eax
shr ebx, 16

ebx now contains the top 16-bits of eax

Jason
  • 2,221
  • 17
  • 14
1

For 16-bit mode, this is the smallest (not fastest) code: only 4 bytes.

push eax  ; 2 bytes: prefix + opcode 
pop ax    ; 1 byte: opcode
pop bx    ; 1 byte: opcode

It's even more compact than single instruction shld ebx,eax,16 which occupies 5 bytes of memory.

Peter Cordes
  • 245,674
  • 35
  • 423
  • 606
kiasari
  • 47
  • 6
  • Those instruction sizes are for 16-bit mode. In 32-bit code, it's 5 bytes total and `shld ebx,eax,16` is 4 bytes. (And `shld` is faster on many CPUs.) – Peter Cordes May 15 '18 at 17:27
  • Thanks Peter. `shld ebx, eax, 16` occupies 5 bytes (66 0F A4 D8 10). `shld r32, r32, cl` and `shld r16, r16, immediate` are 4 bytes. Why are push instructions 5 bytes? – kiasari May 17 '18 at 09:10
  • I said *in 32-bit mode*, where the default operand-size is 32, which is much more common than 16-bit mode on modern computers. (But still mostly obsoleted by 64-bit mode). In 32-bit mode, `pop ax` requires a `66` prefix but `push eax` doesn't, so you get a total of 1 + 2 + 2 = 5. Similarly, `shld r32, r32, imm8` doesn't require any prefixes in 32 or 64-bit mode. (And BTW, you don't want to use `shld r,r,cl`, because it's 4 uops on Skylake vs. 1 for `shld r,r,imm8`. http://agner.org/optimize/) – Peter Cordes May 17 '18 at 09:34