1

I'm writing a C++ program, and decided that it would be more efficient to write a specific function in x86 assembly, due to it's use of the carry-flag. In the disassembly, I noticed that some instructions had been changed, causing my program to throw the exception: "Access violation reading location". Why are instructions changing, and how can I prevent this?

Here is a snippet of my code:

XOR EBX, EBX                ; 31 DB
MOV BL, DH                  ; 88 F3
MOV AH, BYTE PTR [ECX]      ; 8A 21

The disassembler shows this:

xor bx, bx                  ; 66 31 DB
mov bl, dh                  ; 88 F3
mov ah, byte ptr [bx+di]    ; 67 8A 21
はるき
  • 76
  • 1
  • 7

1 Answers1

7

You assembled in 16-bit mode and disassembled in 32-bit mode, making everything the opposite of what it should be. MASM isn't "changing" instructions, just assembling them for the mode you told it to assemble for.

e.g. in 16-bit mode, [ECX] needs a 67 address-size override prefix to encode a 32-bit addressing mode. When decoded in 32-bit mode, that same prefix overrides it to mean 16-bit. (And that bit-pattern means [bx+di]; 16-bit addressing modes can't use a SIB byte so all the different register combos are packed into the ModRM byte. [cx] isn't encodeable.)


Also, if you think xor + mov is the most efficient way to zero-extend DH into EBX, have a look at movzx. That's more efficient on modern CPUs. (See https://agner.org/optimize/ and https://uops.info/).

Generally you want to avoid writing high-8 registers like AH; Haswell/Skylake have merging penalties when reading the full register, and AMD CPUs have a false dependency. Read the info and links on Why doesn't GCC use partial registers? carefully before you use them.

Peter Cordes
  • 245,674
  • 35
  • 423
  • 606