Skip to content

Commit 5b02785

Browse files
committed
Support robust instruction manipulation of PC
1 parent 6833cb7 commit 5b02785

File tree

4 files changed

+284
-168
lines changed

4 files changed

+284
-168
lines changed

assembler.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,24 @@ def pass1(file):
3939
verbose(line)
4040
continue
4141

42+
# Check if PC is valid
43+
try:
44+
pc = ISA.validate_pc(pc)
45+
except Exception as e:
46+
error(line_count, str(e))
47+
break
48+
49+
4250
# Trim any leading and trailing whitespace
4351
line = line.strip()
4452

4553
verbose('{}: {}'.format(pc, line))
4654

47-
4855
# Make line case-insensitive
4956
line = line.lower()
5057

5158
# Parse line
52-
label, op, _ = ISA.get_parts(line)
59+
label, op, operands = ISA.get_parts(line)
5360
if label:
5461
if label in ISA.SYMBOL_TABLE:
5562
error(line_count, "label '{}' is defined more than once".format(label))
@@ -58,12 +65,20 @@ def pass1(file):
5865
ISA.SYMBOL_TABLE[label] = pc
5966

6067
if op:
68+
instr = None
6169
try:
62-
pc += getattr(ISA, ISA.instruction_class(op)).size()
70+
instr = getattr(ISA, ISA.instruction_class(op))
6371
except:
6472
error(line_count, "instruction '{}' is not defined in the current ISA".format(op))
6573
no_errors = False
74+
75+
try:
76+
pc = instr.pc(pc=pc, instruction=op, operands=operands)
77+
except Exception as e:
78+
error(line_count, str(e))
79+
no_errors = False
6680

81+
6782
line_count += 1
6883

6984
verbose("\nFinished Pass 1.\n")
@@ -77,7 +92,7 @@ def pass2(input_file, use_hex):
7792
pc = 0
7893
line_count = 1
7994
success = True
80-
results = []
95+
results = {}
8196

8297
# Seek to beginning of file
8398
input_file.seek(0)
@@ -112,8 +127,16 @@ def pass2(input_file, use_hex):
112127
success = False
113128

114129
if assembled:
115-
results.extend(assembled)
116-
pc += instr.size()
130+
for i in range(len(assembled)):
131+
cur_pc = pc + i
132+
if cur_pc in results:
133+
error(line_count, "PC address {} is defined more than once".format(cur_pc))
134+
success = False
135+
break
136+
137+
results[cur_pc] = assembled[i]
138+
139+
pc = instr.pc(pc=pc, instruction=op, operands=operands)
117140

118141
line_count += 1
119142

@@ -202,7 +225,8 @@ def parse_params(values):
202225
print("Writing to {}...".format(outFileName + code_ext), end="")
203226

204227
with open(outFileName + code_ext, 'w') as write_file:
205-
for r in results:
228+
out_generator = ISA.output_generator(results, 'hex' if args.hex else 'binary')
229+
for r in out_generator:
206230
write_file.write(r + sep)
207231

208232
print('done!')

lc2200.py

Lines changed: 81 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,6 @@
4141
SYMBOL_TABLE = {}
4242

4343

44-
# Public Functions
45-
def receive_params(value_table):
46-
if value_table:
47-
raise RuntimeError('Custom parameters are not supported')
48-
49-
def is_blank(line):
50-
"""Return whether a line is blank and not an instruction."""
51-
return __RE_BLANK__.match(line) is not None
52-
53-
def get_parts(line):
54-
"""Break down an instruction into 3 parts: Label, Opcode, Operand"""
55-
m = __RE_PARTS__.match(line)
56-
try:
57-
return m.group('Label'), m.group('Opcode'), m.group('Operands')
58-
except:
59-
return None
60-
61-
def instruction_class(name):
62-
"""Translate a given instruction name to its corresponding class name."""
63-
return ALIASES.get(name, name)
64-
6544
# Private Variables
6645
__OFFSET_SIZE__ = BIT_WIDTH - OPCODE_WIDTH - (REGISTER_WIDTH * 2)
6746
assert(__OFFSET_SIZE__ > 0) # Sanity check
@@ -210,8 +189,8 @@ def opcode():
210189
raise NotImplementedError()
211190

212191
@staticmethod
213-
def size():
214-
"""Return how many binary machine-level instructions the instruction will expand to."""
192+
def pc(pc, **kwargs):
193+
"""Return the new PC after assembling the given instruction"""
215194
raise NotImplementedError()
216195

217196
@staticmethod
@@ -243,10 +222,10 @@ class add(Instruction):
243222
@staticmethod
244223
def opcode():
245224
return 0
246-
225+
247226
@staticmethod
248-
def size():
249-
return 1
227+
def pc(pc, **kwargs):
228+
return pc + 1
250229

251230
@staticmethod
252231
def binary(operands, **kwargs):
@@ -262,10 +241,10 @@ class neg(Instruction):
262241
@staticmethod
263242
def opcode():
264243
return 1
265-
244+
266245
@staticmethod
267-
def size():
268-
return 1
246+
def pc(pc, operands):
247+
return pc + 1
269248

