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 struct
import sys import sys
import time import time
import fcntl
import os
#dest = 0xac21_0000 #dest = 0xac21_0000
dest = 0xac02_0000 #dest = 0xac02_0000
#dest = 0xac01_0000 dest = 0xac01_0000
ret = [] ret = []
@ -71,7 +73,7 @@ def start_data(ser, b):
size = len(b) size = len(b)
args = struct.pack("<II", size, dest) args = struct.pack("<II", size, dest)
ret = sync(ser, b'DATA' + args) 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(".", end=' ')
print(ret) print(ret)
time.sleep(1) time.sleep(1)
@ -100,9 +102,9 @@ def do(ser, b):
return return
args = struct.pack("<I", dest) args = struct.pack("<I", dest)
print("JUMP") ret = sync(ser, b'JUMP' + args, wait=0)
ret = sync(ser, b'JUMP' + args, wait=1) print("JUMP", ret)
print() print("console:")
console(ser) console(ser)
seen_length = 16 seen_length = 16
@ -122,9 +124,19 @@ def console(ser):
global framebuffer_mode global framebuffer_mode
global framebuffer 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 = [0] * seen_length
seen_ix = 0 seen_ix = 0
while True: 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) b = ser.read(ser.in_waiting)
if b == b'': if b == b'':
continue continue
@ -195,6 +207,11 @@ with serial.Serial(port='/dev/ttyUSB0',
rtscts=False, rtscts=False,
#rtscts=True, #rtscts=True,
) as ser: ) 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) #console(ser)
print("waiting: ", end=' ') print("waiting: ", end=' ')
sys.stdout.flush() sys.stdout.flush()

View File

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

View File

