User:1hals/subleq.py
Jump to navigation
Jump to search
subleq.py is my Python script to compile and emulate Subleq assembler code.
#!/usr/bin/env python3
import argparse, sys
p = argparse.ArgumentParser()
p.add_argument('file', help="the program file to parse")
p.add_argument('-emulate', action='store_true', help="emulate the program")
p.add_argument('-memory', type=int, default=0, help="additional memory when evaluating")
p.add_argument('-1', dest='one', action='store_true', help="emit output on one line")
args = p.parse_args()
with open(args.file, "r") as file:
text = file.read()
def error (msg):
print('error:', msg, file=sys.stderr)
sys.exit(-1)
# preprocess labels and comments
lines = text.split('\n')
labels = dict() # map labels to addresses
code = list() # source code
for i, line in enumerate(lines):
htag = line.find('#')
if htag >= 0: # remove comments
line = line[:htag]
if not line: continue
abc = line.split()
if len(abc) not in (2, 3):
adj = 'many' if len(abc) > 3 else 'few'
error('too %s instructions one line %d' %(adj, i))
for op in abc: # get labels and instructions
colon = op.find(':')
if colon > -1: # no label
if colon == 0: # invalid label
error('empty label')
label = op[:colon]
op = op[colon+1:]
labels[label] = len(code)
code.append(op)
if len(abc) == 2: # default 3rd argument is the next instruction
code.append(len(code) + 1)
# resolve numbers, the question mark, and labels
for i, op in enumerate(code):
n = 0 # final result number
if op[0] == '?': # question mark means the current instruction address
if op[1] == '+': # relative offset
n += i
op = op[2:]
elif op[1] == '-': # relative offset
n -= i
op = op[2:]
else: # no offset
code[i] = i
continue
try: # number
n += int(op)
except ValueError: # label
try:
n += labels[op]
except KeyError:
error('undefined label "%s"' % op)
code[i] = n
if args.emulate: # emulate the program
pc = 0 # program counter
mem = code + ([0] * args.memory) # program & memory
while pc >= 0: # branch to negative address halts the program
a, b, c = mem[pc:pc+3]
if b == -1: # memory map address -1 to output
sys.stdout.write(chr(mem[a]))
pc += 3
# note: you can add more memory-mapped special cases here
else: # subleq
r = mem[b] - mem[a]
mem[b] = r
if r > 0:
pc +=3
else:
pc = c
sys.exit(0)
# just emit the assembled output
sep2 = ' ' if args.one else '\n'
for i in range(0, len(code), 3):
print(' '.join(str(x) for x in code[i:i+3]), end=sep2)