serial_transfer: fully asynchronous maple display write
This commit is contained in:
parent
1ecd87495f
commit
b133879f7e
29
client.py
29
client.py
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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
49
example/serial_dma.cpp
Normal 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;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
23
loader.lds
23
loader.lds
@ -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"
|
||||||
|
@ -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
|
||||||
|
@ -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
61
maple/maple_display.hpp
Normal 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
|
@ -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",,
|
||||||
|
|
Binary file not shown.
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -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
94
sh7091/serial_dma.hpp
Normal 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 TS2–TS0). 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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user