import sys from dataclasses import dataclass import enum from generate import renderer def calculate_line_columns(line): columns = [] span_start = 0 span_end = 0 last_c = None for i, c in enumerate(line): if c == ' ': if last_c != ' ': span_start = i span_end = i elif last_c == ' ': assert span_end >= span_start if span_end > span_start: columns.append(span_end) last_c = c print(columns) def debug_columns(lines): for line in lines: calculate_line_columns(line) def columnize(line): _columns = [18, 45, 51, 65, 70, 82] ix = 0 for col in _columns: if ix > len(line): yield "" else: yield line[ix:col+1] ix = col+1 if ix > len(line): yield "" else: yield line[ix:] def columnize_lines(lines): for line in lines: if not line.strip(): continue columns = list(columnize(line)) yield columns @dataclass class RegisterLine: name: str start_address: int end_address: int bits: tuple[int, int] chips: set[str] r_w: set[str] pipelined: bool fifo: bool description: str def parse_address(s): assert s.startswith("0x") s = s.removeprefix("0x") assert s.endswith(")") s = s.removesuffix(")") addr16s, addr10s = s.split("(") addr16 = int(addr16s, 16) addr10 = int(addr10s, 10) assert addr16 == addr10 return addr16 def parse_address_range(s): if " to " in s: start, end = s.split(" to ") return parse_address(start), parse_address(end) else: address = parse_address(s) return address, address def parse_bits(s): if s == "n/a": return 31, 0 elif ":" in s: high, low = s.split(":") return int(high, 10), int(low, 10) else: v = int(s, 10) return v, v def parse_chips(s): valid_chips = {"Chuck", "Bruce*", "Bruce%", "Bruce-1"} for chip in s.split("+"): assert chip in valid_chips, s if chip == "Bruce-1": chip = "Bruce1" yield chip def parse_r_w(s): valid_r_w = {"R", "R/W", "W"} assert s in valid_r_w, s return set(map(str.lower, s.split("/"))) def parse_pipelined_fifo(s): if s == "(n/a) / No": return False, False elif s == "No / Yes": return False, True elif s == "Yes / No": return True, False elif s == "Yes / Yes": return True, True elif s == "Yes / n/a": return True, False elif s == "n/a": return False, False else: assert False, s def parse(lines): next_address = 0 for columns in columnize_lines(lines): name, address_s, *rest = map(str.strip, columns) start_address, end_address = parse_address_range(address_s) assert next_address == start_address next_address = end_address + 4 if name == 'reserved': continue #print(columns) bits_s, chips_s, r_w_s, pipelined_fifo_s, description = rest bits = parse_bits(bits_s) chips = set(parse_chips(chips_s)) r_w = parse_r_w(r_w_s) pipelined, fifo = parse_pipelined_fifo(pipelined_fifo_s) yield RegisterLine( name=name, start_address=start_address, end_address=end_address, bits=bits, chips=chips, r_w=r_w, pipelined=pipelined, fifo=fifo, description=description, ) def format_rw(r_w): if r_w == {"r", "w"}: return "rw" if r_w == {"w"}: return " w" if r_w == {"r"}: return "r " return ','.join(sorted(r_w)) def sanitize(name): return name.replace("/", "_") def format_register_line(rl, max_length): if rl.start_address == rl.end_address: space = ' ' * (max_length - len(rl.name)) return f"reg32 {sanitize(rl.name)};{space} // ({format_rw(rl.r_w)}) {rl.description}" else: length = (rl.end_address - rl.start_address) // 4 + 1 length_s = f"[{length}]" space = ' ' * (max_length - (len(rl.name) + len(length_s))) return f"reg32 {sanitize(rl.name)}{length_s};{space} // ({format_rw(rl.r_w)}) {rl.description}" def render_register_struct(rls, max_length): last_address = -4 reserved_ix = 0 yield "struct voodoo2_reg {" for rl in rls: assert rl.start_address >= last_address + 4 if rl.start_address != last_address + 4: padding = rl.start_address - (last_address + 4) yield f"reg32 _reserved{reserved_ix}[{padding // 4}];" reserved_ix += 1 yield format_register_line(rl, max_length) last_address = rl.end_address yield "};" def render_static_assert(rls, max_length): for rl in rls: if rl.start_address == rl.end_address: space = ' ' * (max_length - len(rl.name)) yield f"static_assert((offsetof (struct voodoo2_reg, {sanitize(rl.name)})){space} == 0x{rl.start_address:03x});" else: space = ' ' * (max_length - (len(rl.name) + 3)) yield f"static_assert((offsetof (struct voodoo2_reg, {sanitize(rl.name)}[0])){space} == 0x{rl.start_address:03x});" def render_all(rls): max_length = max(len(rl.name) for rl in rls) yield "#pragma once" yield "" yield "#include " yield "#include " yield "" yield '#include "reg.h"' yield "" yield from render_register_struct(rls, max_length) yield from render_static_assert(rls, max_length) with open(sys.argv[1], 'r') as f: buf = f.read() lines = buf.split('\n') rls = list(parse(lines)) render, out = renderer() render(render_all(rls)) sys.stdout.write(out.getvalue())