example/maple_storage: initial example

This commit is contained in:
Zack Buhman 2024-05-23 11:46:39 -05:00
parent be8f06e486
commit ea51bca518
12 changed files with 932 additions and 95 deletions

View File

@ -7,6 +7,7 @@ CFLAGS += -Wall -Werror -Wfatal-errors
CFLAGS += -Wno-array-bounds CFLAGS += -Wno-array-bounds
#CFLAGS += -Wno-error=narrowing -Wno-error=unused-variable -Wno-error=array-bounds= #CFLAGS += -Wno-error=narrowing -Wno-error=unused-variable -Wno-error=array-bounds=
CFLAGS += -Wno-error=maybe-uninitialized CFLAGS += -Wno-error=maybe-uninitialized
CFLAGS += -Wno-error=unused-but-set-variable
CXXFLAGS += -fno-exceptions -fno-non-call-exceptions -fno-rtti -fno-threadsafe-statics CXXFLAGS += -fno-exceptions -fno-non-call-exceptions -fno-rtti -fno-threadsafe-statics

View File

@ -2,7 +2,7 @@ MAKEFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
DIR := $(dir $(MAKEFILE_PATH)) DIR := $(dir $(MAKEFILE_PATH))
LIB ?= . LIB ?= .
OPT ?= -Os OPT ?= -O3
GENERATED ?= GENERATED ?=
AARCH = --isa=sh4 --little AARCH = --isa=sh4 --little

View File

@ -290,7 +290,8 @@ MAPLE_STORAGE_OBJ = \
example/maple_storage.o \ example/maple_storage.o \
holly/video_output.o \ holly/video_output.o \
sh7091/serial.o \ sh7091/serial.o \
maple/maple.o maple/maple.o \
$(LIBGCC)
example/maple_storage.elf: LDSCRIPT = $(LIB)/main.lds example/maple_storage.elf: LDSCRIPT = $(LIB)/main.lds
example/maple_storage.elf: $(START_OBJ) $(MAPLE_STORAGE_OBJ) example/maple_storage.elf: $(START_OBJ) $(MAPLE_STORAGE_OBJ)

View File

