#!/usr/bin/env python3 from types import SimpleNamespace from rply import ParserGenerator, LexerGenerator import re import struct CASE_INSENSITIVE = True OPS = { 'ADD': lambda a, b: a + b, 'SUBTRACT': lambda a, b: a - b, 'MULTIPLY': lambda a, b: a * b, 'DIVIDE': lambda a, b: a / b } OPCODES = { 'SET': (0x01, None), 'ADD': (0x02, None), 'SUB': (0x03, None), 'MUL': (0x04, None), 'MLI': (0x05, None), 'DIV': (0x06, None), 'DVI': (0x07, None), 'MOD': (0x08, None), 'MDI': (0x09, None), 'AND': (0x0a, None), 'BOR': (0x0b, None), 'XOR': (0x0c, None), 'SHR': (0x0d, None), 'ASR': (0x0e, None), 'SHL': (0x0f, None), 'IFB': (0x10, None), 'IFC': (0x11, None), 'IFE': (0x12, None), 'IFN': (0x13, None), 'IFG': (0x14, None), 'IFA': (0x15, None), 'IFL': (0x16, None), 'IFU': (0x17, None), 'ADX': (0x1a, None), 'SBX': (0x1b, None), 'STI': (0x1e, None), 'STD': (0x1f, None), 'JSR': (0x00, 0x01), 'INT': (0x00, 0x08), 'IAG': (0x00, 0x09), 'IAS': (0x00, 0x0a), 'RFI': (0x00, 0x0b), 'IAQ': (0x00, 0x0c), 'HWN': (0x00, 0x10), 'HWQ': (0x00, 0x11), 'HWI': (0x00, 0x12), 'JMP': (0x01, 0x1c) # Alias for SET PC, a } REGISTERS = { 'A': 0x00, 'B': 0x01, 'C': 0x02, 'X': 0x03, 'Y': 0x04, 'Z': 0x05, 'I': 0x06, 'J': 0x07, 'SP': 0x1b, 'PC': 0x1c, 'EX': 0x1d, 'O': 0x1d # For compatibility with specs v1.1 } class SymbolTable: def __init__(self, ignorecase=False): self.ignorecase = ignorecase self.symbols = {} self.orig = {} def __getitem__(self, key): if self.ignorecase: key = self.orig[key.upper()] return self.symbols[key] def __setitem__(self, key, value): ukey = key.upper() if ukey in self.orig: key = self.orig[ukey] else: self.orig[ukey] = key self.symbols[key] = value def __str__(self): return '\n'.join([ f"{k}: {v}" for k, v in sorted(self.symbols.items())]) class ASM(SimpleNamespace): @staticmethod def addr(arg, is_a): if type(arg) == Expr: return (0x1f, arg) if type(arg) == Register: return (REGISTERS[arg.name], None) elif type(arg) == int: if is_a and (-1 <= arg <= 30): return (arg + 0x21, None) else: return (0x1f, arg) elif type(arg) == Indirect: if arg.reg is None: return (0x1e, arg.disp) else: if arg.reg == 'SP': sp = getattr(arg, 'sp', None) if sp is None: if arg.disp == 0: return (0x19, None) else: return (0x1a, arg.disp) else: return (0x18, None) elif arg.reg in ('PC', 'EX'): raise SyntaxError() else: if hasattr(type(arg.reg), "getstr"): r = arg.reg.getstr() else: r = arg.reg print("REG: "+r) if arg.disp == 0: return (0x08+REGISTERS[r], None) else: return (0x10+REGISTERS[r], arg.disp) def code(self): o, b = OPCODES[self.op] a_bits, a_extra = self.addr(self.a, True) if b is not None: b_bits, b_extra = b, None else: b_bits, b_extra = self.addr(self.b, False) r = [o | (a_bits << 10) | (b_bits << 5)] for e in a_extra, b_extra: if e is not None: r.append(e) print("Assembing: %s %r, %r -> %r" % (self.op, self.b, self.a, r)) return r def words(self): return len(self.code()) class Expr(SimpleNamespace): def eval(self, ctx): if self.op == 'SYMBOL': return ctx[self.name] elif self.op == 'NUMBER': return self.value else: return OPS[self.op](self.l.eval(ctx), self.r.eval(ctx)) def simplify(self, ctx=SymbolTable(CASE_INSENSITIVE)): if self.op == 'NUMBER': return self elif self.op == 'SYMBOL': try: return Expr(op='NUMBER', value=ctx[self.name]) except: return self else: e = Expr(op=self.op, l=self.l.simplify(ctx), r=self.r.simplify(ctx)) if e.l.op == 'NUMBER' and e.r.op == 'NUMBER': return Expr(op='NUMBER', value=e.eval(ctx)) else: return e class Directive(SimpleNamespace): pass class Register(SimpleNamespace): pass class Indirect(SimpleNamespace): pass lg = LexerGenerator() STARTOP = r'(? 1: base = 8 text = text[1:] value = int(text, base) if negate: value = -value return Expr(op='NUMBER', value=value) @pg.production("expr : SYMBOL") def expr_sym(p): return Expr(op='SYMBOL', name=p[0].getstr()) lexer = lg.build() parser = pg.build() def assemble(ctx, inst): if inst.label is not None: ctx[inst.label] = ctx['.addr'] if type(inst) == Directive: if inst.directive == ".org": ctx['.addr'] = inst.args.eval(ctx) return None elif inst.directive in (".data", ".word", "DAT"): al = [] for a in inst.args: if type(a) == str: al.extend([ord(x) for x in a]) else: al.append(a.simplify(ctx)) ctx['.addr'] += len(al) return al elif type(inst) == ASM: if type(inst.a) == Expr: inst.a = inst.a.simplify(ctx) if type(inst.b) == Expr: inst.b = inst.b.simplify(ctx) if inst.b is not None: print(f"{ctx['.addr']} {inst.op} {inst.b}, {inst.a} [len={inst.words()}]") else: print(f"{ctx['.addr']} {inst.op} {inst.a} [len={inst.words()}]") ctx['.addr'] += inst.words() return inst.code() class AssemblerError(BaseException): pass if __name__ == '__main__': import sys sym = SymbolTable(CASE_INSENSITIVE) sym['.addr'] = 0 insns = [] for filename in sys.argv[1:]: with open(filename, 'r') as sourcefile: code = parser.parse(lexer.lex(sourcefile.read())) for inst in code: # pylint: disable=E1133 print(sym['.addr'], inst) a = sym['.addr'] c = assemble(sym, inst) if c is not None: insns.append((inst.pos, a, c)) print(sym) print(insns) sym['.addr'] = 0 binimage = b'' for pos, a, c in insns: # pylint: disable=E1133 words = [] for w in c: if type(w) == int: words.append(w) else: words.append(w.eval(sym)) if words: print(pos,["%04x" % x for x in words]) for w in words: if w < 0: w = (1<<16)+w if w < 0 or w > 0xffff: raise AssemblerError("Value out of bounds: "+str(w)) binimage += struct.pack("