I'm making a small calculator in nasm 86x that reads two lines of equations in the form
3 2
6 / 2
which should calculate and output
5
3
its user input, but only 2 digits and it will read what operator to use and then go to wherever I called that operator, and do the equation. I just don't know how to loop it so it reads the next line as well. I tried to just hardcode it, but its difficult with the div operator, as I clear the upper register as I don't care about complicated numbers.
segment .data
NO: db 'Invalid input', 10
nolen: equ $-NO
segment .bss
;defining all variables
space resb 1
num1 resb 1
num2 resb 1
;second equation
num3 resb 1
num4 resb 1
char resb 1
char2 resb 1
;newline for enter char, maybe
newlin resb 1
;result
res resb 1
section .text
global _start ;must be declared for using gcc
_start: ;tell linker entry point
mov cx, 2
;reading space
mov eax, 3
mov ebx, 0
mov ecx, space
mov edx, 1
int 0x80
;reading num 1
mov eax, 3
mov ebx, 0
mov ecx, num1
mov edx, 1
int 0x80
;reading space
mov eax, 3
mov ebx, 0
mov ecx, space
mov edx, 1
int 0x80
;reading char
mov eax, 3
mov ebx, 0
mov ecx, char
mov edx, 1
int 0x80
;reading space
mov eax, 3
mov ebx, 0
mov ecx, space
mov edx, 1
int 0x80
;reading num 2
mov eax, 3
mov ebx, 0
mov ecx, num2
mov edx, 1
int 0x80
;reading space
mov eax, 3
mov ebx, 0
mov ecx, space
mov edx, 1
int 0x80
;reading next line
mov eax, 3
mov ebx, 0
mov ecx, newlin
mov edx, 1
int 0x80
; moving the first number to eax register and second number to ebx
; and subtracting ascii '0' to convert it into a decimal number
;moving variables in lower halfs
mov AX, [char]
;compare char to plus sign
cmp AX, byte ' '
;jump to plus function
je plus
cmp AX, byte '-'
;jump to minus
je minus
cmp AX, byte '*'
;jump to multiplication
je multi
cmp AX, byte '/'
;jump to division
je divi
jmp Nope
cmp AX, 2
je _start
Nope:
mov eax, 4
mov ebx, 1
mov ecx, NO
mov edx, nolen
int 0x80
jmp exit
plus:
mov al, [num1]
mov bl, [num2]
add al, bl
sub al, '0'
mov [res], al
; print the sum
mov eax, 4
mov ebx, 1
mov ecx, res
mov edx, 1
cmp ecx, 2
je _start
int 0x80
jmp exit
minus:
mov al, [num1]
mov bl, [num2]
sub al, bl
add al, '0'
mov [res], al
;print the sub
mov eax, 4
mov ebx, 1
mov ecx, res
mov edx, 1
cmp ecx, 2
je _start
int 0x80
jmp exit
multi:
mov al, [num1]
mov bl, [num2]
sub al, '0'
sub bl, '0'
;multiply them
mul bl
;sub bl, '0'
add al, '0'
mov [res], al
;add al, '0'
; print the sum
mov eax, 4
mov ebx, 1
mov ecx, res
mov edx, 1
cmp ecx, 2
je _start
int 0x80
jmp exit
divi:
mov al, [num1]
mov bl, [num2]
mov dx, 0
mov ah, 0
sub al, '0'
sub bl, '0'
div bl
add ax, '0'
mov [res], al
; print the divide output
mov eax, 4
mov ebx, 1
mov ecx, res
mov edx, 1
cmp ecx, 2
je _start
int 0x80
jmp exit
exit:
mov eax, 1
mov ebx, 0
int 0x80
I was reading my textbook and it says to use the cmp ecx, 2 and je _start but I guess I am not using it correctly. I at least got it to semi work its just that last line I am unable to get. Right now it always correctly outputs the first value, just prints nothing for the other.
CodePudding user response:
It's about register preservation
mov cx, 2 ;reading space mov eax, 3 mov ebx, 0 mov ecx, space mov edx, 1 int 0x80
With the mov cx, 2
instruction you are setting up a loop counter so your code can run twice. That's fine, but just a few instructions later, you destroy this counter with the mov ecx, space
instruction that loads a buffer address. Don't forget that CX
is just the low 16 bits of the ECX
register.
A solution is to store the counter on the stack.
mov ecx, 2 ; Use ECX instead of CX in 32-bit code!
push ecx ; (1) Preserving the counter
;reading space
mov eax, 3
mov ebx, 0
mov ecx, space
mov edx, 1
int 0x80
...
It's about how and where you loop back
As an example, look at the plus code:
; print the sum mov eax, 4 mov ebx, 1 mov ecx, res mov edx, 1 cmp ecx, 2 <-- Extra code to loop back je _start <-- int 0x80 jmp exit
Firstly, you should have placed the extra code below the int 0x80
instruction, so the print function can do its job first.
Secondly, the ECX
register does not contain your loop counter because of the mov ecx, res
instruction just a few lines higher up!
Thirdly, working with a counter means that the code needs to manipulate it. Most often this boils down to decrementing it.
A quick solution:
; print the sum
mov eax, 4
mov ebx, 1
mov ecx, res
mov edx, 1
int 0x80
pop ecx ; (1) Restoring the counter
dec ecx ; Decrementing the counter
jnz _start ; Looping back if the counter is not yet zero
jmp exit ; Exit program if the counter has become zero
Now you could apply this to each of the 4 code blocks plus, minus, multi, and divi, or you could write it somewhat more intelligently:
plus:
... ; Calculating and printing go here
jmp MORE
minus:
... ; Calculating and printing go here
jmp MORE
multi:
... ; Calculating and printing go here
jmp MORE
divi:
... ; Calculating and printing go here
jmp MORE
MORE:
pop ecx ; (1) Restoring the counter
dec ecx ; Decrementing the counter
jnz _start ; Looping back if the counter is not yet zero
exit: ; Exit program if the counter has become zero
mov eax, 1
mov ebx, 0
int 0x80
It's about where you loop back to
The _start label is where the program begins executing. This cannot be the target for looping back, because if we did, we would be reinitializing the counter forever and the program could never terminate.
The line where we preserve our counter is the correct target for looping back:
mov ecx, 2 ; Use ECX instead of CX in 32-bit code!
AGAIN: <----------------------------------------------------------------\
push ecx ; (1) Preserving the counter |
|
... |
|
MORE: |
pop ecx ; (1) Restoring the counter |
dec ecx ; Decrementing the counter |
jnz AGAIN ; Looping back if the counter is not yet zero -----/
exit: ; Exit program if the counter has become zero
mov eax, 1
mov ebx, 0
int 0x80
Something to keep paying attention to
mov AX, [char] ;compare char to plus sign cmp AX, byte ' ' ;jump to plus function je plus
The char variable is a byte. Never read it like it was a word. If nasty things can happen, they will happen...
mov al, [char]
cmp al, ' '
je plus
cmp al, '-'
je minus
cmp al, '*'
je multi
cmp al, '/'
je divi
Nope:
mov eax, 4
mov ebx, 1
mov ecx, NO
mov edx, nolen
int 0x80
jmp exit
There's gotta be one final remark
jmp Nope cmp AX, 2 <- These lines will NEVER execute je _start <- Nope: