diff --git a/base.mk b/base.mk index 82f8536..be80c0a 100644 --- a/base.mk +++ b/base.mk @@ -7,6 +7,7 @@ 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 +CFLAGS += -Wno-error=unused-but-set-variable CXXFLAGS += -fno-exceptions -fno-non-call-exceptions -fno-rtti -fno-threadsafe-statics diff --git a/common.mk b/common.mk index b5fd50c..16cfe68 100644 --- a/common.mk +++ b/common.mk @@ -2,7 +2,7 @@ MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) DIR := $(dir $(MAKEFILE_PATH)) LIB ?= . -OPT ?= -Os +OPT ?= -O3 GENERATED ?= AARCH = --isa=sh4 --little diff --git a/example/example.mk b/example/example.mk index 1a30778..19d5ecc 100644 --- a/example/example.mk +++ b/example/example.mk @@ -290,7 +290,8 @@ MAPLE_STORAGE_OBJ = \ example/maple_storage.o \ holly/video_output.o \ sh7091/serial.o \ - maple/maple.o + maple/maple.o \ + $(LIBGCC) example/maple_storage.elf: LDSCRIPT = $(LIB)/main.lds example/maple_storage.elf: $(START_OBJ) $(MAPLE_STORAGE_OBJ) diff --git a/example/maple_storage.cpp b/example/maple_storage.cpp index 3fbb4d3..e920bff 100644 --- a/example/maple_storage.cpp +++ b/example/maple_storage.cpp @@ -10,10 +10,281 @@ #include "maple/maple_bus_commands.hpp" #include "maple/maple_bus_ft1.hpp" #include "maple/maple_host_command_writer.hpp" +#include "maple/storage.hpp" #include "sh7091/serial.hpp" #include "systembus.hpp" +struct storage_state { + uint32_t * send_buf; + uint32_t * recv_buf; + uint32_t host_port_select; + uint32_t destination_ap; + struct { + uint8_t partitions; + uint16_t bytes_per_block; + uint8_t write_accesses; + uint8_t read_accesses; + } device_status; + struct { + struct { + uint16_t block_number; + } system_area; + struct { + uint16_t block_number; + uint16_t number_of_blocks; + } fat_area; + struct { + uint16_t block_number; + uint16_t number_of_blocks; + } file_information; + } media_info; + storage::system_area system_area; + storage::fat_area * fat_area; + storage::file_information * file_information; // array + + uint32_t bytes_per_read_access() + { + // divide rounding up + return (device_status.bytes_per_block + (device_status.read_accesses - 1)) / device_status.read_accesses; + } + + uint32_t bytes_per_write_access() + { + // divide rounding up + return (device_status.bytes_per_block + (device_status.write_accesses - 1)) / device_status.write_accesses; + } + + uint32_t system_area_size() + { + return device_status.bytes_per_block * 1; + } + + uint32_t fat_area_size() + { + return device_status.bytes_per_block * media_info.fat_area.number_of_blocks; + } + + uint32_t file_information_size() + { + return device_status.bytes_per_block * media_info.file_information.number_of_blocks; + } + + uint32_t file_information_entries() + { + return file_information_size() / (sizeof (struct storage::file_information)); + } +}; + +void parse_storage_function_definition(const uint32_t fd, storage_state& state) +{ + state.device_status.partitions = ((fd >> 24) & 0xff) + 1; + state.device_status.bytes_per_block = (((fd >> 16) & 0xff) + 1) * 32; + state.device_status.write_accesses = (fd >> 12) & 0xf; + state.device_status.read_accesses = (fd >> 8) & 0xf; + + serial::string(" function_definition:\n"); + serial::string(" partitions: "); + serial::integer(state.device_status.partitions); + serial::string(" bytes_per_block: "); + serial::integer(state.device_status.bytes_per_block); + serial::string(" write_accesses: "); + serial::integer(state.device_status.write_accesses); + serial::string(" read_accesses: "); + serial::integer(state.device_status.read_accesses); +} + +template +inline void copy(T * dst, const T * src, const int32_t n) noexcept +{ + int32_t n_t = n / (sizeof (T)); + while (n_t > 0) { + *dst++ = *src++; + n_t--; + } +} + +struct block_read_response { + union responses { + struct maple::file_error::data_fields file_error; + struct maple::data_transfer::data_fields data_transfer; + }; + + using data_fields = union responses; +}; + +bool do_block_read(storage_state& state, uint16_t block_number, uint8_t * dest) +{ + using command_type = maple::block_read; + using response_type = block_read_response; + + auto writer = maple::host_command_writer(state.send_buf, state.recv_buf); + + maple::host_response * host_responses[state.device_status.read_accesses]; + for (int32_t phase = 0; phase < state.device_status.read_accesses; phase++) { + bool end_flag = phase == (state.device_status.read_accesses - 1); + auto [host_command, host_response] + = writer.append_command(state.host_port_select, + state.destination_ap, + end_flag, + 0, // send_trailing + state.bytes_per_read_access() // recv_trailing + ); + + auto& data_fields = host_command->bus_data.data_fields; + data_fields.function_type = std::byteswap(function_type::storage); + data_fields.pt = 0; + data_fields.phase = phase; + data_fields.block_number = std::byteswap(block_number); + + host_responses[phase] = host_response; + + //serial::string("read phase: "); + //serial::integer(phase); + //serial::integer(writer.recv_offset); + } + + maple::dma_start(state.send_buf, writer.send_offset, + state.recv_buf, writer.recv_offset); + + for (uint32_t phase = 0; phase < state.device_status.read_accesses; phase++) { + auto& bus_data = host_responses[phase]->bus_data; + if (bus_data.command_code != maple::data_transfer::command_code) { + auto& file_error = bus_data.data_fields.file_error; + serial::string("lm did not reply block_read: "); + serial::integer(bus_data.command_code); + if (bus_data.command_code == maple::file_error::command_code) { + serial::string("error: "); + serial::hexlify(&file_error.function_error_code, 4); + serial::character('\n'); + } + return false; + } else { + auto& data_transfer = bus_data.data_fields.data_transfer; + copy(dest, data_transfer.data.block_data, state.bytes_per_read_access()); + dest += state.bytes_per_read_access(); + } + } + + return true; +} + +struct get_last_error_response { + union responses { + struct maple::device_reply device_reply; + struct maple::file_error file_error; + }; + + using data_fields = union responses; +}; + +bool do_block_write(storage_state& state, uint16_t block_number, uint8_t * src) +{ + auto writer = maple::host_command_writer(state.send_buf, state.recv_buf); + + uint32_t phase; + for (phase = 0; phase < state.device_status.write_accesses; phase++) { + using command_type = maple::block_write; + using response_type = maple::device_reply; + + auto [host_command, host_response] + = writer.append_command(state.host_port_select, + state.destination_ap, + false, // end_flag + state.bytes_per_write_access(), // send_trailing + 0 // recv_trailing + ); + + auto& data_fields = host_command->bus_data.data_fields; + data_fields.function_type = std::byteswap(function_type::storage); + data_fields.pt = 0; + data_fields.phase = phase; + data_fields.block_number = std::byteswap(block_number); + + copy(data_fields.written_data, src, state.bytes_per_write_access()); + src += state.bytes_per_write_access(); + //serial::string("write phase: "); + //serial::integer(phase); + //serial::integer(writer.send_offset); + } + + using command_type = maple::get_last_error; + using response_type = get_last_error_response; + + auto [host_command, host_response] + = writer.append_command(state.host_port_select, + state.destination_ap, + true); // end_flag + + auto& data_fields = host_command->bus_data.data_fields; + data_fields.function_type = std::byteswap(function_type::storage); + data_fields.pt = 0; + data_fields.phase = phase; + data_fields.block_number = std::byteswap(block_number); + //serial::string("write phase: "); + //serial::integer(phase); + //serial::integer(writer.send_offset); + + maple::dma_start(state.send_buf, writer.send_offset, + state.recv_buf, writer.recv_offset); + + serial::string("block write status: "); + serial::integer(host_response->bus_data.command_code); + + return true; +} + +uint16_t allocate_fat_chain(storage_state& state, uint16_t blocks) +{ + int32_t first_block = -1; + int32_t last_block; + // + for (int32_t i = (state.fat_area_size() / (sizeof (uint16_t))) - 1; i >= 0; i--) { + if (state.fat_area->fat_number[i] == storage::fat_area::data::unused) { + if (first_block == -1) { + first_block = i; + } else { + state.fat_area->fat_number[last_block] = i; + } + last_block = i; + blocks -= 1; + } + + if (blocks == 0) { + state.fat_area->fat_number[last_block] = storage::fat_area::data::data_end; + break; + } + } + for (uint32_t i = 0; i < (state.fat_area_size() / (sizeof (uint16_t))); i++) { + //serial::integer(i, ' '); + //serial::integer(state.fat_area->fat_number[i]); + } + + return first_block; +} + +bool allocate_file_information_data(storage_state& state, + uint16_t start_fat, + uint8_t const * const file_name, + uint16_t block_size) +{ + for (uint32_t i = 0; i < state.file_information_entries(); i++) { + if (state.file_information[i].status == storage::file_information::status::no_data_file) { + + state.file_information[i].status = storage::file_information::status::data_file; + state.file_information[i].copy = 0; + state.file_information[i].start_fat = start_fat; + copy(state.file_information[i].file_name, file_name, 12); + state.file_information[i].block_size = block_size; + state.file_information[i].header = 0; + state.file_information[i]._reserved = 0; + + return true; + } + } + return false; +} + void do_lm_request(uint8_t port, uint8_t lm) { uint32_t send_buf[1024] __attribute__((aligned(32))); @@ -22,9 +293,15 @@ void do_lm_request(uint8_t port, uint8_t lm) 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; + storage_state state; + state.send_buf = send_buf; + state.recv_buf = recv_buf; + state.host_port_select = host_port_select; + state.destination_ap = destination_ap; + { - using command_type = device_request; - using response_type = device_status; + using command_type = maple::device_request; + using response_type = maple::device_status; auto writer = maple::host_command_writer(send_buf, recv_buf); @@ -60,11 +337,13 @@ void do_lm_request(uint8_t port, uint8_t lm) if ((std::byteswap(data_fields.device_id.ft) & function_type::storage) == 0) { return; } + + parse_storage_function_definition(std::byteswap(data_fields.device_id.fd[2]), state); } { - using command_type = get_media_info; - using response_type = data_transfer; + using command_type = maple::get_media_info; + using response_type = maple::data_transfer; auto writer = maple::host_command_writer(send_buf, recv_buf); @@ -87,84 +366,174 @@ void do_lm_request(uint8_t port, uint8_t lm) serial::integer(port, ' '); serial::integer(lm); } else { - serial::string(" total_size: "); + serial::string(" media_info:\n"); + serial::string(" total_size: "); serial::integer(data.total_size); - serial::string(" partition_number: "); + serial::string(" partition_number: "); serial::integer(data.partition_number); - serial::string(" system_area_block_number: "); + serial::string(" system_area_block_number: "); serial::integer(data.system_area_block_number); - serial::string(" fat_area_block_number: "); + serial::string(" fat_area_block_number: "); serial::integer(data.fat_area_block_number); - serial::string(" number_of_fat_area_blocks: "); + serial::string(" number_of_fat_area_blocks: "); serial::integer(data.number_of_fat_area_blocks); - serial::string(" file_information_block_number: "); + serial::string(" file_information_block_number: "); serial::integer(data.file_information_block_number); - serial::string(" number_of_file_information_blocks: "); + serial::string(" number_of_file_information_blocks: "); serial::integer(data.number_of_file_information_blocks); - serial::string(" volume_icon: "); + serial::string(" volume_icon: "); serial::integer(data.volume_icon); - serial::string(" save_area_block_number: "); + serial::string(" save_area_block_number: "); serial::integer(data.save_area_block_number); - serial::string(" number_of_save_area_blocks: "); + serial::string(" number_of_save_area_blocks: "); serial::integer(data.number_of_save_area_blocks); - serial::string(" reserved_for_execution_file: "); + serial::string(" reserved_for_execution_file: "); serial::integer(data.reserved_for_execution_file); } + + state.media_info.system_area.block_number = data.system_area_block_number; + state.media_info.fat_area.block_number = data.fat_area_block_number; + state.media_info.fat_area.number_of_blocks = data.number_of_fat_area_blocks; + state.media_info.file_information.block_number = data.file_information_block_number; + state.media_info.file_information.number_of_blocks = data.number_of_file_information_blocks; + } + + uint8_t fat_area_data[state.fat_area_size()]; + uint8_t file_information_data[state.file_information_size()]; + + { + do_block_read(state, state.media_info.system_area.block_number, + reinterpret_cast(&state.system_area)); + serial::string(" system_area:\n"); + serial::string(" format_information: "); + serial::hexlify(state.system_area.format_information, 16); + serial::string(" volume_label: "); + serial::hexlify(state.system_area.volume_label, 32); + serial::string(" date_and_time_created: "); + serial::hexlify(state.system_area.date_and_time_created, 8); + serial::string(" total_size: "); + serial::integer(state.system_area.total_size); + } + + { + uint8_t * bufi = fat_area_data; + uint16_t chain = state.media_info.fat_area.block_number; + state.fat_area = reinterpret_cast(fat_area_data); + do { + do_block_read(state, chain, bufi); + bufi += state.device_status.bytes_per_block; + chain = state.fat_area->fat_number[chain]; + } while (chain != storage::fat_area::data::data_end); + serial::string(" read fat_area bytes: "); + serial::integer((uint32_t)(bufi - fat_area_data)); + + uint32_t count = 0; + for (uint32_t i = 0; i < (bufi - fat_area_data) / (sizeof (uint16_t)); i++) { + if (state.fat_area->fat_number[i] == 0xfffc) { + count += 1; + } + //serial::integer(i, ' '); + //serial::integer(state.fat_area->fat_number[i]); + } + serial::string(" free blocks: "); + serial::integer(count); + } + + { + uint8_t * bufi = file_information_data; + uint16_t chain = state.media_info.file_information.block_number; + state.file_information = reinterpret_cast(file_information_data); + do { + do_block_read(state, chain, bufi); + bufi += state.device_status.bytes_per_block; + chain = state.fat_area->fat_number[chain]; + } while (chain != storage::fat_area::data::data_end); + serial::string(" read file_information bytes: "); + serial::integer((uint32_t)(bufi - file_information_data)); + + for (uint32_t i = 0; i < state.file_information_entries(); i++) { + if (state.file_information[i].status == storage::file_information::status::data_file + || state.file_information[i].status == storage::file_information::status::execution_file) { + serial::string(" file_name: "); + serial::string(state.file_information[i].file_name, 12); + serial::character('\n'); + serial::string(" status: "); + serial::integer(state.file_information[i].status); + serial::string(" start_fat: "); + serial::integer(state.file_information[i].start_fat); + serial::string(" block_size: "); + serial::integer(state.file_information[i].block_size); + } + } } { - using command_type = block_read; - using response_type = data_transfer>; + uint16_t block_size = 3; + uint16_t start_fat = allocate_fat_chain(state, block_size); + char const * file_name = "HELLOANA.SUP"; + allocate_file_information_data(state, start_fat, + reinterpret_cast(file_name), + block_size); - 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'); - } - } + { + uint16_t chain = state.media_info.fat_area.block_number; + uint8_t * buf = reinterpret_cast(state.fat_area); + do { + do_block_write(state, chain, buf); + buf += state.device_status.bytes_per_block; + chain = state.fat_area->fat_number[chain]; + } while (chain != storage::fat_area::data::data_end); } + + { + uint16_t chain = state.media_info.file_information.block_number; + uint8_t * buf = reinterpret_cast(state.file_information); + do { + do_block_write(state, chain, buf); + buf += state.device_status.bytes_per_block; + chain = state.fat_area->fat_number[chain]; + } while (chain != storage::fat_area::data::data_end); + } + } + + { + uint8_t * bufi = fat_area_data; + uint16_t chain = state.media_info.fat_area.block_number; + state.fat_area = reinterpret_cast(fat_area_data); + do { + do_block_read(state, chain, bufi); + bufi += state.device_status.bytes_per_block; + chain = state.fat_area->fat_number[chain]; + } while (chain != storage::fat_area::data::data_end); + serial::string(" read fat_area bytes: "); + serial::integer((uint32_t)(bufi - fat_area_data)); + + uint32_t count = 0; + for (uint32_t i = 0; i < (bufi - fat_area_data) / (sizeof (uint16_t)); i++) { + if (state.fat_area->fat_number[i] == 0xfffc) { + count += 1; + } + serial::integer(i, ' '); + serial::integer(state.fat_area->fat_number[i]); + } + serial::string(" free blocks: "); + serial::integer(count); + allocate_fat_chain(state, 98); } } 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); + do_lm_request(port, ap::lm_bus::_0); if (lm & ap::lm_bus::_1) - do_lm_request(port, lm & ap::lm_bus::_1); + do_lm_request(port, ap::lm_bus::_1); if (lm & ap::lm_bus::_2) - do_lm_request(port, lm & ap::lm_bus::_2); + do_lm_request(port, ap::lm_bus::_2); if (lm & ap::lm_bus::_3) - do_lm_request(port, lm & ap::lm_bus::_3); + do_lm_request(port, ap::lm_bus::_3); if (lm & ap::lm_bus::_4) - do_lm_request(port, lm & ap::lm_bus::_4); + do_lm_request(port, ap::lm_bus::_4); } void do_device_request() @@ -174,8 +543,8 @@ void do_device_request() auto writer = maple::host_command_writer(send_buf, recv_buf); - using command_type = device_request; - using response_type = device_status; + using command_type = maple::device_request; + using response_type = maple::device_status; auto [host_command, host_response] = writer.append_command_all_ports(); diff --git a/maple/maple_bus_commands.hpp b/maple/maple_bus_commands.hpp index b76ec22..7658c00 100644 --- a/maple/maple_bus_commands.hpp +++ b/maple/maple_bus_commands.hpp @@ -2,6 +2,8 @@ #include +namespace maple { + struct device_id { uint32_t ft; uint32_t fd[3]; @@ -276,3 +278,4 @@ struct ar_error { static_assert((sizeof (struct ar_error::data_fields)) == 4); +} diff --git a/maple/maple_bus_ft1.hpp b/maple/maple_bus_ft1.hpp index 4a7151b..0b849bc 100644 --- a/maple/maple_bus_ft1.hpp +++ b/maple/maple_bus_ft1.hpp @@ -20,15 +20,14 @@ namespace ft1 { } namespace block_read_data_transfer { - template struct data_format { uint8_t pt; uint8_t phase; uint16_t block_number; - uint8_t block_data[n]; + uint8_t block_data[0]; }; - static_assert((sizeof (struct data_format<0>)) % 4 == 0); - static_assert((sizeof (struct data_format<0>)) == 4); + static_assert((sizeof (struct data_format)) % 4 == 0); + static_assert((sizeof (struct data_format)) == 4); } } diff --git a/maple/maple_host_command_writer.hpp b/maple/maple_host_command_writer.hpp index d45fa98..b3e9c09 100644 --- a/maple/maple_host_command_writer.hpp +++ b/maple/maple_host_command_writer.hpp @@ -20,20 +20,21 @@ struct host_command_writer { : send_buf(send_buf), recv_buf(recv_buf), send_offset(0), recv_offset(0) { } - template + template constexpr inline std::tuple *, maple::host_response *> append_command(uint32_t host_port_select, uint32_t destination_ap, - bool end_flag) + bool end_flag, + uint32_t send_trailing = 0, + uint32_t recv_trailing = 0) { 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; + const uint32_t data_size = (sizeof (typename C::data_fields)) + send_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]); @@ -50,8 +51,8 @@ struct host_command_writer { 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)); + send_offset += (sizeof (command_type)) + send_trailing; + recv_offset += (sizeof (response_type)) + recv_trailing; return {host_command, host_response}; } @@ -61,10 +62,10 @@ struct host_command_writer { 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); + 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/storage.hpp b/maple/storage.hpp new file mode 100644 index 0000000..6f8f5b5 --- /dev/null +++ b/maple/storage.hpp @@ -0,0 +1,66 @@ +#include +#include + +namespace storage { + +struct system_area { + uint8_t format_information[16]; + uint8_t volume_label[32]; + uint8_t date_and_time_created[8]; + uint8_t _reserved0[8]; + 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; + uint8_t volume_icon; + uint8_t reserved; + uint16_t save_area_block_number; + uint16_t number_of_save_area_blocks; + uint8_t reserved_for_execution_file[4]; + uint8_t _reserved1[8]; + uint8_t _reserved2[416]; +}; + +static_assert((sizeof (struct system_area)) == 0x200); +static_assert((offsetof (struct system_area, format_information)) == 0x000); +static_assert((offsetof (struct system_area, volume_label)) == 0x010); +static_assert((offsetof (struct system_area, date_and_time_created)) == 0x030); +static_assert((offsetof (struct system_area, total_size)) == 0x040); +static_assert((offsetof (struct system_area, save_area_block_number)) == 0x050); +static_assert((offsetof (struct system_area, _reserved2)) == 0x060); + +struct fat_area { + uint16_t fat_number[0]; + + struct data { + static constexpr uint16_t data_end = 0xfffa; + static constexpr uint16_t unused = 0xfffc; + static constexpr uint16_t block_damaged = 0xffff; + }; +}; + +static_assert((sizeof (struct fat_area)) == 0); + +struct file_information { + uint8_t status; + uint8_t copy; + uint16_t start_fat; + uint8_t file_name[12]; + uint8_t date[8]; + uint16_t block_size; + uint16_t header; + uint32_t _reserved; + + struct status { + static constexpr uint16_t no_data_file = 0x00; + static constexpr uint16_t data_file = 0x33; + static constexpr uint16_t execution_file = 0xcc; + }; +}; + +static_assert((sizeof (struct file_information)) == 32); + +} diff --git a/notes/storage-notes.txt b/notes/storage-notes.txt new file mode 100644 index 0000000..35e00f3 --- /dev/null +++ b/notes/storage-notes.txt @@ -0,0 +1,403 @@ + ft: 0x0000000e + fd[0]: 0x7e7e3f40 timer + fd[1]: 0x00051000 lcd + fd[2]: 0x000f4100 storage + +storage: + pt: 0x00 → 1 partition + bb: 0x0f → (0x0f + 1) * 32 = 512 bytes per block + wa: 0x4 → 1 block = 4 accesses, 128 bytes per access + ra: 0x1 → 1 block = 1 accesss, 512 bytes per access + rm: 0 → fixed + crc: 0 → CRC not needed + fd: 0 → reserved + +---- block ff (system area) + +format information: 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 +volume label: 01 8f af ff ff 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +date and time: 19 98 11 26 01 37 59 03 +reserved: 00 00 00 00 00 00 00 00 +total size: ff 00 +partition number: 00 00 +system area block number: ff 00 +fat area block number: fe 00 +number of fat area blocks: 01 00 +file information block number: fd 00 +number of file information blocks: 0d 00 +volume icon: 74 +reserved: 00 +save area block number: c8 00 +number of save area blocks: 1f 00 +reserved: 00 00 80 00 + +00 00 00 00 00 00 00 00 + +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +----- block fe (fat area) + +00: fc ff +01: fc ff +02: fc ff +03: fc ff +04: fc ff +05: fc ff +06: fc ff +07: fc ff +08: fc ff +09: fc ff +0a: fc ff +0b: fc ff +0c: fc ff +0d: fc ff +0e: fc ff +0f: fc ff +10: fc ff +11: fc ff +12: fc ff +13: fc ff +14: fc ff +15: fc ff +16: fc ff +17: fc ff +18: fc ff +19: fc ff +1a: fc ff +1b: fc ff +1c: fc ff +1d: fc ff +1e: fc ff +1f: fc ff +20: fc ff +21: fc ff +22: fc ff +23: fc ff +24: fc ff +25: fc ff +26: fc ff +27: fc ff +28: fc ff +29: fc ff +2a: fc ff +2b: fc ff +2c: fc ff +2d: fc ff +2e: fc ff +2f: fc ff +30: fc ff +31: fc ff +32: fc ff +33: fc ff +34: fc ff +35: fc ff +36: fc ff +37: fc ff +38: fc ff +39: fc ff +3a: fc ff +3b: fc ff +3c: fc ff +3d: fc ff +3e: fc ff +3f: fc ff +40: fc ff +41: fc ff +42: fc ff +43: fc ff +44: fc ff +45: fc ff +46: fc ff +47: fc ff +48: fc ff +49: fc ff +4a: fc ff +4b: fc ff +4c: fc ff +4d: fc ff +4e: fc ff +4f: fc ff +50: fc ff +51: fc ff +52: fc ff +53: fc ff +54: fc ff +55: fc ff +56: fc ff +57: fc ff +58: fc ff +59: fc ff +5a: fc ff +5b: fc ff +5c: fc ff +5d: fa ff +5e: 5d 00 +5f: 5e 00 +60: fa ff +61: 60 00 +62: 61 00 +63: 62 00 +64: 63 00 +65: 64 00 +66: 65 00 +67: 66 00 +68: 67 00 +69: 68 00 +6a: 69 00 +6b: 6a 00 +6c: 6b 00 +6d: fa ff +6e: 6d 00 +6f: fa ff +70: 6f 00 +71: 70 00 +72: 71 00 +73: 72 00 +74: 73 00 +75: 74 00 +76: 75 00 +77: 76 00 +78: 77 00 +79: 78 00 +7a: 79 00 +7b: 7a 00 +7c: 7b 00 +7d: 7c 00 +7e: 7d 00 +7f: 7e 00 +80: 7f 00 +81: 80 00 +82: 81 00 +83: 82 00 +84: 83 00 +85: 84 00 +86: 85 00 +87: 86 00 +88: 87 00 +89: 88 00 +8a: 89 00 +8b: 8a 00 +8c: 8b 00 +8d: 8c 00 +8e: 8d 00 +8f: 8e 00 +90: fa ff +91: 90 00 +92: 91 00 +93: 92 00 +94: fa ff +95: 94 00 +96: 95 00 +97: 96 00 +98: 97 00 +99: 98 00 +9a: 99 00 +9b: 9a 00 +9c: 9b 00 +9d: 9c 00 +9e: 9d 00 +9f: 9e 00 +a0: 9f 00 +a1: a0 00 +a2: a1 00 +a3: a2 00 +a4: a3 00 +a5: a4 00 +a6: a5 00 +a7: fa ff +a8: a7 00 +a9: a8 00 +aa: a9 00 +ab: aa 00 +ac: ab 00 +ad: ac 00 +ae: ad 00 +af: ae 00 +b0: af 00 +b1: b0 00 +b2: b1 00 +b3: b2 00 +b4: b3 00 +b5: b4 00 +b6: b5 00 +b7: b6 00 +b8: b7 00 +b9: fa ff +ba: b9 00 +bb: ba 00 +bc: bb 00 +bd: fa ff +be: bd 00 +bf: be 00 +c0: bf 00 +c1: c0 00 +c2: c1 00 +c3: fa ff +c4: c3 00 +c5: fa ff +c6: c5 00 +c7: c6 00 +c8: fc ff +c9: fc ff +ca: fc ff +cb: fc ff +cc: fc ff +cd: fc ff +ce: fc ff +cf: fc ff +d0: fc ff +d1: fc ff +d2: fc ff +d3: fc ff +d4: fc ff +d5: fc ff +d6: fc ff +d7: fc ff +d8: fc ff +d9: fc ff +da: fc ff +db: fc ff +dc: fc ff +dd: fc ff +de: fc ff +df: fc ff +e0: fc ff +e1: fc ff +e2: fc ff +e3: fc ff +e4: fc ff +e5: fc ff +e6: fc ff +e7: fc ff +e8: fc ff +e9: fc ff +ea: fc ff +eb: fc ff +ec: fc ff +ed: fc ff +ee: fc ff +ef: fc ff +f0: fc ff +f1: fa ff +f2: f1 00 +f3: f2 00 +f4: f3 00 +f5: f4 00 +f6: f5 00 +f7: f6 00 +f8: f7 00 +f9: f8 00 +fa: f9 00 +fb: fa 00 +fc: fb 00 +fd: fc 00 +fe: fa ff +ff: fa ff + +----- block fd (file information) + +status: 33 +copy: 00 +start fat: c7 00 +file name: 47 41 55 4e 54 4c 45 54 2e 30 30 31 "GAUNTLET.001" +date: 19 98 11 26 00 10 12 03 +block size: 03 00 +header: 00 00 +reserved: 00 00 00 00 + +status: 33 +copy: 00 +start fat: c4 00 +file name: 45 43 43 4f 44 4f 54 46 2e 5a 41 43 "ECCODOTF.ZAC" +date: 19 98 11 26 00 15 17 03 +block size: 02 00 +header: 00 00 +reserved: 00 00 00 00 + +status: 33 +copy: 00 +start fat: c2 00 +file name: 45 43 43 4f 44 4f 54 46 2e 5f 5f 5f "ECCODOTF.___" +date: 19 98 11 26 00 12 35 03 +block size: 06 00 +header: 00 00 +reserved: 00 00 00 00 + +status: 33 +copy: 00 +start fat: bc 00 +file name: 53 48 45 4e 4d 55 45 32 5f 53 59 53 "SHENMUE2_SYS" +date: 19 98 11 27 07 41 15 04 +block size: 04 00 +header: 00 00 +reserved: 00 00 00 00 + +status: 33 +copy: 00 +start fat: b8 00 +file name: 53 48 45 4e 4d 55 45 32 5f 30 30 31 "SHENMUE2_001" +date: 19 98 11 27 07 41 16 04 +block size: 12 00 +header: 00 00 +reserved: 00 00 00 00 + +status: 33 +copy: 00 +start fat: a6 00 +file name: 54 4f 4d 42 43 48 52 4e 2e 30 30 30 "TOMBCHRN.000" +date: 20 23 09 30 18 29 06 00 +block size: 13 00 +header: 00 00 +reserved: 00 00 00 00 + +status: 33 +copy: 00 +start fat: 93 00 +file name: 54 4f 4d 42 43 48 52 4e 2e 53 59 53 "TOMBCHRN.SYS" +date: 20 23 09 30 18 29 06 00 +block size: 04 00 +header: 00 00 +reserved: 00 00 00 00 + +status: 33 +copy: 00 +start fat: 8f 00 +file name: 54 4f 4d 42 52 41 49 44 2e 30 30 30 "TOMBRAID.000" +date: 20 23 09 30 22 09 06 00 +block size: 21 00 +header: 00 00 +reserved: 00 00 00 00 + +status: 33 +copy: 00 +start fat: 6e 00 +file name: 54 4f 4d 42 52 41 49 44 2e 53 59 53 "TOMBRAID.SYS" +date: 20 23 09 30 22 09 06 00 +block size: 02 00 +header: 00 00 +reserved: 00 00 00 00 + +status: 33 +copy: 00 +start fat: 6c 00 +file name: 4c 45 4d 41 4e 53 32 34 2e 4f 50 54 "LEMANS24.OPT" +date: 20 23 10 11 19 30 14 02 +block size: 0d 00 +header: 00 00 +reserved: 00 00 00 00 + +status: 33 +copy: ff +start fat: 5f 00 +file name: 43 48 55 5f 43 48 55 5f 5f 52 43 54 "CHU_CHU__RCT" +date: 20 23 10 11 16 59 43 02 +block size: 03 00 +header: 00 00 +reserved: 00 00 00 00 + + diff --git a/regs/gen/maple_data_format.py b/regs/gen/maple_data_format.py index 31b7fc2..2716745 100644 --- a/regs/gen/maple_data_format.py +++ b/regs/gen/maple_data_format.py @@ -35,6 +35,14 @@ def parse_bits(bits: list[str]): 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 @@ -61,7 +69,9 @@ def parse_data_format(ix, rows): assert len(bits) in {0, 8}, bits fields[field_name].append(Field(field_name, list(parse_bits(bits)))) - size += 1 + _, variable = parse_format_name(field_name) + if not variable: + size += 1 if field_name not in field_order: field_order.append(field_name) @@ -81,17 +91,8 @@ 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): - format_name, variable = parse_format_name(format.name) - yield f"namespace {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): @@ -110,8 +111,6 @@ 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: @@ -133,13 +132,8 @@ def render_format(format): assert False, (len(subfields), field_name) yield "};" - 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 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_ft1.csv b/regs/maple_bus_ft1.csv index 1bf81a1..1f45cc3 100644 --- a/regs/maple_bus_ft1.csv +++ b/regs/maple_bus_ft1.csv @@ -24,9 +24,9 @@ "reserved_for_execution_file",,,,,,,, "reserved_for_execution_file",,,,,,,, ,,,,,,,, -"block_read_data_transfer",7,6,5,4,3,2,1,0 +"block_read_data_transfer",7,6,5,4,3,2,1,0 "pt",,,,,,,, "phase",,,,,,,, "block_number",,,,,,,, "block_number",,,,,,,, -"block_data",,,,,,,, +"block_data<0>",,,,,,,, diff --git a/regs/maple_bus_ft1.ods b/regs/maple_bus_ft1.ods index e574186..544f44f 100644 Binary files a/regs/maple_bus_ft1.ods and b/regs/maple_bus_ft1.ods differ