serial_transfer: implement "maple_raw" command

This commit is contained in:
Zack Buhman 2024-11-10 20:18:23 -06:00
parent 66f8eeeda2
commit c713a084d8
14 changed files with 571 additions and 133 deletions

View File

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

View File

@ -16,6 +16,8 @@
#include "crc32.h"
static struct serial_load::maple_poll_state poll_state __attribute__((aligned(32)));
extern uint32_t _binary_font_portfolio_6x8 __asm("_binary_font_portfolio_6x8_portfolio_6x8_data_start");
template <typename T>
@ -43,22 +45,7 @@ struct serial_error_counter {
struct serial_error_counter error_counter;
enum struct step {
IDLE = 0,
DEVICE_STATUS,
EXTENSION_DEVICE_STATUS,
EXTENSION_DEVICE_REPLY,
};
struct maple_display_poll_state {
bool want_start;
enum step step;
struct {
uint8_t ap__lm;
} port[4];
};
void send_vmu_framebuffer(maple::host_command_writer& writer, uint8_t port, uint8_t lm)
void send_vmu_framebuffer(maple::host_command_writer<>& writer, uint8_t port, uint8_t lm)
{
using command_type = maple::block_write<uint8_t[0]>;
using response_type = maple::device_reply;
@ -88,9 +75,9 @@ void send_vmu_framebuffer(maple::host_command_writer& writer, uint8_t port, uint
copy<uint8_t>(data_fields.written_data, fb, maple::display::vmu::framebuffer_size);
}
void recv_extension_device_status(struct maple_display_poll_state &state)
void recv_extension_device_status(struct serial_load::maple_poll_state &state)
{
auto writer = maple::host_command_writer(send_buf, recv_buf);
auto writer = maple::host_command_writer<>(send_buf, recv_buf);
using response_type = maple::host_response<maple::device_status::data_fields>;
auto host_response = reinterpret_cast<response_type *>(recv_buf);
@ -112,20 +99,25 @@ void recv_extension_device_status(struct maple_display_poll_state &state)
auto& bus_data = host_response[response_index++].bus_data;
auto& data_fields = bus_data.data_fields;
if ((bus_data.command_code == maple::device_status::command_code) &&
(std::byteswap(data_fields.device_id.ft) & function_type::bw_lcd)) {
last_send_offset = writer.send_offset;
send_vmu_framebuffer(writer, port, bit);
if (bus_data.command_code != maple::device_status::command_code) {
state.port[port].lm[i].device_id.ft = 0;
} else {
// this extension device is not a bw_lcd; remove it
state.port[port].ap__lm &= ~bit;
state.port[port].lm[i].device_id.ft = std::byteswap(data_fields.device_id.ft);
state.port[port].lm[i].device_id.fd[0] = std::byteswap(data_fields.device_id.fd[0]);
state.port[port].lm[i].device_id.fd[1] = std::byteswap(data_fields.device_id.fd[1]);
state.port[port].lm[i].device_id.fd[2] = std::byteswap(data_fields.device_id.fd[2]);
if (state.port[port].lm[i].device_id.ft & function_type::bw_lcd) {
last_send_offset = writer.send_offset;
send_vmu_framebuffer(writer, port, bit);
}
}
bit <<= 1;
}
}
{
// change the last command from
using command_type = maple::host_command<maple::block_write<uint8_t[0]>::data_fields>;
auto host_command = reinterpret_cast<command_type *>(&send_buf[last_send_offset / 4]);
host_command->host_instruction |= host_instruction::end_flag;
@ -135,7 +127,7 @@ void recv_extension_device_status(struct maple_display_poll_state &state)
}
}
void send_extension_device_request(maple::host_command_writer& writer, uint8_t port, uint8_t lm)
void send_extension_device_request(maple::host_command_writer<>& writer, uint8_t port, uint8_t lm)
{
uint32_t host_port_select = host_instruction_port_select(port);
uint32_t destination_ap = ap_port_select(port) | ap::de::expansion_device | lm;
@ -148,20 +140,19 @@ void send_extension_device_request(maple::host_command_writer& writer, uint8_t p
false); // end_flag
}
typedef void (* func_t)(maple::host_command_writer& writer, uint8_t port, uint8_t lm);
void do_lm_requests(maple::host_command_writer& writer, uint8_t port, uint8_t lm, func_t func)
typedef void (* func_t)(maple::host_command_writer<>& writer, uint8_t port, uint8_t lm);
void do_lm_requests(maple::host_command_writer<>& writer, uint8_t port, uint8_t lm, func_t func)
{
uint32_t bit = ap::lm_bus::_0;
for (int i = 0; i < 5; i++) {
if (lm & bit) {
lm &= ~bit;
func(writer, port, bit);
}
bit <<= 1;
}
}
void recv_device_status(struct maple_display_poll_state &state)
void recv_device_status(struct serial_load::maple_poll_state &state)
{
auto writer = maple::host_command_writer(send_buf, recv_buf);
@ -170,13 +161,17 @@ void recv_device_status(struct maple_display_poll_state &state)
for (int 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 != maple::device_status::command_code) {
state.port[port].ap__lm = 0;
state.port[port].device_id.ft = 0;
} else {
//auto& data_fields = bus_data.data_fields;
uint8_t lm = bus_data.source_ap & ap::lm_bus::bit_mask;
state.port[port].ap__lm = lm;
state.port[port].device_id.ft = std::byteswap(data_fields.device_id.ft);
state.port[port].device_id.fd[0] = std::byteswap(data_fields.device_id.fd[0]);
state.port[port].device_id.fd[1] = std::byteswap(data_fields.device_id.fd[1]);
state.port[port].device_id.fd[2] = std::byteswap(data_fields.device_id.fd[2]);
do_lm_requests(writer, port, lm, &send_extension_device_request);
}
}
@ -198,11 +193,26 @@ void send_device_request()
recv_buf, writer.recv_offset);
}
void handle_maple(struct maple_display_poll_state& state)
void send_raw(struct serial_load::maple_poll_state& state)
{
maple::dma_start(&__send_buf, state.send_length,
&__recv_buf, state.recv_length);
/*
maple::dma_start((uint32_t*)0xac000020, state.send_length,
(uint32_t*)0xac002020, state.recv_length);
*/
}
void handle_maple(struct serial_load::maple_poll_state& state)
{
using namespace serial_load;
switch (state.step) {
case step::IDLE:
if (state.want_start) {
if (state.want_raw) {
send_raw(state);
state.step = step::RAW;
} else if (state.want_start) {
// always send to all ports
send_device_request();
state.step = step::DEVICE_STATUS;
@ -212,20 +222,25 @@ void handle_maple(struct maple_display_poll_state& state)
case step::DEVICE_STATUS:
if (maple::dma_poll_complete()) {
recv_device_status(state);
state.step = step::EXTENSION_DEVICE_STATUS;
state.step = step::EXTENSION__DEVICE_STATUS;
}
break;
case step::EXTENSION_DEVICE_STATUS:
case step::EXTENSION__DEVICE_STATUS:
if (maple::dma_poll_complete()) {
recv_extension_device_status(state);
state.step = step::EXTENSION_DEVICE_REPLY;
state.step = step::EXTENSION__DEVICE_STATUS__DEVICE_REPLY;
}
break;
case step::EXTENSION_DEVICE_REPLY:
case step::EXTENSION__DEVICE_STATUS__DEVICE_REPLY:
if (maple::dma_poll_complete()) {
state.step = step::IDLE;
}
break;
case step::RAW:
if (maple::dma_poll_complete()) {
state.want_raw = 0;
state.step = step::IDLE;
}
}
}
@ -281,13 +296,16 @@ void render_u8(uint32_t src, char * dst)
for (int i = 0; i < 2; i++) dst[i] = num_buf[i];
}
static int count = 0;
void render_idle_state(char * dst)
{
render_line("idle", &dst[0]);
render_line("idle3", &dst[0]);
render_u32(serial_load::state.buf.u32[0], &dst[8]);
render_u32(serial_load::state.buf.u32[1], &dst[16]);
render_u8(serial_load::state.len, &dst[24]);
render_clear(&dst[26], 6);
render_u8(serial_load::state.len, &dst[16]);
render_clear(&dst[18], 6);
render_u32(count++, &dst[24]);
}
void render_write_state(char * dst)
@ -295,7 +313,7 @@ void render_write_state(char * dst)
render_line("write", &dst[0]);
render_u32(sh7091.DMAC.DMATCR1, &dst[8]);
render_u32(sh7091.DMAC.DAR1, &dst[16]);
render_u32(serial_load::state.write_crc.value, &dst[24]);
render_u32(serial_load::state.reply_crc.value, &dst[24]);
}
void render_read_state(char * dst)
@ -303,7 +321,7 @@ void render_read_state(char * dst)
render_line("read", &dst[0]);
render_u32(sh7091.DMAC.DMATCR1, &dst[8]);
render_u32(sh7091.DMAC.SAR1, &dst[16]);
render_u32(serial_load::state.read_crc.value, &dst[24]);
render_u32(serial_load::state.reply_crc.value, &dst[24]);
}
void render_fsm_state(char * dst)
@ -327,8 +345,25 @@ void render_fsm_state(char * dst)
render_line("speed", &dst[0]);
render_clear(&dst[8], 24);
break;
case fsm_state::maple_raw__command:
render_line("mr__cmd", &dst[0]);
render_u32(sh7091.DMAC.DAR1, &dst[8]);
render_u32(sh7091.DMAC.SAR1, &dst[16]);
render_clear(&dst[24], 8);
break;
case fsm_state::maple_raw__maple_dma:
render_line("mr__dma", &dst[0]);
render_u32(poll_state.send_length, &dst[8]);
render_u32(poll_state.recv_length, &dst[16]);
render_u8(poll_state.want_raw, &dst[24]);
render_clear(&dst[26], 6);
break;
case fsm_state::maple_raw__response:
render_line("mr__resp", &dst[0]);
render_clear(&dst[8], 24);
break;
default:
render_line("invalid", &dst[0]);
render_line("unknown", &dst[0]);
render_clear(&dst[8], 24);
break;
}
@ -362,10 +397,13 @@ void main() __attribute__((section(".text.main")));
void main()
{
poll_state.want_start = 0;
poll_state.want_raw = 0;
poll_state.step = serial_load::step::IDLE;
constexpr uint32_t serial_speed = 0;
serial_load::init(serial_speed);
struct maple_display_poll_state state = {0};
const uint8_t * font = reinterpret_cast<const uint8_t *>(&_binary_font_portfolio_6x8);
auto renderer0 = maple::display::font_renderer(font);
framebuffer[0] = renderer0.fb;
@ -384,6 +422,13 @@ void main()
error_counter.dr = 0;
error_counter.orer = 0;
/*
const uint8_t m[] = {0xe0, 0x22, 0x24, 0xb6, 0x30, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xa5, 0xce, 0x18, 0xda};
for (uint32_t i = 0; i < (sizeof (m)); i++) {
serial_load::recv(state, m[i]);
}
*/
while (1) {
using namespace scif;
@ -411,16 +456,16 @@ void main()
if (serial_load::state.fsm_state == serial_load::fsm_state::idle) {
while (sh7091.SCIF.SCFSR2 & scfsr2::rdf::bit_mask) {
const uint8_t c = sh7091.SCIF.SCFRDR2;
serial_load::recv(c);
serial_load::recv(poll_state, c);
sh7091.SCIF.SCFSR2 = scfsr2 & ~scfsr2::rdf::bit_mask;
}
}
}
serial_load::tick();
serial_load::tick(poll_state);
render(renderer0, renderer1);
state.want_start = 1;
handle_maple(state);
poll_state.want_start = 1;
handle_maple(poll_state);
}
}

