mirror of
https://github.com/buhman/nds.git
synced 2025-06-18 14:35:38 -04:00
registers: add graphics_engine_bits for DISPCNT
This commit is contained in:
parent
977213d712
commit
810c6975b1
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,4 +4,5 @@ __pycache__
|
||||
*.d
|
||||
*.elf
|
||||
*.bin
|
||||
*.nds
|
||||
*.nds
|
||||
.~lock*
|
2
registers/Makefile
Normal file
2
registers/Makefile
Normal 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
27
registers/csv_input.py
Normal 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
56
registers/format_bits.py
Normal 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
35
registers/generate.py
Normal 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
|
@ -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
|
||||
|
50
registers/graphics_engine_bits.h
Normal file
50
registers/graphics_engine_bits.h
Normal 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)
|
BIN
registers/graphics_engine_bits.ods
Normal file
BIN
registers/graphics_engine_bits.ods
Normal file
Binary file not shown.
126
registers/parse_bits.py
Normal file
126
registers/parse_bits.py
Normal 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
|
Loading…
Reference in New Issue
Block a user