COMET虚拟机的设计与实现
2.8 调试器
COMET调试器是一个内嵌在虚拟机里的机器级的调试器。当需要调试一个COMET虚拟机的程序时,只需要在启动COMET虚拟机的时给出相应的命令参数就启动调试功能了[2,4]。
COMET调试器的基本功能有:显示帮助(help),运行程序直到停止(go),分步执行(step n),跳转程序(jump),显示寄存器内容(regs),显示内存数据(dMem),显示内存指令(iMem),修改内存数据(alter),遍历指令(trace),指令记数功能(print),重新装载字节码(clear),退出调试器(quit)。每个调试命令的具体用法可以参考COMET虚拟机的帮助文件。
3 COMET虚拟机实现 3.1 虚拟机数据结构
struct comet
{
off_t pc;
short fr;
short gr[5];
short mem[MEMSIZE];
} cmt;
虚拟机结构变量cmt是一个全局变量,成员分别为:指令计数器(pc)、标志寄存器(fr)、通用寄存器(gr)、存储器(mem)。将cmt设计为全局变量的优点是个函数不用传递复杂的结构体参数,缺点是每个进程同时只能有一个虚拟机实例。
3.2 主函数
int
main(int argc, char *argv[])
{
init(argc, argv);
if(debug) comet_debug();
else while(comet_step());
fclose(source);
return 0;
}
函数init首先初始化COMET虚拟机并装载字节码,如果发生错误则停止。然后根据调试器状态,选择运行虚拟机的方式。如果调试开关(debug)被设置,则调用comet_debug函数在调试状态下运行COMET虚拟机。如果没有打开调试开关,则循环调用单步执行函数comet_step,直到程序结束[1,2]。
3.3 字节码载入
void
comet_load(void)
{
unsigned short n, flag[2];
fseek(source, 0, SEEK_SET);
n = fread(flag,
sizeof(off_t), 2, source);
n = fread(cmt.mem[flag[0]],
sizeof(off_t),tmp[1],source);
/* 其他处理代码 */
}
变量n用于记录读取字节码的数目,如果n小于相应的值,则发生字节码装载错误。变量flag用于保存字节码装载信息,分别字节码装载地址和字节码大小。
COMET字节码设计比较简单,也存在很多不足。例如,没有标志文件格式的魔数,没有更完善的错误检测措施。我们的目的是让读者了解字节码的工作原理,因此只给出了一种最简单的实现[1,2,4]。
3.4 指令解析
指令的解析一般包含这个几个过程:取指令,解码,执行。其中解码对虚拟机的执行效率有很大的影响[5]。这里采用下标索引技术来解码指令。具体代码如下:
void comet_ld(void); /* LD指令*/
void comet_ld(void); /*ST指令 */
void comet_ld(void); /* LEA指令 */
/* 其他指令函数声明 */
int comet_step(void)
{
static void (*comet_op)() = {
comet_ld, comet_st, comet_lea,
/* 其他指令执行函数指针 */
};
/* 解析指令,存放在op中 */
short op = get_op();
/* 执行op对应的代码 */
(*comet_op [op])();
/* 返回执行状态 */
return val;
}
例如,有指令LEA,其对应的机器码为031,那么将通过函数指针数组comet_op直接定位到(*comet_op[031])函数,即并执行相应的comet_lea函数。
3.5 输入输出设备
COMET虚拟机在解析每个指令前,先读取IO设备状态寄存器IO_FLAG中的值,如果IO_FLAG被设置,则执行相应的IO操作[3]。具体代码如下:
void
comet_io(void)
{
switch(cmt.mem[IO_FLAG]IO_TYPE)
{
case IO_NULL:
/* 无IO操作 */
case IO_OCT IO_IN:
/* 八进制输入 */
case IO_DEC IO_IN:
/* 十进制输入 */
case IO_HEX IO_IN:
/* 十六进制输入 */
case IO_OCT IO_OUT:
/* 八进制输出 */
case IO_DEC IO_OUT:
/* 十进制输出 */
case IO_HEX IO_OUT:
/* 十六进制输出 */
default:
/* 未知IO类型 */
}
/* 重置IO状态寄存器IO_FLAG */
}
3.6 调试器
调试程序是建立和单步执行的COMET虚拟机之上的。当没有打开调试功能时,循环执行COMET虚拟机字节码程序,直到停止。当打开了调试功能时,调试函数debug根据调试命令,执行相应步的指令、显示或操作相关的数据。
/* 各种调试命令 */
typedef enum
{
GO, STEP, JUMP, REGS,
IMEM, DMEM, ALTER,
TRACE, PRINT, CLEAR,
HELP, QUIT
} DbType;
/* 调试函数 */
void
comet_debug(void)
{
int cmd; /* 保存调试命令 */
while(1) {
/* 读调试命令 */
switch(cmd) {
case HELP: /* 帮助文件 */
case GO : /* 执行程序 */
case STEP: /* 分步执行 */
/* 其他调试命令 */
default : /* 未知命令 */
}
}
}
调试函数comet_debug根据不同的调试命令执行相应的操作,并显示虚拟机状当前的状态信息。
4 运行虚拟机
下面通过一个求(1 + 2 + … + n)的程序,来介绍其在COMET虚拟机上的执行的流程。程序的字节码由相关的工具生成,保存为sum.comet文件(后缀为comet)。
4.1 普通运行
输入命令:comet sum
在COMET虚拟机获得sum参数后,会自动识别为sum.comet字节码文件。
输入100,表示求1+2+…+100的和。
COMET虚拟机输出:
===============
COMET虚拟计算机
存储器相关文章:存储器原理
尘埃粒子计数器相关文章:尘埃粒子计数器原理
评论