serial_transfer: fully asynchronous maple display write

This commit is contained in:
Zack Buhman 2024-10-22 04:19:37 -05:00
parent 1ecd87495f
commit b133879f7e
16 changed files with 640 additions and 143 deletions

View File

@ -2,10 +2,12 @@ import serial
import struct
import sys
import time
import fcntl
import os
#dest = 0xac21_0000
dest = 0xac02_0000
#dest = 0xac01_0000
#dest = 0xac02_0000
dest = 0xac01_0000
ret = []
@ -71,7 +73,7 @@ def start_data(ser, b):
size = len(b)
args = struct.pack("<II", size, dest)
ret = sync(ser, b'DATA' + args)
if ret != b'data\n':
if ret != b'data\n' and ret != b'\ndata\n' and ret != b'\x00data\n':
print(".", end=' ')
print(ret)
time.sleep(1)
@ -100,9 +102,9 @@ def do(ser, b):
return
args = struct.pack("<I", dest)
print("JUMP")
ret = sync(ser, b'JUMP' + args, wait=1)
print()
ret = sync(ser, b'JUMP' + args, wait=0)
print("JUMP", ret)
print("console:")
console(ser)
seen_length = 16
@ -122,9 +124,19 @@ def console(ser):
global framebuffer_mode
global framebuffer
fd = fcntl.fcntl(sys.stdin, fcntl.F_GETFL)
fcntl.fcntl(sys.stdin, fcntl.F_SETFL, fd | os.O_NONBLOCK)
seen = [0] * seen_length
seen_ix = 0
while True:
while True:
c = sys.stdin.read(1)
if not c:
break
print("write", repr(c))
ser.write(c.encode('utf-8'))
b = ser.read(ser.in_waiting)
if b == b'':
continue
@ -195,6 +207,11 @@ with serial.Serial(port='/dev/ttyUSB0',
rtscts=False,
#rtscts=True,
) as ser:
ser.read(ser.in_waiting)
ser.read(ser.in_waiting)
ser.reset_input_buffer()
ser.reset_output_buffer()
ser.read(ser.in_waiting)
#console(ser)
print("waiting: ", end=' ')
sys.stdout.flush()

View File

