Nand2Tetris——project06

一、前言

项目来源:

该项目是著名课程Nand2Tetris的课程项目,总共分12部分,从零开始构建属于自己的hack计算机。该文项目属于第六个子项目。

项目路线介绍:

在硬件部分,你将进入 01 的世界,用与非门构造出逻辑电路,并逐步搭建出一个 CPU 来运行一套课程作者定义的简易汇编代码。在软件部分,你将编写一个编译器,将作者开发的一个名为Jack的高级语言编译为可以运行在虚拟机上的字节码,然后进一步翻译为汇编代码。你还将开发一个简易的 OS,让你的计算机支持输入输出图形界面。至此,你可以用 Jack 开发一个俄罗斯方块的小游戏,将它编译为汇编代码,运行在你用与非门搭建出的 CPU 上,通过你开发的 OS 进行交互。

二、项目介绍

本章的目标是用高级语言写出一个汇编器,我使用的是Java(并且实现很幼稚),这个Hack语言的汇编器并不难,因为Hack语言里唯一需要动态构建的只有A语句,而A语句的结构又很简单——@+数字/符号,所以各位可以放心大胆地去试试,相信你可以做得比我好的
目标:

  • HackAssembler
    要求:推荐使用一门高级语言,来完成一个可以读取.asm文件,并将其翻译成.hack文件的汇编器程序
    细节:
  1. 推荐使用书里面的分成四个模块来实现

    1. Parser(语法分析器模块):本模块主要做的就是对命令进行获取,进行类型判断,获取符号(预定义符号,标记命令,变量)
    2. Code(编码模块):将Hack汇编语言助记符翻译成二进制码。
    3. 符号表模块:符号表用于建立和维持符号与其地址之间的关联。常用的有哈希表。
    4. 无符号程序的汇编编译器:这种情况意味着不用处理符号,所以只有两个阶段,第一阶段翻译无符号汇编程序,然后将其扩展成具有符号处理能力的汇编编译器。
    5. 有符号程序的汇编编译器:使用两次读取代码的方式来实现,第一遍构建符号表,但不翻译程序;第二遍翻译程序,并把符号变成地址。
  2. 个人对于这个汇编器的理解是:读入语言->解析语言->语句分类->分别处理->封装汇编后语句

  3. 还有一个值得注意的点就是对于注释语句的处理,可以说每个语句都要进行注释语句的判断和之后的改装,还有空格的处理,因为处理频繁,建议独立出一个处理方法处理

三、项目展示

1.程序结构图

HackAssembler

2.实现

2.1.Parser
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

/*
 * 这是hack汇编器的解析器,负责对命令进行提取和对命令进行分类和拆解方便逐个处理
须实现功能如下功能如下:
1.通过给定的文件名创建一条输入流
2.能够判断流后面是否还有命令
3.在得知还存在命令后,读入一条命令,存进“当前命令”变量中
4.对当前命令进行分类主要有三大分类:
	1.A_COMMAND
	2.C_COMMAND
	3,L_COMMAND
5.返回当前A命令或者伪命令后的数字或者符号
6.返回当前C命令的dest字符串
7.返回当前C命令的comp字符串
8.返回当前C命令的jump字符串
*/

