下面是一些我相信会起作用的带注释的代码:
.text
.globl main
# main -- main program
#
# a[i + 1] = a[i - 1] + b[4]
#
# arguments:
# s0 -- address of a
# s1 -- address of b
# s2 -- i
#
# registers:
# t0 -- address of a[i]
# t1 -- value of b[4]
# t2 -- i << 2 (i.e. a byte offset)
# t3 -- value of a[i - 1]
# t4 -- a[i - 1] + b[4]
main:
sll $t2,$s2,2 # get i << 2
add $t0,$s0,$t2 # get &a[i]
lw $t3,-4($t0) # get a[i - 1]
lw $t1,16($s1) # get b[4]
add $t4,$t3,$t1 # sum them
sw $t4,4($t0) # a[i + 1] = sum
这非常接近可以使用的 only 序列。 lw 指令的顺序可以颠倒,否则,除了使用不同的寄存器来保存不同的值之外,指令必须与所示相同。
为了清晰,每个中间术语/步骤都有自己的t 寄存器。但是,如果重新分配/重用寄存器,我们可以减少使用的寄存器数量。例如,在前两条指令中,我们可以将$t2 替换为$t0,因为之后我们不再使用$t2。
优化编译器可以/会做到这一点。我们最终只需要$t0-$t2 而不是$t0-$t4:
.text
.globl main
# main -- main program
#
# a[i + 1] = a[i - 1] + b[4]
#
# arguments:
# s0 -- address of a
# s1 -- address of b
# s2 -- i
#
# registers:
# t0 -- i << 2
# address of a[i]
# t1 -- value of b[4]
# t2 -- value of a[i - 1]
# sum of a[i - 1] + b[4]
main:
sll $t0,$s2,2 # get i << 2
add $t0,$s0,$t0 # get &a[i]
lw $t2,-4($t0) # get a[i - 1]
lw $t1,16($s1) # get b[4]
add $t2,$t2,$t1 # sum them
sw $t2,4($t0) # a[i + 1] = sum
如果代码是函数的一部分,我们可以使用 $v0, $v1, $a0 作为临时寄存器,因为如果函数愿意,可以使用 $v0-$v1 和 $a0-$a3 作为内部临时寄存器。
更新:
cmets 绝对可以帮助我了解每条指令期间发生的情况
良好的编程风格 [因此理解] 有很多 cmets。正确的类型。这对于 asm 来说尤其很重要,因为在 C 中,像 number_of_users 这样的变量有点描述性。但是,$t0 不是。
我添加的 cmets 是我为自己编写的任何 asm 代码所做的。而且,我写 asm 已经有几十年了。
此外,“优化”版本还有一个目的:简化。通过释放$t3-$t4,它们可以用来存放其他东西。在更复杂的函数中,这可以区分简单易懂的函数和难以理解和维护的不必要的复杂函数。
有关这方面的更多信息,以及如何写好 asm 的提示,根据我自己的经验,请参阅我的回答:MIPS linked list
教授不是最擅长传递信息
在课堂环境中只能传达这么多。它可以涵盖基础知识。而且,课程作业可以帮助您学习给定语言的基础知识。而且,IMO,教授们从来都不擅长很好地评论代码[够了;-)]。
当我[在不同的架构上]学习 asm 时,除了课程作业外,我还能够在 100% 用 asm 编写的多用户分时操作系统上作为付费程序员工作。它的评论风格与上述相同。
IMO,最好的学习方法之一是分析“专家”代码。问一个问题:“他们为什么以那种方式这样做?”。这就是我所做的,我学到了很多东西。直到今天,我 [仍然] 将这种经历归功于此。