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)