首先,您通常想使用 $zero
而不是$0
,$v0–$v1
而不是$2–$3
等......它们比普通数字更容易理解(和记忆),并且汇编程序也理解这种表示法,所以这方面没有问题。这也将您的代码从数字中抽象出来,因此如果标准发生变化(例如,$zero 不再是 $0 而是 $256),您的代码仍然可以正确组装。
在每个 CPU 架构中,关于如何使用寄存器进行调用以及如何在函数中使用寄存器,都有一些约定。这称为调用约定。您可以在此处查看 MIPS 调用约定的简要说明。
我听说保持寄存器清洁通常是一种很好的做法,即在程序结束时将寄存器的值清除为 0。所以我的第一个问题是如何/为什么这是必要的/良好的做法?
我个人从未听说过这种做法,但我不太同意。更好的做法是在程序开始之前恢复以前的值。
其次,如果我们有一个函数调用,其参数(p1, p2) 存储在$4 和$5 中;在我们的函数定义结束时,是否有必要清除 $4 和 $5 的值或更好地保留它们在函数调用开始时的状态?
您应该将它们保留在函数调用开始时的状态。对于调用者来说,你修改这个寄存器是没有意义的。
堆栈上的空间是为 $a0-$a3 保留的,以防被调用者需要保存其参数,但调用者不会将寄存器存储在那里。
我还看到了像这样将参数推送到堆栈的示例。何时以及为什么这是必要的/良好的做法?
如果根据定义,堆栈是临时存储。如果你想备份寄存器值(例如你想使用 $aX 或 $sX),你把它们放在那里。和以前一样:
堆栈上的空间是为 $a0-$a3 保留的,以防被调用者需要保存其参数
addi $sp, $sp, -8
移动堆栈指针寄存器(按照惯例,它是 29 美元)。这在堆栈中保留了 8 个字节。
sw $a0, 0($sp)
sw $a1, 4($sp)
将 $a0 保存到堆栈顶部,将 $a1 保存为堆栈中的第二个(请记住,堆栈向较低地址增长)。这将填充保留空间(8 个字节)。这称为函数入口协议。
; At the end of our program:
; You forgot, and it's important:
lw $a0, 0($sp)
lw $a1, 4($sp)
addi $sp, $sp, 8
您恢复保存的寄存器并将堆栈指针放回原始值。然后调用者将保持其堆栈和参数不变。这被称为函数出口协议。
如果我们要在程序中使用一些常量,比如 4 和 1,将它们保存在寄存器中还是堆栈上更好?
两者都不。常量应该用作直接操作数:
li $t0, C ; Load 16-bit constant into $t0
lui $t0, C ; Load upper 16-bit half-word of $t0