3

Playing around a bit with Turbo Assembler and Turbo Debugger, I was surprised about opcodes. More precisely, I have some assembled binary in which Turbo Debugger disassembles the word

29 C3

correctly to sub bx, ax. However, Turbo Assembler assembles the very same instruction sub bx, ax to the following word

2B D8

Being puzzled about that, I found this reference stating that subtraction of a register from a register may indeed begin with both 29 and 2B. Is it indeed the case that exactly the same instruction can be expressed by different opcodes? If so, why is that? Is it for historical reasons and compatibility? The reference states different operand types for the opcodes, they just coincide for sub bx, ax. Is this for the ability to later patch in different operands via self-modifying code or the like? Furthermore, does Turbo Assembler have a syntax construct to choose one opcode over the other?

Note: I was aware of conditional jumps like je and jz having the same opcode as they have the same flag-dependent behaviour and the different mnemonics are there to reflect different semantics of the same operation, but the former confuses me.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Codor
  • 17,447
  • 9
  • 29
  • 56

1 Answers1

7

Most x86 instructions support two operands of which one operand can be a memory operand. This is supported by encoding the operands in a modr/m byte. This byte always encodes one register operand and one register or memory (r/m) operand, but the instruction must decide which of its operands is the register operand and which is the memory operand.

So to support having the memory operand in the source or the destination operand, many instructions are available with one variant where the source operand can be a memory operand and one variant where the destination can be a memory operand. This is generally controlled by bit 01 (dubbed the d bit in some manuals).

As a consequence, instructions that do not need a memory operand can be encoded both ways and assemblers generally pick one or the other as a somewhat random implementation detail.

fuz
  • 88,405
  • 25
  • 200
  • 352
  • 2
    Bit 1 of the first byte is `d` direction you can see that is the one difference. In the second byte bits 5-3 `000` (AX) and bits 2-0 `011` (BX) are swapped. – Weather Vane Aug 22 '21 at 15:32