第17章:自动操作
Last updated
Last updated
我们希望通过一组指令(下图)来实现对硬件的控制。
操作码
代码
Load (加载)
10h
Store (存储)
11h
Add (加)
20h
Substract (减)
21h
Halt (停止)
FFh
注:h表示16进制。
我们先实现一个最简单版本的自动加减法器,直接上图:
以下是一些说明:
通过一个与时钟相连的16位计数器,我们能够从头开始逐个访问每个地址中的内容。两个的RAM分别用于储存指令和数据,指令和对应的数据储存在不同RAM的相同位置。
控制取反器的信号用来选择加法或者是减法,2-1选择器选择数据是直接读入到锁存器还是读入到加法器中,通过W写信号将锁存器内容存储回RAM。通过以上的信号和其他一些多选1逻辑门,我们可以将指令RAM中读取到的指令转化为硬件信号。
如下图的例子所示,如果我们想将56h和2Ah相加,结果再减去38h,我们只需要在对应的地址上输入对应的代码即可。
地址
代码
操作
地址
数据
0000h:
10h
Load
0000h:
56h
0004h:
20h
Add
0004h:
2Ah
0008h:
21h
Substract
0008h:
38h
000Ch:
11h
Store
000Ch:
计算结果
0010h:
FFh
Halt
0010h:
--
我们希望能够处理更高位数的加减法,并且不使用指令地址对应数据地址指令这样笨拙的方法。我们将每个指令的大小从1字节扩展到3字节,除了首字节仍旧存放指令外,剩下两个字节用于存放16位的地址。
为了处理更高位的加减法,如16位加减法,我们可以将其拆解为低8位加减法和高8位加减法,注意到高8位加减法还需要处理低8位的进位/借位信息,因此我们将原有的指令集扩展成7个:
操作码
代码
Load (加载)
10h
Store (存储)
11h
Add (加)
20h
Substract (减)
21h
Add with Carry (进位加法)
22h
Substract with Borrow (借位减法)
23h
Halt (停止)
FFh
新的电路示意图如下:
我们将指令和数据放在同一个RAM中,使用四个锁存器,通过一个选择器来决定每次是将数据加载到指令锁存器、低位地址锁存器、高位地址锁存器还是数据锁存器。
通过一个2-1选择器来决定寻址是从地址锁存器中获得还是从16位计数器中获得。
每次我们能够读取一个8位数据,在现有的结构下,执行一个指令需要4次读取数据或是类似的操作,即需要花费4个时钟周期。
?此处作者并没有说明如何实现在4个锁存器中选择,并且16位计数器的计数会在第四个时钟周期时暂停。
我们首先考虑通过跳转指令(30h)来实现指令存储地址的不连续性。通过跳转指令和其后的两位地址,我们可以将指令地址强行跳转至指定位置继续执行。也就是说,计数器会被强制输出跳转指令后的16位地址。回忆14章的边沿触发器中的预置功能,我们可以将地址锁存器与16位计数器的预置装置相连接,实现地址的强制跳转。(应该也可以利用类似的设置实现计数器暂停?)
我们现在考虑A7h(263)和1Ch(28)相乘得到16位数的结果,我们将所有的数字都用16位表示,并存储于此:
地址
数据
1000h
00h
16位乘数保存在此
A7h
1002h
00h
16位被乘数保存在此
1Ch
1004h
00h
16位乘积保存在此
00h
指令则如下:
地址
数据
0000h
10h
将1005h处的数据载入到累加器
10h
05h
0003h
20h
将1001h处的数据加到到累加器
10h
01h
0006h
11h
将累加器的结果保存到1005h处
10h
05h
0009h
10h
将1004h处的数据载入到累加器
10h
04h
000Ch
22h
将1000h处的数据进位加到到累加器
10h
00h
000Fh
11h
将累加器的结果保存到1004h处
10h
04h
0012h
...
...
通过以上的操作,我们将1000h处的数据加载到了1004h处,也即实现了。
我们考虑引入条件跳转。此处我们考虑的是零条件跳转,即累加器的八位结果全部为0时,才进行跳转。我们引入一个1位锁存器,将其称为零锁存器(zero latch),结构如下:
通过一个8输入-或非门,只有全部为0时输出才为1,并且与进位锁存器的时钟输入一样,只有当Add、Add with Carry、Substract、Substract with Borrow这几个指令执行的时候,锁存器才锁存一个值(?如何实现),将其称为零标志位(zero flag)。在引入进位锁存器之后,我们可以引入新的几条指令:
操作码
代码
Load (加载)
10h
Store (存储)
11h
Add (加)
20h
Substract (减)
21h
Add with Carry (进位加法)
22h
Substract with Borrow (借位减法)
23h
Jump (跳转)
30h
Jump if Zero (零转移)
31h
Jump if Carry (进位转移)
32h
Jump if not Zero (非零转移)
33h
Jump if not Carry (非进位转移)
34h
Halt (停止)
FFh
并且我们可以利用非零转移实现乘法:
地址
数据
...
...
...
0012h
10h
将1003h处的数据载入到累加器
10h
03h
0015h
20h
将1001h处的数据加到到累加器
00h
1Eh
0018h
11h
将累加器的结果保存到1003h处
10h
03h
001Bh
33h
如果零标志位不为0,则跳转至0000h处
00h
00h
001Eh
FFh
停止
注意到我们是将1Ch与001Eh处的FFh相加,相当于将1Ch减1。这样我们就通过机器码的编程,实现了乘法的操作。
我们可以通过助记符来标记不同的指令:
操作码
代码
助记符
Load (加载)
10h
LOD
Store (存储)
11h
STO
Add (加)
20h
ADD
Substract (减)
21h
SUB
Add with Carry (进位加法)
22h
ADC
Substract with Borrow (借位减法)
23h
SBB
Jump (跳转)
30h
JMP
Jump if Zero (零转移)
31h
JZ
Jump if Carry (进位转移)
32h
JC
Jump if not Zero (非零转移)
33h
JNZ
Jump if not Carry (非进位转移)
34h
JNC
Halt (停止)
FFh
HLT
举个例子:
注意到是否加[]的区别,[地址]表示取地址中的数据,而没有[]的地址表示地址本身。