hystricomorph/asm.py

265 lines
4.5 KiB
Python
Raw Permalink Normal View History

2019-08-09 22:28:32 +00:00
from textwrap import indent, dedent
2019-08-10 21:01:51 +00:00
from time import asctime
2019-08-09 22:28:32 +00:00
class Block(object):
def __init__(self):
self.size = 0
2019-08-11 05:13:13 +00:00
self.branch_target = None
self.branch_long = False
self.branch_type = None
self.branch_size = 0
2019-08-09 22:28:32 +00:00
self.labels = []
self.instr = []
self.rts = False
2019-08-11 04:12:10 +00:00
self.mx = 0b11
2019-08-09 22:28:32 +00:00
def empty(self):
2019-08-11 05:13:13 +00:00
return self.size + self.branch_size == 0
2019-08-09 22:28:32 +00:00
class Assembler(object):
2019-08-11 05:13:13 +00:00
__inverted_branch = {
'bne': 'beq',
'beq': 'bne',
'bvc': 'bvs',
'bvs': 'bvc',
'bpl': 'bmi',
'bmi': 'bpl',
'bcc': 'bcs',
'bcs': 'bcc'
}
2019-08-09 22:28:32 +00:00
def __init__(self, name):
self.name = name
2019-08-11 05:13:49 +00:00
self.mx = 0b11
2019-08-09 22:28:32 +00:00
self.blocks = []
self.b = None
self.label = 0
2019-08-11 05:13:49 +00:00
2019-08-09 22:28:32 +00:00
self.new_block()
def reserve_label(self):
self.label = self.label + 1
return "_%d" % (self.label)
def emit_label(self, l):
if not self.b.empty():
self.new_block()
self.b.labels.append(l)
def rts(self):
if not self.b.empty():
self.new_block()
self.emit("rts",1)
self.b.rts = True
self.new_block()
def new_block(self):
self.b = Block()
2019-08-11 05:13:49 +00:00
self.b.mx = self.mx
2019-08-11 04:12:10 +00:00
2019-08-09 22:28:32 +00:00
self.blocks.append(self.b)
def bne(self, l):
2019-08-11 05:13:13 +00:00
self.b.branch_type = 'bne'
self.b.branch_target = l
self.b.branch_size = 2
2019-08-09 22:28:32 +00:00
self.new_block()
def emit(self, op, size):
self.b.size = self.b.size + size
self.b.instr.append("\t" + op)
2019-08-11 04:12:10 +00:00
def mx_common(self, onoff, mask):
2019-08-11 05:13:49 +00:00
mx = self.mx
2019-08-11 04:12:10 +00:00
if onoff: mx |= mask
else: mx &= ~mask
2019-08-11 05:13:49 +00:00
if mx == self.mx: return
2019-08-11 04:12:10 +00:00
if not self.b.empty():
self.new_block()
self.b.mx = mx
2019-08-11 05:13:49 +00:00
self.mx = mx
@property
def longm(self):
return bool(self.mx & 0b10)
2019-08-11 04:12:10 +00:00
2019-08-11 05:13:49 +00:00
@longm.setter
def longm(self, value):
self.mx_common(value, 0b10)
2019-08-11 04:12:10 +00:00
2019-08-11 05:13:49 +00:00
@property
def longx(self):
return bool(self.mx & 0b01)
2019-08-11 04:12:10 +00:00
2019-08-11 05:13:49 +00:00
@longx.setter
def longx(self, value):
self.mx_common(value, 0b01)
2019-08-11 04:12:10 +00:00
2019-08-09 22:28:32 +00:00
def merge_rts(self):
blocks = []
prev = None
for b in self.blocks:
if b.rts and prev and prev.rts:
prev.labels.extend(b.labels)
continue
blocks.append(b)
prev = b
self.blocks = blocks
def merge_labels(self):
map = {}
for b in self.blocks:
ll = b.labels
if len(ll)>0:
first = ll[0]
for l in ll: map[l] = first
b.labels = [first]
2019-08-09 22:28:32 +00:00
for b in self.blocks:
2019-08-11 05:13:13 +00:00
if b.branch_target:
b.branch_target = map[b.branch_target]
2019-08-09 22:28:32 +00:00
def reify_branches(self):
# in practice all branches are forward
# could be clever and try to find a backwards rts branch island
pc = 0
map = {}
for b in self.blocks:
for l in b.labels:
map[l] = pc
2019-08-11 05:13:13 +00:00
pc = pc + b.size + b.branch_size
2019-08-09 22:28:32 +00:00
delta = True
while delta:
pc = 0
delta = False
for b in self.blocks:
pc = pc + b.size
2019-08-11 05:13:13 +00:00
l = b.branch_target
2019-08-09 22:28:32 +00:00
if not l: continue
2019-08-11 05:13:13 +00:00
if b.branch_long:
pc = pc + b.branch_size
2019-08-09 22:28:32 +00:00
continue
target = map[l]
diff = target-(pc+1)
pc = pc + 2
if diff < -128 or diff > 127:
delta = True
2019-08-11 05:13:13 +00:00
b.branch_long = True
if b.branch_type == 'bra':
b.branch_type = 'brl'
b.branch_size = 3
fudge = 1
else:
b.branch_size = 5
fudge = 3
2019-08-09 22:28:32 +00:00
for x in map:
if map[x] >= pc:
2019-08-11 05:13:13 +00:00
map[x] = map[x] + fudge
2019-08-09 22:28:32 +00:00
for b in self.blocks:
2019-08-11 05:13:13 +00:00
l = b.branch_target
2019-08-09 22:28:32 +00:00
if not l: continue
2019-08-11 05:13:13 +00:00
t = b.branch_type
if b.branch_long:
if b.branch_type not in ('bra', 'brl'):
t = self.__inverted_branch[t]
b.instr.append("\t{} *+5".format(t))
2019-08-09 22:28:32 +00:00
b.instr.append("\tbrl " + l)
else:
2019-08-11 05:13:13 +00:00
b.instr.append("\t{} {}".format(t, l))
2019-08-09 22:28:32 +00:00
2019-08-11 05:13:13 +00:00
b.size = b.size + b.branch_size
2019-08-09 22:28:32 +00:00
def finish(self,io):
2019-08-11 04:12:10 +00:00
onoff = ("on", "off")
2019-08-09 22:28:32 +00:00
self.b = None
self.merge_rts()
self.merge_labels()
self.reify_branches()
self.header(io)
2019-08-11 04:12:10 +00:00
mx = 0b11
io.write("\tlongi on\n")
io.write("\tlonga on\n")
2019-08-09 22:28:32 +00:00
for b in self.blocks:
for l in b.labels: io.write(l + "\tanop\n")
2019-08-11 04:12:10 +00:00
# io.write(f"mx: {b.mx}\n")
mxdiff = mx ^ b.mx
if mxdiff:
if mxdiff & 0b01:
io.write("\tlongi " + onoff[mx & 0b01] + "\n")
if mxdiff & 0b10:
io.write("\tlonga " + onoff[(mx & 0b10) >> 1] + "\n")
mx = b.mx
2019-08-09 22:28:32 +00:00
for i in b.instr: io.write(i + "\n")
self.footer(io)
self.blocks = []
2019-08-11 05:13:49 +00:00
self.mx = 0b11
2019-08-09 22:28:32 +00:00
self.new_block()
def header(self, io):
2019-08-10 21:01:51 +00:00
io.write("* generated " + asctime() + "\n\n")
io.write("\tcase on\n\n");
io.write("dummy\tSTART\n\tEND\n\n")
2019-08-09 22:28:32 +00:00
io.write(self.name + "\tSTART\n\n")
io.write("cp\tequ 5\n")
txt = """
phb
tsc
phd
tcd
2019-08-09 22:28:32 +00:00
pei cp+1
plb
plb
jsr _action
rep #$20
lda 1
sta 5
lda 3
sta 7
pld
pla
pla
txa
plb
rtl
"""
io.write(indent(dedent(txt),"\t"))
io.write("\n_action\tanop\n")
io.write("\tldx #0\n\n")
def footer(self, io):
io.write("\tEND\n")