diff options
| author | spicyjpeg <thatspicyjpeg@gmail.com> | 2022-08-21 10:55:33 +0200 |
|---|---|---|
| committer | spicyjpeg <thatspicyjpeg@gmail.com> | 2022-08-21 10:55:33 +0200 |
| commit | c845878cb43da4f7470914a064109db2270fd7e6 (patch) | |
| tree | 0e3c917f59b4b57c36609cc1d8f0e289a5d77f79 | |
| parent | ec3ed1574dac08b1c07a8126f6090adce0e7efb8 (diff) | |
| download | psn00bsdk-c845878cb43da4f7470914a064109db2270fd7e6.tar.gz | |
Fix psxgpu and psxetc bugs, add VSyncHaltFunction()
| -rw-r--r-- | libpsn00b/include/psxgpu.h | 1 | ||||
| -rw-r--r-- | libpsn00b/psxetc/interrupts.c | 16 | ||||
| -rw-r--r-- | libpsn00b/psxgpu/common.c | 70 |
3 files changed, 57 insertions, 30 deletions
diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h index f1e5da8..0e7ec00 100644 --- a/libpsn00b/include/psxgpu.h +++ b/libpsn00b/include/psxgpu.h @@ -490,6 +490,7 @@ int GetODE(void); int VSync(int mode); int DrawSync(int mode); +void *VSyncHaltFunction(void (*func)(void)); void *VSyncCallback(void (*func)(void)); void *DrawSyncCallback(void (*func)(void)); diff --git a/libpsn00b/psxetc/interrupts.c b/libpsn00b/psxetc/interrupts.c index e5c4705..32e91f0 100644 --- a/libpsn00b/psxetc/interrupts.c +++ b/libpsn00b/psxetc/interrupts.c @@ -14,8 +14,8 @@ /* Internal globals */ -static void (*_irq_handlers[NUM_IRQ_CHANNELS])(void) = { (void *) 0 }; -static void (*_dma_handlers[NUM_DMA_CHANNELS])(void) = { (void *) 0 }; +static void (*_irq_handlers[NUM_IRQ_CHANNELS])(void); +static void (*_dma_handlers[NUM_DMA_CHANNELS])(void); static int _num_dma_handlers = 0; static uint32_t _saved_irq_mask, _saved_dma_dpcr, _saved_dma_dicr; @@ -156,10 +156,20 @@ int ResetCallback(void) { if (_isr_installed) return -1; - _saved_irq_mask = 0; + 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; diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index d338d93..de60df4 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -10,20 +10,24 @@ #include <psxgpu.h> #include <hwregs_c.h> -#define QUEUE_LENGTH 8 +#define QUEUE_LENGTH 16 #define DMA_CHUNK_LENGTH 8 #define VSYNC_TIMEOUT 0x100000 +static void _default_vsync_halt(void); + /* Internal globals */ GPU_VideoMode _gpu_video_mode; -static void (*_vsync_callback)(void); -static void (*_drawsync_callback)(void); +static void (*_vsync_halt_func)(void) = &_default_vsync_halt; +static void (*_vsync_callback)(void) = (void *) 0; +static void (*_drawsync_callback)(void) = (void *) 0; static const uint32_t *volatile _draw_queue[QUEUE_LENGTH]; static volatile uint8_t _queue_head, _queue_tail, _queue_length; -static volatile uint32_t _vblank_counter, _last_hblank; +static volatile uint32_t _vblank_counter; +static volatile uint16_t _last_hblank; /* Interrupt handlers */ @@ -35,9 +39,8 @@ static void _vblank_handler(void) { } static void _gpu_dma_handler(void) { - //while (DMA_CHCR(2) & (1 << 24)) - //__asm__ volatile(""); - while (!(GPU_GP1 & (1 << 28))) + //while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(2) & (1 << 24))) + while (!(GPU_GP1 & (1 << 26))) __asm__ volatile(""); if (_queue_length) { @@ -97,7 +100,7 @@ void ResetGraph(int mode) { /* Syncing API */ // TODO: add support for no$psx's "halt" register -static void _vsync_halt(void) { +static void _default_vsync_halt(void) { int counter = _vblank_counter; for (int i = VSYNC_TIMEOUT; i; i--) { @@ -111,16 +114,17 @@ static void _vsync_halt(void) { } int VSync(int mode) { + uint16_t delta = (TIMER_VALUE(1) - _last_hblank) & 0xffff; + if (mode == 1) + return delta; if (mode < 0) return _vblank_counter; - if (mode == 1) - return TIMER_VALUE(1) - _last_hblank; uint32_t status = GPU_GP1; // Wait for at least one vertical blank event to occur. do { - _vsync_halt(); + _vsync_halt_func(); // If interlaced mode is enabled, wait until the GPU starts displaying // the next field. @@ -130,12 +134,7 @@ int VSync(int mode) { } } while ((--mode) > 0); - // Update the horizontal blank counter and return the time elapsed since - // the last time it was updated. - uint16_t counter = TIMER_VALUE(1); - uint16_t delta = counter - _last_hblank; - - _last_hblank = counter; + _last_hblank = TIMER_VALUE(1); return delta; } @@ -150,9 +149,7 @@ int DrawSync(int mode) { // Wait for any DMA transfer to finish if DMA is enabled. if (GPU_GP1 & (3 << 29)) { - while (DMA_CHCR(2) & (1 << 24)) - __asm__ volatile(""); - while (!(GPU_GP1 & (1 << 28))) + while (!(GPU_GP1 & (1 << 28)) || (DMA_CHCR(2) & (1 << 24))) __asm__ volatile(""); } @@ -162,6 +159,13 @@ int DrawSync(int mode) { return 0; } +void *VSyncHaltFunction(void (*func)(void)) { + void *old_callback = _vsync_halt_func; + _vsync_halt_func = func; + + return old_callback; +} + void *VSyncCallback(void (*func)(void)) { EnterCriticalSection(); @@ -206,26 +210,38 @@ void ClearOTag(uint32_t *ot, size_t length) { void DrawOTag(const uint32_t *ot) { // If GPU DMA is currently busy, append the OT to the queue instead of - // drawing it immediately. + // drawing it immediately. Note that interrupts must be disabled *prior* 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. + uint32_t mask = IRQ_MASK; + IRQ_MASK = 0; + if (DMA_CHCR(2) & (1 << 24)) { - if (_queue_length >= QUEUE_LENGTH) { - printf("psxgpu: DrawOTag() failed, draw queue full\n"); + if (_queue_length < QUEUE_LENGTH) { + _draw_queue[_queue_tail++] = ot; + + _queue_length++; + _queue_tail %= QUEUE_LENGTH; + + IRQ_MASK = mask; return; } - _draw_queue[_queue_tail++] = ot; - _queue_length++; - _queue_tail %= QUEUE_LENGTH; + IRQ_MASK = mask; + printf("psxgpu: DrawOTag() failed, draw queue full\n"); return; } + IRQ_MASK = mask; DrawOTag2(ot); } void DrawOTag2(const uint32_t *ot) { GPU_GP1 = 0x04000002; - while (!(GPU_GP1 & (1 << 26))) + while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(2) & (1 << 24))) __asm__ volatile(""); DMA_MADR(2) = (uint32_t) ot; |
