JIT动态编译器的原理与实现之Interpreter3
接下来,就是要实现一个虚拟机了。记得编码高质量的代码中有一条:不要过早地优化你的代码。所以,也本着循序渐进的原则,我将从实现一个解释器开始,逐步过渡到JIT动态编译器,这样的演化可以使原理看起来更清晰。
解释器的原理很简单,就是一条指令一条指令的解释并执行。具体流程分为:取出指令-解码指令-执行-返回主流程。这样形成一个无限循环,如下图所示:

这里的主流程就是上篇定义的程序rom.bin。但rom.bin不能直接运行,需要一个解释器来包裹它,来解释执行。解释器放在一个无限循环中,使得主流程无限运行不停止:
void loop(){ for(;;) { Interpreter(&CPUREG); }} |
这样,整个虚拟机的运行可以定义为:
memInit(); //初始化内存ResetCPU(&CPUREG); //初始化CPUloadROM(); //加载rom.binloop(); //执行主流程memFree(); //释放内存 |
接下来需要做的就是取出指令送入解释器了。为此需要定义读写内存的函数memGet和memSet:
void memSet(unsigned int, unsigned char);unsigned char memGet(unsigned int);void memSet(unsigned int addr, unsigned char data){ char Str_Err[256]; if(addr>64) { sprintf(Str_Err, "MEM: invalid mem write: 0x%8x", addr); MessageBox(NULL, Str_Err, "Warning", MB_OK); } else { RAM[addr & 0xff]=data; }}unsigned char memGet(unsigned int addr){ char Str_Err[256]; unsigned char val = 0; if(addr>64) { sprintf(Str_Err, "MEM: invalid mem read: 0x%8x", addr); MessageBox(NULL, Str_Err, "Warning", MB_OK); } else { val=RAM[addr & 0xff]; } return val;} |
读写均为一个字节。由于上篇定义的CPU寻址范围只有64字节大小,所以超过64字节就要给出错误提示。
然后需要为每一个CPU指令机器码实现一个解码执行函数:
void nop(REG*);void mov(REG*);void add(REG*);void cmp(REG*);void jmp(REG*);void jcp(REG*);void nop(REG* cpuREG){ cpuREG->R_PC++; sprintf("NOP\n");}void mov(REG* cpuREG){ memSet(cpuREG->R_PC+1, memGet(cpuREG->R_PC+2)); sprintf("MOV [0x%4x], [0x%4x]\n", cpuREG->R_PC+1, cpuREG->R_PC+2); cpuREG->R_PC+=3;}void add(REG* cpuREG){ memSet(cpuREG->R_PC+1, memGet(cpuREG->R_PC+1)+memGet(cpuREG->R_PC+2)); sprintf("ADD [0x%4x], [0x%4x]\n", cpuREG->R_PC+1, cpuREG->R_PC+2); cpuREG->R_PC+=3;}void cmp(REG* cpuREG){ if((memGet(cpuREG->R_PC+1)-memGet(cpuREG->R_PC+2)) < 0) { cpuREG->R_CMP=0; } else { cpuREG->R_CMP=1; } sprintf("CMP [0x%4x], [0x%4x]\n", cpuREG->R_PC+1, cpuREG->R_PC+2); cpuREG->R_PC+=3;}void jmp(REG* cpuREG){ sprintf("JMP [0x%4x] \n", cpuREG->R_PC+1); cpuREG->R_PC=memGet(cpuREG->R_PC+1);}void jcp(REG* cpuREG){ sprintf("JCP [0x%4x], [0x%4x]\n", cpuREG->R_PC+1, cpuREG->R_PC+2); if(cpuREG->R_CMP==0) { cpuREG->R_PC=memGet(cpuREG->R_PC+1); } else { cpuREG->R_PC=memGet(cpuREG->R_PC+2); } } |
这里最重要的是要小心处理PC寄存器。一开始CPU初始化的时候,PC寄存器是设为0的,而自定义的rom.bin也是从0地址开始执行的。如果你虚拟的CPU不是从0地址开始执行,那么在CPU初始化的时候就要把PC寄存器设为相应的开始地址。另外每一条指令可能涉及的地址数不相同,那么PC寄存器的变动也要不同。最后,跳转指令也可能要根据比较寄存器的内容来改变PC寄存器。
做了如上的准备之后就可以实现解释器了。这里用switch-case结构来决定哪条指令被执行。为了简单起见,用了一个函数指针来执行解码函数:
void (*func)(REG*);//Interpreter void Interpreter(REG* cpuREG){ char Str_Err[256]; switch(memGet(cpuREG->R_PC)) { case 0: func=nop; break; case 1: func=mov; break; case 2: func=add; break; case 3: func=cmp; break; case 4: func=jmp; break; case 5: func=jcp; break; default: sprintf(Str_Err, "Unhandled Opcode (0x%4x) at [0x%4x]", memGet(cpuREG->R_PC), cpuREG->R_PC); MessageBox(NULL, Str_Err, "Warning", MB_OK); return; } func(cpuREG);} |
首先从内存中取出数据,根据机器码来决定执行解码函数,最后执行。执行结果如下:

JIT动态编译器的原理与实现之Interpreter3的更多相关文章
- JIT动态编译器的原理与实现之Interpreter(解释器)的实现(三)
接下来,就是要实现一个虚拟机了.记得编码高质量的代码中有一条:不要过早地优化你的代码.所以,也本着循序渐进的原则,我将从实现一个解释器开始,逐步过渡到JIT动态编译器,这样的演化可以使原理看起来更清晰 ...
- gcc/g++等编译器 编译原理: 预处理,编译,汇编,链接各步骤详解
摘自http://blog.csdn.net/elfprincexu/article/details/45043971 gcc/g++等编译器 编译原理: 预处理,编译,汇编,链接各步骤详解 C和C+ ...
- java9新特性-21-java的动态编译器
1. 官方Feature 243: Java-Level JVM Compiler Interface 295: Ahead-of-Time Compilation 2. 产生背景 Oracle 一直 ...
- Java动态编译技术原理
这里介绍Java动态编译技术原理! 编译,一般来说就是将源代码转换成机器码的过程,比如在C语言中中,将C语言源代码编译成a.out,,但是在Java中的理解可能有点不同,编译指的是将java 源代码转 ...
- Java 动态调试技术原理及实践
本文转载自Java 动态调试技术原理及实践 导语 断点调试是我们最常使用的调试手段,它可以获取到方法执行过程中的变量信息,并可以观察到方法的执行路径.但断点调试会在断点位置停顿,使得整个应用停止响应. ...
- Android JIT实时编译器的设置
在Android JIT实时编译是在Android 2.2之后才引入的,JIT编译器可以显著的提高机器的性能,经过测试,android 2.2的性能较android 2.1提高了 2-5倍.JIT提 ...
- java高级---->Java动态代理的原理
Java动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程 ...
- Cglib动态代理实现原理
Cglib动态代理实现方式 我们先通过一个demo看一下Cglib是如何实现动态代理的. 首先定义个服务类,有两个方法并且其中一个方法用final来修饰. public class PersonSer ...
- RxJava RxPermissions 动态权限 简介 原理 案例 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
随机推荐
- hdoj 4324 Triangle LOVE 【拓扑】
Triangle LOVE Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) To ...
- 推荐几个靠谱的VPN
最近开发scala程序使用sbt构建工程时,下载很慢,有些依赖只能通过VPN才能下载下来.在网上搜索了一大堆提供VPN服务的.有一大部分不提供试用,而且还必须得按年购买,看起来是像骗子. 在这里推荐几 ...
- MySQL的备份与还原
原文:MySQL的备份与还原 MySQL备份和还原,都是利用mysqldump.mysql和source命令来完成的. 1.Win32下MySQL的备份与还原 1.1 备份 开始菜单 | 运行 | c ...
- dd命令简单易用,例如
dd命令简单易用,例如 bs单位,count为写入的范围区间,例如以下举例: 例:使用dd清除vote disk和ocr(裸设备) $dd if=/dev/zero of=/dev/rrac_ocr ...
- NHibernate系列
NHibernate系列 写在前面 这篇总结本来是昨天要写的,可昨天大学班长来视察工作,多喝了点,回来就倒头就睡了,也就把这篇总结的文章拖到了今天. nhibernate系列从开始着手写,到现在前后耗 ...
- 《Shell十三问》笔记(下)
继续开始shell十三问中11-13问和后续补充的笔记,加油! (14)输入重定向与输出重定向 “>”是标准输出重定向,可以把输出结果送入文件 “<”是标准输入重定向,可以重新指定文件的内 ...
- Spring Resource之应用上下文和资源路径
1.构建应用上下文 一个应用上下文构造器一般需要一个构成Bean定义的上下为你xml字符串路径或者一个字符串数组路径作为参数. 当这样的路径没有前缀的时候,那么从哪个路径构建的资源类型,用于加载bea ...
- 关于 js 中的 call 和 apply使用理解
关于 js 中的 call 和 apply使用理解 在学习新的东西时候,碰到以前看过而又不理解,或则记忆不深的地方不妨回头看看书里知识点,有助于加深理解.正所谓--温故而知新. 废话不多说,直接上代码 ...
- 【【分享】深入浅出WPF全系列教程及源码
】
因为原书作者的一再要求,在此声明,本书中的部分内容引用了原书名为<深入浅出WPF>的部分内容,假设博文不能满足你现有的学习须要,能够购买正版图书! 本人10月份提出离职,可是交接非常慢,预 ...
- sql材料分级统计及汇总案例参考
--第一步:根据系统编号.列.单价分组求和 select CLBH,DJ,sum(SL) as SL,sum(JE) as JE,Lie into #TempSZCMX from #ShouZhiCu ...