aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorspicyjpeg <thatspicyjpeg@gmail.com>2022-10-21 13:24:54 +0200
committerspicyjpeg <thatspicyjpeg@gmail.com>2022-10-21 13:24:54 +0200
commitb1632d7df0e840692612461a80d0e05d6a3228ed (patch)
tree2f804b26c5befa1ed360db034910c8107b8c3ee6
parent982df5fe197c4db2be4bffe59a05c837b7508365 (diff)
downloadpsn00bsdk-b1632d7df0e840692612461a80d0e05d6a3228ed.tar.gz
Rewrite psxsio with new API, fix compiler warnings
-rw-r--r--CHANGELOG.md6
-rw-r--r--libpsn00b/include/assert.h7
-rw-r--r--libpsn00b/include/psxsio.h258
-rw-r--r--libpsn00b/libc/memset.s5
-rw-r--r--libpsn00b/psxcd/isofs.c1
-rw-r--r--libpsn00b/psxetc/dl.c3
-rw-r--r--libpsn00b/psxgpu/common.c68
-rw-r--r--libpsn00b/psxsio/_sio_control.s184
-rw-r--r--libpsn00b/psxsio/sio.c263
-rw-r--r--libpsn00b/psxsio/siocons.c220
-rw-r--r--libpsn00b/psxsio/tty.c107
11 files changed, 631 insertions, 491 deletions
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 <stdint.h>
+
+/* 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 <stdint.h>
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 <stdint.h>
@@ -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 <stdint.h>
+#include <assert.h>
+#include <psxetc.h>
+#include <psxapi.h>
+#include <psxsio.h>
+#include <hwregs_c.h>
+
+#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 <stdio.h>
-#include <string.h>
-#include <ioctl.h>
-#include <psxapi.h>
-#include <psxetc.h>
-#include <psxsio.h>
-
-#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; i<fcb->trns_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; i<fcb->trns_len; i++) {
- while(!(_sio_control(0, 0, 0) & SR_RXRDY));
- ((char*)fcb->trns_addr)[i] = _sio_control(0, 4, 0);
- }*/
-
-
-
- for(i=0; i<fcb->trns_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 <ioctl.h>
+#include <psxapi.h>
+#include <psxsio.h>
+#include <hwregs_c.h>
+
+/* 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();
+}