7
ip.lds
View File

@ -71,8 +71,8 @@ SECTIONS
INCLUDE "debug.lds"
}
__stack_reservation = 0x1000;
__stack_end = ORIGIN(p1ram) + LENGTH(p1ram) - __stack_reservation;
__stack_end = ORIGIN(p1ram) + LENGTH(p1ram);
__text_link_start = 0;
__text_link_end = 0;
@ -97,3 +97,6 @@ __vbr_link_end = 0;
__vbr_load_end = 0;
INCLUDE "addresses.lds"
__send_buf = 0xac000020;
__recv_buf = 0xac002020;

View File

@ -3,7 +3,7 @@ MEMORY
{
p1ram : ORIGIN = 0xac005000, LENGTH = 0x0000
p2ram : ORIGIN = 0xac010000, LENGTH = 0xff0000
ldram : ORIGIN = 0xacffd000, LENGTH = 0x3000
ldram : ORIGIN = 0xacffd000, LENGTH = 0x4000
}
SECTIONS
{
@ -72,4 +72,7 @@ SECTIONS
INCLUDE "symbols.lds"
INCLUDE "addresses.lds"
__stack_end = 0x8c008000;
__stack_end = 0x8c00f000;
__send_buf = 0xac000020;
__recv_buf = 0xac002020;

View File

@ -7,4 +7,4 @@ MEMORY
INCLUDE "common.lds"
__stack_end = ORIGIN(p1ram) + LENGTH(p1ram) - 0x3000;
__stack_end = ORIGIN(p1ram) + LENGTH(p1ram) - 0x4000;

View File

@ -1,6 +1,7 @@
#pragma once
#include <cstdint>
#include <stdint.h>
#include <stddef.h>
#include <tuple>
#include "maple/maple.hpp"
@ -9,6 +10,7 @@
namespace maple {
template <uint32_t base_address = 0>
struct host_command_writer {
uint32_t * const send_buf;
uint32_t * const recv_buf;
@ -43,7 +45,13 @@ struct host_command_writer {
| (host_port_select & host_instruction::port_select::bit_mask)
| host_instruction::transfer_length(data_size / 4);
host_command->receive_data_storage_address = receive_data_storage_address::address(reinterpret_cast<uint32_t>(host_response));
uint32_t host_response_address;
if constexpr (base_address != 0) {
host_response_address = base_address + recv_offset;
} else {
host_response_address = reinterpret_cast<uint32_t>(host_response);
}
host_command->receive_data_storage_address = receive_data_storage_address::address(host_response_address);
host_command->bus_data.command_code = C::command_code;
host_command->bus_data.destination_ap = destination_ap;

View File

@ -23,3 +23,15 @@ constexpr inline uint32_t host_instruction_port_select(const uint32_t port)
case 3: return host_instruction::port_select::d;
}
}
constexpr inline uint32_t ap_lm_bus(const uint32_t lm)
{
switch (lm) {
default: [[fallthrough]];
case 0: return ap::lm_bus::_0;
case 1: return ap::lm_bus::_1;
case 2: return ap::lm_bus::_2;
case 3: return ap::lm_bus::_3;
case 4: return ap::lm_bus::_4;
}
}

View File

@ -51,33 +51,106 @@ void jump_to_func(const uint32_t addr)
// restore our stack
}
static inline void prestart_write()
static void prestart_write()
{
uint32_t dest = state.buf.arg[0];
uint32_t size = state.buf.arg[1];
serial::recv_dma(dest - 1, size + 1);
state.write_crc.value = 0xffffffff;
state.write_crc.offset = dest;
state.reply_crc.value = 0xffffffff;
state.reply_crc.offset = dest;
}
static inline void prestart_read()
static void poststart_read()
{
uint32_t src = state.buf.arg[0];
uint32_t size = state.buf.arg[1];
serial::send_dma(src, size);
state.read_crc.value = 0xffffffff;
state.read_crc.offset = src;
state.reply_crc.value = 0xffffffff;
state.reply_crc.offset = src;
}
static void prestart_maple_raw__command()
{
uint32_t dest = reinterpret_cast<uint32_t>(&__send_buf);
//uint32_t dest = 0xac000020;
uint32_t size = state.buf.arg[0];
serial::recv_dma(dest - 1, size + 1);
state.reply_crc.value = 0xffffffff;
state.reply_crc.offset = dest;
}
static void prestart_maple_raw__response()
{
uint32_t src = reinterpret_cast<uint32_t>(&__recv_buf);
//uint32_t src = 0xac002020;
uint32_t size = state.buf.arg[1];
serial::send_dma(src, size);
state.reply_crc.value = 0xffffffff;
state.reply_crc.offset = src;
}
/*
static reply::maple_list_entry maple_list[4 + 4 * 5];
static void prestart_maple_list(struct maple_port * port)
{
uint32_t function_type = state.buf.arg[0];
uint32_t list_ix = 0;
for (int port_ix = 0; port_ix < 4; port_ix++) {
if ((port[port_ix].device_id.ft & function_type) == 0) {
continue;
}
maple_list[list_ix].port = port_ix;
maple_list[list_ix].lm = 0;
maple_list[list_ix]._res = 0;
maple_list[list_ix].device_id.ft = port[port_ix].device_id.ft;
maple_list[list_ix].device_id.fd[0] = port[port_ix].device_id.fd[0];
maple_list[list_ix].device_id.fd[1] = port[port_ix].device_id.fd[1];
maple_list[list_ix].device_id.fd[2] = port[port_ix].device_id.fd[2];
list_ix++;
int bit = 1;
for (int lm_ix = 0; lm_ix < 5; lm_ix++) {
if ((port[port_ix].ap__lm & bit) != 0) {
maple_list[list_ix].port = port_ix;
maple_list[list_ix].lm = bit;
maple_list[list_ix]._res = 0;
maple_list[list_ix].device_id.ft = port[port_ix].lm[lm_ix].device_id.ft;
maple_list[list_ix].device_id.fd[0] = port[port_ix].lm[lm_ix].device_id.fd[0];
maple_list[list_ix].device_id.fd[1] = port[port_ix].lm[lm_ix].device_id.fd[1];
maple_list[list_ix].device_id.fd[2] = port[port_ix].lm[lm_ix].device_id.fd[2];
list_ix++;
}
bit <<= 1;
}
}
state.buf.arg[1] = list_ix * (sizeof (struct reply::maple_list_entry));
}
static void poststart_maple_list()
{
uint32_t src = reinterpret_cast<uint32_t>(maple_list);
uint32_t size = state.buf.arg[1];
serial::send_dma(src, size);
state.reply_crc.value = 0xffffffff;
state.reply_crc.offset = src;
}
*/
struct state_arglen_reply command_list[] = {
{command::_write, reply::_write, fsm_state::write},
{command::_read , reply::_read , fsm_state::read },
{command::_jump , reply::_jump , fsm_state::jump },
{command::_speed, reply::_speed, fsm_state::speed},
{command::_write , reply::_write , fsm_state::write },
{command::_read , reply::_read , fsm_state::read },
{command::_jump , reply::_jump , fsm_state::jump },
{command::_speed , reply::_speed , fsm_state::speed },
{command::_maple_raw , reply::_maple_raw , fsm_state::maple_raw__command },
};
constexpr uint32_t command_list_length = (sizeof (command_list)) / (sizeof (command_list[0]));
void recv(uint8_t c)
void recv(struct maple_poll_state& poll_state, uint8_t c)
{
state.buf.u8[state.len++] = c;
switch (state.fsm_state) {
@ -90,12 +163,13 @@ void recv(uint8_t c)
if (crc == state.buf.crc) {
// valid command, do the transition
if (state.buf.cmd == command::_write) prestart_write();
if (state.buf.cmd == command::_maple_raw) prestart_maple_raw__command();
state.fsm_state = sar.fsm_state;
state.len = 0;
union command_reply reply = command_reply(sar.reply, state.buf.arg[0], state.buf.arg[1]);
union command_reply reply = command_reply(sar.reply, state.buf.arg[0], state.buf.arg[1]);
serial::string(reply.u8, 16);
if (state.buf.cmd == command::_read) prestart_read();
if (state.buf.cmd == command::_read) poststart_read();
return;
} else {
// do nothing
@ -112,25 +186,66 @@ void recv(uint8_t c)
}
}
void tick()
void tick(struct maple_poll_state& poll_state)
{
switch (state.fsm_state) {
case fsm_state::idle:
break;
case fsm_state::maple_raw__command: [[fallthrough]];
case fsm_state::write:
{
// read chcr1 before dar1 to avoid race
uint32_t chcr1 = sh7091.DMAC.CHCR1;
uint32_t dar1 = sh7091.DMAC.DAR1;
if (dar1 > state.write_crc.offset) {
uint32_t len = dar1 - state.write_crc.offset;
const uint8_t * buf = reinterpret_cast<const uint8_t *>(state.write_crc.offset);
state.write_crc.value = crc32_update(state.write_crc.value, buf, len);
state.write_crc.offset += len;
if (dar1 > state.reply_crc.offset) {
uint32_t len = dar1 - state.reply_crc.offset;
const uint8_t * buf = reinterpret_cast<const uint8_t *>(state.reply_crc.offset);
state.reply_crc.value = crc32_update(state.reply_crc.value, buf, len);
state.reply_crc.offset += len;
}
if (chcr1 & dmac::chcr::te::transfers_completed) {
state.write_crc.value ^= 0xffffffff;
union command_reply reply = reply::write_crc(state.write_crc.value);
state.reply_crc.value ^= 0xffffffff;
union command_reply reply = reply::crc(state.reply_crc.value);
serial::string(reply.u8, 16);
sh7091.DMAC.CHCR1 = 0;
// transition to next state
if (state.fsm_state == fsm_state::maple_raw__command) {
poll_state.send_length = state.buf.arg[0];
poll_state.recv_length = state.buf.arg[1];
poll_state.want_raw = 1;
state.fsm_state = fsm_state::maple_raw__maple_dma;
} else
state.fsm_state = fsm_state::idle;
}
}
break;
case fsm_state::maple_raw__maple_dma:
{
// transition to next state
if (poll_state.want_raw == 0) {
prestart_maple_raw__response();
state.fsm_state = fsm_state::maple_raw__response;
}
}
break;
case fsm_state::maple_raw__response: [[fallthrough]];
case fsm_state::read:
{
// read chcr1 before sar1 to avoid race
uint32_t chcr1 = sh7091.DMAC.CHCR1;
uint32_t sar1 = sh7091.DMAC.SAR1;
if (sar1 > state.reply_crc.offset) {
uint32_t len = sar1 - state.reply_crc.offset;
const uint8_t * buf = reinterpret_cast<const uint8_t *>(state.reply_crc.offset);
state.reply_crc.value = crc32_update(state.reply_crc.value, buf, len);
state.reply_crc.offset += len;
}
if (chcr1 & dmac::chcr::te::transfers_completed) {
state.reply_crc.value ^= 0xffffffff;
union command_reply reply = reply::crc(state.reply_crc.value);
serial::string(reply.u8, 16);
sh7091.DMAC.CHCR1 = 0;
@ -140,29 +255,6 @@ void tick()
}
}
break;
case fsm_state::read:
{
uint32_t chcr1 = sh7091.DMAC.CHCR1;
uint32_t sar1 = sh7091.DMAC.SAR1;
if (sar1 > state.read_crc.offset) {
uint32_t len = sar1 - state.read_crc.offset;
const uint8_t * buf = reinterpret_cast<const uint8_t *>(state.read_crc.offset);
state.read_crc.value = crc32_update(state.read_crc.value, buf, len);
state.read_crc.offset += len;
}
if (chcr1 & dmac::chcr::te::transfers_completed) {
state.read_crc.value ^= 0xffffffff;
union command_reply reply = reply::read_crc(state.read_crc.value);
serial::string(reply.u8, 16);
sh7091.DMAC.CHCR1 = 0;
// transition to next state
//state.fsm_state = fsm_state::idle;
}
}
break;
case fsm_state::jump:
{
using namespace scif;

View File

@ -2,13 +2,14 @@
#include <cstdint>
#include "maple/maple_bus_commands.hpp"
#include "serial_protocol.hpp"
namespace serial_load {
void init(uint32_t speed);
void recv(uint8_t c);
void tick();
void recv(struct maple_poll_state& poll_state, uint8_t c);
void tick(struct maple_poll_state& poll_state);
enum struct fsm_state {
idle,
@ -16,6 +17,9 @@ enum struct fsm_state {
read,
jump,
speed,
maple_raw__command,
maple_raw__maple_dma,
maple_raw__response,
};
struct state_arglen_reply {
@ -33,11 +37,47 @@ struct state {
union command_reply buf;
uint32_t len;
enum fsm_state fsm_state;
struct incremental_crc write_crc;
struct incremental_crc read_crc;
struct incremental_crc reply_crc;
uint32_t speed;
};
extern struct state state;
struct lm {
struct maple::device_id device_id;
};
struct maple_port {
struct maple::device_id device_id;
uint8_t ap__lm;
struct lm lm[5];
};
enum struct step {
IDLE = 0,
DEVICE_STATUS,
EXTENSION__DEVICE_STATUS,
EXTENSION__DEVICE_STATUS__DEVICE_REPLY,
RAW,
};
struct maple_command_parameters {
uint8_t port;
uint8_t lm;
};
struct maple_poll_state {
uint32_t send_length;
uint32_t recv_length;
bool want_start;
bool want_raw;
enum step step;
struct serial_load::maple_port port[4];
struct maple_command_parameters command_parameters;
};
}
extern "C" uint32_t __send_buf __asm("__send_buf");
extern "C" uint32_t __recv_buf __asm("__recv_buf");

View File

@ -2,6 +2,8 @@
#include <stdint.h>
#include "maple/maple_bus_commands.hpp"
#include "crc32.h"
namespace serial_load {
@ -56,13 +58,15 @@ namespace command {
constexpr uint32_t _jump = gen_cmd("JUMP");
constexpr uint32_t _speed = gen_cmd("SPED");
constexpr uint32_t _maple_list = gen_cmd("MPLS");
constexpr uint32_t _maple_raw = gen_cmd("MPRW");
static_assert(_write == 0x2cc46ed8);
static_assert(_read == 0xf18d57c7);
static_assert(_jump == 0xa6696f38);
static_assert(_read == 0xf18d57c7);
static_assert(_jump == 0xa6696f38);
static_assert(_speed == 0x27a7a9f4);
static_assert(_maple_raw == 0xb62422e0);
constexpr union command_reply write(uint32_t dest, uint32_t size)
{
return command_reply(_write, dest, size);
@ -83,9 +87,9 @@ namespace command {
return command_reply(_speed, speed, 0);
}
constexpr union command_reply maple_list(uint32_t function_type)
constexpr union command_reply maple_raw(uint32_t send_size, uint32_t recv_size)
{
return command_reply(_maple_list, function_type, 0);
return command_reply(_maple_raw, send_size, recv_size);
}
}
@ -94,18 +98,19 @@ namespace reply {
constexpr uint32_t _read = gen_cmd("read");
constexpr uint32_t _jump = gen_cmd("jump");
constexpr uint32_t _speed = gen_cmd("sped");
constexpr uint32_t _write_crc = gen_cmd("crcw");
constexpr uint32_t _read_crc = gen_cmd("crcr");
constexpr uint32_t _maple_list = gen_cmd("mpls");
constexpr uint32_t _maple_list_crc = gen_cmd("mlcs");
constexpr uint32_t _maple_raw = gen_cmd("mprw");
constexpr uint32_t _crc = gen_cmd("rcrc");
static_assert(_write == 0x8c661aaa);
static_assert(_read == 0x512f23b5);
static_assert(_jump == 0x06cb1b4a);
static_assert(_speed == 0x8705dd86);
static_assert(_write_crc == 0x3cccc074);
static_assert(_read_crc == 0x99cc92f4);
static_assert(_maple_raw == 0x16865692);
static_assert(_crc == 0xcc9aab7c);
constexpr union command_reply write(uint32_t dest, uint32_t size)
{
@ -127,14 +132,14 @@ namespace reply {
return command_reply(_speed, speed, 0);
}
constexpr union command_reply write_crc(uint32_t crc)
constexpr union command_reply crc(uint32_t crc)
{
return command_reply(_write_crc, crc, 0);
return command_reply(_crc, crc, 0);
}
constexpr union command_reply read_crc(uint32_t crc)
constexpr union command_reply maple_raw(uint32_t send_size, uint32_t recv_size)
{
return command_reply(_read_crc, crc, 0);
return command_reply(_maple_raw, send_size, recv_size);
}
}

View File

@ -99,6 +99,8 @@ void string(const char * s)
void string(const uint8_t * s, uint32_t len)
{
while (len > 0) {
//hexlify(*s++);
//character(' ');
character(*s++);
len--;
}

View File

@ -19,6 +19,7 @@ commands = [
"SPED",
"MPLS",
"MPRW",
]
replies = [
@ -26,11 +27,11 @@ replies = [
"read",
"jump",
"sped",
"crcw",
"crcr",
"mpls",
"mlcs",
"mprw",
"rcrc"
]
seen = set()

View File

@ -15,6 +15,11 @@
#include "crc32.h"
#include "serial_protocol.hpp"
#include "maple/maple_bus_bits.hpp"
#include "maple/maple.hpp"
#include "maple/maple_host_command_writer.hpp"
#include "maple/maple_bus_commands.hpp"
extern "C" int convert_baudrate_UT_export(int baudrate, struct ftdi_context *ftdi,
unsigned short *value, unsigned short *index);
@ -190,6 +195,10 @@ void dump_command_reply(union serial_load::command_reply& cr)
for (uint32_t i = 0; i < (sizeof (union serial_load::command_reply)) / (sizeof (uint32_t)); i++) {
fprintf(stderr, " %08x\n", serial_load::le_bswap(cr.u32[i]));
}
for (uint32_t i = 0; i < (sizeof (union serial_load::command_reply)); i++) {
fprintf(stderr, "%02x ", cr.u8[i]);
}
fprintf(stderr, "\n");
}
int read_reply(struct ftdi_context * ftdi, uint32_t expected_cmd, union serial_load::command_reply& reply)
@ -200,7 +209,7 @@ int read_reply(struct ftdi_context * ftdi, uint32_t expected_cmd, union serial_l
long length = read_with_timeout(ftdi, reply.u8, read_length);
if (length != read_length) {
fprintf(stderr, "short read; want %ld bytes; received: %ld\n", read_length, length);
fprintf(stderr, "read_reply: short read; want %ld bytes; received: %ld\n", read_length, length);
return -1;
}
@ -273,7 +282,7 @@ int do_write(struct ftdi_context * ftdi, const uint32_t dest, const uint8_t * bu
uint32_t buf_crc = crc32(buf, size);
union serial_load::command_reply crc_reply;
res = read_reply(ftdi, serial_load::reply::_write_crc, crc_reply);
res = read_reply(ftdi, serial_load::reply::_crc, crc_reply);
clock_res = clock_gettime(CLOCK_MONOTONIC, &end2);
assert(clock_res == 0);
if (res != 0) {
@ -300,6 +309,10 @@ int do_write(struct ftdi_context * ftdi, const uint32_t dest, const uint8_t * bu
fprintf(stderr, " idealized write time : %.03f seconds\n", idealized_time);
fprintf(stderr, " measured write time : %.03f seconds\n", measured_time2);
if (crc_reply.arg[0] != buf_crc) {
return -1;
}
return 0;
}
@ -471,22 +484,25 @@ int do_read(struct ftdi_context * ftdi, const uint32_t src, uint8_t * buf, const
uint32_t read_length = 0;
while (read_length < size) {
res = ftdi_read_data(ftdi, (uint8_t *)buf, size - read_length);
res = ftdi_read_data(ftdi, (uint8_t *)&buf[read_length], size - read_length);
assert(res >= 0);
read_length += res;
if (read_length < size)
fprintf(stderr, "short read; want %x out of %x\n", size - read_length, size);
fprintf(stderr, "read: short read; want %x out of %x\n", size - read_length, size);
}
uint32_t buf_crc = crc32((uint8_t*)buf, size);
union serial_load::command_reply crc_reply;
res = read_reply(ftdi, serial_load::reply::_read_crc, crc_reply);
res = read_reply(ftdi, serial_load::reply::_crc, crc_reply);
if (res != 0) {
return -1;
}
fprintf(stderr, "remote crc: %08x; local crc %08x\n", crc_reply.arg[0], buf_crc);
fprintf(stderr, "remote crc: %08x; local crc %08x\n", crc_reply.arg[0], buf_crc);
if (crc_reply.arg[0] != buf_crc) {
return -1;
}
return 0;
}
@ -562,6 +578,214 @@ void do_console(struct ftdi_context * ftdi)
}
}
int lm_bit_to_int(uint8_t bit)
{
switch (bit) {
case 0b00001:
return 0;
case 0b00010:
return 1;
case 0b00100:
return 2;
case 0b01000:
return 3;
case 0b10000:
return 4;
default:
return -1;
}
}
constexpr int count_left_set_bits(uint32_t n, int stop_bit)
{
int bit_ix = 31;
int count = 0;
while (bit_ix != stop_bit) {
if (n & (1 << 31)) {
count += 1;
}
n <<= 1;
bit_ix -= 1;
}
return count;
}
static_assert(count_left_set_bits(0xe, 1) == 2);
static_assert(count_left_set_bits(0x2, 1) == 0);
consteval int count_trailing_zeros(uint32_t n)
{
int count = 0;
for (int i = 0; i < 32; i++) {
if ((n & 1) != 0)
break;
count += 1;
n >>= 1;
}
return count;
}
static_assert(count_trailing_zeros(0x80) == 7);
static_assert(count_trailing_zeros(0x2) == 1);
void print_storage_function_definition(const uint32_t fd)
{
int partitions = ((fd >> 24) & 0xff) + 1;
int bytes_per_block = (((fd >> 16) & 0xff) + 1) * 32;
int write_accesses = (fd >> 12) & 0xf;
int read_accesses = (fd >> 8) & 0xf;
fprintf(stderr, " storage function definition:\n");
fprintf(stderr, " partitions: %d\n", partitions);
fprintf(stderr, " bytes_per_block: %d\n", bytes_per_block);
fprintf(stderr, " write_accesses: %d\n", write_accesses);
fprintf(stderr, " read_accesses: %d\n", read_accesses);
}
void print_device_id(struct maple::device_id& device_id)
{
fprintf(stderr, " ft: %08x\n", device_id.ft);
fprintf(stderr, " fd[0]: %08x\n", device_id.fd[0]);
fprintf(stderr, " fd[1]: %08x\n", device_id.fd[1]);
fprintf(stderr, " fd[2]: %08x\n", device_id.fd[2]);
if (device_id.ft & function_type::storage) {
int fd_ix = count_left_set_bits(device_id.ft, count_trailing_zeros(function_type::storage));
print_storage_function_definition(device_id.fd[fd_ix]);
}
}
int do_maple_raw(struct ftdi_context * ftdi,
uint8_t * send_buf,
uint32_t send_size,
uint8_t * recv_buf,
uint32_t recv_size)
{
int res;
union serial_load::command_reply command = serial_load::command::maple_raw(send_size, recv_size);
dump_command_reply(command);
res = ftdi_write_data(ftdi, command.u8, (sizeof (command)));
assert(res == (sizeof (command)));
union serial_load::command_reply reply;
fprintf(stderr, "maple_raw: wait maple_raw reply\n");
res = read_reply(ftdi, serial_load::reply::_maple_raw, reply);
if (res != 0) {
return -2;
}
if (reply.arg[0] != command.arg[0] || reply.arg[1] != command.arg[1]) {
fprintf(stderr, "maple_raw: argument mismatch: (%08x, %08x) != (%08x, %08x)\n",
reply.arg[0], reply.arg[1],
command.arg[0], command.arg[1]);
return -1;
}
res = ftdi_write_data(ftdi, send_buf, send_size);
assert(res >= 0);
assert((uint32_t)res == send_size);
uint32_t send_buf_crc = crc32(send_buf, send_size);
fprintf(stderr, "send_size: %d\n", send_size);
for (uint32_t i = 0; i < send_size; i++) {
fprintf(stderr, "%02x ", send_buf[i]);
if (i % 4 == 3)
fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
union serial_load::command_reply send_crc_reply;
fprintf(stderr, "maple_raw: send: wait crc reply\n");
res = read_reply(ftdi, serial_load::reply::_crc, send_crc_reply);
if (res != 0) {
return -1;
}
fprintf(stderr, "maple_raw: send: remote crc: %08x; local crc %08x\n", send_crc_reply.arg[0], send_buf_crc);
if (send_crc_reply.arg[0] != send_buf_crc) {
dump_command_reply(send_crc_reply);
return -1;
}
uint32_t read_length = 0;
while (read_length < recv_size) {
res = ftdi_read_data(ftdi, &recv_buf[read_length], recv_size - read_length);
assert(res >= 0);
read_length += res;
if (read_length < recv_size)
fprintf(stderr, "maple raw: short read; want %x out of %x\n", recv_size - read_length, recv_size);
}
uint32_t recv_buf_crc = crc32(recv_buf, recv_size);
union serial_load::command_reply recv_crc_reply;
fprintf(stderr, "maple_raw: recv: wait crc reply\n");
res = read_reply(ftdi, serial_load::reply::_crc, recv_crc_reply);
if (res != 0) {
return -1;
}
fprintf(stderr, "maple_raw: recv: remote crc: %08x; local crc %08x\n", recv_crc_reply.arg[0], recv_buf_crc);
if (recv_crc_reply.arg[0] != recv_buf_crc) {
return -1;
}
return 0;
}
int do_maple_list(struct ftdi_context * ftdi)
{
uint8_t send_buf[1024] = {0};
uint8_t recv_buf[1024] = {0};
using command_type = maple::device_request;
using response_type = maple::device_status;
auto writer = maple::host_command_writer<0xac002020>(reinterpret_cast<uint32_t *>(send_buf), reinterpret_cast<uint32_t *>(recv_buf));
auto [host_command, host_response]
= writer.append_command_all_ports<command_type, response_type>();
for (uint32_t i = 0; i < writer.send_offset; i++) {
fprintf(stderr, "%02x ", send_buf[i]);
}
fprintf(stderr, "\n");
int res = do_maple_raw(ftdi,
send_buf, writer.send_offset,
recv_buf, writer.recv_offset);
if (res != 0) {
return -1;
}
for (uint32_t i = 0; i < writer.recv_offset; i++) {
fprintf(stderr, "%02x ", recv_buf[i]);
if (i % 4 == 3)
fprintf(stderr, "\n");
}
fprintf(stderr, "\n");
fprintf(stderr, "%d\n", response_type::command_code);
fprintf(stderr, "%p\n", host_response);
fprintf(stderr, "%p\n", recv_buf);
for (uint8_t port = 0; port < 4; port++) {
auto& bus_data = host_response[port].bus_data;
auto& data_fields = bus_data.data_fields;
fprintf(stderr, "port: %d\n", port);
if (bus_data.command_code != response_type::command_code) {
fprintf(stderr, " disconnected %02x %02x %02x %02x\n",
bus_data.command_code,
bus_data.destination_ap,
bus_data.source_ap,
bus_data.data_size);
} else {
fprintf(stderr, " ft: %08x\n", std::byteswap(data_fields.device_id.ft));
fprintf(stderr, " fd[0]: %08x\n", std::byteswap(data_fields.device_id.fd[0]));
fprintf(stderr, " fd[1]: %08x\n", std::byteswap(data_fields.device_id.fd[1]));
fprintf(stderr, " fd[2]: %08x\n", std::byteswap(data_fields.device_id.fd[2]));
fprintf(stderr, " source_ap.lm_bus: %d\n", bus_data.source_ap & ap::lm_bus::bit_mask);
}
}
return 0;
}
enum struct argument_type {
string,
integer
@ -581,6 +805,7 @@ struct cli_command commands[] = {
{ "list_baudrates" , 1, (void *)&do_list_baudrates },
{ "show_baudrate_error", 1, (void *)&do_show_baudrate_error },
{ "console" , 0, (void *)&do_console },
{ "maple_list" , 0, (void *)&do_maple_list },
};
constexpr int commands_length = (sizeof (commands)) / (sizeof (commands[0]));

View File

@ -1,5 +1,7 @@
#!/bin/sh
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
filename="$1"
if [ -z "$filename" ]; then
@ -9,7 +11,7 @@ fi
set -ex
./ftdi_transfer \
${SCRIPT_DIR}/ftdi_transfer \
write 0xac010000 "$filename" \
jump 0xac010000 \
console
jump 0xac010000
# console