From 0187f66243ddbd28efd2e8704fafc026139e1c97 Mon Sep 17 00:00:00 2001 From: rs <> Date: Sat, 4 Oct 2025 00:53:10 -0500 Subject: [PATCH] Update CPU0 assembler * Add support for multi-file builds * Add line tracking for error messages * Add case sensitivity * Add support for negative byte-values --- projects/cpu_0/asm/as.py | 184 +++++++++++++++++++++++---------------- 1 file changed, 110 insertions(+), 74 deletions(-) diff --git a/projects/cpu_0/asm/as.py b/projects/cpu_0/asm/as.py index 833ce74..56b9d5f 100755 --- a/projects/cpu_0/asm/as.py +++ b/projects/cpu_0/asm/as.py @@ -9,12 +9,8 @@ import struct # ------------------------------------------------------------------------------ -def assemble(source: str) -> Tuple[bytearray, dict]: - b = bytearray() - syms = dict() - forwards = list() - tokens = source.split() - instrs = { +class Assembler(object): + INSTRS = { 'nop': b'\x00', '#8': b'\x01', '#32': b'\x02', '@8': b'\x03', '@32': b'\x04', @@ -29,66 +25,95 @@ def assemble(source: str) -> Tuple[bytearray, dict]: 'lsr': b'\x1b', 'asr': b'\x1c', 'shl': b'\x1d', } - comment = False - for token in tokens: - token = token.lower() - - if comment: - if token == ')': - comment = False - continue - - if token == '(': - comment = True - elif token == 'align': - x = len(b) % 4 - if x != 0: - b += b'\x00'*(4-x) - elif token in instrs: - b += instrs[token] - elif '=' in token: - sym, value = token.split('=', maxsplit=1) - value = int(value, 0) - syms[sym] = value - elif token.endswith(':'): - assert token[0] in '_abcdefghijklmnopqrstuvwxyz' - syms[token[:-1]] = len(b) - elif token[0] in '-0123456789': - if token.endswith('i8'): - val = int(token[:-2], 0) - assert 0 <= val <= 255 - b += bytes([val]) - elif token.endswith('i32'): - val = int(token[:-3], 0) - assert -0x80000000 <= val <= 0xffffffff - if val < 0: - b += struct.pack(' bool: + error = False + comment = False + for token, line_no in self.tokenize(source): + if comment: + if token == ')': + comment = False + continue + + if token == '(': + comment = True + elif token == 'align': + x = len(self.flash_section) % 4 + if x != 0: + self.flash_section += b'\xff' * (4 - x) + elif token.lower() in self.INSTRS: + self.flash_section += self.INSTRS[token.lower()] + elif '=' in token: + sym, value = token.split('=', maxsplit=1) + value = int(value, 0) + self.syms[sym] = value + elif token.endswith(':'): + if token[0] not in '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ': + print(f'{filename}:{line_no} - Invalid label name "{token}"') + error = True + self.syms[token[:-1]] = len(self.flash_section) + elif token[0] in '-0123456789': + if token.endswith('i8'): + val = int(token[:-2], 0) + if val < -0x80 or val > 0xff: + print(f'{filename}:{line_no} - I8 value out of range: "{token}"') + if val < 0: + self.flash_section += struct.pack('b', val) + else: + self.flash_section += struct.pack('B', val) + elif token.endswith('i32'): + val = int(token[:-3], 0) + assert -0x80000000 <= val <= 0xffffffff + if val < 0: + self.flash_section += struct.pack(' Optional[Tuple[bytearray, dict]]: + # Align to two-byte words + if len(self.flash_section) % 2 == 1: + self.flash_section += b'\xff' - # Resolve forward references - for offset, symbol in forwards: - if symbol not in syms: - print(f'Symbol "{symbol}" not defined') - assert symbol in syms - b[offset:offset+4] = struct.pack(' int: parser = argparse.ArgumentParser('AS - CPU0 Assembler') parser.add_argument('--outfile', '-o', help='Output filename') parser.add_argument('--mapfile', '-m', help='Symbol map filename') - parser.add_argument('filename', help='Source filename') + parser.add_argument('filename', nargs='+', help='Source filenames') args = parser.parse_args() - with open(args.filename, 'r') as f: - with open(args.outfile, 'wb') as g: - s = f.read() - b, syms = assemble(s) - g.write(b) - if args.mapfile: - if args.mapfile == 'stdout': + assembler = Assembler() + success = True + for source_filename in args.filename: + with open(source_filename, 'r') as f: + print(f'Assembling "{source_filename}"') + source = f.read() + s = assembler.chomp(source_filename, source) + success = success and s + if not success: + return 1 + emitted = assembler.emit() + if emitted is None: + return 1 + binary, syms = emitted + with open(args.outfile, 'wb') as g: + g.write(binary) + if args.mapfile: + if args.mapfile == 'stdout': + for addr, sym in sorted((addr, sym) for sym, addr in syms.items()): + print(f'0x{addr:08x} {sym}') + else: + with open(args.mapfile, 'w') as h: for addr, sym in sorted((addr, sym) for sym, addr in syms.items()): - print(f'0x{addr:08x}: {sym}') - else: - with open(args.mapfile, 'w') as h: - for addr, sym in sorted((addr, sym) for sym, addr in syms.items()): - h.write(f'0x{addr:08x}: {sym}\n') + h.write(f'0x{addr:08x} {sym}\n') return 0 -- 2.43.0