Edward Atkinson
Edward Atkinson

Reputation: 3

Comparing strings in 8086 real mode

I wrote a function designed to compare a 4 letter string against the first 4 letters in a buffer:

is_cmd:
                                ; bx is the string address argument
    push bx                     ; Push bx to stack
    mov cx, [bx]                ; and also move into cx

    mov bx, BUFFER              ; Reset BUFFER_INDEX
    mov [BUFFER_INDEX], bx      ; so it points to first 2 characters

    mov bx, [BUFFER_INDEX]
    cmp cx, [bx]                ; Compare
    jne is_cmd_no               ; Jump if not equal

    pop bx                      ; Retrive bx from stack
    inc bx                      ; Move pointer to next 2 characters
    mov cx, [bx]                ; Move to cx

    mov bx, [BUFFER_INDEX]      ; Move BUFFER_INDEX into bx
    inc bx                      ; and move to next 2 characters

    cmp cx, [bx]                ; Compare
    jne is_cmd_no               ; Jump if not equal
    jmp is_cmd_yes              ; Jump if equal both times

is_cmd_no:
    mov ax, 1                   ; Set ax to 1 so jz will not jump
    ret

is_cmd_yes:
    mov ax, 0                   ; Set ax to 0 so jz will jump
    ret

Here is an example of it's usage:

mov bx, REBOOT_CMD
call is_cmd
or ax, ax
jz reboot_cmd

And here are all the variables:

REBOOT_CMD:
    db "rset",0

BUFFER_INDEX:
    dw BUFFER

BUFFER:
    times 80 db 0
BUFFER_END:

However when the string is not equal it causes the emulator (BOCHS) to hang. Why?

Upvotes: 0

Views: 739

Answers (2)

Tommylee2k
Tommylee2k

Reputation: 2731

use AX for comparing, it's changed anyways; there's no need to clobber anything else; and let the CPU do the "pointer calculation", you can read from [BX+2], there's no need to change it

mov bx, REBOOT_CMD
call is_cmd
or ax, ax
jz reboot_cmd

is_cmd:
   mov ax, [bx]
   cmp ax, [BUFFER]          ; [buffer] with [bx]
   jne is_cmd_no
   mov ax, [bx+2]
   cmp ax, [BUFFER+2]        ; [buffer+2] with [bx+2]
   jne is_cmd_no
   mov ax, 1                 ; is_cmd_yes
   ret
is_cmd_no:
   xor ax, ax
   ret

or even better, inline it into the use-case

is_reboot:
   mov ax, [REBOOT_CMD]
   cmp ax, [BUFFER]          ; [buffer] with "rs"
   jne no_reboot
   mov ax, [REBOOT_CMD+2]
   cmp ax, [BUFFER+2]        ; [buffer+2] with "et"
   jz reboot_cmd
no_reboot:

note: the 2 branches differ, first is a "jne", second a "jz" to the reboot case. it's like an "AND" in C, where the 2nd part of the expression is only evaluated, if the first had TRUE as result

if you feel like obfuscting if, even the "BUFFER db "rset",0" is obsolete, you could compare [BUFFER] with "rs" ( which is "s" + 0x100 * "r" = 0x7372 ) and [BUFFER+2] with "et" ( = "e" + 0x100 * "t" = 0x7465 ):

is_reboot:
   cmp word ptr [BUFFER],0x7372          ; [buffer] with "rs"
   jne no_reboot
   cmp word ptr [BUFFER+2],0x7465        ; [buffer+2] with "et"
   jz reboot_cmd
no_reboot:

Upvotes: 1

zx485
zx485

Reputation: 29052

A suggestion for checking if it is a command. Pay attention to the fact, that I assigned a ' TRUE' value of 1 to a match and a FALSE value of 0 to a non-matching result. If you really like it the other way, just exchange mov ax,1 and xor ax, ax in the code below. Also note that using BUFFER_INDEX is not necessary.

is_cmd:
  mov cx, [bx]
  mov dx, [bx+2]
  lea bx, BUFFER           ; Reset BUFFER_INDEX to effective address of BUFFER
  ; -- mov [BUFFER_INDEX], bx   ; so it points to first 2 characters
  ; -- mov bx, [BUFFER_INDEX]   ; is redundant, because BX is already equal to BUFFER_INDEX
  cmp cx, [bx]             ; compare first two chars
  je first_two_match
  jmp is_cmd_no
first_two_match:
  cmp dx, [bx+2]           ; compare the second two chars
  jne is_cmd_no
  mov ax, 1                ; is_cmd_yes
  ret
is_cmd_no:
  xor ax, ax
  ret

Upvotes: 0

Related Questions