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

news/2024/12/23 9:12:49/

本文尝试说明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方法,即可得到具体某一条指令的实例。

/

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

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

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


http://www.ppmy.cn/news/563663.html

相关文章

用Lucene的SpellChecker实现Google的“您是不是要找”功能

引言 很多人在使用搜索引擎的时候,会出于各种原因,拼错想要搜索的关键字,比如键盘有问题(某个按键坏了)、不熟悉国际名称(弗洛伊德的全名 Sigmund Freud)、不小心写错字母(Sinpsons…

wang----- 用Java实现Google的“您是不是要找”功能

文章来源:http://blog.csdn.net/haydenwang8287/article/details/5777112 引言 很多人在使用搜索引擎的时候,会出于各种原因,拼错想要搜索的关键字,比如键盘有问题(某个按键坏了)、不熟悉国际名称&#xff…

用Java实现Google的“您是不是要找”功能

引言 很多人在使用搜索引擎的时候,会出于各种原因,拼错想要搜索的关键字,比如键盘有问题(某个按键坏了)、不熟悉国际名称(弗洛伊德的全名Sigmund Freud)、不小心写错字母(Sinpsons&a…

algodoo是什么意思_�教学创新-中国大学mooc-试题题目及答案

请教:计算机等级考试二级公共基础知识练习题(1)第2大题第11小题如何解答? 关于网站sql和js的攻击方式的具体过程 请教:2011年会计从业考试《会计基础》全真模拟试卷(1)第2大题第10小题如何解答? C语言 malloc 有乱码 dev c&#x…

psp模拟java_PSP模拟器 JAVA环境搭建问题:

展开全部 jpcsp-windows-x86 2. start-windows-x86.bat 内如如下:62616964757a686964616fe78988e69d8331333335336434 echo off rem CD to the path of the command line, this is required when running as an administrator cd /D "%~dp0" set PATH%PA…

linux ppsspp速度,PPSSPP模拟器详细使用技巧

PPSSPP模拟器是一款国内非常优秀流行的专业psp模拟器,PPSSPP模拟器虽然出现较晚,但是其拥有比jpcsp更加完善的功能以及更加快速的模拟速度,PPSSPP模拟器超越平台限制的免费游戏模拟器它支持Windows、Linux、iOS、Android等系统,用户通过PPSSPP模拟器能够回味很多经典有趣的…

PSP联机插件pro online

PSP在线联机插件pro online PRO online是PSP用在线联机插件,有了这个插件,你不需要盟卡或者USB线就能和好友进行互联网远程联机。 目前PRO online已经出到PRO oneline 0.10(R58)版,还在开发中,并不是所有游戏都能联机…

PHP openssl_sign()函数代码示例

本文整理汇总了PHP中openssl_sign函数的典型用法代码示例。如果您正苦于以下问题:PHP openssl_sign函数的具体用法?PHP openssl_sign怎么用?PHP openssl_sign使用的例子?那么恭喜您, 这里精选的函数代码示例或许可以为您提供帮助。…