asm.py: improve literal handling; parse source file only once

This commit is contained in:
Maurizio Porrato 2020-04-18 16:23:30 +01:00
parent a75d9c6781
commit d13d72ce8b
1 changed files with 36 additions and 50 deletions

86
asm.py
View File

@ -32,11 +32,13 @@ REGISTERS = {
'I': 0x06, 'J': 0x07, 'SP': 0x1b, 'PC': 0x1c, 'EX': 0x1d 'I': 0x06, 'J': 0x07, 'SP': 0x1b, 'PC': 0x1c, 'EX': 0x1d
} }
class ASM(SimpleNamespace): class ASM(SimpleNamespace):
@staticmethod @staticmethod
def addr(arg, is_a): def addr(arg, is_a):
if type(arg) == Expr: if type(arg) == Expr:
arg = arg.value return (0x1f, arg)
if type(arg) == Register: if type(arg) == Register:
return (REGISTERS[arg.name], None) return (REGISTERS[arg.name], None)
elif type(arg) == int: elif type(arg) == int:
@ -45,12 +47,8 @@ class ASM(SimpleNamespace):
else: else:
return (0x1f, arg) return (0x1f, arg)
elif type(arg) == Indirect: elif type(arg) == Indirect:
if type(arg.disp) == Expr:
disp = getattr(arg.disp, "value", 0)
else:
disp = arg.disp
if arg.reg is None: if arg.reg is None:
return (0x1e, disp) return (0x1e, arg.disp)
else: else:
if arg.reg == 'SP': if arg.reg == 'SP':
sp = getattr(arg, 'sp', None) sp = getattr(arg, 'sp', None)
@ -75,7 +73,8 @@ class ASM(SimpleNamespace):
if arg.disp == 0: if arg.disp == 0:
return (0x08+REGISTERS[r], None) return (0x08+REGISTERS[r], None)
else: else:
return (0x10+REGISTERS[r], disp) return (0x10+REGISTERS[r], arg.disp)
def code(self): def code(self):
o, b = OPCODES[self.op] o, b = OPCODES[self.op]
a_bits, a_extra = self.addr(self.a, True) a_bits, a_extra = self.addr(self.a, True)
@ -84,27 +83,15 @@ class ASM(SimpleNamespace):
else: else:
b_bits, b_extra = self.addr(self.b, False) b_bits, b_extra = self.addr(self.b, False)
r = [o | (a_bits << 10) | (b_bits << 5)] r = [o | (a_bits << 10) | (b_bits << 5)]
for e in a_extra, b_extra: for e in b_extra, a_extra:
if e is not None: if e is not None:
r.append(e) r.append(e)
print("Assembing: %s %r, %r -> %r" % (self.op, self.b, self.a, r))
return r return r
def words(self): def words(self):
r = 1 return len(self.code())
if type(self.a) in (Expr, int): # literal (FIXME: optimize short literals)
if type(self.a) == int:
if not (-1 <= self.a <= 30):
r += 1
else:
r += 1
elif type(self.a) == Indirect:
if self.a.disp != 0:
r += 1
if type(self.b) in (Expr, int): # literal
r += 1
elif type(self.b) == Indirect:
if self.b.disp != 0:
r += 1
return r
class Expr(SimpleNamespace): class Expr(SimpleNamespace):
def eval(self, ctx): def eval(self, ctx):
@ -347,65 +334,64 @@ def expr_sym(p):
lexer = lg.build() lexer = lg.build()
parser = pg.build() parser = pg.build()
def assemble(ctx, inst, step=1): def assemble(ctx, inst):
if inst.label is not None: if inst.label is not None:
if step == 1 and inst.label in ctx:
print(f"Redefining symbol {inst.label}")
ctx[inst.label] = ctx['.addr'] ctx[inst.label] = ctx['.addr']
if type(inst) == Directive: if type(inst) == Directive:
if inst.directive == ".org": if inst.directive == ".org":
ctx['.addr'] = inst.args.eval(ctx) ctx['.addr'] = inst.args.eval(ctx)
return None
elif inst.directive in (".data", ".word", "DAT"): elif inst.directive in (".data", ".word", "DAT"):
al = [] al = []
for a in inst.args: for a in inst.args:
if type(a) == str: if type(a) == str:
al.extend([ord(x) for x in a]) al.extend([ord(x) for x in a])
else: else:
if step == 1: al.append(a.simplify(ctx))
al.append(a)
else:
al.append(a.eval(ctx))
if step > 1:
print(ctx['.addr'], al)
ctx['.addr'] += len(al) ctx['.addr'] += len(al)
return al
elif type(inst) == ASM: elif type(inst) == ASM:
try: if type(inst.a) == Expr:
if type(inst.a) == Expr: inst.a = inst.a.simplify(ctx)
inst.a = inst.a.eval(ctx) if type(inst.b) == Expr:
if type(inst.b) == Expr: inst.b = inst.b.simplify(ctx)
inst.b = inst.b.eval(ctx)
except KeyError as e:
if step != 1:
raise e
if inst.b is not None: if inst.b is not None:
print(f"{ctx['.addr']} {inst.op} {inst.b}, {inst.a} [len={inst.words()}]") print(f"{ctx['.addr']} {inst.op} {inst.b}, {inst.a} [len={inst.words()}]")
else: else:
print(f"{ctx['.addr']} {inst.op} {inst.a} [len={inst.words()}]") print(f"{ctx['.addr']} {inst.op} {inst.a} [len={inst.words()}]")
ctx['.addr'] += inst.words() ctx['.addr'] += inst.words()
return inst.code()
if step != 1:
return inst.code()
if __name__ == '__main__': if __name__ == '__main__':
import sys import sys
sym = {} sym = {}
sym['.addr'] = 0 sym['.addr'] = 0
insns = []
for filename in sys.argv[1:]: for filename in sys.argv[1:]:
with open(filename, 'r') as sourcefile: with open(filename, 'r') as sourcefile:
code = parser.parse(lexer.lex(sourcefile.read())) code = parser.parse(lexer.lex(sourcefile.read()))
for inst in code: # pylint: disable=E1133 for inst in code: # pylint: disable=E1133
# print(sym['.addr'], inst) # print(sym['.addr'], inst)
assemble(sym, inst) a = sym['.addr']
c = assemble(sym, inst)
if c is not None:
insns.append((a, c))
print(sym) print(sym)
print(insns)
sym['.addr'] = 0 sym['.addr'] = 0
binimage = b'' binimage = b''
for inst in code: # pylint: disable=E1133 for a, c in insns: # pylint: disable=E1133
a = assemble(sym, inst, 2) words = []
if a is not None: for w in c:
print(["%04x" % x for x in a]) if type(w) == int:
for w in a: words.append(w)
else:
words.append(w.eval(sym))
if words:
print(["%04x" % x for x in words])
for w in words:
binimage += struct.pack("<H", w) binimage += struct.pack("<H", w)
outfilename = filename[:filename.rfind('.')]+'.bin' outfilename = filename[:filename.rfind('.')]+'.bin'
with open(outfilename, 'wb') as binfile: with open(outfilename, 'wb') as binfile: