Java VMTranslator Part I

news2024/12/28 19:52:39

目录

堆栈运算命令

基本思路

核心代码

Parser

Code Writer

Main

实验结果,使用SimpleAdd、StackTest进行验证

内存访问命令

基本思路

核心代码

Parser

Code Writer

Main

实验结果,使用进行验证。对比生成的二进制代码文件。


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

堆栈运算命令

基本思路

主要写两个类,一个解析器类Parser负责处理输入的vm文件,解析vm指令,一个类CodeWriter负责将经过Parser解析过的vm指令翻译成汇编指令,输出asm文件。

首先编写类Parser,有六个成员函数,包含构造函数负责打开文件流并准备读取vm指令,hasMoreCommands函数判断是否还有指令,advance函数负责将vm指令处理干净,去掉空白和注释,commandType函数判断vm指令的类型,arg1和arg2函数负责返回指令的组成部分。

然后编写CodeWriter类,构造函数打开一个文件,准备写入asm指令,writeArithmetic函数写入算术逻辑运算asm指令,writerPushPop函数写入push和pop的asm指令。

然后最关键的地方来了,如何从vm指令到asm指令?

我们首先从算术逻辑运算指令来看,以二元运算为例,计算的两个数是放在栈上的,位于栈指针SP上面两个位置,而我们只有M和D两个寄存器可以用来计算,在A寄存器保存栈指针地址的情况下。

因此,对于所有二元运算,我们首先要把参与计算的两个数放在M和D寄存器上,具体操作是,栈指针SP自减,把M的值赋给D,然后再将栈指针上移。

然后再执行二元运算,这样就比较简单了。

对于一元运算比较简单,直接栈指针自减,对M进行操作即可。

而对于gt、eq和lt这样比较指令,则比较复杂,因为涉及到跳转,同样是二元运算,因此我们需要先按照上述方法先将参与计算的两个数拿出来放在M和D寄存器,然后计算M和D的差,通过比较差和0的大小来跳转。

而对于push constant x指令,将一个常数压入栈就比较简单了。

拿到常数的值后将它写入栈指针执行的内存,然后栈指针自增就行了。

核心代码

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 Scanner scanner = null;
    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();
            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 {
            return "C_ARITHMETIC";
        }
    }

    public String arg1() {
        if (Objects.equals(commandType(), "C_ARITHMETIC"))
            return cmd0;
        return cmd1;
    }

    public int arg2() {
        return cmd2;
    }

    public void close(){scanner.close();}

}

Code Writer

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;

public class CodeWriter {
    private FileWriter asm = null;
    private String asmCommand;
    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"+Integer.toString(jump));
            asmCommand=asmCommand.replaceAll("END","END"+Integer.toString(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 = "@" + Integer.toString(index) + "\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n";
            }
        }
        asm.write(asmCommand);
    }

    public void close() throws IOException {
        asm.close();
    }
}

Main

import java.io.File;
import java.io.IOException;
import java.util.Objects;

public class Main {
    public static void main(String[] args) throws IOException {
        Parser parser=new Parser(new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\nand2tetris\\projects\\07\\StackArithmetic\\StackTest\\StackTest.vm"));
        CodeWriter codeWriter=new CodeWriter(new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\nand2tetris\\projects\\07\\StackArithmetic\\StackTest\\StackTest.asm"));
        while(parser.hasMoreCommands()){
            parser.advance();
            if(Objects.equals(parser.commandType(), "C_ARITHMETIC")){
                codeWriter.writeArithmetic(parser.arg1());
            }else{
                codeWriter.writePushPop(parser.commandType(), parser.arg1(), parser.arg2());
            }
        }
        codeWriter.close();
        parser.close();
    }
}

实验结果,使用SimpleAdd、StackTest进行验证

我们用CPU Emulator装载.tst文件,用运行程序得到的.out文件和所给的.cmp文件进行比较,其中SimpleAdd比较结果如下图所示,可见成功翻译

StackTest的结果如下图所示,可见第一阶段翻译成功。

内存访问命令

基本思路

首先要搞明白的是,push操作是将内存上的数值压入栈中,而pop操作是将栈中的数值弹出来到内存中。

对于constant段,我们第一阶段已经解决了。

对于local、argument、this和that字段,就是从它们相应的内存地址上读取或写入数据。

Push的话,先拿到segment+i的地址所指向的数值,然后将这个数值压入栈中,栈指针自增。

Pop的话,要复杂一些,因为我们只有A、M和D寄存器可以用,而pop我们首先要拿到segment+i的地址,所以我们要先找一个地方存下来,原本的R系列寄存器在这里已经被字段占用了,所以我们这里取地址255的内存空间暂存一下地址。

而temp字段的push和pop操作相对而言要简单许多。

此时读写的地址为5+i。

对于pointer字段,其实就是把this和that的数值压入栈或者弹栈的数值到this和that中。

当参数为0时,对this进行操作,当参数为1时,对that进行操作,在this和that的地址上进行读写数据。

而对于static字段,与前面的字段相比,不过就是换了运算的地址空间而已。

Asm代码基本和前面的操作一样,就是运算的地址变成了16开始的地址。

核心代码

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();
            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 {
            return "C_ARITHMETIC";
        }
    }

