jboulter11
jboulter11

Reputation: 152

MIPS Calculator

I'm trying to finish up this MIPS calculator, super basic, my first mips program. It doesn't have to handle overflow or anything like that, just has to work on small, positive numbers.

I've not checked my algorithms for multiply and divide, because I am just trying to get add working.

I cannot for the life of me figure out why the ints will not read in and also I'm getting a memory out of bounds when I call lb $a0, op to display the operator for output and don't understand why.

I'm new to this so anything is probably helpful. Thanks in advance.

        .data
                        # const string for welcome
welc:       .asciiz "Welcome to SPIM Calculator 1.0!\n" 
p_int:      .asciiz "\nPlease give an integer: "
p_op:       .asciiz "\nPlease give an operator: "
i_err:      .asciiz "\nInput Incorrect, bad operator!\n"
again_str:  .asciiz "Another calculation? (y/n)"
rmndr:      .asciiz " r: "
new_line:   .asciiz "\n"

int1:   .word 1         # space to hold int 1
int2:   .word 1         # space to hold int 2
raw_in: .space 1        # space to hold raw input
op:     .space 1        # space to hold operator char
a_char: .space 1        # space to hold again char

out:    .word 1         # space to hold output
remain: .word 1         # space to hold remainder

#operator constants
c_plus: .ascii "+"      # const for +
c_min:  .asciiz "-"     # const for -
c_mult: .asciiz "*"     # const for *
c_divi: .asciiz "/"     # const for /
c_eq:   .asciiz "="     # const for =
c_no:   .asciiz "n"     # const for n

        .text
        .globl main
main:   li $v0, 4               # syscall 4, print string
        la $a0, welc            # give argument: string
        syscall                 # actually print string

calc:   la $t6, remain          # load remainder variable
        move $t6, $zero         # store 0 in remainder (reset)

        li $v0, 4               # syscall 4, print string
        la $a0, p_int           # give argument: string
        syscall                 # actually print string

        li $v0, 5               # tell syscall we want to read int 1
        syscall                 # actually read in int 1
        la $s1, int1            # load int1 into $s1
        move $s1, $v0           # copy the integer from $v0 to int1

        li $v0, 4               # syscall 4, print string
        la $a0, p_int           # give argument: string
        syscall                 # actually print string

        li $v0, 5               # tell syscall we want to read int 2
        syscall                 # actually read in int 2
        la $s2, int2            # give $s2 the address to hold int 2
        move $s2, $v0           # copy the integer from $v0 to $s2

        li $v0, 4               # syscall 4, print string
        la $a0, p_op            # give argument: string
        syscall                 # actually print string

        li $v0, 8               # tell syscall we want to read operator
        la $a0, op              # give $a0 the address to hold the operator
        syscall                 # actually read in operator

        lb $t0, op              # load the first byte of op
        li $t1, '+'             # load const for plus
        li $t2, '-'             # load const for minus
        li $t3, '*'             # load const for multiplying
        li $t4, '/'             # load const for dividing

        la $s0, out             # load out to $s0

        beq $t0, $t1, plus      # we're adding
        beq $t0, $t2, minus     # we're subtracting
        beq $t0, $t3, multi     # we're multiplying
        beq $t0, $t4, divi      # we're dividing
        # else
        j error                 # incorrect input

plus:   add $s0, $s1, $s2       # add our ints, store in $t0
        j print

minus:  sub $s0, $s1, $s2       # subtract our ints, store in $t0
        j print

multi:  slt $t1, $t2, $s2       # if our counter is less than int2, set $t1 to 1
        beq $t1, $zero, print   # if we've reached int2, we're done
        add $s0, $s1, $s1       # add int1 and int1, store in out
        j multi                 # loop

divi:   la $t0 remain           # load remainder into $t0
        move $t0, $s1           # set remainder to dividend
        add $s0, $zero, $zero   # set out to 0, just in case
loop:   slt $t1, $t0, $s2       # if remainder is less than divisor, set 1
        beq $t1, $zero, print   # if we're done branch to done
        sub $t0, $t0, $s2       # sub divisor from remainder, store in remainder
        addi $s0, $s0, 1        # increment quotient by 1
        j loop                  # loop

print:  li $v0, 1               # tell syscall we want to print int
        la $a0, int1            # give syscall int1 to print
        syscall                 # actually print int
        li $v0, 4               # tell syscall we want to print string
        lb $a0, op              # tell syscall we want to print operator
        syscall                 # actually print string
        li $v0, 1               # tell syscall we want to print int
        la $a0, int2            # give syscall int2 to print
        syscall                 # actually print int
        li $v0, 4               # tell syscall we want to print string
        la $a0, c_eq            # tell syscall we want to print operator
        syscall                 # actually print string
        li $v0, 1               # tell syscall we want to print integer
        la $a0, out             # give syscall our output
        syscall                 # actually print int
        la $t0, remain          # load remainder
        beq $t0, $zero, again   # if we have no remainder, finish printing
        li $v0, 4               # tell syscall we want to print string
        la $a0, rmndr           # tell syscall we want to print remainder string
        syscall                 # print "r: "
        li $v0, 1               # tell syscall we want to print int
        la $a0, remain          # give syscall our remainder to print
        syscall                 # print remainder

again:  li $v0, 4               # tell syscall we want to print string
        la $a0, new_line        # tell syscall to print new line
        syscall
        la $a0, again_str       # load prompt for again string for syscall
        syscall
        li $v0, 8               # tell syscall we want to read string
        la $a0, a_char          # tell syscall to put it in $a0
        syscall
        lb $t0, a_char
        li $t1, 'n'             # get n char so we can compare
        beq $t0, $t1, exit      # if we are done, exit
        #else loop
        j calc                  # jump to beginning



error:  li $v0, 4               # tell syscall we want to print string
        la $a0, i_err           # give syscall what to print
        syscall                 # actually print
        j again                 # go to prompt for retry

exit:   li $v0, 10              # exit code
        syscall                 #exit!

screenshot

Upvotes: 4

Views: 15220

Answers (1)

MByD
MByD

Reputation: 137372

The problem is that you don't use the appropriate instruction to handle memory.

  1. Instead of move you should use sw (store word). This code will not store the int into int1:

    la $s1, int1            # load int1 into $s1
    move $s1, $v0           # copy the integer from $v0 to int1
    

    instead, you should write:

    la $s1, int1            # load address of int1 into $s1
    sw $v0, 0($s1)          # copy the integer from $v0 to int1
    
  2. Like storing, loading from memory require two instructions:

    la $s1, p_op              # or whatever register you choose to use
    lb $a0, 0($s1)          # load byte from the address stored in $s0 (in index 0)
    

    if you want to load the address of p_op into $a0, you should use la $a0, p_op, not lb

Upvotes: 4

Related Questions