# Author:     Aleksander Krimsky
# 07 JUNE 2017 
# Multivariable Assembly Calculator
#
#
# ----- Implementation Explanation -----
# My implementation of the 5 function calculator takes input from the user
# and uses the string to store variables for order of operations and variable tracking.
#
# Valid characters are the numbers and operators along with ALL EXTENDED ASCII Characters
# During operation, characters that are used are removed from the string, then the value
# is added back into the string as the midpoint of the ascii range (255-127/2)+127 to support
# rolling values ranging from -64 to positive 64.
#
# Example 1:
# 4 * 2 + 3 + 4 * 2
# = 199 + 3 + 4 * 2
# = 199 + 3 + 199
# = 11 + 199
# = 19 (Final Result)
# Therefore any value greater than 127 is a special placeholder and not a literal number. 
# These values are converted during the operation. 
#
# ----- General Flow -----
# Overall this program loops 6 times. Cycles 0 through 4 are for each operator and the sixth cycle 
# simply grabs the last stored variable and prints it out. The inner loop consists of iterating up 
# to 32 times or until the equals sign is reached. When an operation is performed, the inner loop
# restarts. When the inner loop hits the equal sign without performing any operation, the next operator
# mode is started. This means that if we hit an operator thats not in the correct order, we will not 
# make the computation.

.globl main
.globl _add
.globl _sub
.globl _mul
.globl _mul_loop
.globl _div
.globl _mod
.globl _equal
.globl _addop
.globl _subop
.globl _mulop
.globl _modop
.globl _divop
.globl _jumpra            #jumps back to routine after stored variable load
.globl _normalize
.globl _div_loop
.globl _loadleftroutine   #grabs the left variable whether thats a literal or stored number
.globl _leftload          #if the number is a stored left value, retrieve it
.globl _loadrightroutine  #grabs the right variable whether thats a literal or stored number
.globl _rightload         #if the number is a stored right value, retrieve it
.globl found_op
.globl operator_loop
.globl error
.globl left_num
.globl print_result
.globl number_jump
.globl read_string
.globl read_string_loop
.globl exit

.data 0x10010000
input_string: .asciiz "Enter your equation: \n" 
output_string: .asciiz "Your answer is: \n"     
error_message: .asciiz "Invalid Input!"         
insert_into: .word 4                            #Offset is 60, an important magic number that will appear for store/loads

.text

main:
addi $s7, $0, -1  #Op-Mode 0 = mul, 1 =  div, 2 = mod, 3 = add, 4 = sub, 5 = print result
addi $s6, $0, 0   #Index of offset for left variable
addi $s5, $0, 0   #Gets set to 1 if an operator is found
addi $s4, $0, 0   #Index of the operator, used for string sanitizing

lui $t0, 0x1001
ori $a0, $t0, 0
addi $v0, $0, 4
syscall

lui $t0, 0x1001
ori $a0, $t0, 60  #arg0 string buffer
ori $a1, $0, 32   #arg1 string length
addi $t5, $a1, 0
addi $v0, $0, 8
syscall

lui $t0, 0x1001
ori $a0, $t0, 23
addi $v0, $0, 4
syscall
j operator_loop

operator_loop:
sll $zero $zero 0
addi $s7, $s7, 1  #Op-Mode + 1
j read_string

read_string: 
sll $zero $zero 0
addi $t5, $0, -1     #Current index of inner loop, t5 is very important
j read_string_loop

_normalize:         #Used to check if a number is a stored value or invalid character
sll $zero $zero 0        
sll $zero $zero 0
slt $t0, $0, $s0
beq $t0, $0, number_jump  #if the stored number is valid, jump
jr $ra                    #if not, jump back into read_string_loop

read_string_loop:
sll $zero $zero 0
addi $t5, $t5, 1
slti $t4, $t5, 32          #if counter = 32, we are at the end of the max possible string
beq $t4, $0, read_string   #jumps back to index 0

lui $t0, 0x1001
addi $t3, $0, 60
add  $t3, $t3, $t5  
or $t4, $t0, $t3
lb $s0, 0($t4)
sll $zero $zero 0          #You will see a lot of stalls after load/stores   
sll $zero $zero 0          #For branch isntructions I usually use 2 stalls
sll $zero $zero 0          #For load/stores I usually use 3 stalls

beq $s0, $0, read_string_loop 
addi $t0, $0, 32
beq $s0, $t0, read_string_loop 

jal _normalize
sll $zero $zero 0
sll $zero $zero 0

slti $t6, $s0, 58 #should be 1 for a number  
slti $t7, $s0, 48 #should be 0 for a number
sub $t6, $t6, $t7 #if $t6 is 1, its a number

