registers: add graphics_engine_bits for DISPCNT

This commit is contained in:
Zack Buhman 2024-08-31 01:58:07 -05:00
parent 977213d712
commit 810c6975b1
9 changed files with 301 additions and 1 deletions

3
.gitignore vendored
View File

@ -4,4 +4,5 @@ __pycache__
*.d
*.elf
*.bin
*.nds
*.nds
.~lock*

2
registers/Makefile Normal file
View File

@ -0,0 +1,2 @@
%.csv: %.ods
libreoffice --headless --convert-to csv:"Text - txt - csv (StarCalc)":44,34,76,,,,true --outdir $(dir $@) $<

27
registers/csv_input.py Normal file
View File

@ -0,0 +1,27 @@
import csv
def as_dict(header, row0):
row = [s.strip() for s in row0]
return dict(zip(header, row))
def read_input(filename):
with open(filename) as f:
reader = csv.reader(f, delimiter=",", quotechar='"')
header, *rows = reader
rows = [
as_dict(header, row)
for row in rows
if "".join(map(str, row)).strip()
]
return rows
def read_input_headerless(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

56
registers/format_bits.py Normal file
View File

@ -0,0 +1,56 @@
import sys
from csv_input import read_input
from parse_bits import aggregate_registers
from parse_bits import aggregate_all_enums
from parse_bits import Enum, Bit
from generate import renderer
def mask_from_bits(bits):
h, l = max(bits), min(bits)
mask = 2 ** ((h - l) + 1) - 1
return mask
def render_bit(prefix, bit):
macro_name = f"{prefix}__{bit.name}"
if bit.value is None and bit.mask is None:
# value read macro
mask_value = mask_from_bits(bit.bits)
yield f"#define {macro_name}(v) (((v) >> {min(bit.bits)}) & {hex(mask_value)})"
elif bit.mask is None:
# constant value macro
yield f"#define {macro_name} ({hex(bit.value)} << {min(bit.bits)})"
elif bit.value is None:
# variable macro
mask_value = mask_from_bits(bit.bits)
assert bit.mask & mask_value == mask_value, (bit.mask, mask_value)
yield f"#define {macro_name}(v) (((v) & {hex(mask_value)}) << {min(bit.bits)})"
else:
assert False, bit
def render_enum(prefix, enum):
enum_prefix = f"{prefix}__{enum.name}"
for bit in enum.defs:
yield from render_bit(enum_prefix, bit)
def render_register(register):
for bit_or_enum in register.defs:
if type(bit_or_enum) is Enum:
yield from render_enum(register.name, bit_or_enum)
elif type(bit_or_enum) is Bit:
yield from render_bit(register.name, bit_or_enum)
else:
assert False, (bit_or_enum, type(bit_or_enum))
def render_registers(registers):
for register in registers:
yield from render_register(register)
if __name__ == "__main__":
d = read_input(sys.argv[1])
aggregated = aggregate_registers(d)
registers = aggregate_all_enums(aggregated)
render, out = renderer()
render(render_registers(registers))
sys.stdout.write(out.getvalue())

35
registers/generate.py Normal file
View File

@ -0,0 +1,35 @@
import io
def should_autonewline(line):
return (
"static_assert" not in line
and "extern" not in line
and (len(line.split()) < 2 or line.split()[1] != '=') # hacky; meh
)
def _render(out, lines):
indent = " "
level = 0
for l in lines:
if l and (l[0] == "}" or l[0] == ")"):
level -= 2
assert level >= 0, out.getvalue()
if len(l) == 0:
out.write("\n")
else:
out.write(indent * level + l + "\n")
if l and (l[-1] == "{" or l[-1] == "("):
level += 2
if level == 0 and l and l[-1] == ";":
if should_autonewline(l):
out.write("\n")
return out
def renderer():
out = io.StringIO()
def render(lines):
return _render(out, lines)
return render, out

View File

@ -1,2 +1,5 @@
python format.py 0x04000000.txt graphics_engine_a > graphics_engine_a.h
python format.py 0x04001000.txt graphics_engine_b > graphics_engine_b.h
make graphics_engine_bits.csv
python format_bits.py graphics_engine_bits.csv > graphics_engine_bits.h

View File

@ -0,0 +1,50 @@
#define DISPCNT__obj_extended_palette (0x1 << 31)
#define DISPCNT__bg_extended_palette (0x1 << 30)
#define DISPCNT__bg_screen_base_offset(v) (((v) & 0x7) << 27)
#define DISPCNT__bg_character_base_offset(v) (((v) & 0x7) << 24)
#define DISPCNT__obj_processing_during_h_blank_period (0x1 << 23)
#define DISPCNT__obj_vram_capacity___128kb (0x0 << 22)
#define DISPCNT__obj_vram_capacity___256kb (0x1 << 22)
#define DISPCNT__character_vram_capacity___32kb (0x0 << 20)
#define DISPCNT__character_vram_capacity___64kb (0x1 << 20)
#define DISPCNT__character_vram_capacity___128kb (0x2 << 20)
#define DISPCNT__character_vram_capacity___256kb (0x3 << 20)
#define DISPCNT__display_vram_block__vram_a (0x0 << 18)
#define DISPCNT__display_vram_block__vram_b (0x1 << 18)
#define DISPCNT__display_vram_block__vram_c (0x2 << 18)
#define DISPCNT__display_vram_block__vram_d (0x3 << 18)
#define DISPCNT__display_mode__display_off (0x0 << 16)
#define DISPCNT__display_mode__graphics_display (0x1 << 16)
#define DISPCNT__display_mode__vram_display (0x2 << 16)
#define DISPCNT__display_mode__main_memory_display (0x3 << 16)
#define DISPCNT__obj_window__disable (0x0 << 15)
#define DISPCNT__obj_window__enable (0x1 << 15)
#define DISPCNT__window_1__disable (0x0 << 14)
#define DISPCNT__window_1__enable (0x1 << 14)
#define DISPCNT__window_0__disable (0x0 << 13)
#define DISPCNT__window_0__enable (0x1 << 13)
#define DISPCNT__obj__disable (0x0 << 12)
#define DISPCNT__obj__enable (0x1 << 12)
#define DISPCNT__bg3__disable (0x0 << 11)
#define DISPCNT__bg3__enable (0x1 << 11)
#define DISPCNT__bg2__disable (0x0 << 10)
#define DISPCNT__bg2__enable (0x1 << 10)
#define DISPCNT__bg1__disable (0x0 << 9)
#define DISPCNT__bg1__enable (0x1 << 9)
#define DISPCNT__bg0__disable (0x0 << 8)
#define DISPCNT__bg0__enable (0x1 << 8)
#define DISPCNT__2d_display_forced_blank (0x1 << 7)
#define DISPCNT__bitmap_obj_mapping_mode__2d_mapping_with_128_horizontal_dots (0x0 << 5)
#define DISPCNT__bitmap_obj_mapping_mode__2d_mapping_with_256_horizontal_dots (0x1 << 5)
#define DISPCNT__bitmap_obj_mapping_mode__1d_mapping (0x2 << 5)
#define DISPCNT__character_obj_mapping_mode__2d_mapping (0x0 << 4)
#define DISPCNT__character_obj_mapping_mode__1d_mapping (0x1 << 4)
#define DISPCNT__2d_3d_display_selection_for_bg0__display_2d_graphics (0x0 << 3)
#define DISPCNT__2d_3d_display_selection_for_bg0__display_3d_graphics (0x1 << 3)
#define DISPCNT__bg_mode__text0_text1_text2_text3 (0x0 << 0)
#define DISPCNT__bg_mode__text0_text1_text2_affine3 (0x1 << 0)
#define DISPCNT__bg_mode__text0_text1_affine2_affine3 (0x2 << 0)
#define DISPCNT__bg_mode__text0_text1_text2_extended3 (0x3 << 0)
#define DISPCNT__bg_mode__text0_text1_affine2_extended3 (0x4 << 0)
#define DISPCNT__bg_mode__text0_text1_extended2_extended3 (0x5 << 0)
#define DISPCNT__bg_mode__3d_large_screen_256_color_bitmap (0x6 << 0)

Binary file not shown.

126
registers/parse_bits.py Normal file
View File

@ -0,0 +1,126 @@
from collections import defaultdict
from dataclasses import dataclass
from typing import Union
def aggregate_registers(d):
aggregated = defaultdict(list)
for row in d:
#assert row["register_name"] != ""
aggregated[row["register_name"]].append(row)
return dict(aggregated)
def parse_bit_number(s):
assert '-' not in s
return int(s, 10)
def parse_bit_set(s, split_char):
assert len(list(c for c in s if c == split_char)) == 1
left, right = map(parse_bit_number, s.split(split_char, maxsplit=1))
assert left > right, (left, right)
return left, right
def parse_bit_range(s):
if '-' in s:
left, right = parse_bit_set(s, '-')
return set(range(right, left+1))
elif ',' in s:
left, right = parse_bit_set(s, ',')
return set([right, left])
else:
num = parse_bit_number(s)
return set([num])
def parse_bit_range(s):
if '-' in s:
left, right = parse_bit_set(s, '-')
return set(range(right, left+1))
elif ',' in s:
left, right = parse_bit_set(s, ',')
return set([right, left])
else:
num = parse_bit_number(s)
return set([num])
def parse_value(value):
return eval(value)
@dataclass
class Bit:
name: str
bits: set[int]
mask: Union[None, int]
value: Union[None, int]
@dataclass
class Enum:
name: str
defs: list[dict]
@dataclass
class Register:
block: Union[None, str]
name: str
defs: list[Union[dict, Enum]]
def parse_row(row):
return Bit(
name=row['bit_name'],
bits=parse_bit_range(row['bits']),
mask=parse_value(row['mask']) if row['mask'].strip() else None,
value=parse_value(row['value']) if row['value'].strip() else None,
)
def aggregate_enums(aggregated_rows):
non_enum = []
enum_aggregated = defaultdict(list)
all_bits = set()
enum_bits = dict()
def assert_unique_ordered(bits, row):
nonlocal all_bits
assert all(bit not in all_bits for bit in bits), (bits, row)
assert max(all_bits, default=32) > max(bits), (all_bits, bits)
all_bits |= bits
for row in aggregated_rows:
bit = parse_row(row)
assert row["bit_name"] != "", row
if row["enum_name"] == "":
assert_unique_ordered(bit.bits, row)
non_enum.append(bit)
else:
if row["enum_name"] not in enum_bits:
assert_unique_ordered(bit.bits, row)
non_enum.append(row["enum_name"])
else:
assert enum_bits[row["enum_name"]] == bit.bits, row
enum_bits[row["enum_name"]] = bit.bits
enum_aggregated[row["enum_name"]].append(bit)
return non_enum, dict(enum_aggregated)
def aggregate_all_enums(aggregated):
out = []
for register_name, rows in aggregated.items():
non_enum, enum_aggregated = aggregate_enums(rows)
def resolve(row_or_string):
if type(row_or_string) == str:
return Enum(row_or_string,
enum_aggregated[row_or_string])
elif type(row_or_string) == Bit:
return row_or_string
else:
assert False, (row_or_string, type(row_or_string))
defs = [resolve(aggregate) for aggregate in non_enum]
if 'block' in rows[0]:
blocks = set(row['block'] for row in rows)
assert len(blocks) == 1, blocks
block_name = next(iter(blocks))
out.append(
Register(block_name, register_name, defs))
else:
out.append(
Register(None, register_name, defs))
return out