0%

Assembler Project

Overview

Objective Develop an assembler that translates programs written in Hack assembly language into the binary code understood by the Hack hardware platform.

Resources The only tool needed for completing this project is the program- ming language in which you will implement your assembler. You may also find the following two tools useful: the assembler and CPU emulator supplied with the book. These tools allow you to experiment with a working assembler before you set out to build one yourself. In addition, the supplied assembler provides a visual line-by-line translation GUI and allows online code comparisons with the outputs that your assembler will generate. For more information about these capabilities, refer to the assembler tutorial (part of the book’s software suite).

Contract When loaded into your assembler, a Prog.asm file containing a valid Hack assembly language program should be translated into the correct Hack binary code and stored in a Prog.hack file. The output produced by your assembler must be identical to the output produced by the assembler supplied with the book.

Building Plan We suggest building the assembler in two stages. First write a symbol-less assembler, namely, an assembler that can only translate programs that contain no symbols. Then extend your assembler with symbol handling capabilities. The test programs that we supply here come in two such versions (without and with symbols), to help you test your assembler incrementally.

Test Programs Each test program except the first one comes in two versions: ProgL.asm is symbol-less, and Prog.asm is with symbols. - Add: Adds the constants 2 and 3 and puts the result in R0. - Max: Computes maxðR0; R1Þ and puts the result in R2. -Rect: Draws a rectangle at the top left corner of the screen. The rectangle is 16 pixels wide and R0 pixels high. - Pong: A single-player Ping-Pong game. A ball bounces constantly off the screen’s ‘‘walls.’’ The player attempts to hit the ball with a bat by pressing the left and right arrow keys. For every successful hit, the player gains one point and the bat shrinks a little to make the game harder. If the player misses the ball, the game is over. To quit the game, press ESC.

The Pong program was written in the Jack programming language (chapter 9) and translated into the supplied assembly program by the Jack compiler (chap- ters 10–11). Although the original Jack program is only about 300 lines of code, the executable Pong application is about 20,000 lines of binary code, most of which being the Jack operating system (chapter 12). Running this interactive pro- gram in the CPU emulator is a slow affair, so don’t expect a high-powered Pong game. This slowness is actually a virtue, since it enables your eye to track the graphical behavior of the program. In future projects in the book, this game will run much faster.

API

Parser: unpacks each instruction into its underlying fields Code: translates each field into its corresponding binary value SymbolTable: manages the symbol table Main: initializes I/O files and drives the process.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
import sys

A_COMMAND = 1
C_COMMAND = 2
L_COMMAND = 3


class SymbolTable(object):
def __init__(self):
self.symbol_table = {
"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,
"SP":0,
"LCL":1,
"ARG":2,
"THIS":3,
"THAT":4
}

def addEntry(self,symbol,address):
if not self.contains(symbol):
self.symbol_table[symbol] = address

def contains(self,symbol):
return symbol in self.symbol_table

def getAddress(self,symbol):
return self.symbol_table.get(symbol,-1)

def __repr__(self):
return str(self.symbol_table)



class Code(object):
def __init__(self):
self.dest_table = {
"None": "000",
"M": "001",
"D": "010",
"MD": "011",
"A":"100",
"AM": "101",
"AD": "110",
"AMD":"111"
}

self.comp_table = {
"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"
}

self.jump_table = {
"None": "000",
"JGT" : "001",
"JEQ" : "010",
"JGE" : "011",
"JLT" : "100",
"JNE" : "101",
"JLE" : "110",
"JMP" : "111"
}


def comp(self,mnemonic):
if mnemonic in self.comp_table:
return self.comp_table[mnemonic]
else:
print(mnemonic)
print("error finding when code comp")

def dest(self,mnemonic):
if mnemonic in self.dest_table:
return self.dest_table[mnemonic]
else:
print("error finding when code dest")

def jump(self,mnemonic):
if mnemonic in self.jump_table:
return self.jump_table[mnemonic]
else:
print("error finding when code jump")


class Parser(object):

def __init__(self, filename):
self.file = open(filename,"r")
self.current_command = None


def __uncomment__(self):
index = self.current_command.find("//")
if index != -1:
self.current_command = self.current_command[:index]

def hasMoreCommands(self):
line = self.file.readline()
if line:
self.current_command = line
return True
else:
return False


def advance(self):
if self.hasMoreCommands():
self.current_command = self.current_command.strip()
self.__uncomment__()
if self.current_command == "":
self.advance()
return self.current_command
else:
return None


def commandType(self):
if self.current_command.startswith("@"):
return A_COMMAND
elif self.current_command.startswith("("):
return L_COMMAND
else:
return C_COMMAND


def symbol(self):
if self.commandType() == A_COMMAND:
return self.current_command[1:]
elif self.commandType() == L_COMMAND:
return self.current_command[1:-1]

def dest(self):
# dest = comp ; jump
if self.commandType() == C_COMMAND:
if "=" in self.current_command:
return self.current_command.split("=")[0]
else:
return "None"
else:
raise("error find in dest in parser")



def comp(self):
if self.commandType() == C_COMMAND:
result = ""
if "=" in self.current_command and ";" in self.current_command:
result = self.current_command.split("=")[1].strip()
result = result.split(";")[0].strip()
elif "=" in self.current_command:
result = self.current_command.split("=")[1].strip()
elif ";" in self.current_command:
result = self.current_command.split(";")[0].strip()
return result
else:
raise("error find in comp in parser")

def jump(self):
if self.commandType() == C_COMMAND:
if ";" in self.current_command:
return self.current_command.split(";")[-1].strip()
else:
return "None"
else:
raise("error find in jump in parser")



def main(file):
symbol_table = SymbolTable()
coder = Code()


output_file = open(file.split(".")[0]+"."+"hack","w")

parser = Parser(file)
command_index = 0
while True:
current_command = parser.advance()
if not current_command:
break
# print(current_command)
if parser.commandType() == L_COMMAND:
symbol_table.addEntry(parser.symbol(),command_index)
continue

command_index+=1


parser = Parser(file)
command_index = 16

while True:
current_command = parser.advance()
if not current_command:
break
# print(current_command)
if parser.commandType() == A_COMMAND:
symbol = parser.symbol()
if symbol_table.contains(symbol):
address = symbol_table.getAddress(symbol)
else:
if symbol.isdigit():
address = int(symbol)
else:
symbol_table.addEntry(symbol,command_index)
address = command_index
command_index += 1

# print("{:0>16b}".format(address))
output_file.write("{:0>16b}\n".format(address))

elif parser.commandType() == C_COMMAND:

c=parser.comp()
d=parser.dest()
j=parser.jump()

# print(c)
# print(d)
# print(j)

cc = coder.comp(c)
dd = coder.dest(d)
jj = coder.jump(j)

# print(cc)
# print(dd)
# print(jj)


# print("111" + cc + dd + jj)
output_file.write("111" + cc + dd + jj + "\n")




if __name__ == '__main__':
file = sys.argv[1]
main(file)