# # Assignment #2 # T.A. version # Coded by Philippe Chaintreuil # (pecst13+@pitt.edu) # # This program is a simple "calculator". It asks the user to choose # from a list of functions (multiply, factorial, or exponent) and then # gets input from the user for the function. It executes the functions # (multiply and factorial recursively and exponent iteratively), and # displays the answer, and redisplays the menu. # ####################### Strings for use in output. ###################### .data # This is the menu which will be displayed and asks for the user's # input. SPIM notices the newlines that are in the string, so the menu # will output just as you see it below. No \n's are needed. Although # notice that there will be one newline printed before the first bit of # text because the first line of text is a line below the opening double- # quote. Also notice that the cursor will be left after the arrow but on # the same line because there is no [unnecessary] newline at the end. str_menu: .asciiz " Press 1 for multiply Press 2 for factorial Press 3 for exponent Press 4 to quit Your choice? ===> " # This is the error message that will be outputted if the user # inputs a value which is out of the menu range. The \n's will print # extra blank lines on either side (vertically) to offset it from the # rest of the text. str_errorMsg: .asciiz "\nPlease enter a menu choice.\n" # These are the prompts to get "n" and "m" from the user. # Notice how there are no extra \n's. Each of these lines will start on # its own line because when the user presses after entering the # last choice (the menu or the "Enter n" prompt) moves the cursor to the # next line. There is no trailing \n to keep the cursor on the same line # as the prompt. str_EnterN: .asciiz "Enter n ========> " str_EnterM: .asciiz "Enter m ========> " # String to announce the answer. No \n's for the above reasons. (Well, # there's no trailing \n to keep the cursor on the same line for printing # the result on the same line.) str_answer: .asciiz "The answer is " # Newline string - it comes in handy sometimes. (ie making sure the # program ends on a new line so the command prompt isn't someplace funny.) str_newline: .asciiz "\n" .text ###################### "Main" of the program ########################### main: # The program hops back up here quite often. la $a0, str_menu li $v0, 4 # Print out the menu. syscall li $v0, 5 # Get the user's menu choice by reading in an int syscall # and save it to $t0. A $t register can be used move $t0, $v0 # because no functions will be called before the # value of the input no longer matters. (It # would also be acceptable to use an $s register # in this case.) #################### # $t0 = MenuChoice # #################### bne $t0, 4, DoNotExit # if (MenuChoice == 4) then exit # else skip ahead to "DoNotExit" li $v0, 10 syscall # Make the exit program syscall. DoNotExit: # The choice was not to exit, so make sure sgt $t1, $t0, $zero # that the choice was in the range of choices slt $t2, $t0, 4 # if ((MenuChoice > 0) && (MenuChoice < 4)) then and $t1, $t1, $t2 # MenuChoice is a valid input # else MenuChoice is a bad input - error! bne $t1, $zero, valid_input # If you got here, they entered a bad input. Output an error # message and jump back up to main/the menu. la $a0, str_errorMsg li $v0, 4 # Print the error message. syscall j main # Hop back up to main/the menu. valid_input: # If we got here, the input must be valid (MenuChoice is between 1 # and 3 (4 was already taken care of)). Get the value(s) for the function # to work on. la $a0, str_EnterN li $v0, 4 # Prompt the user for "n". syscall li $v0, 5 # Get the input from the user and store it into syscall # $t1 until it can be passed to the function. move $t1, $v0 #################### # $t0 = MenuChoice # # $t1 = n # #################### beq $t0, 2, skipM # If (MenuChoice == 2) then the function # will be factorial, so we don't need to ask for # an "m" - skip ahead. Otherwise, ask for "m". la $a0, str_EnterM li $v0, 4 # Prompt for "m". syscall li $v0, 5 # Get an integer from the user ("m") and place it syscall # straight into the register for the 2nd argument move $a1, $v0 # for the function call. skipM: move $a0, $t1 # Throw "n" into the 1st argment register. #################### # $t0 = MenuChoice # # $a0 = n # $a1/m can hold garbage that won't matter if # $a1 = m # MenuChoice == 2 (Factorial). #################### bne $t0, 1, NotMult # If (MenuChoice == 1) multiply(n, m) # else skip to next test (NotMult). # Okay, this section of code is just an optimization. It's not at all # needed. If it confuses you, that's okay, just jump ahead to the line # that says "jal multiply". The optimization would make more sense in the # multiply function, but I don't want to have it there in case it confuses # people. Actually, it would best go in a wrapper function that would # then call the multiply function that appears later. # The optimization is based on the fact that more recursive calls # are needed, the larger $a1/m is. So the optimization is simply to make # sure $a1/m is less than or equal to $a0/n. In a iterative world, the # the optimization would only save time, but in the recursive world # it also saves memory (as the stack doesn't have to grow as many times). slt $t0, $a0, $a1 # if (n < m) beq $t0, $zero, NoSwap # { // swap n and m. move $t0, $a0 # temp = n; move $a0, $a1 # n = m; move $a1, $t0 # m = temp; # } NoSwap: jal multiply # Call the multiply function. j answer # Jump to the answer processing section. NotMult: bne $t0, 2, IsExp # if (MenuChoice == 2) Factorial(n, m) # else IsExp jal factorial # Call the Factorial function. j answer # Jump down to the answer processing. IsExp: # It must be exponent. jal exponent # Call the Exponent function. # Fall through to answer processing. answer: move $t0, $v0 # Get the answer and throw it in $t0. ################ # $t0 = answer # ################ la $a0, str_answer li $v0, 4 # Print out "The answer is ". syscall move $a0, $t0 li $v0, 1 # Print the answer. syscall la $a0, str_newline li $v0, 4 # Print an extra newline to space the menu syscall j main # Loop back up, to do it all over again. ## END OF MAIN ## ######################### Functions ######################### # Multiply takes two arguments and multiplies them together # recursively, returning its results in $v0 as is standard practice. # The function will loop infinitely if a negative number is entered for # "b". # # C/C++ prototype for this function: # int multiply(int a, int b); # $v0 $a0 $a1 # # Formula/algorithm: # multiply(a, b) = a + multiply(a, b - 1) # multiply(a, 0) = 0; # # NOTE: because of the nature of the algorithm it is faster and more # memory efficient to have "b" be the smaller of the two numbers. # If "a" is smaller, the results will be the same but they might take an # extra millisecond or two to get. # multiply: bne $a1, $zero, NotMultBaseCase # if (b == 0) return 0; # else skip to NotMultBaseCase move $v0, $zero # return 0; jr $ra # send control back. NotMultBaseCase: # We'll have to make the recursive call. Therefore, we should throw # anything we're going to overwrite onto the stack, so we don't lose it. # Then, decrement "b" ($a1) and recursively call multiply. Add the result # of that to "a" ($a0), and return that answer. Also, we have to get the # stuff back off the stack. sub $sp, $sp, 12 # Make room on the stack. sw $a0, 0($sp) # Throw "a" on. sw $a1, 4($sp) # Throw "b" on. sw $ra, 8($sp) # Put the return Address on. sub $a1, $a1, 1 # Decrement "b" to be passed to the # recursive call. (The old "b" is safe # on the stack.) jal multiply # Make the recursive call. # $v0 = multiply(a, b - 1); add $v0, $a0, $v0 # $v0 = a + $v0; # That puts the result of multiply(a, b-1) # + "a" into the return register ($v0) lw $a0, 0($sp) # Get "a" back off the stack. lw $a1, 4($sp) # Get "b" back off the stack. lw $ra, 8($sp) # Get the return address off the stack. add $sp, $sp, 12 # Free back up the memory from the stack. jr $ra # return the result and control. ##### END OF multiply ##### # Factorial takes a single input and returns the factorial of that # number. It uses the multiply function in this program instead of the # mult instruction. # # C/C++ prototype for this function: # int factorial(int n); # $v0 $a0 # # Formula/algorithm: # factorial(n) = multiply(n, factorial(n - 1)) # factorial(0) = 1 # # factorial: bne $a0, $zero, NotFactBaseCase # if (n == 0) return 1; # else skip to NotFactBaseCase; li $v0, 1 # return 1; jr $ra # return control. NotFactBaseCase: # It's not the base-case so we have to return n * (n-1!). So we'll # throw "n" and the return address on the stack, make a recursive call of # (n-1)! and return that multiplied by "n" (which will be safely on the # stack. sub $sp, $sp, 8 # Make room on the stack. sw $a0, 0($sp) # Throw $a0/n on the stack. sw $ra, 4($sp) # Throw the return address on the stack. sub $a0, $a0, 1 # (n-1) jal factorial # make the call: factorial(n-1) move $a0, $v0 # Move the result ($v0) to be passed # to multiply(). lw $a1, 0($sp) # And put n in the other argument # register (get it off the stack). jal multiply # make the call: multiply((n-1)!, n) # This will place the result in $v0 all # set to be returned. lw $a0, 0($sp) # Get $a0/n back off the stack. lw $ra, 4($sp) # Get the return address off the stack. add $sp, $sp, 8 # Free the stack memory back up. jr $ra # return the result and control. ##### END OF factorial ##### exponent: # Exponent takes two integer inputs and finds the first raised to # the power of the second. The second (the power) must be zero or # greater, or the result will be incorrect. # # C/C++ prototype: # int exponent(int base, int power); # $v0 $a0 $a1 # # Formula/Algorithm: # exponent(base, 0) = 1 # exponent(base, power) = # base * base * base * ... (power number of times) # sub $sp, $sp, 16 # Make room on the stack. sw $a0, 0($sp) # Store base on the stack at 0($sp) sw $a1, 4($sp) # Store power on the stack at 4($sp) sw $s0, 8($sp) # Store $s0 on the stack because we're going to # use it. sw $ra, 12($sp) # Store the return address on the stack. move $s0, $a1 # Move power to $s0 (it will be used to count down # in the loop.) li $v0, 1 # set the result to 1 (result = 1) # C++ equivalent of the loop below: # while (power > 0) # { # result *= base; # power--; # } exponentLoop: sgt $t0, $s0, $zero # while (power > 0) don't quit the loop. beq $t0, $zero, ExpLoopExit # power must be greater than zero. ################################### # $v0 = result # # $a0 = result being passed # # into multiply # # $a1 = base being passed # # into multiply # # $s0 = power which is being # # decremented until it # # reaches zero. # # 0($sp) = pointer to where base # # is saved on the stack. # ################################### move $a0, $v0 # $a0 = result lw $a1, 0($sp) # $a1 = base jal multiply # result = multiply(result, base); sub $s0, $s0, 1 # power--; j exponentLoop # Hop back up, and test for the loop again. ExpLoopExit: # power must be less than or equal to zero, # and our result should be hanging out in $v0 - # all ready to be returned. lw $a0, 0($sp) # Get the base back off the stack. lw $a1, 4($sp) # Get the power back off the stack. lw $s0, 8($sp) # Replace whatever the user had in $s0 lw $ra, 12($sp) # Get back the return address from the stack. jr $ra # Return the result and control. ##### END OF exponent ##### ##### END OF PROGRAM! #####