1 ECE 473 Computer Architecture and Organization Lab 4: MIPS Assembly Programming Due: Thursday, October 10 (100 points) Objectives: Get familiar with MIPS instructions Assemble, execute and debug MIPS assembly programs What to hand in: 1. Assembly Programs Submit your project codes in one zip file to http://web.eece.maine.edu/hw/ 1. MIPS Instructions MIPS stands for Microprocessor without Interlocked Pipeline Stages. MIPS designs are used in many embedded systems such as Sony PlayStation 2, Cisco routers, and Windows CE devices. Since MIPS has greatly influenced later design of instruction sets and processors, MIPS will be the main assembly language used in ECE 473. The following shows a simple loop example in C. We can translate this simple loop into a MIPS program: 2. MIPS simulator running on MARS: SPIM MARS is an interactive software simulator for MIPS based on Java. You can download the latest version of MARS from here http://courses.missouristate.edu/KenVollmar/MARS/index.htm In Linux, in command line, run: java -jar Mars.jar In Windows, either double-click the Mars.jar file or run java -jar Mars.jar 2 The website also provides the following three sample programs: Fibonacci.asm, row-major.asm, column-major.asm 3. Quick Reference of MIPS Programming 3.1 Accessing Array list: .word 3, 0, 1, 2, 6, -2, 4, 7, 3, 7 la $t3, list # put address of list into $t3 lw $t4, 0($t3) # get the value list[0] lw $t4, 4($t3) # get the value list[1] lw $t4, 8($t3) # get the value list[2] 3.2 Conditional Statement Assume $r1 stores i and $r2 stores j. if ( i == j ) i++ ; j-- ; bne $r1, $r2, L1 # branch if ! ( i == j ) addi $r1, $r1, 1 # i++ L1: addi $r2, $r2, -1 # j if ( i == j ) i++ ; else j-- ; j += i ; bne $r1, $r2, ELSE # branch if ! ( i == j ) addi $r1, $r1, 1 # i++ j L1 # jump over else (ADD THIS!!!) ELSE: addi $r2, $r2, -1 # j-- L1: add $r2, $r2, $r1 # j += i if ( i == j && i == k ) i++ ; // if-body else j-- ; // else-body j = i + k ; bne $r1, $r2, ELSE # cond1: branch if ! ( i == j ) bne $r1, $r3, ELSE # cond2: branch if ! ( i == k ) addi $r1, $r1, 1 # if-body: i++ j L1 # jump over else ELSE: addi $r2, $r2, -1 # else-body: j-- L1: add $r2, $r1, $r3 # j = i + k if ( i == j | | i == k ) i++ ; // if-body else j-- ; // else-body j = i + k ; beq $r1, $r2, IF # cond1: branch if ( i == j ) bne $r1, $r3, ELSE # cond2: branch if ! ( i == k ) IF: addi $r1, $r1, 1 # if-body: i++ j L1 # jump over else ELSE: addi $r2, $r2, -1 # else-body: j-- L1: add $r2, $r1, $r3 # j = i + k 3 3.3 Loop Statement There's only one special case. Suppose thehas a continue statement. Then, you need to jump to the code, which is at the UPDATE label. Name Number Usage Name Number Usage $zero 0 constant 0 $s0-$s7 16 temporary saved $at 1 reserved for assembler $t8 24 temporary $v0 2 return value $t9 25 temporary $v1 3 return value $k0 26 reserved for OS kernel $a0 4 procedure argument 1 $k1 27 reserved for OS kernel $a1 5 procedure argument 2 $gp 28 pointer to global area $a2 6 procedure argument 3 $sp 29 stack pointer $a3 7 procedure argument 4 $fp 30 frame pointer $t0 8 temporary $ra 31 return address $t1-$7 9 temporary while ( i < k ){ k++ ; i = i * 2 ; } L1: if ( i < k ){ k++; i = i * 2; goto L1; } # $r1 = i and $r3 = k. L1: bge $r1, $r3, EXIT # branch if ! ( i < k ) addi $r3, $r3, 1 # k++ add $r1, $r1, $r1 # i = i * 2 j L1 # jump back EXIT: for ( ; ; ) { } L1: if ( ) { UPDATE: goto L1 ; } EXIT: switch( i ) { case 1: i++ ; // falls through case 2: i += 2 ; break; case 3: i += 3 ; } addi $r4, $r0, 1 # set temp to 1 bne $r1, $r4, C2_COND # case 1 false: branch to case 2 cond j C1_BODY # case 1 true: branch to case 1 C2_COND: addi $r4, $r0, 2 # set temp to 2 bne $r1, $r4, C3_COND # case 2 false: branch to case 2 cond j C2_BODY # case 2 true: branch to case 2 body C3_COND: addi $r4, $r0, 3 # set temp to 3 bne $r1, $r4, EXIT # case 3 false: branch to exit j C3_BODY # case 3 true: branch to case 3 body C1_BODY: addi $r1, $r1, 1 # case 1 body: i++ C2_BODY: addi $r1, $r1, 2 # case 2 body: i += 2 j EXIT # break C3_BODY: addi $r1, $r1, 3 # case 3 body: i += 3 EXIT: # j = i + k 4 3.4 Function Calls Calling a subroutine is between a caller, who makes the subroutine call, and the callee, which is the subroutine itself. The caller passes arguments to the callee by placing the values into the argument registers $a0-$a3. The caller calls jal followed by the label of the subroutine. This saves the return address in $ra. The return address is PC + 4, where PC is the address of the jal instruction If the callee uses a frame pointer, then it usually sets it to the stack pointer. The old frame pointer must be saved on the stack before this happens. The callee usually starts by pushing any registers it needs to save on the stack. If the callee calls a helper subroutine, then it must push $ra on the stack. It may need to push temporary or saved registers as well. Once the subroutine is complete, the return value is place in $v0-$v1. The callee then calls jr $ra to return back to the caller. The following give two examples of recursive function calls. Example 1: Sum all elements of an array. Assume arr is in $a0 and size is in $a1. int sum( int arr[], int size ) { if ( size == 0 ) return 0 ; else return sum( arr, size - 1 ) + arr[ size - 1 ] ; } sum: addi $sp, $sp, -8 # Adjust sp addi $t0, $a0, -1 # Compute size - 1 sw $t0, 0($sp) # Save size - 1 to stack sw $ra, 4($sp) # Save return address bne $t0, $zero, ELSE # branch ! ( size == 0 ) li $v0, 0 # Set return value to 0 addi $sp, $sp, 8 # Adjust sp jr $ra # Return ELSE: move $a1, $t0 # update second arg jal sum lw $t0, 0($sp) # Restore size - 1 from stack sll $t1, $t0, 2 # Multiple size - 1 by 4 add $t1, $t1, $a0 # Compute & arr[ size - 1 ] lw $t2, 0($t1) # t2 = arr[ size - 1 ] add $v0, $v0, $t2 # retval = $v0 + arr[size - 1] lw $ra, 4($sp) # restore return address from stack addi $sp, $sp, 8 # Adjust sp jr $ra # Return 5 Example 2: Sum only the odd elements of an array. The difficult part is to decide what registers to save. arr is stored in $a0, and it is not changed throughout. size may need to be saved, though size - 1 appears to be more useful. Since we make calls to sumOdd, we need to save $ra. So, let's save size - 1 and $ra to the stack. It turns out we also need to save arr[ size - 1 ] to the stack too. int sumOdd( int arr[], int size ) { if ( size == 0 ) return 0 ; else if ( arr[ size - 1 ] % 2 == 1 ) return sumOdd( arr, size - 1 ) + arr[ size - 1 ] ; else return sumOdd( arr, size - 1 ) ; } sumOdd: addi $sp, $sp, -12 # Adjust sp addi $t0, $a0, -1 # Compute size - 1 sw $t0, 0($sp) # Save size - 1 to stack sw $ra, 4($sp) # Save return address bne $t0, $zero, ELSE2 # branch !( size == 0 ) li $v0, 0 # Set return value to 0 addi $sp, $sp, 12 # Adjust sp jr $ra # Return ELSE2: sll $t1, $t0, 2 # Multiple size - 1 by 4 add $t1, $t1, $a0 # Compute & arr[ size - 1 ] lw $t2, 0($t1) # t2 = arr[ size - 1 ] andi $t3, $t2, 1 # is arr[ size - 1 ] odd beq $t3, $zero, ELSE3 # branch if even sw $t2, 8($sp) # save arr[ size - 1 ] on stack move $a1, $t0 # update second arg jal sumOdd lw $t2, 8($sp) # restore arr[ size - 1 ] from stack add $v0, $v0, $t2 # update return value lw $ra, 4($sp) # restore return address from stack addi $sp, $sp, 12 # Adjust sp jr $ra # Return 6 4. Quick Reference of Mars 4.1 User interface Pictures are obtained from http://courses.missouristate.edu/KenVollmar/MARS 7 4.2 Comments and directives “#” starts a comment .text is a directive. A directive is a statement that tells the assembler something about what the programmer wants, but does not itself result in any machine instructions. This directive tells the assembler that the following lines are ".text" - - source code for the program. .globl main is another directive. It says that the identifier main will be used outside of this source file (that is, used "globally") as the label of a particular location in main memory (here, it is 0x00400020. The address of the 1 st instruction). .data starts the data segment. All of these are used in the data segment: .asciiz string Defines a null-terminated string (strings you output with a syscall must end with a null, which is a 00 in ascii) .byte b0,b1,b2 Defines and initializes subsequent bytes in memory .half h0,h1,h2 Defines and initializes subsequent half-words (16-bit values) in memory – alignment forced to next even address .word w0,w1,w2 Defines and initializes subsequent words (32-bit values) in memory – alignment forced to next word address (multiple of 4) .space n allocates n bytes of space Identifier names are sequence of letters, numbers, underbars (_) and dots (.). Labels are declared by putting them at beginning of line followed by colon. Use labels for variables and code locations. Instruction format: op field followed by one or more operands: addi $t0, $t0, 1 Operands may be literal values or registers. Register is hardware primitive, can stored 32-bit value: $s0 Numbers are base 10 by default. 0x prefix indicates hexadecimal. Strings are enclosed in quotes. May include \n=newline or \t=tab. Used for prompts. 4.3 Endianness Endianness is the byte ordering used to represent data. The two orders are called "Little Endian" and "Big Endian". Little Endian means that the low-order byte of the number is stored in memory at the lowest address, and the high-order byte at the highest address. (The little end comes first.) For example, a 4 byte LongInt Byte3 Byte2 Byte1 Byte0 will be arranged in memory as follows: Base Address+0 Byte0 Base Address+1 Byte1 Base Address+2 Byte2 Base Address+3 Byte3 Intel processors (those used in PC's) use "Little Endian" byte order. 8 Big Endian means that the high-order byte of the number is stored in memory at the lowest address, and the low-order byte at the highest address. (The big end comes first.) Our LongInt, would then be stored as: Base Address+0 Byte3 Base Address+1 Byte2 Base Address+2 Byte1 Base Address+3 Byte0 Motorola processors (those used in Mac's) use "Big Endian" byte order. 4.4 Data Storage in Memory For ASCII, every character is represented by its corresponding ASCII code (a byte). For example, str: .asciiz “the answer = ” You will see the following in PCSPim (in a system with little endianness). [0x10010000] 0x20656874 0x77736E61 0x3D207265 0x00000020 The data represents: Address Data(hex) 1001000F 00 undefined 1001000E 00 undefined 1001000D 00 1001000C 20 1001000B 3D = 1001000A 20 10010009 72 r 10010008 65 e 10010007 77 w 10010006 73 s 10010005 6E n 10010004 61 a 10010003 20 10010002 65 e 10010001 68 h 10010000 74 t Here is another example: #specify some data to be loaded into memory .data nums: .byte 1,2,3,4 .half 5,6,7,8 .word 9,10,11,12 .space 5 .word 9,10,11,12 .asciiz “ABCD” 9 .ascii “ABCD” .byte -1 .text .globl main main: li $v0,10 #sys call for exit syscall After loading the program, the data display would show: DATA [0x10010000] 0x04030201 0x00060005 0x00080007 0x00000009 [0x10010010] 0x0000000A 0x0000000B 0x0000000C 0x00000000 [0x10010020] 0x00000000 0x00000009 0x0000000A 0x0000000B [0x10010030] 0x0000000C 0x44434241 0x43424100 0x0000FF44 Address Data 1001003C 0000FF44 10010038 43424100 10010034 44434241 10010030 0000000C 1001002C 0000000B 10010028 0000000A 10010024 00000009 10010020 00000000 1001001C 00000000 10010018 0000000C 10010014 0000000B 10010010 0000000A 1001000C 00000009 10010008 00080007 10010004 00060005 10010000 04030201 10 ECE 473 Computer Architecture and Organization Fall 2013 Lab Assignment 4 1. String handling [30 points] Declare a string in the data section: .data string: .asciiz "ABCDEFG" Write a program that converts the string to all lower case characters. Hint: add 0x20 to each character in the string. 2. Pointers [30 points] Write a program in MIPS assembly language that will compute the sum of all the elements in an array. Write this program using a function “PSum”, which takes two parameters, including a pointer to the running sum and a pointer to the current element. The “C” function looks like this: int sum = 0; int *sump = ∑ int a[10]; void PSum(int *s, int *e) { *s += *e; } int main() { for (int i=0; i<10; i++) { a[i] = i; } for (int i=0; i<10; i++) { PSum(sump, a+i); } } 3. Recursion [40 points] Write a program in MIPS Assembly Language that to find fix(i, x), where fix(i, x) is defined recursively as: int fix(int i, int x) { // assume i > 0, x > 0 if (x>0) return fix(i, x-1); else if (i>0) return fix(i-1, i-1)+2; else return 0; } 11 ECE 473 Lab Check off Sheet Lab 4: MIPS Assembly Programming Fall 2013 Student Name: _______________________________ TA: ___________________________________ Time & Date: ________________________ 1. String Manipulation (30%) 2. Pointers (30%) 3. Recursion (40%) Total