MIPS function inside a function
I am trying to have the function vbsme call another function called sad... is the following procedure correct about saving the re开发者_运维问答gisters and return address?? the caller is supposed to save register $t0-$t7, but where and how should I do that?
vbsme: li $v0, 0 # reset $v0
li $v1, 0 # reset $v1
li $t0, 1 # i(row) = 1
li $t1, 1 # j(col) = 1
lw $t2, 0($a0) # row size
lw $t3, 4($a0) # col size
mul $t4, $t2, $t3 # row * col
li $t5, 0 # element = 0
loop: bgeq $t5, $t4, exit # if element >= row * col then exit
addi $sp, $sp, -16 # create space on the stack pointer
sw $ra, -12($sp) # save return address
sw $s6, -8($sp) # save return address
sw $s7, -4($sp) # save return address
subi $s7, $t0, 1 # 1st parameter: i-1
subi $s6, $t1, 1 # 2nd parameter: j-1
jal sad # calculate the sum of absolute difference using the frame starting from row a0 and col a1
lw $ra, -12($sp) # restore return address
lw $s6, -8($sp)
lw $s7, -4($sp)
addi $sp, $sp, 16 # restore stack pointer
jr $ra
$sx registers are guaranteed to be unchanged accross function calls, so its the callee (sum function) the responsible of saving them, only if its going to change their value.
$tx registers, on the other hand, are not guaranteed to be unchanged over function calls, so its the responsability of the caller (vbsme) to save them.
You should save $sx in the callee stack.
So when you start coding the sum function, you should save space in the stack If you want to save n registers, then save n*4.
Space in the stack is saved by subtracting on the $sp register, which points to the base of the stack. Before your function code, you should create the stack for that function, saving all caller-saved registers, return address and global pointer registers when neccesary
sum:
#stack frame creation. Caller registers saved,
# return address and frame pointer
subu $sp,$sp,36 #Save space in the stack for registers $s0, $s7 + $ra
sw $ra,32($sp)
sw $s0,0($sp)
sw $s1,4($sp)
#and so on. Note that also you should save the $ra register only if you are
# going to call another function
#do something with $sx
#stack frame destruction
#restore $sx and $ra registers
lw $ra,32($sp)
lw $s0,0($sp)
lw $s1,4($sp)
...
lw $s7,28($sp)
jr $ra
By the way, by convention, registers $a0, $a3 should keep the arguments to the function you are calling. Also, note that because you are using the $s0, $s7 registers, you have to do some extra work. Convention says that if you don't use them, then you shouldn't save them, so maybe you could use the $tx (temporary) registers instead.
Alexander,
What Tom said is pretty much correct the important thing to note with doing programming in assembly is everything is by convention. While in MIPS the common convention is what Tom noted it is not the only convention. For instance if you are using macro's (which if you are going to be writing more than 1 or 2 functions in assembly it is a much easier to use macros) then you can define your calling convention in the macro. The easiest way to do this is instead of having the callee save the stack is to have the caller save the stack. This is less efficient because sometimes (perhaps many times) unused registers will be saved, however it will save you a lot of heartache because your convention is applied consistently.
Caller Stack Save:
sw $fp 0($sp) # save the old frame pointer
addu $fp $sp $0 # move the frame pointer to point at top of frame
subu $sp $sp 44 # move the stack pointer down 44
sw $fp 40($sp) # save the old stack pointer
sw $ra 36($sp) # save the return address
sw $s0 32($sp) # save registers $s0 - $s7
sw $s1 28($sp)
sw $s2 24($sp)
sw $s3 20($sp)
sw $s4 16($sp)
sw $s5 12($sp)
sw $s6 8($sp)
sw $s7 4($sp)
Function call
jal my_func
Caller Stack Restore
subu $sp $fp 44 # move the stack pointer to the orginal unmodified bottom
lw $ra 36($sp) # load the return address
lw $s0 32($sp) # load registers $s0 - $s7
lw $s1 28($sp)
lw $s2 24($sp)
lw $s3 20($sp)
lw $s4 16($sp)
lw $s5 12($sp)
lw $s6 8($sp)
lw $s7 4($sp)
lw $fp 44($sp) # load the old frame pointer
lw $sp 40($sp) # load the old stack pointer
Your function:
my_func:
do some stuff
jr $ra # return to the previous function
As I said the best way to apply this convention is to use macros. I did a project in SPIM (which you may being using or you may not be) for a class. As part of the project we wrote a macro engine (it does other cool stuff as well) for SPIM you can get it here: http://github.com/timtadh/mpp I would also recommend checking out http://github.com/timtadh/jist which is a toy operating system written on top of SPIM. It will give you a sense of how to use the macro engine.
cheers
精彩评论