assemblyconditional-statementsx86-64calculatoryasm

Handling multiple conditional jumps in Assembly (x86-64)


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.


Solution

  • 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:

    %macro  convert 2
        movzx ax, dl                             <<<
                                                 <<<  
        mov   bl, %1              ; {100,10,1}
        div   bl
        add   al, '0'                            <<<
        mov   byte[result+%2], al ; {0,1,2}      <<<
        mov   dl, ah                             <<<
    %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