    public String arg1() {
        if (Objects.equals(commandType(), "C_ARITHMETIC"))
            return cmd0;
        return cmd1;
    }

    public int arg2() {
        return cmd2;
    }

    public void close() {
        scanner.close();
    }

}

Code Writer

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 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 = "@" + (16 + 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@" + (16 + index) + "\nM=D\n";
            }
        }
        asm.write(asmCommand);
    }

    public void close() throws IOException {
        asm.close();
    }
}

Main

main函数没变

实验结果,使用进行验证。对比生成的二进制代码文件。

我们用CPU Emulator装载.tst文件,用运行程序得到的.out文件和所给的.cmp文件进行比较,其中BasicTest的比较结果如下图所示,可见成功翻译。

PointerTest的比较结果如下图所示,可见成功翻译

StaticTest的结果如下图所示,可见第二阶段翻译成功。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL6:索引使用原则,联合索引,联合主键/复合主键,覆盖索引、什么是回表?索引条件下推,索引的创建与使用,索引的创建与使用,索引失效

MySQL6&#xff1a;索引使用原则&#xff0c;联合索引&#xff0c;联合主键/复合主键&#xff0c;覆盖索引、什么是回表&#xff1f;索引条件下推&#xff0c;索引的创建与使用&#xff0c;索引的创建与使用&#xff0c;索引失效 索引使用原则列的离散(sdn)度 联合索引创建联合…

【C++初探:简单易懂的入门指南】二

【C初探&#xff1a;简单易懂的入门指南】二 1.引用1.1引用做函数的参数1.2 引用做返回值1.2.1 关于引用做返回值的几点补充 1.3 多引用(对一个变量取多个别名)1.4 引用类型一致性原则以及权限的问题阐述1.5引用的效率问题1.6引用和指针的比较 2.auto关键字2.1 auto关键字的使用…

BSTree二叉树讲解

二叉搜索树的概念&#xff1a; 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者是具有以下性质的二叉树: 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点的值 若它的右子树不为空&#xff0c;则右子树上所有节点的值…

重置 VCSA 6.7 root密码和SSO密码

原贴地址&#xff1a;https://www.cnblogs.com/airoot/p/16059033.html 问题描述 1、用root用户登录 VMware vCenter Server Appliance虚拟机失败&#xff0c;无法登录 2、vCenter Server Appliance 6.7 U1的root帐户错误尝试次数超过3次已锁定或帐户已过期 官方说明 在VC…

【Spring Boot 源码学习】RedisAutoConfiguration 详解

Spring Boot 源码学习系列 RedisAutoConfiguration 详解 引言往期内容主要内容1. Spring Data Redis2. RedisAutoConfiguration2.1 加载自动配置组件2.2 过滤自动配置组件2.2.1 涉及注解2.2.2 redisTemplate 方法2.2.3 stringRedisTemplate 方法 总结 引言 上篇博文&#xff0…

【C++基础入门】44.C++中对象模型分析(上)

一、回归本质 class 是一种特殊的 struct 在内存中 class 依旧可以看作变量的集合class 与 struct 遵循相同的内存对齐规则class 中的成员函数与成员变量是分开存放的 每个对象有独立的成员变量所有对象共享类中的成员函数值得思考的问题 下面看一个对象内存布局的代码&#x…

Go学习第十七章——Gin中间件与路由

Go web框架——Gin中间件与路由 1 单独注册中间件1.1 入门案例1.2 多个中间件1.3 中间件拦截响应1.4 中间件放行 2 全局注册中间件3 自定义参数传递4 路由分组4.1 入门案例4.2 路由分组注册中间件4.3 综合使用 5 使用内置的中间件6 中间件案例权限验证耗时统计 1 单独注册中间件…

Java项目之网络考试系统

视频教程&#xff1a; 01-创建数据库_哔哩哔哩_bilibili 源码下载&#xff1a;百度网盘 请输入提取码 准备工作 创建数据库配置IDEA后端导入前端 前言&#xff1a; 把代码掰开写进博客里&#xff0c;主要是让自己在整理笔记的过程中&#xff0c;多去思考完成这个功能的核心…

基于深度学习的单图像人群计数研究:网络设计、损失函数和监控信号

