Subjects for Final Exam
slt set on less slt $t0m $s3 ,$s4
slti immediate value
registers A0- A3 for arguments
v0-v1 return values
ra stores point of origin for returning
jump and link (jal) automatically sets ra to the next line of code after the current place the pc points to
jr $ra goes back to point of origin
sp stack pointer
t0-t9 temporary registers, not stored
$s0 -$s7 saved registers.
addi $sp, $sp , -12 makes 3 words of space in the stack for storing s registers, etc.
fp points to start of frame, usefull for referencing local variables stored in stack
By convention (not part of mips architecture) these are the memory locations of mips programs. (see chart p. 87)
Stack pointer ($sp) is set to 7fff fffchex and goes down from there but cannot pass 1000 0000 hex
$gp (global pointer ) is set to 1000 8000 hex and is used to access data from 1000 0000
until 1000 ffffe. It will be used when creating dynamic memory allocations. And could theoretically conflict with static data.
static data start at 1000 0000hex
text is located at 0040 0000hex
.data myspacer: .space 4 myarray: .byte 1,2,3,4 .text lui $1, 4097 ori $t0, $1, 4Avi Treistman's explanation of above code:
The only pseudo instructions you are allowed to use are la and li.
extra function parameters are stored above the fp frame pointer
lb loads byte from memory into low end of register
sb takes low byte from register and puts it into memory. Remember MIPS keeps memory locations for every byte so therefore we can locate single bytes of memory
In a leaf procedure use t registers first so that you don't need to save s register values in
lh loads a half word from memory into low 16 bits of register.
sh reads low 16 bits of a register and puts into memory
lh $t0, 0($sp ) #read from memory into t0
sh $t0, 2($sp) #write from register t0 into memory
lw and sw must be aligned to starts of words (4 byte intervals)
lui allows one to load immediate values that won't fit into 16bits. Normal li only allows one
to load a value of 16bit length. But a register can contain 32 bits. Therefore we can load the high
end 16bits first into a register using lui and then we can insert the lower end.
Lui also clears low bits to zero.
lui $s0, 87
ori $s0, $s0, 1212
now $s0 contains 87 followed by 1212
Use ori not addi to load the lower portion, addi won't work because it copies the leftmost bit of the lower 16 bits into the high section. ( I think this is the sign of the number)
When you do a command like lw you specify a memory location from which to get the value. But how does the MIPS chip get the whole (large) value at that memory location into memory? The answer is it uses a temporary register $at to assemble the full value (using lui and ori) and then copies that value into the register you specified in your lw.
The jump instruction has 6 bits for the command and 26 bits for the address but the bne instruction which also has 2 register addresses has only 16 bits left for the address. In fact, bne jumps to the location $pc + the address given. This makes sense since most conditional jumps are to nearby code. This is called PC-relative addressing. J and jal are likely to jump anywhere, therefore their address space is larger. Furthermore we can get an effective 28 bits (instead of the actual 26) by assuming the address is in words not bytes (i.e. increments of 4). Obviously a jr can jump to the full 32 bit location since it is jumping to the location stored in a 32 bit register. To get bne to jump to a distant location simply place an uncontional jump ( j or jr) at a label and use the bne to jump there.
see sort example on page 121
Arrays vs. Pointers
The official data segment in MIPS starts at 0x10000000, and this is what you will see in Patterson and Hennessey. In the MARS simulator, the default .data address is 0x10010000. This is the address used for labels defined in the .data section of code. However, this is NOT the start of the data segment, which is 0x10000000 as stated before. The segment from 0x10000000 until 0x10010000 is reserved in MARS as .extern, which is the data segment for global variables.
How can we demonstrate this ?
MARS can compile more than one file at a time if they are in the same directory. In order to do this, you need to set "Assemble all files in directory" under "Settings" from the main menu. I also suggest that you turn on "Show Labels Window", and "Initialize Program Counter" to "global main"
Below are two files:
#Test.s .data mylocal: # this is a local variable at 10010000 .byte 1,2,3,4 .extern myspace 4 # this is a global variable 10000000 .text .globl main # declares main as global, PC is set to here based on Settings. main: la $t0, myspace li $t1, 0x1234 sw $t1, 0($t0) # write to global jal myproc # jump to external routine li $v0, 9 #heap allocation li $a0, 4 syscall li $t0, 0x1234 sw $t0, 0($v0) li $v0, 10 syscall #Test1.s .globl myproc # this is a global procedure .data mylocal: # this is a local variable at 10010004 .byte 1,2,3,4 .text myproc: li $t0, 4 la $t1, myspace jr $ra
If you assemble both of these files in MARS (open both of them by File:Open, while they are in the same directory), in the Labels window you will see two global variables myproc and myspace, and you will see two local variables, both named mylocal with different addresses.
Two additional options that are available are in .Memory Configuration. under Settings. You can set either the code to start at 0x0, or the data and .data segment to start at 0x0
The end of the code in test.s shows how to allocate on the heap, with the .heap segment at 0x10040000.
One last note: If you define the same global variable in two different files, the definition will be ignored in all of the files except the first in order of compilation.
What I have said about storing s registers in the stack before using them is a general rule, but if your code is short and you know that changing s registers will not have "side effects" then you don't need to store them. In fact, compiler optimation may discover this and also not store s registers.
000000000000000000000000000001 = 1
011111111111111111111111111111 = 2,147,483,647
100000000000000000000000000000 = -2,147,483,648
100000000000000000000000000001 = -2,147,483,647
100000000000000000000000000010 = -2,147,483,646
111111111111111111111111111111 = -1
111111111111111111111111111110 = -2
111111111111111111111111111101 = -3
Thus leading ones indicate negative numbers. Positive numbers always have at least one leading zeros and are represented intuitively. This is better than representing magnitude and value seperately (we have no negative zero!). The only disadvantage is that there is one more negative number than positive.
lb treats byte as signed and therefore fills 24 msbits with appropriate values based on sign. lbu does an unsigned copied and therefore does not affect the 24msb. lh and lhu work similarly.
"Set on less than unsigned" (sltu) for comparing unsigned integers like addresses. And there is also lstiu. Regular slt assumes signed numbers. (p. 165)
To negate a number simply invert every 0 and 1. and then add 1 to the result. When inserting a 16 bit number to a 32 bit version ( as when running li, si,add, slt) you need to represent it correctly, for example the MSB must be set. What the mips does is called sign extension. It takes the MSB for the 16 number and extends it all throught the upper 16 bits of the new word. LB does this also. There is also a shortcut to the effect that if you want to check if $a1 < 0 or if $t2<= $a1 you can do it in one check. Simply use the set less than unsigned ( sltu $t0, $a1, $tu ) and then jump on the reverse condition.
Mips has no subtraction, simply change one sign to negative and perform addition. But infact, there is a command sub. Overflow can occur when adding two possitive or two negative numbers. ( Or revese cases with subtraction.) In such a case the sign bit will contain a value instead of the sign. If we perform an addition on two negative number and get a postive result, we know an overflow occured. So too, if we perform an addition of two positive numbers and get a negative result, we know an overflow occured. (and reverse for subtraction) When adding unsigned numbers use addu, addiu and subu. These will not cause a exception on overflow. see example on p. 173