From b133879f7e49763018c4fe1a9de1fc567c6dc5ed Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Tue, 22 Oct 2024 04:19:37 -0500 Subject: [PATCH] serial_transfer: fully asynchronous maple display write --- client.py | 29 +++- example/example.mk | 38 ++++- example/maple_font.cpp | 70 ++------- example/serial_dma.cpp | 49 +++++++ example/serial_transfer.cpp | 284 +++++++++++++++++++++++++++++++++++- loader.lds | 23 +-- maple/maple.cpp | 20 ++- maple/maple.hpp | 4 + maple/maple_display.hpp | 61 ++++++++ regs/sh7091_bits.csv | 2 + regs/sh7091_bits.ods | Bin 20874 -> 21307 bytes serial_load.cpp | 61 ++------ serial_load.hpp | 30 +++- sh7091/serial.cpp | 10 +- sh7091/serial_dma.hpp | 94 ++++++++++++ sh7091/sh7091_bits.hpp | 8 + 16 files changed, 640 insertions(+), 143 deletions(-) create mode 100644 example/serial_dma.cpp create mode 100644 maple/maple_display.hpp create mode 100644 sh7091/serial_dma.hpp diff --git a/client.py b/client.py index 59d58e6..db19a3d 100644 --- a/client.py +++ b/client.py @@ -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("> 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(&_binary_font_portfolio_6x8); - uint8_t * dst = reinterpret_cast(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(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(0x0000); - copy(data_fields.written_data, reinterpret_cast(vmu_framebuffer), vmu_display::framebuffer_size); + copy(data_fields.written_data, + reinterpret_cast(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(&_binary_font_portfolio_6x8); + auto renderer = maple::display::font_renderer(font); + make_vmu_framebuffer(renderer); + framebuffer = renderer.fb; do_device_request(); diff --git a/example/serial_dma.cpp b/example/serial_dma.cpp new file mode 100644 index 0000000..7125f7a --- /dev/null +++ b/example/serial_dma.cpp @@ -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(0xa000'0000 + | reinterpret_cast(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(&bufi[0]); + + serial::integer(sh7091.DMAC.DAR1); + serial::integer(reinterpret_cast(&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;"); + } + } +} diff --git a/example/serial_transfer.cpp b/example/serial_transfer.cpp index f0d2a38..32bfa67 100644 --- a/example/serial_transfer.cpp +++ b/example/serial_transfer.cpp @@ -1,24 +1,300 @@ #include +#include #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 +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; + 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(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(0x0000); + + copy(data_fields.written_data, + reinterpret_cast(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; + auto host_response = reinterpret_cast(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::data_fields>; + auto host_command = reinterpret_cast(&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(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; + auto host_response = reinterpret_cast(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(); + + 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(&_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; } } } diff --git a/loader.lds b/loader.lds index 3041900..ce467d1 100644 --- a/loader.lds +++ b/loader.lds @@ -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" diff --git a/maple/maple.cpp b/maple/maple.cpp index e0ff3e8..d77a3e4 100644 --- a/maple/maple.cpp +++ b/maple/maple.cpp @@ -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(&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 diff --git a/maple/maple.hpp b/maple/maple.hpp index c099170..7b92600 100644 --- a/maple/maple.hpp +++ b/maple/maple.hpp @@ -34,6 +34,10 @@ struct host_response { }; static_assert((sizeof (host_response)) == align_32byte((sizeof (host_response)))); +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, diff --git a/maple/maple_display.hpp b/maple/maple_display.hpp new file mode 100644 index 0000000..062e5fa --- /dev/null +++ b/maple/maple_display.hpp @@ -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 diff --git a/regs/sh7091_bits.csv b/regs/sh7091_bits.csv index f165e1c..db82ffd 100644 --- a/regs/sh7091_bits.csv +++ b/regs/sh7091_bits.csv @@ -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",, diff --git a/regs/sh7091_bits.ods b/regs/sh7091_bits.ods index 747ad83c93fdcbe1508907da3dfb821d3bbc5b8c..e15da92314a91c7eb88302ea0b29e86082e67003 100644 GIT binary patch delta 12583 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 delta 12142 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 diff --git a/serial_load.cpp b/serial_load.cpp index 9cad9cb..9bc6d3f 100644 --- a/serial_load.cpp +++ b/serial_load.cpp @@ -1,33 +1,13 @@ #include -#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(dst); const uint8_t * s = reinterpret_cast(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(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(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; } } } + +} diff --git a/serial_load.hpp b/serial_load.hpp index 9ed6ce7..26c7d60 100644 --- a/serial_load.hpp +++ b/serial_load.hpp @@ -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; + +} diff --git a/sh7091/serial.cpp b/sh7091/serial.cpp index 7e122e8..867031e 100644 --- a/sh7091/serial.cpp +++ b/sh7091/serial.cpp @@ -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(~scfsr2::tdfe::bit_mask); sh7091.SCIF.SCFTDR2 = static_cast(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'); } diff --git a/sh7091/serial_dma.hpp b/sh7091/serial_dma.hpp new file mode 100644 index 0000000..b8bf603 --- /dev/null +++ b/sh7091/serial_dma.hpp @@ -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(&sh7091.SCIF.SCFRDR2); + sh7091.DMAC.DAR1 = reinterpret_cast(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 */ +} + +} diff --git a/sh7091/sh7091_bits.hpp b/sh7091/sh7091_bits.hpp index cd49338..2a931d9 100644 --- a/sh7091/sh7091_bits.hpp +++ b/sh7091/sh7091_bits.hpp @@ -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; + } + } + }