我知道这个问题有点老了,但也许我仍然可以帮助其他试图理解这个问题的人。
显而易见的答案
给定一个乘法a * b。如您所料,首先想到的显而易见的答案就是计算b + b + b + b + [...]。而且实际上这是一种正确的方法,虽然现在需要的计算周期完全取决于 b,所以 b 越大,这种方法计算所需的时间就越长。
更好的方法
但是,当然,有一个解决方案。给定 a * b 的先前乘法,其中两个数字都是无符号和 32 位,b 可以描述为 b(i) * 2^(i)(0 b(i) * (a * 2^(i))(0
在 C 代码中看起来像这样:
unsigned multiply(unsigned a, unsigned b) {
unsigned i, product = 0;
for (i = 0; i < 32; ++i) {
if ((b & 1) == 1) {
product = product + a;
}
a = a << 1;
b = b >> 1;
}
return product;
}
但是这个代码目前还不能翻译成微指令。
-
for 和 if 不能 1:1 翻译
- 我们受限于 ALU 的功能,即移位操作
- ALU 只能产生数字 0、1 和 -1
在下面的第二个方法中,我们将 for 循环替换为 while 循环。这是可能的,因为我们每次通过移位使 b 减半,所以 b 在某一点必须等于 0。明确在 32 个班次之后。
unsigned multiply(unsigned a, unsigned b) {
unsigned product = 0;
while (b != 0) {
if ((b & 1) == 1) {
product = product + a;
}
a = a << 1;
b = b >> 1;
}
return product;
}
现在我们可以替换更高级的控制结构,比如while循环:
unsigned multiply(unsigned a, unsigned b) {
unsigned product = 0;
loop:
if (b == 0) goto finish;
if ((b & 1) == 0) goto next;
product = product + a;
next:
a = a << 1;
b = b >> 1;
goto loop;
finish:
return product;
}
现在我们可以开始将它投射到 mic-1 上。
- 变量 a 在堆栈上的变量 b 下。因此,我们首先必须启动一个读取指令。这意味着 a 将在 MDR 寄存器中。
- 变量 b 位于堆栈顶部。这意味着它的值已经在寄存器 TOS 中。
- 为了保存和更新最终结果,我将其命名为“产品”,我们留下了寄存器 OPC,因为它是唯一没有限制的其他寄存器
我们还必须将所有操作投射到 mic-1 上。
- 可以通过查看 Z 标志(零标志,如果 ALU 的输出为 0,则为 1)立即检查 b == 0
- 测试
b & 1 == 0是否必须分两步完成,所以我们使用寄存器H来存储一个1
- ALU 无法直接将 a 向左移动 1,但
a = a << 1 只不过是 a = a + a,因为我们是二进制的
- ALU 可以直接将 b 右移 1 位
所有这些都结束了,让我们看看我们在哪里:
unsigned multiply(unsigned mdr, unsigned tos) {
unsigned z, h, opc = 0;
loop:
z = tos;
if (z == 0) goto finish; h = 1;
z = tos & h;
if (z == 0) goto next;
h = mdr;
opc = opc + h;
next:
h = mdr;
mdr = mdr + h; tos = tos >> 1; goto loop;
finish:
return opc;
}
现在我们可以直接把它翻译成一个微汇编程序:
imul1 | MAR = SP = SP - 1; rd
imul2 | OPC = 0
loop | Z = TOS; if (Z) goto finish; else goto imul4
imul4 | H=1
imul5 | Z = TOS AND H; if (Z) goto next; else goto imul6
imul6 | H = MDR
imul7 | OPC = OPC + H
next | H = MDR
imul9 | MDR = MDR + H
imul10 | TOS = TOS >> 1; goto loop
finish | MDR = TOS = OPC; wr; goto Main1