Day 16
I. Last Time:
Hw #5 Posted
Lab #1 Due next time
A. Insts so far:
add, addu, sub, subu, addi, addiu
lw, sw, lb, sb
beq, bne, j, slt, sltu,
syscall
Pseudo-Ops make reading/writing code easier:
(Helps explain "why"/logic)
li, la, move
blt, bltu, bgt, bgtu, ble, bleu, bge, bgeu
bnez, beqz,
mul, div, rem
B. How does a program do IO/talk to hardware/get memory? OS!
How does the computer contact the OS?
In Many os's through syscalls (There are other mechanisms
though like jump gates)
Ex: What if I want to print the int in $t7?
C. How the assembler uses memory
1. Memory is divided into "segments" (See A-9)
2. Each of these segments has a specific use:
Reserved: ? OS/Hardware/whatever. We can't use it.
Text Segment: For the program ("text")
This is where the actual instructions are
stored. Remember that they are word aligned.
If you see an address in this range - it's
refering to an instruction!
Data Segment: Static and Dynamic:
Static: For "static" (global) variables
and "constant" arrays.
Ex: .asciiz "Hi ..."
In a C prog. and initialized arrays
initial values. NOT the array itself,
just the stuff to initialize it with.
Dynamic: the "Heap" - where the new/alloc
memory comes from. (Free store)
Note: This can "Grow" Upward
Stack: A "stack" used for functions.
Memory "MAP" / Usage Notes:
0->0x400,000 Reserved:
Probably part of the OS instruction/Hardware
Memory Mapped Hardware
0x400,000->0x10,000,000 Text Segment
This is where ALL instructions of your
programs are placed.
(Program Must have less than 66 Million Insts?)
We can think of "constants" being stored here too.
0x10,000,000-> Static Data Segment -"Load" Time allocated space
The things that exist for the entire
lifetime (global scope) of the program:
C/C++: Global Variables,
Local Static Variables,
Initializers for arrays, classes
Char Constants (E.g.: "Hello")
ASM:
Anything following a .data directive
Dynamic Data Segment - Run Time allocated space
C/C++: Space allocated while the program
is running: new/malloc/alloc/realloc
("Free Store"/"Heap")
This stuff is typically dynamically
allocate because it's space isn't
known in advance.
ASM: Anything allocated with the
sbrk syscall
Note: This can "Grow" Upward
0x7fffffff-> Stack Segment - Where all "local" variables
will be created/destroyed
Local Variables to functions (ALL Functions)
C/C++: ALL locally scopped (non-static) variables.
I.e. almost all variables declared in functions.
Assembler reads through program and packs into approp.
segments
As it finds labels they are added to a table and later
the actual numeric address is filled in.
This is actually somwhat helpful to understand
when you're de-bugging c/c++ programs.
Remeber that c/c++ is converted to ASM,
so it'll use the same segmentation.
D. j / beq data stored
j - store 26 bits (upper 26) direct address of location
beq/bne - store 16-bit displacement (how far to go)
Both change the PC register (I.e. the CPUs inst. bookmark)
II. New Stuff:
A. Other useful assembler directives:
Declaring Space/Data (Uninitialized):
.space n - Set aside N bytes of space
Initialized Space:
.word w1,,,,wn - Put the following words in memory
.asciiz "..." - Put the following text in memory and
NULL terminate it
.ascii "..." - Put the following text in memory and
DON'T NULL terminate it.
.byte b1,,,,bn - Put the following bytes in memory
.float f1,,,,fn - Put the following floating point
numbers in memory
.half h1,,,,hn- Store Half words
Misc.
.align N - Force the following items to be aligned
to a N-bit boundary
I.e. .align 2 forces word alignment
B. LAB - MISC
Don't assume regsiters are initialized for you (just like in C)
Showing test runs:
Create Test Runs
File -> Save Log File As (Create a "log" of runs)
Edit this file and copy out the "Console" section
which will show the Console IO
C. Functions in MIPS
Functions - What is a function/subroutine?
A "self contained" set of instructions that does some work
It normally requires some input (arguments) and produces
some output (return value).
Why functions?
Code Re-use
Complexity reduction
4. MIPS function calls:
Much like syscalls:
a. Setup-Arguments
Save and "temp" data that the function may trounce!
b. "Call" the function
c. Use/store the return value
Arguments: The a0-a3 registers
(What if we need more?)
"Call": jal Jump AND Link for calling a function and
saving the way back to the caller.
Go to a specific address AND save the current
IP/PC position so we can get back
(leave a trail of bread crumbs)
5. MIPS functions:
It's name is a "label"
Shouldn't "step on" variables being used by main prog.
(I.e. they can only freely use the t registers....)
Must "return" to the right position in the calling program.
jr - Return to the address provided in a specific reg
Ex: jr $ra # Return to the "return point" in the caller.
return value in $v0
Example Function:
Write a function to add 4 arguments.
Ex: a=add_args(b,c,d,e);
... (What happens next?...what's changed?)
(a=b+c+d+e)
add_args:
add $v0,$a0,$a1
add $t0,$a2,$a3
add $v0,$v0,$t0
jr $ra
Function Call:
li $a0,1
li $a1,2
li $a2,3
li $a3,4
jal add_args
move $s0,$v0
...
D. This isn't enough...Local variables and recursion:
Example: Factorial Function & fact(5)
Factorial = N*(N-1)*(N-1)*...*1
(Silly Example - "better" iterative solution and approximation)
unsigned fact(unsigned n)
{
if(n<1)
return 1; // base case of recursion
else
return n*fact(n-1);
}
fact(5) = 5*fact(4) - 4*fact(3) - 3*fact(2) - 2*fact(1) - 1
- 2*1=2
3*2=6
4*6=24
5*24=120
Details of assembly Language:
a single set of assembly instructions is being
executed repreatedly?
where/how does it return to the caller? WHICH Caller?
how does it keep track/use local variables/registers?
E. The Stack - Like a stack in C/C++, but simplier.
The Stack is a Stack of Function FRAMES.
Whenever a function is called, it creates a FRAME for itself.
The Frame contains all the local information to the function -
things like local variables - both explicit (the ones you know about)
and some implicit (the compiler creates for you - Things like return
values and in some cases arguments to functions)
The Stack Operations: Push, Pop, "Peek"
Push/Pop - Stack Frames are being pushed/poped off the stack.
Peek - Whenver a function is running it may need to use the
space it has set aside for itself (I.e. peek at it's
data)
A stack is made of bytes
Must be allocated in double word increments (multiple of 8)
Must be used in a very consistent way to be complatible with C/C++
(C will pass arguments and expect arguments to be passed in a very
rigid format - we will be using this format)
Grows DOWN in memory. I.e. to allocate space, subtract from $sp
It's a pointer, so use unsigned instructions (techincally)
The Stack:
Why a stack?
To support recursion, more efficient use/re-use of space
(localy scoped variables)
Think:
If every variable in every function had to actually
have space set aside for it in memory at the same time
VERY inefficient - instead re-use parts of memory only
for the currently executing function...
And ONLY ONE function can really run at a time...
Implications:
Local variables of functions are NOT implicitly
initialized.
Limited Recursion - Stack Overflow
Alternatives:
No Runtime stack - no recursion, static variables only
Very Small stack - limited recursion
Both are common in embedded applications
1. Stack Frames/Activation Records
When a function is called it creates a "frame" or activation
record
Two purposes:
1. Space for local variables
2. Space for any called functions to store caller's info.
(Arguments and large return values typically)
2. Special Instructions:
jal - Jump AND Link for calling a function and
saving the way back to the caller.
Go to a specific address AND save the current
IP/PC position so we can get back
(leave a trail of bread crumbs)
jr - Return to the address provided in a specific reg
3. What's Stored:
Arguments, Local Variables and Return Points
(Args to other functions as well)
Stores "local" scopped variables for a specific instance
of a function. I.e. a specific call. This info is called
the "frame" or activation record. and it is accessed via
the stack pointer ($sp)
Advantages: Re-uses "space" - more efficient
Potential Problems - Limited size - stack overflow
Stack variables are not implicitly
initialized.
III. Everything you ever wanted to know about Fibannoci,
but were afraid to ask...
DETAILED, SLOW Fibannoci example.
If you pay attention/understand, the next Hw will be a breeze
(take ~1hr) if NOT, it will take a lot longer)
A. Definition:
Note: This problem is 800 years old - proposed by Fibonacci
in 1202.
Suppose that you have a pair of rabbits - every month the
rabbits breed and have a pair of offspring, who will begin
the breeding process themselves in two months.
Month 1 - 1 Pair - New Born (NB)
Month 2 - 1 Pair - Ready to Breed (RTB)
Month 3 - 2 Pair (1 breeding (B), 1 NB)
Month 4 - 3 Pair (1 B, 1 RTB, 1 NB)
Month 5 - 5 Pair (2 B, 1 RTB, 2 NB)
Month 6 - 8 Pair (3 B, 2 RTB, 3 NB)
Notice that each term is the sum of the 2 proceeding.
(I.e. month 6 = month 5+month 4)
As a function:
F(0)=1
F(1)=1
F(N)=F(N-1)+F(N-2)
Notice that this is a function with 2 base cases,
and a single "recursive" case which recurses in
2 "directions".
NOTE: Solving this with recursion is (in my opinion),
the WORST possible way to solve the problem.
(Closed Form is best, Iteration second best)
We do this merly to gain experience with the
stack.
B. Solution - in C++:
unsigned Fib(unsigned n)
{
if(n<=1) // Neat observation based on seq
return n; // Base Case
else
return Fib(n-1)+Fib(n-2);
}
Notes on C++ Conversion:
n will be in $a0
We may need temp places to store Fib(n-1) & Fib(n-2)
($t0, $t1)
C. Solution in ASM:
All ASM routines have 3 parts -
a begining, a middle, and an end
fib:
# Allocate Stack Space
subu $sp,$sp,32 # (let's try 32)
# Store Registers that may be changed by called functions:
sw $a0,32($sp)
sw $ra,28($sp)
bgt $a0,1,fib_recurse # Check Base Case
move $v0,$a0 # Put return value in ret val reg
j fib_end
fib_recurse: # Recursive Case
addiu $a0,$a0,-1 # Setup for fib(n-1)
jal fib # call fib(n-1)
sw $v0,24($sp) # deal with return value
addiu $a0,$a0,-1 # setup for fib(n-2)
jal fib # call fib(n-2)
lw $t0,24($sp) # retreive fib(n-1)
add $v0,$v0,$t0 # v0=fib(n-1)+fib(n-2)
fib_end: # Clean-up/return to caller
lw $ra, 28($sp) # Restore ra for caller
lw $a0, 32($sp) # Restore a0
addu $sp,$sp,32 # Restore stack
jr $ra # return to caller
D. A sample Run:
Memory Address Instruction(s)
-------------- -------------------------------------------
[0x00400020] subu $sp,$sp,32 # (let's try 32)
[0x00400024] sw $a0,32($sp)
[0x00400028] sw $ra,28($sp)
[0x0040002c] bgt $a0,1,fib_recurse
[0x00400030] (beq $1, $0, 12 [fib_recurse-0x00400030])
[0x00400034] move $v0,$a0
[0x00400038] j fib_end
[0x0040003c] addiu $a0,$a0,-1 # Setup for fib(n-1)
[0x00400040] jal fib
[0x00400044] sw $v0,24($sp)
[0x00400048] addiu $a0,$a0,-1 # setup for fib(n-2)
[0x0040004c] jal fib
[0x00400050] lw $t0,24($sp)
[0x00400054] add $v0,$v0,$t0
[0x00400058] lw $ra, 28($sp)
[0x0040005c] lw $a0, 32($sp)
[0x00400060] addu $sp,$sp,32
[0x00400064] jr $ra
What happens during fib(1)?
fib(2)?
fib(5)?
E. Summary:
Stack is used to implement locally scoped variables and to
"re-use" memory for local variables so we don't have to
waste space for variables not currently in use.
Stack allows recursion.
Some accounting info. must be stored on the stack: args & ra.
VI. Next Time:
A. More Mips Memory and Functions
B. MIPS memory segmentation
C. Using SPIM