教我系统编程的教授使用他所谓的“原子-C”作为 C 和汇编之间的垫脚石。 atomic-C 的规则是(据我所知):
- 只允许使用简单的表达式,即允许使用
a = b + c; 不允许使用a = b + c + d;,因为那里有两个运算符。
- if 语句中只允许使用简单的布尔表达式,即允许使用
if (a < b),但不允许使用if (( a < b) && (c < d))。
- 只有 if 语句,没有 else 块。
- 不允许 for / while 或 do-while,只允许 goto 和 label
因此,上述程序将转换为;
label1:
if (a == b)
goto label2;
if (a < b)
goto label4;
a = a - b;
goto label3;
label4:
b = b - a;
label3:
goto label1;
label2:
return a;
我希望我没看错……自从我上次编写 atomic-C 以来已经快二十年了。现在假设以上是正确的,让我们开始将一些 atomic-C 语句转换为 MIPS(假设这是您正在使用的)程序集。从 Elliott Frisch 提供的链接中,我们几乎可以立即翻译减法步骤:
a = a - b becomes R0 = R0 - R1 which is: SUBU R0, R0, R1
b = b - a becomes R1 = R1 - R0 which is: SUBU R1, R1, R0
由于 a 和 b 都是正整数,所以我使用了无符号减法。
可以这样进行比较:
if(a == b) goto label2 becomes if(R0 == R1) goto label2 which is: beq R0, R1, L2?
这里的问题是beq操作码的第三个参数是PC移动的位移。在我们完成这里的手工组装之前,我们不会知道这个值。
不平等是更多的工作。如果我们离开伪代码指令,我们首先需要使用set on less than 操作码,如果第一个寄存器小于第二个,则将一个放在目标寄存器中。完成此操作后,我们可以如上所述使用branch on equal。
if(a < b) becomes slt R2, R0, R1
goto label4 beq R2, 1, L4?
跳转很简单,它们只是 j 和要跳转到的标签。所以,
goto label1 becomes j label1
我们必须处理的最后一件事是退货。返回是通过移动我们想要的值来完成的
一个特殊的寄存器V0,然后在调用这个函数后跳转到下一条指令。问题是 MIPS 没有寄存器来注册移动命令(或者如果有的话,我已经忘记了),所以我们从寄存器移动到 RAM,然后再返回。最后,我们使用保存返回地址的特殊寄存器 R31。
return a becomes var = a which is SW R0, var
ret = var which is LW var, V0
jump RA which is JR R31
有了这些信息,程序就变成了。而且我们还可以调整我们之前不知道的跳跃:
L1:
0x0100 BEQ R0, R1, 8
0x0104 SLT R2, R0, R1 ; temp = (a < b) temp = 1 if true, 0 otherwise
0x0108 LUI R3, 0x01 ; load immediate 1 into register R3
0x010C BEQ R2, 1, 2 ; goto label4
0x0110 SUBU R0, R0, R1 ; a = a - b
0x0114 J L3 ; goto label3
L4:
0x0118 SUBU R1, R1, R0 ; b = b - a;
L3:
0x011C J L1 ; goto lable1
L2:
0x0120 SW R0, ret ; move return value from register to a RAM location
0x0123 LW ret, V0 ; move return value from RAM to the return register.
0x0124 JR R31 ; return to caller
自从我不得不做这样的事情以来已经快二十年了(现在,如果我需要汇编,我只是按照其他人的建议去做,让编译器完成所有繁重的工作)。我确信我在此过程中犯了一些错误,如果有任何更正或建议,我会很高兴。我之所以参加这个冗长的讨论,是因为我将 OP 问题解释为进行手动翻译——有人在学习汇编时可能会这样做。
干杯。