303 lines
8.9 KiB
C++
303 lines
8.9 KiB
C++
#include <cstdint>
|
|
|
|
#include "sh7091/serial.hpp"
|
|
#include "sh7091/serial_dma.hpp"
|
|
#include "sh7091/cache.hpp"
|
|
|
|
#include "serial_load.hpp"
|
|
#include "crc32.h"
|
|
#include "memory.hpp"
|
|
|
|
#include "align.hpp"
|
|
#include "memory.hpp"
|
|
|
|
namespace serial_load {
|
|
|
|
struct state state;
|
|
|
|
void init(uint32_t speed)
|
|
{
|
|
state.len = 0;
|
|
state.fsm_state = fsm_state::idle;
|
|
state.speed = speed & 0xff;
|
|
sh7091.DMAC.CHCR1 = 0;
|
|
serial::init(state.speed);
|
|
}
|
|
|
|
void jump_to_func(const uint32_t addr)
|
|
{
|
|
serial::string("jump to: ");
|
|
serial::integer<uint32_t>(addr);
|
|
// save our stack
|
|
asm volatile ("mov.l r14,@-r15; "
|
|
"ldc r15, gbr; "
|
|
"jsr @%0; "
|
|
"mov #0, r15; "
|
|
"stc gbr, r15; "
|
|
"mov.l @r15+,r14; "
|
|
:
|
|
: "r"(addr) /* input */
|
|
/* clobbered register */
|
|
: "r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10","r11","r12","r13","macl","mach","gbr","pr"
|
|
);
|
|
// restore our stack
|
|
}
|
|
|
|
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.reply_crc.value = 0xffffffff;
|
|
state.reply_crc.offset = dest;
|
|
}
|
|
|
|
static void poststart_read()
|
|
{
|
|
uint32_t src = state.buf.arg[0];
|
|
uint32_t size = state.buf.arg[1];
|
|
serial::send_dma(src, size);
|
|
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 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 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::_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(struct maple_poll_state& poll_state, uint8_t c)
|
|
{
|
|
state.buf.u8[state.len++] = c;
|
|
switch (state.fsm_state) {
|
|
case fsm_state::idle:
|
|
if (state.len == 16) {
|
|
for (uint32_t i = 0; i < command_list_length; i++) {
|
|
struct state_arglen_reply& sar = command_list[i];
|
|
if (state.buf.cmd == sar.command) {
|
|
uint32_t crc = crc32(&state.buf.u8[0], 12);
|
|
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]);
|
|
serial::string(reply.u8, 16);
|
|
|
|
if (state.buf.cmd == command::_read) poststart_read();
|
|
return;
|
|
} else {
|
|
// do nothing
|
|
}
|
|
}
|
|
}
|
|
// invalid command
|
|
memory::move(&state.buf.u8[0], &state.buf.u8[1], state.len - 1);
|
|
state.len -= 1;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
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.reply_crc.offset) {
|
|
uint32_t len = dar1 - state.reply_crc.offset;
|
|
const uint8_t * buf = reinterpret_cast<const uint8_t *>(state.reply_crc.offset);
|
|
const uint8_t * buf32 = reinterpret_cast<const uint8_t *>((state.reply_crc.offset / 32) * 32); // round down
|
|
|
|
// invalidate operand cache blocks for the data written by DMA, rounding up twice
|
|
for (uint32_t i = 0; i < align_32byte(len) + 32; i += 32) {
|
|
asm volatile ("ocbi @%0"
|
|
: // output
|
|
: "r" (reinterpret_cast<uint32_t>(&buf32[i])) // input
|
|
);
|
|
}
|
|
|
|
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;
|
|
|
|
// 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;
|
|
|
|
// transition to next state
|
|
state.fsm_state = fsm_state::idle;
|
|
}
|
|
}
|
|
break;
|
|
case fsm_state::jump:
|
|
{
|
|
using namespace scif;
|
|
// wait for serial transmission to end
|
|
constexpr uint32_t transmission_end = scfsr2::tend::bit_mask | scfsr2::tdfe::bit_mask;
|
|
if ((sh7091.SCIF.SCFSR2 & transmission_end) != transmission_end)
|
|
return;
|
|
|
|
// clear the cache before jumping
|
|
cache::init();
|
|
|
|
const uint32_t dest = state.buf.arg[0];
|
|
jump_to_func(dest);
|
|
|
|
// cautiously re-initialize serial; it is possible the called
|
|
// function modified serial state
|
|
sh7091.DMAC.CHCR1 = 0;
|
|
serial::init(state.speed);
|
|
|
|
// transition to next state
|
|
state.fsm_state = fsm_state::idle;
|
|
}
|
|
break;
|
|
case fsm_state::speed:
|
|
{
|
|
using namespace scif;
|
|
// wait for serial transmission to end
|
|
constexpr uint32_t transmission_end = scfsr2::tend::bit_mask | scfsr2::tdfe::bit_mask;
|
|
if ((sh7091.SCIF.SCFSR2 & transmission_end) != transmission_end)
|
|
return;
|
|
|
|
const uint32_t speed = state.buf.arg[0];
|
|
state.speed = speed & 0xff;
|
|
serial::init(state.speed);
|
|
|
|
// transition to next state
|
|
state.fsm_state = fsm_state::idle;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|