This makes it possible to change the serial baud rate without uploading a new serial transfer program. I'm not sure how useful this will be, but it is simple enough to add. The client program is also substantially improved. Sincerely I do not understand how/why this works. Experimentally, I found that feeding the ft232h data in chunks of up to roughly 384 bytes works reliably, both for reads and writes. Larger chunk sizes are (as expected) faster, but the tranfers do not appear to be consistently correct in this case. I have no logical explanation for this. The size of the ft232h FIFO is 1K each for the transmit and receive buffer respectively. This also enables RTS/CTS hardware flow control. Surprisingly, this doesn't appear to affect reliability significantly.
165 lines
3.2 KiB
C++
165 lines
3.2 KiB
C++
#include <cstdint>
|
|
|
|
#include "sh7091/sh7091.hpp"
|
|
#include "sh7091/sh7091_bits.hpp"
|
|
#include "sh7091/serial.hpp"
|
|
#include "holly/holly.hpp"
|
|
|
|
enum load_command {
|
|
CMD_NONE,
|
|
CMD_DATA, // DATA 0000 0000 {data}
|
|
CMD_JUMP, // JUMP 0000
|
|
CMD_RATE, // RATE 0000
|
|
};
|
|
|
|
struct load_state {
|
|
union {
|
|
uint8_t buf[12];
|
|
struct {
|
|
uint8_t cmd[4];
|
|
uint32_t addr1;
|
|
uint32_t addr2;
|
|
};
|
|
};
|
|
uint32_t len;
|
|
enum load_command command;
|
|
};
|
|
|
|
static struct load_state state;
|
|
|
|
void move(void *dst, const void *src, size_t n)
|
|
{
|
|
uint8_t * d = reinterpret_cast<uint8_t *>(dst);
|
|
const uint8_t * s = reinterpret_cast<const uint8_t *>(src);
|
|
|
|
if (d==s) return;
|
|
if (d<s) {
|
|
for (; n; n--) *d++ = *s++;
|
|
} else {
|
|
while (n) n--, d[n] = s[n];
|
|
}
|
|
}
|
|
|
|
void load_init()
|
|
{
|
|
state.len = 0;
|
|
state.command = CMD_NONE;
|
|
}
|
|
|
|
void debug(const char * s)
|
|
{
|
|
char c;
|
|
while ((sh7091.SCIF.SCFSR2 & scif::scfsr2::tdfe::bit_mask) == 0);
|
|
while ((c = *s++)) {
|
|
sh7091.SCIF.SCFTDR2 = (uint8_t)c;
|
|
}
|
|
}
|
|
|
|
void jump_to_func(const uint32_t addr)
|
|
{
|
|
serial::string("jump to: ");
|
|
serial::integer<uint32_t>(addr);
|
|
// save our stack
|
|
asm volatile ("ldc r15, gbr; "
|
|
"mov #0, r15; "
|
|
"jsr @%0; "
|
|
"nop; "
|
|
"stc gbr, r15; "
|
|
:
|
|
: "r"(addr) /* input */
|
|
/* clobbered register */
|
|
: "r0","r1","r2","r3","r4","r5","r6","r7","r8","r9","r10","r11","r12","macl","mach","gbr","pr"
|
|
);
|
|
// restore our stack
|
|
}
|
|
|
|
void load_recv(uint8_t c)
|
|
{
|
|
while (1) {
|
|
switch (state.command) {
|
|
case CMD_NONE:
|
|
state.buf[state.len++] = c;
|
|
if (state.len >= 4) {
|
|
if (state.buf[0] == 'D' &&
|
|
state.buf[1] == 'A' &&
|
|
state.buf[2] == 'T' &&
|
|
state.buf[3] == 'A') {
|
|
if (state.len < 12) {
|
|
return;
|
|
} else {
|
|
debug("data\n");
|
|
state.command = CMD_DATA;
|
|
return;
|
|
}
|
|
} else if (state.buf[0] == 'J' &&
|
|
state.buf[1] == 'U' &&
|
|
state.buf[2] == 'M' &&
|
|
state.buf[3] == 'P') {
|
|
if (state.len < 8) {
|
|
return;
|
|
} else {
|
|
debug("jump\n");
|
|
state.command = CMD_JUMP;
|
|
}
|
|
} else if (state.buf[0] == 'R' &&
|
|
state.buf[1] == 'A' &&
|
|
state.buf[2] == 'T' &&
|
|
state.buf[3] == 'E') {
|
|
if (state.len < 8) {
|
|
return;
|
|
} else {
|
|
debug("rate\n");
|
|
state.command = CMD_RATE;
|
|
}
|
|
} else {
|
|
move(&state.buf[0], &state.buf[1], state.len - 1);
|
|
state.len -= 1;
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
break;
|
|
case CMD_DATA:
|
|
{
|
|
uint32_t * size = &state.addr1;
|
|
uint8_t * dest = reinterpret_cast<uint8_t *>(state.addr2);
|
|
if (*size > 0) {
|
|
sh7091.SCIF.SCFTDR2 = c;
|
|
|
|
// write c to dest
|
|
*dest = c;
|
|
state.addr2++;
|
|
|
|
(*size)--;
|
|
}
|
|
if (*size == 0) {
|
|
state.len = 0;
|
|
state.command = CMD_NONE;
|
|
debug("next\n");
|
|
}
|
|
return;
|
|
break;
|
|
}
|
|
case CMD_JUMP:
|
|
// jump
|
|
state.len = 0;
|
|
state.command = CMD_NONE;
|
|
debug("prejump\n");
|
|
holly.VO_BORDER_COL = (31 << 11);
|
|
jump_to_func(state.addr1);
|
|
holly.VO_BORDER_COL = (63 << 5) | (31 << 0);
|
|
debug("postjump\n");
|
|
return;
|
|
break;
|
|
case CMD_RATE:
|
|
state.len = 0;
|
|
state.command = CMD_NONE;
|
|
debug("prerate\n");
|
|
serial::init(state.addr1 & 0xff);
|
|
debug("postrate\n");
|
|
return;
|
|
break;
|
|
}
|
|
}
|
|
}
|