@ -31,7 +31,7 @@ MACAW_OBJ = \
holly/ta_fifo_polygon_converter.o \
holly/video_output.o \
sh7091/serial.o \
macaw.data.o
texture/macaw/macaw.data.o
example/macaw.elf: LDSCRIPT = $(LIB)/main.lds
example/macaw.elf: $(START_OBJ) $(MACAW_OBJ)
@ -150,7 +150,8 @@ WIFFLE_ATTENUATION_OBJ = \
holly/core.o \
holly/region_array.o \
holly/background.o \
holly/ta_fifo_polygon_converter.o
holly/ta_fifo_polygon_converter.o \
sh7091/serial.o
example/wiffle_attenuation.elf: LDSCRIPT = $(LIB)/main.lds
example/wiffle_attenuation.elf: $(START_OBJ) $(WIFFLE_ATTENUATION_OBJ)
@ -308,6 +309,17 @@ MAPLE_WINK_OBJ = \
example/maple_wink.elf: LDSCRIPT = $(LIB)/main.lds
example/maple_wink.elf: $(START_OBJ) $(MAPLE_WINK_OBJ)
MAPLE_FONT_OBJ = \
example/maple_font.o \
holly/video_output.o \
rgb.o \
sh7091/serial.o \
maple/maple.o \
font/portfolio_6x8/portfolio_6x8.data.o
example/maple_font.elf: LDSCRIPT = $(LIB)/main.lds
example/maple_font.elf: $(START_OBJ) $(MAPLE_FONT_OBJ)
MAPLE_VIBRATOR_OBJ = \
example/maple_vibrator.o \
holly/video_output.o \
@ -346,11 +358,20 @@ example/maple_mouse.elf: $(START_OBJ) $(MAPLE_MOUSE_OBJ)
SERIAL_TRANSFER_OBJ = \
example/serial_transfer.o \
sh7091/serial.o \
serial_load.o
serial_load.o \
maple/maple.o \
font/portfolio_6x8/portfolio_6x8.data.o
example/serial_transfer.elf: LDSCRIPT = $(LIB)/loader.lds
example/serial_transfer.elf: $(START_OBJ) $(SERIAL_TRANSFER_OBJ)
SERIAL_DMA_OBJ = \
example/serial_dma.o \
sh7091/serial.o
example/serial_dma.elf: LDSCRIPT = $(LIB)/main.lds
example/serial_dma.elf: $(START_OBJ) $(SERIAL_DMA_OBJ)
INTERRUPT_OBJ = \
example/interrupt.o \
example/illslot.o \
@ -565,7 +586,8 @@ TETRAHEDRON_OBJ = \
holly/region_array.o \
holly/background.o \
holly/ta_fifo_polygon_converter.o \
holly/video_output.o
holly/video_output.o \
sh7091/serial.o
example/tetrahedron.elf: LDSCRIPT = $(LIB)/main.lds
example/tetrahedron.elf: $(START_OBJ) $(TETRAHEDRON_OBJ)
@ -594,7 +616,7 @@ CUBE_VQ_OBJ = \
sh7091/serial.o \
texture/panda/panda.vq.o
example/cube_vq.elf: LDSCRIPT = $(LIB)/alt.lds
example/cube_vq.elf: LDSCRIPT = $(LIB)/main.lds
example/cube_vq.elf: $(START_OBJ) $(CUBE_VQ_OBJ)
CUBE_VQ_RECTANGULAR_OBJ = \
@ -607,7 +629,7 @@ CUBE_VQ_RECTANGULAR_OBJ = \
sh7091/serial.o \
texture/panda/panda_rectangular.vq.o
example/cube_vq_rectangular.elf: LDSCRIPT = $(LIB)/alt.lds
example/cube_vq_rectangular.elf: LDSCRIPT = $(LIB)/main.lds
example/cube_vq_rectangular.elf: $(START_OBJ) $(CUBE_VQ_RECTANGULAR_OBJ)
SHEIK_OBJ = \
@ -621,7 +643,7 @@ SHEIK_OBJ = \
model/sheik/sheik_00.data.o \
model/sheik/xc_eye01.data.o
example/sheik.elf: LDSCRIPT = $(LIB)/alt.lds
example/sheik.elf: LDSCRIPT = $(LIB)/main.lds
example/sheik.elf: $(START_OBJ) $(SHEIK_OBJ)
SHEIK_VQ_OBJ = \
@ -637,5 +659,5 @@ SHEIK_VQ_OBJ = \
model/sheik/sheik_00.vq.o \
model/sheik/xc_eye01.data.o
example/sheik_vq.elf: LDSCRIPT = $(LIB)/alt.lds
example/sheik_vq.elf: LDSCRIPT = $(LIB)/main.lds
example/sheik_vq.elf: $(START_OBJ) $(SHEIK_VQ_OBJ)

View File

@ -6,6 +6,7 @@
#include "maple/maple_bus_bits.hpp"
#include "maple/maple_host_command_writer.hpp"
#include "maple/maple_port.hpp"
#include "maple/maple_display.hpp"
#include "align.hpp"
#include "sh7091/serial.hpp"
@ -13,60 +14,8 @@
extern uint32_t _binary_font_portfolio_6x8 __asm("_binary_font_portfolio_6x8_portfolio_6x8_data_start");
namespace vmu_display {
constexpr int32_t width = 48;
constexpr int32_t height = 32;
constexpr int32_t pixels_per_byte = 8;
constexpr int32_t framebuffer_size = width * height / pixels_per_byte;
}
uint32_t vmu_framebuffer[vmu_display::framebuffer_size / 4];
static inline void render_glyph(uint8_t * dst, const uint8_t * src, uint8_t c, int x, int y)
void make_vmu_framebuffer(maple::display::font_renderer& renderer)
{
int y_ix = 186 - (y * 6 * 8);
for (int i = 0; i < 8; i++) {
switch (x) {
case 0:
dst[y_ix - i * 6 + 5] = src[(c - ' ') * 8 + i];
break;
case 1:
dst[y_ix - i * 6 + 5] |= (src[(c - ' ') * 8 + i] & 0b11) << 6;
dst[y_ix - i * 6 + 4] = src[(c - ' ') * 8 + i] >> 2; // 0b1111
break;
case 2:
dst[y_ix - i * 6 + 4] |= (src[(c - ' ') * 8 + i] & 0b1111) << 4;
dst[y_ix - i * 6 + 3] = src[(c - ' ') * 8 + i] >> 4; // 0b11
break;
case 3:
dst[y_ix - i * 6 + 3] |= src[(c - ' ') * 8 + i] << 2;
break;
case 4:
dst[y_ix - i * 6 + 2] = src[(c - ' ') * 8 + i];
break;
case 5:
dst[y_ix - i * 6 + 2] |= (src[(c - ' ') * 8 + i] & 0b11) << 6;
dst[y_ix - i * 6 + 1] = src[(c - ' ') * 8 + i] >> 2; // 0b1111
break;
case 6:
dst[y_ix - i * 6 + 1] |= (src[(c - ' ') * 8 + i] & 0b1111) << 4;
dst[y_ix - i * 6 + 0] = src[(c - ' ') * 8 + i] >> 4; // 0b11
break;
case 7:
dst[y_ix - i * 6 + 0] |= src[(c - ' ') * 8 + i] << 2;
break;
}
}
}
void make_vmu_framebuffer(uint32_t * buf)
{
const uint8_t * src = reinterpret_cast<const uint8_t *>(&_binary_font_portfolio_6x8);
uint8_t * dst = reinterpret_cast<uint8_t *>(buf);
for (int i = 0; i < vmu_display::framebuffer_size; i++) {
dst[i] = 0;
}
const char * s =
" very "
" funneh "
@ -75,7 +24,7 @@ void make_vmu_framebuffer(uint32_t * buf)
for (int i = 0; i < 8 * 4; i++) {
int x = i % 8;
int y = i / 8;
render_glyph(dst, src, s[i], x, y);
renderer.glyph(s[i], x, y);
}
}
@ -89,6 +38,8 @@ inline void copy(T * dst, const T * src, const int32_t n) noexcept
}
}
static uint8_t * framebuffer;
void send_vmu_framebuffer(uint8_t port, uint8_t lm)
{
uint32_t send_buf[1024] __attribute__((aligned(32)));
@ -106,7 +57,7 @@ void send_vmu_framebuffer(uint8_t port, uint8_t lm)
= writer.append_command<command_type, response_type>(host_port_select,
destination_ap,
true, // end_flag
vmu_display::framebuffer_size, // send_trailing
maple::display::vmu::framebuffer_size, // send_trailing
0 // recv_trailing
);
auto& data_fields = host_command->bus_data.data_fields;
@ -115,7 +66,9 @@ void send_vmu_framebuffer(uint8_t port, uint8_t lm)
data_fields.phase = 0;
data_fields.block_number = std::byteswap<uint16_t>(0x0000);
copy<uint8_t>(data_fields.written_data, reinterpret_cast<uint8_t *>(vmu_framebuffer), vmu_display::framebuffer_size);
copy<uint8_t>(data_fields.written_data,
reinterpret_cast<uint8_t *>(framebuffer),
maple::display::vmu::framebuffer_size);
maple::dma_start(send_buf, writer.send_offset,
recv_buf, writer.recv_offset);
@ -235,7 +188,10 @@ void main()
{
serial::init(4);
make_vmu_framebuffer(vmu_framebuffer);
const uint8_t * font = reinterpret_cast<const uint8_t *>(&_binary_font_portfolio_6x8);
auto renderer = maple::display::font_renderer(font);
make_vmu_framebuffer(renderer);
framebuffer = renderer.fb;
do_device_request();

49
example/serial_dma.cpp Normal file
View File

@ -0,0 +1,49 @@
#include "sh7091/sh7091.hpp"
#include "sh7091/serial.hpp"
#include "sh7091/serial_dma.hpp"
int main()
{
serial::init(4);
uint8_t buf[4] __attribute__((aligned(32))) = {0};
uint8_t * bufi =
reinterpret_cast<uint8_t *>(0xa000'0000
| reinterpret_cast<uint32_t>(buf));
for (int i = 0; i < 10000; i++) {
asm volatile ("nop;");
}
serial::integer(sh7091.DMAC.CHCR1);
serial::integer(sh7091.DMAC.DMAOR);
serial::string("DAR\n");
sh7091.DMAC.DAR1 = reinterpret_cast<uint32_t>(&bufi[0]);
serial::integer(sh7091.DMAC.DAR1);
serial::integer(reinterpret_cast<uint32_t>(&bufi[0]));
serial::string("wait\n");
while (true) {
serial::recv_dma(&bufi[0], 4);
while ((sh7091.DMAC.CHCR1 & dmac::chcr::te::transfers_completed) == 0) {
};
for (uint32_t i = 0; i < 4; i++) {
serial::hexlify(bufi[i]);
serial::character(' ');
}
serial::string("end\n");
serial::integer(sh7091.DMAC.DAR1);
while ((sh7091.SCIF.SCFSR2 & 0x60) != 0x60) {}
for (int i = 0; i < 1000000; i++) {
asm volatile ("nop;");
}
}
}

View File

@ -1,24 +1,300 @@
#include <cstdint>
#include <bit>
#include "sh7091/sh7091.hpp"
#include "sh7091/sh7091_bits.hpp"
#include "sh7091/serial.hpp"
#include "maple/maple.hpp"
#include "maple/maple_bus_commands.hpp"
#include "maple/maple_bus_bits.hpp"
#include "maple/maple_host_command_writer.hpp"
#include "maple/maple_port.hpp"
#include "maple/maple_display.hpp"
#include "serial_load.hpp"
extern uint32_t _binary_font_portfolio_6x8 __asm("_binary_font_portfolio_6x8_portfolio_6x8_data_start");
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--;
}
}
static uint8_t * framebuffer;
static uint32_t send_buf[1024 / 4] __attribute__((aligned(32)));
static uint32_t recv_buf[1024 / 4] __attribute__((aligned(32)));
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)
{
using command_type = maple::block_write<uint8_t[0]>;
using response_type = maple::device_reply;
uint32_t host_port_select = host_instruction_port_select(port);
uint32_t destination_ap = ap_port_select(port) | ap::de::expansion_device | lm;
auto [host_command, host_response]
= writer.append_command<command_type, response_type>(host_port_select,
destination_ap,
false, // end_flag
maple::display::vmu::framebuffer_size, // send_trailing
0 // recv_trailing
);
auto& data_fields = host_command->bus_data.data_fields;
data_fields.function_type = std::byteswap(function_type::bw_lcd);
data_fields.pt = 0;
data_fields.phase = 0;
data_fields.block_number = std::byteswap<uint16_t>(0x0000);
copy<uint8_t>(data_fields.written_data,
reinterpret_cast<uint8_t *>(framebuffer),
maple::display::vmu::framebuffer_size);
}
void recv_extension_device_status(struct maple_display_poll_state &state)
{
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);
uint32_t last_send_offset = 0;
int response_index = 0;
for (int port = 0; port < 4; port++) {
uint32_t bit = ap::lm_bus::_0;
uint8_t lm = state.port[port].ap__lm;
for (int i = 0; i < 5; i++) {
if (lm == 0)
break;
if ((lm & bit) == 0)
continue;
lm &= ~bit;
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);
} else {
// this extension device is not a bw_lcd; remove it
state.port[port].ap__lm &= ~bit;
}
bit <<= 1;
}
}
{
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;
maple::dma_start(send_buf, writer.send_offset,
recv_buf, writer.recv_offset);
}
}
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;
using command_type = maple::device_request;
using response_type = maple::device_status;
writer.append_command<command_type, response_type>(host_port_select,
destination_ap,
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)
{
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)
{
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);
for (int port = 0; port < 4; port++) {
auto& bus_data = host_response[port].bus_data;
if (bus_data.command_code != maple::device_status::command_code) {
state.port[port].ap__lm = 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;
do_lm_requests(writer, port, lm, &send_extension_device_request);
}
}
maple::dma_start(send_buf, writer.send_offset,
recv_buf, writer.recv_offset);
}
void send_device_request()
{
auto writer = maple::host_command_writer(send_buf, recv_buf);
using command_type = maple::device_request;
using response_type = maple::device_status;
writer.append_command_all_ports<command_type, response_type>();
maple::dma_start(send_buf, writer.send_offset,
recv_buf, writer.recv_offset);
}
void handle_maple(struct maple_display_poll_state& state)
{
switch (state.step) {
case step::IDLE:
if (state.want_start) {
// always send to all ports
send_device_request();
state.step = step::DEVICE_STATUS;
state.want_start = 0;
}
break;
case step::DEVICE_STATUS:
if (maple::dma_poll_complete()) {
recv_device_status(state);
state.step = step::EXTENSION_DEVICE_STATUS;
}
break;
case step::EXTENSION_DEVICE_STATUS:
if (maple::dma_poll_complete()) {
recv_extension_device_status(state);
state.step = step::EXTENSION_DEVICE_REPLY;
}
break;
case step::EXTENSION_DEVICE_REPLY:
if (maple::dma_poll_complete()) {
state.step = step::IDLE;
}
break;
}
}
void render_glyphs(maple::display::font_renderer& renderer, char * s)
{
for (int i = 0; i < 8 * 4; i++) {
int x = i % 8;
int y = i / 8;
renderer.glyph(s[i], x, y);
}
}
void render_serial_state(maple::display::font_renderer& renderer, char * s, const char * msg)
{
bool end = false;
for (int i = 0; i < 8; i++) {
if (end || msg[i] == 0) {
s[0 + i] = ' ';
end = true;
} else {
s[0 + i] = msg[i];
}
}
char num_buf[8];
string::hex(num_buf, 8, sh7091.SCIF.SCFSR2);
for (int i = 0; i < 8; i++) s[8 + i] = num_buf[i];
string::hex(num_buf, 8, sh7091.SCIF.SCFDR2);
for (int i = 0; i < 8; i++) s[16 + i] = num_buf[i];
render_glyphs(renderer, s);
}
void main() __attribute__((section(".text.main")));
void main()
{
serial::init(12);
load_init();
serial::init(0);
struct maple_display_poll_state state = {0};
const uint8_t * font = reinterpret_cast<const uint8_t *>(&_binary_font_portfolio_6x8);
auto renderer = maple::display::font_renderer(font);
framebuffer = renderer.fb;
char s[33] =
"1562500 " // 0
" " // 8
" " // 16
" "; // 24
render_glyphs(renderer, s);
state.want_start = 1;
serial_load::init();
// reset serial status
sh7091.SCIF.SCFSR2 = 0;
// reset line status
sh7091.SCIF.SCLSR2 = 0;
serial::string("ready\n");
while (1) {
using namespace scif;
while ((scfdr2::receive_data_bytes(sh7091.SCIF.SCFDR2)) > 0) {
const uint16_t scfsr2 = sh7091.SCIF.SCFSR2;
if (scfsr2 & scfsr2::brk::bit_mask) {
render_serial_state(renderer, s, "brk");
// clear framing error and break
} else if (scfsr2 & scfsr2::er::bit_mask) {
render_serial_state(renderer, s, "er");
} else if (scfsr2 & scfsr2::dr::bit_mask) {
render_serial_state(renderer, s, "dr");
} else if (sh7091.SCIF.SCLSR2 & sclsr2::orer::bit_mask) {
render_serial_state(renderer, s, "orer");
} else if (scfsr2 & scfsr2::rdf::bit_mask) {
render_serial_state(renderer, s, "rdf");
const uint8_t c = sh7091.SCIF.SCFRDR2;
load_recv(c);
serial_load::recv(c);
} else {
render_serial_state(renderer, s, "idle");
}
state.want_start = 1;
handle_maple(state);
uint16_t error_bits = scfsr2::er::bit_mask | scfsr2::brk::bit_mask;
if (sh7091.SCIF.SCFSR2 & error_bits) {
sh7091.SCIF.SCFSR2 = ~error_bits;
}
}
}

View File

@ -1,9 +1,9 @@
OUTPUT_FORMAT("elf32-shl", "elf32-shl", "elf32-shl")
MEMORY
{
p1ram : ORIGIN = 0x8c010000, LENGTH = 0xff0000
p1ram : ORIGIN = 0xac005000, LENGTH = 0x0000
p2ram : ORIGIN = 0xac010000, LENGTH = 0xff0000
ldram : ORIGIN = 0x8cfff000, LENGTH = 0x1000
ldram : ORIGIN = 0xacffe000, LENGTH = 0x2000
}
SECTIONS
{
@ -12,41 +12,42 @@ SECTIONS
.text.startup ALIGN(4) : SUBALIGN(4)
{
KEEP(*(.text.start))
*(.text.startup.*)
KEEP(*(.text.startup.*))
. = ALIGN(4);
} > p2ram AT> p2ram
} > p2ram
.ctors ALIGN(4) : SUBALIGN(4)
{
KEEP(*(.ctors))
KEEP(*(.ctors.*))
. = ALIGN(4);
} > p2ram AT> p2ram
} > p2ram
. = ORIGIN(ldram);
.text ALIGN(4) : SUBALIGN(4)
{
KEEP(*(.text.main))
*(.text.*)
*(.text)
. = ALIGN(4);
} > ldram AT> p2ram
.data ALIGN(4) : SUBALIGN(4)
.data ALIGN(4) :
{
*(.data)
*(.data.*)
. = ALIGN(4);
} > ldram AT> p2ram
.rodata ALIGN(4) : SUBALIGN(4)
.rodata ALIGN(4) :
{
*(.rodata)
*(.rodata.*)
. = ALIGN(4);
} > ldram AT> p2ram
.bss ALIGN(4) (NOLOAD) : SUBALIGN(4)
.bss ALIGN(4) (NOLOAD) :
{
*(.bss)
*(.bss.*)
@ -56,15 +57,17 @@ SECTIONS
.text.vbr ALIGN(4) : SUBALIGN(4)
{
/*
KEEP(*(.vbr.100))
. = ALIGN(0x300);
KEEP(*(.vbr.400))
. = ALIGN(0x200);
KEEP(*(.vbr.600))
} > p1ram
*/
} > p2ram
INCLUDE "debug.lds"
}
__stack_reservation = 0x0000;
__stack_reservation = 0x0;
INCLUDE "symbols.lds"
INCLUDE "addresses.lds"

View File

@ -145,6 +145,22 @@ static inline void _dma_start(const uint32_t * command_buf)
maple_if.MDST = mdst::start_status::start;
}
void dma_wait_complete()
{
// wait for maple DMA completion
while ((system.ISTNRM & istnrm::end_of_dma_maple_dma) == 0);
system.ISTNRM = istnrm::end_of_dma_maple_dma;
}
bool dma_poll_complete()
{
bool complete = (system.ISTNRM & istnrm::end_of_dma_maple_dma) != 0;
if (complete) {
system.ISTNRM = istnrm::end_of_dma_maple_dma;
}
return complete;
}
void dma_start(const uint32_t * send_buf,
const uint32_t send_size,
const uint32_t * recv_buf,
@ -169,10 +185,6 @@ void dma_start(const uint32_t * send_buf,
: "r" (reinterpret_cast<uint32_t>(&recv_buf[(32 * i) / 4])) // input
);
}
// wait for maple DMA completion
while ((system.ISTNRM & istnrm::end_of_dma_maple_dma) == 0);
system.ISTNRM = istnrm::end_of_dma_maple_dma;
}
// wait for completion

View File

@ -34,6 +34,10 @@ struct host_response {
};
static_assert((sizeof (host_response<uint8_t[0]>)) == align_32byte((sizeof (host_response<uint8_t[0]>))));
void dma_wait_complete();
bool dma_poll_complete();
void dma_start(uint32_t const * const command_buf,
const uint32_t command_size,
uint32_t const * const receive_buf,

61
maple/maple_display.hpp Normal file
View File

@ -0,0 +1,61 @@
namespace maple {
namespace display {
namespace vmu {
constexpr int32_t width = 48;
constexpr int32_t height = 32;
constexpr int32_t pixels_per_byte = 8;
constexpr int32_t framebuffer_size = width * height / pixels_per_byte;
}
struct font_renderer {
// 6x8 px font assumed
uint8_t const * const font;
uint8_t fb[vmu::framebuffer_size];
constexpr font_renderer(uint8_t const * const font)
: font(font)
{ }
constexpr inline void glyph(uint8_t c, int x, int y)
{
int y_ix = 186 - (y * 6 * 8);
for (int i = 0; i < 8; i++) {
switch (x) {
case 0:
fb[y_ix - i * 6 + 5] = font[(c - ' ') * 8 + i];
break;
case 1:
fb[y_ix - i * 6 + 5] |= (font[(c - ' ') * 8 + i] & 0b11) << 6;
fb[y_ix - i * 6 + 4] = font[(c - ' ') * 8 + i] >> 2; // 0b1111
break;
case 2:
fb[y_ix - i * 6 + 4] |= (font[(c - ' ') * 8 + i] & 0b1111) << 4;
fb[y_ix - i * 6 + 3] = font[(c - ' ') * 8 + i] >> 4; // 0b11
break;
case 3:
fb[y_ix - i * 6 + 3] |= font[(c - ' ') * 8 + i] << 2;
break;
case 4:
fb[y_ix - i * 6 + 2] = font[(c - ' ') * 8 + i];
break;
case 5:
fb[y_ix - i * 6 + 2] |= (font[(c - ' ') * 8 + i] & 0b11) << 6;
fb[y_ix - i * 6 + 1] = font[(c - ' ') * 8 + i] >> 2; // 0b1111
break;
case 6:
fb[y_ix - i * 6 + 1] |= (font[(c - ' ') * 8 + i] & 0b1111) << 4;
fb[y_ix - i * 6 + 0] = font[(c - ' ') * 8 + i] >> 4; // 0b11
break;
case 7:
fb[y_ix - i * 6 + 0] |= font[(c - ' ') * 8 + i] << 2;
break;
}
}
}
};
} // namespace display
} // namespace maple

View File

@ -291,3 +291,5 @@
"SCIF","SCSPTR2","SPB2IO","1","spb2dt_is_output_to_txd2","1",,
"SCIF","SCSPTR2","SPB2DT","0","input_output_data_is_low_level","0",,
"SCIF","SCSPTR2","SPB2DT","0","input_output_data_is_high_level","1",,
,,,,,,,
"SCIF","SCLSR2","ORER","0","overrun_error_occured","1",,

1 block register_name enum_name bits bit_name value mask description
291 SCIF SCSPTR2 SPB2IO 1 spb2dt_is_output_to_txd2 1
292 SCIF SCSPTR2 SPB2DT 0 input_output_data_is_low_level 0
293 SCIF SCSPTR2 SPB2DT 0 input_output_data_is_high_level 1
294
295 SCIF SCLSR2 ORER 0 overrun_error_occured 1

Binary file not shown.

View File

@ -1,33 +1,13 @@
#include <cstdint>
#include "sh7091/sh7091.hpp"
#include "sh7091/sh7091_bits.hpp"
#include "sh7091/serial.hpp"
#include "holly/holly.hpp"
#include "serial_load.hpp"
enum load_command {
CMD_NONE,
CMD_DATA, // DATA 0000 0000 {data}
CMD_JUMP, // JUMP 0000
CMD_RATE, // RATE 0000
};
namespace serial_load {
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;
};
struct state state;
static struct load_state state;
void move(void *dst, const void *src, size_t n)
static void move(void *dst, const void *src, uint32_t n)
{
uint8_t * d = reinterpret_cast<uint8_t *>(dst);
const uint8_t * s = reinterpret_cast<const uint8_t *>(src);
@ -40,22 +20,13 @@ void move(void *dst, const void *src, size_t n)
}
}
void load_init()
void 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)
static void jump_to_func(const uint32_t addr)
{
serial::string("jump to: ");
serial::integer<uint32_t>(addr);
@ -73,7 +44,7 @@ void jump_to_func(const uint32_t addr)
// restore our stack
}
void load_recv(uint8_t c)
void recv(uint8_t c)
{
while (1) {
switch (state.command) {
@ -87,7 +58,7 @@ void load_recv(uint8_t c)
if (state.len < 12) {
return;
} else {
debug("data\n");
serial::string("data");
state.command = CMD_DATA;
return;
}
@ -98,7 +69,7 @@ void load_recv(uint8_t c)
if (state.len < 8) {
return;
} else {
debug("jump\n");
serial::string("jump");
state.command = CMD_JUMP;
}
} else if (state.buf[0] == 'R' &&
@ -108,7 +79,7 @@ void load_recv(uint8_t c)
if (state.len < 8) {
return;
} else {
debug("rate\n");
serial::string("rate");
state.command = CMD_RATE;
}
} else {
@ -124,7 +95,7 @@ void load_recv(uint8_t c)
uint32_t * size = &state.addr1;
uint8_t * dest = reinterpret_cast<uint8_t *>(state.addr2);
if (*size > 0) {
sh7091.SCIF.SCFTDR2 = c;
serial::character(c);
// write c to dest
*dest = c;
@ -135,7 +106,7 @@ void load_recv(uint8_t c)
if (*size == 0) {
state.len = 0;
state.command = CMD_NONE;
debug("next\n");
serial::string("next");
}
return;
break;
@ -144,21 +115,17 @@ void load_recv(uint8_t c)
// 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;
}
}
}
}

