diff --git a/example/clipping.cpp b/example/clipping.cpp index 1aa8cfb..7b23f46 100644 --- a/example/clipping.cpp +++ b/example/clipping.cpp @@ -58,7 +58,7 @@ void do_get_condition(uint32_t * command_buf, return; } auto& data_fields = bus_data.data_fields; - if ((data_fields.function_type & std::byteswap(function_type::controller)) == 0) { + if ((std::byteswap(data_fields.function_type) & function_type::controller) == 0) { return; } diff --git a/example/clipping2.cpp b/example/clipping2.cpp index 8ef15aa..12b09bd 100644 --- a/example/clipping2.cpp +++ b/example/clipping2.cpp @@ -59,7 +59,7 @@ void do_get_condition(uint32_t * command_buf, return; } auto& data_fields = bus_data.data_fields; - if ((data_fields.function_type & std::byteswap(function_type::controller)) == 0) { + if ((std::byteswap(data_fields.function_type) & function_type::controller) == 0) { return; } diff --git a/example/clipping_textured.cpp b/example/clipping_textured.cpp index 1559b4d..f1e8d1c 100644 --- a/example/clipping_textured.cpp +++ b/example/clipping_textured.cpp @@ -64,7 +64,7 @@ void do_get_condition(uint32_t * command_buf, return; } auto& data_fields = bus_data.data_fields; - if ((data_fields.function_type & std::byteswap(function_type::controller)) == 0) { + if ((std::byteswap(data_fields.function_type) & function_type::controller) == 0) { return; } diff --git a/example/example.mk b/example/example.mk index 9877d0b..1a30778 100644 --- a/example/example.mk +++ b/example/example.mk @@ -286,6 +286,15 @@ MAPLE_CONTROLLER_OBJ = \ example/maple_controller.elf: LDSCRIPT = $(LIB)/main.lds example/maple_controller.elf: $(START_OBJ) $(MAPLE_CONTROLLER_OBJ) +MAPLE_STORAGE_OBJ = \ + example/maple_storage.o \ + holly/video_output.o \ + sh7091/serial.o \ + maple/maple.o + +example/maple_storage.elf: LDSCRIPT = $(LIB)/main.lds +example/maple_storage.elf: $(START_OBJ) $(MAPLE_STORAGE_OBJ) + MAPLE_WINK_OBJ = \ example/maple_wink.o \ holly/video_output.o \ diff --git a/example/maple_analog.cpp b/example/maple_analog.cpp index 98c6701..ac8d926 100644 --- a/example/maple_analog.cpp +++ b/example/maple_analog.cpp @@ -58,7 +58,7 @@ void do_get_condition(uint32_t * command_buf, return; } auto& data_fields = bus_data.data_fields; - if ((data_fields.function_type & std::byteswap(function_type::controller)) == 0) { + if ((std::byteswap(data_fields.function_type) & function_type::controller) == 0) { return; } diff --git a/example/maple_controller.cpp b/example/maple_controller.cpp index 8602cc1..3dd21e0 100644 --- a/example/maple_controller.cpp +++ b/example/maple_controller.cpp @@ -39,7 +39,7 @@ void do_get_condition() 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) { + } else if ((std::byteswap(data_fields.function_type) & function_type::controller) != 0) { bool a = ft0::data_transfer::digital_button::a(data_fields.data.digital_button); if (a == 0) { serial::string("port "); diff --git a/example/maple_storage.cpp b/example/maple_storage.cpp new file mode 100644 index 0000000..3fbb4d3 --- /dev/null +++ b/example/maple_storage.cpp @@ -0,0 +1,220 @@ +#include + +#include "align.hpp" +#include "holly/video_output.hpp" +#include "holly/core_bits.hpp" +#include "holly/holly.hpp" +#include "maple/maple.hpp" +#include "maple/maple_port.hpp" +#include "maple/maple_bus_bits.hpp" +#include "maple/maple_bus_commands.hpp" +#include "maple/maple_bus_ft1.hpp" +#include "maple/maple_host_command_writer.hpp" +#include "sh7091/serial.hpp" + +#include "systembus.hpp" + +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))); + + const uint32_t host_port_select = host_instruction_port_select(port); + const uint32_t destination_ap = ap_port_select(port) | ap::de::expansion_device | lm; + + { + using command_type = device_request; + using response_type = device_status; + + auto writer = maple::host_command_writer(send_buf, recv_buf); + + 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; + auto& data_fields = bus_data.data_fields; + if (bus_data.command_code != response_type::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::hexlify(&data_fields.device_id.ft, 4); + serial::string(" fd[0]: "); + serial::hexlify(&data_fields.device_id.fd[0], 4); + serial::string(" fd[1]: "); + serial::hexlify(&data_fields.device_id.fd[1], 4); + serial::string(" fd[2]: "); + serial::hexlify(&data_fields.device_id.fd[2], 4); + serial::string(" source_ap.lm_bus: "); + serial::integer(bus_data.source_ap & ap::lm_bus::bit_mask); + } + + if ((std::byteswap(data_fields.device_id.ft) & function_type::storage) == 0) { + return; + } + } + + { + using command_type = get_media_info; + using response_type = data_transfer; + + auto writer = maple::host_command_writer(send_buf, recv_buf); + + auto [host_command, host_response] + = writer.append_command(host_port_select, + destination_ap, + true); // end_flag + host_command->bus_data.data_fields.function_type = std::byteswap(function_type::storage); + host_command->bus_data.data_fields.pt = 0; + + maple::dma_start(send_buf, writer.send_offset, + recv_buf, writer.recv_offset); + + auto& bus_data = host_response->bus_data; + auto& data_fields = bus_data.data_fields; + auto& data = data_fields.data; + + if (bus_data.command_code != response_type::command_code) { + serial::string("lm did not reply: "); + serial::integer(port, ' '); + serial::integer(lm); + } else { + serial::string(" total_size: "); + serial::integer(data.total_size); + serial::string(" partition_number: "); + serial::integer(data.partition_number); + serial::string(" system_area_block_number: "); + serial::integer(data.system_area_block_number); + serial::string(" fat_area_block_number: "); + serial::integer(data.fat_area_block_number); + serial::string(" number_of_fat_area_blocks: "); + serial::integer(data.number_of_fat_area_blocks); + serial::string(" file_information_block_number: "); + serial::integer(data.file_information_block_number); + serial::string(" number_of_file_information_blocks: "); + serial::integer(data.number_of_file_information_blocks); + serial::string(" volume_icon: "); + serial::integer(data.volume_icon); + serial::string(" save_area_block_number: "); + serial::integer(data.save_area_block_number); + serial::string(" number_of_save_area_blocks: "); + serial::integer(data.number_of_save_area_blocks); + serial::string(" reserved_for_execution_file: "); + serial::integer(data.reserved_for_execution_file); + } + } + + { + using command_type = block_read; + using response_type = data_transfer>; + + auto writer = maple::host_command_writer(send_buf, recv_buf); + + auto [host_command, host_response] + = writer.append_command(host_port_select, + destination_ap, + true); // end_flag + host_command->bus_data.data_fields.function_type = std::byteswap(function_type::storage); + host_command->bus_data.data_fields.pt = 0; + host_command->bus_data.data_fields.phase = 0; + host_command->bus_data.data_fields.block_number = std::byteswap(0xff); + + maple::dma_start(send_buf, writer.send_offset, + recv_buf, writer.recv_offset); + + auto& bus_data = host_response->bus_data; + auto& data_fields = bus_data.data_fields; + auto& data = data_fields.data; + serial::integer(std::byteswap(host_command->bus_data.data_fields.block_number)); + if (bus_data.command_code != response_type::command_code) { + serial::string("lm did not reply block_read: "); + serial::integer(port, ' '); + serial::integer(lm, ' '); + serial::integer(bus_data.command_code); + auto error = reinterpret_cast(&data_fields); + serial::hexlify(&error->function_error_code, 4); + } else { + for (int i = 0; i < 512; i++) { + serial::hexlify(data.block_data[i]); + serial::character(' '); + if (i % 16 == 15) { + serial::character('\n'); + } + } + } + } +} + +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; + + 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("port: "); + serial::integer(port); + serial::string(" disconnected\n"); + } else { + serial::string("port: "); + serial::integer(port); + serial::string(" ft: "); + serial::hexlify(&data_fields.device_id.ft, 4); + serial::string(" fd[0]: "); + serial::hexlify(&data_fields.device_id.fd[0], 4); + serial::string(" fd[1]: "); + serial::hexlify(&data_fields.device_id.fd[1], 4); + serial::string(" fd[2]: "); + serial::hexlify(&data_fields.device_id.fd[2], 4); + 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() +{ + // flycast needs this in HLE mode, or else it won't start the vcount + // counter. + video_output::set_mode_vga(); + + do_device_request(); +} diff --git a/example/modifier_volume_with_two_volumes.cpp b/example/modifier_volume_with_two_volumes.cpp index d07fd56..9b5d438 100644 --- a/example/modifier_volume_with_two_volumes.cpp +++ b/example/modifier_volume_with_two_volumes.cpp @@ -59,7 +59,7 @@ void do_get_condition(uint32_t * command_buf, return; } auto& data_fields = bus_data.data_fields; - if ((data_fields.function_type & std::byteswap(function_type::controller)) == 0) { + if ((std::byteswap(data_fields.function_type) & function_type::controller) == 0) { return; } diff --git a/example/texture_filtering_maple.cpp b/example/texture_filtering_maple.cpp index 5af8327..1684c59 100644 --- a/example/texture_filtering_maple.cpp +++ b/example/texture_filtering_maple.cpp @@ -258,7 +258,7 @@ void do_get_condition(uint32_t * command_buf, return; } auto& data_fields = bus_data.data_fields; - if ((data_fields.function_type & std::byteswap(function_type::controller)) == 0) { + if ((std::byteswap(data_fields.function_type) & function_type::controller) == 0) { return; } diff --git a/headers.mk b/headers.mk index 8c8b254..4217714 100644 --- a/headers.mk +++ b/headers.mk @@ -50,6 +50,9 @@ maple/maple_bus_bits.hpp: regs/maple_bus_bits.csv regs/gen/core_bits.py maple/maple_bus_ft0.hpp: regs/maple_bus_ft0.csv regs/gen/maple_data_format.py python regs/gen/maple_data_format.py $< > $@ +maple/maple_bus_ft1.hpp: regs/maple_bus_ft1.csv regs/gen/maple_data_format.py + python regs/gen/maple_data_format.py $< > $@ + maple/maple_bus_ft6.hpp: regs/maple_bus_ft6.csv regs/gen/maple_data_format.py python regs/gen/maple_data_format.py $< > $@ diff --git a/maple/maple_bus_commands.hpp b/maple/maple_bus_commands.hpp index 7a6c704..b76ec22 100644 --- a/maple/maple_bus_commands.hpp +++ b/maple/maple_bus_commands.hpp @@ -137,7 +137,7 @@ struct block_read { uint32_t function_type; uint8_t pt; uint8_t phase; - uint16_t block_no; + uint16_t block_number; }; }; @@ -152,7 +152,7 @@ struct block_write { uint32_t function_type; uint8_t pt; uint8_t phase; - uint16_t block_no; + uint16_t block_number; T written_data; }; @@ -167,7 +167,7 @@ struct get_last_error { uint32_t function_type; uint8_t pt; uint8_t phase; - uint16_t block_no; + uint16_t block_number; }; }; diff --git a/maple/maple_bus_ft0.hpp b/maple/maple_bus_ft0.hpp index aa835a1..999ab82 100644 --- a/maple/maple_bus_ft0.hpp +++ b/maple/maple_bus_ft0.hpp @@ -1,4 +1,5 @@ #pragma once + namespace ft0 { namespace data_transfer { namespace digital_button { diff --git a/maple/maple_bus_ft1.hpp b/maple/maple_bus_ft1.hpp new file mode 100644 index 0000000..4a7151b --- /dev/null +++ b/maple/maple_bus_ft1.hpp @@ -0,0 +1,35 @@ +#pragma once + +namespace ft1 { + namespace get_media_info_data_transfer { + struct data_format { + uint16_t total_size; + uint16_t partition_number; + uint16_t system_area_block_number; + uint16_t fat_area_block_number; + uint16_t number_of_fat_area_blocks; + uint16_t file_information_block_number; + uint16_t number_of_file_information_blocks; + uint16_t volume_icon; + uint16_t save_area_block_number; + uint16_t number_of_save_area_blocks; + uint32_t reserved_for_execution_file; + }; + static_assert((sizeof (struct data_format)) % 4 == 0); + static_assert((sizeof (struct data_format)) == 24); + } + + namespace block_read_data_transfer { + template + struct data_format { + uint8_t pt; + uint8_t phase; + uint16_t block_number; + uint8_t block_data[n]; + }; + static_assert((sizeof (struct data_format<0>)) % 4 == 0); + static_assert((sizeof (struct data_format<0>)) == 4); + } + +} + diff --git a/regs/gen/maple_data_format.py b/regs/gen/maple_data_format.py index da86a23..31b7fc2 100644 --- a/regs/gen/maple_data_format.py +++ b/regs/gen/maple_data_format.py @@ -1,4 +1,5 @@ import sys +import re from dataclasses import dataclass from collections import defaultdict @@ -80,8 +81,17 @@ def parse(rows): assert len(formats) > 0 return formats +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 render_format(format): - yield f"namespace {format.name} {{" + format_name, variable = parse_format_name(format.name) + 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): @@ -100,23 +110,36 @@ def render_format(format): yield "}" yield "" + if variable is not None: + yield f"template " yield f"struct data_format {{" - for field_name in format.field_order: - subfields = format.fields[field_name] - if len(subfields) == 1: + 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) == 6: - yield f"uint8_t {field_name}[6];" + elif len(subfields) in {3, 6}: + yield f"uint8_t {field_name}[{len(subfields)}];" + elif len(subfields) == 4: + yield f"uint32_t {field_name};" else: assert False, (len(subfields), field_name) yield "};" - assert format.size % 4 == 0, format.size - yield f"static_assert((sizeof (struct data_format)) == {format.size});" + format_name, variable = parse_format_name(format.name) + if variable is not None: + yield f"static_assert((sizeof (struct data_format<0>)) % 4 == 0);" + yield f"static_assert((sizeof (struct data_format<0>)) == {format.size - 1});" + else: + 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): diff --git a/regs/maple_bus_commands.csv b/regs/maple_bus_commands.csv index a065431..d31f6a9 100644 --- a/regs/maple_bus_commands.csv +++ b/regs/maple_bus_commands.csv @@ -37,18 +37,18 @@ "block_read","host","0x0b","0x02","function_type",4 "block_read",,,,"pt",1 "block_read",,,,"phase",1 -"block_read",,,,"block_no",2 +"block_read",,,,"block_number",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",,,,"block_number",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 +"get_last_error",,,,"block_number",2 ,,,,, "set_condition","host","0x0e","0x01+n/4","function_type",4 "set_condition",,,,"write_in_data","n" diff --git a/regs/maple_bus_commands.ods b/regs/maple_bus_commands.ods index 1b56cf8..ed58d40 100644 Binary files a/regs/maple_bus_commands.ods and b/regs/maple_bus_commands.ods differ diff --git a/regs/maple_bus_ft1.csv b/regs/maple_bus_ft1.csv new file mode 100644 index 0000000..1bf81a1 --- /dev/null +++ b/regs/maple_bus_ft1.csv @@ -0,0 +1,32 @@ +"get_media_info_data_transfer",7,6,5,4,3,2,1,0 +"total_size",,,,,,,, +"total_size",,,,,,,, +"partition_number",,,,,,,, +"partition_number",,,,,,,, +"system_area_block_number",,,,,,,, +"system_area_block_number",,,,,,,, +"fat_area_block_number",,,,,,,, +"fat_area_block_number",,,,,,,, +"number_of_fat_area_blocks",,,,,,,, +"number_of_fat_area_blocks",,,,,,,, +"file_information_block_number",,,,,,,, +"file_information_block_number",,,,,,,, +"number_of_file_information_blocks",,,,,,,, +"number_of_file_information_blocks",,,,,,,, +"volume_icon",,,,,,,, +"volume_icon",,,,,,,, +"save_area_block_number",,,,,,,, +"save_area_block_number",,,,,,,, +"number_of_save_area_blocks",,,,,,,, +"number_of_save_area_blocks",,,,,,,, +"reserved_for_execution_file",,,,,,,, +"reserved_for_execution_file",,,,,,,, +"reserved_for_execution_file",,,,,,,, +"reserved_for_execution_file",,,,,,,, +,,,,,,,, +"block_read_data_transfer",7,6,5,4,3,2,1,0 +"pt",,,,,,,, +"phase",,,,,,,, +"block_number",,,,,,,, +"block_number",,,,,,,, +"block_data",,,,,,,, diff --git a/regs/maple_bus_ft1.ods b/regs/maple_bus_ft1.ods new file mode 100644 index 0000000..e574186 Binary files /dev/null and b/regs/maple_bus_ft1.ods differ diff --git a/sh7091/serial.cpp b/sh7091/serial.cpp index 6f4937f..7e122e8 100644 --- a/sh7091/serial.cpp +++ b/sh7091/serial.cpp @@ -101,6 +101,19 @@ void hexlify(const uint8_t n) character(num_buf[1]); } +void hexlify(const uint8_t * s, uint32_t len) +{ + for (uint32_t i = 0; i < len; i++) { + hexlify(s[i]); + } + character('\n'); +} + +void hexlify(const uint32_t * s, uint32_t len) +{ + hexlify(reinterpret_cast(s), len); +} + template void integer(const T n, const char end, const uint32_t length) { diff --git a/sh7091/serial.hpp b/sh7091/serial.hpp index 2a87c12..80027cd 100644 --- a/sh7091/serial.hpp +++ b/sh7091/serial.hpp @@ -14,6 +14,10 @@ void string(const uint8_t * s, uint32_t len); void hexlify(const uint8_t n); +void hexlify(const uint8_t * s, uint32_t len); + +void hexlify(const uint32_t * s, uint32_t len); + using hex = string::hex_type; using dec = string::dec_type; diff --git a/text_editor/keyboard.cpp b/text_editor/keyboard.cpp index 7ceb190..3d846c2 100644 --- a/text_editor/keyboard.cpp +++ b/text_editor/keyboard.cpp @@ -32,7 +32,7 @@ void keyboard_do_get_condition(ft6::data_transfer::data_format& data) if (bus_data.command_code != response_type::command_code) { continue; } - if ((data_fields.function_type & std::byteswap(function_type::keyboard)) == 0) { + if ((std::byteswap(data_fields.function_type) & function_type::keyboard) == 0) { continue; }