diff --git a/maple_bus_commands.h b/maple_bus_commands.h new file mode 100644 index 0000000..fce4be1 --- /dev/null +++ b/maple_bus_commands.h @@ -0,0 +1,210 @@ +#include +#include + +#include "type.h" + +namespace device_request { + constexpr uint32_t command_code = 0x1; +} + +namespace all_status_request { + constexpr uint32_t command_code = 0x2; +} + +namespace device_reset { + constexpr uint32_t command_code = 0x3; +} + +namespace device_kill { + constexpr uint32_t command_code = 0x4; +} + +namespace device_status { + constexpr uint32_t command_code = 0x5; + + struct data_fields { + uint8_t device_id[16]; + uint8_t destination_code[1]; + uint8_t connection_direction[1]; + uint8_t product_name[30]; + uint8_t license[60]; + uint8_t low_consumption_standby_current[2]; + uint8_t maximum_current_consumption[2]; + }; + + static_assert((sizeof (struct data_fields)) == 112); +} + +namespace device_all_status { + constexpr uint32_t command_code = 0x6; + + template + struct data_fields { + uint8_t device_id[16]; + uint8_t destination_code[1]; + uint8_t connection_direction[1]; + uint8_t product_name[30]; + uint8_t license[60]; + uint8_t low_consumption_standby_current[2]; + uint8_t maximum_current_consumption[2]; + uint8_t free_device_status[N]; + }; + + static_assert((sizeof (struct data_fields<0>)) == 112); +} + +namespace device_reply { + constexpr uint32_t command_code = 0x7; +} + +namespace data_transfer { + constexpr uint32_t command_code = 0x8; + + template + struct data_fields { + uint8_t data[N]; + }; + + static_assert((sizeof (struct data_fields<0>)) == 0); +} + +namespace get_condition { + constexpr uint32_t command_code = 0x9; + + struct data_fields { + uint8_t function_type[4]; + }; + + static_assert((sizeof (struct data_fields)) == 4); +} + +namespace get_media_info { + constexpr uint32_t command_code = 0xa; + + struct data_fields { + uint8_t function_type[4]; + uint8_t pt[4]; + }; + + static_assert((sizeof (struct data_fields)) == 8); +} + +namespace block_read { + constexpr uint32_t command_code = 0xb; + + struct data_fields { + uint8_t function_type[4]; + uint8_t pt[1]; + uint8_t phase[1]; + uint8_t block_no[2]; + }; + + static_assert((sizeof (struct data_fields)) == 8); +} + +namespace block_write { + constexpr uint32_t command_code = 0xc; + + template + struct data_fields { + uint8_t function_type[4]; + uint8_t pt[1]; + uint8_t phase[1]; + uint8_t block_no[2]; + uint8_t written_data[N]; + }; + + static_assert((sizeof (struct data_fields<0>)) == 8); +} + +namespace get_last_error { + constexpr uint32_t command_code = 0xd; + + struct data_fields { + uint8_t function_type[4]; + uint8_t pt[1]; + uint8_t phase[1]; + uint8_t block_no[2]; + }; + + static_assert((sizeof (struct data_fields)) == 8); +} + +namespace set_condition { + constexpr uint32_t command_code = 0xe; + + template + struct data_fields { + uint8_t function_type[4]; + uint8_t write_in_data[N]; + }; + + static_assert((sizeof (struct data_fields<0>)) == 4); +} + +namespace ft4_control { + constexpr uint32_t command_code = 0xf; + + template + struct data_fields { + uint8_t function_type[4]; + uint8_t ft4_data[N]; + }; + + static_assert((sizeof (struct data_fields<0>)) == 4); +} + +namespace ar_control { + constexpr uint32_t command_code = 0x10; + + template + struct data_fields { + uint8_t function_type[4]; + uint8_t data[N]; + }; + + static_assert((sizeof (struct data_fields<0>)) == 4); +} + +namespace function_type_unknown { + constexpr uint32_t command_code = 0xfe; +} + +namespace command_unknown { + constexpr uint32_t command_code = 0xfd; +} + +namespace transmit_again { + constexpr uint32_t command_code = 0xfc; +} + +namespace file_error { + constexpr uint32_t command_code = 0xfb; + + struct data_fields { + uint8_t function_error_code[4]; + }; + + static_assert((sizeof (struct data_fields)) == 4); +} + +namespace lcd_error { + constexpr uint32_t command_code = 0xfa; + + struct data_fields { + uint8_t function_error_code[4]; + }; + + static_assert((sizeof (struct data_fields)) == 4); +} + +namespace ar_error { + constexpr uint32_t command_code = 0xf9; + + struct data_fields { + uint8_t function_error_code[4]; + }; + + static_assert((sizeof (struct data_fields)) == 4); +} + diff --git a/regs/gen/maple_commands.py b/regs/gen/maple_commands.py new file mode 100644 index 0000000..d67595a --- /dev/null +++ b/regs/gen/maple_commands.py @@ -0,0 +1,160 @@ +from dataclasses import dataclass +from typing import Union +import sys + +from sh7091 import read_input +from sh7091 import headers +from generate import renderer + +@dataclass +class CommandNamespace: + name: str + issuing_right: set[str] + command_code: int + data_size: int + +def command_namespace(namespace: CommandNamespace, + data_fields: list[tuple[str, tuple[int, str]]]): + yield f"namespace {namespace.name} {{" + yield f"constexpr uint32_t command_code = {hex(namespace.command_code)};" + + if namespace.data_size == (0, None): + assert data_fields == [] + # do nothing + else: + length, variable = namespace.data_size + + yield "" + + if variable is not None: + assert variable.lower() == "n" + yield "template " + + yield "struct data_fields {" + + for field_name, field_size in data_fields: + const, var = field_size + if var is None: + yield f"uint8_t {field_name}[{const}];" + elif const == 0: + assert var == "n" + yield f"uint8_t {field_name}[{var.upper()}];" + else: + yield f"uint8_t {field_name}[{const} + {var.upper()}];" + + yield "};" + + yield "" + + if variable is not None: + assert variable == "n" + yield f"static_assert((sizeof (struct data_fields<0>)) == {length});" + else: + yield f"static_assert((sizeof (struct data_fields)) == {length});" + + yield "}" + yield "" + +def parse_data_size(data_size, base, multiple) -> tuple[int, str]: + def parse_term(s): + try: + return int(s, base) * multiple + except ValueError: + div = s.split("/") + if len(div) == 1: + # this must be a variable + assert multiple == 1, s + a, = div + return a + elif len(div) == 2: + # this must be a variable divided by a number + a, b = div + b = int(b, 10) + assert b == multiple + return a + else: + assert False, div + + _terms = data_size.split("+") + terms = tuple(parse_term(term) for term in _terms) + if len(terms) == 1: + term, = terms + if type(term) == str: + return (0, term) + elif type(term) == int: + return (term, None) + else: + assert False, (_terms, terms) + elif len(terms) == 2: + assert type(terms[0]) == int + assert type(terms[1]) == str + return terms + else: + assert False, (_terms, terms) + +def new_aggregator(): + last_name = None + namespace = None + data_fields = [] + all_names = set() + + def process_row(row): + nonlocal last_name + nonlocal namespace + nonlocal data_fields + + if row["name"] == "": + assert all(v == "" for v in row.values()), row + return + + if row["name"] != last_name: + if namespace is not None: + yield namespace, data_fields + else: + assert data_fields == [] + assert row["name"] != "" + last_name = row["name"] + issuing_right = set(row["issuing_right"].split(", ")) + assert all(s in {"host", "peripheral"} for s in issuing_right), row + assert issuing_right != set(), issuing_right + data_size = parse_data_size(row["data_size"], 16, 4) + + namespace = CommandNamespace( + name = row["name"], + issuing_right = issuing_right, + command_code = int(row["command_code"], 16), + data_size = data_size, + ) + data_fields = [] + # fall through + + assert last_name is None or row["name"] == last_name, (row["name"], last_name) + if row["data_field"] != "": + assert row["data_field_size"] != "" + data_fields.append(( + row["data_field"], + parse_data_size(row["data_field_size"], 10, 1) + )) + + def terminate(): + nonlocal namespace + nonlocal data_fields + if namespace is not None: + yield namespace, data_fields + + def process(rows): + for row in rows: + yield from process_row(row) + yield from terminate() + + return process + +input_file = sys.argv[1] +rows = read_input(input_file) +process = new_aggregator() + +render, out = renderer() +render(headers()) +for namespace, data_fields in process(rows): + render(command_namespace(namespace, data_fields)) +sys.stdout.write(out.getvalue()) diff --git a/regs/maple_bus_commands.csv b/regs/maple_bus_commands.csv new file mode 100644 index 0000000..e3700de --- /dev/null +++ b/regs/maple_bus_commands.csv @@ -0,0 +1,71 @@ +"name","issuing_right","command_code","data_size","data_field","data_field_size" +"device_request","host","0x01","0x00",, +,,,,, +"all_status_request","host","0x02","0x00",, +,,,,, +"device_reset","host","0x03","0x00",, +,,,,, +"device_kill","host","0x04","0x00",, +,,,,, +"device_status","peripheral","0x05","0x1c","device_id",16 +"device_status",,,,"destination_code",1 +"device_status",,,,"connection_direction",1 +"device_status",,,,"product_name",30 +"device_status",,,,"license",60 +"device_status",,,,"low_consumption_standby_current",2 +"device_status",,,,"maximum_current_consumption",2 +,,,,, +"device_all_status","peripheral","0x06","0x1c+n/4","device_id",16 +"device_all_status",,,,"destination_code",1 +"device_all_status",,,,"connection_direction",1 +"device_all_status",,,,"product_name",30 +"device_all_status",,,,"license",60 +"device_all_status",,,,"low_consumption_standby_current",2 +"device_all_status",,,,"maximum_current_consumption",2 +"device_all_status",,,,"free_device_status","n" +,,,,, +"device_reply","peripheral","0x07","0x00",, +,,,,, +"data_transfer","peripheral","0x08","n/4","data","n" +,,,,, +"get_condition","host","0x09","0x01","function_type",4 +,,,,, +"get_media_info","host","0x0a","0x02","function_type",4 +"get_media_info",,,,"pt",4 +,,,,, +"block_read","host","0x0b","0x02","function_type",4 +"block_read",,,,"pt",1 +"block_read",,,,"phase",1 +"block_read",,,,"block_no",2 +,,,,, +"block_write","host","0x0c","0x02+n/4","function_type",4 +"block_write",,,,"pt",1 +"block_write",,,,"phase",1 +"block_write",,,,"block_no",2 +"block_write",,,,"written_data","n" +,,,,, +"get_last_error","host","0x0d","0x02","function_type",4 +"get_last_error",,,,"pt",1 +"get_last_error",,,,"phase",1 +"get_last_error",,,,"block_no",2 +,,,,, +"set_condition","host","0x0e","0x01+n/4","function_type",4 +"set_condition",,,,"write_in_data","n" +,,,,, +"ft4_control","host","0x0f","0x01+n/4","function_type",4 +"ft4_control",,,,"ft4_data","n" +,,,,, +"ar_control","host","0x10","0x01+n/4","function_type",4 +"ar_control",,,,"data","n" +,,,,, +"function_type_unknown","peripheral","0xfe","0x00",, +,,,,, +"command_unknown","peripheral","0xfd","0x00",, +,,,,, +"transmit_again","host, peripheral","0xfc","0x00",, +,,,,, +"file_error","peripheral","0xfb","0x01","function_error_code",4 +,,,,, +"lcd_error","peripheral","0xfa","0x01","function_error_code",4 +,,,,, +"ar_error","peripheral","0xf9","0x01","function_error_code",4 diff --git a/regs/maple_bus_commands.ods b/regs/maple_bus_commands.ods new file mode 100644 index 0000000..abd09bd Binary files /dev/null and b/regs/maple_bus_commands.ods differ diff --git a/regs/maple_bus_ft0.ods b/regs/maple_bus_ft0.ods new file mode 100644 index 0000000..55a72af Binary files /dev/null and b/regs/maple_bus_ft0.ods differ diff --git a/regs/maple_bus_ft6.ods b/regs/maple_bus_ft6.ods new file mode 100644 index 0000000..c4c51d5 Binary files /dev/null and b/regs/maple_bus_ft6.ods differ diff --git a/regs/maple_bus_ft6_key_mapping.ods b/regs/maple_bus_ft6_key_mapping.ods new file mode 100644 index 0000000..453bee3 Binary files /dev/null and b/regs/maple_bus_ft6_key_mapping.ods differ