Skip to content

Instantly share code, notes, and snippets.

@jweissbock
Created January 31, 2011 01:00
Show Gist options
  • Save jweissbock/803500 to your computer and use it in GitHub Desktop.
Save jweissbock/803500 to your computer and use it in GitHub Desktop.
HACK Assembler
# we need our imports
import string, sys
# global hash table
hashTable = {}
RAMstart = 16 # variables are stored in RAM[16] to... RAM[255]??
# parse the assembly
def Assembler(filename):
# define the variables we need to
lineCount = 0
# open the file
print "Opening File... "
f = open(filename, 'r')
print "Looking for symbols"
# first read through
# scan through once for symbols... into our hash table
for line in f:
# skip new lines
if line[:2] == "//" or line == '\n':
continue
# parse line
line = parseLine(line)
if not line:
continue
commandType = cType(line)
if commandType == 'L_COMMAND':
hashTable[line[1:-1]] = lineCount
else:
lineCount += 1
# seek file back to start
f.seek(0);
# second read through - parse
# loop through it, print out non empty and new-line lines.
print "Reading, Assembling and Writing Files..."
# open file to write to
nFile = filename[0:filename.find('.')] + ".hack"
newFile = open(nFile, "w")
for line in f:
if line[:2] == "//" or line == '\n':
continue
# parse the line
line = parseLine(line)
# check if it's a blank line
if not line:
continue
# get the cType
commandType = cType(line)
# if we dunno what it is, break system
if commandType == 'Error':
print "Assembler Error on line : " + line
# print machine code
if commandType == 'A_COMMAND':
machineCode = aCommand(line)
newFile.write(machineCode + "\n")
elif commandType == 'C_COMMAND':
machineCode = cCommand(line)
newFile.write(machineCode + "\n")
# close the file
f.close()
newFile.close()
print "Assembled Successfully"
# returns the c-instruction code
def cCommand(input):
return '111' + comp(input) + dest(input) + jump(input)
# get the comp value
def comp(input) :
if input.find('=') > -1:
input = input[input.find('=')+1 :]
elif input.find(';') > -1:
input = input[0:input.find(';')]
else:
return '0000000'
comp = {'0': '0101010', '1': '0111111', '-1': '0111010', 'D': '0001100',
'A': '0110000', '!D': '0001101', '!A': '0110001', '-D': '0001111',
'-A': '0110011', 'D+1': '0011111', 'A+1': '0110111', 'D-1': '0001110',
'A-1': '0110010', 'D+A': '0000010', 'D-A': '0010011', 'A-D': '0000111',
'D&A': '0000000', 'D|A': '0010101',
'M': '1110000', '!M': '1110001', '-M': '1110011', 'M+1': '1110111',
'M-1': '1110010', 'D+M': '1000010', 'D-M': '1010011', 'M-D': '1000111',
'D&M': '1000000', 'D|M': '1010101'}
return comp[input]
# get the dest value
def dest(input) :
dest = {'M': '001', 'D': '010', 'MD': '011', 'A': '100',
'AM': '101', 'AD': '110', 'AMD': '111'}
if input.find('=') == -1:
return '000'
else:
destSgmt = input[0: input.find('=')]
return dest[destSgmt]
# get the jump value
def jump(input) :
jmp = {'JGT': '001', 'JEQ': '010', 'JGE': '011', 'JLT': '100',
'JNE': '101', 'JLE': '110', 'JMP': '111'}
if input.find(';') == -1:
return '000'
else:
jmpSgmt = input[input.find(';')+1 :]
return jmp[jmpSgmt]
# returns a-instruction code
def aCommand(input):
#map the special addresses
symbols = {'SP': '0', 'LCL': '1', 'ARG': '2', 'THIS': '3', 'THAT': '4',
'R0': '0', 'R1': '1', 'R2': '2', 'R3': '3', 'R4': '4', 'R5': '5',
'R6': '6', 'R7': '7', 'R8': '8', 'R9': '9', 'R10': '10', 'R11': '11',
'R12': '12', 'R13': '13', 'R14': '14', 'R15': '15', 'SCREEN': '16384',
'KBD': '24576' }
global RAMstart
input = input[1:]
if input in symbols.keys():
input = symbols[input]
elif input.isdigit():
input = input
elif input in hashTable.keys():
input = hashTable[input]
else:
# it's a variable, store it, return RAM address
hashTable[input] = RAMstart
input = RAMstart
RAMstart += 1
num = bin(int(input))[2:].zfill(15)
return '0' + str(num)
# Get the command type of this line
def cType(c):
# grab the variables we need
command = ['A_COMMAND', 'C_COMMAND', 'L_COMMAND', 'ERROR']
# check if it is an A_COMMAND @likethis
if c[0] == '@':
return command[0] # is an A-instruction
elif c[0] == '(' and c[len(c)-1] == ')':
return command[2] # is a label
elif c.find(';') > -1 or c.find('=') > -1:
return command[1] # is C-instruction
else:
return command[3] # dunno what it is - break assembler error
# parse a line, remove white space and \n
def parseLine(input):
# parse out white space
input = string.join(input.split(), "")
# and new lines
input = input.rstrip('\n')
## check and remove comments
if input.find('//') > -1:
input = input[0:input.find('//')]
return input
# get file name
file = raw_input("Enter file name: ")
Assembler(file)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment