Home > Back-end >  Handling multiple conditional jumps in Assembly (x86-64)
Handling multiple conditional jumps in Assembly (x86-64)

Time:12-07

I'm trying to make a calculator in Assembly where the equation is read in as a string (ie 9 3-2/5*4) as opposed to reading in one digit at a time and asking the user which operation they want to perform. I figured out how to parse the string so I can convert the ASCII digits to decimal and store the operators for comparison.

However, I don't know how to tackle the problem of following the order of operations correctly.

Full source code here:

;calculator.asm

%macro  convert 2
    mov al, dl
    mov dl, 0
    mov bl, %1
    div bl
    add byte[result %2], al
%endmacro

section .data
    msg db  "Enter an equation: "
    input   db  "000000000", 10
    decOne  db  0
    decTwo  db  0
    result  db  0
    endLoop dq  9
section .text
    global _start

_start:
    ;cout << msg
    mov     rax, 1
    mov     rdi, 1
    mov     rsi, msg
    mov     rdx, 19
    syscall
    
    ;cin >> input
    mov     rax, 0
    mov     rdi, 0
    mov     rsi, input
    mov     rdx, 10
    syscall

    xor r10d, r10d
    ;convert first digit and store in decOne
    sub byte[input r10], '0'
    mov al, byte[input r10]
    mov byte[decOne], al
    inc r10
    
    ;operator comparison
operatorComp:
    mov al, byte[input r10]
    cmp al, ' '
    je  addition
    cmp al, '-'
    je  subtraction
    cmp al, '*'
    je  multiplication
    cmp al, '/'
    je  division
subtraction:
    inc r10

    sub byte[input r10], '0'
    mov al, byte[input r10]
    mov byte[decTwo], al
    
    mov al, byte[decOne]
    sub al, byte[decTwo]
    mov byte[result], al
    mov byte[decOne], al
    
    inc r10
    cmp r10, qword[endLoop]
    je  done
    jmp operatorComp
addition:
    inc r10
    
    sub byte[input r10], '0'
    mov al, byte[input r10]
    mov byte[decTwo], al
    
    mov al, byte[decOne]
    add al, byte[decTwo]
    mov byte[result], al
    mov byte[decOne], al
    
    inc r10 
    cmp r10, qword[endLoop]
    je  done
    jmp operatorComp
multiplication:
    inc r10

    sub byte[input r10], '0'
    mov al, byte[input r10]
    mov byte[decTwo], al
    
    mov al, byte[decOne]
    mul byte[decTwo]
    mov byte[result], al
    mov byte[decOne], al
    
    inc r10
    cmp r10, qword[endLoop]
    je  done
    jmp operatorComp
division:
    inc r10
    
    sub byte[input r10], '0'
    mov al, byte[input r10]
    mov byte[decTwo], al
    
    mov al, byte[decOne]
    div byte[decTwo]
    mov byte[result], al
    mov byte[decOne], al
    
    inc r10
    cmp r10, qword[endLoop]
    je  done
    jmp operatorComp
done:
    ;convert result to ASCII
    mov dl, byte[result]
    convert 100, 0
    convert 10, 1
    convert 1, 2
    add byte[result 2], dl

    ;output result
    mov     rax, 1
    mov     rdi, 1
    mov     rsi, result
    mov     rdx, 3
    syscall
    
    ;exit program
    mov     rax, 60
    mov     rdi, 0
    syscall

Currently this program only parses the string in the order it was provided and makes the jumps accordingly.

CodePudding user response:

R10 is not callee-preserved. Move its setup to after the prompt and input syscalls:

xor r10d, r10d      ; Better than `mov r10, 0`
;convert first digit and store in decOne
sub byte[input r10], '0'

mov al, byte[decOne]
div byte[decTwo]
mov byte[result], al

The byte-sized division divides AX by the specified source operand. You forgot to zero AH.

movzx ax, byte[decOne]
div   byte[decTwo]
mov   byte[result], al

Because endLoop is hard-coded for an expression of 9 characters (endLoop dq 9), make sure the expression is that long. The example is too short: turn "9 3-2/5" into "9 3-2/5*4" which will produce "8".


Because the result will be equal to 8, turn it into the character "8" with the instruction add byte[result], 48 prior to outputting:

done:
    ;output result
    mov     rax, 1
    mov     rdi, 1
    mov     rsi, result
    add     byte [rsi], '0'
    mov     rdx, 1
    syscall

The convert macro has multiple issues:

  • mov dl, 0 destroys the source and you need it 3 times, but each time a bit reduced
  • For this byte-sized division you need to zero AH
  • The quotient is added to memory that you did not reserve for that purpose
  • The quotient is still not a character, requires adding 48
%macro  convert 2
    movzx ax, dl                             <<<
                                             <<<  
    mov   bl, %1
    div   bl
    add   al, '0'                            <<<
    mov   byte[result %2], al                <<<
    sub   al, '0'                            <<<
    mul   bl                                 <<<
    sub   dl, al                             <<<
%endmacro

section .data
    msg db  "Enter an equation: "
    input   db  "000000000", 10
    decOne  db  0
    decTwo  db  0
    result  db  0, 0, 0                      <<<
    endLoop dq  9

Use the macro this way:

done:
    ;convert result to ASCII
    mov dl, byte[result]
    convert 100, 0
    convert 10, 1
    convert 1, 2
                                              <<<
    ;output result
  • Related