270249
@staticmethod
271250
def binary(operands, **kwargs):
@@ -282,10 +261,10 @@ class addi(Instruction):
282261
@staticmethod
283262
def opcode():
284263
return 2
285-
264+
286265
@staticmethod
287-
def size():
288-
return 1
266+
def pc(pc, **kwargs):
267+
return pc + 1
289268

290269
@staticmethod
291270
def binary(operands, **kwargs):
@@ -304,8 +283,8 @@ def opcode():
304283
return 3
305284

306285
@staticmethod
307-
def size():
308-
return 1
286+
def pc(pc, **kwargs):
287+
return pc + 1
309288

310289
@staticmethod
311290
def binary(operands, **kwargs):
@@ -324,8 +303,8 @@ def opcode():
324303
return 4
325304

326305
@staticmethod
327-
def size():
328-
return 1
306+
def pc(pc, **kwargs):
307+
return pc + 1
329308

330309
@staticmethod
331310
def binary(operands, **kwargs):
@@ -343,8 +322,8 @@ def opcode():
343322
return 5
344323

345324
@staticmethod
346-
def size():
347-
return 1
325+
def pc(pc, **kwargs):
326+
return pc + 1
348327

349328
@staticmethod
350329
def binary(operands, **kwargs):
@@ -365,8 +344,8 @@ def opcode():
365344
return 6
366345

367346
@staticmethod
368-
def size():
369-
return 1
347+
def pc(pc, **kwargs):
348+
return pc + 1
370349

371350
@staticmethod
372351
def binary(operands, **kwargs):
@@ -385,8 +364,8 @@ def opcode():
385364
return 7
386365

387366
@staticmethod
388-
def size():
389-
return 1
367+
def pc(pc, **kwargs):
368+
return pc + 1
390369

391370
@staticmethod
392371
def binary(operands, **kwargs):
@@ -413,8 +392,8 @@ def opcode():
413392
return None
414393

415394
@staticmethod
416-
def size():
417-
return 4
395+
def pc(pc, **kwargs):
396+
return pc + 4
418397

419398
@staticmethod
420399
def binary(operands, **kwargs):
@@ -458,8 +437,8 @@ def opcode():
458437
return None
459438

460439
@staticmethod
461-
def size():
462-
return 1
440+
def pc(pc, **kwargs):
441+
return pc + 1
463442

464443
@staticmethod
465444
def binary(operands, **kwargs):
@@ -476,8 +455,8 @@ def opcode():
476455
return None
477456

478457
@staticmethod
479-
def size():
480-
return 1
458+
def pc(pc, **kwargs):
459+
return pc + 1
481460

482461
@staticmethod
483462
def binary(operands, **kwargs):
@@ -496,8 +475,8 @@ def opcode():
496475
return None
497476

498477
@staticmethod
499-
def size():
500-
return 1
478+
def pc(pc, **kwargs):
479+
return pc + 1
501480

502481
@staticmethod
503482
def binary(operands, **kwargs):
@@ -506,3 +485,55 @@ def binary(operands, **kwargs):
506485
@staticmethod
507486
def hex(operands, **kwargs):
508487
return [__bin2hex__(instr) for instr in halt.binary(operands, **kwargs)]
488+
489+
490+
491+
492+
# Public Functions
493+
def receive_params(value_table):
494+
if value_table:
495+
raise RuntimeError('Custom parameters are not supported')
496+
497+
def is_blank(line):
498+
"""Return whether a line is blank and not an instruction."""
499+
return __RE_BLANK__.match(line) is not None
500+
501+
def get_parts(line):
502+
"""Break down an instruction into 3 parts: Label, Opcode, Operand"""
503+
m = __RE_PARTS__.match(line)
504+
try:
505+
return m.group('Label'), m.group('Opcode'), m.group('Operands')
506+
except:
507+
return None
508+
509+
def instruction_class(name):
510+
"""Translate a given instruction name to its corresponding class name."""
511+
return ALIASES.get(name, name)
512+
513+
def validate_pc(pc):
514+
"""Returns or modifies the PC to a permitted value, if possible. Throws an error if the PC is invalid."""
515+
if pc >= 2**BIT_WIDTH:
516+
raise RuntimeError("PC value {} is too large for {} bits.".format(pc, BIT_WIDTH))
517+
518+
return pc
519+
520+
def output_generator(assembled_dict, output_format='binary'):
521+
"""Returns a generator that creates output from {pc : assembly}-formatted dictionary."""
522+
pc = 0
523+
count = 0
524+
525+
while count < len(assembled_dict):
526+
if pc in assembled_dict:
527+
yield assembled_dict[pc]
528+
pc += 1
529+
count += 1
530+
else:
531+
if output_format == 'hex':
532+
results = noop.hex()
533+
elif output_format == 'binary':
534+
results = noop.binary()
535+
536+
for r in results:
537+
yield r
538+
539+
pc = noop.pc(pc)

0 commit comments

Comments
 (0)