From 783014e53254fe17102a34c30120eeabf5227a47 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Wed, 19 Oct 2022 14:15:28 +0200 Subject: Clean up SDK debug logging, fix getTPage() --- libpsn00b/include/assert.h | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'libpsn00b/include/assert.h') diff --git a/libpsn00b/include/assert.h b/libpsn00b/include/assert.h index 32301e2..eabe369 100644 --- a/libpsn00b/include/assert.h +++ b/libpsn00b/include/assert.h @@ -1,20 +1,33 @@ /* - * PSn00bSDK assert macro + * PSn00bSDK assert macro and internal logging * (C) 2022 spicyjpeg - MPL licensed + * + * Note that the _sdk_log() macro is used internally by PSn00bSDK to output + * debug messages and warnings. */ #ifndef __ASSERT_H #define __ASSERT_H -void _assert_abort(const char *file, int line, const char *expr); - #ifdef NDEBUG -#define assert(x) + +#define assert(expr) +#define _sdk_log(fmt, ...) + #else + +void _assert_abort(const char *file, int line, const char *expr); +void _sdk_log_inner(const char *fmt, ...); + #define assert(expr) { \ - if (!(expr)) \ - _assert_abort(__FILE__, __LINE__, #expr); \ + if (!(expr)) _assert_abort(__FILE__, __LINE__, #expr); \ } +#ifdef SDK_LIBRARY_NAME +#define _sdk_log(fmt, ...) _sdk_log_inner(SDK_LIBRARY_NAME ": " fmt, ##__VA_ARGS__) +#else +#define _sdk_log(fmt, ...) _sdk_log_inner(fmt, ##__VA_ARGS__) +#endif + #endif #endif -- cgit v1.2.3 From b1632d7df0e840692612461a80d0e05d6a3228ed Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Fri, 21 Oct 2022 13:24:54 +0200 Subject: Rewrite psxsio with new API, fix compiler warnings --- CHANGELOG.md | 6 +- libpsn00b/include/assert.h | 7 +- libpsn00b/include/psxsio.h | 258 +++++++++++++++++++++++++++++++-------- libpsn00b/libc/memset.s | 5 + libpsn00b/psxcd/isofs.c | 1 + libpsn00b/psxetc/dl.c | 3 +- libpsn00b/psxgpu/common.c | 68 ++++++----- libpsn00b/psxsio/_sio_control.s | 184 ---------------------------- libpsn00b/psxsio/sio.c | 263 ++++++++++++++++++++++++++++++++++++++++ libpsn00b/psxsio/siocons.c | 220 --------------------------------- libpsn00b/psxsio/tty.c | 107 ++++++++++++++++ 11 files changed, 631 insertions(+), 491 deletions(-) delete mode 100644 libpsn00b/psxsio/_sio_control.s create mode 100644 libpsn00b/psxsio/sio.c delete mode 100644 libpsn00b/psxsio/siocons.c create mode 100644 libpsn00b/psxsio/tty.c (limited to 'libpsn00b/include/assert.h') diff --git a/CHANGELOG.md b/CHANGELOG.md index fb40eb6..e9d7e16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ to ensure the changelog can be parsed correctly. ------------------------------------------------------------------------------- -## 2022-10-19: 0.21 +## 2022-10-21: 0.21 spicyjpeg: @@ -31,6 +31,10 @@ spicyjpeg: for more advanced control of the drawing queue. The `getTPage()` macro now supports extended Y coordinates (512-1023) on systems with 2 MB of VRAM. +- psxsio: Removed `_sio_control()` and replaced it with a completely new + asynchronous buffered serial port driver. Rewritten the serial TTY driver to + make use of the new API. + ## 2022-10-16 spicyjpeg: diff --git a/libpsn00b/include/assert.h b/libpsn00b/include/assert.h index eabe369..e93c983 100644 --- a/libpsn00b/include/assert.h +++ b/libpsn00b/include/assert.h @@ -9,6 +9,9 @@ #ifndef __ASSERT_H #define __ASSERT_H +void _assert_abort(const char *file, int line, const char *expr); +void _sdk_log_inner(const char *fmt, ...); + #ifdef NDEBUG #define assert(expr) @@ -16,12 +19,10 @@ #else -void _assert_abort(const char *file, int line, const char *expr); -void _sdk_log_inner(const char *fmt, ...); - #define assert(expr) { \ if (!(expr)) _assert_abort(__FILE__, __LINE__, #expr); \ } + #ifdef SDK_LIBRARY_NAME #define _sdk_log(fmt, ...) _sdk_log_inner(SDK_LIBRARY_NAME ": " fmt, ##__VA_ARGS__) #else diff --git a/libpsn00b/include/psxsio.h b/libpsn00b/include/psxsio.h index 3f571d7..e0cc49b 100644 --- a/libpsn00b/include/psxsio.h +++ b/libpsn00b/include/psxsio.h @@ -1,64 +1,220 @@ +/* + * PSn00bSDK serial port library + * (C) 2019-2022 Lameguy64, spicyjpeg - MPL licensed + */ + #ifndef __PSXSIO_H #define __PSXSIO_H -#define SR_TXRDY 0x1 -#define SR_RXRDY 0x2 -#define SR_TXU 0x4 -#define SR_PERROR 0x8 -#define SR_OE 0x10 -#define SR_FE 0x20 -#define SR_DSR 0x80 -#define SR_CTS 0x100 -#define SR_IRQ 0x200 - -#define SIO_TXRDY 0x1 -#define SIO_RXRDY 0x2 -#define SIO_TXU 0x4 -#define SIO_PERROR 0x8 -#define SIO_OE 0x10 -#define SIO_FE 0x20 -#define SIO_DSR 0x80 -#define SIO_CTS 0x100 -#define SIO_IRQ 0x200 - -#define MR_CHLEN_5 0x00 -#define MR_CHLEN_6 0x04 -#define MR_CHLEN_7 0x08 -#define MR_CHLEN_8 0x0C -#define MR_PEN 0x10 -#define MR_P_EVEN 0x30 -#define MR_SB_01 0x40 -#define MR_SB_10 0x80 -#define MR_SB_11 0xc0 - -#define CR_TXEN 0x1 -#define CR_DTR 0x2 -#define CR_RXEN 0x4 -#define CR_BRK 0x8 -#define CR_INTRST 0x10 -#define CR_RTS 0x20 -#define CR_ERRRST 0x40 -#define CR_BUFSZ_1 0x00 -#define CR_BUFSZ_2 0x100 -#define CR_BUFSZ_4 0x200 -#define CR_BUFSZ_8 0x300 -#define CR_TXIEN 0x400 -#define CR_RXIEN 0x800 -#define CR_DSRIEN 0x1000 +#include + +/* Register definitions (used internally) */ + +typedef enum _SIO_StatusRegFlag { + SR_TXRDY = 1 << 0, + SR_RXRDY = 1 << 1, + SR_TXU = 1 << 2, + SR_PERROR = 1 << 3, + SR_OE = 1 << 4, + SR_FE = 1 << 5, + SR_DSR = 1 << 7, + SR_CTS = 1 << 8, + SR_IRQ = 1 << 9 +} SIO_StatusRegFlag; + +typedef enum _SIO_ModeRegFlag { + MR_BR_1 = 1 << 0, + MR_BR_16 = 2 << 0, + MR_BR_64 = 3 << 0, + MR_CHLEN_5 = 0 << 2, + MR_CHLEN_6 = 1 << 2, + MR_CHLEN_7 = 2 << 2, + MR_CHLEN_8 = 3 << 2, + MR_PEN = 1 << 4, + MR_P_EVEN = 1 << 5, + MR_SB_01 = 1 << 6, + MR_SB_10 = 2 << 6, + MR_SB_11 = 3 << 6 +} SIO_ModeRegFlag; + +typedef enum _SIO_ControlRegFlag { + CR_TXEN = 1 << 0, + CR_DTR = 1 << 1, + CR_RXEN = 1 << 2, + CR_BRK = 1 << 3, + CR_INTRST = 1 << 4, + CR_RTS = 1 << 5, + CR_ERRRST = 1 << 6, + CR_BUFSZ_1 = 0 << 8, + CR_BUFSZ_2 = 1 << 8, + CR_BUFSZ_4 = 2 << 8, + CR_BUFSZ_8 = 3 << 8, + CR_TXIEN = 1 << 10, + CR_RXIEN = 1 << 11, + CR_DSRIEN = 1 << 12 +} SIO_ControlRegFlag; + +typedef enum _SIO_FlowControl { + SIO_FC_NONE = 0, + SIO_FC_RTS_CTS = 1 + //SIO_FC_DTR_DSR = 2 +} SIO_FlowControl; + +/* Public API */ #ifdef __cplusplus extern "C" { #endif -int _sio_control(int cmd, int arg, int param); -void AddSIO(int baud); -void DelSIO(void); +/** + * @brief Resets the serial port, initializes the library's internal ring + * buffers and installs a serial IRQ handler. The given mode value (normally + * MR_CHLEN_8|MR_SB_01 for 8 data bits, 1 stop bit and no parity) is copied to + * the SIO_MODE register. Flow control is disabled by default (see + * SIO_SetFlowControl() for more details). + * + * This function must be called prior to using SIO_ReadByte(), SIO_ReadSync(), + * SIO_WriteByte(), SIO_WriteSync() or SIO_SetFlowControl(), and must not be + * called from an IRQ callback. + * + * @param baud Baud rate in bits per second + * @param mode Binary OR of SIO_ModeRegFlag enum members + */ +void SIO_Init(int baud, uint16_t mode); + +/** + * @brief Resets the serial port and removes the IRQ callback added by + * SIO_Init(), restoring any previously installed handler. + */ +void SIO_Quit(void); -void *Sio1Callback(void (*func)(void)); +/** + * @brief Changes the serial port's flow control mode. The following modes are + * available: + * + * - SIO_FC_NONE (default): do not assert RTS or DTR automatically and ignore + * DSR. Note that the hardware will still wait for CTS to be asserted before + * sending any data; there is no way to disable this behavior. + * - SIO_FC_RTS_CTS: assert RTS when the RX buffer is full and wait for CTS to + * be asserted before sending any data. + * + * The flow control mode shall only be changed while the TX and RX buffers are + * empty. + * + * @param mode + */ +void SIO_SetFlowControl(SIO_FlowControl mode); -// ORIGINAL -void WaitSIO(void); -int kbhit(); +/** + * @brief Reads a byte from the RX buffer. If the buffer is empty, blocks + * indefinitely until a byte is received. + * + * WARNING: this function shall not be used in a critical section or IRQ + * callback as no data is sent or received while interrupts are disabled. It + * also lacks a timeout, so consider polling for new data using SIO_ReadByte2() + * or SIO_ReadSync(1) and implementing a timeout instead. + * + * @return Received byte + */ +int SIO_ReadByte(void); + +/** + * @brief Non-blocking variant of SIO_ReadByte(). Reads a byte from the RX + * buffer or returns -1 if the buffer is empty. Unlike SIO_ReadByte() this + * function is safe to use in a critical section (although no data will be + * received while interrupts are disabled). + * + * @return Received byte, -1 if no data is available + */ +int SIO_ReadByte2(void); + +/** + * @brief Waits for at least one byte to be available in the RX buffer (if + * mode = 0) or returns the length of the RX buffer (if mode = 1). + * + * WARNING: this function shall not be used in a critical section or IRQ + * callback as no data is sent or received while interrupts are disabled. Using + * SIO_ReadSync(0) is additionally discouraged as it lacks a timeout; consider + * polling for new data using SIO_ReadByte2() or SIO_ReadSync(1) and + * implementing a timeout instead. + * + * @param mode + * @return Number of RX bytes in the buffer + */ +int SIO_ReadSync(int mode); + +/** + * @brief Registers a function to be called whenever a byte is received. The + * received byte is appended to the RX buffer and passed as an argument to the + * callback. The callback will run in the exception handler's context, so it + * should be as fast as possible and not use any function that relies on + * interrupts in order to work. + * + * @param func + * @return Previously set callback or NULL + */ +void *SIO_ReadCallback(void (*func)(uint8_t)); + +/** + * @brief Sends the given byte, or appends it to the TX buffer if the serial + * port is busy. If the buffer is full, blocks until the byte can be stored in + * the buffer (with a timeout). + * + * WARNING: this function shall not be used in a critical section or IRQ + * callback as no data is sent or received while interrupts are disabled. + * + * @param value + * @return Number of TX bytes previously pending, -1 in case of a timeout + */ +int SIO_WriteByte(uint8_t value); + +/** + * @brief Non-blocking variant of SIO_WriteByte(). Sends the given byte, or + * appends it to the TX buffer if the serial port is busy. If the buffer is + * full, returns -1 without actually sending the byte. Unlike SIO_WriteByte() + * this function is safe to use in a critical section (although no data will be + * sent while interrupts are disabled). + * + * @param value + * @return Number of TX bytes previously pending, -1 in case of failure + */ +int SIO_WriteByte2(uint8_t value); + +/** + * @brief Waits for all bytes pending in the TX buffer to be sent (if mode = 0) + * or returns the length of the TX buffer (if mode = 1). + * + * WARNING: this function shall not be used in a critical section or IRQ + * callback as no data is sent or received while interrupts are disabled. + * + * @param mode + * @return Number of TX bytes pending, -1 in case of a timeout (mode = 0) + */ +int SIO_WriteSync(int mode); + +/** + * @brief Installs a BIOS file driver to redirect TTY stdin/stdout (including + * BIOS messages as well as PSn00bSDK's own debug logging) to the serial port. + * Uses SIO_Init() internally. The port is configured for 8 data bits, 1 stop + * bit and no parity. + * + * This function shall only be used for debugging purposes. Picking a high baud + * rate is recommended as all TTY writes are blocking and bypass the TX buffer. + * + * NOTE: some executable loaders, such as Unirom and Caetla, already replace + * the BIOS TTY driver with a custom one. Calling AddSIO() will break the + * built-in TTY functionality of these loaders. + * + * @param baud Baud rate in bits per second + */ +void AddSIO(int baud); + +/** + * @brief Uninstalls the BIOS driver installed by AddSIO() and attempts to + * restore a "dummy" TTY driver. Calling this function is not recommended as + * any further TTY usage may crash the system. + */ +void DelSIO(void); #ifdef __cplusplus } diff --git a/libpsn00b/libc/memset.s b/libpsn00b/libc/memset.s index 5a1589d..73f92bd 100644 --- a/libpsn00b/libc/memset.s +++ b/libpsn00b/libc/memset.s @@ -9,6 +9,7 @@ memset: # If more than 16 bytes have to be written then take the "large" path, # otherwise use the code below. + blez $a2, .Lnull_count addiu $t0, $a2, -16 bgtz $t0, .Llarge_fill move $v0, $a0 # return_value = dest @@ -41,6 +42,10 @@ memset: jr $ra sb $a1, 0xf($a0) +.Lnull_count: + jr $ra + move $v0, $a0 # return_value = dest + .Llarge_fill: # Initialize fast filling by repeating the fill byte 4 times, so it can be # written 32 bits at a time. diff --git a/libpsn00b/psxcd/isofs.c b/libpsn00b/psxcd/isofs.c index cb60152..e00ddeb 100644 --- a/libpsn00b/psxcd/isofs.c +++ b/libpsn00b/psxcd/isofs.c @@ -1,4 +1,5 @@ +#undef SDK_LIBRARY_NAME #define SDK_LIBRARY_NAME "psxcd/iso" #include diff --git a/libpsn00b/psxetc/dl.c b/libpsn00b/psxetc/dl.c index 51c1d2d..fa5e74d 100644 --- a/libpsn00b/psxetc/dl.c +++ b/libpsn00b/psxetc/dl.c @@ -23,6 +23,7 @@ * of entries */ +#undef SDK_LIBRARY_NAME #define SDK_LIBRARY_NAME "psxetc/dl" #include @@ -234,7 +235,7 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { // insists on printing 64-bit addresses... wtf) and normalize the // type letter to upper case, then check if the entry is valid and // non-null. - void *address = (void *) address64; + void *address = (void *) ((uint32_t) address64); char _type = toupper(type_string[0]); uint32_t hash = _elf_hash(name); uint32_t hash_mod = hash % _symbol_map.nbucket; diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index 9eb4ea4..9f45f10 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -38,6 +38,9 @@ static volatile uint16_t _last_hblank; /* Private interrupt handlers */ +#define _ENTER_CRITICAL() uint16_t mask = IRQ_MASK; IRQ_MASK = 0; +#define _EXIT_CRITICAL() IRQ_MASK = mask; + static void _vblank_handler(void) { _vblank_counter++; @@ -51,9 +54,10 @@ static void _gpu_dma_handler(void) { __asm__ volatile(""); if (--_queue_length) { - volatile QueueEntry *entry = &_draw_queue[_queue_head++]; - _queue_head %= QUEUE_LENGTH; + int head = _queue_head; + _queue_head = (head + 1) % QUEUE_LENGTH; + volatile QueueEntry *entry = &_draw_queue[head]; entry->func(entry->arg1, entry->arg2, entry->arg3); } else { GPU_GP1 = 0x04000000; // Disable DMA request @@ -145,19 +149,22 @@ int VSync(int mode) { } void *VSyncHaltFunction(void (*func)(void)) { + //_ENTER_CRITICAL(); + void *old_callback = _vsync_halt_func; _vsync_halt_func = func; + //_EXIT_CRITICAL(); return old_callback; } void *VSyncCallback(void (*func)(void)) { - EnterCriticalSection(); + _ENTER_CRITICAL(); void *old_callback = _vsync_callback; _vsync_callback = func; - ExitCriticalSection(); + _EXIT_CRITICAL(); return old_callback; } @@ -176,37 +183,36 @@ int EnqueueDrawOp( // to checking if DMA is busy; disabling them afterwards would create a // race condition where the DMA transfer could end while interrupts are // being disabled. Interrupts are disabled through the IRQ_MASK register - // rather than by calling EnterCriticalSection() for performance reasons. - uint16_t mask = IRQ_MASK; - IRQ_MASK = 0; + // rather than via syscalls for performance reasons. + _ENTER_CRITICAL(); + int length = _queue_length; - if (_queue_length) { - int length = _queue_length; - - if (length >= QUEUE_LENGTH) { - IRQ_MASK = mask; - _sdk_log("draw queue overflow, dropping commands\n"); - return -1; - } + if (!length) { + _queue_length = 1; + _EXIT_CRITICAL(); - volatile QueueEntry *entry = &_draw_queue[_queue_tail++]; - _queue_tail %= QUEUE_LENGTH; - _queue_length = length + 1; - - entry->func = func; - entry->arg1 = arg1; - entry->arg2 = arg2; - entry->arg3 = arg3; + func(arg1, arg2, arg3); + return 0; + } + if (length >= QUEUE_LENGTH) { + _EXIT_CRITICAL(); - IRQ_MASK = mask; - return length; + _sdk_log("draw queue overflow, dropping commands\n"); + return -1; } - _queue_length = 1; + int tail = _queue_tail; + _queue_tail = (tail + 1) % QUEUE_LENGTH; + _queue_length = length + 1; + + volatile QueueEntry *entry = &_draw_queue[tail]; + entry->func = func; + entry->arg1 = arg1; + entry->arg2 = arg2; + entry->arg3 = arg3; - IRQ_MASK = mask; - func(arg1, arg2, arg3); - return 0; + _EXIT_CRITICAL(); + return length; } int DrawSync(int mode) { @@ -236,12 +242,12 @@ int DrawSync(int mode) { } void *DrawSyncCallback(void (*func)(void)) { - EnterCriticalSection(); + _ENTER_CRITICAL(); void *old_callback = _drawsync_callback; _drawsync_callback = func; - ExitCriticalSection(); + _EXIT_CRITICAL(); return old_callback; } diff --git a/libpsn00b/psxsio/_sio_control.s b/libpsn00b/psxsio/_sio_control.s deleted file mode 100644 index 6378def..0000000 --- a/libpsn00b/psxsio/_sio_control.s +++ /dev/null @@ -1,184 +0,0 @@ -.set noreorder - -.include "hwregs_a.inc" - -.section .text - -# Currently implemented serial control functions: -# -# cmd(a0) sub(a1) -# 0 0 Get serial status -# 0 1 Get control line status -# 0 2 Get serial mode -# 0 3 Get baud rate -# 0 4 Read 1 byte -# 1 1 Set serial control -# 1 2 Set serial mode -# 1 3 Set baud rate -# 1 4 Write 1 byte -# 2 0 Reset serial -# 2 1 Acknowledge serial - -.global _sio_control -.type _sio_control, @function -_sio_control: - - # a0 - command - # a1 - subcommand - # a2 - argument - - lui $a3, IOBASE - - beq $a0, $0, .Lcmd0 - nop - beq $a0, 1, .Lcmd1 - nop - beq $a0, 2, .Lcmd2 - nop - jr $ra - nop - - -.Lcmd0: - - beq $a1, $0, .Lcmd0arg0 - nop - beq $a1, 1, .Lcmd0arg1 - nop - beq $a1, 2, .Lcmd0arg2 - nop - beq $a1, 3, .Lcmd0arg3 - nop - beq $a1, 4, .Lcmd0arg4 - nop - jr $ra - nop - -.Lcmd0arg0: # Get SIO status - - lhu $v0, SIO_STAT($a3) - nop - - jr $ra - andi $v0, 0x3FF - -.Lcmd0arg1: # Get control line status - - lhu $v1, SIO_CTRL($a3) - nop - srl $v0, $v1, 1 - andi $v0, 0x1 - srl $v1, 4 - andi $v1, 0x2 - - jr $ra - or $v0, $v1 - - -.Lcmd0arg2: # Get serial mode - - lhu $v0, SIO_MODE($a3) - nop - jr $ra - andi $v0, 0xFF - -.Lcmd0arg3: - - lui $v1, 0x1f - lhu $v0, SIO_BAUD($a3) - ori $v1, 0xa400 - div $v1, $v0 - nop - nop - mflo $v0 - jr $ra - nop - -.Lcmd0arg4: # Serial RX read - - lbu $v0, SIO_TXRX($a3) - nop - jr $ra - nop - - -.Lcmd1: - - beq $a1, 1, .Lcmd1arg1 - nop - beq $a1, 2, .Lcmd1arg2 - nop - beq $a1, 3, .Lcmd1arg3 - nop - beq $a1, 4, .Lcmd1arg4 - nop - jr $ra - nop - -.Lcmd1arg1: - - andi $v0, $a2, 0x1CFF - sh $a2, SIO_CTRL($a3) - jr $ra - nop - -.Lcmd1arg2: - - sh $a2, SIO_MODE($a3) - jr $ra - nop - -.Lcmd1arg3: - - lui $v0, 0x1f - ori $v0, 0xa400 - divu $v0, $a2 - bnez $a2, .Lgood_baud - nop - jr $ra - nop - -.Lgood_baud: - - mflo $v0 - sh $v0, SIO_BAUD($a3) - nop - jr $ra - nop - -.Lcmd1arg4: - - sb $a2, SIO_TXRX($a3) - nop - jr $ra - nop - -.Lcmd2: - - beq $a1, $0 , .Lcmd2arg0 - li $v0, 1 - beq $a1, $v0, .Lcmd2arg1 - nop - jr $ra - nop - -.Lcmd2arg0: - - li $v0, 0x40 - sh $v0, SIO_CTRL($a3) - sh $0 , SIO_MODE($a3) - sh $0 , SIO_BAUD($a3) - nop - jr $ra - nop - -.Lcmd2arg1: - - lhu $v0, SIO_CTRL($a3) - nop - ori $v0, 0x10 - sh $v0, SIO_CTRL($a3) - jr $ra - nop - - \ No newline at end of file diff --git a/libpsn00b/psxsio/sio.c b/libpsn00b/psxsio/sio.c new file mode 100644 index 0000000..3fb3caf --- /dev/null +++ b/libpsn00b/psxsio/sio.c @@ -0,0 +1,263 @@ +/* + * PSn00bSDK buffered serial port driver + * (C) 2022 spicyjpeg - MPL licensed + * + * TODO: add proper support for DTR/DSR flow control + */ + +#include +#include +#include +#include +#include +#include + +#define BUFFER_LENGTH 128 +#define SIO_SYNC_TIMEOUT 0x100000 + +/* Private types */ + +typedef struct { + uint8_t data[BUFFER_LENGTH]; + uint8_t head, tail, length; +} RingBuffer; + +/* Internal globals */ + +static SIO_FlowControl _flow_control; +static uint16_t _ctrl_reg_flag; + +static void (*_read_callback)(uint8_t) = (void *) 0; +static void (*_old_sio_handler)(void) = (void *) 0; + +static volatile RingBuffer _tx_buffer, _rx_buffer; + +/* Private interrupt handler */ + +#define _ENTER_CRITICAL() uint16_t mask = IRQ_MASK; IRQ_MASK = 0; +#define _EXIT_CRITICAL() IRQ_MASK = mask; + +static void _sio_handler(void) { + // Handle any incoming bytes. + while (SIO_STAT & SR_RXRDY) { + uint8_t value = SIO_TXRX; + int length = _rx_buffer.length; + + if (length >= BUFFER_LENGTH) { + //_sdk_log("RX overrun, dropping bytes\n"); + break; + } + + int tail = _rx_buffer.tail; + _rx_buffer.tail = (tail + 1) % BUFFER_LENGTH; + _rx_buffer.length = length + 1; + + _rx_buffer.data[tail] = value; + if (_read_callback) + _read_callback(value); + } + + // Send the next byte in the buffer if the TX unit is ready. Note that + // checking for CTS is unnecessary as the serial port is already hardwired + // to do so. + if (SIO_STAT & (SR_TXRDY | SR_TXU)) { + int length = _tx_buffer.length; + + if (length) { + int head = _tx_buffer.head; + _tx_buffer.head = (head + 1) % BUFFER_LENGTH; + _tx_buffer.length = length - 1; + + SIO_CTRL |= CR_TXIEN; + SIO_TXRX = _tx_buffer.data[head]; + } else { + SIO_CTRL &= CR_TXIEN ^ 0xffff; + } + } + + // Acknowledge the IRQ and update flow control signals. + if (_rx_buffer.length < BUFFER_LENGTH) + SIO_CTRL = CR_INTRST | (SIO_CTRL | _ctrl_reg_flag); + else + SIO_CTRL = CR_INTRST | (SIO_CTRL & (_ctrl_reg_flag ^ 0xffff)); +} + +/* Serial port initialization API */ + +void SIO_Init(int baud, uint16_t mode) { + EnterCriticalSection(); + _old_sio_handler = InterruptCallback(8, &_sio_handler); + + SIO_CTRL = CR_ERRRST; + SIO_MODE = (mode & 0xfffc) | MR_BR_16; + SIO_BAUD = (uint16_t) ((int) 0x1fa400 / baud); + SIO_CTRL = CR_TXEN | CR_RXEN | CR_RXIEN; + + _tx_buffer.head = 0; + _tx_buffer.tail = 0; + _tx_buffer.length = 0; + _rx_buffer.head = 0; + _rx_buffer.tail = 0; + _rx_buffer.length = 0; + + _flow_control = SIO_FC_NONE; + _ctrl_reg_flag = 0; + + ExitCriticalSection(); +} + +void SIO_Quit(void) { + EnterCriticalSection(); + InterruptCallback(8, _old_sio_handler); + + SIO_CTRL = CR_ERRRST; + + ExitCriticalSection(); +} + +void SIO_SetFlowControl(SIO_FlowControl mode) { + _ENTER_CRITICAL(); + + switch (mode) { + case SIO_FC_NONE: + _flow_control = SIO_FC_NONE; + _ctrl_reg_flag = 0; + + SIO_CTRL &= 0xffff ^ CR_DSRIEN; + break; + + case SIO_FC_RTS_CTS: + _flow_control = SIO_FC_RTS_CTS; + _ctrl_reg_flag = CR_RTS; + + SIO_CTRL &= 0xffff ^ CR_DSRIEN; + break; + + /*case SIO_FC_DTR_DSR: + _flow_control = SIO_FC_DTR_DSR; + _ctrl_reg_flag = CR_DTR; + + SIO_CTRL |= CR_DSRIEN; + break;*/ + } + + _EXIT_CRITICAL(); +} + +/* Reading API */ + +int SIO_ReadByte(void) { + /*for (int i = SIO_SYNC_TIMEOUT; i; i--) { + if (_rx_buffer.length) + return SIO_ReadByte2(); + }*/ + while (!_rx_buffer.length) + __asm__ volatile(""); + + return SIO_ReadByte2(); +} + +int SIO_ReadByte2(void) { + if (!_rx_buffer.length) + return -1; + + _ENTER_CRITICAL(); + + int head = _rx_buffer.head; + _rx_buffer.head = (head + 1) % BUFFER_LENGTH; + _rx_buffer.length--; + + _EXIT_CRITICAL(); + return _rx_buffer.data[head]; +} + +int SIO_ReadSync(int mode) { + if (mode) + return _rx_buffer.length; + + /*for (int i = SIO_SYNC_TIMEOUT; i; i--) { + if (_rx_buffer.length) + return 0; + }*/ + while (!_rx_buffer.length) + __asm__ volatile(""); + + return 0; +} + +void *SIO_ReadCallback(void (*func)(uint8_t)) { + EnterCriticalSection(); + + void *old_callback = _read_callback; + _read_callback = func; + + ExitCriticalSection(); +} + +/* Writing API */ + +int SIO_WriteByte(uint8_t value) { + for (int i = SIO_SYNC_TIMEOUT; i; i--) { + if (_tx_buffer.length < BUFFER_LENGTH) + return SIO_WriteByte2(value); + } + + //_sdk_log("SIO_WriteByte() timeout\n"); + return -1; +} + +int SIO_WriteByte2(uint8_t value) { + // If the TX unit is currently busy, append the byte to the buffer instead + // of sending it immediately. Note that interrupts must be disabled *prior* + // to checking if TX is busy; disabling them afterwards would create a race + // condition where the transfer could end while interrupts are being + // disabled. Interrupts are disabled through the IRQ_MASK register rather + // than via syscalls for performance reasons. + _ENTER_CRITICAL(); + + if (SIO_STAT & (SR_TXRDY | SR_TXU)) { + SIO_TXRX = value; + _EXIT_CRITICAL(); + return 0; + } + + int length = _tx_buffer.length; + + if (length >= BUFFER_LENGTH) { + _EXIT_CRITICAL(); + + //_sdk_log("TX overrun, dropping bytes\n"); + return -1; + } + + int tail = _tx_buffer.tail; + _tx_buffer.tail = (tail + 1) % BUFFER_LENGTH; + _tx_buffer.length = length + 1; + + _tx_buffer.data[tail] = value; + SIO_CTRL |= CR_TXIEN; + + _EXIT_CRITICAL(); + return length; +} + +int SIO_WriteSync(int mode) { + if (mode) + return _tx_buffer.length; + + // Wait for the buffer to become empty. + for (int i = SIO_SYNC_TIMEOUT; i; i--) { + if (!_tx_buffer.length) + break; + } + + if (!_tx_buffer.length) { + // Wait for the TX unit to finish sending the last byte. + while (!(SIO_STAT & (SR_TXRDY | SR_TXU))) + __asm__ volatile(""); + } else { + //_sdk_log("SIO_WriteSync() timeout\n"); + } + + return _tx_buffer.length; +} diff --git a/libpsn00b/psxsio/siocons.c b/libpsn00b/psxsio/siocons.c deleted file mode 100644 index 5937920..0000000 --- a/libpsn00b/psxsio/siocons.c +++ /dev/null @@ -1,220 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#define SIO_BUFF_LEN 32 - -static volatile int _sio_key_pending; - -static volatile int _sio_buff_rpos; -static volatile int _sio_buff_wpos; -static volatile char _sio_buff[SIO_BUFF_LEN]; - -static void _sio_init() { - - _sio_key_pending = 0; - - memset((void*)_sio_buff, 0, SIO_BUFF_LEN); - _sio_buff_rpos = 0; - _sio_buff_wpos = 0; - -} - -static int _sio_open(FCB *fcb, const char* file, int mode) { - - fcb->diskid = 1; - - return 0; - -} - -static int _sio_inout(FCB *fcb, int cmd) { - - int i; - - if(cmd == 2) { // Write - - for(i=0; itrns_len; i++) { - while(!(_sio_control(0, 0, 0) & SR_TXU)); - _sio_control(1, 4, ((char*)fcb->trns_addr)[i]); - } - - return fcb->trns_len; - - } else if (cmd == 1) { // Read - - /*for(i=0; itrns_len; i++) { - while(!(_sio_control(0, 0, 0) & SR_RXRDY)); - ((char*)fcb->trns_addr)[i] = _sio_control(0, 4, 0); - }*/ - - - - for(i=0; itrns_len; i++) { - - while( _sio_key_pending == 0 ); - - ((char*)fcb->trns_addr)[i] = _sio_buff[_sio_buff_rpos]; - _sio_key_pending--; - _sio_buff_rpos++; - if( _sio_buff_rpos >= SIO_BUFF_LEN ) - { - _sio_buff_rpos = 0; - } - - } - - return fcb->trns_len; - - } - - return 0; - -} - -static int _sio_ioctl(FCB *fcb, int cmd, int arg) -{ - if( cmd == FIOCSCAN ) - { - if( _sio_key_pending ) - { - return 0; - } - } - - return -1; -} - -static int _sio_close(int h) { - - return h; - -} - -static void _sio_tty_cb(void) -{ - _sio_key_pending++; - - // Get received byte - if( _sio_key_pending < SIO_BUFF_LEN ) - { - _sio_buff[_sio_buff_wpos] = _sio_control(0, 4, 0); - _sio_buff_wpos++; - if( _sio_buff_wpos >= SIO_BUFF_LEN ) - { - _sio_buff_wpos = 0; - } - } - else - { - _sio_control(0, 4, 0); - } - - // Acknowledge SIO IRQ - _sio_control(2, 1, 0); -} - -static int _sio_dummy(void) -{ - return -1; -} - -static DCB _sio_dcb = { - "tty", - 0x3, - 0x1, - 0x0, - (void*)_sio_init, // init - (void*)_sio_open, // open - (void*)_sio_inout, // inout - _sio_close, // close - _sio_ioctl, // ioctl - _sio_dummy, // read - _sio_dummy, // write - _sio_dummy, // erase - _sio_dummy, // undelete - _sio_dummy, // firstfile - _sio_dummy, // nextfile - _sio_dummy, // format - _sio_dummy, // chdir - _sio_dummy, // rename - _sio_dummy, // remove - _sio_dummy // testdevice -}; - - -volatile void (*_sio_callback)(void) = NULL; - -void AddSIO(int baud) { - - _sio_control(2, 0, 0); - _sio_control(1, 2, MR_SB_01|MR_CHLEN_8|0x02); - _sio_control(1, 3, baud); - _sio_control(1, 1, CR_RXEN|CR_TXEN|CR_RXIEN); - - close(0); - close(1); - - DelDev(_sio_dcb.name); - AddDev(&_sio_dcb); - - Sio1Callback(_sio_tty_cb); - - open(_sio_dcb.name, 2); - open(_sio_dcb.name, 1); - -} - -void DelSIO(void) { - - Sio1Callback(NULL); - - // Reset serial interface - _sio_control(2, 0, 0); - - // Remove TTY device - DelDev(_sio_dcb.name); - - // Add dummy TTY device - AddDummyTty(); - -} - -void WaitSIO(void) { - - while((_sio_control(0, 0, 0)&(SR_RXRDY)) != (SR_RXRDY)); - _sio_control(0, 4, NULL); - -} - -void *Sio1Callback(void (*func)()) { - - void *old_isr; //= *((int*)&_sio_callback); - - EnterCriticalSection(); - - if( func ) { - - old_isr = InterruptCallback(8, func); - //_sio_callback = func; - - } else { - - old_isr = InterruptCallback(8, NULL); - //_sio_callback = NULL; - - } - - ExitCriticalSection(); - - return old_isr; - -} - -int kbhit() -{ - return(_sio_key_pending>0); -} diff --git a/libpsn00b/psxsio/tty.c b/libpsn00b/psxsio/tty.c new file mode 100644 index 0000000..4dc9fd1 --- /dev/null +++ b/libpsn00b/psxsio/tty.c @@ -0,0 +1,107 @@ +/* + * PSn00bSDK serial port BIOS TTY driver + * (C) 2019-2022 Lameguy64, spicyjpeg - MPL licensed + * + * This driver is designed to be as simple and reliable as possible: as such it + * only relies on the SIO_*() API for receiving and sends data synchronously. + * This allows printf() to work without issues, albeit slowly, if called from a + * critical section or even from an interrupt handler. + */ + +#include +#include +#include +#include + +/* TTY device control block */ + +static int _sio_open(FCB *fcb, const char* file, int mode) { + fcb->diskid = 1; + return 0; +} + +static int _sio_inout(FCB *fcb, int cmd) { + char *ptr = (char*) fcb->trns_addr; + + switch (cmd) { + case 1: // read + for (int i = 0; i < fcb->trns_len; i++) + *(ptr++) = (char) SIO_ReadByte(); + + return fcb->trns_len; + + case 2: // write + for (int i = 0; i < fcb->trns_len; i++) { + while (!(SIO_STAT & (SR_TXRDY | SR_TXU))) + __asm__ volatile(""); + + SIO_TXRX = *(ptr++); + } + + return fcb->trns_len; + + default: + return 0; + } +} + +static int _sio_close(FCB *fcb) { + return 0; +} + +static int _sio_ioctl(FCB *fcb, int cmd, int arg) { + switch (cmd) { + case FIOCSCAN: + return SIO_ReadSync(1) ? 0 : -1; + + default: + return -1; + } +} + +static int _sio_dummy(void) { + return -1; +} + +static DCB _sio_dcb = { + .name = "tty", + .flags = 3, + .ssize = 1, + .desc = "PSXSIO SERIAL CONSOLE", + .f_init = &_sio_dummy, + .f_open = &_sio_open, + .f_inout = &_sio_inout, + .f_close = &_sio_close, + .f_ioctl = &_sio_ioctl, + .f_read = &_sio_dummy, + .f_write = &_sio_dummy, + .f_erase = &_sio_dummy, + .f_undelete = &_sio_dummy, + .f_firstfile = &_sio_dummy, + .f_nextfile = &_sio_dummy, + .f_format = &_sio_dummy, + .f_chdir = &_sio_dummy, + .f_rename = &_sio_dummy, + .f_remove = &_sio_dummy, + .f_testdevice = &_sio_dummy +}; + +/* Public API */ + +void AddSIO(int baud) { + SIO_Init(baud, MR_SB_01 | MR_CHLEN_8); + + close(0); + close(1); + DelDev(_sio_dcb.name); + AddDev(&_sio_dcb); + open(_sio_dcb.name, 2); + open(_sio_dcb.name, 1); +} + +void DelSIO(void) { + SIO_Quit(); + + DelDev(_sio_dcb.name); + AddDummyTty(); +} -- cgit v1.2.3 From 5f25c0bf306d316c87fca9d3fe160d6661be230d Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Sun, 23 Oct 2022 15:03:16 +0200 Subject: Library bugfixes and additions, _sdk_log_inner() removal --- libpsn00b/include/assert.h | 7 ++- libpsn00b/include/psxsio.h | 14 +++-- libpsn00b/include/psxspu.h | 22 +++++-- libpsn00b/libc/_start.s | 17 +++++ libpsn00b/libc/_stubs.s | 27 -------- libpsn00b/libc/abort.c | 8 ++- libpsn00b/psxpress/vlc.s | 152 ++++++++++++++++++--------------------------- libpsn00b/psxsio/sio.c | 16 +++-- libpsn00b/psxspu/common.c | 9 ++- 9 files changed, 131 insertions(+), 141 deletions(-) create mode 100644 libpsn00b/libc/_start.s delete mode 100644 libpsn00b/libc/_stubs.s (limited to 'libpsn00b/include/assert.h') diff --git a/libpsn00b/include/assert.h b/libpsn00b/include/assert.h index e93c983..12212af 100644 --- a/libpsn00b/include/assert.h +++ b/libpsn00b/include/assert.h @@ -9,8 +9,9 @@ #ifndef __ASSERT_H #define __ASSERT_H +#include + void _assert_abort(const char *file, int line, const char *expr); -void _sdk_log_inner(const char *fmt, ...); #ifdef NDEBUG @@ -24,9 +25,9 @@ void _sdk_log_inner(const char *fmt, ...); } #ifdef SDK_LIBRARY_NAME -#define _sdk_log(fmt, ...) _sdk_log_inner(SDK_LIBRARY_NAME ": " fmt, ##__VA_ARGS__) +#define _sdk_log(fmt, ...) printf(SDK_LIBRARY_NAME ": " fmt, ##__VA_ARGS__) #else -#define _sdk_log(fmt, ...) _sdk_log_inner(fmt, ##__VA_ARGS__) +#define _sdk_log(fmt, ...) printf(fmt, ##__VA_ARGS__) #endif #endif diff --git a/libpsn00b/include/psxsio.h b/libpsn00b/include/psxsio.h index e0cc49b..d5f7d9a 100644 --- a/libpsn00b/include/psxsio.h +++ b/libpsn00b/include/psxsio.h @@ -145,15 +145,19 @@ int SIO_ReadSync(int mode); /** * @brief Registers a function to be called whenever a byte is received. The - * received byte is appended to the RX buffer and passed as an argument to the - * callback. The callback will run in the exception handler's context, so it - * should be as fast as possible and not use any function that relies on - * interrupts in order to work. + * received byte is passed as an argument to the callback, which shall then + * return a zero value to also store the byte in the RX buffer or a non-zero + * value to drop it. This can be used to e.g. filter or validate incoming data, + * or to bypass the library's RX buffer for custom buffering purposes. + * + * The callback will run in the exception handler's context, so it should be as + * fast as possible and shall not call any function that relies on interrupts + * being enabled. * * @param func * @return Previously set callback or NULL */ -void *SIO_ReadCallback(void (*func)(uint8_t)); +void *SIO_ReadCallback(int (*func)(uint8_t)); /** * @brief Sends the given byte, or appends it to the TX buffer if the serial diff --git a/libpsn00b/include/psxspu.h b/libpsn00b/include/psxspu.h index cf78e3d..7858e88 100644 --- a/libpsn00b/include/psxspu.h +++ b/libpsn00b/include/psxspu.h @@ -73,6 +73,20 @@ typedef struct _SpuCommonAttr { SpuExtAttr cd, ext; } SpuCommonAttr; +/* Macros */ + +#define getSPUAddr(addr) ((uint16_t) (((addr) + 7) / 8)) +#define getSPUSampleRate(rate) ((uint16_t) (((rate) * (1 << 12)) / 44100)) + +#define getSPUADSR(ar, dr, sr, rr, sl) ( \ + (sl) | \ + ((dr) << 4) | \ + ((ar) << 8) | \ + ((rr) << 16) | \ + ((sr) << 22) | \ + (1 << 30) \ +) + /* "Useless" macros for official SDK compatibility */ #define SpuSetCommonMasterVolume(left, right) \ @@ -87,18 +101,18 @@ typedef struct _SpuCommonAttr { ((enable) ? (SPU_CTRL |= 0x0002) : (SPU_CTRL &= 0xfffd)) #define SpuSetReverbAddr(addr) \ - (SPU_REVERB_ADDR = ((addr) + 7) / 8) + (SPU_REVERB_ADDR = getSPUAddr(addr)) #define SpuSetIRQAddr(addr) \ - (SPU_IRQ_ADDR = ((addr) + 7) / 8) + (SPU_IRQ_ADDR = getSPUAddr(addr)) #define SpuSetVoiceVolume(ch, left, right) \ (SPU_CH_VOL_L(ch) = (left), SPU_CH_VOL_R(ch) = (right)) #define SpuSetVoicePitch(ch, pitch) \ (SPU_CH_FREQ(ch) = (pitch)) #define SpuSetVoiceStartAddr(ch, addr) \ - (SPU_CH_ADDR(ch) = ((addr) + 7) / 8) + (SPU_CH_ADDR(ch) = getSPUAddr(addr)) #define SpuSetVoiceADSR(ch, ar, dr, sr, rr, sl) \ - (SPU_CH_ADSR(ch) = ((sl)) | ((dr) << 4) | ((ar) << 8) | ((rr) << 16) | ((sr) << 22) | (1 << 30)) + (SPU_CH_ADSR(ch) = getSPUADSR(ar, dr, sr, rr, sl)) #define SpuSetKey(enable, voice_bit) \ ((enable) ? (SPU_KEY_ON = (voice_bit)) : (SPU_KEY_OFF = (voice_bit))) diff --git a/libpsn00b/libc/_start.s b/libpsn00b/libc/_start.s new file mode 100644 index 0000000..fcd4c4c --- /dev/null +++ b/libpsn00b/libc/_start.s @@ -0,0 +1,17 @@ +# PSn00bSDK _start() trampoline +# (C) 2022 spicyjpeg - MPL licensed +# +# This file provides a weak function that can be easily overridden to e.g. set +# $sp or perform additional initialization before the "real" _start() function +# (_start_inner()) is called. + +.set noreorder + +.section .text._start +.global _start +.type _start, @function +.weak _start +_start: + la $gp, _gp + j _start_inner + nop diff --git a/libpsn00b/libc/_stubs.s b/libpsn00b/libc/_stubs.s deleted file mode 100644 index aa7bfbe..0000000 --- a/libpsn00b/libc/_stubs.s +++ /dev/null @@ -1,27 +0,0 @@ -# PSn00bSDK _start() trampoline and logging endpoint -# (C) 2022 spicyjpeg - MPL licensed -# -# This file provides a weak function that can be easily overridden to e.g. set -# $sp or perform additional initialization before the "real" _start() function -# (_start_inner()) is called. The _sdk_log_inner() function called by other -# libraries to log debug messages can also be overridden in a similar way. - -.set noreorder - -.section .text._start -.global _start -.type _start, @function -.weak _start -_start: - la $gp, _gp - j _start_inner - nop - -.section .text._sdk_log_inner -.global _sdk_log_inner -.type _sdk_log_inner, @function -.weak _sdk_log_inner -_sdk_log_inner: - li $t2, 0xa0 - jr $t2 - li $t1, 0x3f diff --git a/libpsn00b/libc/abort.c b/libpsn00b/libc/abort.c index 9a4661a..0a3c325 100644 --- a/libpsn00b/libc/abort.c +++ b/libpsn00b/libc/abort.c @@ -3,13 +3,15 @@ * (C) 2022 spicyjpeg - MPL licensed */ +#undef SDK_LIBRARY_NAME + #include #include /* Internal function used by assert() macro */ void _assert_abort(const char *file, int line, const char *expr) { - _sdk_log_inner("%s:%d: assert(%s)\n", file, line, expr); + _sdk_log("%s:%d: assert(%s)\n", file, line, expr); for (;;) __asm__ volatile(""); @@ -18,7 +20,7 @@ void _assert_abort(const char *file, int line, const char *expr) { /* Standard abort */ void abort(void) { - _sdk_log_inner("abort()\n"); + _sdk_log("abort()\n"); for (;;) __asm__ volatile(""); @@ -27,7 +29,7 @@ void abort(void) { /* Pure virtual function call (C++) */ void __cxa_pure_virtual(void) { - _sdk_log_inner("__cxa_pure_virtual()\n"); + _sdk_log("__cxa_pure_virtual()\n"); for (;;) __asm__ volatile(""); diff --git a/libpsn00b/psxpress/vlc.s b/libpsn00b/psxpress/vlc.s index 885a3f7..75e33d3 100644 --- a/libpsn00b/psxpress/vlc.s +++ b/libpsn00b/psxpress/vlc.s @@ -29,17 +29,17 @@ .set VLC_Context_block_index, 20 .set VLC_Context_coeff_index, 21 -.set DECDCTSMALLTAB_lut0, 0 -.set DECDCTSMALLTAB_lut2, 4 -.set DECDCTSMALLTAB_lut3, 36 -.set DECDCTSMALLTAB_lut4, 292 -.set DECDCTSMALLTAB_lut5, 308 -.set DECDCTSMALLTAB_lut7, 324 -.set DECDCTSMALLTAB_lut8, 356 -.set DECDCTSMALLTAB_lut9, 420 -.set DECDCTSMALLTAB_lut10, 484 -.set DECDCTSMALLTAB_lut11, 548 -.set DECDCTSMALLTAB_lut12, 612 +.set DECDCTTAB_lut0, 0 +.set DECDCTTAB_lut2, 4 +.set DECDCTTAB_lut3, 36 +.set DECDCTTAB_lut4, 292 +.set DECDCTTAB_lut5, 308 +.set DECDCTTAB_lut7, 324 +.set DECDCTTAB_lut8, 356 +.set DECDCTTAB_lut9, 420 +.set DECDCTTAB_lut10, 484 +.set DECDCTTAB_lut11, 548 +.set DECDCTTAB_lut12, 612 .section .text.DecDCTvlcStart .global DecDCTvlcStart @@ -115,7 +115,7 @@ _vlc_skip_context_load: # Obtain the addresses of the lookup table and jump area in advance so that # they don't have to be retrieved for each coefficient decoded. lw $t8, _vlc_huffman_table - la $t9, .Lac_jump_area + la $t9, .Lac_prefix_10 beqz $a2, .Lstop_processing addiu $a1, 4 # output = (uint16_t *) &output[1] @@ -123,32 +123,32 @@ _vlc_skip_context_load: .Lprocess_next_code_loop: # while (max_size) # This is the "hot" part of the decoder, executed for each code in the # bitstream. The first step is to determine if the next code is a DC or AC - # coefficient. The GTE is also given the task of counting the number of - # leading zeroes/ones, which takes 2 more cycles. + # coefficient. bnez $t7, .Lprocess_ac_coefficient - mtc2 $t0, $30 + addiu $t7, 1 # coeff_index++ bnez $t4, .Lprocess_dc_v3_coefficient - #nop + li $v1, 0x01ff .Lprocess_dc_v2_coefficient: # if (!coeff_index && !is_v3) # The DC coefficient in version 2 frames is not compressed. Value 0x1ff is # used to signal the end of the bitstream. srl $v0, $t0, 22 # prefix = (window >> (32 - 10)) - li $v1, 0x01ff beq $v0, $v1, .Lstop_processing # if (prefix == 0x1ff) break or $v0, $t3 # *output = prefix | quant_scale sll $t0, 10 # window <<= 10 - addiu $t5, -10 # bit_offset -= 10 b .Lwrite_value - addiu $t7, 1 # coeff_index++ + addiu $t5, -10 # bit_offset -= 10 .Lprocess_dc_v3_coefficient: # if (!coeff_index && is_v3) # TODO: version 3 is currently not supported. jr $ra li $v0, -1 - #b .Lwrite_value .Lprocess_ac_coefficient: # if (coeff_index) + # Start counting the number of leading zeroes/ones using the GTE. This + # takes 2 more cycles. + mtc2 $t0, $30 + # Check whether the prefix code is one of the shorter, more common ones. srl $v0, $t0, 30 li $v1, 3 @@ -157,33 +157,34 @@ _vlc_skip_context_load: beq $v0, $v1, .Lac_prefix_10 li $v1, 1 beq $v0, $v1, .Lac_prefix_01 - #srl $v0, $t0, 29 - #beq $v0, $v1, .Lac_prefix_001 - #nop + nop # If the code is longer, retrieve the number of leading zeroes from the GTE # and use it as an index into the jump area. Each block in the area is 8 # instructions long and handles decoding a specific prefix. mfc2 $v0, $31 - nop - andi $v0, 15 # jump_addr = &ac_jump_area[(prefix % 16) * 8 * sizeof(u32)] - sll $v0, 5 + li $v1, 11 + bgt $v0, $v1, .Lreturn_error # if (prefix > 11) return -1 + sll $v0, 5 # jump_addr = &ac_jump_area[prefix * 8 * sizeof(u32)] addu $v0, $t9 jr $v0 nop +.Lreturn_error: + jr $ra + li $v0, -1 + .Lac_prefix_11: # Prefix 11 is followed by a single bit. srl $v0, $t0, 28 # index = ((window >> (32 - 2 - 1)) & 1) * sizeof(u16) andi $v0, 2 addu $v0, $t8 # value = table->lut0[index] - lhu $v0, DECDCTSMALLTAB_lut0($v0) + lhu $v0, DECDCTTAB_lut0($v0) sll $t0, 3 # window <<= 3 - addiu $t5, -3 # bit_offset -= 3 b .Lwrite_value - addiu $t7, 1 # coeff_index++ + addiu $t5, -3 # bit_offset -= 3 + #.word 0 -.Lac_jump_area: .Lac_prefix_10: # Prefix 10 marks the end of a block. li $v0, 0xfe00 # value = 0xfe00 @@ -202,11 +203,10 @@ _vlc_skip_context_load: srl $v0, $t0, 25 # index = ((window >> (32 - 2 - 3)) & 7) * sizeof(u32) andi $v0, 28 addu $v0, $t8 # value = table->lut2[index] - lw $v0, DECDCTSMALLTAB_lut2($v0) - addiu $t7, 1 # coeff_index++ + lw $v0, DECDCTTAB_lut2($v0) b .Lupdate_window_and_write srl $v1, $v0, 16 # length = value >> 16 - .word 0 + .word 0, 0 .Lac_prefix_001: # Prefix 001 can be followed by a 6-bit lookup index starting with 00, or a @@ -214,136 +214,106 @@ _vlc_skip_context_load: srl $v0, $t0, 21 # index = ((window >> (32 - 3 - 6)) & 63) * sizeof(u32) andi $v0, 252 addu $v0, $t8 # value = table->lut3[index] - lw $v0, DECDCTSMALLTAB_lut3($v0) - addiu $t7, 1 # coeff_index++ + lw $v0, DECDCTTAB_lut3($v0) b .Lupdate_window_and_write srl $v1, $v0, 16 # length = value >> 16 - .word 0 + .word 0, 0 .Lac_prefix_0001: # Prefix 0001 is followed by a 3-bit lookup index. srl $v0, $t0, 24 # index = ((window >> (32 - 4 - 3)) & 7) * sizeof(u16) andi $v0, 14 addu $v0, $t8 # value = table->lut4[index] - lhu $v0, DECDCTSMALLTAB_lut4($v0) + lhu $v0, DECDCTTAB_lut4($v0) sll $t0, 7 # window <<= 4 + 3 - addiu $t5, -7 # bit_offset -= 4 + 3 b .Lwrite_value - addiu $t7, 1 # coeff_index++ + addiu $t5, -7 # bit_offset -= 4 + 3 + .word 0 .Lac_prefix_00001: # Prefix 00001 is followed by a 3-bit lookup index. srl $v0, $t0, 23 # index = ((window >> (32 - 5 - 3)) & 7) * sizeof(u16) andi $v0, 14 addu $v0, $t8 # value = table->lut5[index] - lhu $v0, DECDCTSMALLTAB_lut5($v0) + lhu $v0, DECDCTTAB_lut5($v0) sll $t0, 8 # window <<= 5 + 3 - addiu $t5, -8 # bit_offset -= 5 + 3 b .Lwrite_value - addiu $t7, 1 # coeff_index++ + addiu $t5, -8 # bit_offset -= 5 + 3 + .word 0 .Lac_prefix_000001: # Prefix 000001 is an escape code followed by a full 16-bit MDEC value. srl $v0, $t0, 10 # value = window >> (32 - 6 - 16) sll $t0, 22 # window <<= 6 + 16 - addiu $t5, -22 # bit_offset -= 6 + 16 b .Lwrite_value - addiu $t7, 1 # coeff_index++ - .word 0, 0, 0 + addiu $t5, -22 # bit_offset -= 6 + 16 + .word 0, 0, 0, 0 .Lac_prefix_0000001: # Prefix 0000001 is followed by a 4-bit lookup index. srl $v0, $t0, 20 # index = ((window >> (32 - 7 - 4)) & 15) * sizeof(u16) andi $v0, 30 addu $v0, $t8 # value = table->lut7[index] - lhu $v0, DECDCTSMALLTAB_lut7($v0) + lhu $v0, DECDCTTAB_lut7($v0) sll $t0, 11 # window <<= 7 + 4 - addiu $t5, -11 # bit_offset -= 7 + 4 b .Lwrite_value - addiu $t7, 1 # coeff_index++ + addiu $t5, -11 # bit_offset -= 7 + 4 + .word 0 .Lac_prefix_00000001: # Prefix 00000001 is followed by a 5-bit lookup index. srl $v0, $t0, 18 # index = ((window >> (32 - 8 - 5)) & 31) * sizeof(u16) andi $v0, 62 addu $v0, $t8 # value = table->lut8[index] - lhu $v0, DECDCTSMALLTAB_lut8($v0) + lhu $v0, DECDCTTAB_lut8($v0) sll $t0, 13 # window <<= 8 + 5 - addiu $t5, -13 # bit_offset -= 8 + 5 b .Lwrite_value - addiu $t7, 1 # coeff_index++ + addiu $t5, -13 # bit_offset -= 8 + 5 + .word 0 .Lac_prefix_000000001: # Prefix 000000001 is followed by a 5-bit lookup index. srl $v0, $t0, 17 # index = ((window >> (32 - 9 - 5)) & 31) * sizeof(u16) andi $v0, 62 addu $v0, $t8 # value = table->lut9[index] - lhu $v0, DECDCTSMALLTAB_lut9($v0) + lhu $v0, DECDCTTAB_lut9($v0) sll $t0, 14 # window <<= 9 + 5 - addiu $t5, -14 # bit_offset -= 9 + 5 b .Lwrite_value - addiu $t7, 1 # coeff_index++ + addiu $t5, -14 # bit_offset -= 9 + 5 + .word 0 .Lac_prefix_0000000001: # Prefix 0000000001 is followed by a 5-bit lookup index. srl $v0, $t0, 16 # index = ((window >> (32 - 10 - 5)) & 31) * sizeof(u16) andi $v0, 62 addu $v0, $t8 # value = table->lut10[index] - lhu $v0, DECDCTSMALLTAB_lut10($v0) + lhu $v0, DECDCTTAB_lut10($v0) sll $t0, 15 # window <<= 10 + 5 - addiu $t5, -15 # bit_offset -= 10 + 5 b .Lwrite_value - addiu $t7, 1 # coeff_index++ + addiu $t5, -15 # bit_offset -= 10 + 5 + .word 0 .Lac_prefix_00000000001: # Prefix 00000000001 is followed by a 5-bit lookup index. srl $v0, $t0, 15 # index = ((window >> (32 - 11 - 5)) & 31) * sizeof(u16) andi $v0, 62 addu $v0, $t8 # value = table->lut11[index] - lhu $v0, DECDCTSMALLTAB_lut11($v0) + lhu $v0, DECDCTTAB_lut11($v0) sll $t0, 16 # window <<= 11 + 5 - addiu $t5, -16 # bit_offset -= 11 + 5 b .Lwrite_value - addiu $t7, 1 # coeff_index++ + addiu $t5, -16 # bit_offset -= 11 + 5 + .word 0 .Lac_prefix_000000000001: # Prefix 000000000001 is followed by a 5-bit lookup index. srl $v0, $t0, 14 # index = ((window >> (32 - 12 - 5)) & 31) * sizeof(u16) andi $v0, 62 addu $v0, $t8 # value = table->lut12[index] - lhu $v0, DECDCTSMALLTAB_lut12($v0) + lhu $v0, DECDCTTAB_lut12($v0) sll $t0, 17 # window <<= 12 + 5 - addiu $t5, -17 # bit_offset -= 12 + 5 b .Lwrite_value - addiu $t7, 1 # coeff_index++ - - # Prefix 0000000000001 is not valid. - beqz $t0, .Lstop_processing - nop - jr $ra - li $v0, -1 - .word 0, 0, 0, 0 - - # Prefix 00000000000001 is not valid. - beqz $t0, .Lstop_processing - nop - jr $ra - li $v0, -1 - .word 0, 0, 0, 0 - - # Prefix 000000000000001 is not valid. - beqz $t0, .Lstop_processing - nop - jr $ra - li $v0, -1 - .word 0, 0, 0, 0 - - # Prefix 0000000000000001 is not valid. - beqz $t0, .Lstop_processing - nop - jr $ra - li $v0, -1 - #.word 0, 0, 0, 0 + addiu $t5, -17 # bit_offset -= 12 + 5 + .word 0 .Lupdate_window_and_write: sllv $t0, $t0, $v1 # window <<= length diff --git a/libpsn00b/psxsio/sio.c b/libpsn00b/psxsio/sio.c index 3fb3caf..6b80352 100644 --- a/libpsn00b/psxsio/sio.c +++ b/libpsn00b/psxsio/sio.c @@ -27,7 +27,7 @@ typedef struct { static SIO_FlowControl _flow_control; static uint16_t _ctrl_reg_flag; -static void (*_read_callback)(uint8_t) = (void *) 0; +static int (*_read_callback)(uint8_t) = (void *) 0; static void (*_old_sio_handler)(void) = (void *) 0; static volatile RingBuffer _tx_buffer, _rx_buffer; @@ -41,7 +41,15 @@ static void _sio_handler(void) { // Handle any incoming bytes. while (SIO_STAT & SR_RXRDY) { uint8_t value = SIO_TXRX; - int length = _rx_buffer.length; + + // Skip storing this byte into the RX buffer if the callback returns a + // non-zero value. + if (_read_callback) { + if (_read_callback(value)) + continue; + } + + int length = _rx_buffer.length; if (length >= BUFFER_LENGTH) { //_sdk_log("RX overrun, dropping bytes\n"); @@ -53,8 +61,6 @@ static void _sio_handler(void) { _rx_buffer.length = length + 1; _rx_buffer.data[tail] = value; - if (_read_callback) - _read_callback(value); } // Send the next byte in the buffer if the TX unit is ready. Note that @@ -185,7 +191,7 @@ int SIO_ReadSync(int mode) { return 0; } -void *SIO_ReadCallback(void (*func)(uint8_t)) { +void *SIO_ReadCallback(int (*func)(uint8_t)) { EnterCriticalSection(); void *old_callback = _read_callback; diff --git a/libpsn00b/psxspu/common.c b/libpsn00b/psxspu/common.c index 42f7cb7..1613ca9 100644 --- a/libpsn00b/psxspu/common.c +++ b/libpsn00b/psxspu/common.c @@ -38,13 +38,16 @@ static void _dma_transfer(uint32_t *data, size_t length, int write) { length += DMA_CHUNK_LENGTH - 1; } - SPU_CTRL &= 0xffcf; // Disable DMA request + SPU_DMA_CTRL = 0x0004; // Reset transfer mode + SPU_CTRL &= 0xffcf; // Disable DMA request _wait_status(0x0030, 0x0000); // Enable DMA request for writing (2) or reading (3) + uint16_t ctrl = write ? 0x0020 : 0x0030; + SPU_ADDR = _transfer_addr; - SPU_CTRL |= write ? 0x0020 : 0x0030; - _wait_status(0x0400, 0x0000); + SPU_CTRL |= ctrl; + _wait_status(0x0430, ctrl); DMA_MADR(4) = (uint32_t) data; if (length < DMA_CHUNK_LENGTH) -- cgit v1.2.3