Determining When Nasm Can Infer the Size of the Mov Operation

Determining when NASM can infer the size of the mov operation

The operand-size would be ambiguous (and so must be specified) for any instruction with a memory destination and an immediate source. (Neither operand actually being a register, even if using one or more in an addressing mode.)

Address-size and operand-size are separate attributes of an instruction.


Quoting what you said in a comment on another answer, since I think this gets at the core of your confusion:

I would expect mov [eax], 1 to set the 4 bytes held in memory address eax to the 32 bit representation of 1

The BYTE/WORD/DWORD [PTR] annotation is not about the size of the memory address; it's about the size of the variable in memory at that address. Assuming flat 32-bit addressing, addresses are always four bytes long, and therefore must go in Exx registers. So, when the source operand is an immediate value, the dword (or whatever) annotation on the destination operand is the only way the assembler can know whether it's supposed to modify 1, 2, or 4 bytes of RAM.

Perhaps it will help if I demonstrate the effect of these annotations on machine code:

$ objdump -d -Mintel test.o
...
0: c6 00 01 mov BYTE PTR [eax], 0x1
3: 66 c7 00 01 00 mov WORD PTR [eax], 0x1
8: c7 00 01 00 00 00 mov DWORD PTR [eax], 0x1

(I've adjusted the spacing a bit compared to how objdump actually prints it.)

Take note of two things: (1) the three different operand prefixes produce three different machine instructions, and (2) using a different prefix changes the length of the source operand as emitted into the machine code.

How do you specify the size of a 3 Byte storage in XOR statement in Assembly?

I will add over Jester's correct answer in comments.

If you would insist on "xor-ing" the memory (doesn't make sense for zeroing, but may be worth for other values), then "xor 3B [inbuf],3B [inbuf]" can be done in x86 assembly like this:

mov  eax,[inbuf]   ; loads value from inbuf + 1B undef
xor [inbuf],ax ; word
shr eax,16 ; al = b16..b23 of value @inbuf
xor [inbuf+2],al ; byte

The 4B dword variant:

mov  eax,[outbuf]
xor [outbuf],eax

And all of that is horrible for zeroing, for zeroing this is better:

mov word [inbuf],0
mov byte [inbuf+2],0
mov dword [outbuf],0

Or eventually, if you have already zero in some 32b register:

xor eax,eax
mov [inbuf],ax
mov [inbuf+2],al
mov [outbuf],eax

You can access memory only for power-of-two sizes, and only for some of them, in 32b mode: 1, 2 and 4 with general purpose integer arithmetic.

And 8 or 10 with FPU. Oh yeah, 10 is not power of two, I know, it's special one only for some FP things.

And then there're various SIMD instructions, which can access even 128/256/512 bits (16,32 and 64 bytes).

Then non arithmetic special instructions can sometimes use additional extra sizes, like 5 or 6 maybe (I'm not even sure) with some far jumping, etc... Generally I wouldn't count them even as exception, as the whole x86 instruction decoding is using variable-byte-amount approach and the denominating size is 1B, so it's not about powers of two in that part.

Anyway, almost nobody works with 3 bytes only in Assembly, that's "incorrect" hexed size and bring lot of misfortune upon user, you should avoid it whenever possible.

Sometimes people stretch it so far, that even video ram consisting of RGB data is 32-bit-per-pixel aligned, wasting every 4th byte for "nothing" only like padding (25% of VRAM wasted and it was back in time, when RAM was expensive).

(Early the SVGA VESA modes did have also memory-efficient 24bit modes, but as the addressing per pixel is *3, it was very annoying to use in code, or even by HW accelerators ... nowadays it helps that most of the video ram usage is for textures, where 4th byte can store alpha or other additional information for pixel shader, so it's no more wasted memory, yet the size is 32 bit)


And how to load 3B value from memory:

For generic 3B load which must work all the time:

movzx eax,byte [inbuf+2]
shl eax,16
mov ax,[inbuf]

And when you know that 3B value is not at end of memory page followed by restricted memory page (so the value is either on address aligned by 4, or there's always another legal memory page after it):

mov  eax,[inbuf]      ; loads desired 3B + 1B garbage
and eax,0x00FFFFFF ; truncate it to 3B only

(this would crash on the read over memory page boundary when next memory page is restricted, like if "inbuf" is address 4093, and address 4096 is restricted to this process => illegal memory access crash, but that's usually not where you would have "inbuf" defined, so this shorter variant is usually shown as the correct solution, without this stupidly long explanation, when it actually may crash).

MOVZBQ equivalent in NASM

In Intel syntax, the size of memory operands is indicated by a prefix on the operand. So you would write movzx rbx, byte [address].

However, writes to 32-bit registers automatically zero-extend into the 64-bit register. So movzx ebx, byte [address] is equivalent and saves one byte of code (no REX prefix needed).

Generally movzx ebx, byte [address] is preferable to mov bl, [address] because it overwrites the entire register. Writing to the byte-sized register can have a minor performance penalty in some cases, see Why doesn't GCC use partial registers? for details. Though mov bl, [address] is fewer bytes of code, which may be worth the tradeoff if you need to optimize for size.

Emacs weirdness when trying to comment in Assembly

; is bound to asm-comment in assembly mode. You can either do a quoted insert with C-q ; on a case-by-case basis, or remove the binding and just use M-; (comment-dwim) for fancier commenting. If you want to do the latter, set ";" locally to do a self-insert command:

(defun my-hook ()
(local-set-key ";" 'self-insert-command))

(add-hook 'asm-mode-hook 'my-hook)


Related Topics



Leave a reply



Submit