218 lines
5.8 KiB
Python
218 lines
5.8 KiB
Python
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 <stddef.h>"
|
|
yield "#include <stdint.h>"
|
|
yield ""
|
|
yield '#include "reg.h"'
|
|
yield ""
|
|
yield from render_register_struct(rls, max_length)
|
|
yield from render_static_assert(rls, max_length)
|
|
yield ""
|
|
yield "typedef struct voodoo2_reg voodoo2_reg;"
|
|
|
|
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())
|