addi $t7, $s7, -5 
beq $t7, $0, print_result #if Op-Mode is 5, jump to print out the stored result  
addi $t0, $0, 1
beq $t6, $t0, number_jump 

addi $t0, $0, 42      #just general operator checking
beq $s0, $t0, _mul

addi $t0, $0, 47
beq $s0, $t0, _div

addi $t0, $0, 37
beq $s0, $t0, _mod

addi $t0, $0, 43
beq $s0, $t0, _add

addi $t0, $0, 45
beq $s0, $t0, _sub

addi $t0, $0, 61
beq $s0, $t0, _equal 
j error                #not one of these operators, its invalid

print_result:
sll $zero $zero 0
sll $zero $zero 0

lui $t0, 0x1001
addi $t3, $0, 60
add  $t3, $t3, $s6   
or $t4, $t0, $t3
lb $s0, 0($t4)       
sll $zero $zero 0     
sll $zero $zero 0
sll $zero $zero 0
addi $a0, $s0, 65 #grabs s0 which is loaded from number_loop. This value MUST
addi $v0, $0, 1   #be stored (non-literal) so conversion is necessary
syscall
j exit

found_op:
sll $zero $zero 0  #When an operator is found, the index is stored
addi $s5, $0, 1    #and register 21 is set to 1
addi $s4, $t5, 0 
j read_string_loop

_mul:                   #These following labels are to ensure we are in the correct
sll $zero $zero 0       #operator mode
beq $s7, $0, found_op
j read_string_loop

_div:
sll $zero $zero 0
addi $t0, $0, 1
beq $s7, $t0, found_op
j read_string_loop

_mod:
sll $zero $zero 0
addi $t0, $0, 2
beq $s7, $t0, found_op 
j read_string_loop

_add:
sll $zero $zero 0
addi $t0, $0, 3
beq $s7, $t0, found_op
j read_string_loop

_sub:
sll $zero $zero 0
addi $t0, $0, 4
beq $s7, $t0, found_op
j read_string_loop

error:              #Self-Explanatory, prints error message
sll $zero $zero 0
lui $t0, 0x1001 
ori $a0, $t0, 41
addi $v0, $0, 4
syscall
j exit

exit:              #Important to close program 
sll $zero $zero 0
sll $zero $zero 0
addi $v0, $0, 10
syscall 
jr $ra

number_jump:        
sll $zero $zero 0
beq $s5, $0, left_num       #If R21 is 0, then this must be a left number
beq $s7, $0, _mulop         #Otherwise we already have a left number so we can
addi $t0, $0, 1             #begin our operation
beq $s7, $t0, _divop
addi $t0, $0, 2
beq $s7, $t0, _modop
addi $t0, $0, 3
beq $s7, $t0, _addop
addi $t0, $0, 4
beq $s7, $t0, _subop
j exit            

_equal:
sll $zero $zero 0
slti $t6, $s5, 1            
addi $t0, $0, 1
beq $t6, $t0, operator_loop  #If we reached the equal sign without operating   
j read_string                #Go to next Op-Mode, otherwise reset R13


left_num:             #left_num exists but not right_num because
sll $zero $zero 0     #we jump to operation if we have a left_num, operator, and new value
addi $s6, $t5, 0 
j read_string_loop

_addop:               #WARNING: Your eyes will hurt because there is a lot of recycled
sll $zero $zero 0     #My older revision had a cleaner flow but became corrupt on my wonderful Linux machine
lui $t0, 0x1001        
addi $t3, $0, 60
add $t4, $t3, $s4   
or $t4, $t0, $t4
addi $t1, $0, 32     #This is where some of the magic happens. At this point I'm replacing the operator
sll $zero $zero 0    #with a space so we won't revisit again
sb $t1, 0($t4)
jal _loadleftroutine 
sll $zero $zero 0    #Important to have stalls after a branch instruction, otherwise
sll $zero $zero 0    #there is a potential to skip the next jal
jal _loadrightroutine  
sll $zero $zero 0
sll $zero $zero 0
add  $t6, $s1, $s0  
addi $t6, $t6, 191  #loads the value back as a stored value where the old right operand was 
sb $t6,  0($t4)      
addi $s5, $0, 0                        
j read_string       #finished with the operation

_loadleftroutine:
sll $zero $zero 0
add $t4, $t3, $s6   
or $t4, $t0, $t4
lb $s1, 0($t4)
sll $zero $zero 0     #As mentioned in the description, load/store instructions usually get
sll $zero $zero 0     #get a healthy 3 stalls
sll $zero $zero 0
sb $t1,  0($t4)       #More magic, this time I'm erasing the operands
slt $t2, $0, $s1
beq $t2, $0, _leftload #If the number is a stored value, grab it
sll $zero $zero 0
sll $zero $zero 0 
addi $s1, $s1, -48     #If not, it's a literal and we can subtract 48
jr   $ra

