aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/psxetc
diff options
context:
space:
mode:
authorJohn "Lameguy" Wilbert Villamor <lameguy64@gmail.com>2022-09-26 16:49:56 +0800
committerGitHub <noreply@github.com>2022-09-26 16:49:56 +0800
commitc4a2533d21dfd05cde841ea48c67b05e0e6a853f (patch)
treec7ef61653b157b69fb0956709366996ddbc4ecfa /libpsn00b/psxetc
parenta8b404b3400c3ebd8e0b923dcaefcc49ea563e36 (diff)
parent86f0064afb8200e60dd80827535cac30d0eab028 (diff)
downloadpsn00bsdk-c4a2533d21dfd05cde841ea48c67b05e0e6a853f.tar.gz
Merge pull request #55 from spicyjpeg/psxmdec
Full MDEC support, C library refactors, cleanups and bugfixes (v0.20)
Diffstat (limited to 'libpsn00b/psxetc')
-rw-r--r--libpsn00b/psxetc/_dl_resolve_wrapper.s15
-rw-r--r--libpsn00b/psxetc/dmacallback.s191
-rw-r--r--libpsn00b/psxetc/getinterruptcallback.s19
-rw-r--r--libpsn00b/psxetc/interruptcallback.s48
-rw-r--r--libpsn00b/psxetc/interrupts.c225
-rw-r--r--libpsn00b/psxetc/isr.s107
-rw-r--r--libpsn00b/psxetc/restartcallback.s54
7 files changed, 229 insertions, 430 deletions
diff --git a/libpsn00b/psxetc/_dl_resolve_wrapper.s b/libpsn00b/psxetc/_dl_resolve_wrapper.s
index 01ebf3a..eedfa10 100644
--- a/libpsn00b/psxetc/_dl_resolve_wrapper.s
+++ b/libpsn00b/psxetc/_dl_resolve_wrapper.s
@@ -10,11 +10,11 @@
# - $t8 = index of the function in the .dynsym symbol table
# - $t9 = _dl_resolve_wrapper itself's address
-.set noreorder
-.section .text
+.set noreorder
-.global _dl_resolve_wrapper
-.type _dl_resolve_wrapper, @function
+.section .text._dl_resolve_wrapper
+.global _dl_resolve_wrapper
+.type _dl_resolve_wrapper, @function
_dl_resolve_wrapper:
# Push the registers we're going to use onto the stack.
addiu $sp, -16
@@ -47,10 +47,3 @@ _dl_resolve_wrapper:
jr $t0
nop
-
-.section .data
-
-.global _dl_credits
-.type _dl_credits, @object
-_dl_credits:
- .asciiz "psxetc runtime dynamic linker by spicyjpeg\n"
diff --git a/libpsn00b/psxetc/dmacallback.s b/libpsn00b/psxetc/dmacallback.s
deleted file mode 100644
index 8ea8ec0..0000000
--- a/libpsn00b/psxetc/dmacallback.s
+++ /dev/null
@@ -1,191 +0,0 @@
-.set noreorder
-
-.include "hwregs_a.h"
-
-.section .text
-
-.global DMACallback
-.type DMACallback, @function
-DMACallback:
-
- # a0 - DMA channel
- # a1 - Callback function
-
- addiu $sp, -8
- sw $ra, 0($sp)
-
- beqz $a1, .Lremove_cb # Remove callback if function is NULL
- nop
-
- addiu $sp, -8 # Install IRQ handler for DMA handler
- sw $a0, 0($sp) # if not set installed yet
- sw $a1, 4($sp)
-
- jal GetInterruptCallback
- li $a0, 3
-
- bnez $v0, .Lskip_install
- nop
-
- la $a1, _dma_handler
- jal InterruptCallback
- li $a0, 3
-
-.Lskip_install:
-
- lw $a0, 0($sp)
- lw $a1, 4($sp)
- addiu $sp, 8
-
- la $v0, _dma_func_table
- sll $v1, $a0, 2
- addu $v0, $v1
- lw $v1, 0($v0)
- sw $a1, 0($v0)
- sw $v1, 4($sp)
-
- lui $a2, IOBASE
-
- lw $v0, DMA_DICR($a2) # Enable DMA interrupt
- lui $v1, 0x1
- sll $v1, $a0
- or $v0, $v1
- lui $v1, 0x80
- or $v0, $v1
- sw $v0, DMA_DICR($a2)
-
- b .Lskip_remove
- nop
-
-.Lremove_cb:
-
- la $v0, _dma_func_table # Set callback address
- sll $v1, $a0, 2
- addu $v0, $v1
- lw $v1, 0($v0)
- sw $a1, 0($v0)
- sw $v1, 4($sp)
-
- lui $a2, IOBASE # Disable DMA interrupt
- lw $v0, DMA_DICR($a2)
- lui $v1, 0x1
- sll $v1, $a0
- .set noat
- addiu $at, $0, -1
- xor $v1, $at
- and $v0, $v1
- lui $v1, 0x7f00
- xor $v1, $at
- and $v0, $v1
- .set at
- sw $v0, DMA_DICR($a2)
-
- jal _dma_has_cb # Check if callbacks are present
- nop
- bnez $v0, .Lskip_remove
- nop
- sw $0 , DMA_DICR($a2)
-
- jal GetInterruptCallback # Check if callback is the DMA handler
- li $a0, 3
- la $v1, _dma_handler
- bne $v0, $v1, .Lskip_remove
- nop
-
- li $a0, 3
- jal InterruptCallback
- move $a1, $0
-
-.Lskip_remove:
-
- lw $ra, 0($sp)
- lw $v0, 4($sp)
- jr $ra
- addiu $sp, 8
-
-
-.type _dma_has_cb, @function
-_dma_has_cb:
-
- la $v1, _dma_func_table
- li $t0, 6
-
-.Lscan_loop:
-
- lw $v0, 0($v1)
- addiu $v1, 4
- bnez $v0, .Lhas_cb
- nop
-
- bgtz $t0, .Lscan_loop
- addiu $t0, -1
-
- jr $ra
- move $v0, $0
-
-.Lhas_cb:
-
- jr $ra
- li $v0, 1
-
-
-.type _dma_handler, @function
-_dma_handler:
-
- addiu $sp, -12
- sw $ra, 0($sp)
- sw $s0, 4($sp)
- sw $s1, 8($sp)
-
- move $s0, $0
- la $s1, _dma_func_table
-
-.Lhandler_loop:
-
- lui $a0, IOBASE
- lw $v0, DMA_DICR($a0)
- li $v1, 24
- addu $v1, $s0
- srl $v0, $v1
- andi $v0, 0x1
-
- lw $v1, 0($s1)
-
- beqz $v0, .Lno_irq
- addiu $s1, 4
-
- beqz $v1, .Lno_irq
- nop
-
- jalr $v1
- nop
-
-.Lno_irq:
-
- blt $s0, 6, .Lhandler_loop
- addi $s0, 1
-
- lui $a0, IOBASE
- lw $v0, DMA_DICR($a0)
- nop
- sw $v0, DMA_DICR($a0)
-
- lw $ra, 0($sp)
- lw $s0, 4($sp)
- lw $s1, 8($sp)
-
- jr $ra
- addiu $sp, -12
-
-
-.section .data
-
-_dma_func_table:
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
- \ No newline at end of file
diff --git a/libpsn00b/psxetc/getinterruptcallback.s b/libpsn00b/psxetc/getinterruptcallback.s
deleted file mode 100644
index 510447f..0000000
--- a/libpsn00b/psxetc/getinterruptcallback.s
+++ /dev/null
@@ -1,19 +0,0 @@
-.set noreorder
-
-.section .text
-
-.global GetInterruptCallback
-.type GetInterruptCallback, @function
-GetInterruptCallback:
-
- # a0 - Interrupt number
-
- la $a1, _irq_func_table
- sll $a0, 2
- addu $a1, $a0
- lw $v0, 0($a1)
- nop
- jr $ra
- nop
-
- \ No newline at end of file
diff --git a/libpsn00b/psxetc/interruptcallback.s b/libpsn00b/psxetc/interruptcallback.s
deleted file mode 100644
index 78e5e6e..0000000
--- a/libpsn00b/psxetc/interruptcallback.s
+++ /dev/null
@@ -1,48 +0,0 @@
-.set noreorder
-
-.include "hwregs_a.h"
-
-.section .text
-
-.global InterruptCallback
-.type InterruptCallback, @function
-InterruptCallback:
-
- # a0 - Interrupt number
- # a1 - Callback function
-
- lui $a2, IOBASE
-
- beqz $a1, .Ldisable_irq
- nop
-
- lw $v0, IRQ_MASK($a2) # Enable interrupt mask
- li $v1, 1
- sll $v1, $a0
- or $v0, $v1
-
- b .Lcont
- sw $v0, IRQ_MASK($a2)
-
-.Ldisable_irq:
-
-.set noat
- lw $v0, IRQ_MASK($a2) # Disable interrupt mask
- li $v1, 1
- sll $v1, $a0
- addiu $at, $0 , -1
- xor $v1, $at
-.set at
- and $v0, $v1
- sw $v0, IRQ_MASK($a2)
-
-.Lcont:
-
- la $a2, _irq_func_table # Get address to IRQ function table
-
- sll $v1, $a0, 2 # Compute the slot
- addu $v1, $a2, $v1
- lw $v0, 0($v1) # Get old handler address
-
- jr $ra # Return and set new IRQ handler
- sw $a1, 0($v1)
diff --git a/libpsn00b/psxetc/interrupts.c b/libpsn00b/psxetc/interrupts.c
new file mode 100644
index 0000000..859209a
--- /dev/null
+++ b/libpsn00b/psxetc/interrupts.c
@@ -0,0 +1,225 @@
+/*
+ * PSn00bSDK interrupt management library
+ * (C) 2022 spicyjpeg - MPL licensed
+ */
+
+#include <stdint.h>
+#include <psxapi.h>
+#include <psxetc.h>
+#include <hwregs_c.h>
+
+#define NUM_IRQ_CHANNELS 11
+#define NUM_DMA_CHANNELS 7
+#define ISR_STACK_SIZE 0x1000
+
+/* Internal globals */
+
+static void (*_irq_handlers[NUM_IRQ_CHANNELS])(void);
+static void (*_dma_handlers[NUM_DMA_CHANNELS])(void);
+static int _num_dma_handlers = 0;
+
+static uint16_t _saved_irq_mask;
+static uint32_t _saved_dma_dpcr, _saved_dma_dicr;
+static int _isr_installed = 0;
+
+/* Custom ISR jmp_buf */
+
+// The ISR and all functions called by it (thus, all callbacks registered using
+// InterruptCallback() and DMACallback()) use an independent stack, isolated
+// from the main thread's stack. As the size of this stack is limited, custom
+// callbacks shall keep the number of nested subroutine calls to a minimum and
+// avoid allocating large buffers (e.g. for receiving a sector from the CD
+// drive) on the stack.
+static uint8_t _isr_stack[ISR_STACK_SIZE];
+
+extern uint8_t _gp[];
+static void _global_isr(void);
+
+static const struct JMP_BUF _isr_jmp_buf = {
+ .ra = (uint32_t) &_global_isr,
+ .sp = (uint32_t) &_isr_stack[ISR_STACK_SIZE],
+ .fp = 0,
+ .s0 = 0,
+ .s1 = 0,
+ .s2 = 0,
+ .s3 = 0,
+ .s4 = 0,
+ .s5 = 0,
+ .s6 = 0,
+ .s7 = 0,
+ .gp = (uint32_t) _gp
+};
+
+/* Internal IRQ and DMA handlers */
+
+static void _global_isr(void) {
+ uint16_t stat = IRQ_STAT, mask = IRQ_MASK;
+
+ // Clear all IRQ flags in one shot. This is not the "proper" way to do it
+ // but it's much faster than clearing one flag at a time.
+ IRQ_STAT = ~mask;
+
+ //for (int i = 0; i < NUM_IRQ_CHANNELS; i++) {
+ for (int i = 0; stat; i++, stat >>= 1) {
+ if (!(stat & 1))
+ continue;
+
+ if (_irq_handlers[i])
+ _irq_handlers[i]();
+ }
+
+ ReturnFromException();
+}
+
+static void _global_dma_handler(void) {
+ uint32_t stat = DMA_DICR;
+
+ // Clear all DMA IRQ flags in one shot (note that flags are cleared by
+ // writing 1 to them rather than 0).
+ stat &= 0x7fff0000;
+ DMA_DICR = stat;
+ stat >>= 24;
+
+ //for (int i = 0; i < NUM_DMA_CHANNELS; i++) {
+ for (int i = 0; stat; i++, stat >>= 1) {
+ if (!(stat & 1))
+ continue;
+
+ if (_dma_handlers[i])
+ _dma_handlers[i]();
+ }
+}
+
+/* Callback registration API */
+
+void *InterruptCallback(int irq, void (*func)(void)) {
+ if ((irq < 0) || (irq >= NUM_IRQ_CHANNELS))
+ return 0;
+
+ void *old_callback = _irq_handlers[irq];
+ _irq_handlers[irq] = func;
+
+ // Enable or disable the IRQ in the IRQ_MASK register depending on whether
+ // the callback is being registered or removed.
+ if (func)
+ IRQ_MASK |= 1 << irq;
+ else
+ IRQ_MASK &= ~(1 << irq);
+
+ return old_callback;
+}
+
+void *GetInterruptCallback(int irq) {
+ if ((irq < 0) || (irq >= NUM_IRQ_CHANNELS))
+ return 0;
+
+ return _irq_handlers[irq];
+}
+
+void *DMACallback(int dma, void (*func)(void)) {
+ if ((dma < 0) || (dma >= NUM_DMA_CHANNELS))
+ return 0;
+
+ void *old_callback = _dma_handlers[dma];
+ _dma_handlers[dma] = func;
+
+ // Enable or disable the IRQ in the DMA_DICR register depending on whether
+ // the callback is being registered or removed. The main DMA IRQ dispatcher
+ // is also registered if this is the first DMA callback being configured,
+ // or disabled if it's the last one being removed.
+ if (func) {
+ DMA_DICR |= (0x10000 << dma) | (1 << 23);
+
+ if (!(_num_dma_handlers++))
+ InterruptCallback(3, &_global_dma_handler);
+ } else {
+ if (--_num_dma_handlers) {
+ DMA_DICR &= ~(0x10000 << dma);
+ } else {
+ DMA_DICR = 0;
+ InterruptCallback(3, 0);
+ }
+ }
+
+ return old_callback;
+}
+
+void *GetDMACallback(int dma) {
+ if ((dma < 0) || (dma >= NUM_DMA_CHANNELS))
+ return 0;
+
+ return _dma_handlers[dma];
+}
+
+/* Hook installation/removal API */
+
+int ResetCallback(void) {
+ if (_isr_installed)
+ return -1;
+
+ EnterCriticalSection();
+ _saved_irq_mask = 1 << 3; // Enable DMA IRQ by default
+ _saved_dma_dpcr = 0x03333333;
+ _saved_dma_dicr = 0;
+
+ for (int i = 0; i < NUM_IRQ_CHANNELS; i++)
+ _irq_handlers[i] = (void *) 0;
+ for (int i = 0; i < NUM_DMA_CHANNELS; i++)
+ _dma_handlers[i] = (void *) 0;
+
+ // Set up the DMA IRQ handler. This handler shall *not* be overridden using
+ // InterruptCallback().
+ _irq_handlers[3] = &_global_dma_handler;
+
+ _96_remove();
+ RestartCallback();
+ return 0;
+}
+
+void RestartCallback(void) {
+ if (_isr_installed)
+ return;
+
+ IRQ_STAT = 0;
+ IRQ_MASK = _saved_irq_mask;
+ DMA_DPCR = _saved_dma_dpcr;
+ DMA_DICR = _saved_dma_dicr;
+
+ // Install the ISR hook and prevent the kernel's internal handlers from
+ // automatically acknowledging SPI and timer IRQs.
+ SetCustomExitFromException(&_isr_jmp_buf);
+ ChangeClearPAD(0);
+ ChangeClearRCnt(0, 0);
+ ChangeClearRCnt(1, 0);
+ ChangeClearRCnt(2, 0);
+ ChangeClearRCnt(3, 0);
+
+ _isr_installed = 1;
+ ExitCriticalSection();
+}
+
+void StopCallback(void) {
+ if (!_isr_installed)
+ return;
+
+ // Save the state of IRQ and DMA registers, then reset them and undo the
+ // changes that were made to the kernel's state.
+ EnterCriticalSection();
+ _saved_irq_mask = IRQ_MASK;
+ _saved_dma_dpcr = DMA_DPCR;
+ _saved_dma_dicr = DMA_DICR;
+
+ IRQ_STAT = 0;
+ IRQ_MASK = 0;
+ DMA_DPCR = _saved_dma_dpcr & 0x07777777;
+ DMA_DICR = 0;
+
+ SetDefaultExitFromException();
+ ChangeClearPAD(1);
+ ChangeClearRCnt(0, 1);
+ ChangeClearRCnt(1, 1);
+ ChangeClearRCnt(2, 1);
+ ChangeClearRCnt(3, 1);
+
+ _isr_installed = 0;
+}
diff --git a/libpsn00b/psxetc/isr.s b/libpsn00b/psxetc/isr.s
deleted file mode 100644
index 440be50..0000000
--- a/libpsn00b/psxetc/isr.s
+++ /dev/null
@@ -1,107 +0,0 @@
-.set noreorder
-
-.include "hwregs_a.h"
-
-.set ISR_STACK_SIZE, 4096
-
-
-.section .text
-
-# Global ISR handler of PSn00bSDK
-
-.set at
-
-.type _global_isr, @function
-_global_isr:
-
-.Lisr_loop:
-
- #la $gp, _gp # Keep restoring GP since it gets
- # changed elsewhere sometimes
-
- lui $a0, IOBASE # Get IRQ status
- lw $v0, IRQ_MASK($a0)
- nop
-
- srl $v0, $s1 # Check IRQ mask bit if set
- andi $v0, 0x1
-
- beqz $v0, .Lno_irq # Don't execute callback if IRQ not enabled
- nop
-
- lw $v0, IRQ_STAT($a0)
- nop
- srl $v0, $s1 # Check IRQ status bit if set
- andi $v0, 0x1
- beqz $v0, .Lno_irq # Don't execute callback if no IRQ
- nop
-
- lw $v1, 0($s0) # Load IRQ callback function
- nop
-
- lw $v0, IRQ_STAT($a0) # Acknowledge the IRQ (by writing a 0 bit)
- li $a1, 1
- sll $a1, $s1
- addiu $a2, $0 , -1
- xor $a1, $a2
- sw $a1, IRQ_STAT($a0)
-
- beqz $v1, .Lno_irq # Don't execute if callback is not set
- nop
-
- jalr $v1 # Call interrupt handler
- nop
-
-.Lno_irq:
-
- addiu $s0, 4
-
- blt $s1, 11, .Lisr_loop
- addiu $s1, 1
-
- j ReturnFromException
- nop
-
-
-.section .data
-
-# Global ISR callback table
-
-.global _irq_func_table
-.type _irq_func_table, @object
-_irq_func_table:
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
- .word 0
-
-# Global ISR hook structure
-.global _custom_exit
-.type _custom_exit, @object
-_custom_exit:
- .word _global_isr # pc
- .word _isr_stack+ISR_STACK_SIZE # sp
- .word 0 # fp
- .word _irq_func_table # s0
- .word 0 # s1
- .word 0 # s2
- .word 0 # s3
- .word 0 # s4
- .word 0 # s5
- .word 0 # s6
- .word 0 # s7
- .word _gp # gp
-
-# Global ISR stack
-# .fill 1024
-#_custom_exit_stack:
-# .fill 4
-.comm _isr_stack, ISR_STACK_SIZE+4
diff --git a/libpsn00b/psxetc/restartcallback.s b/libpsn00b/psxetc/restartcallback.s
deleted file mode 100644
index 036a5a0..0000000
--- a/libpsn00b/psxetc/restartcallback.s
+++ /dev/null
@@ -1,54 +0,0 @@
-.set noreorder
-
-.include "hwregs_a.h"
-
-.section .text
-
-.global RestartCallback
-.type RestartCallback, @function
-RestartCallback:
-
- addiu $sp, -4
- sw $ra, 0($sp)
-
- la $a0, _custom_exit
- jal SetCustomExitFromException
- addiu $sp, -4
- addiu $sp, 4
-
- move $a0, $0
- jal ChangeClearPAD
- addiu $sp, -4
- addiu $sp, 4
-
- li $a0, 3
- move $a1, $0
- jal ChangeClearRCnt
- addiu $sp, -8
- addiu $sp, 8
-
- la $a0, _irq_func_table
- move $a1, $0
- move $v0, $0
-
-.Lcheck_cbs: # Set up the interrupt masks
- lw $v1, 0($a0)
- nop
- beqz $v1, .Lno_cb
- addiu $a0, 4
- li $v1, 1
- sll $v1, $a1
- or $v0, $v1
-.Lno_cb:
- blt $a1, 10, .Lcheck_cbs
- addiu $a1, 1
-
- lui $a0, IOBASE
- sw $0 , IRQ_STAT($a0)
- sw $v0, IRQ_MASK($a0)
-
- lw $ra, 0($sp)
- addiu $sp, 4
- jr $ra
- nop
- \ No newline at end of file