0

How can I fix the problem of moving 8 bit value to the BX register (16 bit)?

mov al, 10h
mov bx, al

for this I get:

operands do not match: 16 bit and 8 bit register 
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Slack Bot
  • 64
  • 1
  • 9
  • 1
    You need to convert size or rely on the partial registers. E.g. `movzx bx, al`, `movsx bx, al` or `xor bh, bh; mov bl, al`. – Jester Nov 08 '19 at 13:56
  • Or better, `movzx ebx, al` to write the full register instead of just the 16-bit partial register. – Peter Cordes Nov 08 '19 at 14:02
  • @PeterCordes we don't know if this is 16 bit code or 32 :) – Jester Nov 08 '19 at 14:02
  • @Jester: It's not tagged x86-16 so I assumed modern x86. Even in 16-bit mode on a modern CPU, the extra operand-size prefix byte will avoid the false dependency on the old value of EBX. 32-bit registers are accessible in 16-bit mode. So that was my thought process for recommending writing ebx even if this was 16-bit code. – Peter Cordes Nov 08 '19 at 14:05
  • I added the x86 tag :) – Jester Nov 08 '19 at 14:05
  • 2
    @Jester: ah. this is probably emu8086 or some other legacy thing then, given the OP's reports of "not working". Fortunately Fuz answered for both cases. – Peter Cordes Nov 08 '19 at 14:06
  • 1
    The error message is specifically an emu8086 generated error, so there is little doubt as to the environment. I've updated the tags. – Michael Petch Nov 08 '19 at 14:30
  • Related, basically a duplicate: [MASM Assembly move 8 bit register to the 16 bit register (ie. mov cx, ch)](//stackoverflow.com/q/29716796) – Peter Cordes Nov 18 '19 at 07:37

1 Answers1

8

The answer depends on whether you want to zero extend or sign extend the value and on whether you can or cannot use instructions available starting with the 80386. For better performance, the 80386 or later code should be used if movzx and movsx are available.

zero extend on an 8086 or 80286

Zero the upper half, then move to the low half.

xor bh, bh
mov bl, al

(Or equivalently, and more efficient on some later CPUs, xor bx, bx to zero the whole 16 bits before replacing the low 8.)

sign extend on an 8086 or 80286

Without movsx, you probably want to use cbw which only works with AL -> AX. The simplest way overwrites AH before copying to BX, since your value was already in AL.

 cbw               ; AH = 0 or 0xFF according to top bit of AL
 mov  bx, ax

If you want to preserve AH, you could copy the old AX first then swap after cbw:

 mov  bx, ax      ; save the old AH (and AL)
 cbw              ; sign-extend AL into AX
 xchg bx, ax      ; BX = sign-extended result, restore original AX

Saving instructions on 8086 can involve planning what you keep in which register so it's already in the right place for an instruction like cbw or mul that uses an implicit register. By 386, Intel added versions of some of these that work with any register.


zero extend on an 80386 or newer

Use movzx.

movzx bx, al

For best performance, zero extend all the way to 32 bit.

movzx ebx, al

sign extend on an 80386 or newer

Use movsx, which is like cbw but works for any dst, src, even including a memory source.

movsx bx, al

If possible, sign extend all the way to 32 bit for better performance.

movsx ebx, al

Other methods: setting the top half with neg/sbb is also possible, and so are arithmetic shifts or logical shifts for sign or zero extension. (Especially if your value started in a register like AH). See MASM Assembly move 8 bit register to the 16 bit register (ie. mov cx, ch)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
fuz
  • 88,405
  • 25
  • 200
  • 352
  • 3
    If you can destroy AH, you can `cbw` / `mov bx, ax` on 8086. – Peter Cordes Nov 08 '19 at 14:07
  • Your edit makes the code less efficient if it runs on some modern CPUs. `xor bx,bx` first is probably better than `xor bh,bh`. It still has a false dependency on the old value because it doesn't write the full 32 bits which are accessible in 16-bit mode, but on P6 family it will possibly avoid a partial reg stall when you read the full bx. (Not sure if that's true for 16-bit registers). Writing BH also introduces BH-merging on SnB-family. I see your point that it might be easier for some people to understand writing the upper/lower halves separately, though. – Peter Cordes Nov 08 '19 at 14:16
  • 1
    @PeterCordes I know. Given that OP is clearly not programming in a performance sensitive context, the educative part is more important here. – fuz Nov 08 '19 at 14:18
  • I don't care about the OP, I care about this being a good canonical answer for future readers who will use this directly in whatever project if they need to search and find this answer. – Peter Cordes Nov 08 '19 at 14:19
  • On second thought, performance penalties in 16-bit code don't really bother me. If people are writing 16-bit code, having it run slow is their own fault. Including my `cbw` suggestion is plenty since that's actually good for code-size, too. (And the extend-to-32-bit for 386 is good) – Peter Cordes Nov 08 '19 at 14:29
  • 1
    Wait a minute, does test / sbb work? `test` always clears CF. I think you need `cmp al, 80h` + `cmc` or something to set CF according to the sign bit of AL. Or some one-byte `xchg ax,bx` stuff around CBW. (I had been doing to comment this block while editing; that made me think about which flag SBB actually reads) – Peter Cordes Nov 08 '19 at 14:35
  • 2
    `mov bx, ax` / `cbw` / `xchg bx, ax` is the smallest code-size way to avoid destroying AH, and similar or better performance to cmp/cmc/sbb on modern CPUs (especially Intel before Broadwell where SBB is 2 uops). Had to let that bounce around in my head for a while to come up it. – Peter Cordes Nov 08 '19 at 15:07
  • Was already about to save my edit when yours popped up. Glad you hadn't made any major changes that conflicted. – Peter Cordes Nov 08 '19 at 15:21