制作一个支持条件跳转的计算机
到目前为止,我们制作的计算机能够进行加法运算、支持数据缓存到内存、支持数码管显示输出。然而,我们在“只读存储器”中设计的指令都是按照固定顺序执行的,即当执行完当前指令后,当前指令的低4位D3~D0就指示下一条指令的地址。本节中,我们要把它改造成一个可以根据不同条件按照不同顺序执行的计算机,即让我们的计算机支持条件指令跳转,这才是计算机的灵魂。那么什么情况下会使用到条件指令跳转呢?
我举个例子:如果这台计算机支持按键输入,我希望按第1个按键的时候计算3+4显示到数码管上;当我按第2个按键的时候计算5+6显示到数码管上。这种情况我们就需要设计两段指令,然后根据我按下哪个按钮来选择哪一段代码执行。
再举一个例子,我如果我想计算1+2+3+4+5+6+7+8+9的结果,我该怎么做呢?很简单,我可以先计算1+2,把结果存到内存0x0地址处,再把内存0x0地址结果取出来和3相加,计算结果再次覆盖并存到内存0x0地址处,我们可以这样重复编写10次类似的指令到ROM中,即可完成从1加到9的累计求和功能。那么如果想计算从1一直加到10000呢?我要编写10000次这种类似代码来实现这个功能么?其实不需要,这里稍微拓展下,如果你曾经有编程经验的话,你就会知道我们只需要编写一段两个数求和的功能即可,通过循环跳转来完成累计求和的功能。这里就会遇到一个问题,什么时候跳出求和的循环?答案就是需要使用“条件跳转指令”。编程知识我们下一章节会讲解,所以如果你这里有些困惑,可以先了解一下即可。
那么,什么是条件指令跳转呢?我们该如何改造前一节的计算机电路图呢?回想一下,我们上一章哪个元件还没有被用到?是“比较器”!这里,我们主要增加一个“比较器”即可,其电路图如下图所示。
和前一节相比,我们增加了一个“大于等于比较器”,并给“只读存储器”增加了3个输出端用于控制“比较器”的数据读写。同时,我们还使用一个“与门”来控制“比较器”的输出结果是否生效。当比较器生效的时候,当前指令的下一条指令会和比较器的结果相加来决定下一条指令是多少。比如,当前指令的下一条指令地址为0010,如果比较生效时并且比较结果输出1,那么下一条指令地址就变成了0011。这样我们就实现了通过比较两个数字的大小来实现指令分支的功能。
下面我们来看下“只读存储器”多出来的输出端都是做什么的。
- D15~D0和前一节功能相同,不再赘述
- D16控制总线数据是否传输到比较器输入端2
- D17控制总线数据是否传输到比较器输入端1
- D18控制比较器的输出结果是否生效
现在我们开始编写指令来测试下条件指令能否正常工作。我们使用这台计算机来比较3和2的大小,然后根据比较结果做不同的处理:如果3大于等于2,跳过一条指令,否者执行下一条指令。由于这两条指令处理不同的逻辑,一种情况会把7写入“十六进制数码管”,另一种情况什么也不处理,所以最终“十六进制数码管”看到的数字会有所不同。我把编写好的指令绘制在如下的表格中。
地址 | D3~D0 | D7~D4 | D8 | D9 | D10~D15 | D16 | D17 | D18 | 十六进制 |
---|---|---|---|---|---|---|---|---|---|
0000 | 0001 | 0011 | 1 | 0 | 000000 | 0 | 0 | 0 | 0x131 |
0001 | 0010 | 0011 | 1 | 0 | 000000 | 0 | 1 | 0 | 0x20132 |
0010 | 0011 | 0010 | 1 | 0 | 000000 | 0 | 0 | 0 | 0x123 |
0011 | 0100 | 0010 | 1 | 0 | 000000 | 1 | 0 | 0 | 0x10124 |
0100 | 0101 | 0000 | 0 | 0 | 000000 | 0 | 0 | 1 | 0x40005 |
0101 | 1000 | 0000 | 0 | 0 | 000000 | 0 | 0 | 0 | 0x8 |
0110 | 0111 | 0111 | 1 | 0 | 000000 | 0 | 0 | 0 | 0x177 |
0111 | 1000 | 0111 | 1 | 1 | 000000 | 0 | 0 | 0 | 0x378 |
1000 | 1000 | 0000 | 0 | 0 | 000000 | 0 | 0 | 0 | 0x8 |
我们来逐条分析下这些指令是如何工作的。
地址0000:设置D8=1,使D7~D4的数据0011传送到总线
地址0001:设置D17=1使比较器的“输入端1”由0变成1,总线数据0011被保存到比较器的寄存器1里
地址0010:设置D8=1,使D7~D4的数据0010传送到总线
地址0011:设置D16=1使比较器的“输入端2”由0变成1,总线数据0010被传送到比较器的寄存器2里
地址0100:设置D18=1,使比较器的输出结果生效
地址0101:跳转到地址1000
地址0110:设置D8=1,使D7~D4的数据0111传送到总线
地址0111:设置D9=1,把总线数据传送给“十六进制数码管”显示出来
地址1000:跳转到地址1000,在本指令无限循环
它的演示动画如下图所示。
我们可以看到,电路运行后,“十六进制数码管”显示了7,符合我们预期的结果,因为3大于等于2,指令运行了“把7传送给数码管”的逻辑。如果把开始4条指令中的数据部分“3”和“2”互换一下,那么当电路运行后,你看到结果就是最终数码管显示0,因为3大于等于2不成立,指令运行了“什么也不做”的逻辑。