example/maple_storage: initial example
This commit is contained in:
parent
be8f06e486
commit
ea51bca518
1
base.mk
1
base.mk
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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,6 +366,7 @@ 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(" media_info:\n");
|
||||||
serial::string(" total_size: ");
|
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: ");
|
||||||
@ -110,61 +390,150 @@ void do_lm_request(uint8_t port, uint8_t lm)
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
using command_type = block_read;
|
uint8_t * bufi = fat_area_data;
|
||||||
using response_type = data_transfer<ft1::block_read_data_transfer::data_format<512>>;
|
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));
|
||||||
|
|
||||||
auto writer = maple::host_command_writer(send_buf, recv_buf);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
auto [host_command, host_response]
|
{
|
||||||
= writer.append_command<command_type, response_type>(host_port_select,
|
uint8_t * bufi = file_information_data;
|
||||||
destination_ap,
|
uint16_t chain = state.media_info.file_information.block_number;
|
||||||
true); // end_flag
|
state.file_information = reinterpret_cast<storage::file_information *>(file_information_data);
|
||||||
host_command->bus_data.data_fields.function_type = std::byteswap(function_type::storage);
|
do {
|
||||||
host_command->bus_data.data_fields.pt = 0;
|
do_block_read(state, chain, bufi);
|
||||||
host_command->bus_data.data_fields.phase = 0;
|
bufi += state.device_status.bytes_per_block;
|
||||||
host_command->bus_data.data_fields.block_number = std::byteswap<uint16_t>(0xff);
|
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));
|
||||||
|
|
||||||
maple::dma_start(send_buf, writer.send_offset,
|
for (uint32_t i = 0; i < state.file_information_entries(); i++) {
|
||||||
recv_buf, writer.recv_offset);
|
if (state.file_information[i].status == storage::file_information::status::data_file
|
||||||
|
|| state.file_information[i].status == storage::file_information::status::execution_file) {
|
||||||
auto& bus_data = host_response->bus_data;
|
serial::string(" file_name: ");
|
||||||
auto& data_fields = bus_data.data_fields;
|
serial::string(state.file_information[i].file_name, 12);
|
||||||
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');
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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<uint8_t const *>(file_name),
|
||||||
|
block_size);
|
||||||
|
|
||||||
|
{
|
||||||
|
uint16_t chain = state.media_info.fat_area.block_number;
|
||||||
|
uint8_t * buf = reinterpret_cast<uint8_t *>(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<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>();
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
66
maple/storage.hpp
Normal 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
403
notes/storage-notes.txt
Normal 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
|
||||||
|
|
||||||
|
|
@ -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,6 +69,8 @@ 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))))
|
||||||
|
_, variable = parse_format_name(field_name)
|
||||||
|
if not variable:
|
||||||
size += 1
|
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,11 +132,6 @@ 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)
|
|
||||||
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)) % 4 == 0);"
|
||||||
yield f"static_assert((sizeof (struct data_format)) == {format.size});"
|
yield f"static_assert((sizeof (struct data_format)) == {format.size});"
|
||||||
yield "}"
|
yield "}"
|
||||||
|
@ -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>",,,,,,,,
|
||||||
|
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user