5

I am trying to assign variable to register here is the code:

       ORG 100h

        var1 DB 10  ;
        var2 DB 20  ;

        MOV BX,var1 ; error : operands do not match: 16 bit register and 8 bit address
RET
END

But if swap the 4th line with:

MOV BL, var1;

it works. So my question is why can’t I move 8 bit variable into much larger 16 bit register?

I have already referred this, this and this OP but it does not answer my question.

NOTE:

  1. I am using emu8086 assembler
  2. I am new to assembly language so I apologise if it's a stupid question.
Community
  • 1
  • 1
Ven
  • 73
  • 1
  • 1
  • 4
  • You need to use `movzx` or `movsx` if available to move with zero or sign extension respectively. It might not be available in 8086, in which case you need to move into `BL` as you have done and zero `BH` yourself. As to why: because they created the architecture this way :) – Jester Nov 27 '15 at 14:26
  • @Jester: It's not, only cbw. I updated the http://stackoverflow.com/tags/8086/info tag wiki with a link to wikipedia with a table of when various instructions were introduced. Also with a diatribe about how learning asm on 16bit 8086 is dumb. But anyway, ome of the major insns 8086 is missing are movzx/sx, and `bt*` bit-manip. – Peter Cordes Nov 27 '15 at 14:43
  • upvoting only because that error message is badly worded and misleading. – Peter Cordes Nov 27 '15 at 14:51
  • BTW, this actually *is* a duplicate of http://stackoverflow.com/questions/15540450/how-can-i-move-an-8-bit-address-into-a-16-bit-register-in-x86-assembly, which you linked. The answers there don't try to explain why it's an error, and movzx doesn't work on 8086, but still. Voted to close that as a duplicate of this, since this one has more detailed answers. – Peter Cordes Nov 27 '15 at 15:04

2 Answers2

7

why can’t I move 8 bit variable into much larger 16 bit register?

Becase the machine code MOV instruction needs both the source operand and destination operand to be the same size. This is needed because a MOV instruction by itself doesn't specify how to fill the remaining bits of the larger destination register.

To allow for different size move operations, Intel added MOVZX and MOVSX in the 80386 CPU, which allow a smaller source operand (the destination is always a 32 bit register). -SX and -ZX suffixes denotes what the previously unused bits of the destination register should be filled with.

On 16-bit Intel processors, there is the instruction CBW (Convert Byte to Word), which does a sign extend from 8 to 16 bits. Unfortunately, this only works for the accumulator (register AL/AX), so you'd have to do something like:

mov al,var1
cbw
mov bx,ax

cbw does a sign extension. If your var1 is unsigned, you can do simply as this:

mov bl,var1
xor bh,bh  ; equivalent to mov bh,0 but faster and only one byte opcode

Or, as Peter Cordes states:

xor bx,bx    ;clear the whole destination register
mov bl,var1  ;update the least significant byte of the register with the 8-bit value
mcleod_ideafix
  • 11,128
  • 2
  • 24
  • 32
  • 3
    `xor bx,bx / mov bl, var1` would be better, because it's more like one of the good idioms you'd want to use in 32 or 64bit mode (of zeroing the full register before using a smaller part). It breaks the false dependency on the old contents of the full register, but zeroing the upper and lower halves separately won't. Also, `movs/zx r16, r/m8` is valid. It's also valid with a 64bit dest reg, but that's discouraged for `movzx` because it's identical to movzx with a 32bit dest. There's a new opcode for `movsx r64, r/m32`, but not for movzx. – Peter Cordes Nov 27 '15 at 14:54
  • Also, `mov bh,bh` is a 2-byte instruction, same code-size as `mov bh, 0`. If you are going to write BH, mov is actually better for 8-bit registers on modern CPUs. [How exactly do partial registers on Haswell/Skylake perform? Writing AL seems to have a false dependency on RAX, and AH is inconsistent](https://stackoverflow.com/q/45660139). (But both ways are bad vs. xor-zeroing the whole 16-bit register, like my previous comment.) – Peter Cordes Dec 08 '20 at 19:40
1

You're using an assembler that keeps track of how you declare symbols to figure out what operand size to use.

var1 isn't an 8 bit address, it's a 16bit address (not counting the segment) that points at the first of two 8-bit variables. So the assembler error message is badly worded and confusing.

NASM would do what you said, and do a 16bit load. You'd find var1 in bl and var2 in bh. Presumably you could write mov bx, word [var1], or word ptr or whatever, to get your assembler to emit the 16bit load.

(Actually, NASM would assemble mov BX, var1 into a mov r16, imm16, putting the address into the register. Always use [] around memory references, for consistency, because that works in NASM and MASM variants of Intel syntax. NASM doesn't support the mov BX, offset var1 syntax for writing the mov-immediate form, though.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847