diff --git a/.gitignore b/.gitignore index 941c5c0..e52c654 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ hardware/src/verilog/*.out # java files assembler/src/java/*.class +assembler/src/java/*.MF +assembler/src/java/*.jar diff --git a/README.md b/README.md index 114309e..68e992b 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,4 @@ I am still working on it. So there might be some bugs! * [Hardware](hardware/src/verilog/README.md) * [Machine Language](sample_program/assembly/README.md) +* [Assembler](assembler/src/java/README.md) diff --git a/assembler/src/java/Assembler.java b/assembler/src/java/Assembler.java index ed0c1e0..81750a8 100644 --- a/assembler/src/java/Assembler.java +++ b/assembler/src/java/Assembler.java @@ -6,33 +6,59 @@ import java.nio.file.Path; import java.nio.file.Paths; -public class Assembler{ +public class Assembler { private static final String OUTPUT_EXTENSION = ".hack"; - public static void main(String[] args) throws IOException{ - Parser parser; - for(String arg: args){ + public static void main(String[] args) throws IOException { + for (String arg : args) { + // For each (XXX): + // Add the pair (XXX, address) to the symbol table. + int lineNumber = 0; + SymbolTable symbolTable = new SymbolTable(); + Parser parser = new Parser(arg); + while (parser.hasMoreCommands()) { + parser.advance(); + if (parser.commandType() == Parser.CommandType.L_COMMAND) { + String label = parser.symbol(); + if (symbolTable.contains(label)) + throw new IllegalStateException("Invalid or duplicate label \"" + label + "\"."); + else + symbolTable.addEntry(label, lineNumber); + continue; + } + lineNumber++; + } + + // Binary output file. File currentFile = new File(arg); String fileName = currentFile.getName(); int index = fileName.indexOf("."); - if(index > 0) + if (index > 0) fileName = fileName.substring(0, index); fileName += OUTPUT_EXTENSION; String filePath = Paths.get(currentFile.getParent()).resolve(fileName).toString(); File outFile = new File(filePath); - PrintWriter printW = new PrintWriter(new BufferedWriter(new FileWriter(outFile))); + int varIndex = 16; // Variable index. parser = new Parser(arg); - while(parser.hasMoreCommands()){ - String line = ""; + while (parser.hasMoreCommands()) { parser.advance(); - if(parser.commandType() == Parser.CommandType.A_COMMAND - || parser.commandType() == Parser.CommandType.L_COMMAND){ - int i = Integer.valueOf(parser.symbol()) & 0xffff; - line = "0" + String.format("%15s", Integer.toBinaryString(i)).replaceAll("\\s", "0"); - }else if(parser.commandType() == Parser.CommandType.C_COMMAND){ + if (parser.commandType() == Parser.CommandType.A_COMMAND) { + String symbol = parser.symbol(); + String line = ""; + if (symbolTable.contains(symbol)) { + line = to16bitBinaryString(symbolTable.getAddress(symbol)); + } else if (isNaturalNumber(symbol)) { + line = to16bitBinaryString(symbol); + } else { + symbolTable.addEntry(symbol, varIndex); + line = to16bitBinaryString(varIndex); + varIndex++; + } + printW.println(line); + } else if(parser.commandType() == Parser.CommandType.C_COMMAND) { String c = parser.comp(); String d = parser.dest(); String j = parser.jump(); @@ -40,12 +66,25 @@ public static void main(String[] args) throws IOException{ String dd = Code.dest(d); String jj = Code.jump(j); - line = "111" + cc + dd + jj; + String line = "111" + cc + dd + jj; + printW.println(line); } - printW.println(line); } printW.flush(); printW.close(); } } + + static boolean isNaturalNumber(String numStr) { + return numStr.matches("\\d+"); + } + + static String to16bitBinaryString(int num) { + return String.format("%16s", Integer.toBinaryString(num)).replaceAll("\\s", "0"); + } + + static String to16bitBinaryString(String numStr) { + int i = Integer.valueOf(numStr) & 0x7fff; + return String.format("%16s", Integer.toBinaryString(i)).replaceAll("\\s", "0"); + } } diff --git a/assembler/src/java/Code.java b/assembler/src/java/Code.java index 2d7094e..34ff545 100644 --- a/assembler/src/java/Code.java +++ b/assembler/src/java/Code.java @@ -9,11 +9,11 @@ class Code { {"", "000"}, {"M", "001"}, {"D", "010"}, - {"MD", "011"}, + {"MD", "011"}, {"DM", "011"}, {"A", "100"}, - {"AM", "101"}, - {"AD", "110"}, - {"AMD", "111"} + {"AM", "101"}, {"MA", "101"}, + {"AD", "110"}, {"DA", "110"}, + {"AMD", "111"}, {"ADM", "111"}, {"DAM", "111"}, {"DMA", "111"}, {"MAD", "111"}, {"MDA", "111"} }; private static final String[][] compCode = { @@ -26,25 +26,25 @@ class Code { {"!A", "0110001"}, {"-D", "0001111"}, {"-A", "0110011"}, - {"D+1", "0011111"}, - {"A+1", "0110111"}, + {"D+1", "0011111"}, {"1+D", "0011111"}, + {"A+1", "0110111"}, {"1+A", "0110111"}, {"D-1", "0001110"}, {"A-1", "0110010"}, - {"D+A", "0000010"}, + {"D+A", "0000010"}, {"A+D", "0000010"}, {"D-A", "0010011"}, {"A-D", "0000111"}, - {"D&A", "0000000"}, - {"D|A", "0010101"}, + {"D&A", "0000000"}, {"A&D", "0000000"}, + {"D|A", "0010101"}, {"A|D", "0010101"}, {"M", "1110000"}, {"!M", "1110001"}, {"-M", "1110011"}, - {"M+1", "1110111"}, + {"M+1", "1110111"}, {"1+M", "1110111"}, {"M-1", "1110010"}, - {"D+M", "1000010"}, + {"D+M", "1000010"}, {"M+D", "1000010"}, {"D-M", "1010011"}, {"M-D", "1000111"}, - {"D&M", "1000000"}, - {"D|M", "1010101"} + {"D&M", "1000000"}, {"M&D", "1000000"}, + {"D|M", "1010101"}, {"M|D", "1010101"} }; private static final String[][] jumpCode = { @@ -73,19 +73,20 @@ static String dest(String mnemonic){ if(destMap.containsKey(mnemonic)) return destMap.get(mnemonic); else - throw new IllegalStateException("Unsupported dest code."); + throw new IllegalStateException("Unsupported dest code \"" + mnemonic + "\""); } static String comp(String mnemonic){ if(compMap.containsKey(mnemonic)) return compMap.get(mnemonic); else - throw new IllegalStateException("Unsupported comp code."); + throw new IllegalStateException("Unsupported comp code \"" + mnemonic + "\""); } static String jump(String mnemonic){ if(jumpMap.containsKey(mnemonic)) return jumpMap.get(mnemonic); - else throw new IllegalStateException("Unsupported jump code."); + else + throw new IllegalStateException("Unsupported jump code \"" + mnemonic + "\""); } } diff --git a/assembler/src/java/README.md b/assembler/src/java/README.md new file mode 100644 index 0000000..d5e4037 --- /dev/null +++ b/assembler/src/java/README.md @@ -0,0 +1,14 @@ +## Assembler + +### Compile + +``` +javac Assembler.java +``` + + +### Usage + +``` +java Assembler assembly_program.asm +``` diff --git a/assembler/src/java/SymbolTable.java b/assembler/src/java/SymbolTable.java new file mode 100644 index 0000000..8b0b31a --- /dev/null +++ b/assembler/src/java/SymbolTable.java @@ -0,0 +1,36 @@ +import java.util.HashMap; + +class SymbolTable{ + private HashMap symbolTable = new HashMap<>(); + + private static final String[] defaultSymbol = { + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", + "SCREEN", "KBD", + "SP", "LCL", "ARG", "THIS", "THAT" + }; + + private static final int[] defaultSymbolValues = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16384, 24576, + 0, 1, 2, 3, 4 + }; + + SymbolTable(){ + for(int i = 0 ; i < defaultSymbol.length ; i++) + symbolTable.put(defaultSymbol[i], defaultSymbolValues[i]); + } + + void addEntry(String symbol, int address){ + symbolTable.put(symbol, address); + } + + boolean contains(String symbol){ + return symbolTable.containsKey(symbol); + } + + int getAddress(String symbol){ + return symbolTable.get(symbol); + } +}