Skip to main content
2 of 3
deleted 2 characters in body
nhgrif
  • 25.3k
  • 3
  • 63
  • 129

Assembler for Hack Assembly Language

So I have been reading this great book and I decided to do the Project in Chapter 5: Writing an Assembler for the Hack Assembly Language.

You can find the specification for the Hack Machine Language and the Hack Assembly Language here.

Here is my implementation:

package biz.tugay.hack.assembler;
/* User: [email protected] Date: 07/03/15 Time: 10:47 */

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class HackAssembler {

    private static File machineLanguageFile;
    private static File assembled;
    private static FileWriter fileWriter;
    private static int variableAddressCounter = 16;
    private static Map<String,Integer> variableAddressMap = new HashMap<String,Integer>();
    private static Map<String,Integer> loopAddressMap = new HashMap<String, Integer>();

    public static void main(String[] args) throws IOException {
        doChecksForSourceFile(args);
        checkLabelsAndPopulateLoopAddressMap(machineLanguageFile);
        Scanner machineLanguageFileScanner = new Scanner(machineLanguageFile);
        fileWriter = new FileWriter(assembled);
        while(machineLanguageFileScanner.hasNextLine()){
            String nextAssemblyLine = machineLanguageFileScanner.nextLine();
            assemble(nextAssemblyLine);
        }
        fileWriter.flush();
        fileWriter.close();
    }

    private static void assemble(String lineToBeAssembled) throws IOException {
        if(lineToBeAssembled.startsWith("(")) {

        }
        else if(lineToBeAssembled.startsWith("@")) {
            assemble_A_Instruction(lineToBeAssembled);
            fileWriter.write(System.lineSeparator());
        }
        else {
            assemble_C_Instruction(lineToBeAssembled);
            fileWriter.write(System.lineSeparator());
        }
    }

    private static void assemble_C_Instruction(String lineToBeAssembled) throws IOException {
        String compMnemonnic;
        String destinationMnemonnic = "null";
        String jumpMnemonnic = "null";

        String compBinaryRepresentation;
        String jumpBinaryRepresentation;
        String destinationBinaryRepresentation;

        String[] split = lineToBeAssembled.split("=");
        String s = split[split.length - 1];
        String[] split1 = s.split(";");
        compMnemonnic = split1[0];
        compBinaryRepresentation = BInstructionsMap.computationMnemonicMap.get(compMnemonnic);

        if(lineToBeAssembled.contains("=")) {
            destinationMnemonnic = lineToBeAssembled.split("=")[0];
        }
        destinationBinaryRepresentation = BInstructionsMap.destinationMnemonnicMap.get(destinationMnemonnic);

        if(lineToBeAssembled.contains(";")) {
            String[] x = lineToBeAssembled.split(";");
            jumpMnemonnic = x[x.length-1];
        }
        jumpBinaryRepresentation = BInstructionsMap.jumpMnemonnicMap.get(jumpMnemonnic);

        fileWriter.write("111" + compBinaryRepresentation + destinationBinaryRepresentation + jumpBinaryRepresentation);

    }

    private static void assemble_A_Instruction(String lineToBeAssembled) throws IOException {
        String subString = lineToBeAssembled.substring(1);
        try {
            Integer.parseInt(subString);
            assemble_A_InstructionWithDirectAddress(subString);
        } catch (NumberFormatException e) {
            assemble_A_InstructionWithVariableOrLabel(subString);
        }
    }

    private static void assemble_A_InstructionWithVariableOrLabel(String subString) throws IOException {
        int assignedAddress = variableAddressCounter;
        if(loopAddressMap.containsKey(subString)) {
            assignedAddress = loopAddressMap.get(subString);
        }
        else if(variableAddressMap.containsKey(subString)) {
            assignedAddress = variableAddressMap.get(subString);
        }
        else if(!variableAddressMap.containsKey(subString)) {
            variableAddressMap.put(subString,variableAddressCounter);
            variableAddressCounter++;
        }
        fileWriter.write(appendZerosToString(Integer.toBinaryString(assignedAddress)));
    }

    private static void assemble_A_InstructionWithDirectAddress(String lineToBeAssembled) throws IOException {
        fileWriter.write(appendZerosToString(Integer.toBinaryString(Integer.parseInt(lineToBeAssembled))));
    }

    private static String appendZerosToString(String stringToBeAppended) {
        char[] chars = stringToBeAppended.toCharArray();
        int zerosToAppend = 16 - chars.length;
        StringBuilder stringBuilder = new StringBuilder();
        for(int i=0;i<zerosToAppend;i++) {
            stringBuilder.append("0");
        }
        stringBuilder.append(stringToBeAppended);
        return stringBuilder.toString();
    }

    public static void doChecksForSourceFile(String[] args) throws IOException {
        // Check if filename has been passed to the program.
        if(args.length == 0) {
            System.out.println("Please pass the filename as parameter without the file extension.");
            System.exit(0);
        }
        // Check if the passed file exists.
        machineLanguageFile = new File(args[0]+".asm");
        if(!machineLanguageFile.exists()) {
            System.out.println("File does not exist..");
            System.exit(0);
        }
        // Create the new file which will contain the machine instructions in binary format.
        assembled = new File(args[0]+".hack");
        boolean delete = true;
        if(assembled.exists()) {
            delete = assembled.delete();
        }
        boolean newFile = assembled.createNewFile();
        if(!delete ||!newFile) {
            System.out.println("Can not create new file..");
            System.exit(0);
        }
    }

    private static void checkLabelsAndPopulateLoopAddressMap(File machineLanguageFile)
            throws FileNotFoundException {
        Scanner machineLanguageFileScanner = new Scanner(machineLanguageFile);
        int counter = 0;
        while(machineLanguageFileScanner.hasNextLine()){
            String nextAssemblyLine = machineLanguageFileScanner.nextLine();
            if(nextAssemblyLine.startsWith("(")) {
                String loopLabel = nextAssemblyLine.substring(1,nextAssemblyLine.length()-1);
                loopAddressMap.put(loopLabel,counter);
                counter--;
            }
            counter++;
        }
    }

}

