190 likes | 362 Views
Chapter 2c: Branch and Jump Details, Subroutines. Branch limitations. A conditional branch (BEQ, BNE) uses an I-type Instruction Allows a 16-bit immediate operand 2 16 = 64K Locations is too small. Notice that most branches don’t go very far away (back to the beginning of a loop, etc.)
E N D
Chapter 2c: Branch and Jump Details, Subroutines
Branch limitations • A conditional branch (BEQ, BNE) uses an I-type Instruction • Allows a 16-bit immediate operand • 216 = 64K Locations is too small • Notice that most branches don’t go very far away (back to the beginning of a loop, etc.) • UsePC-relativeaddresses for branches • Immediate operand is a displacement in 4-byte words from the next instruction • May be positive (forward branches) or negative (backwards branches) WARNING: BEQ,BNE example in text canbe misleading. Make a note in text to see slide Ch3c-3.
8000 loop: slt $1, $8, $9 8004 bne $1, $0, bigger 8000 0 8 9 1 0 42 8004 5 1 0 +3 8008 35 8 16 0 8012 8 8 8 1 8016 4 16 17 -5 8020 0 8 0 18 0 32 Branches: offset is in instructions fromthe instruction following the branch Branch example loop: -5 instructions +3 loop: bgt $t0, $t1, bigger lw $s0, 0($t0) addi $t0, $t0, 1 beq $s0, $s1, loop bigger: move $s2, $t0 8008 lw $16, 0($8) 8012 addi $8, $8, 1 -5 8016 beq $16, $17, loop 8020 bigger: add $18, $8, $0 bigger: + 3 instructions Note: Negative numbers require 2’s complement WARNING: BEQ,BNE example inside text canbe misleading. Make a note in text to see slide Ch3c-3.
2004 2000 0 2 4 1 0 32 2 501 Jump Addressing • Jumps use a J-type instruction and have a 26-bit offset • A lot more range than 16-bit branches • Allows a range of 226=64M possibilites • Because the range is so much larger, jumps use absolute addressing • Absolute addressing just specifies the address of the jump target • Since all instructions have addresses that are multiples of 4, we only need to put the address divided by 4 in the instruction field • Actual byte address is obtained by multiplying by 4 • Allows a range of 64M instructions = 256M bytes Example: 2000: j target 501 = 2004/4 2004: target: add $1, $2, $4
Jumping based on Registers • Jumps change the PC to a fixed location • j OverThere • Always goes to the same place • Is there a way to jump to a place to be determined by the program? • Put the address to go to in a register • addi $14, $0, 10000 • Jump to the address contained in that register • jr $14 JR - Jump Register
---- ---- $8: 31 16 15 0 Lui, Lui • Registers can hold 32 bits, but we can only load in 16-bit immediate data • LUI - Load Upper Immediate • Loads constant into bits 31-16 of register • Bits 15-0 become zero Put the constant with value 0x1A2B3C4D in register $8 Warning: Don’t use addi for the second instruction – use LI or ORI (sign extension) lui $8, 0x1A2B li $8, 0x3C4D We can let the assembler do the work:li $8, 0x1A2B3C4D will be converted to these two instructions 1A2B 0000 3C4D
Getting around branch limitations Branches have a limit of +/- 215 instructions - How can we branch further? 1000: bne $s0,$t1,stay 1004: j far 1008: stay: sub $s2, $v0, $a0 … 500000: far: add $t1, $t2, $t3 1000: beq $s0,$t1,far 1004: sub $s2, $v0, $a0… 500000: far: add $t1, $t2, $t3 Can’t branch that far... Use the larger range of the jump instruction:
Getting around jump limitations Jumps can address anywhere in the range 0 - 226 words (0 - 228 bytes) MIPS has an address space of 0 - 232 bytes... How can we get to programs in the top 15/16 of memory? Use 32-bit range of jr 1000: lui $t1, 0x31B9 1004: li $t1, 0x28F0 1008: jr $t1 … 834218224: reallyfar: move $t1,$s1 1000: j reallyfar… 834218224: reallyfar: move $t1,$s1 Can’t jump there... 83421822410=31B928F016 Aside: Can we use regular jumps with addresses >= 256MB? Yes: The high-order four bits are unchanged by the jump instruction
Subroutines • Subroutines allow common program portions to be reused • int CalcInterest(int principle, int time) { ... } • y = CalcInterest(1000, 10) • z = CalcInterest(1000, 15) • When calling a subroutine, you must: • Set up any parameters • Save the current position so the program will know where to return to • Jump to the location of the subroutine
Jumpin’ and Linkin’ • JAL - Jump and Link • Saves the return address • Write address of next instruction in $31 ($ra) • Jumps to the address specified and keeps going Call the Calc subroutine Save addr. of next instr. in $ra ($31) 100: jal CALC 104: sub $4, $4, $2 ... 342: CALC: add $14,$15,$2 346: mult $14, $2 ... 368: jr $ra 104 $ra Start of Subroutine End of Subroutine. Return to ReturnAddress in $ra
Passing Parameters • Most subroutines have parameters to make them more interesting... • Z = Addemup(A,B,C) • In order to pass parameters, we must place the parameters in registers before calling the subroutine • $a0, $a1, $a2, $a3 used for these ($4 - $7) • $a0 <-- A, $a1 <--B, $a2 <--C • Before calling a routine, copy all parameters to the appropriate registers • Return values are put in $v0, $v1 ($2, $3) • $v0 <-- Z • After subroutine finished, copy return values back
Example – Check the weight of various packages; Output message if >600 lbs. .data numItems: .word 8 weight: .word 999, 688, 222, 1002, 333, 2, 888, 14000 over: .asciiz " is too heavy.\n" .text # $t0 is loop counter, $t1 holds ending value main: li $t0,0 # initialize $t0 to 0 lw $t1,numItems($0) # load number of items into $t1 mul $t1,$t1,4 # convert to bytescheck: lw $a0, weight($t0) # load current weight jal validate # check for overweight addi $t0,$t0,4 # increment loop counter bltu $t0,$t1,check # loop back if not done # exit program li $v0,10 # code for exit syscall # exit program # validate checks for overweight loads. Weight to check is passed in $a0. validate: blt $a0, 600, OK # check if value is < 600 li $v0,1 # code for print_int syscall # print out result (in $a0) li $v0,4 # code for print_string la $a0,over # point $a0 to over string syscallOK: jr $ra # return to caller Data: List of 8words containingweights of boxes Find number ofwords to check andconvert to bytes Get current weightand put in param.$a0 Check it by callingvalidate procedure Advance to nextweight and checkif at the end End Program Check if weight(in param. $a0) is less than 600 Overweight -Print error msg Done withsubroutine
Higher Memory Addresses Memory A Stack Free Memory ... ... 2000 876 Top of Stack 2004 23 111 2008 2008 2212 2004 Values already onstack 2012 2323 $sp 2008 2016 12 2020 22 $sp($29) points to the top of the stack 2024 928832 To push a new value on: Decrement $sp (by 4) and write to memory 2028 32 2032 134 ... ... lw $t0, 0($sp) # read the top of the stack Read Top addi $sp, $sp, -4 # decrement stack for push sw $t0, 0($sp) # push reg. $t0 onto stack $t0:111 Push $t0 (111) lw $t0, 0($sp) # pop stack into reg. $t0 addi $sp, $sp, 4 # increment stack for pop Pop into $t0
A bit of a problem • Some wise guy (or gal) is going to decide to call a procedure from another procedure • When we JAL the second time, we overwrite the value in $ra • We’re lost now... Somehow, we’ve got to save the old R.A. for future use. ... jal procA addi $3,$4,5 ... procA: li $a1,1 jal procB move $t0,$v0 jr $ra procB: move $t1,$a1 jr $ra $ra:
2000 25 2004 342 2008 221 2012 965 2016 2 Stacking things up • If we’re going to a new subroutine, save the old $ra value on the stack first • This is the primary reason for the system stack • Push the old RA on the stack... • 8112 addi $sp, $sp, -4 • 8116 sw $ra, 0($sp) • 8120 jal B • 8124 lw $ra, 0($sp) • 8128 addi $sp, $sp, 4 Adjust the SP first Now, store old RA on stack Call the subroutine Restore the RA Re-adjust the SP 16308 16308 8124 2008 $ra 2004 16308 $sp 2008 2004
Modify validate subr. to also check if the weight is odd # validate checks for overweight loads. Weight to check is passed in $a0. validate: blt $a0, 600, WeightOK # if value < 600 then OK # print out an error message<code deleted to save space on slide> WeightOK: addi $sp,$sp,-4 # decrement stack pointer by 4 sw $ra, 0($sp) # push RA on stack jal checkOdd # now check if odd (return value in $v0) lw $ra, 0($sp) # pop RA off stack addi $sp, $sp, 4 # increment stack pointer by 4 beqz $v0, OK # if weight is even, OK # print out an error message <code deleted to save space on slide> OK: jr $ra # return to caller # checkOdd checks if weight is Odd. Weight passed in $a0. $v0 is 0 if even, 1 if odd checkOdd: rem $v0, $a0, 2 # $v0 <-- 1 if weight is odd, 0 if even jr $ra # return to caller check if weight is > 600 - print error if it is get ready to call checkOdd by pushing old $ra on the stack call checkOdd (parampassed in $a0) check result, print error msg if not restore old $ra end subr. by going to $ra checks if odd, returns value in $v0
Extra parameters • Your overachiever friend has a procedure with 8 parameters and 3 return values • Won’t work: Only get 4 params, and 2 return values • Pass the extra parameters by placing them on the stack before calling the subroutine • Local variables • Local variables for procedures need space allocated each time the procedure is called • Use the stack for this: • addi $sp, $sp, -8 #allocate space for 2 words • De-allocate memory before returning from subroutine
Clobbering things • Let’s say that you’re using registers $t3 and $s2 in your program • You call a procedure • What if the procedure overwrites $t3 or $s2 • You’ve been clobbered! • Somebody’s got to save the registers and restore them when beginning and ending the procedure call • Caller saved: Calling program responsible for saving registers. Procedure may use any at will. • $t0 - $t9 • Callee saved: Called program must save and restore any registers it uses. • $s0 - $s7
Thoughts about MIPS programming • Write the program in C/C++ first • Break it into procedures as appropriate • Convert the program to assembly language • Start out simple • Get basic parts working before adding error checking, etc. • Don’t forget: Words are 4 bytes • Use pseudoinstructions when applicable