dreamcast/regs/gen/maple_data_format.py
Zack Buhman 25fef821b0 maple: read controller input on real hardware
There were two notable bugs:

- the maple transfer/data sizes were not being set correctly

- align_32byte always realigned the address of `_scene`, and not the
  `mem` parameter as expected. This had the effect of the maple-DMA
  send and receive buffers being the same buffer. On real hardware,
  this causes unpredicable behavior.
2023-12-10 00:44:33 +08:00

125 lines
3.3 KiB
Python

import csv
import sys
from dataclasses import dataclass
from collections import defaultdict
from generate import renderer
@dataclass
class Field:
name: str
bits: list[str]
@dataclass
class Format:
name: str
fields: list[Field]
field_order: list[str]
size: int
def read_input(filename):
with open(filename) as f:
reader = csv.reader(f, delimiter=",", quotechar='"')
rows = [
[s.strip() for s in row]
for row in reader
]
return rows
def parse_data_format(ix, rows):
if ix >= len(rows):
return None
while rows[ix][0] == "":
ix += 1
if ix >= len(rows):
return None
format_name, *header = rows[ix]
ix += 1
assert format_name != ""
assert header == ["7", "6", "5", "4", "3", "2", "1", "0"]
fields = defaultdict(list)
field_order = list()
size = 0
while ix < len(rows) and rows[ix][0] != "":
field_name, *_bits = rows[ix]
ix += 1
excess_bits = [b for b in _bits[8:] if b != ""]
assert excess_bits == []
bits = [b for b in _bits[:8] if b != ""]
assert len(bits) in {0, 8}, bits
fields[field_name].append(Field(field_name, list(bits)))
size += 1
if field_name not in field_order:
field_order.append(field_name)
return ix, Format(format_name, dict(fields), field_order, size)
def parse(rows):
ix = 0
formats = []
while True:
ix_format = parse_data_format(ix, rows)
if ix_format is None:
break
ix, format = ix_format
formats.append(format)
assert len(formats) > 0
return formats
bit_order = [7, 6, 5, 4, 3, 2, 1, 0]
def render_format(format):
yield f"namespace {format.name} {{"
for field_name in format.field_order:
subfields = format.fields[field_name]
if not any(field.bits != [] for field in subfields):
continue
yield f"namespace {field_name} {{"
for ix, field in enumerate(subfields):
bit_offset = 8 * ix
if field.bits != []:
assert len(field.bits) == 8
for byte_ix, bit in zip(bit_order, field.bits):
bit_ix = byte_ix + bit_offset
yield f"constexpr uint32_t {bit.lower()} = 1 << {bit_ix};"
yield "}"
yield ""
yield f"struct data_format {{"
for field_name in format.field_order:
subfields = format.fields[field_name]
if len(subfields) == 1:
field, = subfields
yield f"uint8_t {field_name};"
elif len(subfields) == 2:
yield f"uint16_t {field_name};"
else:
assert False, len(subfields)
yield "};"
assert format.size % 4 == 0, format.size
yield f"static_assert((sizeof (struct data_format)) == {format.size});"
yield "}"
def render_formats(name, formats):
yield f"namespace {name} {{"
for format in formats:
yield from render_format(format)
yield "}"
if __name__ == "__main__":
rows = read_input(sys.argv[1])
name = sys.argv[1].split('.')[0].split('_')[-1]
assert len(name) == 3 or len(name) == 4
formats = parse(rows)
render, out = renderer()
render(render_formats(name, formats))
print(out.getvalue())