Skip to content

Commit

Permalink
Build a Hack Assembler!
Browse files Browse the repository at this point in the history
  • Loading branch information
codecyang committed Jul 9, 2020
1 parent cbc3654 commit 3902c3c
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 31 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ hardware/src/verilog/*.out

# java files
assembler/src/java/*.class
assembler/src/java/*.MF
assembler/src/java/*.jar
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
69 changes: 54 additions & 15 deletions assembler/src/java/Assembler.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,85 @@
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();
String cc = Code.comp(c);
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");
}
}
33 changes: 17 additions & 16 deletions assembler/src/java/Code.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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 = {
Expand Down Expand Up @@ -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 + "\"");
}
}
14 changes: 14 additions & 0 deletions assembler/src/java/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## Assembler

### Compile

```
javac Assembler.java
```


### Usage

```
java Assembler assembly_program.asm
```
36 changes: 36 additions & 0 deletions assembler/src/java/SymbolTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import java.util.HashMap;

class SymbolTable{
private HashMap<String, Integer> 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);
}
}

0 comments on commit 3902c3c

Please sign in to comment.