摘要 https://arxiv.org/pdf/2012.15685v2.pdf 单图像人群计数是一个具有挑战性的计算机视觉问题,在公共安全、城市规划、交通管理等领域有着广泛的应用。近年来,随着深度学习技术的发展,人群计数引起了广泛的关注并取得了巨大的成功。通过系统地回顾和总结2015年以来基于深…

rust学习——智能指针Rc

文章目录 Rc 与 ArcRcRc::clone观察引用计数的变化不可变引用一个综合例子Rc 简单总结 多线程无力的 RcArcArc 的性能损耗 总结 Rc 与 Arc Rust 所有权机制要求一个值只能有一个所有者&#xff0c;在大多数情况下&#xff0c;都没有问题&#xff0c;但是考虑以下情况&#xff1…

二维码智慧门牌管理系统升级解决方案:采集要素为智慧城市建设提供精准数据支持

文章目录 前言一、二维码智慧门牌管理系统的升级需求二、采集要素在系统升级中的应用三、消防栓、井盖等采集要素的应用 前言 随着城市化进程的加速&#xff0c;智慧城市的建设已成为未来城市发展的必然趋势。其中&#xff0c;二维码智慧门牌管理系统作为智慧城市的重要组成部…

基于Spring Boot的大学课程排课系统设计与实现

摘 要 大学课程排课是现代教育管理中重要的一环。目前&#xff0c;传统的排课方式已经无法满足日益增长的课程需求和学生个性化的诉求。因此&#xff0c;研究一种基于遗传算法的大学课程排课系统是非常必要的。本研究旨在开发一种基于SpringBoot Vue的大学课程排课系统&#x…

【Java 进阶篇】在Java Web应用中获取ServletContext对象详解

在Java Web应用开发中&#xff0c;ServletContext对象扮演着重要的角色&#xff0c;它允许你在整个Web应用程序中存储和共享数据。ServletContext对象是Servlet容器提供的一种用于管理Web应用程序的全局信息的方式。本文将详细探讨ServletContext对象的概念、用途以及如何在Jav…

算法笔记【8】-合并排序算法

文章目录 一、前言二、合并排序算法基本原理三、实现步骤四、优缺点分析 一、前言 合并排序算法通过采用分治策略和递归思想&#xff0c;实现了高效、稳定的排序功能。本文将深入探讨合并排序算法的原理、实现步骤&#xff0c;并讨论其优缺点。 二、合并排序算法基本原理 合…

AntDB数据库荣获 “2023年信创物联网优秀服务商”

日前&#xff0c;在2023世界数字经济大会暨第十三届智博会 2023京甬信创物联网产融对接会上&#xff0c;AntDB数据库再获殊荣&#xff0c;获评“2023年信创物联网优秀服务商”。 图1&#xff1a;2023年信创物联网优秀服务商颁奖现场 信创物联网是信息技术应用创新与物联网的结…

网络爬虫入门导学

一、内容组织 2、常用的python IDE工具 比较推荐以下几种&#xff1a; 其中IDLE是python自带的/默认的/常用的/入门级编写工具&#xff0c;包含交互式和文件式 适用于&#xff1a;简单直接/入门级/代码不超过300行 Sublime Text是专为程序员开发的第三方专用编程工具&#xff…

OPNET <<< Program Abort >>> Standard function stack imbalance

OPNET <<< Program Abort >>> Standard function stack imbalance OPNET 问题原因及解决办法 OPNET 问题 OPNET仿真时遇到此问题&#xff1a; <<< Program Abort >>> Standard function stack imbalance 原因及解决办法 出现此问题是因…

【逗老师的无线电】艾德克斯ITECH电源电子负载网口适配器

艾德克斯的产品还是不错的&#xff0c;但是ITECH的大部分中低端设备都不带网口&#xff0c;只带了一个串口&#xff0c;并且这个串口还是个完全非标定义的5V TTL串口&#xff0c;原装的适配器300多还只能转接成RS-232。 那么&#xff0c;这回咱们来整个骚活&#xff0c;直接给艾…

Go-Python-Java-C-LeetCode高分解法-第十二周合集

前言 本题解Go语言部分基于 LeetCode-Go 其他部分基于本人实践学习 个人题解GitHub连接&#xff1a;LeetCode-Go-Python-Java-C 欢迎订阅CSDN专栏&#xff0c;每日一题&#xff0c;和博主一起进步 LeetCode专栏 我搜集到了50道精选题&#xff0c;适合速成概览大部分常用算法 突…

简单明了!网关Gateway路由配置filters实现路径重写及对应正则表达式的解析

问题背景&#xff1a; 前端需要发送一个这样的请求&#xff0c;但出现404 首先解析请求的变化&#xff1a; http://www.51xuecheng.cn/api/checkcode/pic 1.请求先打在nginx&#xff0c;www.51xuecheng.cn/api/checkcode/pic部分匹配到了之后会转发给网关进行处理变成localho…