CS/COE 447 Programming Assignment 2 Due February 26, 2007 ================================== Note: Your programs should be submitted via e-mail to your grader (mhanna@cs.pitt.edu). Make a single zip file of both files you are submitting. The subject line of your message should be 447: Programming Assignment 2 -- . The stack is used for various things. For subroutines, it is used to store local variables, passed arguments (when $a0-$a3 are not sufficient), returned values (when $v0 and $v1 are not sufficient), and for the return address (for subroutines that call other subroutines). The stack pointer $sp points to the element that is currently on the top of the stack. The stack grows downward in memory (toward lower numbered addresses). In Mars, $sp is initialized to 0x7fffeffc, perhaps to simulate the situation in which other elements are already on the stack. (On a MIPS architecture, the stack starts higher in memory, at 0x7fffffc.) To push the value in $a0 onto the stack: addi $sp,$sp,-4 sw $a0,0($sp) To pop the top of the stack into $v0: lw $v0,0($sp) addi $sp,$sp,4 =============== 1.[30pts] (Practice with the stack) Assign values to $t0, $t1, $t2, $t3, and $t4. Use the stack to reverse these values (i.e., the value that was in $t0 is now in $t4; the value that was in $t1 is now in $t3; and so on). Your program should print the values of the registers before you reverse them, and then print the values after you reverse them. For example, suppose you have assigned the values 10,11,12,13, 14 to $t0..$t4. Your program should print the following: $t0 ~ $t4 contain 10 11 12 13 14 $t0 ~ $t4 contain 14 13 12 11 10 =============== 2. [70pts] For this question, you will write a recursive factorial function, where factorial(N) = N * N-1 * ... 2 * 1 and factorial(0) is 1. The argument N should be passed to the function in $a0, and the value should be returned back in $v0. If factorial is called with a negative number, it should return -1. Your main program should load a 1-word integer, N, from memory and call factorial(N). If the value returned from factorial is negative, then your program should print: Error: fact was called with a negative number, but fact(N) is defined only for N >= 0 If the value returned from factorial is >= 0, then your program should print a message specifying N and factorial(N). For example, if N is 6, your program should print: Factorial of 6 is 720 A "function frame" or an "activation record" of a subroutine is a data structure in the stack that contains, if any, saved argument registers, saved return address, saved "saved" registers, and variables that are local to the subroutine (see page 86 in the text, though you don't need to use $fp). For function factorial, your activation record will need to contain only the saved argument and the saved return address. Make sure you understand why the argument and return address must be saved on the stack! Step through your code in the simulator so you appreciate what's going on. =============== 3. Given the size of the class and his alloted time commitment for the class, the grader cannot grade 3 programs for this assignment. Though we are limited in person-power for grading (We need a 'grade-grinder' as we have for CS1502!) you still need this experience to get the needed understanding and skill in assembly language programming. So, this is a self-study question; there is nothing to hand in. Below is a program with subroutines that do not follow the rules for preserving registers across subroutine calls. If you comment out the subroutine calls, then the main-program code works. But if you keep them in, the main program does not work anymore. Your task is to fix the subroutine calls so they "follow the rules" so that both the main code and the subroutines work (see the in-line comments for more details). Notes on rules for preserving registers across subroutine calls: Look at the table describing the registers on your Green Card. The rightmost column is labeled "Preserved Across a Call?" and is filled with Yes'es and No's. Let's first consider the caller: if a register R has Yes in this column, than the caller can call any subroutine (which follows the rules), and know that R's value will be the same before and after the call. If the register is listed as No, then the caller cannot assume that the register values will not be changed by the subroutine. Now consider the callee (i.e., a subroutine called from the main program or another subroutine): if a register is No, then the callee can go ahead and use that register without saving and restoring its value. But if it is Yes, the callee needs to save the initial value on the stack before it uses the register, and then restore the original value from the stack before it returns. We saw this in question 2 with $a0 and $ra. .data nums: .word 0x5,0x55,0x44,0x77,0x88,0x99 array1: .word 8, 100, 4, 15, 1, 102, -3, -8, 6 #the array to be sorted #static output strings follow str1: .asciiz "The array1 has " str2: .asciiz " elements.\r\n" str3: .asciiz "They are " str4: .asciiz "." str5: .asciiz ", " .text # # The main program uses the stack to reverse the values stored in # array nums. Subroutine selectionSort is repeatedly called on # array1, while we are pushing and popping the elements of nums on the stack. # This doesn't make much sense, but allows us to demonstate the # register calling-convention issues question 3 is about. # # Start by commenting out the calls to selectionSort and printArray, # assemble and run the program, and make sure you understand what the # main program does. # # Then, uncomment the calls to selectionSort and printArray, and # assemble and run the program. You'll see that the subroutines still # work, but the main program doesn't work anymore. # # Now modify the subroutine(s) so that they follow the calling # conventions. Don't change anything in the main program. In your # final version, both the main program code and the subroutines should # work. # main: la $s0,nums lw $s1,0($s0) #$s1 number of elements in array nums addi $s0,$s0,4 #$s0 base address of the actual array nums li $s2,0 pushes: beq $s2,$s1,pops lw $t0,0($s0) addi $sp,$sp,-4 sw $t0,0($sp) #Take a break, and call selection sort la $a0, array1 #load the address of "array1" into parameter0 #jal selectionSort #call the selection sort procedure la $a0, array1 #load the address of "array1" into parameter0 #jal printArray #call the printarray procedure #Now, back to pushing array nums onto the stack addi $s2,$s2,1 addi $s0,$s0,4 j pushes pops: la $s0,nums lw $s1,0($s0) #$s1 number of elements in array nums addi $s0,$s0,4 #$s0 base address of the actual array nums li $s2,0 popsloop: beq $s2,$s1,finish lw $t0,0($sp) addi $sp,$sp,4 sw $t0,0($s0) #Take a break, and call selection sort la $a0, array1 #load the address of "array1" into parameter0 #jal selectionSort #call the selection sort procedure la $a0, array1 #load the address of "array1" into parameter0 #jal printArray #call the printarray procedure #Now, back to popping the stack into array nums addi $s2,$s2,1 addi $s0,$s0,4 j popsloop finish: # #exit from the program # li $v0,10 syscall selectionSort: move $s0, $a0 #load the array into $s0 lw $s1, 0($s0) #$s1 is the number of elements in the array add $s3, $zero, $s1 #$s3 = curTop, $s1 = numElems loop: addi $s6, $zero, 1 #s6 is maxpos, maxpos = 1 addi $s5, $zero, 1 #$s5=i, i = 1 inner: sll $t2, $s5, 2 #multiply i by 4 to get the offset of array[i] sll $t3, $s6, 2 #multiply maxpos by 4 to get the offset of array[maxpos] add $t2, $t2, $s0 #add the offset of array[i] to the base addr of arry add $t3, $t3, $s0 #add the offset of array[maxpos] to the base addr of arry lw $t2 0($t2) #retreive the value stored in array[i] lw $t3 0($t3) #retreive the value stored in array[maxpos] slt $t4, $t3, $t2 #if array[maxpos] < array[i], $t4 = 1 beq $t4, 1, assign #if array[maxpos] < array[i], set maxpos=i j endassign #if we don't need to set maxpos=i, jump past that instruction assign: addi $s6, $s5, 0 #set maxpos = i endassign: beq $s3, $s5, exitinner #if i = curtop we are done, go to exitinner addi $s5, $s5, 1 #increment i j inner #restart the inner loop exitinner: sll $t5, $s6, 2 #multiply maxpos by 4 to get the offset of array[maxpos] add $t9, $s0, $t5 #t9 = array[maxpos] address addi $sp,$sp,-4 # remove this! for their sample code sw $a0,0($sp) addi $sp,$sp,-4 # remove this! for their sample code sw $a1,0($sp) move $a0,$t9 sll $t3, $s3, 2 #multiply curtop by 4 to get the offset of array[curtop] add $t2, $s0, $t3 #t2 = array[curtop] address move $a1,$t2 addi $sp,$sp,-4 # remove this! for their sample code sw $ra,0($sp) jal swap lw $ra,0($sp) addi $sp,$sp,4 lw $a1,0($sp) addi $sp,$sp,4 lw $a0,0($sp) addi $sp,$sp,4 beq $s3, 1, endsort #if curtop = 1 we are finished, jump to exit addi $s3, $s3, -1 #decrement curTop j loop ##iterate over all elements in the array up to curtop ($s3) endsort: jr $ra swap: ## $a0 and $a1 contain addresses of memory locations. Swap swaps their values. lw $t0,0($a0) # make these s's for your sample code !!!!!!! lw $t1,0($a1) sw $t1,0($a0) sw $t0,0($a1) jr $ra printArray: #prints the results move $s0, $a0 li $v0, 4 #prints str1: "The array has" la $a0, str1 syscall lw $s1, 0($s0) li $v0, 1 move $a0, $s1 #prints the number of elements syscall li $v0, 4 #prints str2: "elements." la $a0, str2 syscall li $v0, 4 #prints str3: "They are" la $a0, str3 syscall addi $t1, $zero, 1 addi $t2, $s1, 1 exitloop: #prints all of the elements in the array beq $t1, $t2, endPrintArray #if we are done looping, jump to endPrintArray sll $t6, $t1, 2 add $t4, $s0, $t6 lw $t5, 0($t4) li $v0, 1 move $a0, $t5 #prints an element in the array syscall addi $t1, $t1, 1 beq $t1, $t2, endPrintArray li $v0, 4 #prints str5: ", " la $a0, str5 syscall j exitloop endPrintArray: li $v0, 4 #prints str4: "." la $a0, str4 syscall jr $ra #procedure is complete, return to calling address