public class Parser implements CommandInterface{
    String nowCommad;
    BufferedReader asmReader;
    public Parser(File file) {
        try {
            asmReader = new BufferedReader(new FileReader(file) );

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public boolean HasCommand() {
        boolean tag=false;
        try {
            String line = asmReader.readLine();
            if (line != null) {
                nowCommad = line;
                tag = true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return tag;
    }

    public int typeOfCommand() {
        String command = nowCommad;
        command=filtration(command);
        if (command.contains("(")) {
            return L_COMMAND;
        }else if (command.contains("@")) {
            return A_COMMAND;
        }else if(command.equals("")){
            return NO_COMMAND;
        }
        return C_COMMAND;
    }
    public String getACommand() {
        String command = filtration(nowCommad);
        return command.substring(1);
    }
    public String getLabel() {
        String label;
        String command = filtration(nowCommad);
        int start = command.indexOf("(");
        int end = command.indexOf(")");
        label = command.substring(start+1, end);
        return label.trim();
    }
    public String getDest() {
        String dest = "null";
        String command = filtration(nowCommad);
        if (command.contains("=")) {
            int one = command.indexOf('=');
            dest = command.substring(0, one);
        }
        return dest.trim();
    }

    public String getJump() {
        String jump = "null";
        String command = filtration(nowCommad);
        if (command.contains(";")) {
            int one = command.indexOf(';');
            jump = command.substring(one+1);
        }
        return jump.trim();
    }

    public String getComp() {
        int end;
        int start;
        String comp = "0";
        String command = filtration(nowCommad);
        if (command.contains("=")&&command.contains(";")) {
            start = command.indexOf("=");
            end = command.indexOf(";");
            comp = command.substring(start+1,end);
        }else if(command.contains(";")) {
            end = command.indexOf(";");
            comp = command.substring(0,end);
        }else if (command.contains("=")) {
            start = command.indexOf("=");
            comp = command.substring(start+1);
        }else {
            comp = command;
        }
        return comp.trim();
    }
    public void close() {
        try {
            asmReader.close();
        } catch (IOException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }

//	public static void main(String[] args) {
//
//		Parser parser = new Parser(new File("C:\\Users\\LinKa\\Desktop\\nand2tetris\\projects\\06\\max\\Max.asm"));
//		parser.nowCommad = "D=M";
//		System.out.println(parser.getComp());
//	}
}

2.2.Code
import java.util.HashMap;
import java.util.Map;

/*
这是hack汇编器中真正进行翻译的模块,可以根据各种命令字符串给出其机器命令
1.根据dest字符串参数返回其机器码
2.根据comp字符串参数返回其机器码
4.根据jump字符串参数返回其机器码
5.将十进制字符串转换成2进制字符串并返回
*/
public class Code {

    Map<String,String> destTable = new HashMap<>();
    Map<String,String> compTable = new HashMap<>();
    Map<String, String> jumpTable = new HashMap<>();
    //构造函数初始化翻译表
    public Code() {
        destTable.put("null", "000");
        destTable.put("M", "001");
        destTable.put("D", "010");
        destTable.put("MD", "011");
        destTable.put("A", "100");
        destTable.put("AM", "101");
        destTable.put("AD", "110");
        destTable.put("AMD", "111");
        jumpTable.put("null", "000");
        jumpTable.put("JGT", "001");
        jumpTable.put("JEQ", "010");
        jumpTable.put("JGE", "011");
        jumpTable.put("JLT", "100");
        jumpTable.put("JNE", "101");
        jumpTable.put("JLE", "110");
        jumpTable.put("JMP", "111");
        compTable.put("0", "0101010");
        compTable.put("1", "0111111");
        compTable.put("-1", "0111010");
        compTable.put("D", "0001100");
        compTable.put("A", "0110000");
        compTable.put("!D", "0001101");
        compTable.put("!A", "0110001");
        compTable.put("-D", "0001111");
        compTable.put("-A", "0110011");
        compTable.put("D+1", "0011111");
        compTable.put("A+1", "0110111");
        compTable.put("D-1", "0001110");
        compTable.put("A-1", "0110010");
        compTable.put("D+A", "0000010");
        compTable.put("D-A", "0010011");
        compTable.put("A-D", "0000111");
        compTable.put("D&A", "0000000");
        compTable.put("D|A", "0010101");
        compTable.put("M", "1110000");
        compTable.put("!M", "1110001");
        compTable.put("M+1", "1110111");
        compTable.put("M-1", "1110010");
        compTable.put("D+M", "1000010");
        compTable.put("D-M", "1010011");
        compTable.put("M-D", "1000111");
        compTable.put("D&M", "1000000");
        compTable.put("D|M", "1010101");
    }

    public String destCode(String dest) {
        String code = destTable.get(dest);
        return code;
    }
    public String jumpCode(String jump) {
        String code = jumpTable.get(jump);
        return code;
    }
    public String compCode(String comp) {
        String code = compTable.get(comp);
        return code;
    }
    public String decimalToBinay(String decimal) {

        int decimalNumber = Integer.parseInt(decimal);

        String binay = Integer.toString(decimalNumber, 2);
        while (binay.length()<16) {
            binay = "0"+binay;
        }
        return binay;
    }

}


2.3.Assembler
import java.awt.image.SampleModel;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.util.Scanner;

//这是Hack汇编器的入口,也就是程序的main模块,在此模块要实现的事情如下:
//1.获得汇编程序的名字
//2.通过获得名字创建Parser对象和一个写入文件流对象
//3.创建一个Code对象,方便翻译使用
//4.创建一个SymbolTable,初始化符号表
//5.进行第一遍预汇编,将标签连同其地址添加进符号表
//6.进行第二遍汇编,通过解析器(Paser)获得命令,通过code类获得汇编成的机器指令,并将机器指令写进.hack文件(注意每行后面写换行)
//7.完成编译
public class Assembler implements CommandInterface {

    public static void main(String[] args) {
        int preCount=0;
        int variableAddress = 16;
        Code code = new Code();
        SymbolTable table = new SymbolTable();
        String adderssString = "磁盘根目录\\nand2tetris\\projects\\06";
        Scanner scanner = new Scanner(System.in);
        File asmFile;
        do {
            System.out.println("请输入正确文件:");
            String fileName = scanner.next();
            fileName = adderssString + fileName;
            asmFile = new File(fileName);
        } while (!asmFile.exists());
        System.out.println("请输入目标文件名字及上一级目录:(如:\\add\\add.hack)");
        String hackFile = scanner.next();
        FileOutputStream fos;
        BufferedWriter fileWriter = null;
        try {
            fos = new FileOutputStream(adderssString+hackFile);
            fileWriter = new BufferedWriter(new OutputStreamWriter(fos));
        } catch (FileNotFoundException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
        System.out.println("进入预汇编。");
        Parser preParser = new Parser(asmFile);
        Parser parser = new Parser(asmFile);
        while (preParser.HasCommand()) {

            if (preParser.typeOfCommand()==L_COMMAND) {
                String label = preParser.getLabel();
                table.put(label, Integer.toString(preCount));
            }
            if (preParser.typeOfCommand()==A_COMMAND||preParser.typeOfCommand()==C_COMMAND) {
                preCount++;
            }
        }
        System.out.println("预汇编结束,开始汇编。");
        preParser.close();
        while (parser.HasCommand()) {
            if (parser.typeOfCommand()==C_COMMAND) {
                String dest = parser.getDest();
                String comp = parser.getComp();
                String jump = parser.getJump();
                String destCode = code.destCode(dest);
                String compCode = code.compCode(comp);
                String jumpCode = code.jumpCode(jump);
                String C_Code = "111"+compCode+destCode+jumpCode;
                try {
                    fileWriter.write(C_Code);
                    fileWriter.newLine();
                } catch (IOException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }else if (parser.typeOfCommand()==A_COMMAND) {
                String A_Code;
                boolean tag = true;
                String symbol = parser.getACommand();
                for ( byte c : symbol.getBytes()) {
                    if(c<'0'||c>'9') {
                        tag = false;
                    }
                }
                if (tag) {
                    A_Code = code.decimalToBinay(symbol);
                }else {
                    String address;
                    if (table.isHas(symbol)) {
                        address = table.getAddress(symbol);
                    }else {
                        table.put(symbol, Integer.toString(variableAddress));
                        address = table.getAddress(symbol);
                        variableAddress++;
                    }
                    A_Code = code.decimalToBinay(address);
                }
                try {
                    fileWriter.write(A_Code);
                    fileWriter.newLine();
                } catch (IOException e) {
                    // TODO 自动生成的 catch 块
                    e.printStackTrace();
                }
            }

        }
        System.out.println("汇编完成");
        parser.close();
        try {
            fileWriter.close();
        } catch (IOException e) {
            // TODO 自动生成的 catch 块
            e.printStackTrace();
        }
    }
}

2.4.SymbolTable
/*
这是hack汇编器的符号表,存储<符号,数字>单元,实现用map
功能如下:
1.创建一个空表并进行预定义的初始化
2.将<symbol,address>添加进表
3.查看表中是否存在某个符号
4.返回以目的symbol为键的相关联的地址*/
public class SymbolTable {
    Map<String,String> table = new HashMap<>();
    public SymbolTable() {
        table.put("SP", "0");
        table.put("LCL", "1");
        table.put("ARG", "2");
        table.put("THIS", "3");
        table.put("THAT", "4");
        table.put("R0", "0");
        table.put("R1", "1");
        table.put("R2", "2");
        table.put("R3", "3");
        table.put("R4", "4");
        table.put("R5", "5");
        table.put("R6", "6");
        table.put("R7", "7");
        table.put("R8", "8");
        table.put("R9", "9");
        table.put("R10", "10");
        table.put("R11", "11");
        table.put("R12", "12");
        table.put("R13", "13");
        table.put("R14", "14");
        table.put("R15", "15");
        table.put("SCREEN", "16384");
        table.put("KBD", "24576");
    }
    public boolean isHas(String symbol) {
        return table.containsKey(symbol);
    }

    public void put(String symbol, String address) {
        table.put(symbol, address);
    }

    public String getAddress(String symbol) {
        return table.get(symbol);
    }
}


2.5.CommandInterface
public interface CommandInterface {
    int A_COMMAND = 1;
    int C_COMMAND = 2;
    int L_COMMAND = 3;
    int NO_COMMAND = 4;
    default String filtration(String commandLine) {
        String command = commandLine;
        command=command.trim();
        if (command.contains("//")) {
            int one = command.indexOf('/');
            command = command.substring(0,one);
        }
        return command;
    }
}

参考文章:https://blog.csdn.net/qq_41634283/article/details/104044354

  • 20
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值