#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 "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))); 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; 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 = maple::device_request; using response_type = maple::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 serial::string("lm command send_offset "); serial::integer(writer.send_offset); serial::hexlify(send_buf, writer.send_offset); 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; serial::hexlify(recv_buf, writer.recv_offset); 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; } parse_storage_function_definition(std::byteswap(data_fields.device_id.fd[2]), state); } { using command_type = maple::get_media_info; using response_type = maple::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(" media_info:\n"); 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); } 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); } } } { 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); { 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, ap::lm_bus::_0); if (lm & ap::lm_bus::_1) do_lm_request(port, ap::lm_bus::_1); if (lm & ap::lm_bus::_2) do_lm_request(port, ap::lm_bus::_2); if (lm & ap::lm_bus::_3) do_lm_request(port, ap::lm_bus::_3); if (lm & ap::lm_bus::_4) do_lm_request(port, 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 = maple::device_request; using response_type = maple::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() { serial::init(4); do_device_request(); while (1); }