View File

@ -1,4 +1,30 @@
#pragma once
void load_init();
void load_recv(uint8_t c);
namespace serial_load {
void init();
void recv(uint8_t c);
enum command {
CMD_NONE,
CMD_DATA, // DATA 0000 0000 {data}
CMD_JUMP, // JUMP 0000
CMD_RATE, // RATE 0000
};
struct state {
union {
uint8_t buf[12];
struct {
uint8_t cmd[4];
uint32_t addr1;
uint32_t addr2;
};
};
uint32_t len;
enum command command;
};
extern struct state state;
}

View File

@ -37,7 +37,8 @@ void init(uint8_t bit_rate)
sh7091.SCIF.SCFCR2 = scfcr2::rtrg::trigger_on_1_byte
| scfcr2::ttrg::trigger_on_8_bytes
| scfcr2::mce::modem_signals_enabled;
//| scfcr2::mce::modem_signals_enabled
;
sh7091.SCIF.SCSMR2 = scsmr2::chr::_8_bit_data
| scsmr2::pe::parity_disabled
@ -67,12 +68,10 @@ void init(uint8_t bit_rate)
void character(const char c)
{
using namespace scif;
// wait for transmit fifo to become empty
// wait for transmit fifo to become partially empty
while ((sh7091.SCIF.SCFSR2 & scfsr2::tdfe::bit_mask) == 0);
for (int i = 0; i < 1000; i++) {
asm volatile ("nop;");
}
sh7091.SCIF.SCFSR2 = static_cast<uint16_t>(~scfsr2::tdfe::bit_mask);
sh7091.SCIF.SCFTDR2 = static_cast<uint8_t>(c);
}
@ -105,6 +104,7 @@ void hexlify(const uint8_t * s, uint32_t len)
{
for (uint32_t i = 0; i < len; i++) {
hexlify(s[i]);
character(' ');
}
character('\n');
}

