这里要说的指令,并不是x86体系结构下的机器指令,而是虚拟机指令。Jerry虚拟机的设计并不是非常复杂——甚至看起来很不灵活——它是一个栈式虚拟机,不包括任何寄存器(除了指令计数器之外),所有的指令都是基于栈进行的。在文章最后将给出完整的指令集。现在先给出指令数据结构,它们包括
typedef enum {
// ... 指令集,文章最后会贴出完整的
} InstructionCode;
// 依旧 COOL 风格的成员
// segOffset 指令的偏移值
// code 指令码
#define memberAbstractInstruction \
int segOffset; \
InstructionCode code; /*******/
// 指令数据结构基类
struct AbstractInstruction {
memberAbstractInstruction
};
// 无参数的指令
struct NoParamInstruction {
memberAbstractInstruction
};
// 带整型参数的指令
struct IntParamInstruction {
memberAbstractInstruction
int param;
};
// 带实型参数的指令
struct RealParamInstruction {
memberAbstractInstruction
double param;
};
// 跳转指令
// targetIns 目标指令指针
struct JumpInstruction {
memberAbstractInstruction
struct AbstractInstruction* targetIns;
};
也许你会觉得奇怪,为什么跳转指令不计入整型参数的指令,要单独拿出来,还弄个指针参数?如果这是个解释器的话,问题还不大,但指针如果存放进字节码文件再取回内存,如果运气不是好到雇佣大猩猩能敲出Jerry编译程序的话,那么指针指向的地址将不是我们期望的。因此,这里的目标指令指针的含义将会在最后写出指令时变化为整数偏移量。
不在这一步就算好所有指令的地址,然后用整数来保存这个域,是考虑到后期优化。否则,某些删去无用指令的优化,将会导致跳转指令的偏移失效。为了避免每次这样小动作都重新计算跳转指令的偏移,方法就是,让跳转指令的偏移量在写出指令到文件之前的最后关口才确定下来。确定的方法就是用指令的 segOffset 域差值来计算——这样一来 segOffset 的值也必须到那个时候(之前一点点)计算才行。
刚才提到了,虚拟机是基于栈的,这就带来了很多偷懒的机会,比如加法指令就不用去操心拿那几个寄存器摆弄了。顺带提一下,Jerry的指令大多数是定长的,只有一部分带参数指令会将参数放在指令末尾,它们的长度较其他指令长。
运算指令
INT_PLUS 弹出栈顶 B 和次栈顶 A,计算 A + B 压入栈中
INT_MINUS -
INT_MULTIPLY *
INT_DIVIDE /
INT_LT <
INT_LE <=
INT_EQ ==
INT_NE !=
INT_GT >
INT_GE >=
注意上面 A 是次栈顶,而 B 是栈顶,下面也是。
INT_ASSIGN 弹出栈顶 B 和次栈顶 A,将 B 的值写入 A 指向的地址中去,然后在栈顶压入 B 的值。
对于实型数,有一整套对应的指令:
REAL_PLUS, REAL_MINUS, REAL_MULTIPLY, REAL_DIVIDE, REAL_ASSIGN,
REAL_LT, REAL_LE, REAL_EQ, REAL_NE, REAL_GT, REAL_GE
IO 指令
READ_INT 弹出栈顶 A,输入一个整数,将值放入 A 指向的地址中去。
READ_REAL 实数
WRITE_INT 弹出栈顶整数 A 输出。
WRITE_REAL 实数
运行时类型转换
INT_2_REAL 弹出栈顶整数,转换成实数压回栈中
REAL_2_INT 实数 整数
常数加载
LOAD_INT 弹出栈顶 A,输入一个整数,从 A 指向的地址加载一个整数到栈顶
LOAD_REAL 实数
弹栈
POP 其实就是单纯地让栈顶指针向基部靠拢,这是一个带整型参数的指令,参数说明了弹出的字节数。
常数加载
CONST_INT 加载一个常数整数到栈顶,这是一个带整型参数的指令。
CONST_REAL 实数 实型
控制指令
JMP 无条件跳转,参数为整数,表示偏移量
JMP_IF_TOP 弹出栈顶整数 A,如果 A 不为 0,则跳转,参数为整数,表示偏移量
JMP_NOT_TOP 为
END_PROGRAM 终止程序
其他
NOP 无操作
结束。
typedef enum {
INT_PLUS, INT_MINUS, INT_MULTIPLY, INT_DIVIDE, INT_ASSIGN,
INT_LT, INT_LE, INT_EQ, INT_NE, INT_GT, INT_GE,
REAL_PLUS, REAL_MINUS, REAL_MULTIPLY, REAL_DIVIDE, REAL_ASSIGN,
REAL_LT, REAL_LE, REAL_EQ, REAL_NE, REAL_GT, REAL_GE,
INS_NOT,
READ_INT, READ_REAL, WRITE_INT, WRITE_REAL,
INT_2_REAL, REAL_2_INT,
LOAD_INT, LOAD_REAL, POP,
CONST_INT, CONST_REAL,
JMP, JMP_IF_TOP, JMP_NOT_TOP,
NOP, END_PROGRAM
} InstructionCode;
// 根据算术符号的枚举值获取对应操作码,注意两个枚举的顺序要一致
#define getCodeByIntOp(x) ((x) - PLUS + INT_PLUS)
#define getCodeByRealOp(x) ((x) - PLUS + REAL_PLUS)
// 是否为无参数操作
#define isNoParamIns(x) (((x) >= INT_PLUS && (x) <= LOAD_REAL) \
|| NOP == (x) || END_PROGRAM == (x))
// 是否为整型参数操作
#define isIntParamIns(x) (POP == (x) || CONST_INT == (x))
// 是否为实型参数操作
#define isRealParamIns(x) (CONST_REAL == (x))
// 是否为跳转
#define isJump(x) (JMP <= (x) && (x) <= JMP_NOT_TOP)
分享到:
相关推荐
总的来说,TM虚拟机是一个基于C语言实现的微型计算机系统,它通过指令集、寄存器和数据区来模拟计算机的运行环境。学习和研究TM虚拟机不仅可以深化对计算机系统理解,也是提升编程技能和解决实际问题的有效途径。
### ANDROID 指令集 Bytecode #### 一、概览 在深入探讨之前,我们需要先理解几个关键概念:Android、Dalvik虚拟机(Dalvik Virtual Machine, DVM)以及字节码(bytecode)。Android操作系统是基于Linux内核的操作...
2. 数据类型和数据结构:Java虚拟机支持的数据类型分为基本类型和引用类型,其中基本类型包括整型、浮点型、布尔型和字符型等,引用类型则包括类类型、接口类型和数组类型。JVM规定了这些类型在虚拟机中的表示方法和...
- **ltable**:定义了Lua表的数据结构; - **ltm**:包含一些标签函数; - **lundump**:支持预编译Lua块的功能。 #### 八、`Lua_State` 结构 `Lua_State`是Lua全局状态的核心结构,它包含了Lua运行时的所有重要...
- **class文件格式**:定义了类和接口的数据结构,用于描述类或接口的方法和字段。 - **数据类型**: - **原始类型**: - **整型**:包括byte、short、int、long等。 - **浮点型**:包括float、double等。 - **...
最后,代码生成阶段将AST转换为目标代码,这可能是汇编语言或者像TM虚拟机这样的特定虚拟机指令。 TM虚拟机是一种简单的指令集架构(ISA),它模拟计算机硬件的行为,执行编译器产生的目标代码。TM虚拟机的指令集...
4. **数据结构与算法**:在汇编层面,可能需要使用到简单的数据结构(如数组)来表示游戏地图和蛇的状态,以及算法(如搜索算法)来决定蛇的移动方向。 5. **输入/输出处理**:游戏需要接收用户输入,并显示游戏...
例如,jstack用于生成虚拟机当前时刻的线程快照,jmap用于生成堆内存转储文件,jconsole和VisualVM等工具用于可视化监控JVM的运行状况。 #### 12. 跨平台的原理 Java的跨平台能力主要归功于JVM的抽象,它允许Java...
`set.c`和`set.h`可能与栈操作有关,用于支持编译器内部的数据结构管理。而`编译原理实践教程.doc`则可能是一个详细的指南,涵盖了PL/0编译器设计的理论和实践步骤,包括如何进行词法分析、语法分析,如何生成栈式...
JVM的指令集包括控制指令、数据处理指令、类型转换指令、异常处理指令等,涵盖了基本的数据运算和流程控制功能。 #### 堆管理与垃圾回收 堆是JVM中最活跃的内存区域,负责对象的动态分配。为了防止内存泄漏,JVM...
1. **指令集架构**:TM虚拟机有一个特定的指令集,这些指令是TINY语言编译器产生的。每个指令对应一个特定的操作,如加载、存储、算术运算、逻辑运算以及控制流程转移等。 2. **内存模型**:在TM虚拟机中,程序的...
**Android Dalvik虚拟机指令格式与位描述索引表** 在Android系统中,Dalvik虚拟机(Dalvik Virtual Machine,DVM)是运行应用程序的核心组件。它采用了优化的字节码格式,使得在资源受限的移动设备上高效执行代码...
编译器需要考虑目标架构的指令集,如X86或ARM,并进行指令选择、寄存器分配、循环展开等一系列优化,以生成高效的目标代码。 5. **基于寄存器的虚拟机**:不同于传统的栈式虚拟机,基于寄存器的虚拟机使用一组预先...
- **概念**:帧是用于支持虚拟机进行方法调用和执行的基础数据结构之一,对应于虚拟机栈中的一个条目。 - **组成**: - **局部变量表**:用于存储方法参数和方法内部定义的局部变量。 - **操作数栈**:用于执行...
CASL(可能代表一种特定的计算机抽象组装语言)汇编器和CASL虚拟机是用于理解和执行基于CASL指令集的程序的工具。在软件开发领域,汇编器是一种编程工具,它将人类可读的汇编语言代码转换成机器可执行的二进制代码。...
Oracle发布的《Java虚拟机规范_JavaSE 10》是一份详细描述JVM工作方式的官方文档,涵盖了JVM的结构、数据类型、指令集以及如何处理Java类文件等多个方面。 首先,文档介绍了Java虚拟机的历史和组织结构。JVM最初由...
Java虚拟机规范详细定义了JVM的工作原理和行为,包括类文件结构、内存模型、指令集、垃圾收集机制、类加载机制等关键概念。这份PDF版本的《Java虚拟机规范》将提供全面的知识点,帮助读者掌握以下核心内容: 1. **...