N+3、将JAVA编译为IJVM

1.机器指令和微指令的关系
① 一条机器指令对应一个微程序,这个微程序是由若干条微指令构成的。
②从一般指令的微程序执行流程图可以看出。每个CPU周期就对于一条微指令。
2.寄存器
CPP:指向常量池
LV:指向局部变量结构
SP:栈顶的指针
PC:指向指令流中下一个字节的地址
MBR:保存来自内存中的指令流的一个等待解释执行的字节
TOS:在每条指令开始和结束时,TOS都保存SP指向的内存位置的值。
OPC:OPC寄存器是个临时寄存器,没有固定用途
3.微指令特点
①对于上篇文章的图,那些 符号语言 在一行中定义了在一个时钟周期内发生的所有动作,即,表中的一行就是一个时钟周期。而表中我们可以发现,也许一行不止一个命令,那些是可以并行的指令。
②其实,从上篇文章就可以看出,除了第一条指令Main1之外,每条语句微指令的最后都有一条返回Main1的语句。主循环的起点起点就是Main1。而主循环开始时,PC已经指向包含操作码的内存地址。
③关于微指令中的rd、wr是用于指示内存(MDR)读写4字节的数据字。
4.微指令解释
以上篇博文的那张表为参照。从主循环开始的微指令为例,即Main1
①PC加1,让它指向操作码之后的第一个字节的地址。
②启动把下一个字节读入MBR的内存操作。反正这个字节迟早会用到的。
③执行多路转移,跳转到Main1起始处MBR指向的地址,取得下一个字节操作。

如果主循环的跳转到了标号为iadd1的微指令处时,即开始执行整个IADD
①明显TOS寄存器早已经有内容了(TOS里存储的一直是栈顶的值),也但还需要把栈中栈顶之下的字从内存中读出。
②把TOS和刚才从栈中读出的字相加。
③把结果压入栈,也就是存入内存,同时还要将结果存入TOS寄存器中。
iadd1:iadd1 目的是为了从内存中取操作数,要把栈顶指针减1写入MAR中。过程是 首先将SP减1,然后把结果同时写回这两个寄存器。另外,在MPC相应的值也在这个周期发生改变,MPC的值变成了iadd1的NEXT_ADDRESS字段的值,即iadd2的地址。
iadd2:通过MPC的地址,从 控制存储器 中读出iadd2的操作内容:在第二个周期等待 从内存中读出的操作数,与此同时,我们把栈顶的字从TOS拷贝到H。
iadd3:在这个周期中,MDR和H中的内容相加,结果同时存回MDR和TOS,还要启动写操作,把新的栈顶字写回内存。最后,用goto为MPC分配Main1的地址。
如果MBR中的下一条指令是ISUB,则与IADD类似,只是最后一步是减法。IORIAND的指令也与其类似,都只是最后一步的操作不一样。

(有疑问可以查上一篇博文,如果对指令集不熟悉,则查阅上上篇博文。)

DUP指令比较容易理解,单纯地拷贝栈顶的值,而栈顶的值已经在TOS中了,则把SP加1,再把TOS的值放入栈顶,就OK了。

POP指令也简单,对SP-1,指向下面的值,上面的值自然就被抛弃了。

BIPUSH指令复杂一点,操作码的后面还有一个 字节,这个字节代表 一个带符号整数。而在Main1周期中,这个字节已经被取到了MBR中,此字节被扩展到32位然后压入栈。其中的fetch指令就是通过字节端口取一个字。
因此,其操作就是把MBR中的字节符号扩展到32位,再拷贝到MDR中,最后,将SP+1并拷贝到MAR中,这样就可以把操作数压入栈顶了。
ILOAD指令也是在操作码的后面有一个字节,这个字节是一个指向局部变量结构的指针它所指的结构变量将被压入栈中。
因为只有一个字节,只能区分2^8=256个字。为了决定读操作的地址,MBR中的值将和LV,由于MBR和LV都只能通过V总线访问,所以LV将首先拷贝到H寄存器中,然后再和MBR相加。假发的结果将存入MAR然后启动读操作。

ISTORE的指令与ILOAD的操作相反,它是专门将一个字从栈顶弹出,存到内存中,可以轻易看出,他们的微指令是比较相似的。指令格式也比较类似。
实际上,这两条指令都是受限制的,因为后面都只有一个字节,只能访问到256个字。此时,就需要WIDE指令——这个特殊的操作码,来作为前缀增加能访问的变量了。如下图,添加 WIDE前缀的时候,就能在后面带上两个字节了,即16位的索引了。
WIDE指令的执行过程比较特殊,会在下一篇博文中阐述。

猜你喜欢

转载自blog.csdn.net/qq_40891541/article/details/80030411