@ -6,6 +6,7 @@
#include "maple/maple_bus_bits.hpp" #include "maple/maple_bus_bits.hpp"
#include "maple/maple_host_command_writer.hpp" #include "maple/maple_host_command_writer.hpp"
#include "maple/maple_port.hpp" #include "maple/maple_port.hpp"
#include "maple/maple_display.hpp"
#include "align.hpp" #include "align.hpp"
#include "sh7091/serial.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"); extern uint32_t _binary_font_portfolio_6x8 __asm("_binary_font_portfolio_6x8_portfolio_6x8_data_start");
namespace vmu_display { void make_vmu_framebuffer(maple::display::font_renderer& renderer)
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)
{ {
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 = const char * s =
" very " " very "
" funneh " " funneh "
@ -75,7 +24,7 @@ void make_vmu_framebuffer(uint32_t * buf)
for (int i = 0; i < 8 * 4; i++) { for (int i = 0; i < 8 * 4; i++) {
int x = i % 8; int x = i % 8;
int y = 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) void send_vmu_framebuffer(uint8_t port, uint8_t lm)
{ {
uint32_t send_buf[1024] __attribute__((aligned(32))); 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, = writer.append_command<command_type, response_type>(host_port_select,
destination_ap, destination_ap,
true, // end_flag true, // end_flag
vmu_display::framebuffer_size, // send_trailing maple::display::vmu::framebuffer_size, // send_trailing
0 // recv_trailing 0 // recv_trailing
); );
auto& data_fields = host_command->bus_data.data_fields; 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.phase = 0;
data_fields.block_number = std::byteswap<uint16_t>(0x0000); 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, maple::dma_start(send_buf, writer.send_offset,
recv_buf, writer.recv_offset); recv_buf, writer.recv_offset);
@ -235,7 +188,10 @@ void main()
{ {
serial::init(4); 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(); 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 <cstdint>
#include <bit>
#include "sh7091/sh7091.hpp" #include "sh7091/sh7091.hpp"
#include "sh7091/sh7091_bits.hpp" #include "sh7091/sh7091_bits.hpp"
#include "sh7091/serial.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" #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() __attribute__((section(".text.main")));
void main() void main()
{ {
serial::init(12); serial::init(0);
load_init();
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) { while (1) {
using namespace scif; 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; 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") OUTPUT_FORMAT("elf32-shl", "elf32-shl", "elf32-shl")
MEMORY MEMORY
{ {
p1ram : ORIGIN = 0x8c010000, LENGTH = 0xff0000 p1ram : ORIGIN = 0xac005000, LENGTH = 0x0000
p2ram : ORIGIN = 0xac010000, LENGTH = 0xff0000 p2ram : ORIGIN = 0xac010000, LENGTH = 0xff0000
ldram : ORIGIN = 0x8cfff000, LENGTH = 0x1000 ldram : ORIGIN = 0xacffe000, LENGTH = 0x2000
} }
SECTIONS SECTIONS
{ {
@ -12,41 +12,42 @@ SECTIONS
.text.startup ALIGN(4) : SUBALIGN(4) .text.startup ALIGN(4) : SUBALIGN(4)
{ {
KEEP(*(.text.start)) KEEP(*(.text.start))
*(.text.startup.*) KEEP(*(.text.startup.*))
. = ALIGN(4); . = ALIGN(4);
} > p2ram AT> p2ram } > p2ram
.ctors ALIGN(4) : SUBALIGN(4) .ctors ALIGN(4) : SUBALIGN(4)
{ {
KEEP(*(.ctors)) KEEP(*(.ctors))
KEEP(*(.ctors.*)) KEEP(*(.ctors.*))
. = ALIGN(4); . = ALIGN(4);
} > p2ram AT> p2ram } > p2ram
. = ORIGIN(ldram); . = ORIGIN(ldram);
.text ALIGN(4) : SUBALIGN(4) .text ALIGN(4) : SUBALIGN(4)
{ {
KEEP(*(.text.main))
*(.text.*) *(.text.*)
*(.text) *(.text)
. = ALIGN(4); . = ALIGN(4);
} > ldram AT> p2ram } > ldram AT> p2ram
.data ALIGN(4) : SUBALIGN(4) .data ALIGN(4) :
{ {
*(.data) *(.data)
*(.data.*) *(.data.*)
. = ALIGN(4); . = ALIGN(4);
} > ldram AT> p2ram } > ldram AT> p2ram
.rodata ALIGN(4) : SUBALIGN(4) .rodata ALIGN(4) :
{ {
*(.rodata) *(.rodata)
*(.rodata.*) *(.rodata.*)
. = ALIGN(4); . = ALIGN(4);
} > ldram AT> p2ram } > ldram AT> p2ram
.bss ALIGN(4) (NOLOAD) : SUBALIGN(4) .bss ALIGN(4) (NOLOAD) :
{ {
*(.bss) *(.bss)
*(.bss.*) *(.bss.*)
@ -56,15 +57,17 @@ SECTIONS
.text.vbr ALIGN(4) : SUBALIGN(4) .text.vbr ALIGN(4) : SUBALIGN(4)
{ {
/*
KEEP(*(.vbr.100)) KEEP(*(.vbr.100))
. = ALIGN(0x300); . = ALIGN(0x300);
KEEP(*(.vbr.400)) KEEP(*(.vbr.400))
. = ALIGN(0x200); . = ALIGN(0x200);
KEEP(*(.vbr.600)) KEEP(*(.vbr.600))
} > p1ram */
} > p2ram
INCLUDE "debug.lds" INCLUDE "debug.lds"
} }
__stack_reservation = 0x0000; __stack_reservation = 0x0;
INCLUDE "symbols.lds" INCLUDE "symbols.lds"
INCLUDE "addresses.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; 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, void dma_start(const uint32_t * send_buf,
const uint32_t send_size, const uint32_t send_size,
const uint32_t * recv_buf, 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 : "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 // 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]>)))); 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, void dma_start(uint32_t const * const command_buf,
const uint32_t command_size, const uint32_t command_size,
uint32_t const * const receive_buf, 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","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_low_level","0",,
"SCIF","SCSPTR2","SPB2DT","0","input_output_data_is_high_level","1",, "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 <cstdint>
#include "sh7091/sh7091.hpp"
#include "sh7091/sh7091_bits.hpp"
#include "sh7091/serial.hpp" #include "sh7091/serial.hpp"
#include "holly/holly.hpp" #include "serial_load.hpp"
enum load_command { namespace serial_load {
CMD_NONE,
CMD_DATA, // DATA 0000 0000 {data}
CMD_JUMP, // JUMP 0000
CMD_RATE, // RATE 0000
};
struct load_state { struct state 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; static void move(void *dst, const void *src, uint32_t n)
void move(void *dst, const void *src, size_t n)
{ {
uint8_t * d = reinterpret_cast<uint8_t *>(dst); uint8_t * d = reinterpret_cast<uint8_t *>(dst);
const uint8_t * s = reinterpret_cast<const uint8_t *>(src); 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.len = 0;
state.command = CMD_NONE; state.command = CMD_NONE;
} }
void debug(const char * s) static void jump_to_func(const uint32_t addr)
{
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::string("jump to: ");
serial::integer<uint32_t>(addr); serial::integer<uint32_t>(addr);
@ -73,7 +44,7 @@ void jump_to_func(const uint32_t addr)
// restore our stack // restore our stack
} }
void load_recv(uint8_t c) void recv(uint8_t c)
{ {
while (1) { while (1) {
switch (state.command) { switch (state.command) {
@ -87,7 +58,7 @@ void load_recv(uint8_t c)
if (state.len < 12) { if (state.len < 12) {
return; return;
} else { } else {
debug("data\n"); serial::string("data");
state.command = CMD_DATA; state.command = CMD_DATA;
return; return;
} }
@ -98,7 +69,7 @@ void load_recv(uint8_t c)
if (state.len < 8) { if (state.len < 8) {
return; return;
} else { } else {
debug("jump\n"); serial::string("jump");
state.command = CMD_JUMP; state.command = CMD_JUMP;
} }
} else if (state.buf[0] == 'R' && } else if (state.buf[0] == 'R' &&
@ -108,7 +79,7 @@ void load_recv(uint8_t c)
if (state.len < 8) { if (state.len < 8) {
return; return;
} else { } else {
debug("rate\n"); serial::string("rate");
state.command = CMD_RATE; state.command = CMD_RATE;
} }
} else { } else {
@ -124,7 +95,7 @@ void load_recv(uint8_t c)
uint32_t * size = &state.addr1; uint32_t * size = &state.addr1;
uint8_t * dest = reinterpret_cast<uint8_t *>(state.addr2); uint8_t * dest = reinterpret_cast<uint8_t *>(state.addr2);
if (*size > 0) { if (*size > 0) {
sh7091.SCIF.SCFTDR2 = c; serial::character(c);
// write c to dest // write c to dest
*dest = c; *dest = c;
@ -135,7 +106,7 @@ void load_recv(uint8_t c)
if (*size == 0) { if (*size == 0) {
state.len = 0; state.len = 0;
state.command = CMD_NONE; state.command = CMD_NONE;
debug("next\n"); serial::string("next");
} }
return; return;
break; break;
@ -144,21 +115,17 @@ void load_recv(uint8_t c)
// jump // jump
state.len = 0; state.len = 0;
state.command = CMD_NONE; state.command = CMD_NONE;
debug("prejump\n");
holly.VO_BORDER_COL = (31 << 11);
jump_to_func(state.addr1); jump_to_func(state.addr1);
holly.VO_BORDER_COL = (63 << 5) | (31 << 0);
debug("postjump\n");
return; return;
break; break;
case CMD_RATE: case CMD_RATE:
state.len = 0; state.len = 0;
state.command = CMD_NONE; state.command = CMD_NONE;
debug("prerate\n");
serial::init(state.addr1 & 0xff); serial::init(state.addr1 & 0xff);
debug("postrate\n");
return; return;
break; break;
} }
} }
} }
}

View File

@ -1,4 +1,30 @@
#pragma once #pragma once
void load_init(); namespace serial_load {
void load_recv(uint8_t c);
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 sh7091.SCIF.SCFCR2 = scfcr2::rtrg::trigger_on_1_byte
| scfcr2::ttrg::trigger_on_8_bytes | scfcr2::ttrg::trigger_on_8_bytes
| scfcr2::mce::modem_signals_enabled; //| scfcr2::mce::modem_signals_enabled
;
sh7091.SCIF.SCSMR2 = scsmr2::chr::_8_bit_data sh7091.SCIF.SCSMR2 = scsmr2::chr::_8_bit_data
| scsmr2::pe::parity_disabled | scsmr2::pe::parity_disabled
@ -67,12 +68,10 @@ void init(uint8_t bit_rate)
void character(const char c) void character(const char c)
{ {
using namespace scif; 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); while ((sh7091.SCIF.SCFSR2 & scfsr2::tdfe::bit_mask) == 0);
for (int i = 0; i < 1000; i++) { sh7091.SCIF.SCFSR2 = static_cast<uint16_t>(~scfsr2::tdfe::bit_mask);
asm volatile ("nop;");
}
sh7091.SCIF.SCFTDR2 = static_cast<uint8_t>(c); 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++) { for (uint32_t i = 0; i < len; i++) {
hexlify(s[i]); hexlify(s[i]);
character(' ');
} }
character('\n'); 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;
}
}
} }