Home > Software design >  looping onto the second equation
looping onto the second equation

Time:09-18

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