函数内的 MIPS 函数
我试图让函数 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
I am trying to have the function vbsme call another function called sad... is the following procedure correct about saving the registers 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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
$sx 寄存器保证在函数调用中保持不变,因此被调用者(sum 函数)负责保存它们,只有当它们的值要改变时。
另一方面,不能保证 $tx 寄存器在函数调用过程中保持不变,因此调用者 (vbsme) 有责任保存它们。
您应该将 $sx 保存在被调用者堆栈中。
因此,当您开始编写 sum 函数时,应该节省堆栈空间
如果要保存n个寄存器,则保存n*4。
通过减去 $sp 寄存器来节省堆栈空间,该寄存器指向堆栈的基址。在编写函数代码之前,您应该为该函数创建堆栈,必要时保存所有调用者保存的寄存器、返回地址和全局指针寄存器顺便说一句
,按照惯例,寄存器 $a0、$a3 应该保留您要调用的函数的参数。正在打电话。另请注意,由于您使用的是 $s0、$s7 寄存器,因此您必须做一些额外的工作。按照惯例,如果您不使用它们,那么您就不应该保存它们,因此也许您可以使用 $tx(临时)寄存器来代替。
$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
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.
亚历山大,
汤姆所说的非常正确,在汇编中进行编程时需要注意的重要一点是一切都是按照惯例。 Tom 指出,在 MIPS 中,通用约定并不是唯一的约定。例如,如果您正在使用宏(如果您要在汇编中编写超过 1 或 2 个函数,则使用宏会更容易),那么您可以在宏中定义调用约定。最简单的方法是让调用者保存堆栈,而不是让被调用者保存堆栈。这效率较低,因为有时(也许很多时候)未使用的寄存器会被保存,但是它会为您节省很多心痛,因为您的约定是一致应用的。
调用者堆栈保存:
函数调用
调用者堆栈恢复
您的函数:
正如我所说,应用此约定的最佳方法是使用宏。我在 SPIM(你可能正在使用也可能没有)中为课程做了一个项目。作为该项目的一部分,我们为 SPIM 编写了一个宏引擎(它也做了其他很酷的事情),您可以在这里获取它:
http://github.com/timtadh/mpp
我还建议您查看 http://github.com/timtadh/jist 这是一个玩具操作系统是在 SPIM 之上编写的。它将让您了解如何使用宏引擎。
干杯
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:
Function call
Caller Stack Restore
Your 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