package biz.tugay.hack.assembler;
/* User: [email protected] Date: 07/03/15 Time: 13:14 */

import java.util.HashMap;
import java.util.Map;

public class BInstructionsMap {

    public static Map<String,String> computationMnemonicMap
            = new HashMap<String,String>();

    public static Map<String,String> jumpMnemonnicMap
            = new HashMap<String, String>();

    public static Map<String,String> destinationMnemonnicMap
            = new HashMap<String, String>();

    static {
        computationMnemonicMap.put("0"  ,"0101010");
        computationMnemonicMap.put("1"  ,"0111111");
        computationMnemonicMap.put("-1" ,"0111010");
        computationMnemonicMap.put("D"  ,"0001100");
        computationMnemonicMap.put("A"  ,"0110000");
        computationMnemonicMap.put("!D" ,"0001101");
        computationMnemonicMap.put("!A" ,"0110001");
        computationMnemonicMap.put("-D" ,"0001111");
        computationMnemonicMap.put("-A" ,"0110011");
        computationMnemonicMap.put("D+1","0011111");
        computationMnemonicMap.put("A+1","0110111");
        computationMnemonicMap.put("D-1","0001110");
        computationMnemonicMap.put("A-1","0110010");
        computationMnemonicMap.put("D+A","0000010");
        computationMnemonicMap.put("D-A","0010011");
        computationMnemonicMap.put("A-D","0000111");
        computationMnemonicMap.put("D&A","0000000");
        computationMnemonicMap.put("D|A","0010101");

        computationMnemonicMap.put("M"  ,"1110000");
        computationMnemonicMap.put("!M" ,"1110001");
        computationMnemonicMap.put("-M" ,"1110011");
        computationMnemonicMap.put("M+1","1110111");
        computationMnemonicMap.put("M-1","1110010");
        computationMnemonicMap.put("D+M","1000010");
        computationMnemonicMap.put("D-M","1010011");
        computationMnemonicMap.put("M-D","1000111");
        computationMnemonicMap.put("D&M","1000000");
        computationMnemonicMap.put("D|M","1010101");
    }

    static {
        jumpMnemonnicMap.put("null","000");
        jumpMnemonnicMap.put("JGT" ,"001");
        jumpMnemonnicMap.put("JEQ" ,"010");
        jumpMnemonnicMap.put("JGE" ,"011");
        jumpMnemonnicMap.put("JLT" ,"100");
        jumpMnemonnicMap.put("JNE" ,"101");
        jumpMnemonnicMap.put("JLE" ,"110");
        jumpMnemonnicMap.put("JMP" ,"111");
    }

    static {
        destinationMnemonnicMap.put("null","000");
        destinationMnemonnicMap.put("M"   ,"001");
        destinationMnemonnicMap.put("D"   ,"010");
        destinationMnemonnicMap.put("MD"  ,"011");
        destinationMnemonnicMap.put("A"   ,"100");
        destinationMnemonnicMap.put("AM"  ,"101");
        destinationMnemonnicMap.put("AD"  ,"110");
        destinationMnemonnicMap.put("AMD" ,"111");
    }
}

If you would also like to do this exercise, you can find the software here.

Koray Tugay
  • 1.5k
  • 5
  • 20
  • 44