I'm working through the Assembler assignment in the Nand2Tetris course (chapter 6). The suggested implementation contains 4 modules: Main, Parser, Code, SyntaxTree. Today I wanted to get some feedback on my implementation of the Code module.
The book specifies a general API in order to allow students to use whichever programming language they want; I choose Python 2.7. The Code module takes a line of a simplified dialect of Assembly code called Hack and produces binary.
The Code module only deals with a single class of instruction in Hack language: The C-Instruction. This instruction takes the form dest=comp;jump
, where either dest=
OR ;jump
may be optionally omitted. comp
is one of 28 valid expressions. dest
is the destination to store the result of the computation: it can be any permutation of the three registers available (A
, D
, M
). jump
is a condition which, if true, moves control flow the address in instruction memory currently stored in register A
.
The resulting opcode takes the form: 111a cccc ccdd djjj
, where a
and cccc cc
correspond to the comp
, ddd
corresponds to the dest
, and jjj
corresponds to the jump
. For more details, see Chapters 4 and 6 of the textbook.
Please review on style, implementation (especially those terribly ugly lookup tables), etc.
#!/usr/bin/python
# hack code translator
# Sample conversion:
# HACK Instruction: M = 1
# 111
# a = 0
# cccccc = 1111 11
# ddd = 001
# jjj = 000
# BINARY Opcode: 1110 1111 1100 1000
jumpTable = { 'null': '000',
'JGT' : '001',
'JEQ' : '010',
'JEQ' : '010',
'JGE' : '011',
'JLT' : '100',
'JNE' : '101',
'JLE' : '110',
'JMP' : '111' }
compTable = { '0' : '101010',
'1' : '111111',
'-1' : '111010',
'D' : '001100',
'A' : '110000',
'!D' : '001101',
'!A' : '110001',
'-D' : '001111',
'-A' : '110011',
'D+1' : '011111',
'A+1' : '110111',
'D-1' : '001110',
'A-1' : '110010',
'D+A' : '000010',
'D-A' : '010011',
'A-D' : '000111',
'D&A' : '000000',
'D|A' : '010101' }
def dest(srcStr):
# Of the three portions of the opcodes,
# this is the only one that makes obvious logical sense
# No point in a lookup table
# Any combination of A, D, M
# in that order
# TODO: bitops this
bits = ['0', '0', '0'] # 0 by default
if 'A' in srcStr:
bits[0] = '1'
if 'D' in srcStr:
bits[1] = '1'
if 'M' in srcStr:
bits[2] = '1'
return ''.join(bits)
def comp(srcStr):
aBit = '0'
if 'M' in srcStr:
if 'A' in srcStr:
raise SyntaxError
else:
aBit = '1'
nStr = srcStr.replace('M', 'A') # this way we use only one lookup table
else:
nStr = srcStr[:] # non-destructive copy
return aBit + compTable[nStr]
def jump(srcStr):
return jumpTable[srcStr]