Search code examples
assemblyx86comparenasm

"error: invalid combination of opcode and operands", when comparing "dereferenced" addresses


I wrote this code to compare two strings and print string2, if the code strings are the same:

enter:
  pusha
  mov ah, 0x0e
  mov al, 0x0a
  int 0x10
  mov al, 0x0d
  int 0x10
  popa
  mov di, reserved_string
  mov bx, string2
  jmp loop1

loop1:
  cmp di, si
  je middle_loop1
  cmp [di], [bx] ; error: invalid combination of opcode and operands
  jne go_back_loop1
  inc di
  inc bx
  jmp loop1

go_back_loop1:
  mov si, reserved_string
  jmp key_press

middle_loop1:
  mov bx, string2
  pusha
  jmp print_ping

print_ping:
  cmp [bx], 0 ; error: operation size not specified
  je go_back_print_ping
  mov ah, 0x0e
  mov al, [bx]
  int 0x10
  inc bx
  jmp print_ping

go_back_print_ping:
  popa
  jmp key_press

It does not compile and I get errors about two of the lines. I don't know how to fix the first problem, as I should be able to compare the two values.

Solved: The instruction does not support two dereferenced addresses.


Solution

  • It doesn't compile.

    cmp [di], [bx] ; error: invalid combination of opcode and operands
    

    The cmp instruction does not allow 2 memory operands. As @Jester proposed in a comment, you could write this as:

    mov  al, [di]
    cmp  al, [bx]
    jne  go_back_loop1
    

    Alternatively, you could use the cmpsb instruction. It just needs using SI instead of BX, and it does not require mentioning any operands as these are implied. Additionally and if it is not already the case, for your present code the direction flag should be cleared and your ES segment register should be equal to your DS segment register.

    cmpsb                    ; Intel `cmps byte ptr [si], [di]`
    jne  go_back_loop1
    
    cmp [bx], 0 ; error: operation size not specified
    

    Here NASM does not know whether the datum at the address BX is a byte, word, or dword. It's your task to specify this as in:

    cmp  byte [bx], 0
    

    Sometimes it so happens that you have a register with a known value like CL=0. Then you could write the above shorter and without the assembler complaining because CL is a byte for sure:

    cmp  [bx], cl
    

    Now it compiles, but will it run?

    loop1:
      cmp di, si
      je middle_loop1
    

    This cmp compares the addresses of both strings. Normally, these will always be different from each other and the loop also modifies them in sync. This cmp does not work to end the loop correctly. What you need is looking for the terminating zero:

    loop1:
      mov  al, [di]
      cmp  al, [bx]
      jne  go_back_loop1     ; different
      inc  di
      inc  bx
      cmp  al, 0             ; terminating zeroes
      jne  loop1
      jmp  middle_loop1      ; identical
    

    Watch out for these.

      jmp loop1
    loop1:
    
      jmp print_ping
    print_ping:
    

    Jumps like these are redundant. The execution can just fall through in the labeled address.