一开始其实我是一头雾水的,完全不知道要干什么,不过看了几个Example之后,也自己能猜的大概,然后试试,果真如此,非常开心。
下面就来一句句地实现。
首先指令是这样的格式:
现在是我来设计指令,那一切都是听我的,OP-CODE
就完全是我自己定义的了,我想让他是多少就是多少。
第一条指令是INL
,就是INPUT
的低 $8$ 位送到寄存器DR
,我想让这句话的OP-CODE
是 $001$。
而RS
在这里是不适用的,因为没有源寄存器,就是从I/O
获得的数据,我用 $XX$ 表示。
RD
是目标寄存器,是编码的时候确定的,我用 $YY$ 表示。
$0010XXYY$ 就是这条指令。
由于 $XXYY$ 是不确定的,所以显然不能用来散转,那么散转的指令就应该是 $0010$,是 $4$ 位操作码,因此,运用散转规则,得到的散转地址是 $11001000000$。
散转地址用 $16$ 进制表示,就是 $640$。
这意味着,我第一句指令,应该写在散转地址为 $640$ 的地方。
这之前的都不需要动,程序复位以后这两句话是没错的。
找到微地址为 $640$ 的地方。
这里要执行一句话,是从I/O
到寄存器,取低 $8$ 位,也就是可以认为是偶字节传递。
目的编码应该是通用寄存器。
原编码应该是I/O
。
因为是取第 $8$ 位,所以可以认为是偶字节传递。
这条指令到这里就执行完了,是一条长度为 $1$ 的指令。
因此,执行完以后,$\mu PC$ 需要做结尾变址,且 $PC = PC+1$。
单击“修改”,这条指令就被录入了,是在 $640$ 的地方。
接下来去指令系统定义这条指令。
这条指令的助记码是INL
,需要的操作数只有一个寄存器R0
,长度为 $1$。
指令码的话,如上所言,我定义的是 $0010XXYY$,用 $16$ 进制表示就是 $2X$,不妨就设置为 $20$。
那么,这条指令就应该被定义为:
INL R0 20 1
事实上,正如上面说的,指令码是 $2X$,所以其实定义为 $22$ 或者 $29$ 或者其他的形式都是可以的,最后程序装载、运行得到的散转地址都是 $640$,都能够正常运行。
为此,我还特意尝试过,确实是可以的。
但是定义操作码为 $30$ 显然不行,那就散转到 $660$ 去了。
类似地,定义第二条指令。
OUTL SR
,是将源寄存器的低 $8$ 位取出,输出到I/O
区域。
上面我用掉了 $001$,现在我想用 $010$。
那么这条指令就是 $0100XXYY$,取 $0100$ 进行散转,得到的散转地址是 $11010000000$,用 $16$ 进制表示就是 $680$,所以这一条指令应该写在散转地址为 $680$ 的地方。
是将寄存器的值输出到I/O
,所以源地址是寄存器,目的地址是准双向输入输出,取的是低 $8$ 位,所以总线规则是偶字节传递,这是单条指令,执行完就好了,所以 $PC++$ 且 $\mu PC$ 是结尾变址回到 $0001$。
类似地,定义指令系统。
指令码应该是 $0100XXYY$,所以就是 $4X$,我定义的是 $40$。
OUTL R0 40 1
第三条指令,
JMP
。
下面轮到 $011$ 了,那就是 $11011000000$,换成 $16$ 进制是 $6C0$。
因为地址是 $16$ 位的,所以势必需要分批次将数据读入。
且,这条指令的长度为 $3$,其中第 $1$ 个字节是指令,后面 $2$ 个字节都是数据,因此,应该要做 $PC=PC+3$。
第 $1$ 步,由于要读内存,所以要先取出 $PC$ 的值放到 $AR$,这时候 $PC$ 做第 $1$ 次递增,微地址递增。
因此,指令是 $4307C0$。
第 $2$ 步,将内存里面的值取出一个字节来,放到低位,那就相当于是奇字节到偶字节,我放到了寄存器BX
里面。
这时候,源地址就是内存,注意是RAM
而不是ROM
,所以选择了源是MRD
以后,需要选上E/M
,目的地址是我选择了BX
, 都无所谓,总线规则是奇字节到偶字节。
因此,指令是 $850400$。
第 $3$ 步,和第 $1$ 步是一样的,指令是 $4307C0$。
第 $4$ 步,要读内存里面的下一个字节了,放到高位,那就相当于是奇字节到奇字节,与第 $2$ 步是类似的,只要稍微改动一点,指令是 $840400$。
第 $5$ 步,数据准备完毕就可以跳了,跳的本质上就是用跳转到的地址覆盖掉 $PC$ 里面的值,这样的话下一次 $PC$ 取指取出来的就是跳过去那个地方的下一条指令了。
所以,执行的是,$PC$ 装载。
我是把数据放在BX
的,所以应该是源地址选择ALU
。
而ALU
是算数逻辑单元,现在我不需要计算 $A+B$ 或者 $A \& B$之类的东西,我只想要BX
的值,所以选择的是
目的地址应该是选择 $PC$ 装载。
微变址是结尾变址,因为已经执行完了。
$PC$ 装载了,自然也不存在保持还是增量这一说。
因此,指令是 $C039F2$。
回去定义指令系统。
操作数是内存,所以是*
,指令的长度是 $3$,操作码我刚才定义的是 $0110$,也就是$6X$,所以我写的是 $60$。
JMP * 60 3
指令全部设计好了,回过头看一眼,所有的微地址都是唯一的,不冲突,所以满足要求。
下面就可以编写汇编代码了。
这个就非常简单了。
data segment ;将程序装载到数据存储器 assume ds:data org 0 start: INL r0 OUTL r0 JMP start data ends end start
装载,看一眼,好像还挺正常。
微单步执行,正常取出第 $1$ 条指令 $20$,散转,得到 $640$,跳过去了。
然后执行INL
,我输入的是 $5936$,取低 $8$ 位,放到了寄存器。
然后正常回去,取下一条指令,并产生下址。
我重置I/O
为 $0000$,观察得到低 $8$ 位输出成功。
然后是JMP
。
这时候BX
的值已经是要回去的地址了,$0000$,然后ALU
将BX
的值存放到PC
。
然后就跳回去了,回到起点了,$0000$。实现了循环往复。
程序到此就结束了。
最后完整的代码附于本文文末。
下面就是画指令流程图。
机器指令格式在上面已经解释过了。
INL
是 $XXX0YYZZ$,OUTL
是 $XXX0YYZZ$,JMP
是 $XXX0YYYY[ADDR][ADDR]$。
至此,作业就写完了。
下面玩一些好玩的。
例如,我想从输入获得一个数,放到AX
,从输入获得一个数,放到BX
,然后执行 $AX+BX$,结果存到SP
。
也是完全可以的。
最后附上代码:
data segment ;将程序装载到数据存储器 assume ds:data org 0 start: INL r0 OUTL r0 JMP start data ends end start
INL R0 20 1 OUTL R0 40 1 JMP * 60 3
微地址 | 微指令 | 表达式 | μPC |
---|---|---|---|
0000 | 000000 | +1 | |
0001 | 0307C0 | AR=PC | +1 |
0002 | 800405 | IR=RAM | 0600 |
0640 | 4F0392 | REG=IO, PC++ | 0001 |
0680 | 4B0692 | IO=REG, PC++ | 0001 |
06C0 | 4307C0 | AR=PC, PC++ | +1 |
06C1 | 850400 | BX=RAM | +1 |
06C2 | 4307C0 | AR=PC, PC++ | +1 |
06C3 | 840400 | BX=RAM | +1 |
06C4 | C039F2 | PC=B | 0001 |
ROM和RAM的区别是什么啊 是指令放在ROM中 数据放在RAM中吗
不太理解为什么 JMP addr是从内存中读地址 为什么不是直接把addr赋值给PC
跳转,本质上是用一个地址覆盖掉 PC 的值,这样下一次执行的语句就是目标地址。
但是你没有办法把内存的值直接装载到 PC,你只能把寄存器或者 ALU 出来的东西装载到 PC,所以我们先把内存读到 BX,然后把 BX 装载到 PC。同时,这样还可以处理大小端,或者其他的内存计算。
太棒啦
tttttql 不过计组实验更新好少啊
顶!
非常感谢!
真的感谢楼主分享,期末实验差点就挂了,多谢多谢
希望出个后续,比如习题讲解和模拟题讲解
这玩意真的会有人看么……那我周末生产一下
支持支持
tqltqltql
放弃看ppt了,直接快速跟着jxtxzzw学习
keep up
我给你的回复,你能收到邮件通知吗?
62阅读,0回复,0点赞惨案?我以后要强制评论后才能阅读了…
回复