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
}
class ASM(SimpleNamespace):
@staticmethod
def addr(arg, is_a):
if type(arg) == Expr:
arg = arg.value
return (0x1f, arg)
if type(arg) == Register:
return (REGISTERS[arg.name], None)
elif type(arg) == int:
@ -45,12 +47,8 @@ class ASM(SimpleNamespace):
else:
return (0x1f, arg)
elif type(arg) == Indirect:
if type(arg.disp) == Expr:
disp = getattr(arg.disp, "value", 0)
else:
disp = arg.disp
if arg.reg is None:
return (0x1e, disp)
return (0x1e, arg.disp)
else:
if arg.reg == 'SP':
sp = getattr(arg, 'sp', None)
@ -75,7 +73,8 @@ class ASM(SimpleNamespace):
if arg.disp == 0:
return (0x08+REGISTERS[r], None)
else:
return (0x10+REGISTERS[r], disp)
return (0x10+REGISTERS[r], arg.disp)
def code(self):
o, b = OPCODES[self.op]
a_bits, a_extra = self.addr(self.a, True)
@ -84,27 +83,15 @@ class ASM(SimpleNamespace):
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:
for e in b_extra, a_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):
r = 1
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
return len(self.code())
class Expr(SimpleNamespace):
def eval(self, ctx):
@ -347,65 +334,64 @@ def expr_sym(p):
lexer = lg.build()
parser = pg.build()
def assemble(ctx, inst, step=1):
def assemble(ctx, inst):
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']
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:
if step == 1:
al.append(a)
else:
al.append(a.eval(ctx))
if step > 1:
print(ctx['.addr'], al)
al.append(a.simplify(ctx))
ctx['.addr'] += len(al)
return al
elif type(inst) == ASM:
try:
if type(inst.a) == Expr:
inst.a = inst.a.eval(ctx)
if type(inst.b) == Expr:
inst.b = inst.b.eval(ctx)
except KeyError as e:
if step != 1:
raise e
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()
if step != 1:
return inst.code()
return inst.code()
if __name__ == '__main__':
import sys
sym = {}
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)
assemble(sym, inst)
a = sym['.addr']
c = assemble(sym, inst)
if c is not None:
insns.append((a, c))
print(sym)
print(insns)
sym['.addr'] = 0
binimage = b''
for inst in code: # pylint: disable=E1133
a = assemble(sym, inst, 2)
if a is not None:
print(["%04x" % x for x in a])
for w in a:
for 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(["%04x" % x for x in words])
for w in words:
binimage += struct.pack("<H", w)
outfilename = filename[:filename.rfind('.')]+'.bin'
with open(outfilename, 'wb') as binfile: