aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/psxsio
diff options
context:
space:
mode:
authorJohn "Lameguy" Wilbert Villamor <lameguy64@gmail.com>2022-11-03 10:14:22 +0800
committerGitHub <noreply@github.com>2022-11-03 10:14:22 +0800
commit4139331d233b7a962e747c5564fa68a285f81cc8 (patch)
treed4d3374afd5e36e8580cc424ab2c63ee9e7d357c /libpsn00b/psxsio
parente08a3d9366f8ca14a76b3dd569dac1fb9f569748 (diff)
parent37d963f724113e45d15aa9b8ee86baa9c4362b8f (diff)
downloadpsn00bsdk-4139331d233b7a962e747c5564fa68a285f81cc8.tar.gz
Merge pull request #60 from spicyjpeg/bugfix
Bugfixes, new serial port API and sound examples
Diffstat (limited to 'libpsn00b/psxsio')
-rw-r--r--libpsn00b/psxsio/_sio_control.s184
-rw-r--r--libpsn00b/psxsio/sio.c269
-rw-r--r--libpsn00b/psxsio/siocons.c220
-rw-r--r--libpsn00b/psxsio/tty.c107
4 files changed, 376 insertions, 404 deletions
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..6b80352
--- /dev/null
+++ b/libpsn00b/psxsio/sio.c
@@ -0,0 +1,269 @@
+/*
+ * 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 int (*_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;
+
+ // 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");
+ break;
+ }
+
+ int tail = _rx_buffer.tail;
+ _rx_buffer.tail = (tail + 1) % BUFFER_LENGTH;
+ _rx_buffer.length = length + 1;
+
+ _rx_buffer.data[tail] = 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(int (*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();
+}