@ -10,10 +10,281 @@
#include "maple/maple_bus_commands.hpp" #include "maple/maple_bus_commands.hpp"
#include "maple/maple_bus_ft1.hpp" #include "maple/maple_bus_ft1.hpp"
#include "maple/maple_host_command_writer.hpp" #include "maple/maple_host_command_writer.hpp"
#include "maple/storage.hpp"
#include "sh7091/serial.hpp" #include "sh7091/serial.hpp"
#include "systembus.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 <typename T>
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<ft1::block_read_data_transfer::data_format>::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<response_type::data_fields> * 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<command_type, response_type>(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<uint16_t>(block_number);
host_responses[phase] = host_response;
//serial::string("read phase: ");
//serial::integer<uint8_t>(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<uint8_t[0]>::command_code) {
auto& file_error = bus_data.data_fields.file_error;
serial::string("lm did not reply block_read: ");
serial::integer<uint8_t>(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<uint8_t>(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<uint8_t[0]>;
using response_type = maple::device_reply;
auto [host_command, host_response]
= writer.append_command<command_type, response_type>(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<uint16_t>(block_number);
copy<uint8_t>(data_fields.written_data, src, state.bytes_per_write_access());
src += state.bytes_per_write_access();
//serial::string("write phase: ");
//serial::integer<uint8_t>(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<command_type, response_type>(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<uint16_t>(block_number);
//serial::string("write phase: ");
//serial::integer<uint8_t>(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<uint16_t>(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<uint8_t>(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) void do_lm_request(uint8_t port, uint8_t lm)
{ {
uint32_t send_buf[1024] __attribute__((aligned(32))); 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 host_port_select = host_instruction_port_select(port);
const uint32_t destination_ap = ap_port_select(port) | ap::de::expansion_device | lm; 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 command_type = maple::device_request;
using response_type = device_status; using response_type = maple::device_status;
auto writer = maple::host_command_writer(send_buf, recv_buf); 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) { if ((std::byteswap(data_fields.device_id.ft) & function_type::storage) == 0) {
return; return;
} }
parse_storage_function_definition(std::byteswap(data_fields.device_id.fd[2]), state);
} }
{ {
using command_type = get_media_info; using command_type = maple::get_media_info;
using response_type = data_transfer<ft1::get_media_info_data_transfer::data_format>; using response_type = maple::data_transfer<ft1::get_media_info_data_transfer::data_format>;
auto writer = maple::host_command_writer(send_buf, recv_buf); 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<uint8_t>(port, ' '); serial::integer<uint8_t>(port, ' ');
serial::integer<uint8_t>(lm); serial::integer<uint8_t>(lm);
} else { } else {
serial::string(" total_size: "); serial::string(" media_info:\n");
serial::string(" total_size: ");
serial::integer<uint32_t>(data.total_size); serial::integer<uint32_t>(data.total_size);
serial::string(" partition_number: "); serial::string(" partition_number: ");
serial::integer<uint32_t>(data.partition_number); serial::integer<uint32_t>(data.partition_number);
serial::string(" system_area_block_number: "); serial::string(" system_area_block_number: ");
serial::integer<uint32_t>(data.system_area_block_number); serial::integer<uint32_t>(data.system_area_block_number);
serial::string(" fat_area_block_number: "); serial::string(" fat_area_block_number: ");
serial::integer<uint32_t>(data.fat_area_block_number); serial::integer<uint32_t>(data.fat_area_block_number);
serial::string(" number_of_fat_area_blocks: "); serial::string(" number_of_fat_area_blocks: ");
serial::integer<uint32_t>(data.number_of_fat_area_blocks); serial::integer<uint32_t>(data.number_of_fat_area_blocks);
serial::string(" file_information_block_number: "); serial::string(" file_information_block_number: ");
serial::integer<uint32_t>(data.file_information_block_number); serial::integer<uint32_t>(data.file_information_block_number);
serial::string(" number_of_file_information_blocks: "); serial::string(" number_of_file_information_blocks: ");
serial::integer<uint32_t>(data.number_of_file_information_blocks); serial::integer<uint32_t>(data.number_of_file_information_blocks);
serial::string(" volume_icon: "); serial::string(" volume_icon: ");
serial::integer<uint32_t>(data.volume_icon); serial::integer<uint32_t>(data.volume_icon);
serial::string(" save_area_block_number: "); serial::string(" save_area_block_number: ");
serial::integer<uint32_t>(data.save_area_block_number); serial::integer<uint32_t>(data.save_area_block_number);
serial::string(" number_of_save_area_blocks: "); serial::string(" number_of_save_area_blocks: ");
serial::integer<uint32_t>(data.number_of_save_area_blocks); serial::integer<uint32_t>(data.number_of_save_area_blocks);
serial::string(" reserved_for_execution_file: "); serial::string(" reserved_for_execution_file: ");
serial::integer<uint32_t>(data.reserved_for_execution_file); serial::integer<uint32_t>(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<uint8_t *>(&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<storage::fat_area *>(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<uint16_t>(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<storage::file_information *>(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<uint16_t>(state.file_information[i].status);
serial::string(" start_fat: ");
serial::integer<uint16_t>(state.file_information[i].start_fat);
serial::string(" block_size: ");
serial::integer<uint16_t>(state.file_information[i].block_size);
}
}
} }
{ {
using command_type = block_read; uint16_t block_size = 3;
using response_type = data_transfer<ft1::block_read_data_transfer::data_format<512>>; 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<uint8_t const *>(file_name),
block_size);
auto writer = maple::host_command_writer(send_buf, recv_buf); {
uint16_t chain = state.media_info.fat_area.block_number;
auto [host_command, host_response] uint8_t * buf = reinterpret_cast<uint8_t *>(state.fat_area);
= writer.append_command<command_type, response_type>(host_port_select, do {
destination_ap, do_block_write(state, chain, buf);
true); // end_flag buf += state.device_status.bytes_per_block;
host_command->bus_data.data_fields.function_type = std::byteswap(function_type::storage); chain = state.fat_area->fat_number[chain];
host_command->bus_data.data_fields.pt = 0; } while (chain != storage::fat_area::data::data_end);
host_command->bus_data.data_fields.phase = 0;
host_command->bus_data.data_fields.block_number = std::byteswap<uint16_t>(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<uint8_t>(port, ' ');
serial::integer<uint8_t>(lm, ' ');
serial::integer<uint8_t>(bus_data.command_code);
auto error = reinterpret_cast<file_error::data_fields *>(&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.file_information.block_number;
uint8_t * buf = reinterpret_cast<uint8_t *>(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<storage::fat_area *>(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<uint16_t>(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) void do_lm_requests(uint8_t port, uint8_t lm)
{ {
if (lm & ap::lm_bus::_0) 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) 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) 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) 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) 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() void do_device_request()
@ -174,8 +543,8 @@ void do_device_request()
auto writer = maple::host_command_writer(send_buf, recv_buf); auto writer = maple::host_command_writer(send_buf, recv_buf);
using command_type = device_request; using command_type = maple::device_request;
using response_type = device_status; using response_type = maple::device_status;
auto [host_command, host_response] auto [host_command, host_response]
= writer.append_command_all_ports<command_type, response_type>(); = writer.append_command_all_ports<command_type, response_type>();

View File

@ -2,6 +2,8 @@
#include <cstdint> #include <cstdint>
namespace maple {
struct device_id { struct device_id {
uint32_t ft; uint32_t ft;
uint32_t fd[3]; uint32_t fd[3];
@ -276,3 +278,4 @@ struct ar_error {
static_assert((sizeof (struct ar_error::data_fields)) == 4); static_assert((sizeof (struct ar_error::data_fields)) == 4);
}

View File

@ -20,15 +20,14 @@ namespace ft1 {
} }
namespace block_read_data_transfer { namespace block_read_data_transfer {
template <int n>
struct data_format { struct data_format {
uint8_t pt; uint8_t pt;
uint8_t phase; uint8_t phase;
uint16_t block_number; 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)) % 4 == 0);
static_assert((sizeof (struct data_format<0>)) == 4); static_assert((sizeof (struct data_format)) == 4);
} }
} }

View File

@ -20,20 +20,21 @@ struct host_command_writer {
: send_buf(send_buf), recv_buf(recv_buf), send_offset(0), recv_offset(0) : send_buf(send_buf), recv_buf(recv_buf), send_offset(0), recv_offset(0)
{ } { }
template <typename C, typename R, int data_fields_trailing = 0> template <typename C, typename R>
constexpr inline std::tuple<maple::host_command<typename C::data_fields> *, constexpr inline std::tuple<maple::host_command<typename C::data_fields> *,
maple::host_response<typename R::data_fields> *> maple::host_response<typename R::data_fields> *>
append_command(uint32_t host_port_select, append_command(uint32_t host_port_select,
uint32_t destination_ap, 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<typename C::data_fields>; using command_type = maple::host_command<typename C::data_fields>;
using response_type = maple::host_response<typename R::data_fields>; using response_type = maple::host_response<typename R::data_fields>;
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 (command_type)) % 4 == 0);
static_assert((sizeof (response_type)) % 4 == 0); static_assert((sizeof (response_type)) % 4 == 0);
static_assert(data_size % 4 == 0);
auto host_command = reinterpret_cast<command_type *>(&send_buf[send_offset / 4]); auto host_command = reinterpret_cast<command_type *>(&send_buf[send_offset / 4]);
auto host_response = reinterpret_cast<response_type *>(&recv_buf[recv_offset / 4]); auto host_response = reinterpret_cast<response_type *>(&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.source_ap = destination_ap & ap::port_select::bit_mask;
host_command->bus_data.data_size = data_size / 4; host_command->bus_data.data_size = data_size / 4;
send_offset += (sizeof (command_type)) + data_fields_trailing; send_offset += (sizeof (command_type)) + send_trailing;
recv_offset += (sizeof (response_type)); recv_offset += (sizeof (response_type)) + recv_trailing;
return {host_command, host_response}; return {host_command, host_response};
} }
@ -61,10 +62,10 @@ struct host_command_writer {
maple::host_response<typename R::data_fields> *> maple::host_response<typename R::data_fields> *>
append_command_all_ports() append_command_all_ports()
{ {
auto ret = append_command<C, R, 0>(host_instruction::port_select::a, ap::de::device | ap::port_select::a, false); auto ret = append_command<C, R>(host_instruction::port_select::a, ap::de::device | ap::port_select::a, false);
append_command<C, R, 0>(host_instruction::port_select::b, ap::de::device | ap::port_select::b, false); append_command<C, R>(host_instruction::port_select::b, ap::de::device | ap::port_select::b, false);
append_command<C, R, 0>(host_instruction::port_select::c, ap::de::device | ap::port_select::c, false); append_command<C, R>(host_instruction::port_select::c, ap::de::device | ap::port_select::c, false);
append_command<C, R, 0>(host_instruction::port_select::d, ap::de::device | ap::port_select::d, true); append_command<C, R>(host_instruction::port_select::d, ap::de::device | ap::port_select::d, true);
return ret; return ret;
} }
}; };

66
maple/storage.hpp Normal file
View File

@ -0,0 +1,66 @@
#include <cstdint>
#include <cstddef>
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);
}

403
notes/storage-notes.txt Normal file
View File

@ -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

View File

@ -35,6 +35,14 @@ def parse_bits(bits: list[str]):
position=min(indicies), position=min(indicies),
) )
def parse_format_name(name):
if '<' in name:
head, middle, tail = re.split('[<>]', name)
assert tail == "", name
return head, middle
else:
return name, None
def parse_data_format(ix, rows): def parse_data_format(ix, rows):
if ix >= len(rows): if ix >= len(rows):
return None return None
@ -61,7 +69,9 @@ def parse_data_format(ix, rows):
assert len(bits) in {0, 8}, bits assert len(bits) in {0, 8}, bits
fields[field_name].append(Field(field_name, fields[field_name].append(Field(field_name,
list(parse_bits(bits)))) list(parse_bits(bits))))
size += 1 _, variable = parse_format_name(field_name)
if not variable:
size += 1
if field_name not in field_order: if field_name not in field_order:
field_order.append(field_name) field_order.append(field_name)
@ -81,17 +91,8 @@ def parse(rows):
assert len(formats) > 0 assert len(formats) > 0
return formats 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): 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: for field_name in format.field_order:
subfields = format.fields[field_name] subfields = format.fields[field_name]
if not any(field.bits != [] for field in subfields): if not any(field.bits != [] for field in subfields):
@ -110,8 +111,6 @@ def render_format(format):
yield "}" yield "}"
yield "" yield ""
if variable is not None:
yield f"template <int {variable}>"
yield f"struct data_format {{" yield f"struct data_format {{"
for _field_name in format.field_order: for _field_name in format.field_order:
@ -133,13 +132,8 @@ def render_format(format):
assert False, (len(subfields), field_name) assert False, (len(subfields), field_name)
yield "};" yield "};"
format_name, variable = parse_format_name(format.name) yield f"static_assert((sizeof (struct data_format)) % 4 == 0);"
if variable is not None: yield f"static_assert((sizeof (struct data_format)) == {format.size});"
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 "}" yield "}"
def render_formats(name, formats): def render_formats(name, formats):

View File

@ -24,9 +24,9 @@
"reserved_for_execution_file",,,,,,,, "reserved_for_execution_file",,,,,,,,
"reserved_for_execution_file",,,,,,,, "reserved_for_execution_file",,,,,,,,
,,,,,,,, ,,,,,,,,
"block_read_data_transfer<n>",7,6,5,4,3,2,1,0 "block_read_data_transfer",7,6,5,4,3,2,1,0
"pt",,,,,,,, "pt",,,,,,,,
"phase",,,,,,,, "phase",,,,,,,,
"block_number",,,,,,,, "block_number",,,,,,,,
"block_number",,,,,,,, "block_number",,,,,,,,
"block_data<n>",,,,,,,, "block_data<0>",,,,,,,,

1 get_media_info_data_transfer 7 6 5 4 3 2 1 0
24 reserved_for_execution_file
25 reserved_for_execution_file
26
27 block_read_data_transfer<n> block_read_data_transfer 7 6 5 4 3 2 1 0
28 pt
29 phase
30 block_number
31 block_number
32 block_data<n> block_data<0>

Binary file not shown.