Normally I see 'lw' double used with 2 args, the destination register and the memory location to load from:
lw $2, 3($1)
In a CPU implementation the $2 part is the write address sent to the register file and the 3($1) is calculated by the ALU and then passed to memory. How does work when 'lw' has 3 args as follows:
array:
.word 1, 2, 3, 4, 5, 6, 7, 8, 9
lw $r2, $r24, array
I believe array here is a label, which will be translated into the immediate field of the lw instruction by the loader, so at load time it might look like this:
lw $r2, 121($r24)
where 121 will be related to the location in memory where the loader placed the initialized array.
Related
Currently I'm studying for my computer organization midterm, and I'm trying to fully understand the stack pointer and the stack. I know these following facts that surround the concept:
It follows the first in last out principle
And adding something to the stack takes a two step process:
addi $sp, $sp, -4
sw $s0, 0($sp)
What I think is stopping me from fully understanding is that I can't come up with a relevant, self apparent situation where I would need and/or want to keep track of data with a stack pointer.
Could someone elaborate on the concept as a whole and give me some useful code examples?
An important use of stack is nesting subroutine calls.
Each subroutine may have a set of variables local to that subroutine. These variables can be conveniently stored on a stack in a stack frame. Some calling conventions pass arguments on the stack as well.
Using subroutines also means you have to keep track of the caller, that is the return address.
Some architectures have a dedicated stack for this purpose, while others implicitly use the "normal" stack. MIPS by default only uses a register, but in non-leaf functions (ie. functions that call other functions) that return address is overwritten. Hence you have to save the original value, typically on the stack among your local variables. The calling conventions may also declare that some register values must be preserved across function calls, you can similarly save and restore them using the stack.
Suppose you have this C fragment:
extern void foo();
extern int bar();
int baz()
{
int x = bar();
foo();
return x;
}
MIPS assembly may then look like:
addiu $sp, $sp, -8 # allocate 2 words on the stack
sw $ra, 4($sp) # save $ra in the upper one
jal bar # this overwrites $ra
sw $v0, ($sp) # save returned value (x)
jal foo # this overwrites $ra and possibly $v0
lw $v0, ($sp) # reload x so we can return it
lw $ra, 4($sp) # reload $ra so we can return to caller
addiu $sp, $sp, 8 # restore $sp, freeing the allocated space
jr $ra # return
The MIPS calling convention requires first four function parameters to be in registers a0 through a3 and the rest, if there are more, on the stack. What's more, it also requires the function caller to allocate four slots on the stack for the first four parameters, despite those being passed in the registers.
So, if you want to access parameter five (and further parameters), you need to use sp. If the function in turn calls other functions and uses its parameters after the calls, it will need to store a0 through a3 in those four slots on the stack to avoid them being lost/overwritten. Again, you use sp to write these registers to the stack.
If your function has local variables and can't keep all of them in registers (like when it can't keep a0 through a3 when it calls other functions), it will have to use the on-stack space for those local variables, which again necessitates the use of sp.
For example, if you had this:
int tst5(int x1, int x2, int x3, int x4, int x5)
{
return x1 + x2 + x3 + x4 + x5;
}
its disassembly would be something like:
tst5:
lw $2,16($sp) # r2 = x5; 4 slots are skipped
addu $4,$4,$5 # x1 += x2
addu $4,$4,$6 # x1 += x3
addu $4,$4,$7 # x1 += x4
j $31 # return
addu $2,$4,$2 # r2 += x1
See, sp is used to access x5.
And then if you have code something like this:
int binary(int a, int b)
{
return a + b;
}
void stk(void)
{
binary(binary(binary(1, 2), binary(3, 4)), binary(binary(5, 6), binary(7, 8)));
}
this is what it looks in disassembly after compilation:
binary:
j $31 # return
addu $2,$4,$5 # r2 = a + b
stk:
subu $sp,$sp,32 # allocate space for local vars & 4 slots
li $4,0x00000001 # 1
li $5,0x00000002 # 2
sw $31,24($sp) # store return address on stack
sw $17,20($sp) # preserve r17 on stack
jal binary # call binary(1,2)
sw $16,16($sp) # preserve r16 on stack
li $4,0x00000003 # 3
li $5,0x00000004 # 4
jal binary # call binary(3,4)
move $16,$2 # r16 = binary(1,2)
move $4,$16 # r4 = binary(1,2)
jal binary # call binary(binary(1,2), binary(3,4))
move $5,$2 # r5 = binary(3,4)
li $4,0x00000005 # 5
li $5,0x00000006 # 6
jal binary # call binary(5,6)
move $17,$2 # r17 = binary(binary(1,2), binary(3,4))
li $4,0x00000007 # 7
li $5,0x00000008 # 8
jal binary # call binary(7,8)
move $16,$2 # r16 = binary(5,6)
move $4,$16 # r4 = binary(5,6)
jal binary # call binary(binary(5,6), binary(7,8))
move $5,$2 # r5 = binary(7,8)
move $4,$17 # r4 = binary(binary(1,2), binary(3,4))
jal binary # call binary(binary(binary(1,2), binary(3,4)), binary(binary(5,6), binary(7,8)))
move $5,$2 # r5 = binary(binary(5,6), binary(7,8))
lw $31,24($sp) # restore return address from stack
lw $17,20($sp) # restore r17 from stack
lw $16,16($sp) # restore r16 from stack
addu $sp,$sp,32 # remove local vars and 4 slots
j $31 # return
nop
I hope I've annotated the code without making mistakes.
So, note that the compiler chooses to use r16 and r17 in the function but preserves them on the stack. Since the function calls another one, it also needs to preserve its return address on the stack instead of simply keeping it in r31.
PS Remember that all branch/jump instructions on MIPS effectively execute the immediately following instruction before actually transferring control to a new location. This might be confusing.
I need to build a program (hard-code) in MIPS that gets an array of 10 integers and finds the difference between two nearby numbers in the array. This is what I have build:
.data
array: .word 23,-2,45,67,89,12,-100,0,120,6
arrend:
comma: .asciiz ", "
# array = {23,-2,45,67,89,12,-100,0,120,6}
# Algorithm being implemented to find the difference between nearby elements of the array
# difference = 0 (use $t0 for difference)
# loop i = 0 to length-1 do (use $t1 for i)
# difference = array[i]-array[i+1]
# end loop (use $t3 for base addr. of array)
# registers:
# t0 -- difference
#
# t3 -- pointer to current array element (e.g. arrptr)
# t2 -- pointer to end of array
#
# t4 -- current value fetched from array (i)
# t5 -- value fetched from array (i+1)
.text
main:
li $t0,0 # difference = 0
la $t3,array # load base addr. of array
la $t2,arrend # load address of array end
j test
loop:
lw $t4,0($t3) # load array[i]
addi $t3,$t3,4 # increment array pointer
lw $t5,0($t3) # load array[i+1]
sub $t0, $t4, $t5 # the difference of two nearby elements
# print value of difference
li $v0,1
addi $a0,$t0,0
syscall
# print comma
li $v0,4
la $a0,comma
syscall
test:
blt $t3,$t2,loop # more to do? if yes, loop
the output should be :
25, -47, -22, -22, 77, 112, -100, -120, 114,
but I get the output 25, -47, -22, -22, 77, 112, -100, -120, 114, -8230,
I found out if I change la $t2,arrend to la $t2,0x10010024, it will work, but I don't know how to write it in the code.
furthermore, how can I improve my code?
Ok... So, you are iterating one more time then you need... Just sub 1 from t2 right after you assign the end pointer of the array.
It's also called bufferoverflow.
My question is: What is the size of integer constants in MIPS?
Here I found how they are used.
If I have such a constant defined in my data segment and I want to
calculate the size of the data segment, what size do I take for this
constant: size of word, byte, half,..?
Here's a data segment example:
.data
array: .word 1, 2, 3
LEN = 2 ; Here's the constant
The size of data segment is: 3 * 32(bit) + ?(bit)
Thank you in advance!
I assume you are calling constants to "equates".
Constants do not occupy space in the data segment, whenever used as an operand they will be replaced by their expression and the size should match that of the operand, so in your example the data segment would be using 4*3 bytes = 12 bytes (96 bits).
For example if you write in MARS simulator
.data
array: .word 1,2,3
.eqv LEN 2
.eqv LARGE_VALUE 20000
buffer: .space LARGE_VALUE
then you can use identifier LEN as a substitute for 2, e.g.
li $a1, LEN
li $a2, LARGE_VALUE
In this case, LEN will be a 16 bit immediate when assembling the first instruction, and the assembler will emit code to do a 32-bit load for the second pseudo instruction. The buffer defined in data segment will be 20000 bytes (as defined by eqv LARGE_VALUE)
I have this code :
void test(int x)
{
cout<<x;
double y=x+4.0;
cout<<y;
}
void main ()
{
test(7); // call the test in main
}
In MIPS :
after I put the value of parameter x in 0($fp) in stack and jump to test :
lw $a0,0($fp) // load value of x and print it
li $v0,1
syscall
lw $t1,0($fp)
sw $t1,0($sp) // put the value of x in stack and point it by $sp
li.d $f0,4.0
s.d $f0,4($sp) // put the value 4.0 in stack and point it by $sp
l.d $f0,0($sp)
l.d $f2,4($sp)
add.d $f4,$f0,$f2
s.d $f4,8($sp) // put the result of add
l.d $f12,8($sp) // print the value of y
li $v0,3
syscall
My problem is the result of y in QTSPIM is 4 .... the problem because I load an integer value in a double register ... How I can solve this problem ???
You need to load the integer value into an fp register and then convert it to floating point format. You have an 8-byte load (which will load the 4 bytes of your value, plus the following 4 bytes, whatever they happen to be), so you probably want to change that, and then do a cvt:
l.w $f0,0($fp)
cvt.d.w $f0,$f0
Based on this figure, executing the SW instruction would cause these values to be assigned to the signals labeled in blue:
RegWrite = 0
ALUSrc = 1
ALU operation = 0010
MemRead = 0
MemWrite = 1
MemtoReg = X
PCSrc =
However, I am a little confused which inputs will be used in the Registers block? Can anyone describe the overall SW procedure in the MIPS datapath?
The execution of sw would follow the following steps in your diagram:
Instruction is read and decoded from the PC in the Instruction Memory subcircuit.
The register file is read for $rs and $rt (Registers subcircuit)
The value of $rs is added to the sign extended immediate (selected by ALUSrc) (ALU subcircuit).
The added value and $rt are passed to the Data Memory subcircuit where the value of $rt is written to memory.