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