jpcsp源码解读9:指令的抽象描述与指令的译码

本文尝试说明jpcsp中译码器单元的实现方式。

/

首先是对指令的一个抽象描述,Instruction类:

    public static abstract class Instruction

/

java科普:

注意这是一个抽象类,不可以被实例化。只有在其某个子类中实现该抽象类中的所有抽象方法,才可对该子类实例化。而该子类的实例可以被赋值给这个抽象类的实例。

也就是说,抽象类实例化时,实例一定是某个实现了所有抽象方法的子类构造出的实例,而不是从该抽象类本身构造实例。

/

在这个指令抽象类中,提供了一些抽象方法和非抽象方法。抽象方法需要在子类中实现;非抽象方法,子类则不需要实现。

成员变量:

    private int m_count = 0;//这条指令被执行的次数作计数
    private int flags = 0;//关于这条指令的一些信息

flag中包含的信息有:是否分支指令,是否跳转指令,是否是一个基本块的入口 等。在这里提供一个感性的认识,不一一列出,具体可以参加源码。

提供的一个关键方法

    public abstract void interpret(Processor processor, int insn)

也就是这种类型的指令的实现方法,或者说,这种类型的指令打算如何从指令本身中提取自己需要的参数,然后使用这些参数要求处理器做出怎样的动作。

注意这是一个抽象方法,jpcsp采用的手法是,为每种类型的指令,定义Instruction的一个子类,在这个子类中实现这些抽象方法。每个子类实例化一个对象出来,以便译码操作时使用。

另外还提供了一些方法,关于指令的编译,反汇编,提取指令的flag信息,该指令被执行的次数统计 等。

/

以上说明了指令的抽象描述,现在看这个抽象类的子类。

首先比较容易找到的一个子类是:

    public static abstract class STUB extends Instruction

注意这又是一个抽象类,并没有对指令的抽象类做实现。

stub的意思是存根,存根负责接收本地方法调用,并将它们委派给各自的具体实现对象。这里先提供一个语义模糊的描述,具体含义会在后文说明。

/

然后是对指令集中的每一条指令,定义Instruction的一个子类。

这个步骤在一个类中完成:

public class Instructions

现在举其中一个例子,比如加法指令:

public static final Instruction ADD = new Instruction(22) {


@Override
public final String name() { return "ADD"; }


@Override
public final String category() { return "MIPS I"; }


@Override
public void interpret(Processor processor, int insn) {
int rd = (insn>>11)&31;
int rt = (insn>>16)&31;
int rs = (insn>>21)&31;




                // just ignore overflow exception as it is useless
                processor.cpu.doADDU(rd, rs, rt);
            
}
@Override
public void compile(ICompilerContext context, int insn) {
if (!context.isRdRegister0()) {
if (context.isRsRegister0() && context.isRtRegister0()) {
context.storeRd(0);
} else {
context.prepareRdForStore();
if (context.isRsRegister0()) {
context.loadRt();
} else {
context.loadRs();
if (!context.isRtRegister0()) {
context.loadRt();
context.getMethodVisitor().visitInsn(Opcodes.IADD);
}
}
context.storeRd();
}
}
}
@Override
public String disasm(int address, int insn) {
int rd = (insn>>11)&31;
int rt = (insn>>16)&31;
int rs = (insn>>21)&31;


return Common.disasmRDRSRT("add", rd, rs, rt);
}
};

我们来看这段代码的结构:

public static final Instruction ADD = new Instruction(22) {

};

这是在定义抽象类 Instruction的一个实例,这个实例是其实现了抽象方法的子类的实例。{ ...};中就是子类的定义,其中实现了 Instruction中的所有抽象方法。

然后看他使用的构造函数,是Instruction(22),看构造函数的实现,是在 Instruction类中的非抽象方法,行为就是把这个实例放进了一个数组里。那个数组的用处暂时未知。

再看其中对抽象方法 interpret的实现:

@Override
public void interpret(Processor processor, int insn) {
int rd = (insn>>11)&31;
int rt = (insn>>16)&31;
int rs = (insn>>21)&31;




                // just ignore overflow exception as it is useless
                processor.cpu.doADDU(rd, rs, rt);
            
}

也就是说,从指令中提取了rd,rt,rs这三个寄存器号,作为参数,要求处理器做加法操作。关于cpu类的内部实现,见前一篇,jpcsp源码解读8.

/

至此,我们已经有了每一条指令的行为描述。

然后,将这些东西组织成一个译码器:

         public class Decoder

实现的手法是,将这些指令的实例组织进一个数组:

public static final Instruction table_0[] = {

...

...

}

这样就可以用指令中的操作码作为索引,从数组中提取出相应的指令。

/

MIPS科普:

mips指令集中,每条指令32位,其中最高6位(31:26)是opcode,也就是操作码,决定这条指令的类型。比如,跳转指令。对于其中的某一种类型,比如r-type,寄存器类型,行为是两个寄存器中的内容做运算,结果存放到第三个寄存器中。寄存器号被编码在指令中(25:21,20:16,15:11)。具体做哪一种运算,取决于指令的最低6位(5:0)

/

回到译码器类,这个类中只有一个方法:

    public static final Instruction instruction(int insn) {
        return table_0[(insn >> 26) & 0x0000003f].instance(insn);
    }

行为很简单,提取指令的最高6位,作为索引,取出数组中的相应元素,并调用该元素的instance方法,取得实例。

但是这里有一个问题,有些指令,根据最高6位只能确定指令的大类型(比如r-type),然后还要解析指令中的其他位,来确定具体是哪一条指令(是r-type中的add,还是sub)。

这里的手法是,对于最高6位就可以确定具体哪一条指令的,数组中放置该指令的实例作为元素(比如跳转指令);对于最高6位只能确定是某个大类的,数组中放置一个stub作为元素。这个stub元素的instance方法,会提取指令中相应的位(比如r-type位置的stub就提取指令中的最低6位),然后用提取出的这些位,去索引另一张表,从而确定具体是哪一条指令。

这也就是前文提到的stub的作用,作为存根,然后提取参数并分发任务。

这也解释了为什么译码方法中,取得数组元素之后还要调用元素的instance方法。对于已经查到的指令实例,instance方法直接返回this,也就是该元素本身;对于索引位置的元素为stub的情形,stub的instance方法进一步索引另一个表,并调用所得元素的instance方法,即可得到具体某一条指令的实例。

/

总结起来,就是每条指令有一个行为描述,将这些指令的行为描述组织为表格;从指令中提取出操作码,用操作码去索引表格,就可以得到指令的行为描述。

现在,通过译码单元,我们可以从二进制编码的指令得到该指令的行为描述。

下一篇,将说明模拟器中指令的执行过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值