Hack汇编语言是一种特定于计算机体系结构的汇编语言,使用Hack架构的机器码指令来编写程序。Hack是一种基于Von Neumann结构的计算机体系结构,由Harvard大学的Nand to Tetris项目开发出来,用于实现计算机硬件和软件。
Hack汇编语言主要用于在Nand to Tetris项目中编写计算机硬件和软件。该项目旨在教授计算机系统的基本原理和构造,从最基本的逻辑门开始,一步步地构建出完整的计算机系统,包括CPU、内存、输入/输出、操作系统和编程语言等方面。通过设计和构造这些部分,参与者可以对计算机系统的工作原理有更深入的了解,同时也可以提高程序设计和算法分析能力。
HackAssemblerNoSymbol
按照要求先写一个没有符号的,再写一个有符号的
首先读取文本文件,去掉空白行,然后去掉单独的注释行,除了单独的注释行还有与代码在同一行的尾随代码的注释也要去掉。
然后开始准备翻译hack汇编语言,即翻译A指令和C指令。
对于A指令,如图所示,它的格式是"@"加上一个十进制数字或者一个符号(标签),由于此处我们编写的是无符号的汇编器,所以我们暂时忽略符号的翻译,因此@后面只可能出现数字,所以我们直接将数字变成二进制并进行补0就行。
对于C指令,如图所示,它的格式由三个部分组成:目标位dest、计算位comp和跳转位jmp。因此我们为了方便可以直接建立起dest、comp和jmp三个表方便查询与替换。
然后开始翻译C指令,C指令中的dest和jmp可以为空,因此先根据指令中是否包含=和;分情况提取dest、jmp和comp,再从之前建立的三个表中寻找相应的字符串进行替换。
其中的comp表,分成a=0和a=1两种情况,但是不需要建立两个表,直接在对应的字符串前插入字符0或者1即可
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;
import java.util.Scanner;
import java.util.Vector;
public class hackAssemblerNoSymbol {
public static void main(String[] args) throws IOException {
HashMap<String, String> Dest = new HashMap<>(); //目标位替换表
Dest.put("null", "000");
Dest.put("M", "001");
Dest.put("D", "010");
Dest.put("MD", "011");
Dest.put("A", "100");
Dest.put("AM", "101");
Dest.put("AD", "110");
Dest.put("AMD", "111");
HashMap<String, String> Jmp = new HashMap<>(); //跳转位替换表
Jmp.put("null", "000");
Jmp.put("JGT", "001");
Jmp.put("JEQ", "010");
Jmp.put("JGE", "011");
Jmp.put("JLT", "100");
Jmp.put("JNE", "101");
Jmp.put("JLE", "110");
Jmp.put("JMP", "111");
HashMap<String, String>Comp = new HashMap<>(); //计算位替换表
// values in comp a=0
Comp.put("0", "0101010");
Comp.put("1", "0111111");
Comp.put("-1", "0111010");
Comp.put("D", "0001100");
Comp.put("A", "0110000");
Comp.put("!D", "0001111");
Comp.put("!A", "0110001");
Comp.put("-D", "0001111");
Comp.put("D+1", "0011111");
Comp.put("A+1", "0110111");
Comp.put("D-1", "0001110");
Comp.put("A-1", "0110010");
Comp.put("D+A", "0000010");
Comp.put("D-A", "0010011");
Comp.put("A-D", "0000111");
Comp.put("D&A", "0000000");
Comp.put("D|A", "0010101");
// values in comp a=1
Comp.put("M", "1110000");
Comp.put("!M", "1110001");
Comp.put("-M", "1110011");
Comp.put("M+1", "1110111");
Comp.put("M-1", "1110010");
Comp.put("D+M", "1000010");
Comp.put("D-M", "1010011");
Comp.put("M-D", "1000111");
Comp.put("D&M", "1000000");
Comp.put("D|M", "1010101");
Scanner asm = new Scanner(new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\HW2\\nand2tetris\\projects\\06\\max\\MaxL.asm"));
Vector<String>code=new Vector<>();
while (asm.hasNextLine()) {
String line = asm.nextLine();
if (!Objects.equals(line, "") && line.charAt(0) != '/') { //去掉空白行和注释行
String[] pure = line.split("/"); //去掉尾随代码的注释
pure[0]=pure[0].replaceAll("\\s",""); //去掉空格
code.add(pure[0]);
}
}
asm.close();
FileWriter binary=new FileWriter(new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\HW2\\HackAssembler\\MaxL.hack"));
for(String line:code){
if(line.charAt(0)=='@'){ //A指令
String address=line.substring(1); //取@后面的内容
int number=Integer.parseInt(address); //取数字
String numberBinary=Integer.toBinaryString(number); //数字转二进制
String zeros="0".repeat(16-numberBinary.length()); //补0齐16位
binary.write(zeros+numberBinary+'\n');
}else{ //C指令
String dest="null";
String jmp="null";
String comp;
String[] destCompJmp=line.split(";");
if(destCompJmp[0].contains("=")){ // 有dest
String[] destComp=destCompJmp[0].split("=");
dest=destComp[0];
comp=destComp[1];
}else{ //没有dest
comp=destCompJmp[0];
}
if(destCompJmp.length>1){ //有jmp
jmp=destCompJmp[1];
}
binary.write("111"+Comp.get(comp)+Dest.get(dest)+Jmp.get(jmp)+'\n');
}
}
binary.close();
}
}
HackAssembler
这个是有符号的
基本与无符号的解决思路相同,所需要特别处理的就是hack汇编语言中的符号。
首先建立起一个符号表,如图所示,将一些系统预定义的符号如R系列寄存器等装入其中。
对于标签符号的处理,需要对代码进行一次扫描,如图所示,确定标签符号的地址,将其加入符号表中。
然后就是处理变量符号了,如图所示,对于变量符号,会将它们放在从地址16开始的空间,因此对于首次出现的变量符号给予它们从16开始的值,再次出现的变量符号就取他们先前赋予的值。
由于只有A指令中涉及到符号,所以我们只需要改变对A指令的翻译即可,在先前的无符号汇编器的编写中我们A指令中@后面只会出现数字,在增加了符号后,@后面可以出现变量、标签和预定义的符号,所以我们需要增加判断条件,先在符号表中寻找相应的匹配符号,如果找到了,直接替换成相应的数值,如果没有找到,再判断是否是数字,如果不是数字,则是首次输出的变量符号,那么将其添加到符号表中即可。
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Objects;
import java.util.Scanner;
import java.util.Vector;
public class hackAssembler {
public static void main(String[] args) throws IOException {
HashMap<String,Integer> Symbol=new HashMap<>(); //符号表
Symbol.put("SP", 0);
Symbol.put("LCL", 1);
Symbol.put("ARG", 2);
Symbol.put("THIS", 3);
Symbol.put("THAT", 4);
Symbol.put("SCREEN", 16384);
Symbol.put("KBD", 24567);
Symbol.put("R0", 0);
Symbol.put("R1", 1);
Symbol.put("R2", 2);
Symbol.put("R3", 3);
Symbol.put("R4", 4);
Symbol.put("R5", 5);
Symbol.put("R6", 6);
Symbol.put("R7", 7);
Symbol.put("R8", 8);
Symbol.put("R9", 9);
Symbol.put("R10", 10);
Symbol.put("R11", 11);
Symbol.put("R12", 12);
Symbol.put("R13", 13);
Symbol.put("R14", 14);
Symbol.put("R15", 15);
HashMap<String, String> Dest = new HashMap<>(); //目标位替换表
Dest.put("null", "000");
Dest.put("M", "001");
Dest.put("D", "010");
Dest.put("MD", "011");
Dest.put("A", "100");
Dest.put("AM", "101");
Dest.put("AD", "110");
Dest.put("AMD", "111");
HashMap<String, String> Jmp = new HashMap<>(); //跳转位替换表
Jmp.put("null", "000");
Jmp.put("JGT", "001");
Jmp.put("JEQ", "010");
Jmp.put("JGE", "011");
Jmp.put("JLT", "100");
Jmp.put("JNE", "101");
Jmp.put("JLE", "110");
Jmp.put("JMP", "111");
HashMap<String, String>Comp = new HashMap<>(); //计算位替换表
// values in comp a=0
Comp.put("0", "0101010");
Comp.put("1", "0111111");
Comp.put("-1", "0111010");
Comp.put("D", "0001100");
Comp.put("A", "0110000");
Comp.put("!D", "0001111");
Comp.put("!A", "0110001");
Comp.put("-D", "0001111");
Comp.put("D+1", "0011111");
Comp.put("A+1", "0110111");
Comp.put("D-1", "0001110");
Comp.put("A-1", "0110010");
Comp.put("D+A", "0000010");
Comp.put("D-A", "0010011");
Comp.put("A-D", "0000111");
Comp.put("D&A", "0000000");
Comp.put("D|A", "0010101");
// values in comp a=1
Comp.put("M", "1110000");
Comp.put("!M", "1110001");
Comp.put("-M", "1110011");
Comp.put("M+1", "1110111");
Comp.put("M-1", "1110010");
Comp.put("D+M", "1000010");
Comp.put("D-M", "1010011");
Comp.put("M-D", "1000111");
Comp.put("D&M", "1000000");
Comp.put("D|M", "1010101");
Scanner asm = new Scanner(new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\HW2\\nand2tetris\\projects\\06\\rect\\Rect.asm"));
Vector<String>code=new Vector<>();
while (asm.hasNextLine()) {
String line = asm.nextLine();
if (!Objects.equals(line, "") && line.charAt(0) != '/') { //去掉空白行和注释行
String[] pure = line.split("/"); //去掉尾随代码的注释
pure[0]=pure[0].replaceAll("\\s",""); //去掉空格
if(pure[0].charAt(0)=='('){ //如果是标签符号,将之添加到符号表中
String label=pure[0].substring(1,pure[0].length()-1);
Symbol.put(label,code.size());
}else{
code.add(pure[0]); //不是标签是代码
}
}
}
asm.close();
FileWriter binary=new FileWriter(new File("C:\\Users\\Yezi\\Desktop\\Java程序设计\\HW2\\HackAssembler\\Rect.hack"));
int start=16;
for(String line:code){
if(line.charAt(0)=='@'){ //A指令
String address=line.substring(1); //取@后面的内容
int number;
if(Symbol.containsKey(address)){ // 出现过的符号,预定义符号、变量和标签
number=Symbol.get(address);
}else if(address.matches("\\d+")){ //数字
number=Integer.parseInt(address); //取数字
}else{ //首次出现的变量符号
number=start++;
Symbol.put(address,number);
}
String numberBinary=Integer.toBinaryString(number); //数字转二进制
String zeros="0".repeat(16-numberBinary.length()); //补0齐16位
binary.write(zeros+numberBinary+'\n');
}else{ //C指令
String dest="null";
String jmp="null";
String comp;
String[] destCompJmp=line.split(";");
if(destCompJmp[0].contains("=")){ // 有dest
String[] destComp=destCompJmp[0].split("=");
dest=destComp[0];
comp=destComp[1];
}else{ //没有dest
comp=destCompJmp[0];
}
if(destCompJmp.length>1){ //有jmp
jmp=destCompJmp[1];
}
binary.write("111"+Comp.get(comp)+Dest.get(dest)+Jmp.get(jmp)+'\n');
}
}
binary.close();
}
}