2021-02-17 21:20:30 +00:00
|
|
|
import argparse
|
|
|
|
import enum
|
|
|
|
import lark
|
2022-05-07 18:33:45 +00:00
|
|
|
import os
|
2021-02-17 21:20:30 +00:00
|
|
|
import struct
|
|
|
|
import sys
|
|
|
|
|
|
|
|
import obj_pb2
|
|
|
|
|
2022-05-07 18:33:45 +00:00
|
|
|
_HERE = os.path.dirname(__file__)
|
|
|
|
GRAMMAR_FILE = os.path.join(_HERE, 'as.ebnf')
|
2021-02-17 21:20:30 +00:00
|
|
|
|
|
|
|
opcodes = {
|
|
|
|
'nop' : lambda: '0000',
|
|
|
|
'load' : lambda p0, p1, p2: f'1{p0:x}{p1:x}{(p2 >> 1)&0xf:x}',
|
|
|
|
'store': lambda p0, p1, p2: f'2{p0:x}{p1:x}{(p2 >> 1)&0xf:x}',
|
|
|
|
'add' : lambda p0, p1, p2: f'3{p0:x}{p1:x}{p2:x}',
|
|
|
|
'sub' : lambda p0, p1, p2: f'4{p0:x}{p1:x}{p2:x}',
|
|
|
|
'or' : lambda p0, p1, p2: f'5{p0:x}{p1:x}{p2:x}',
|
|
|
|
'and' : lambda p0, p1, p2: f'6{p0:x}{p1:x}{p2:x}',
|
|
|
|
'not' : lambda p0, p1: f'7{p0:x}{p1:x}0',
|
|
|
|
'xor' : lambda p0, p1, p2: f'8{p0:x}{p1:x}{p2:x}',
|
|
|
|
'seth' : lambda p0, p1: f'9{p0:x}{p1&0xff:02x}',
|
|
|
|
'shr' : lambda p0, p1, p2: f'a{p0:x}{p1:x}{p2:x}',
|
|
|
|
'mul' : lambda p0, p1, p2: f'b{p0:x}{p1:x}{p2:x}',
|
|
|
|
'cmp' : lambda p0, p1: f'c{p0:x}{p1:x}0',
|
2021-03-14 19:14:29 +00:00
|
|
|
'beq' : lambda p0, p1: f'd{(p1 >> 1)&0xfff:03x}',
|
2021-02-17 21:20:30 +00:00
|
|
|
'set' : lambda p0, p1: f'e{p0:x}{p1&0xff:02x}',
|
2021-03-14 19:14:29 +00:00
|
|
|
'bneq' : lambda p0, p1: f'f{(p1 >> 1)&0xfff:03x}',
|
2021-02-17 21:20:30 +00:00
|
|
|
'.word': lambda p0: f'{p0:04x}',
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class AsTransformer(lark.Transformer):
|
|
|
|
def __init__(self):
|
|
|
|
self.addr = 0
|
|
|
|
|
|
|
|
def statement(self, s):
|
|
|
|
opcode, *params = s
|
|
|
|
addr = self.addr
|
|
|
|
self.addr += 2
|
|
|
|
return ('OP', addr, opcode, *params)
|
|
|
|
|
|
|
|
def opcode(self, o):
|
|
|
|
(o, ) = o
|
|
|
|
return str(o)
|
|
|
|
|
|
|
|
def reg_offset(self, r):
|
|
|
|
if len(r) < 2:
|
|
|
|
return r[0], 0
|
|
|
|
return tuple(r[:])
|
|
|
|
|
|
|
|
LABEL = str
|
|
|
|
HEX_LITTERAL = lambda _, x: int(x[2:], 16)
|
|
|
|
|
|
|
|
def label(self, l):
|
|
|
|
(l,) = l
|
|
|
|
return ('LBL', l, self.addr)
|
|
|
|
|
|
|
|
def label_ref(self, l):
|
|
|
|
(l,) = l
|
|
|
|
return l
|
|
|
|
|
|
|
|
def raw_word(self, w):
|
|
|
|
(w,) = w
|
|
|
|
addr = self.addr
|
|
|
|
self.addr += 2
|
|
|
|
return ('OP', addr, '.word', w)
|
|
|
|
|
|
|
|
def register(self, r):
|
|
|
|
(r,) = r
|
|
|
|
if r == 'pc':
|
|
|
|
return 14
|
|
|
|
if r == 'lr':
|
|
|
|
return 13
|
|
|
|
if r == 'sp':
|
|
|
|
return 12
|
|
|
|
return int(r[1:])
|
|
|
|
|
|
|
|
def immediate(self, i):
|
|
|
|
(i,) = i
|
|
|
|
return int(i)
|
|
|
|
|
|
|
|
def symbol_def(self, s):
|
|
|
|
(sym,) = s
|
|
|
|
return ('SYM', s[0])
|
|
|
|
|
|
|
|
start = list
|
|
|
|
|
|
|
|
|
|
|
|
def generate_ops(ops, labels, relocs):
|
|
|
|
def filter_label(params, pc):
|
|
|
|
for p in params:
|
|
|
|
if isinstance(p, str): # label ref
|
|
|
|
if len(params) == 1: # branch
|
|
|
|
yield 14 # pc
|
2021-04-18 06:02:58 +00:00
|
|
|
yield labels[p] - pc - 4
|
2021-02-17 21:20:30 +00:00
|
|
|
else: # set, allow relocs here
|
|
|
|
relocs.append((pc, p))
|
|
|
|
yield 0xff
|
|
|
|
elif isinstance(p, tuple): # reg offset
|
|
|
|
yield p[0]
|
|
|
|
yield p[1]
|
|
|
|
else:
|
|
|
|
yield p
|
|
|
|
|
|
|
|
for op in ops:
|
|
|
|
addr = op[0]
|
|
|
|
fmt = opcodes[op[1]]
|
|
|
|
params = filter_label(op[2:], addr)
|
|
|
|
yield fmt(*params)
|
|
|
|
|
|
|
|
|
|
|
|
def larkparse(f, debug=False):
|
|
|
|
with open(GRAMMAR_FILE) as g:
|
|
|
|
asparser = lark.Lark(g.read())
|
|
|
|
tree = asparser.parse(f.read())
|
|
|
|
if debug:
|
|
|
|
print(tree.pretty())
|
|
|
|
lines = AsTransformer().transform(tree)
|
|
|
|
labels = {l[1]: l[2] for l in lines if l[0] == 'LBL'}
|
|
|
|
ops = [l[1:] for l in lines if l[0] == 'OP']
|
|
|
|
syms = [(l[1], labels[l[1]]) for l in lines if l[0] == 'SYM']
|
|
|
|
relocs = []
|
|
|
|
return [int(x, 16) for x in generate_ops(ops, labels, relocs)], syms, relocs
|
|
|
|
|
|
|
|
|
|
|
|
# any record: >BHs, type, length, data
|
|
|
|
# header: >cccc, "pol0"
|
|
|
|
# text record: >BHs, name, text_len, text
|
|
|
|
# reloc record: >pHip, txt_rec_name, offset, target
|
|
|
|
|
|
|
|
def write_obj(fout, ops, syms, relocs):
|
|
|
|
obj = obj_pb2.ObjFile(header=obj_pb2.Header(magic='pol0'))
|
|
|
|
|
|
|
|
ends = [s[1] for s in syms[1:]] + [2 * len(ops)]
|
|
|
|
data = b''.join(struct.pack('>H', op) for op in ops)
|
|
|
|
|
|
|
|
for (name, begin), end in zip(syms, ends):
|
|
|
|
section = obj_pb2.Section(name=name, text=data[begin:end])
|
|
|
|
obj.sections.append(section)
|
|
|
|
for offs, target in relocs:
|
|
|
|
if offs >= begin and offs < end:
|
|
|
|
section_off = offs - begin
|
|
|
|
reloc = obj_pb2.Reloc(section=name, offset=section_off, target=target)
|
|
|
|
obj.relocs.append(reloc)
|
|
|
|
|
|
|
|
fout.write(obj.SerializeToString())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def parse_args():
|
|
|
|
parser = argparse.ArgumentParser(description='Assemble.')
|
|
|
|
group = parser.add_mutually_exclusive_group()
|
|
|
|
group.add_argument('--vhdl', action='store_true',
|
|
|
|
help='output VHDL code')
|
|
|
|
group.add_argument('--coe', action='store_true',
|
|
|
|
help='output a Vivado COE file')
|
|
|
|
parser.add_argument('--debug', action='store_true',
|
|
|
|
help='print the AST')
|
|
|
|
parser.add_argument('--input', '-c', type=argparse.FileType('r'),
|
|
|
|
default=sys.stdin, help='input file (default: stdin)')
|
|
|
|
parser.add_argument('--output', '-o', type=argparse.FileType('wb'),
|
|
|
|
default=sys.stdout.buffer, help='output file')
|
|
|
|
return parser.parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
args = parse_args()
|
|
|
|
|
|
|
|
ops, syms, relocs = larkparse(args.input, debug=args.debug)
|
|
|
|
|
|
|
|
if args.vhdl:
|
|
|
|
print(',\n'.join(f'x"{x:04x}"' for x in ops))
|
|
|
|
for addr, target in relocs:
|
|
|
|
print(f'-- reloc: {target} @{addr}')
|
|
|
|
elif args.coe:
|
|
|
|
assert not relocs, "relocs not supported with coe output"
|
|
|
|
print('memory_initialization_radix=16;')
|
|
|
|
print('memory_initialization_vector=')
|
|
|
|
code = ',\n'.join(f'{x:04x}' for x in ops)
|
|
|
|
print(f'{code};')
|
|
|
|
else:
|
|
|
|
write_obj(args.output, ops, syms, relocs)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|