94
sh7091/serial_dma.hpp Normal file
View File

@ -0,0 +1,94 @@
#pragma once
#include "sh7091/sh7091_bits.hpp"
/*
(4) Channels 1 and 3 can be used in the following manner:
The allowed transfer data length (8/16/32 bits, 32 bytes) depends on the
transfer area. The 64-bit transfer data length specification is permitted only for
system memory.
Address mode: Only dual address mode is permitted.
Transfer initiation request: SCIF interrupt and auto request are permitted.
Bus mode: Only cycle steal mode is permitted.
DMA end interrupt: Can be used.
*/
/*
After the desired transfer conditions have been set in the DMA source address register (SAR),
DMA destination address register (DAR), DMA transfer count register (DMATCR), DMA
channel control register (CHCR), and DMA operation register (DMAOR), the DMAC transfers
data according to the following procedure:
1. The DMAC checks to see if transfer is enabled (DE = 1, DME = 1, TE = 0, NMIF = 0, AE = 0).
2. When a transfer request is issued and transfer has been enabled, the DMAC transfers one
transfer unit of data (determined by the setting of TS2TS0). In auto-request mode, the transfer
begins automatically when the DE bit and DME bit are set to 1. The DMATCR value is
decremented by 1 for each transfer. The actual transfer flow depends on the address mode and
bus mode.
3. When the specified number of transfers have been completed (when the DMATCR value
reaches 0), the transfer ends normally. If the IE bit in CHCR is set to 1 at this time, a DMTE
interrupt request is sent to the CPU.
4. If a DMAC address error or NMI interrupt occurs, the transfer is suspended. Transfer is also
suspended when the DE bit in CHCR or the DME bit in DMAOR is cleared to 0. In the event
of an address error, a DMAE interrupt request is forcibly sent to the CPU.
*/
namespace serial {
static void recv_dma(void * destination_address, uint32_t length)
{
using namespace dmac;
/* Initial settings (SAR, DAR, DMATCR, CHCR, DMAOR) */
// RS: SCFRDR2 (SCIF receive-data-full transfer request)
// RS: 0b1011
// SAR1 = SCFRDR2
// DAR1 = (address)
// DMATCR1 = (transfer count, 24 bit)
sh7091.DMAC.CHCR3 = 0;
sh7091.DMAC.CHCR2 = 0;
sh7091.DMAC.CHCR1 = 0;
sh7091.DMAC.CHCR0 = 0;
sh7091.DMAC.SAR1 = reinterpret_cast<uint32_t>(&sh7091.SCIF.SCFRDR2);
sh7091.DMAC.DAR1 = reinterpret_cast<uint32_t>(destination_address);
sh7091.DMAC.DMATCR1 = length & 0x00ff'ffff;
// SSA (only used for PCMCIA)
// STC (only used for PCMCIA)
// DSA (only used for PCMCIA)
// DTC (only used for PCMCIA)
// DS (only used for DREQ requests)
// RL (only used for DRAK requests)
// AM (only used for DACK requests)
// AL (only used for DACK requests)
// DM incremented
// SM fixed
// RS SCIF
// TM cycle steal
// TS byte?
// IE interrupt enable
// TE transfer end (status bit)
// DE channel enable
sh7091.DMAC.CHCR1 = chcr::dm::destination_address_incremented
| chcr::sm::source_address_fixed
| chcr::rs::resource_select(0b1011) /* SCIF */
| chcr::tm::cycle_steal_mode /* transmit mode */
| chcr::ts::_8_bit /* transfer size */
//| chcr::ie::interrupt_request_generated
| chcr::de::channel_operation_enabled;
sh7091.DMAC.DMAOR = dmaor::ddt::on_demand_data_transfer_mode /* on-demand data transfer mode */
| dmaor::pr::ch2_ch0_ch1_ch3 /* priority mode; CH2 > CH0 > CH1 > CH3 */
| dmaor::dme::operation_enabled_on_all_channels; /* DMAC master enable */
}
}

View File

@ -817,4 +817,12 @@ namespace scif {
}
}
namespace sclsr2 {
namespace orer {
constexpr uint32_t overrun_error_occured = 1 << 0;
constexpr uint32_t bit_mask = 0x1 << 0;
}
}
}