diff --git a/dreamcast2/maple/maple.hpp b/dreamcast2/maple/maple.hpp new file mode 100644 index 0000000..fe5291e --- /dev/null +++ b/dreamcast2/maple/maple.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace maple { + + struct protocol_header { + uint8_t command_code; + uint8_t destination_ap; + uint8_t source_ap; + uint8_t data_size; + }; + + template + struct host_command { + uint32_t host_instruction; // interpreted by the Maple DMA controller + uint32_t receive_data_address; // interpreted by the Maple DMA controller + protocol_header header; + T data; + }; + + static_assert((sizeof (host_command)) == 12); + static_assert((sizeof (host_command[4])) == 48); + + constexpr inline uint32_t align(uint32_t mem) + { + return (mem + 31) & ~31; + } + + template + struct host_response { + protocol_header header; + T data; + uint8_t _pad[align((sizeof (protocol_header)) + (sizeof (T))) - ((sizeof (protocol_header)) + (sizeof (T)))]; + }; + + static_assert((sizeof (host_response)) == 32); +} diff --git a/dreamcast2/maple/maple_bits.hpp b/dreamcast2/maple/maple_bits.hpp new file mode 100644 index 0000000..115a7bd --- /dev/null +++ b/dreamcast2/maple/maple_bits.hpp @@ -0,0 +1,90 @@ +#pragma once + +#include + +namespace maple { + namespace mdstar { + constexpr inline uint32_t table_address(uint32_t num) { return (num & 0xfffffe0) << 0; } + } + + namespace mdtsel { + namespace trigger_select { + constexpr uint32_t software_initiation = 0 << 0; + constexpr uint32_t v_blank_initiation = 1 << 0; + + constexpr uint32_t bit_mask = 0x1 << 0; + } + } + + namespace mden { + namespace dma_enable { + constexpr uint32_t abort = 0 << 0; + constexpr uint32_t enable = 1 << 0; + constexpr inline uint32_t status(uint32_t reg) { return (reg >> 0) & 0x1; } + + constexpr uint32_t bit_mask = 0x1 << 0; + } + } + + namespace mdst { + namespace start_status { + constexpr inline uint32_t status(uint32_t reg) { return (reg >> 0) & 0x1; } + constexpr uint32_t start = 1 << 0; + + constexpr uint32_t bit_mask = 0x1 << 0; + } + } + + namespace msys { + constexpr inline uint32_t time_out_counter(uint32_t num) { return (num & 0xffff) << 16; } + constexpr uint32_t single_hard_trigger = 1 << 12; + + namespace sending_rate { + constexpr uint32_t _2M = 0 << 8; + constexpr uint32_t _1M = 1 << 8; + + constexpr uint32_t bit_mask = 0x3 << 8; + } + + constexpr inline uint32_t delay_time(uint32_t num) { return (num & 0xf) << 0; } + } + + namespace mst { + constexpr inline uint32_t move_status(uint32_t reg) { return (reg >> 31) & 0x1; } + constexpr inline uint32_t internal_frame_monitor(uint32_t reg) { return (reg >> 24) & 0x7; } + constexpr inline uint32_t internal_state_monitor(uint32_t reg) { return (reg >> 16) & 0x3f; } + constexpr inline uint32_t line_monitor(uint32_t reg) { return (reg >> 0) & 0xff; } + } + + namespace mshtcl { + constexpr uint32_t hard_dma_clear = 1 << 0; + } + + namespace mdapro { + constexpr uint32_t security_code = 0x6155 << 16; + constexpr inline uint32_t top_address(uint32_t num) { return (num & 0x7f) << 8; } + constexpr inline uint32_t bottom_address(uint32_t num) { return (num & 0x7f) << 0; } + } + + namespace mmsel { + namespace msb_selection { + constexpr uint32_t bit7 = 0 << 0; + constexpr uint32_t bit31 = 1 << 0; + + constexpr uint32_t bit_mask = 0x1 << 0; + } + } + + namespace mtxdad { + constexpr inline uint32_t txd_address_counter(uint32_t reg) { return (reg >> 0) & 0x1fffffff; } + } + + namespace mrxdad { + constexpr inline uint32_t rxd_address_counter(uint32_t reg) { return (reg >> 0) & 0x1fffffff; } + } + + namespace mrxdbd { + constexpr inline uint32_t rxd_base_address(uint32_t reg) { return (reg >> 0) & 0x1fffffff; } + } + +} diff --git a/dreamcast2/maple/maple_bus_bits.hpp b/dreamcast2/maple/maple_bus_bits.hpp new file mode 100644 index 0000000..ae0b5a0 --- /dev/null +++ b/dreamcast2/maple/maple_bus_bits.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include + +namespace maple { + namespace host_instruction { + constexpr uint32_t end_flag = 1 << 31; + + namespace port_select { + constexpr uint32_t a = 0 << 16; + constexpr uint32_t b = 1 << 16; + constexpr uint32_t c = 2 << 16; + constexpr uint32_t d = 3 << 16; + + constexpr uint32_t bit_mask = 0x3 << 16; + } + + namespace pattern { + constexpr uint32_t normal = 0b000 << 8; + constexpr uint32_t light_gun_mode = 0b010 << 8; + constexpr uint32_t reset = 0b011 << 8; + constexpr uint32_t return_from_light_gun_mode = 0b100 << 8; + constexpr uint32_t nop = 0b111 << 8; + + constexpr uint32_t bit_mask = 0x7 << 8; + } + + constexpr inline uint32_t transfer_length(uint32_t num) { return (num & 0xff) << 0; } + } + + namespace receive_data_storage_address { + constexpr inline uint32_t address(uint32_t num) { return (num & 0x1fffffff) << 0; } + } + + namespace ap { + namespace port_select { + constexpr uint32_t a = 0b00 << 6; + constexpr uint32_t b = 0b01 << 6; + constexpr uint32_t c = 0b10 << 6; + constexpr uint32_t d = 0b11 << 6; + + constexpr uint32_t bit_mask = 0x3 << 6; + } + + namespace de { + constexpr uint32_t device = 1 << 5; + constexpr uint32_t expansion_device = 0 << 5; + constexpr uint32_t port = 0 << 5; + + constexpr uint32_t bit_mask = 0x1 << 5; + } + + namespace lm_bus { + constexpr uint32_t _4 = 0b10000 << 0; + constexpr uint32_t _3 = 0b01000 << 0; + constexpr uint32_t _2 = 0b00100 << 0; + constexpr uint32_t _1 = 0b00010 << 0; + constexpr uint32_t _0 = 0b00001 << 0; + + constexpr uint32_t bit_mask = 0x1f << 0; + } + } + + namespace function_type { + constexpr uint32_t camera = 1 << 11; + constexpr uint32_t exchange_media = 1 << 10; + constexpr uint32_t pointing = 1 << 9; + constexpr uint32_t vibration = 1 << 8; + constexpr uint32_t light_gun = 1 << 7; + constexpr uint32_t keyboard = 1 << 6; + constexpr uint32_t ar_gun = 1 << 5; + constexpr uint32_t audio_input = 1 << 4; + constexpr uint32_t timer = 1 << 3; + constexpr uint32_t bw_lcd = 1 << 2; + constexpr uint32_t storage = 1 << 1; + constexpr uint32_t controller = 1 << 0; + } + +} diff --git a/dreamcast2/maple/maple_bus_commands.hpp b/dreamcast2/maple/maple_bus_commands.hpp new file mode 100644 index 0000000..408ea02 --- /dev/null +++ b/dreamcast2/maple/maple_bus_commands.hpp @@ -0,0 +1,260 @@ +#pragma once + +#include + +namespace maple { + + struct device_id { + uint32_t ft; + uint32_t fd[3]; + }; + static_assert((sizeof (struct device_id)) == 16); + + struct device_request { + static constexpr uint32_t command_code = 0x1; + + struct data_fields { + uint8_t _empty[0]; + }; + }; + static_assert((sizeof (struct device_request::data_fields)) == 0); + + struct all_status_request { + static constexpr uint32_t command_code = 0x2; + + struct data_fields { + uint8_t _empty[0]; + }; + }; + static_assert((sizeof (struct all_status_request::data_fields)) == 0); + + struct device_reset { + static constexpr uint32_t command_code = 0x3; + + struct data_fields { + uint8_t _empty[0]; + }; + }; + static_assert((sizeof (struct device_reset::data_fields)) == 0); + + struct device_kill { + static constexpr uint32_t command_code = 0x4; + + struct data_fields { + uint8_t _empty[0]; + }; + }; + static_assert((sizeof (struct device_kill::data_fields)) == 0); + + struct device_status { + static constexpr uint32_t command_code = 0x5; + + struct data_fields { + struct device_id device_id; + uint8_t destination_code; + uint8_t connection_direction; + uint8_t product_name[30]; + uint8_t license[60]; + uint16_t low_consumption_standby_current; + uint16_t maximum_current_consumption; + }; + + }; + static_assert((sizeof (struct device_status::data_fields)) == 112); + + template + struct device_all_status { + static constexpr uint32_t command_code = 0x6; + + struct data_fields { + struct device_id device_id; + uint8_t destination_code; + uint8_t connection_direction; + uint8_t product_name[30]; + uint8_t license[60]; + uint16_t low_consumption_standby_current; + uint16_t maximum_current_consumption; + T free_device_status; + }; + + }; + static_assert((sizeof (struct device_all_status::data_fields)) == 112); + + struct device_reply { + static constexpr uint32_t command_code = 0x7; + + struct data_fields { + uint8_t _empty[0]; + }; + }; + static_assert((sizeof (struct device_reply::data_fields)) == 0); + + template + struct data_transfer { + static constexpr uint32_t command_code = 0x8; + + struct data_fields { + uint32_t function_type; + T data; + }; + + }; + static_assert((sizeof (struct data_transfer::data_fields)) == 4); + + struct get_condition { + static constexpr uint32_t command_code = 0x9; + + struct data_fields { + uint32_t function_type; + }; + + }; + static_assert((sizeof (struct get_condition::data_fields)) == 4); + + struct get_media_info { + static constexpr uint32_t command_code = 0xa; + + struct data_fields { + uint32_t function_type; + uint32_t pt; + }; + + }; + static_assert((sizeof (struct get_media_info::data_fields)) == 8); + + struct block_read { + static constexpr uint32_t command_code = 0xb; + + struct data_fields { + uint32_t function_type; + uint8_t pt; + uint8_t phase; + uint16_t block_number; + }; + + }; + static_assert((sizeof (struct block_read::data_fields)) == 8); + + template + struct block_write { + static constexpr uint32_t command_code = 0xc; + + struct data_fields { + uint32_t function_type; + uint8_t pt; + uint8_t phase; + uint16_t block_number; + T written_data; + }; + + }; + static_assert((sizeof (struct block_write::data_fields)) == 8); + + struct get_last_error { + static constexpr uint32_t command_code = 0xd; + + struct data_fields { + uint32_t function_type; + uint8_t pt; + uint8_t phase; + uint16_t block_number; + }; + + }; + static_assert((sizeof (struct get_last_error::data_fields)) == 8); + + template + struct set_condition { + static constexpr uint32_t command_code = 0xe; + + struct data_fields { + uint32_t function_type; + T write_in_data; + }; + + }; + static_assert((sizeof (struct set_condition::data_fields)) == 4); + + template + struct ft4_control { + static constexpr uint32_t command_code = 0xf; + + struct data_fields { + uint32_t function_type; + T ft4_data; + }; + + }; + static_assert((sizeof (struct ft4_control::data_fields)) == 4); + + template + struct ar_control { + static constexpr uint32_t command_code = 0x10; + + struct data_fields { + uint32_t function_type; + T data; + }; + + }; + static_assert((sizeof (struct ar_control::data_fields)) == 4); + + struct function_type_unknown { + static constexpr uint32_t command_code = 0xfe; + + struct data_fields { + uint8_t _empty[0]; + }; + }; + static_assert((sizeof (struct function_type_unknown::data_fields)) == 0); + + struct command_unknown { + static constexpr uint32_t command_code = 0xfd; + + struct data_fields { + uint8_t _empty[0]; + }; + }; + static_assert((sizeof (struct command_unknown::data_fields)) == 0); + + struct transmit_again { + static constexpr uint32_t command_code = 0xfc; + + struct data_fields { + uint8_t _empty[0]; + }; + }; + static_assert((sizeof (struct transmit_again::data_fields)) == 0); + + struct file_error { + static constexpr uint32_t command_code = 0xfb; + + struct data_fields { + uint32_t function_error_code; + }; + + }; + static_assert((sizeof (struct file_error::data_fields)) == 4); + + struct lcd_error { + static constexpr uint32_t command_code = 0xfa; + + struct data_fields { + uint32_t function_error_code; + }; + + }; + static_assert((sizeof (struct lcd_error::data_fields)) == 4); + + struct ar_error { + static constexpr uint32_t command_code = 0xf9; + + struct data_fields { + uint32_t function_error_code; + }; + + }; + static_assert((sizeof (struct ar_error::data_fields)) == 4); + + +} diff --git a/dreamcast2/maple/maple_bus_ft0.hpp b/dreamcast2/maple/maple_bus_ft0.hpp new file mode 100644 index 0000000..2be5ffe --- /dev/null +++ b/dreamcast2/maple/maple_bus_ft0.hpp @@ -0,0 +1,67 @@ +#pragma once + +#include + +namespace maple::ft0 { + namespace data_transfer { + namespace digital_button { + constexpr uint32_t ra() { return 0b1 << 7; } + constexpr uint32_t ra(uint32_t reg) { return (reg >> 7) & 0b1; } + + constexpr uint32_t la() { return 0b1 << 6; } + constexpr uint32_t la(uint32_t reg) { return (reg >> 6) & 0b1; } + + constexpr uint32_t da() { return 0b1 << 5; } + constexpr uint32_t da(uint32_t reg) { return (reg >> 5) & 0b1; } + + constexpr uint32_t ua() { return 0b1 << 4; } + constexpr uint32_t ua(uint32_t reg) { return (reg >> 4) & 0b1; } + + constexpr uint32_t start() { return 0b1 << 3; } + constexpr uint32_t start(uint32_t reg) { return (reg >> 3) & 0b1; } + + constexpr uint32_t a() { return 0b1 << 2; } + constexpr uint32_t a(uint32_t reg) { return (reg >> 2) & 0b1; } + + constexpr uint32_t b() { return 0b1 << 1; } + constexpr uint32_t b(uint32_t reg) { return (reg >> 1) & 0b1; } + + constexpr uint32_t c() { return 0b1 << 0; } + constexpr uint32_t c(uint32_t reg) { return (reg >> 0) & 0b1; } + + constexpr uint32_t rb() { return 0b1 << 15; } + constexpr uint32_t rb(uint32_t reg) { return (reg >> 15) & 0b1; } + + constexpr uint32_t lb() { return 0b1 << 14; } + constexpr uint32_t lb(uint32_t reg) { return (reg >> 14) & 0b1; } + + constexpr uint32_t db() { return 0b1 << 13; } + constexpr uint32_t db(uint32_t reg) { return (reg >> 13) & 0b1; } + + constexpr uint32_t ub() { return 0b1 << 12; } + constexpr uint32_t ub(uint32_t reg) { return (reg >> 12) & 0b1; } + + constexpr uint32_t d() { return 0b1 << 11; } + constexpr uint32_t d(uint32_t reg) { return (reg >> 11) & 0b1; } + + constexpr uint32_t x() { return 0b1 << 10; } + constexpr uint32_t x(uint32_t reg) { return (reg >> 10) & 0b1; } + + constexpr uint32_t y() { return 0b1 << 9; } + constexpr uint32_t y(uint32_t reg) { return (reg >> 9) & 0b1; } + + constexpr uint32_t z() { return 0b1 << 8; } + constexpr uint32_t z(uint32_t reg) { return (reg >> 8) & 0b1; } + + } + + struct data_format { + uint16_t digital_button; + uint8_t analog_coordinate_axis[6]; + }; + static_assert((sizeof (struct data_format)) % 4 == 0); + static_assert((sizeof (struct data_format)) == 8); + } + +} + diff --git a/dreamcast2/maple/maple_bus_ft6.hpp b/dreamcast2/maple/maple_bus_ft6.hpp new file mode 100644 index 0000000..4f27a03 --- /dev/null +++ b/dreamcast2/maple/maple_bus_ft6.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include + +namespace maple::ft6 { + namespace data_transfer { + namespace modifier_key { + constexpr uint32_t s2() { return 0b1 << 7; } + constexpr uint32_t s2(uint32_t reg) { return (reg >> 7) & 0b1; } + + constexpr uint32_t right_alt() { return 0b1 << 6; } + constexpr uint32_t right_alt(uint32_t reg) { return (reg >> 6) & 0b1; } + + constexpr uint32_t right_shift() { return 0b1 << 5; } + constexpr uint32_t right_shift(uint32_t reg) { return (reg >> 5) & 0b1; } + + constexpr uint32_t right_control() { return 0b1 << 4; } + constexpr uint32_t right_control(uint32_t reg) { return (reg >> 4) & 0b1; } + + constexpr uint32_t left_gui() { return 0b1 << 3; } + constexpr uint32_t left_gui(uint32_t reg) { return (reg >> 3) & 0b1; } + + constexpr uint32_t left_alt() { return 0b1 << 2; } + constexpr uint32_t left_alt(uint32_t reg) { return (reg >> 2) & 0b1; } + + constexpr uint32_t left_shift() { return 0b1 << 1; } + constexpr uint32_t left_shift(uint32_t reg) { return (reg >> 1) & 0b1; } + + constexpr uint32_t left_control() { return 0b1 << 0; } + constexpr uint32_t left_control(uint32_t reg) { return (reg >> 0) & 0b1; } + + } + + namespace led_state { + constexpr uint32_t shift() { return 0b1 << 7; } + constexpr uint32_t shift(uint32_t reg) { return (reg >> 7) & 0b1; } + + constexpr uint32_t power() { return 0b1 << 6; } + constexpr uint32_t power(uint32_t reg) { return (reg >> 6) & 0b1; } + + constexpr uint32_t kana() { return 0b1 << 5; } + constexpr uint32_t kana(uint32_t reg) { return (reg >> 5) & 0b1; } + + constexpr uint32_t reserved0() { return 0b1 << 4; } + constexpr uint32_t reserved0(uint32_t reg) { return (reg >> 4) & 0b1; } + + constexpr uint32_t reserved1() { return 0b1 << 3; } + constexpr uint32_t reserved1(uint32_t reg) { return (reg >> 3) & 0b1; } + + constexpr uint32_t scroll_lock() { return 0b1 << 2; } + constexpr uint32_t scroll_lock(uint32_t reg) { return (reg >> 2) & 0b1; } + + constexpr uint32_t caps_lock() { return 0b1 << 1; } + constexpr uint32_t caps_lock(uint32_t reg) { return (reg >> 1) & 0b1; } + + constexpr uint32_t num_lock() { return 0b1 << 0; } + constexpr uint32_t num_lock(uint32_t reg) { return (reg >> 0) & 0b1; } + + } + + struct data_format { + uint8_t modifier_key; + uint8_t led_state; + uint8_t scan_code_array[6]; + }; + static_assert((sizeof (struct data_format)) % 4 == 0); + static_assert((sizeof (struct data_format)) == 8); + } + + namespace set_condition { + struct data_format { + uint8_t led_setting; + uint8_t w1_reserved; + uint8_t w2_reserved; + uint8_t w3_reserved; + }; + static_assert((sizeof (struct data_format)) % 4 == 0); + static_assert((sizeof (struct data_format)) == 4); + } + +} + diff --git a/dreamcast2/maple/maple_bus_ft6_scan_code.hpp b/dreamcast2/maple/maple_bus_ft6_scan_code.hpp new file mode 100644 index 0000000..2105976 --- /dev/null +++ b/dreamcast2/maple/maple_bus_ft6_scan_code.hpp @@ -0,0 +1,151 @@ +#pragma once + +#include + +namespace maple::ft6::scan_code { + constexpr uint32_t no_operation = 0x0; + constexpr uint32_t rollover_error = 0x1; + constexpr uint32_t post_fail = 0x2; + constexpr uint32_t undefined_error = 0x3; + constexpr uint32_t a_A = 0x4; + constexpr uint32_t b_B = 0x5; + constexpr uint32_t c_C = 0x6; + constexpr uint32_t d_D = 0x7; + constexpr uint32_t e_E = 0x8; + constexpr uint32_t f_F = 0x9; + constexpr uint32_t g_G = 0xa; + constexpr uint32_t h_H = 0xb; + constexpr uint32_t i_I = 0xc; + constexpr uint32_t j_J = 0xd; + constexpr uint32_t k_K = 0xe; + constexpr uint32_t l_L = 0xf; + constexpr uint32_t m_M = 0x10; + constexpr uint32_t n_N = 0x11; + constexpr uint32_t o_O = 0x12; + constexpr uint32_t p_P = 0x13; + constexpr uint32_t q_Q = 0x14; + constexpr uint32_t r_R = 0x15; + constexpr uint32_t s_S = 0x16; + constexpr uint32_t t_T = 0x17; + constexpr uint32_t u_U = 0x18; + constexpr uint32_t v_V = 0x19; + constexpr uint32_t w_W = 0x1a; + constexpr uint32_t x_X = 0x1b; + constexpr uint32_t y_Y = 0x1c; + constexpr uint32_t z_Z = 0x1d; + constexpr uint32_t _1_exclam = 0x1e; + constexpr uint32_t _2_at = 0x1f; + constexpr uint32_t _3_numbersign = 0x20; + constexpr uint32_t _4_dollar = 0x21; + constexpr uint32_t _5_percent = 0x22; + constexpr uint32_t _6_asciicircum = 0x23; + constexpr uint32_t _7_ampersand = 0x24; + constexpr uint32_t _8_asterisk = 0x25; + constexpr uint32_t _9_parenleft = 0x26; + constexpr uint32_t _0_parenright = 0x27; + constexpr uint32_t _return = 0x28; + constexpr uint32_t esc = 0x29; + constexpr uint32_t backspace = 0x2a; + constexpr uint32_t tab = 0x2b; + constexpr uint32_t spacebar = 0x2c; + constexpr uint32_t minus_underscore = 0x2d; + constexpr uint32_t equal_plus = 0x2e; + constexpr uint32_t bracketleft_braceleft = 0x2f; + constexpr uint32_t bracketright_braceright = 0x30; + constexpr uint32_t backslash_bar = 0x31; + constexpr uint32_t iso_numbersign_tilde = 0x32; + constexpr uint32_t semicolon_colon = 0x33; + constexpr uint32_t apostrophe_quotedbl = 0x34; + constexpr uint32_t grave_asciitilde = 0x35; + constexpr uint32_t comma_less = 0x36; + constexpr uint32_t period_greater = 0x37; + constexpr uint32_t slash_question = 0x38; + constexpr uint32_t caps_lock = 0x39; + constexpr uint32_t F1 = 0x3a; + constexpr uint32_t F2 = 0x3b; + constexpr uint32_t F3 = 0x3c; + constexpr uint32_t F4 = 0x3d; + constexpr uint32_t F5 = 0x3e; + constexpr uint32_t F6 = 0x3f; + constexpr uint32_t F7 = 0x40; + constexpr uint32_t F8 = 0x41; + constexpr uint32_t F9 = 0x42; + constexpr uint32_t F10 = 0x43; + constexpr uint32_t F11 = 0x44; + constexpr uint32_t F12 = 0x45; + constexpr uint32_t print_screen = 0x46; + constexpr uint32_t scroll_lock = 0x47; + constexpr uint32_t pause = 0x48; + constexpr uint32_t insert = 0x49; + constexpr uint32_t home = 0x4a; + constexpr uint32_t page_up = 0x4b; + constexpr uint32_t _delete = 0x4c; + constexpr uint32_t end = 0x4d; + constexpr uint32_t page_down = 0x4e; + constexpr uint32_t right_arrow = 0x4f; + constexpr uint32_t left_arrow = 0x50; + constexpr uint32_t down_arrow = 0x51; + constexpr uint32_t up_arrow = 0x52; + constexpr uint32_t last_printable = 0x38; + + static const uint8_t code_point[last_printable + 1][2] = { + [scan_code::no_operation] = { 0, 0 }, + [scan_code::rollover_error] = { 0, 0 }, + [scan_code::post_fail] = { 0, 0 }, + [scan_code::undefined_error] = { 0, 0 }, + [scan_code::a_A] = { 'a', 'A' }, + [scan_code::b_B] = { 'b', 'B' }, + [scan_code::c_C] = { 'c', 'C' }, + [scan_code::d_D] = { 'd', 'D' }, + [scan_code::e_E] = { 'e', 'E' }, + [scan_code::f_F] = { 'f', 'F' }, + [scan_code::g_G] = { 'g', 'G' }, + [scan_code::h_H] = { 'h', 'H' }, + [scan_code::i_I] = { 'i', 'I' }, + [scan_code::j_J] = { 'j', 'J' }, + [scan_code::k_K] = { 'k', 'K' }, + [scan_code::l_L] = { 'l', 'L' }, + [scan_code::m_M] = { 'm', 'M' }, + [scan_code::n_N] = { 'n', 'N' }, + [scan_code::o_O] = { 'o', 'O' }, + [scan_code::p_P] = { 'p', 'P' }, + [scan_code::q_Q] = { 'q', 'Q' }, + [scan_code::r_R] = { 'r', 'R' }, + [scan_code::s_S] = { 's', 'S' }, + [scan_code::t_T] = { 't', 'T' }, + [scan_code::u_U] = { 'u', 'U' }, + [scan_code::v_V] = { 'v', 'V' }, + [scan_code::w_W] = { 'w', 'W' }, + [scan_code::x_X] = { 'x', 'X' }, + [scan_code::y_Y] = { 'y', 'Y' }, + [scan_code::z_Z] = { 'z', 'Z' }, + [scan_code::_1_exclam] = { '1', '!' }, + [scan_code::_2_at] = { '2', '@' }, + [scan_code::_3_numbersign] = { '3', '#' }, + [scan_code::_4_dollar] = { '4', '$' }, + [scan_code::_5_percent] = { '5', '%' }, + [scan_code::_6_asciicircum] = { '6', '^' }, + [scan_code::_7_ampersand] = { '7', '&' }, + [scan_code::_8_asterisk] = { '8', '*' }, + [scan_code::_9_parenleft] = { '9', '(' }, + [scan_code::_0_parenright] = { '0', ')' }, + [scan_code::_return] = { 0, 0 }, + [scan_code::esc] = { 0, 0 }, + [scan_code::backspace] = { 0, 0 }, + [scan_code::tab] = { 0, 0 }, + [scan_code::spacebar] = { 0, 0 }, + [scan_code::minus_underscore] = { '-', '_' }, + [scan_code::equal_plus] = { '=', '+' }, + [scan_code::bracketleft_braceleft] = { '[', '{' }, + [scan_code::bracketright_braceright] = { ']', '}' }, + [scan_code::backslash_bar] = { '\\', '|' }, + [scan_code::iso_numbersign_tilde] = { '#', '~' }, + [scan_code::semicolon_colon] = { ';', ':' }, + [scan_code::apostrophe_quotedbl] = { '\'', '"' }, + [scan_code::grave_asciitilde] = { '\'', '~' }, + [scan_code::comma_less] = { ',', '<' }, + [scan_code::period_greater] = { '.', '>' }, + [scan_code::slash_question] = { '/', '?' }, + }; +} + diff --git a/dreamcast2/regs.mk b/dreamcast2/regs.mk index ac70528..f9163db 100644 --- a/dreamcast2/regs.mk +++ b/dreamcast2/regs.mk @@ -43,3 +43,23 @@ systembus/systembus.hpp: regs/systembus/systembus.csv regs/render_block_regs.py systembus/systembus_bits.hpp: regs/systembus/systembus_bits.csv regs/render_bits.py python regs/render_bits.py $< systembus > $@ + +# MAPLE + +maple/maple_bits.hpp: regs/maple/maple_bits.csv regs/render_bits.py + python regs/render_bits.py $< maple > $@ + +maple/maple_bus_bits.hpp: regs/maple/maple_bus_bits.csv regs/render_bits.py + python regs/render_bits.py $< maple > $@ + +maple/maple_bus_commands.hpp: regs/maple/maple_bus_commands.csv regs/render_maple_bus_commands.py + python regs/render_maple_bus_commands.py $< > $@ + +maple/maple_bus_ft0.hpp: regs/maple/maple_bus_ft0.csv regs/render_maple_data_format.py + python regs/render_maple_data_format.py $< > $@ + +maple/maple_bus_ft6.hpp: regs/maple/maple_bus_ft6.csv regs/render_maple_data_format.py + python regs/render_maple_data_format.py $< > $@ + +maple/maple_bus_ft6_scan_code.hpp: regs/maple/maple_bus_ft6_scan_code.csv regs/render_maple_scan_code.py + python regs/render_maple_scan_code.py $< > $@ diff --git a/dreamcast2/regs/maple/bus_bits.ods b/dreamcast2/regs/maple/bus_bits.ods new file mode 100644 index 0000000..c856a5e Binary files /dev/null and b/dreamcast2/regs/maple/bus_bits.ods differ diff --git a/dreamcast2/regs/maple/maple_bits.ods b/dreamcast2/regs/maple/maple_bits.ods new file mode 100644 index 0000000..abbd202 Binary files /dev/null and b/dreamcast2/regs/maple/maple_bits.ods differ diff --git a/dreamcast2/regs/maple/maple_bus_commands.ods b/dreamcast2/regs/maple/maple_bus_commands.ods new file mode 100644 index 0000000..ed58d40 Binary files /dev/null and b/dreamcast2/regs/maple/maple_bus_commands.ods differ diff --git a/dreamcast2/regs/maple/maple_bus_ft0.ods b/dreamcast2/regs/maple/maple_bus_ft0.ods new file mode 100644 index 0000000..36a8ebf Binary files /dev/null and b/dreamcast2/regs/maple/maple_bus_ft0.ods differ diff --git a/dreamcast2/regs/maple/maple_bus_ft6.ods b/dreamcast2/regs/maple/maple_bus_ft6.ods new file mode 100644 index 0000000..a847423 Binary files /dev/null and b/dreamcast2/regs/maple/maple_bus_ft6.ods differ diff --git a/dreamcast2/regs/maple/maple_bus_ft6_scan_code.ods b/dreamcast2/regs/maple/maple_bus_ft6_scan_code.ods new file mode 100644 index 0000000..f5f6d3e Binary files /dev/null and b/dreamcast2/regs/maple/maple_bus_ft6_scan_code.ods differ diff --git a/dreamcast2/regs/render_maple_bus_commands.py b/dreamcast2/regs/render_maple_bus_commands.py new file mode 100644 index 0000000..f88b909 --- /dev/null +++ b/dreamcast2/regs/render_maple_bus_commands.py @@ -0,0 +1,184 @@ +from dataclasses import dataclass +from typing import Union +import sys + +from csv_input import read_input +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]]]): + length, variable = namespace.data_size + if variable is not None: + assert variable.lower() == "n" + yield "template " + + yield f"struct {namespace.name} {{" + yield f"static constexpr uint32_t command_code = {hex(namespace.command_code)};" + yield "" + + if namespace.data_size == (0, None): + assert data_fields == [] + yield "struct data_fields {" + yield "uint8_t _empty[0];" + yield "};" + else: + yield "struct data_fields {" + + for field_name, field_size in data_fields: + const, var = field_size + if var is None: + if const in {1, 2, 4}: + bits = const * 8 + yield f"uint{bits}_t {field_name};" + elif field_name == "device_id": + yield f"struct device_id {field_name};" + else: + yield f"uint8_t {field_name}[{const}];" + elif const == 0: + assert var == "n" + yield f"T {field_name};" + else: + yield f"uint8_t {field_name}[{const} + {var.upper()}];" + + yield "};" + + yield "" + + yield "};" + if variable is not None: + assert variable == "n" + yield f"static_assert((sizeof (struct {namespace.name}::data_fields)) == {length});" + else: + yield f"static_assert((sizeof (struct {namespace.name}::data_fields)) == {length});" + + 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 + +def headers(): + yield "#pragma once" + yield "" + yield "#include " + yield "" + +def namespace(): + yield "namespace maple {" + yield "" + yield "struct device_id {" + yield "uint32_t ft;" + yield "uint32_t fd[3];" + yield "};" + yield "static_assert((sizeof (struct device_id)) == 16);" + yield "" + for namespace, data_fields in process(rows): + yield from command_namespace(namespace, data_fields) + yield "" + yield "}" + +input_file = sys.argv[1] +rows = read_input(input_file) +process = new_aggregator() + +render, out = renderer() +render(headers()) +render(namespace()) +sys.stdout.write(out.getvalue()) diff --git a/dreamcast2/regs/render_maple_data_format.py b/dreamcast2/regs/render_maple_data_format.py new file mode 100644 index 0000000..796c067 --- /dev/null +++ b/dreamcast2/regs/render_maple_data_format.py @@ -0,0 +1,163 @@ +import sys +import re +from dataclasses import dataclass +from collections import defaultdict + +from csv_input import read_input_headerless +from generate import renderer + +@dataclass +class Bit: + name: str + length: int + position: int + +@dataclass +class Field: + name: str + bits: list[str] + +@dataclass +class Format: + name: str + fields: list[Field] + field_order: list[str] + size: int + +def parse_bits(bits: list[str]): + bit_order = [7, 6, 5, 4, 3, 2, 1, 0] + by_name = defaultdict(list) + for bit_ix, bit in zip(bit_order, bits): + if bit == '': + continue + by_name[bit].append(bit_ix) + for name, indicies in by_name.items(): + yield Bit(name=name, + length=len(indicies), + position=min(indicies), + ) + +def parse_format_name(name): + if '<' in name: + head, middle, tail = re.split('[<>]', name) + assert tail == "", name + return head, middle + else: + return name, None + +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]] + assert len(bits) == 8, bits + fields[field_name].append(Field(field_name, + list(parse_bits(bits)))) + _, variable = parse_format_name(field_name) + if not variable: + 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 + +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 + for bit in field.bits: + name = bit.name.lower() + pos = bit_offset + bit.position + mask = bin(2 ** bit.length - 1) + yield f"constexpr uint32_t {name}() {{ return {mask} << {pos}; }}" + yield f"constexpr uint32_t {name}(uint32_t reg) {{ return (reg >> {pos}) & {mask}; }}" + yield "" + yield "}" + yield "" + + yield f"struct data_format {{" + + for _field_name in format.field_order: + field_name, variable = parse_format_name(_field_name) + subfields = format.fields[_field_name] + if variable is not None: + assert len(subfields) == 1 + yield f"uint8_t {field_name}[{variable}];" + elif len(subfields) == 1: + field, = subfields + yield f"uint8_t {field_name};" + elif len(subfields) == 2: + yield f"uint16_t {field_name};" + elif len(subfields) in {3, 6}: + yield f"uint8_t {field_name}[{len(subfields)}];" + elif len(subfields) == 16: + # bleh: hacky + yield f"uint16_t {field_name}[8];" + elif len(subfields) == 4: + yield f"uint32_t {field_name};" + else: + assert False, (len(subfields), field_name) + + yield "};" + yield f"static_assert((sizeof (struct data_format)) % 4 == 0);" + yield f"static_assert((sizeof (struct data_format)) == {format.size});" + yield "}" + +def render_formats(name, formats): + yield "#pragma once" + yield "" + yield "#include " + yield "" + yield f"namespace maple::{name} {{" + for format in formats: + yield from render_format(format) + yield "" + + yield "}" + +if __name__ == "__main__": + rows = read_input_headerless(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()) diff --git a/dreamcast2/regs/render_maple_scan_code.py b/dreamcast2/regs/render_maple_scan_code.py new file mode 100644 index 0000000..2e4cda9 --- /dev/null +++ b/dreamcast2/regs/render_maple_scan_code.py @@ -0,0 +1,65 @@ +import sys +import string + +from csv_input import read_input +from generate import renderer + +def render_row(row): + usage = row['usage'] + code = int(row['code'], 16) + yield f"constexpr uint32_t {usage} = {hex(code)};" + +def render_rows(rows): + for row in rows: + yield from render_row(row) + +def code_point(s): + if not s.strip(): + return '0' + elif len(s) == 1: + assert s in string.printable + if s == '\\': + return "'\\\\'" + elif s.strip() == "'": + return "'\\''" + else: + return f"'{s}'" + else: + assert False, s + +last_printable = 0x38 + +def render_normal_shift(row): + usage = row['usage'] + normal = code_point(row['normal']) + shift = code_point(row['shift']) + yield f"[scan_code::{usage}] = {{ {normal}, {shift} }}," + +def render_scancode_code_point(rows): + yield f"constexpr uint32_t last_printable = {hex(last_printable)};" + yield "" + yield f"static const uint8_t code_point[last_printable + 1][2] = {{" + for i, row in enumerate(rows): + yield from render_normal_shift(row) + if i == last_printable: + break + yield "};" + +def render_all(rows): + yield "namespace maple::ft6::scan_code {" + yield from render_rows(rows) + yield from render_scancode_code_point(rows) + yield "}" + +def header(): + yield "#pragma once" + yield "" + yield "#include " + yield "" + +if __name__ == "__main__": + rows = read_input(sys.argv[1]) + render, out = renderer() + render(header()) + render(render_all(rows)) + print(out.getvalue())