aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorspicyjpeg <thatspicyjpeg@gmail.com>2022-08-13 21:26:36 +0200
committerspicyjpeg <thatspicyjpeg@gmail.com>2022-08-13 21:26:36 +0200
commitec3ed1574dac08b1c07a8126f6090adce0e7efb8 (patch)
tree825f4bb5e044ce2fa674780b66c77e51e48ab9a4
parentedb967394d22420c9aaad529862a670c016cc2c7 (diff)
downloadpsn00bsdk-ec3ed1574dac08b1c07a8126f6090adce0e7efb8.tar.gz
Rewrite libpsxetc in C, add ResetCallback()
-rw-r--r--libpsn00b/include/psxapi.h5
-rw-r--r--libpsn00b/include/psxetc.h22
-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.c214
-rw-r--r--libpsn00b/psxetc/isr.s107
-rw-r--r--libpsn00b/psxetc/restartcallback.s54
-rw-r--r--libpsn00b/psxgpu/common.c13
9 files changed, 235 insertions, 438 deletions
diff --git a/libpsn00b/include/psxapi.h b/libpsn00b/include/psxapi.h
index 1bdbdbf..5d1097e 100644
--- a/libpsn00b/include/psxapi.h
+++ b/libpsn00b/include/psxapi.h
@@ -224,9 +224,10 @@ int Exec(struct EXEC *exec, int argc, char **argv);
void FlushCache(void);
void b_setjmp(struct JMP_BUF *buf);
-void b_longjmp(struct JMP_BUF *buf, int param);
+void b_longjmp(const struct JMP_BUF *buf, int param);
void SetDefaultExitFromException(void);
-void SetCustomExitFromException(struct JMP_BUF *buf);
+void SetCustomExitFromException(const struct JMP_BUF *buf);
+void ReturnFromException(void);
int GetSystemInfo(int index);
void *GetB0Table(void);
diff --git a/libpsn00b/include/psxetc.h b/libpsn00b/include/psxetc.h
index a55e593..24485d9 100644
--- a/libpsn00b/include/psxetc.h
+++ b/libpsn00b/include/psxetc.h
@@ -1,15 +1,25 @@
-#ifndef _PSXETC_H
-#define _PSXETC_H
+/*
+ * PSn00bSDK interrupt management library
+ * (C) 2019-2022 Lameguy64, spicyjpeg - MPL licensed
+ */
+
+#ifndef __PSXETC_H
+#define __PSXETC_H
+
+/* Public API */
#ifdef __cplusplus
extern "C" {
#endif
-// Interrupt callback functions
-void *DMACallback(int dma, void (*func)(void));
void *InterruptCallback(int irq, void (*func)(void));
-void *GetInterruptCallback(int irq); // Original
-void RestartCallback();
+void *GetInterruptCallback(int irq);
+void *DMACallback(int dma, void (*func)(void));
+void *GetDMACallback(int dma);
+
+int ResetCallback(void);
+void RestartCallback(void);
+void StopCallback(void);
#ifdef __cplusplus
}
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..e5c4705
--- /dev/null
+++ b/libpsn00b/psxetc/interrupts.c
@@ -0,0 +1,214 @@
+/*
+ * 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) = { (void *) 0 };
+static void (*_dma_handlers[NUM_DMA_CHANNELS])(void) = { (void *) 0 };
+static int _num_dma_handlers = 0;
+
+static uint32_t _saved_irq_mask, _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) {
+ uint32_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;
+
+ _saved_irq_mask = 0;
+ _saved_dma_dpcr = 0x03333333;
+ _saved_dma_dicr = 0;
+
+ _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
diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c
index 4591012..d338d93 100644
--- a/libpsn00b/psxgpu/common.c
+++ b/libpsn00b/psxgpu/common.c
@@ -58,23 +58,14 @@ static void _gpu_dma_handler(void) {
void ResetGraph(int mode) {
// Perform some basic system initialization when ResetGraph() is called for
// the first time.
- static int setup_done = 0;
- if (!setup_done) {
+ if (!ResetCallback()) {
EnterCriticalSection();
-
- DMA_DPCR = 0x03333333;
- DMA_DICR = 0;
- IRQ_MASK = 0;
-
InterruptCallback(0, &_vblank_handler);
DMACallback(2, &_gpu_dma_handler);
- RestartCallback();
- _96_remove();
_gpu_video_mode = (GPU_GP1 >> 20) & 1;
- setup_done = 1;
-
ExitCriticalSection();
+
printf("psxgpu: setup done, default mode is %s\n", _gpu_video_mode ? "PAL" : "NTSC");
}