我试图让函数vbsme调用另一个名为sad的函数...以下过程是否正确保存寄存器和返回地址?调用者应该保存寄存器$ t0- $ t7,但我应该在哪里以及如何做到这一点?
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
答案 0 :(得分:2)
$ sx寄存器保证在函数调用中保持不变,因此它的被调用者(sum函数)负责保存它们,只有它会改变它们的值。
另一方面,$ tx寄存器不能保证在函数调用时保持不变,因此调用者(vbsme)负责保存它们。你应该在被调用者堆栈中保存$ sx。
因此,当您开始对sum函数进行编码时,应该节省堆栈中的空间 如果要保存n个寄存器,则保存n * 4。
通过在$ sp寄存器上减去来保存堆栈中的空间,$ sp寄存器指向堆栈的基址。在您的功能代码之前,您应该为该功能创建堆栈,在需要时保存所有调用者保存的寄存器,返回地址和全局指针寄存器
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
顺便说一下,按照惯例,注册$ a0,$ a3应该保留你正在调用的函数的参数。另请注意,因为您使用的是$ s0,$ s7寄存器,所以您必须做一些额外的工作。惯例说,如果你不使用它们,那么你不应该保存它们,所以也许你可以使用$ tx(临时)寄存器。
答案 1 :(得分:1)
亚历山大,
汤姆所说的非常正确,在汇编中进行编程时要注意的重要事项是一切都是按照惯例。虽然在MIPS中常见的约定是汤姆指出它不是唯一的惯例。例如,如果您正在使用宏(如果您要在汇编中编写超过1或2个函数,则使用宏更容易),那么您可以在宏中定义调用约定。最简单的方法是让被调用者保存堆栈,而不是让调用者保存堆栈。这样效率较低,因为有时(可能很多次)未使用的寄存器将被保存,但是它会为您节省很多心痛,因为您的约定是一致的。
来电堆栈保存:
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)
函数调用
jal my_func
来电堆栈还原
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
你的职能:
my_func:
do some stuff
jr $ra # return to the previous function
正如我所说,应用此约定的最佳方法是使用宏。我在SPIM做了一个项目(你可能正在使用或者你可能不是)。作为项目的一部分,我们为SPIM编写了一个宏引擎(它还有其他很酷的东西),你可以在这里得到它: http://github.com/timtadh/mpp 我还建议您查看http://github.com/timtadh/jist这是一个在SPIM之上编写的玩具操作系统。它将让您了解如何使用宏引擎。
欢呼声