_leftload:
sll $zero $zero 0
sll $zero $zero 0
addi $s1, $s1, 65    #Simple number conversion to grab the stored value
jr   $ra

_loadrightroutine:  #Identical to loadleftroutine except for the right operand
sll $zero $zero 0
add $t4, $t3, $t5   
or $t4, $t0, $t4
lb $s0, 0($t4)  
sll $zero $zero 0   
sll $zero $zero 0
sll $zero $zero 0
sb $t1,  0($t4)       
slt $t2, $0, $s0
beq $t2, $0, _rightload
sll $zero $zero 0
sll $zero $zero 0 
addi $s0, $s0, -48
jr   $ra

_rightload:       #right version of leftload
sll $zero $zero 0
sll $zero $zero 0
addi $s0, $s0, 65
jr   $ra

_mulop:
sll $zero $zero 0      #identical to addop
lui $t0, 0x1001
addi $t3, $0, 60
add $t4, $t3, $s4  
or $t4, $t0, $t4
addi $t1, $0, 32
sb $t1, 0($t4)
jal _loadleftroutine 
sll $zero $zero 0
sll $zero $zero 0
jal _loadrightroutine  
sll $zero $zero 0
sll $zero $zero 0
addi $t3, $0, 0  
addi $t6, $0, 0  
jal _mul_loop         #except for this loop
sll $zero $zero 0
sll $zero $zero 0

addi $t6, $t6, 191   
sb $t6,  0($t4) 
addi $s5, $0, 0                        
j read_string

_mul_loop:
sll $zero $zero 0         #simple mulitplication loop, store the rolling sum into R14
sll $zero $zero 0
beq $t3, $s0, _jumpra     #exits the loop then hops back to the mulop
sll $zero $zero 0
sll $zero $zero 0
add $t6, $t6, $s1
addi $t3, $t3, 1
j _mul_loop

_jumpra:           #used to exit operation loops
sll $zero $zero 0
sll $zero $zero 0
jr $ra


_subop:
sll $zero $zero 0   #identical to addop except subtraction
lui $t0, 0x1001
addi $t3, $0, 60
add $t4, $t3, $s4   
or $t4, $t0, $t4
addi $t1, $0, 32
sb $t1, 0($t4)
jal _loadleftroutine 
sll $zero $zero 0
sll $zero $zero 0
jal _loadrightroutine  
sll $zero $zero 0
sll $zero $zero 0  

sub  $t6, $s1, $s0    
addi $t6, $t6, 191   
sb $t6,  0($t4) 
addi $s5, $0, 0                          
j read_string

_divop:
sll $zero $zero 0  #similar to multiplication loop, except I subtract instead of add
lui $t0, 0x1001
addi $t3, $0, 60
add $t4, $t3, $s4   
or $t4, $t0, $t4
addi $t1, $0, 32
sb $t1, 0($t4)
jal _loadleftroutine 
sll $zero $zero 0
sll $zero $zero 0
jal _loadrightroutine  
sll $zero $zero 0
sll $zero $zero 0

addi $t2, $s1, 0   
addi $t6, $0, 0  
jal _div_loop
sll $zero $zero 0
sll $zero $zero 0

addi $t6, $t6, 191   
sb $t6,  0($t4) 
addi $s5, $0, 0                            
j read_string

_div_loop:   
sll $zero $zero 0  
sll $zero $zero 0        
slt $t1, $t2, $s0      
addi $t0, $0, 1
beq $t1, $t0, _jumpra   
sll $zero $zero 0
sll $zero $zero 0
sub $t2, $t2, $s0      
addi $t6, $t6, 1    
j _div_loop

_modop:
sll $zero $zero 0
lui $t0, 0x1001
addi $t3, $0, 60
add $t4, $t3, $s4  
or $t4, $t0, $t4
addi $t1, $0, 32
sb $t1, 0($t4)
jal _loadleftroutine 
sll $zero $zero 0
sll $zero $zero 0
jal _loadrightroutine  
sll $zero $zero 0
sll $zero $zero 0
addi $t2, $s1, 0    
addi $t6, $0, 0  
jal _div_loop       #I used the div_loop over again except instead of grabbing the result
sll $zero $zero 0   #the remainder is taken and stored
sll $zero $zero 0
addi $t6, $t2, 191   
sb $t6,  0($t4) 
addi $s5, $0, 0                           
j read_string
sll $zero $zero 0  #This part is really important. Without stalls
sll $zero $zero 0  #The branch above may try jumping into an address that cant be executed