From 98015575353b4626b50ca64af8b2048bce0fe621 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Tue, 21 May 2024 15:05:25 -0500 Subject: [PATCH] maple: slightly refactor maple command initialization This adopts a "writer" concept, vaguely inspired by the ta parameter writer. This might turn out to be not a great idea if the response/offsets for heterogenous commands are too inconvenient to keep track of. This breaks every example that uses maple--only example/maple_controller is updated to use the new interface. --- base.mk | 3 +- common.mk | 2 +- example/clipping.cpp | 2 +- example/clipping2.cpp | 5 +- example/clipping_textured.cpp | 6 +- example/maple_analog.cpp | 2 +- example/maple_controller.cpp | 182 ++++++++++++------- example/maple_device_request.cpp | 2 +- example/maple_vibrator.cpp | 14 +- example/maple_wink.cpp | 8 +- example/modifier_volume_with_two_volumes.cpp | 2 +- example/texture_filtering_maple.cpp | 2 +- maple/maple.cpp | 2 + maple/maple.hpp | 4 +- maple/maple_bus_ft6_key_scan_codes.hpp | 71 +++++++- maple/maple_host_command_writer.hpp | 72 ++++++++ maple/maple_impl.hpp | 81 --------- maple/maple_port.hpp | 25 +++ regs/gen/maple_data_format.py | 2 +- regs/gen/maple_key_scan_codes.py | 49 ++++- regs/maple_bus_ft6_key_scan_codes.csv | 168 ++++++++--------- regs/maple_bus_ft6_key_scan_codes.ods | Bin 17292 -> 18669 bytes sh7091/serial.hpp | 2 + text_editor/keyboard.cpp | 85 +++------ text_editor/text_editor.cpp | 4 +- 25 files changed, 472 insertions(+), 323 deletions(-) create mode 100644 maple/maple_host_command_writer.hpp delete mode 100644 maple/maple_impl.hpp create mode 100644 maple/maple_port.hpp diff --git a/base.mk b/base.mk index 1008f47..82f8536 100644 --- a/base.mk +++ b/base.mk @@ -2,10 +2,11 @@ DEBUG = -g -gdwarf-4 AFLAGS += --fatal-warnings -CFLAGS += -falign-functions=4 -ffunction-sections -fdata-sections -fshort-enums -ffreestanding -nostdlib +CFLAGS += -falign-functions=4 -ffunction-sections -fdata-sections -fshort-enums -ffreestanding -nostdlib -fno-builtin -finline-stringops CFLAGS += -Wall -Werror -Wfatal-errors CFLAGS += -Wno-array-bounds #CFLAGS += -Wno-error=narrowing -Wno-error=unused-variable -Wno-error=array-bounds= +CFLAGS += -Wno-error=maybe-uninitialized CXXFLAGS += -fno-exceptions -fno-non-call-exceptions -fno-rtti -fno-threadsafe-statics diff --git a/common.mk b/common.mk index 696f83f..b5fd50c 100644 --- a/common.mk +++ b/common.mk @@ -2,7 +2,7 @@ MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) DIR := $(dir $(MAKEFILE_PATH)) LIB ?= . -OPT ?= -O2 +OPT ?= -Os GENERATED ?= AARCH = --isa=sh4 --little diff --git a/example/clipping.cpp b/example/clipping.cpp index 8179988..1aa8cfb 100644 --- a/example/clipping.cpp +++ b/example/clipping.cpp @@ -46,7 +46,7 @@ void do_get_condition(uint32_t * command_buf, const uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf, data_fields); - using host_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); maple::dma_start(command_buf, command_size, diff --git a/example/clipping2.cpp b/example/clipping2.cpp index f4b4389..8ef15aa 100644 --- a/example/clipping2.cpp +++ b/example/clipping2.cpp @@ -47,9 +47,8 @@ void do_get_condition(uint32_t * command_buf, .function_type = std::byteswap(function_type::controller) }; - const uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf, - data_fields); - using host_response_type = struct maple::command_response; + const uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf, data_fields); + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); maple::dma_start(command_buf, command_size, receive_buf, maple::sizeof_command(host_response)); diff --git a/example/clipping_textured.cpp b/example/clipping_textured.cpp index 2a62628..1559b4d 100644 --- a/example/clipping_textured.cpp +++ b/example/clipping_textured.cpp @@ -51,14 +51,14 @@ void do_get_condition(uint32_t * command_buf, const uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf, data_fields); - using host_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); maple::dma_start(command_buf, command_size, receive_buf, maple::sizeof_command(host_response)); - using command_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; for (uint8_t port = 0; port < 4; port++) { - auto response = reinterpret_cast(receive_buf); + auto response = reinterpret_cast(receive_buf); auto& bus_data = response[port].bus_data; if (bus_data.command_code != response_type::command_code) { return; diff --git a/example/maple_analog.cpp b/example/maple_analog.cpp index 030e904..98c6701 100644 --- a/example/maple_analog.cpp +++ b/example/maple_analog.cpp @@ -46,7 +46,7 @@ void do_get_condition(uint32_t * command_buf, const uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf, data_fields); - using host_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); maple::dma_start(command_buf, command_size, diff --git a/example/maple_controller.cpp b/example/maple_controller.cpp index 78f277f..68b58df 100644 --- a/example/maple_controller.cpp +++ b/example/maple_controller.cpp @@ -5,114 +5,162 @@ #include "holly/core_bits.hpp" #include "holly/holly.hpp" #include "maple/maple.hpp" -#include "maple/maple_impl.hpp" +#include "maple/maple_port.hpp" #include "maple/maple_bus_bits.hpp" #include "maple/maple_bus_commands.hpp" #include "maple/maple_bus_ft0.hpp" +#include "maple/maple_host_command_writer.hpp" #include "sh7091/serial.hpp" -uint32_t _command_buf[(1024 + 32) / 4]; -uint32_t _receive_buf[(1024 + 32) / 4]; +#include "systembus.hpp" -static uint32_t * command_buf; -static uint32_t * receive_buf; - -void do_get_condition(uint32_t port) +void do_get_condition() { - uint32_t destination_port; - uint32_t destination_ap; + uint32_t send_buf[1024] __attribute__((aligned(32))); + uint32_t recv_buf[1024] __attribute__((aligned(32))); - switch (port) { - case 0: - destination_port = host_instruction::port_select::a; - destination_ap = ap::de::device | ap::port_select::a; - break; - case 1: - destination_port = host_instruction::port_select::b; - destination_ap = ap::de::device | ap::port_select::b; - break; - case 2: - destination_port = host_instruction::port_select::c; - destination_ap = ap::de::device | ap::port_select::c; - break; - case 3: - destination_port = host_instruction::port_select::d; - destination_ap = ap::de::device | ap::port_select::d; - break; - default: - return; - } - - const uint32_t command_size = maple::init_get_condition(command_buf, receive_buf, - destination_port, - destination_ap, - std::byteswap(function_type::controller)); + auto writer = maple::host_command_writer(send_buf, recv_buf); + using command_type = get_condition; using response_type = data_transfer; - using command_response_type = struct maple::command_response; - auto host_response = reinterpret_cast(receive_buf); - maple::dma_start(command_buf, command_size, - receive_buf, maple::sizeof_command(host_response)); + auto [host_command, host_response] + = writer.append_command_all_ports(); + + maple::dma_start(send_buf, writer.send_offset, + recv_buf, writer.recv_offset); + + for (uint8_t port = 0; port < 4; port++) { + auto& bus_data = host_response[port].bus_data; + auto& data_fields = bus_data.data_fields; + + if (bus_data.command_code != response_type::command_code) { + //serial::string("device did not reply to get_condition: "); + //serial::integer(port); + } else if ((data_fields.function_type & std::byteswap(function_type::controller)) != 0) { + bool a = ft0::data_transfer::digital_button::a(data_fields.data.digital_button); + if (a == 0) { + serial::string("port "); + serial::integer(port); + serial::string(" `a` press "); + serial::integer(a); + } + } + } +} + +void do_lm_request(uint8_t port, uint8_t lm) +{ + uint32_t send_buf[1024] __attribute__((aligned(32))); + uint32_t recv_buf[1024] __attribute__((aligned(32))); + + auto writer = maple::host_command_writer(send_buf, recv_buf); + + uint32_t host_port_select = host_instruction_port_select(port); + uint32_t destination_ap = ap_port_select(port) | ap::de::expansion_device | lm; + + using command_type = device_request; + using response_type = device_status; + + auto [host_command, host_response] + = writer.append_command(host_port_select, + destination_ap, + true); // end_flag + + maple::dma_start(send_buf, writer.send_offset, + recv_buf, writer.recv_offset); auto& bus_data = host_response->bus_data; - if (bus_data.command_code != response_type::command_code) { - return; - } auto& data_fields = bus_data.data_fields; - if ((data_fields.function_type & std::byteswap(function_type::controller)) == 0) { - return; + if (bus_data.command_code != device_status::command_code) { + serial::string("lm did not reply: "); + serial::integer(port, ' '); + serial::integer(lm); + } else { + serial::string(" lm: "); + serial::integer(lm); + serial::string(" ft: "); + serial::integer(std::byteswap(data_fields.device_id.ft)); + serial::string(" fd[0]: "); + serial::integer(std::byteswap(data_fields.device_id.fd[0])); + serial::string(" fd[1]: "); + serial::integer(std::byteswap(data_fields.device_id.fd[1])); + serial::string(" fd[2]: "); + serial::integer(std::byteswap(data_fields.device_id.fd[2])); + serial::string(" source_ap.lm_bus: "); + serial::integer(bus_data.source_ap & ap::lm_bus::bit_mask); } +} - bool a = ft0::data_transfer::digital_button::a(data_fields.data.digital_button); - if (a == 0) { - serial::string("port "); - serial::integer(port); - serial::string(" `a` press "); - serial::integer(a); - } +void do_lm_requests(uint8_t port, uint8_t lm) +{ + if (lm & ap::lm_bus::_0) + do_lm_request(port, lm & ap::lm_bus::_0); + if (lm & ap::lm_bus::_1) + do_lm_request(port, lm & ap::lm_bus::_1); + if (lm & ap::lm_bus::_2) + do_lm_request(port, lm & ap::lm_bus::_2); + if (lm & ap::lm_bus::_3) + do_lm_request(port, lm & ap::lm_bus::_3); + if (lm & ap::lm_bus::_4) + do_lm_request(port, lm & ap::lm_bus::_4); } void do_device_request() { + uint32_t send_buf[1024] __attribute__((aligned(32))); + uint32_t recv_buf[1024] __attribute__((aligned(32))); + + auto writer = maple::host_command_writer(send_buf, recv_buf); + using command_type = device_request; using response_type = device_status; - const uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf); - using host_response_type = struct maple::command_response; - auto host_response = reinterpret_cast(receive_buf); + auto [host_command, host_response] + = writer.append_command_all_ports(); - maple::dma_start(command_buf, command_size, - receive_buf, maple::sizeof_command(host_response)); + maple::dma_start(send_buf, writer.send_offset, + recv_buf, writer.recv_offset); for (uint8_t port = 0; port < 4; port++) { auto& bus_data = host_response[port].bus_data; - auto& data_fields = host_response[port].bus_data.data_fields; + auto& data_fields = bus_data.data_fields; if (bus_data.command_code != device_status::command_code) { - // the controller is disconnected + serial::string("port: "); + serial::integer(port); + serial::string(" disconnected\n"); } else { - if ((data_fields.device_id.ft & std::byteswap(function_type::controller)) != 0) { - //serial::string("is controller: "); - //serial::integer(port); - do_get_condition(port); - } + serial::string("port: "); + serial::integer(port); + serial::string(" ft: "); + serial::integer(std::byteswap(data_fields.device_id.ft)); + serial::string(" fd[0]: "); + serial::integer(std::byteswap(data_fields.device_id.fd[0])); + serial::string(" fd[1]: "); + serial::integer(std::byteswap(data_fields.device_id.fd[1])); + serial::string(" fd[2]: "); + serial::integer(std::byteswap(data_fields.device_id.fd[2])); + serial::string(" source_ap.lm_bus: "); + serial::integer(bus_data.source_ap & ap::lm_bus::bit_mask); + + do_lm_requests(port, + bus_data.source_ap & ap::lm_bus::bit_mask); } } } void main() { - command_buf = align_32byte(_command_buf); - command_buf = reinterpret_cast(reinterpret_cast(command_buf) | 0xa000'0000); - receive_buf = align_32byte(_receive_buf); - // flycast needs this in HLE mode, or else it won't start the vcount // counter. video_output::set_mode_vga(); + do_device_request(); + while (1) { while (!spg_status::vsync(holly.SPG_STATUS)); while (spg_status::vsync(holly.SPG_STATUS)); - do_device_request(); + + do_get_condition(); }; } diff --git a/example/maple_device_request.cpp b/example/maple_device_request.cpp index 9781b9c..7857d68 100644 --- a/example/maple_device_request.cpp +++ b/example/maple_device_request.cpp @@ -18,7 +18,7 @@ void main() uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf); - constexpr uint32_t host_response_size = (sizeof (maple::command_response)); + constexpr uint32_t host_response_size = (sizeof (maple::host_response)); maple::dma_start(command_buf, command_size, receive_buf, host_response_size); diff --git a/example/maple_vibrator.cpp b/example/maple_vibrator.cpp index c86f8f9..9ae5fbb 100644 --- a/example/maple_vibrator.cpp +++ b/example/maple_vibrator.cpp @@ -47,16 +47,17 @@ void do_lm_request(uint8_t port, uint8_t lm) maple::init_host_command(command_buf, receive_buf, destination_port, destination_ap, get_media_info::command_code, (sizeof (struct get_media_info::data_fields)), - true); + true); // end_flag - using host_command_type = struct maple::host_command; + using command_type = get_media_info; + using host_command_type = struct maple::host_command; auto host_command = reinterpret_cast(command_buf); auto& fields = host_command->bus_data.data_fields; fields.function_type = std::byteswap(function_type::vibration); fields.pt = std::byteswap(1 << 24); using response_type = data_transfer; - using host_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); serial::string("dma start\n"); @@ -118,7 +119,7 @@ void do_lm_request(uint8_t port, uint8_t lm) fields.write_in_data.freq = 0x27; fields.write_in_data.inc = 0x00; - using host_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); maple::dma_start(command_buf, maple::sizeof_command(host_command), receive_buf, maple::sizeof_command(host_response)); @@ -154,11 +155,11 @@ void do_device_request() { using command_type = device_request; using response_type = device_status; - using host_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); const uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf); maple::dma_start(command_buf, command_size, - receive_buf, maple::sizeof_command(host_response)); + receive_buf, maple::sizeof_command(host_response) * 4); for (uint8_t port = 0; port < 4; port++) { auto& bus_data = host_response[port].bus_data; @@ -193,5 +194,6 @@ void main() while (spg_status::vsync(holly.SPG_STATUS)); } do_device_request(); + break; }; } diff --git a/example/maple_wink.cpp b/example/maple_wink.cpp index 9b9fe29..675641e 100644 --- a/example/maple_wink.cpp +++ b/example/maple_wink.cpp @@ -42,12 +42,12 @@ void main() uint32_t * receive_buf = align_32byte(_receive_buf); const uint32_t command_size = maple::init_block_write(command_buf, receive_buf, - host_instruction::port_select::a, - ap::de::expansion_device | ap::port_select::a | ap::lm_bus::_0, + host_instruction::port_select::b, + ap::de::expansion_device | ap::port_select::b | ap::lm_bus::_0, wink_buf, wink_size); using response_type = device_reply; - using host_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); maple::dma_start(command_buf, command_size, receive_buf, maple::sizeof_command(host_response)); @@ -56,6 +56,4 @@ void main() serial::integer(host_response->bus_data.destination_ap); serial::integer(host_response->bus_data.source_ap); serial::integer(host_response->bus_data.data_size); - - while(1); } diff --git a/example/modifier_volume_with_two_volumes.cpp b/example/modifier_volume_with_two_volumes.cpp index e881bed..d07fd56 100644 --- a/example/modifier_volume_with_two_volumes.cpp +++ b/example/modifier_volume_with_two_volumes.cpp @@ -47,7 +47,7 @@ void do_get_condition(uint32_t * command_buf, const uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf, data_fields); - using host_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); maple::dma_start(command_buf, command_size, diff --git a/example/texture_filtering_maple.cpp b/example/texture_filtering_maple.cpp index 31ac0cf..5af8327 100644 --- a/example/texture_filtering_maple.cpp +++ b/example/texture_filtering_maple.cpp @@ -244,7 +244,7 @@ void do_get_condition(uint32_t * command_buf, const uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf, data_fields); - using host_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); maple::dma_start(command_buf, command_size, diff --git a/maple/maple.cpp b/maple/maple.cpp index 3ee4494..79e72b1 100644 --- a/maple/maple.cpp +++ b/maple/maple.cpp @@ -15,6 +15,7 @@ namespace maple { +/* void init_host_command(uint32_t * command_buf, uint32_t * receive_buf, uint32_t destination_port, uint8_t destination_ap, uint8_t command_code, uint8_t data_size, @@ -104,6 +105,7 @@ uint32_t init_block_write(uint32_t * command_buf, uint32_t * receive_buf, return (reinterpret_cast(&host_command[1]) - reinterpret_cast(&host_command[0])) + data_size; } +*/ static inline void _dma_start(const uint32_t * command_buf) { diff --git a/maple/maple.hpp b/maple/maple.hpp index 2ae140e..eaa5188 100644 --- a/maple/maple.hpp +++ b/maple/maple.hpp @@ -22,7 +22,7 @@ static_assert((sizeof (host_command)) == 12); static_assert((sizeof (host_command[4])) == 48); template -struct command_response { +struct host_response { struct bus_data { uint8_t command_code; uint8_t destination_ap; @@ -32,7 +32,7 @@ struct command_response { } bus_data; uint8_t _pad[align_32byte((sizeof (bus_data))) - (sizeof (bus_data))]; }; -static_assert((sizeof (command_response)) == align_32byte((sizeof (command_response)))); +static_assert((sizeof (host_response)) == align_32byte((sizeof (host_response)))); void init_host_command(uint32_t * buf, uint32_t * receive_buf, uint32_t destination_port, diff --git a/maple/maple_bus_ft6_key_scan_codes.hpp b/maple/maple_bus_ft6_key_scan_codes.hpp index 43f2067..70df769 100644 --- a/maple/maple_bus_ft6_key_scan_codes.hpp +++ b/maple/maple_bus_ft6_key_scan_codes.hpp @@ -1,7 +1,9 @@ +#pragma once + #include namespace ft6 { - namespace scan_codes { + namespace scan_code { constexpr uint32_t no_operation = 0x0; constexpr uint32_t rollover_error = 0x1; constexpr uint32_t post_fail = 0x2; @@ -52,7 +54,7 @@ namespace ft6 { constexpr uint32_t bracketleft_braceleft = 0x2f; constexpr uint32_t bracketright_braceright = 0x30; constexpr uint32_t backslash_bar = 0x31; - constexpr uint32_t _unknown_ = 0x32; + 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; @@ -87,4 +89,69 @@ namespace ft6 { constexpr uint32_t up_arrow = 0x52; } } +namespace ft6 { + namespace scan_code { + constexpr uint32_t last_printable = 0x38; + + 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/maple/maple_host_command_writer.hpp b/maple/maple_host_command_writer.hpp new file mode 100644 index 0000000..d45fa98 --- /dev/null +++ b/maple/maple_host_command_writer.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +#include "maple/maple.hpp" +#include "maple/maple_bus_commands.hpp" +#include "maple/maple_bus_bits.hpp" + +namespace maple { + +struct host_command_writer { + uint32_t * const send_buf; + uint32_t * const recv_buf; + uint32_t send_offset; + uint32_t recv_offset; + + constexpr host_command_writer(uint32_t * const send_buf, + uint32_t * const recv_buf) + : send_buf(send_buf), recv_buf(recv_buf), send_offset(0), recv_offset(0) + { } + + template + constexpr inline std::tuple *, + maple::host_response *> + append_command(uint32_t host_port_select, + uint32_t destination_ap, + bool end_flag) + { + using command_type = maple::host_command; + using response_type = maple::host_response; + constexpr uint32_t data_size = (sizeof (typename C::data_fields)) + data_fields_trailing; + + static_assert((sizeof (command_type)) % 4 == 0); + static_assert((sizeof (response_type)) % 4 == 0); + static_assert(data_size % 4 == 0); + + auto host_command = reinterpret_cast(&send_buf[send_offset / 4]); + auto host_response = reinterpret_cast(&recv_buf[recv_offset / 4]); + + host_command->host_instruction = (end_flag ? host_instruction::end_flag : 0) + | (host_port_select & host_instruction::port_select::bit_mask) + | host_instruction::transfer_length(data_size / 4); + + host_command->receive_data_storage_address = receive_data_storage_address::address(reinterpret_cast(host_response)); + + host_command->bus_data.command_code = C::command_code; + host_command->bus_data.destination_ap = destination_ap; + + host_command->bus_data.source_ap = destination_ap & ap::port_select::bit_mask; + host_command->bus_data.data_size = data_size / 4; + + send_offset += (sizeof (command_type)) + data_fields_trailing; + recv_offset += (sizeof (response_type)); + + return {host_command, host_response}; + } + + template + constexpr inline std::tuple *, + maple::host_response *> + append_command_all_ports() + { + auto ret = append_command(host_instruction::port_select::a, ap::de::device | ap::port_select::a, false); + append_command(host_instruction::port_select::b, ap::de::device | ap::port_select::b, false); + append_command(host_instruction::port_select::c, ap::de::device | ap::port_select::c, false); + append_command(host_instruction::port_select::d, ap::de::device | ap::port_select::d, true); + return ret; + } +}; + +} diff --git a/maple/maple_impl.hpp b/maple/maple_impl.hpp deleted file mode 100644 index 5349d0e..0000000 --- a/maple/maple_impl.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#include - -#include "maple/maple.hpp" -#include "maple/maple_bus_commands.hpp" -#include "maple/maple_bus_bits.hpp" - -namespace maple { - -template -uint32_t init_host_command_all_ports(uint32_t * command_buf, uint32_t * receive_buf, - const typename C::data_fields& data_fields) -{ - using command_type = maple::host_command; - using response_type = maple::command_response; - - auto host_command = reinterpret_cast(command_buf); - auto response_command = reinterpret_cast(receive_buf); - - init_host_command((uint32_t*)&host_command[0], (uint32_t*)&response_command[0], - host_instruction::port_select::a, // destination_port - ap::de::device | ap::port_select::a, C::command_code, (sizeof (typename C::data_fields)), - false); // end_flag - host_command[0].bus_data.data_fields = data_fields; - - init_host_command((uint32_t*)&host_command[1], (uint32_t*)&response_command[1], - host_instruction::port_select::b, // destination_port - ap::de::device | ap::port_select::b, C::command_code, (sizeof (typename C::data_fields)), - false); // end_flag - host_command[1].bus_data.data_fields = data_fields; - - init_host_command((uint32_t*)&host_command[2], (uint32_t*)&response_command[2], - host_instruction::port_select::c, // destination_port - ap::de::device | ap::port_select::c, C::command_code, (sizeof (typename C::data_fields)), - false); // end_flag - host_command[2].bus_data.data_fields = data_fields; - - init_host_command((uint32_t*)&host_command[3], (uint32_t*)&response_command[3], - host_instruction::port_select::d, // destination_port - ap::de::device | ap::port_select::d, C::command_code, (sizeof (typename C::data_fields)), - true); // end_flag - host_command[3].bus_data.data_fields = data_fields; - - return reinterpret_cast(&host_command[4]) - reinterpret_cast(&host_command[0]); -} - -template -uint32_t init_host_command_all_ports(uint32_t * command_buf, uint32_t * receive_buf) -{ - using command_type = maple::host_command; - using response_type = maple::command_response; - //static_assert((sizeof (command_type)) == 12); - - auto host_command = reinterpret_cast(command_buf); - auto response_command = reinterpret_cast(receive_buf); - - init_host_command((uint32_t*)&host_command[0], (uint32_t*)&response_command[0], - host_instruction::port_select::a, // destination_port - ap::de::device | ap::port_select::a, C::command_code, (sizeof (typename C::data_fields)), - false); // end_flag - - init_host_command((uint32_t*)&host_command[1], (uint32_t*)&response_command[1], - host_instruction::port_select::b, // destination_port - ap::de::device | ap::port_select::b, C::command_code, (sizeof (typename C::data_fields)), - false); // end_flag - - init_host_command((uint32_t*)&host_command[2], (uint32_t*)&response_command[2], - host_instruction::port_select::c, // destination_port - ap::de::device | ap::port_select::c, C::command_code, (sizeof (typename C::data_fields)), - false); // end_flag - - init_host_command((uint32_t*)&host_command[3], (uint32_t*)&response_command[3], - host_instruction::port_select::d, // destination_port - ap::de::device | ap::port_select::d, C::command_code, (sizeof (typename C::data_fields)), - true); // end_flag - - return reinterpret_cast(&host_command[1]) - reinterpret_cast(&host_command[0]); -} - -} diff --git a/maple/maple_port.hpp b/maple/maple_port.hpp new file mode 100644 index 0000000..dfe2246 --- /dev/null +++ b/maple/maple_port.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "maple_bus_bits.hpp" + +constexpr inline uint32_t ap_port_select(const uint32_t port) +{ + switch (port) { + default: [[fallthrough]]; + case 0: return ap::port_select::a; + case 1: return ap::port_select::b; + case 2: return ap::port_select::c; + case 3: return ap::port_select::d; + } +} + +constexpr inline uint32_t host_instruction_port_select(const uint32_t port) +{ + switch (port) { + default: [[fallthrough]]; + case 0: return host_instruction::port_select::a; + case 1: return host_instruction::port_select::b; + case 2: return host_instruction::port_select::c; + case 3: return host_instruction::port_select::d; + } +} diff --git a/regs/gen/maple_data_format.py b/regs/gen/maple_data_format.py index 49a519c..da86a23 100644 --- a/regs/gen/maple_data_format.py +++ b/regs/gen/maple_data_format.py @@ -121,7 +121,7 @@ def render_format(format): def render_formats(name, formats): yield "#pragma once" - + yield "" yield f"namespace {name} {{" for format in formats: yield from render_format(format) diff --git a/regs/gen/maple_key_scan_codes.py b/regs/gen/maple_key_scan_codes.py index 36738de..175be51 100644 --- a/regs/gen/maple_key_scan_codes.py +++ b/regs/gen/maple_key_scan_codes.py @@ -1,4 +1,5 @@ import sys +import string from csv_input import read_input from generate import renderer @@ -9,17 +10,59 @@ def render_row(row): yield f"constexpr uint32_t {usage} = {hex(code)};" def render_rows(rows): - yield "#include " - yield "" yield "namespace ft6 {" - yield "namespace scan_codes {" + yield "namespace scan_code {" for row in rows: yield from render_row(row) yield "}" yield "}" +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 "namespace ft6 {" + yield "namespace scan_code {" + yield f"constexpr uint32_t last_printable = {hex(last_printable)};" + yield "" + yield f"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 "};" + yield "}" + 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_rows(rows)) + render(render_scancode_code_point(rows)) print(out.getvalue()) diff --git a/regs/maple_bus_ft6_key_scan_codes.csv b/regs/maple_bus_ft6_key_scan_codes.csv index e31bc99..5b8e460 100644 --- a/regs/maple_bus_ft6_key_scan_codes.csv +++ b/regs/maple_bus_ft6_key_scan_codes.csv @@ -1,84 +1,84 @@ -"code","usage", -"0x00","no_operation", -"0x01","rollover_error", -"0x02","post_fail", -"0x03","undefined_error", -"0x04","a_A", -"0x05","b_B", -"0x06","c_C", -"0x07","d_D", -"0x08","e_E", -"0x09","f_F", -"0x0a","g_G", -"0x0b","h_H", -"0x0c","i_I", -"0x0d","j_J", -"0x0e","k_K", -"0x0f","l_L", -"0x10","m_M", -"0x11","n_N", -"0x12","o_O", -"0x13","p_P", -"0x14","q_Q", -"0x15","r_R", -"0x16","s_S", -"0x17","t_T", -"0x18","u_U", -"0x19","v_V", -"0x1a","w_W", -"0x1b","x_X", -"0x1c","y_Y", -"0x1d","z_Z", -"0x1e","_1_exclam", -"0x1f","_2_at", -"0x20","_3_numbersign", -"0x21","_4_dollar", -"0x22","_5_percent", -"0x23","_6_asciicircum", -"0x24","_7_ampersand", -"0x25","_8_asterisk", -"0x26","_9_parenleft", -"0x27","_0_parenright", -"0x28","_return", -"0x29","esc", -"0x2a","backspace", -"0x2b","tab", -"0x2c","spacebar", -"0x2d","minus_underscore", -"0x2e","equal_plus","underscore carot" -"0x2f","bracketleft_braceleft","at" -"0x30","bracketright_braceright","braceleft" -"0x31","backslash_bar", -"0x32","_unknown_", -"0x33","semicolon_colon", -"0x34","apostrophe_quotedbl", -"0x35","grave_asciitilde", -"0x36","comma_less", -"0x37","period_greater", -"0x38","slash_question", -"0x39","caps_lock", -"0x3a","F1", -"0x3b","F2", -"0x3c","F3", -"0x3d","F4", -"0x3e","F5", -"0x3f","F6", -"0x40","F7", -"0x41","F8", -"0x42","F9", -"0x43","F10", -"0x44","F11", -"0x45","F12", -"0x46","print_screen", -"0x47","scroll_lock", -"0x48","pause", -"0x49","insert", -"0x4a","home", -"0x4b","page_up", -"0x4c","_delete", -"0x4d","end", -"0x4e","page_down", -"0x4f","right_arrow", -"0x50","left_arrow", -"0x51","down_arrow", -"0x52","up_arrow", +"code","usage","normal","shift" +"0x00","no_operation",, +"0x01","rollover_error",, +"0x02","post_fail",, +"0x03","undefined_error",, +"0x04","a_A","a","A" +"0x05","b_B","b","B" +"0x06","c_C","c","C" +"0x07","d_D","d","D" +"0x08","e_E","e","E" +"0x09","f_F","f","F" +"0x0a","g_G","g","G" +"0x0b","h_H","h","H" +"0x0c","i_I","i","I" +"0x0d","j_J","j","J" +"0x0e","k_K","k","K" +"0x0f","l_L","l","L" +"0x10","m_M","m","M" +"0x11","n_N","n","N" +"0x12","o_O","o","O" +"0x13","p_P","p","P" +"0x14","q_Q","q","Q" +"0x15","r_R","r","R" +"0x16","s_S","s","S" +"0x17","t_T","t","T" +"0x18","u_U","u","U" +"0x19","v_V","v","V" +"0x1a","w_W","w","W" +"0x1b","x_X","x","X" +"0x1c","y_Y","y","Y" +"0x1d","z_Z","z","Z" +"0x1e","_1_exclam",1,"!" +"0x1f","_2_at",2,"@" +"0x20","_3_numbersign",3,"#" +"0x21","_4_dollar",4,"$" +"0x22","_5_percent",5,"%" +"0x23","_6_asciicircum",6,"^" +"0x24","_7_ampersand",7,"&" +"0x25","_8_asterisk",8,"*" +"0x26","_9_parenleft",9,"(" +"0x27","_0_parenright",0,")" +"0x28","_return",, +"0x29","esc",, +"0x2a","backspace",, +"0x2b","tab",, +"0x2c","spacebar",, +"0x2d","minus_underscore","-","_" +"0x2e","equal_plus","=","+" +"0x2f","bracketleft_braceleft","[","{" +"0x30","bracketright_braceright","]","}" +"0x31","backslash_bar","\","|" +"0x32","iso_numbersign_tilde","#","~" +"0x33","semicolon_colon",";",":" +"0x34","apostrophe_quotedbl","'","""" +"0x35","grave_asciitilde","'","~" +"0x36","comma_less",",","<" +"0x37","period_greater",".",">" +"0x38","slash_question","/","?" +"0x39","caps_lock",, +"0x3a","F1",, +"0x3b","F2",, +"0x3c","F3",, +"0x3d","F4",, +"0x3e","F5",, +"0x3f","F6",, +"0x40","F7",, +"0x41","F8",, +"0x42","F9",, +"0x43","F10",, +"0x44","F11",, +"0x45","F12",, +"0x46","print_screen",, +"0x47","scroll_lock",, +"0x48","pause",, +"0x49","insert",, +"0x4a","home",, +"0x4b","page_up",, +"0x4c","_delete",, +"0x4d","end",, +"0x4e","page_down",, +"0x4f","right_arrow",, +"0x50","left_arrow",, +"0x51","down_arrow",, +"0x52","up_arrow",, diff --git a/regs/maple_bus_ft6_key_scan_codes.ods b/regs/maple_bus_ft6_key_scan_codes.ods index 6d4333de77c17f388ad4a98b710717841b23436c..f5f6d3e25472c2cbf0175812daece1684e7be419 100644 GIT binary patch delta 14677 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 Ivpeel0p58@%m4rY diff --git a/sh7091/serial.hpp b/sh7091/serial.hpp index 070621c..2a87c12 100644 --- a/sh7091/serial.hpp +++ b/sh7091/serial.hpp @@ -1,3 +1,5 @@ +#pragma once + #include "string.hpp" namespace serial { diff --git a/text_editor/keyboard.cpp b/text_editor/keyboard.cpp index bfe45e2..f7a7406 100644 --- a/text_editor/keyboard.cpp +++ b/text_editor/keyboard.cpp @@ -21,7 +21,7 @@ void keyboard_do_get_condition(uint32_t * command_buf, }; const uint32_t command_size = maple::init_host_command_all_ports(command_buf, receive_buf, data_fields); - using host_response_type = struct maple::command_response; + using host_response_type = struct maple::host_response; auto host_response = reinterpret_cast(receive_buf); maple::dma_start(command_buf, command_size, @@ -68,12 +68,21 @@ void keyboard_debug(ft6::data_transfer::data_format * keyboards, uint32_t frame_ } } +static inline bool is_shifted(const uint8_t modifier_key) +{ + return + (ft6::data_transfer::modifier_key::right_shift() & modifier_key) || + (ft6::data_transfer::modifier_key::left_shift() & modifier_key); +} + void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame_ix, gap_buffer& gb) { uint32_t this_frame = (frame_ix + 0) & 1; uint32_t next_frame = (frame_ix + 1) & 1; for (int i = 0; i < 6; i++) { - if (i < 5 && keyboards[this_frame].scan_code_array[i + 1] != ft6::scan_codes::no_operation) + if (keyboards[this_frame].scan_code_array[i] == ft6::scan_code::no_operation) + break; + if (i < 5 && keyboards[this_frame].scan_code_array[i + 1] != ft6::scan_code::no_operation) continue; bool make = true; for (int j = 0; j < 6; j++) { @@ -85,63 +94,25 @@ void keyboard_update(ft6::data_transfer::data_format * keyboards, uint32_t frame if (make) { // make uint8_t scan_code = keyboards[this_frame].scan_code_array[i]; + if (scan_code <= ft6::scan_code::last_printable) { + bool shifted = is_shifted(keyboards[this_frame].modifier_key); + char_type code_point = ft6::scan_code::code_point[scan_code][shifted]; + if (code_point != 0) { + gap_append(gb, code_point); + continue; + } + } switch (scan_code) { - case ft6::scan_codes::a_A: [[fallthrough]]; - case ft6::scan_codes::b_B: [[fallthrough]]; - case ft6::scan_codes::c_C: [[fallthrough]]; - case ft6::scan_codes::d_D: [[fallthrough]]; - case ft6::scan_codes::e_E: [[fallthrough]]; - case ft6::scan_codes::f_F: [[fallthrough]]; - case ft6::scan_codes::g_G: [[fallthrough]]; - case ft6::scan_codes::h_H: [[fallthrough]]; - case ft6::scan_codes::i_I: [[fallthrough]]; - case ft6::scan_codes::j_J: [[fallthrough]]; - case ft6::scan_codes::k_K: [[fallthrough]]; - case ft6::scan_codes::l_L: [[fallthrough]]; - case ft6::scan_codes::m_M: [[fallthrough]]; - case ft6::scan_codes::n_N: [[fallthrough]]; - case ft6::scan_codes::o_O: [[fallthrough]]; - case ft6::scan_codes::p_P: [[fallthrough]]; - case ft6::scan_codes::q_Q: [[fallthrough]]; - case ft6::scan_codes::r_R: [[fallthrough]]; - case ft6::scan_codes::s_S: [[fallthrough]]; - case ft6::scan_codes::t_T: [[fallthrough]]; - case ft6::scan_codes::u_U: [[fallthrough]]; - case ft6::scan_codes::v_V: [[fallthrough]]; - case ft6::scan_codes::w_W: [[fallthrough]]; - case ft6::scan_codes::x_X: [[fallthrough]]; - case ft6::scan_codes::y_Y: [[fallthrough]]; - case ft6::scan_codes::z_Z: - { - char_type code_point = (scan_code - ft6::scan_codes::a_A) + 'a'; - gap_append(gb, code_point); - } - break; - case ft6::scan_codes::_1_exclam: [[fallthrough]]; - case ft6::scan_codes::_2_at: [[fallthrough]]; - case ft6::scan_codes::_3_numbersign: [[fallthrough]]; - case ft6::scan_codes::_4_dollar: [[fallthrough]]; - case ft6::scan_codes::_5_percent: [[fallthrough]]; - case ft6::scan_codes::_6_asciicircum: [[fallthrough]]; - case ft6::scan_codes::_7_ampersand: [[fallthrough]]; - case ft6::scan_codes::_8_asterisk: [[fallthrough]]; - case ft6::scan_codes::_9_parenleft: - { - char_type code_point = (scan_code - ft6::scan_codes::_1_exclam) + '1'; - gap_append(gb, code_point); - } - break; - case ft6::scan_codes::_0_parenright: gap_append(gb, '0'); break; - case ft6::scan_codes::_return: gap_append(gb, '\n'); break; - case ft6::scan_codes::backspace: gap_pop(gb); break; - case ft6::scan_codes::spacebar: gap_append(gb, ' '); break; + case ft6::scan_code::_return: gap_append(gb, '\n'); break; + case ft6::scan_code::backspace: gap_pop(gb); break; + case ft6::scan_code::spacebar: gap_append(gb, ' '); break; - case ft6::scan_codes::left_arrow: gap_cursor_pos(gb, -1); break; - case ft6::scan_codes::right_arrow: gap_cursor_pos(gb, 1); break; - case ft6::scan_codes::up_arrow: gap_cursor_pos_line(gb, -1); break; - case ft6::scan_codes::down_arrow: gap_cursor_pos_line(gb, 1); break; - default: - break; + case ft6::scan_code::left_arrow: gap_cursor_pos(gb, -1); break; + case ft6::scan_code::right_arrow: gap_cursor_pos(gb, 1); break; + case ft6::scan_code::up_arrow: gap_cursor_pos_line(gb, -1); break; + case ft6::scan_code::down_arrow: gap_cursor_pos_line(gb, 1); break; + default: + break; } } } diff --git a/text_editor/text_editor.cpp b/text_editor/text_editor.cpp index da9fd9c..e1830b6 100644 --- a/text_editor/text_editor.cpp +++ b/text_editor/text_editor.cpp @@ -80,13 +80,13 @@ void main() constexpr uint32_t ta_alloc = ta_alloc_ctrl::pt_opb::no_list | ta_alloc_ctrl::tm_opb::no_list - //| ta_alloc_ctrl::t_opb::_16x4byte + | ta_alloc_ctrl::t_opb::_16x4byte | ta_alloc_ctrl::om_opb::no_list | ta_alloc_ctrl::o_opb::_16x4byte; constexpr struct opb_size opb_size = { .opaque = 16 * 4 , .opaque_modifier = 0 - //, .translucent = 16 * 4 + , .translucent = 16 * 4 , .translucent_modifier = 0 , .punch_through = 0 };