diff options
| author | John "Lameguy" Wilbert Villamor <lameguy64@gmail.com> | 2022-09-26 16:49:56 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-26 16:49:56 +0800 |
| commit | c4a2533d21dfd05cde841ea48c67b05e0e6a853f (patch) | |
| tree | c7ef61653b157b69fb0956709366996ddbc4ecfa /libpsn00b/psxetc | |
| parent | a8b404b3400c3ebd8e0b923dcaefcc49ea563e36 (diff) | |
| parent | 86f0064afb8200e60dd80827535cac30d0eab028 (diff) | |
| download | psn00bsdk-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.s | 15 | ||||
| -rw-r--r-- | libpsn00b/psxetc/dmacallback.s | 191 | ||||
| -rw-r--r-- | libpsn00b/psxetc/getinterruptcallback.s | 19 | ||||
| -rw-r--r-- | libpsn00b/psxetc/interruptcallback.s | 48 | ||||
| -rw-r--r-- | libpsn00b/psxetc/interrupts.c | 225 | ||||
| -rw-r--r-- | libpsn00b/psxetc/isr.s | 107 | ||||
| -rw-r--r-- | libpsn00b/psxetc/restartcallback.s | 54 |
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 |
