与其他拱门不同,MIPS 没有推送或弹出寄存器/立即指令。所以你依靠自己管理堆栈。这实际上在 mul/div 之外的大多数拱门中都有说明,您的寄存器没有特定用途,只是建议的使用方式。现在,如果您随心所欲地使用它,例如,如果您尝试与 C 集成,则会破坏某些东西。
为了将某些东西压入堆栈,您需要使用存储指令。这些是sb, sh, sw, swl, swr
。byte, half, word, word left, word right分别。
addiu $sp, $sp, -4 # push stack 1 word
sw $t0, 0($sp) # place item on newly pushed space
为了从堆栈中弹出一些东西,你只需要用 addiu 减量它。但是,您可能希望使用lb, lh, lw, lwl, lwr
.
lw $t0, 0($sp)
addiu $sp, $sp, 4 # pop stack 1 word
这是一个将其与两个单词 push 一起使用的示例。
addiu $sp, $sp, -8 # allocate two words
sw $t0, 0($sp) # push two registers t0 t1
sw $t1, 4($sp)
lw $t1, 4($sp) # pop two registers t0 t1
lw $t0, 0($sp)
addiu $sp, $sp, 8 # deallocate two words
这是一个将它用于返回地址的示例,因此对非叶函数的调用不会弄乱你。
# grab us a quick string
.data
example_str: .asciiz "hello world :^)"
# grab us a function
.text
.globl example
.type test, @function
test:
addiu $sp, $sp, -4 # push stack for 1 word
sw $ra, 0($sp) # save return address
la $a0, example_str # call puts and give it a string
jal puts
nop
lw $ra, 0($sp) # load return address
addiu $sp, $sp, 4 # pop stack for 1 word
jr $ra # return from function to caller
nop
这是一个连续推送多个元素的示例。爆破当然是相反的。
.data
example_arr: .word 0, 0, 0, 0
.text
addiu $sp, $sp, -16
la $t0, example_arr
lw $t1, 0($t0)
sw $t1, 0($sp)
lw $t1, 0($t0)
sw $t1, 4($sp)
lw $t1, 0($t0)
sw $t1, 8($sp)
sw $t1, 12($sp)
这是一个使用 malloc/calloc 的例子。
# grab us a function
.text
.globl example
.type test, @function
test:
addiu $sp, $sp, -4 # push stack for 1 word
sw $ra, 0($sp) # save return address
li $a0, 4 # allocate 4*4 bytes (16)
li $a1, 4
jal calloc
nop
addiu $sp, $sp, -4 # push stack for 1 word
sw $v0, 0($sp) # save calloc'd buffer
move $t0, $v0 # get the buffer into a temp
li $t1, 1 # fill some temps with numbers
li $t2, 2
li $t3, 3
li $t4, 4
sw $t1, 0($t0) # save some temps to buffer
sw $t2, 4($t0)
sw $t3, 8($t0)
sw $t4, 12($t0)
... do stuff with the buffer ...
lw $a0, 0($sp) # pop buffer from stack
jal free # run it through free
nop
addiu $sp, $sp, 4 # don't forget to decrement
lw $ra, 0($sp) # load return address
addiu $sp, $sp, 4 # pop stack for 1 word
jr $ra # return from function to caller
nop
就像我之前提到的,没有什么是硬定义的特定用途,所以你也可以使用你自己的堆栈,如果你愿意的话,忘记使用 $sp。我展示了我使用 $t* 作为 $s* 的示例。这适用于强制每个函数拥有自己的堆栈或您能想到的其他用例的情况。例如,Lua ( https://lua.org ) 在某种程度上做到了这一点。但是,不是 MIPS。多个堆栈很可爱,尤其是在处理多个目标时。
编辑:我意识到我省略了堆栈帧指针。请注意,如果您的代码与用 C 编写的内容相关联,那么您正确处理堆栈帧指针。