From 7e350980d5c09bbc81a0de01bf016a87ecfc4feb Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Wed, 28 Dec 2022 12:18:29 +0100 Subject: Add CdUnlock() and DMA priority API --- libpsn00b/psxgpu/common.c | 36 +++++++++++++++++++----------------- libpsn00b/psxgpu/image.c | 10 ++++++---- 2 files changed, 25 insertions(+), 21 deletions(-) (limited to 'libpsn00b/psxgpu') diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index e41bd31..e66e46c 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -46,7 +46,7 @@ static void _vblank_handler(void) { } static void _gpu_dma_handler(void) { - //while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(2) & (1 << 24))) + //while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(DMA_GPU) & (1 << 24))) while (!(GPU_GP1 & (1 << 26))) __asm__ volatile(""); @@ -85,9 +85,10 @@ void ResetGraph(int mode) { return; } - DMA_DPCR |= 0x0b000b00; // Enable DMA2 and DMA6 - DMA_CHCR(2) = 0x00000201; // Stop DMA2 - DMA_CHCR(6) = 0x00000200; // Stop DMA6 + SetDMAPriority(DMA_GPU, 3); + SetDMAPriority(DMA_OTC, 3); + DMA_CHCR(DMA_GPU) = 0x00000201; // Stop DMA + DMA_CHCR(DMA_OTC) = 0x00000200; // Stop DMA if (mode == 1) { GPU_GP1 = 0x01000000; // Reset command buffer @@ -225,7 +226,7 @@ int DrawSync(int mode) { if (!_queue_length) { // Wait for any DMA transfer to finish if DMA is enabled. if (GPU_GP1 & (3 << 29)) { - while (!(GPU_GP1 & (1 << 28)) || (DMA_CHCR(2) & (1 << 24))) + while (!(GPU_GP1 & (1 << 28)) || (DMA_CHCR(DMA_GPU) & (1 << 24))) __asm__ volatile(""); } @@ -251,11 +252,11 @@ void *DrawSyncCallback(void (*func)(void)) { /* OT and primitive drawing API */ void ClearOTagR(uint32_t *ot, size_t length) { - DMA_MADR(6) = (uint32_t) &ot[length - 1]; - DMA_BCR(6) = length & 0xffff; - DMA_CHCR(6) = 0x11000002; + DMA_MADR(DMA_OTC) = (uint32_t) &ot[length - 1]; + DMA_BCR(DMA_OTC) = length & 0xffff; + DMA_CHCR(DMA_OTC) = 0x11000002; - while (DMA_CHCR(6) & (1 << 24)) + while (DMA_CHCR(DMA_OTC) & (1 << 24)) __asm__ volatile(""); } @@ -283,13 +284,14 @@ void DrawPrim(const uint32_t *pri) { // NOTE: if length >= DMA_CHUNK_LENGTH then it also has to be a multiple of // DMA_CHUNK_LENGTH, otherwise the DMA channel will get stuck waiting for // more data indefinitely. - DMA_MADR(2) = (uint32_t) &pri[1]; + DMA_MADR(DMA_GPU) = (uint32_t) &pri[1]; if (length < DMA_CHUNK_LENGTH) - DMA_BCR(2) = 0x00010000 | length; + DMA_BCR(DMA_GPU) = 0x00010000 | length; else - DMA_BCR(2) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16); + DMA_BCR(DMA_GPU) = DMA_CHUNK_LENGTH | + ((length / DMA_CHUNK_LENGTH) << 16); - DMA_CHCR(2) = 0x01000201; + DMA_CHCR(DMA_GPU) = 0x01000201; } int DrawOTag(const uint32_t *ot) { @@ -299,12 +301,12 @@ int DrawOTag(const uint32_t *ot) { void DrawOTag2(const uint32_t *ot) { GPU_GP1 = 0x04000002; - while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(2) & (1 << 24))) + while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(DMA_GPU) & (1 << 24))) __asm__ volatile(""); - DMA_MADR(2) = (uint32_t) ot; - DMA_BCR(2) = 0; - DMA_CHCR(2) = 0x01000401; + DMA_MADR(DMA_GPU) = (uint32_t) ot; + DMA_BCR(DMA_GPU) = 0; + DMA_CHCR(DMA_GPU) = 0x01000401; } /* Misc. functions */ diff --git a/libpsn00b/psxgpu/image.c b/libpsn00b/psxgpu/image.c index fc018a4..96f5366 100644 --- a/libpsn00b/psxgpu/image.c +++ b/libpsn00b/psxgpu/image.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -49,13 +50,14 @@ static void _dma_transfer(const RECT *rect, uint32_t *data, int write) { // Enable DMA request, route to GP0 (2) or from GPU_READ (3) GPU_GP1 = 0x04000002 | (write ^ 1); - DMA_MADR(2) = (uint32_t) data; + DMA_MADR(DMA_GPU) = (uint32_t) data; if (length < DMA_CHUNK_LENGTH) - DMA_BCR(2) = 0x00010000 | length; + DMA_BCR(DMA_GPU) = 0x00010000 | length; else - DMA_BCR(2) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16); + DMA_BCR(DMA_GPU) = DMA_CHUNK_LENGTH | + ((length / DMA_CHUNK_LENGTH) << 16); - DMA_CHCR(2) = 0x01000200 | write; + DMA_CHCR(DMA_GPU) = 0x01000200 | write; } /* VRAM transfer API */ -- cgit v1.2.3 From 50faf92a8cb099420aa956dcac3a8105067e117c Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Wed, 28 Dec 2022 14:27:34 +0100 Subject: Re-enable MoveImage(), add tagless primitive structs --- libpsn00b/include/psxgpu.h | 212 ++++++++++++++++++++++++--------------------- libpsn00b/psxgpu/common.c | 31 +------ libpsn00b/psxgpu/env.c | 27 ++++++ libpsn00b/psxgpu/image.c | 12 ++- 4 files changed, 151 insertions(+), 131 deletions(-) (limited to 'libpsn00b/psxgpu') diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h index 26e560f..4dbcc6c 100644 --- a/libpsn00b/include/psxgpu.h +++ b/libpsn00b/include/psxgpu.h @@ -101,9 +101,12 @@ typedef enum _GPU_VideoMode { #define setlen(p, _len) (((P_TAG *) (p))->len = (uint8_t) (_len)) #define setaddr(p, _addr) (((P_TAG *) (p))->addr = (uint32_t) (_addr)) #define setcode(p, _code) (((P_TAG *) (p))->code = (uint8_t) (_code)) +#define setcode_T(p, _code) (((P_TAG_T *) (p))->code = (uint8_t) (_code)) + #define getlen(p) (((P_TAG *) (p))->len) #define getaddr(p) (((P_TAG *) (p))->addr) #define getcode(p) (((P_TAG *) (p))->code) +#define getcode_T(p) (((P_TAG_T *) (p))->code) #define nextPrim(p) (void *) (0x80000000 | (((P_TAG *) (p))->addr)) #define isendprim(p) ((((P_TAG *) (p))->addr) == 0xffffff) @@ -147,16 +150,41 @@ typedef enum _GPU_VideoMode { #define setTile(p) setlen(p, 3), setcode(p, 0x60) #define setLineF2(p) setlen(p, 3), setcode(p, 0x40) #define setLineG2(p) setlen(p, 4), setcode(p, 0x50) -#define setLineF3(p) setlen(p, 5), setcode(p, 0x48), \ - (p)->pad = 0x55555555 -#define setLineG3(p) setlen(p, 7), setcode(p, 0x58), \ - (p)->pad = 0x55555555, (p)->p1 = 0, (p)->p2 = 0 -#define setLineF4(p) setlen(p, 6), setcode(p, 0x4c), \ - (p)->pad = 0x55555555 -#define setLineG4(p) setlen(p, 9), setcode(p, 0x5c), \ - (p)->pad = 0x55555555, (p)->p1 = 0, (p)->p2 = 0, (p)->p3 = 0 -#define setFill(p) setlen(p, 3), setcode(p, 0x02) -#define setVram2Vram(p) setlen(p, 8), setcode(p, 0x80), \ +#define setLineF3(p) setlen(p, 5), setcode(p, 0x48), (p)->pad = 0x55555555 +#define setLineG3(p) setlen(p, 7), setcode(p, 0x58), (p)->pad = 0x55555555, \ + (p)->p1 = 0, (p)->p2 = 0 +#define setLineF4(p) setlen(p, 6), setcode(p, 0x4c), (p)->pad = 0x55555555 +#define setLineG4(p) setlen(p, 9), setcode(p, 0x5c), (p)->pad = 0x55555555, \ + (p)->p1 = 0, (p)->p2 = 0, (p)->p3 = 0 +#define setFill(p) setlen(p, 3), setcode(p, 0x02) +#define setBlit(p) setlen(p, 8), setcode(p, 0x80), \ + (p)->pad[0] = 0, (p)->pad[1] = 0, (p)->pad[2] = 0, (p)->pad[3] = 0 + +#define setPolyF3_T(p) setcode_T(p, 0x20) +#define setPolyFT3_T(p) setcode_T(p, 0x24) +#define setPolyG3_T(p) setcode_T(p, 0x30) +#define setPolyGT3_T(p) setcode_T(p, 0x34) +#define setPolyF4_T(p) setcode_T(p, 0x28) +#define setPolyFT4_T(p) setcode_T(p, 0x2c) +#define setPolyG4_T(p) setcode_T(p, 0x38) +#define setPolyGT4_T(p) setcode_T(p, 0x3c) +#define setSprt8_T(p) setcode_T(p, 0x74) +#define setSprt16_T(p) setcode_T(p, 0x7c) +#define setSprt_T(p) setcode_T(p, 0x64) +#define setTile1_T(p) setcode_T(p, 0x68) +#define setTile8_T(p) setcode_T(p, 0x70) +#define setTile16_T(p) setcode_T(p, 0x78) +#define setTile_T(p) setcode_T(p, 0x60) +#define setLineF2_T(p) setcode_T(p, 0x40) +#define setLineG2_T(p) setcode_T(p, 0x50) +#define setLineF3_T(p) setcode_T(p, 0x48), (p)->pad = 0x55555555 +#define setLineG3_T(p) setcode_T(p, 0x58), (p)->pad = 0x55555555, \ + (p)->p1 = 0, (p)->p2 = 0 +#define setLineF4_T(p) setcode_T(p, 0x4c), (p)->pad = 0x55555555 +#define setLineG4_T(p) setcode_T(p, 0x5c), (p)->pad = 0x55555555, \ + (p)->p1 = 0, (p)->p2 = 0, (p)->p3 = 0 +#define setFill_T(p) setcode_T(p, 0x02) +#define setBlit_T(p) setcode_T(p, 0x80), \ (p)->pad[0] = 0, (p)->pad[1] = 0, (p)->pad[2] = 0, (p)->pad[3] = 0 #define setDrawTPage(p, dfe, dtd, tpage) \ @@ -200,6 +228,11 @@ typedef enum _GPU_VideoMode { /* Primitive structure definitions */ +typedef struct _P_TAG_T { + uint32_t color:24; + uint32_t code:8; +} P_TAG_T; + typedef struct _P_TAG { uint32_t addr:24; uint32_t len:8; @@ -212,25 +245,31 @@ typedef struct _P_COLOR { uint32_t pad:8; } P_COLOR; -typedef struct _POLY_F3 { - uint32_t tag; +// These macros are used to define two variants of each primitive, a regular one +// and a "tagless" one (_T suffix) without the OT/display list header. +#define _DEF_PRIM(name, ...) \ + typedef struct _##name##_T { __VA_ARGS__ } name##_T; \ + typedef struct _##name { uint32_t tag; __VA_ARGS__ } name; +#define _DEF_ALIAS(name, target) \ + typedef struct _##target##_T name##_T; \ + typedef struct _##target name; + +_DEF_PRIM(POLY_F3, uint8_t r0, g0, b0, code; int16_t x0, y0; int16_t x1, y1; int16_t x2, y2; -} POLY_F3; +) -typedef struct _POLY_F4 { - uint32_t tag; +_DEF_PRIM(POLY_F4, uint8_t r0, g0, b0, code; int16_t x0, y0; int16_t x1, y1; int16_t x2, y2; int16_t x3, y3; -} POLY_F4; +) -typedef struct _POLY_FT3 { - uint32_t tag; +_DEF_PRIM(POLY_FT3, uint8_t r0, g0, b0, code; int16_t x0, y0; uint8_t u0, v0; @@ -241,10 +280,9 @@ typedef struct _POLY_FT3 { int16_t x2, y2; uint8_t u2, v2; uint16_t pad; -} POLY_FT3; +) -typedef struct _POLY_FT4 { - uint32_t tag; +_DEF_PRIM(POLY_FT4, uint8_t r0, g0, b0, code; uint16_t x0, y0; uint8_t u0, v0; @@ -258,20 +296,18 @@ typedef struct _POLY_FT4 { int16_t x3, y3; uint8_t u3, v3; uint16_t pad1; -} POLY_FT4; +) -typedef struct _POLY_G3 { - uint32_t tag; +_DEF_PRIM(POLY_G3, uint8_t r0, g0, b0, code; int16_t x0, y0; uint8_t r1, g1, b1, pad0; int16_t x1, y1; uint8_t r2, g2, b2, pad1; int16_t x2, y2; -} POLY_G3; +) -typedef struct _POLY_G4 { - uint32_t tag; +_DEF_PRIM(POLY_G4, uint8_t r0, g0, b0, code; int16_t x0, y0; uint8_t r1, g1, b1, pad0; @@ -280,10 +316,9 @@ typedef struct _POLY_G4 { int16_t x2, y2; uint8_t r3, g3, b3, pad2; int16_t x3, y3; -} POLY_G4; +) -typedef struct _POLY_GT3 { - uint32_t tag; +_DEF_PRIM(POLY_GT3, uint8_t r0, g0, b0, code; int16_t x0, y0; uint8_t u0, v0; @@ -296,10 +331,9 @@ typedef struct _POLY_GT3 { int16_t x2, y2; uint8_t u2, v2; uint16_t pad2; -} POLY_GT3; +) -typedef struct _POLY_GT4 { - uint32_t tag; +_DEF_PRIM(POLY_GT4, uint8_t r0, g0, b0, code; int16_t x0, y0; uint8_t u0, v0; @@ -316,34 +350,30 @@ typedef struct _POLY_GT4 { int16_t x3, y3; uint8_t u3, v3; uint16_t pad4; -} POLY_GT4; +) -typedef struct _LINE_F2 { - uint32_t tag; +_DEF_PRIM(LINE_F2, uint8_t r0, g0, b0, code; int16_t x0, y0; int16_t x1, y1; -} LINE_F2; +) -typedef struct _LINE_G2 { - uint32_t tag; +_DEF_PRIM(LINE_G2, uint8_t r0, g0, b0, code; int16_t x0, y0; uint8_t r1, g1, b1, p1; int16_t x1, y1; -} LINE_G2; +) -typedef struct _LINE_F3 { - uint32_t tag; +_DEF_PRIM(LINE_F3, uint8_t r0, g0, b0, code; int16_t x0, y0; int16_t x1, y1; int16_t x2, y2; uint32_t pad; -} LINE_F3; +) -typedef struct _LINE_G3 { - uint32_t tag; +_DEF_PRIM(LINE_G3, uint8_t r0, g0, b0, code; int16_t x0, y0; uint8_t r1, g1, b1, p1; @@ -351,20 +381,18 @@ typedef struct _LINE_G3 { uint8_t r2, g2, b2, p2; int16_t x2, y2; uint32_t pad; -} LINE_G3; +) -typedef struct _LINE_F4 { - uint32_t tag; +_DEF_PRIM(LINE_F4, uint8_t r0, g0, b0, code; int16_t x0, y0; int16_t x1, y1; int16_t x2, y2; int16_t x3, y3; uint32_t pad; -} LINE_F4; +) -typedef struct _LINE_G4 { - uint32_t tag; +_DEF_PRIM(LINE_G4, uint8_t r0, g0, b0, code; int16_t x0, y0; uint8_t r1, g1, b1, p1; @@ -374,88 +402,70 @@ typedef struct _LINE_G4 { uint8_t r3, g3, b3, p3; int16_t x3, y3; uint32_t pad; -} LINE_G4; +) -typedef struct _TILE { - uint32_t tag; +_DEF_PRIM(TILE, uint8_t r0, g0, b0, code; int16_t x0, y0; int16_t w, h; -} TILE; +) -struct _TILE_FIXED { - uint32_t tag; +_DEF_PRIM(TILE_1, uint8_t r0, g0, b0, code; int16_t x0, y0; -}; -typedef struct _TILE_FIXED TILE_1; -typedef struct _TILE_FIXED TILE_8; -typedef struct _TILE_FIXED TILE_16; +) +_DEF_ALIAS(TILE_8, TILE_1) +_DEF_ALIAS(TILE_16, TILE_1) -typedef struct _SPRT { - uint32_t tag; +_DEF_PRIM(SPRT, uint8_t r0, g0, b0, code; int16_t x0, y0; uint8_t u0, v0; uint16_t clut; uint16_t w, h; -} SPRT; +) -struct _SPRT_FIXED { - uint32_t tag; +_DEF_PRIM(SPRT_1, uint8_t r0, g0, b0, code; int16_t x0, y0; uint8_t u0, v0; uint16_t clut; -}; -typedef struct _SPRT_FIXED SPRT_8; -typedef struct _SPRT_FIXED SPRT_16; +) +_DEF_ALIAS(SPRT_8, SPRT_1) +_DEF_ALIAS(SPRT_16, SPRT_1) -typedef struct _DR_ENV { - uint32_t tag; +_DEF_PRIM(DR_ENV, uint32_t code[8]; -} DR_ENV; - -typedef struct _DR_AREA { - uint32_t tag; +) +_DEF_PRIM(DR_AREA, uint32_t code[2]; -} DR_AREA; - -typedef struct _DR_OFFSET { - uint32_t tag; +) +_DEF_PRIM(DR_OFFSET, uint32_t code[1]; -} DR_OFFSET; - -typedef struct _DR_TWIN { - uint32_t tag; +) +_DEF_PRIM(DR_TWIN, uint32_t code[2]; -} DR_TWIN; - -typedef struct _DR_TPAGE { - uint32_t tag; +) +_DEF_PRIM(DR_TPAGE, uint32_t code[1]; -} DR_TPAGE; - -typedef struct _DR_MASK { - uint32_t tag; +) +_DEF_PRIM(DR_MASK, uint32_t code[1]; -} DR_MASK; +) -typedef struct _FILL { - uint32_t tag; +_DEF_PRIM(FILL, uint8_t r0, g0, b0, code; - uint16_t x0, y0; // Note: coordinates must be in 16 pixel steps + uint16_t x0, y0; uint16_t w, h; -} FILL; +) -typedef struct _VRAM2VRAM { - uint32_t tag; +_DEF_PRIM(BLIT, uint8_t p0, p1, p2, code; uint16_t x0, y0; uint16_t x1, y1; uint16_t w, h; uint32_t pad[4]; -} VRAM2VRAM; +) /* Structure definitions */ @@ -536,10 +546,10 @@ void *DrawSyncCallback(void (*func)(void)); int LoadImage(const RECT *rect, const uint32_t *data); int StoreImage(const RECT *rect, uint32_t *data); -//int MoveImage(const RECT *rect, int x, int y); +int MoveImage(const RECT *rect, int x, int y); void LoadImage2(const RECT *rect, const uint32_t *data); void StoreImage2(const RECT *rect, uint32_t *data); -//void MoveImage2(const RECT *rect, int x, int y); +void MoveImage2(const RECT *rect, int x, int y); void ClearOTagR(uint32_t *ot, size_t length); void ClearOTag(uint32_t *ot, size_t length); diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index e66e46c..8d2119b 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -46,7 +46,8 @@ static void _vblank_handler(void) { } static void _gpu_dma_handler(void) { - //while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(DMA_GPU) & (1 << 24))) + if (GPU_GP1 & (1 << 24)) + GPU_GP1 = 0x02000000; while (!(GPU_GP1 & (1 << 26))) __asm__ volatile(""); @@ -72,6 +73,7 @@ void ResetGraph(int mode) { if (!ResetCallback()) { EnterCriticalSection(); InterruptCallback(IRQ_VBLANK, &_vblank_handler); + InterruptCallback(IRQ_GPU, &_gpu_dma_handler); DMACallback(DMA_GPU, &_gpu_dma_handler); _gpu_video_mode = (GPU_GP1 >> 20) & 1; @@ -308,30 +310,3 @@ void DrawOTag2(const uint32_t *ot) { DMA_BCR(DMA_GPU) = 0; DMA_CHCR(DMA_GPU) = 0x01000401; } - -/* Misc. functions */ - -GPU_VideoMode GetVideoMode(void) { - return _gpu_video_mode; -} - -void SetVideoMode(GPU_VideoMode mode) { - uint32_t _mode, stat = GPU_GP1; - - _gpu_video_mode = mode & 1; - - _mode = (mode & 1) << 3; - _mode |= (stat >> 17) & 0x37; // GPUSTAT 17-22 -> cmd bits 0-5 - _mode |= (stat >> 10) & 0x40; // GPUSTAT bit 16 -> cmd bit 6 - _mode |= (stat >> 7) & 0x80; // GPUSTAT bit 14 -> cmd bit 7 - - GPU_GP1 = 0x08000000 | mode; -} - -int GetODE(void) { - return (GPU_GP1 >> 31); -} - -void SetDispMask(int mask) { - GPU_GP1 = 0x03000000 | (mask ? 0 : 1); -} diff --git a/libpsn00b/psxgpu/env.c b/libpsn00b/psxgpu/env.c index 8784947..96e065e 100644 --- a/libpsn00b/psxgpu/env.c +++ b/libpsn00b/psxgpu/env.c @@ -233,3 +233,30 @@ void PutDispEnvRaw(const DISPENV_RAW *env) { GPU_GP1 = 0x08000000 | env->vid_mode; // Set video mode GPU_GP1 = 0x05000000 | fb_pos; // Set VRAM location to display } + +/* Misc. display functions */ + +GPU_VideoMode GetVideoMode(void) { + return _gpu_video_mode; +} + +void SetVideoMode(GPU_VideoMode mode) { + uint32_t _mode, stat = GPU_GP1; + + _gpu_video_mode = mode & 1; + + _mode = (mode & 1) << 3; + _mode |= (stat >> 17) & 0x37; // GPUSTAT bits 17-22 -> command bits 0-5 + _mode |= (stat >> 10) & 0x40; // GPUSTAT bit 16 -> command bit 6 + _mode |= (stat >> 7) & 0x80; // GPUSTAT bit 14 -> command bit 7 + + GPU_GP1 = 0x08000000 | _mode; +} + +int GetODE(void) { + return (GPU_GP1 >> 31); +} + +void SetDispMask(int mask) { + GPU_GP1 = 0x03000000 | (mask ? 0 : 1); +} diff --git a/libpsn00b/psxgpu/image.c b/libpsn00b/psxgpu/image.c index 96f5366..09c7ab9 100644 --- a/libpsn00b/psxgpu/image.c +++ b/libpsn00b/psxgpu/image.c @@ -90,6 +90,10 @@ int StoreImage(const RECT *rect, uint32_t *data) { ); } +int MoveImage(const RECT *rect, int x, int y) { + return EnqueueDrawOp((void *) &MoveImage2, (uint32_t) rect, x, y); +} + void LoadImage2(const RECT *rect, const uint32_t *data) { _dma_transfer(rect, (uint32_t *) data, 1); } @@ -98,14 +102,18 @@ void StoreImage2(const RECT *rect, uint32_t *data) { _dma_transfer(rect, data, 0); } -/*void MoveImage2(const RECT *rect, int x, int y) { +void MoveImage2(const RECT *rect, int x, int y) { GPU_GP0 = 0x80000000; //GPU_GP0 = rect->x | (rect->y << 16); GPU_GP0 = *((const uint32_t *) &(rect->x)); GPU_GP0 = (x & 0xffff) | (y << 16); //GPU_GP0 = rect->w | (rect->h << 16); GPU_GP0 = *((const uint32_t *) &(rect->w)); -}*/ + + // As no DMA transfer is performed by this command, the GPU IRQ is used + // instead of the DMA IRQ to trigger the draw queue callback. + GPU_GP0 = 0x1f000000; +} /* .TIM image parsers */ -- cgit v1.2.3 From 3095b4571dabc8d6cee90673d679f3e77b21b164 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Fri, 13 Jan 2023 18:04:49 +0100 Subject: Fix VSync() rate limiter, add missing tagless packets --- libpsn00b/include/psxetc.h | 9 +++-- libpsn00b/include/psxgpu.h | 90 ++++++++++++++++++++++++++-------------------- libpsn00b/psxgpu/common.c | 58 +++++++++++++++++------------- libpsn00b/psxgpu/env.c | 60 +++++++++++++------------------ libpsn00b/psxgpu/image.c | 13 +++++-- 5 files changed, 128 insertions(+), 102 deletions(-) (limited to 'libpsn00b/psxgpu') diff --git a/libpsn00b/include/psxetc.h b/libpsn00b/include/psxetc.h index ebf7966..e45abf4 100644 --- a/libpsn00b/include/psxetc.h +++ b/libpsn00b/include/psxetc.h @@ -1,6 +1,6 @@ /* * PSn00bSDK interrupt management library - * (C) 2019-2022 Lameguy64, spicyjpeg - MPL licensed + * (C) 2019-2023 Lameguy64, spicyjpeg - MPL licensed */ /** @@ -68,7 +68,7 @@ extern "C" { * | ID | Channel | Used by | * | --: | :--------------- | :-------------------------------------- | * | 0 | IRQ_VBLANK | psxgpu (use VSyncCallback() instead) | - * | 1 | IRQ_GPU | | + * | 1 | IRQ_GPU | psxgpu (use DrawSyncCallback() instead) | * | 2 | IRQ_CD | psxcd (use CdReadyCallback() instead) | * | 3 | IRQ_DMA | psxetc (use DMACallback() instead) | * | 4 | IRQ_TIMER0 | | @@ -227,6 +227,11 @@ void RestartCallback(void); * Note that interrupts are (obviously) disabled until RestartCallback() is * called. * + * WARNING: any ongoing background processing or DMA transfer must be stopped + * before calling StopCallback(), otherwise crashes may occur. This includes + * flushing psxgpu's command queue using DrawSync(), stopping CD-ROM reading + * and calling StopPAD() to disable the BIOS controller driver if used. + * * @see RestartCallback() */ void StopCallback(void); diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h index 4dbcc6c..2329908 100644 --- a/libpsn00b/include/psxgpu.h +++ b/libpsn00b/include/psxgpu.h @@ -1,6 +1,6 @@ /* * PSn00bSDK GPU library - * (C) 2019-2022 Lameguy64, spicyjpeg - MPL licensed + * (C) 2019-2023 Lameguy64, spicyjpeg - MPL licensed */ #ifndef __PSXGPU_H @@ -117,9 +117,13 @@ typedef enum _GPU_VideoMode { #define setSemiTrans(p, abe) \ ((abe) ? (getcode(p) |= 2) : (getcode(p) &= ~2)) +#define setSemiTrans_T(p, abe) \ + ((abe) ? (getcode_T(p) |= 2) : (getcode_T(p) &= ~2)) #define setShadeTex(p, tge) \ ((tge) ? (getcode(p) |= 1) : (getcode(p) &= ~1)) +#define setShadeTex_T(p, tge) \ + ((tge) ? (getcode_T(p) |= 1) : (getcode_T(p) &= ~1)) #define getTPage(tp, abr, x, y) ( \ (((x) / 64) & 15) | \ @@ -187,27 +191,26 @@ typedef enum _GPU_VideoMode { #define setBlit_T(p) setcode_T(p, 0x80), \ (p)->pad[0] = 0, (p)->pad[1] = 0, (p)->pad[2] = 0, (p)->pad[3] = 0 -#define setDrawTPage(p, dfe, dtd, tpage) \ - setlen(p, 1), \ +#define setDrawTPage_T(p, dfe, dtd, tpage) \ (p)->code[0] = (0xe1000000 | \ (tpage) | \ ((dtd) << 9) | \ ((dfe) << 10) \ ) +#define setDrawTPage(p, dfe, dtd, tpage) \ + setlen(p, 1), setDrawTPage_T(p, dfe, dtd, tpage) -#define setDrawOffset(p, _x, _y) \ - setlen(p, 1), \ - (p)->code[0] = (0xe5000000 | \ - ((_x) % 1024) | \ - (((_y) % 1024) << 11) \ +#define setTexWindow_T(p, r) \ + (p)->code[0] = (0xe2000000 | \ + ((r)->w % 32) | \ + (((r)->h % 32) << 5) | \ + (((r)->x % 32) << 10) | \ + (((r)->y % 32) << 15) \ ) +#define setTexWindow(p, r) \ + setlen(p, 1), setTexWindow_T(p, r) -#define setDrawMask(p, sb, mt) \ - setlen(p, 1), \ - (p)->code[0] = (0xe6000000 | (sb) | ((mt) << 1)) - -#define setDrawArea(p, r) \ - setlen(p, 2), \ +#define setDrawArea_T(p, r) \ (p)->code[0] = (0xe3000000 | \ ((r)->x % 1024) | \ (((r)->y % 1024) << 10) \ @@ -216,15 +219,21 @@ typedef enum _GPU_VideoMode { (((r)->x + (r)->w - 1) % 1024) | \ ((((r)->y + (r)->h - 1) % 1024) << 10) \ ) +#define setDrawArea(p, r) \ + setlen(p, 2), setDrawArea_T(p, r) -#define setTexWindow(p, r) \ - setlen(p, 1), \ - (p)->code[0] = (0xe2000000 | \ - ((r)->w % 32) | \ - (((r)->h % 32) << 5) | \ - (((r)->x % 32) << 10) | \ - (((r)->y % 32) << 15) \ +#define setDrawOffset_T(p, _x, _y) \ + (p)->code[0] = (0xe5000000 | \ + ((_x) % 1024) | \ + (((_y) % 1024) << 11) \ ) +#define setDrawOffset(p, _x, _y) \ + setlen(p, 1), setDrawOffset_T(p, _x, _y) + +#define setDrawMask_T(p, sb, mt) \ + (p)->code[0] = (0xe6000000 | (sb) | ((mt) << 1)) +#define setDrawMask(p, sb, mt) \ + setlen(p, 1), setDrawMask_T(p, sb, mt) /* Primitive structure definitions */ @@ -434,9 +443,20 @@ _DEF_PRIM(SPRT_1, _DEF_ALIAS(SPRT_8, SPRT_1) _DEF_ALIAS(SPRT_16, SPRT_1) -_DEF_PRIM(DR_ENV, - uint32_t code[8]; +_DEF_PRIM(FILL, + uint8_t r0, g0, b0, code; + uint16_t x0, y0; + uint16_t w, h; ) + +_DEF_PRIM(BLIT, + uint8_t p0, p1, p2, code; + uint16_t x0, y0; + uint16_t x1, y1; + uint16_t w, h; + uint32_t pad[4]; +) + _DEF_PRIM(DR_AREA, uint32_t code[2]; ) @@ -444,7 +464,7 @@ _DEF_PRIM(DR_OFFSET, uint32_t code[1]; ) _DEF_PRIM(DR_TWIN, - uint32_t code[2]; + uint32_t code[1]; ) _DEF_PRIM(DR_TPAGE, uint32_t code[1]; @@ -453,18 +473,12 @@ _DEF_PRIM(DR_MASK, uint32_t code[1]; ) -_DEF_PRIM(FILL, - uint8_t r0, g0, b0, code; - uint16_t x0, y0; - uint16_t w, h; -) - -_DEF_PRIM(BLIT, - uint8_t p0, p1, p2, code; - uint16_t x0, y0; - uint16_t x1, y1; - uint16_t w, h; - uint32_t pad[4]; +_DEF_PRIM(DR_ENV, + DR_TPAGE_T tpage; + DR_TWIN_T twin; + DR_AREA_T area; + DR_OFFSET_T offset; + FILL_T fill; ) /* Structure definitions */ @@ -488,13 +502,13 @@ typedef struct _DISPENV { typedef struct _DRAWENV { RECT clip; // Drawing area int16_t ofs[2]; // GPU draw offset (relative to draw area) - RECT tw; // Texture window (doesn't do anything atm) + RECT tw; // Texture window uint16_t tpage; // Initial tpage value uint8_t dtd; // Dither processing flag (simply OR'ed to tpage) uint8_t dfe; // Drawing to display area blocked/allowed (simply OR'ed to tpage) uint8_t isbg; // Clear draw area if non-zero uint8_t r0, g0, b0; // Draw area clear color (if isbg iz nonzero) - DR_ENV dr_env; // Draw mode packet area (used by PutDrawEnv) + DR_ENV dr_env; // GPU primitive cache area (used internally) } DRAWENV; typedef struct _TIM_IMAGE { diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index 8d2119b..c52ab8a 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -1,6 +1,6 @@ /* * PSn00bSDK GPU library (common functions) - * (C) 2022 spicyjpeg - MPL licensed + * (C) 2022-2023 spicyjpeg - MPL licensed */ #include @@ -11,7 +11,7 @@ #include #define QUEUE_LENGTH 16 -#define DMA_CHUNK_LENGTH 8 +#define DMA_CHUNK_LENGTH 16 #define VSYNC_TIMEOUT 0x100000 static void _default_vsync_halt(void); @@ -33,7 +33,7 @@ static void (*_drawsync_callback)(void) = (void *) 0; static volatile QueueEntry _draw_queue[QUEUE_LENGTH]; static volatile uint8_t _queue_head, _queue_tail, _queue_length; -static volatile uint32_t _vblank_counter; +static volatile uint32_t _vblank_counter, _last_vblank; static volatile uint16_t _last_hblank; /* Private interrupt handlers */ @@ -47,9 +47,7 @@ static void _vblank_handler(void) { static void _gpu_dma_handler(void) { if (GPU_GP1 & (1 << 24)) - GPU_GP1 = 0x02000000; - while (!(GPU_GP1 & (1 << 26))) - __asm__ volatile(""); + GPU_GP1 = 0x02000000; // Reset IRQ if (--_queue_length) { int head = _queue_head; @@ -82,8 +80,14 @@ void ResetGraph(int mode) { _sdk_log("setup done, default mode is %s\n", _gpu_video_mode ? "PAL" : "NTSC"); } - if (mode == 3) { + _queue_head = 0; + _queue_tail = 0; + _queue_length = 0; + + if (mode == 1) { GPU_GP1 = 0x01000000; // Reset command buffer + GPU_GP1 = 0x02000000; // Reset IRQ + GPU_GP1 = 0x04000000; // Disable DMA request return; } @@ -92,20 +96,20 @@ void ResetGraph(int mode) { DMA_CHCR(DMA_GPU) = 0x00000201; // Stop DMA DMA_CHCR(DMA_OTC) = 0x00000200; // Stop DMA - if (mode == 1) { + if (mode) { GPU_GP1 = 0x01000000; // Reset command buffer - return; + GPU_GP1 = 0x02000000; // Reset IRQ + GPU_GP1 = 0x04000000; // Disable DMA request + } else { + GPU_GP1 = 0x00000000; // Reset GPU } - GPU_GP1 = 0x00000000; // Reset GPU - TIMER_CTRL(0) = 0x0500; - TIMER_CTRL(1) = 0x0500; - - _queue_head = 0; - _queue_tail = 0; - _queue_length = 0; _vblank_counter = 0; + _last_vblank = 0; _last_hblank = 0; + + TIMER_CTRL(0) = 0x0500; + TIMER_CTRL(1) = 0x0500; } /* VSync() API */ @@ -129,11 +133,13 @@ int VSync(int mode) { return delta; if (mode < 0) return _vblank_counter; + if (!mode) + mode = 1; // VSync(0) = wait for one vblank - uint32_t status = GPU_GP1; - - // Wait for at least one vertical blank event to occur. - do { + // Wait for at least one vertical blank event since the last call to + // VSync() to occur. + for (uint32_t target = _last_vblank + mode; _vblank_counter < target;) { + uint32_t status = GPU_GP1; _vsync_halt_func(); // If interlaced mode is enabled, wait until the GPU starts displaying @@ -142,9 +148,11 @@ int VSync(int mode) { while (!((GPU_GP1 ^ status) & (1 << 31))) __asm__ volatile(""); } - } while ((--mode) > 0); + } + _last_vblank = _vblank_counter; _last_hblank = TIMER_VALUE(1); + return delta; } @@ -269,8 +277,10 @@ void ClearOTag(uint32_t *ot, size_t length) { // https://problemkaputt.de/psx-spx.htm#dmachannels for (int i = 0; i < (length - 1); i++) ot[i] = (uint32_t) &ot[i + 1] & 0x00ffffff; + //setaddr(&ot[i], &ot[i + 1]); ot[length - 1] = 0x00ffffff; + //termPrim(&ot[length - 1]); } void AddPrim(uint32_t *ot, const void *pri) { @@ -281,7 +291,7 @@ void DrawPrim(const uint32_t *pri) { size_t length = getlen(pri); DrawSync(0); - GPU_GP1 = 0x04000002; + GPU_GP1 = 0x04000002; // Enable DMA request, route to GP0 // NOTE: if length >= DMA_CHUNK_LENGTH then it also has to be a multiple of // DMA_CHUNK_LENGTH, otherwise the DMA channel will get stuck waiting for @@ -301,9 +311,9 @@ int DrawOTag(const uint32_t *ot) { } void DrawOTag2(const uint32_t *ot) { - GPU_GP1 = 0x04000002; + GPU_GP1 = 0x04000002; // Enable DMA request, route to GP0 - while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(DMA_GPU) & (1 << 24))) + while (DMA_CHCR(DMA_GPU) & (1 << 24)) __asm__ volatile(""); DMA_MADR(DMA_GPU) = (uint32_t) ot; diff --git a/libpsn00b/psxgpu/env.c b/libpsn00b/psxgpu/env.c index 96e065e..b90a431 100644 --- a/libpsn00b/psxgpu/env.c +++ b/libpsn00b/psxgpu/env.c @@ -1,6 +1,6 @@ /* * PSn00bSDK GPU library (DRAWENV/DISPENV functions) - * (C) 2022 spicyjpeg - MPL licensed + * (C) 2022-2023 spicyjpeg - MPL licensed */ #include @@ -60,54 +60,42 @@ DRAWENV *SetDefDrawEnv(DRAWENV *env, int x, int y, int w, int h) { } int DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { - DR_ENV *prim = &(env->dr_env); - // All commands are grouped into a single display list packet for - // performance reasons (the GPU does not care about the grouping as the - // display list is parsed by the DMA unit in the CPU). + // performance reasons using tagless primitives (the GPU does not care + // about the grouping as the display list is parsed by the CPU). + DR_ENV *prim = &(env->dr_env); setaddr(prim, ot); setlen(prim, 5); // Texture page (reset active page and set dither/mask bits) - prim->code[0] = 0xe1000000 | env->tpage; - prim->code[0] |= (env->dtd & 1) << 9; - prim->code[0] |= (env->dfe & 1) << 10; + setDrawTPage_T(&(prim->tpage), env->dfe & 1, env->dtd & 1, env->tpage); // Texture window - prim->code[1] = 0xe2000000; - prim->code[1] |= _get_window_mask(env->tw.w); - prim->code[1] |= _get_window_mask(env->tw.h) << 5; - prim->code[1] |= (env->tw.x & 0xf8) << 7; // ((tw.x / 8) & 0x1f) << 10 - prim->code[1] |= (env->tw.y & 0xf8) << 12; // ((tw.y / 8) & 0x1f) << 15 - - // Set drawing area top left - prim->code[2] = 0xe3000000; - prim->code[2] |= env->clip.x & 0x3ff; - prim->code[2] |= (env->clip.y & 0x3ff) << 10; - - // Set drawing area bottom right - prim->code[3] = 0xe4000000; - prim->code[3] |= (env->clip.x + (env->clip.w - 1)) & 0x3ff; - prim->code[3] |= ((env->clip.y + (env->clip.h - 1)) & 0x3ff) << 10; - - // Set drawing offset - prim->code[4] = 0xe5000000; - prim->code[4] |= (env->clip.x + env->ofs[0]) & 0x7ff; - prim->code[4] |= ((env->clip.y + env->ofs[1]) & 0x7ff) << 11; + //setTexWindow_T(&(prim->twin), &(env->tw)); + prim->twin.code[0] = 0xe2000000; + prim->twin.code[0] |= _get_window_mask(env->tw.w); + prim->twin.code[0] |= _get_window_mask(env->tw.h) << 5; + prim->twin.code[0] |= (env->tw.x & 0xf8) << 7; // ((tw.x / 8) & 0x1f) << 10 + prim->twin.code[0] |= (env->tw.y & 0xf8) << 12; // ((tw.y / 8) & 0x1f) << 15 + + // Set drawing area + setDrawArea_T(&(prim->area), &(env->clip)); + setDrawOffset_T( + &(prim->offset), + env->clip.x + env->ofs[0], + env->clip.y + env->ofs[1] + ); if (env->isbg) { + FILL_T *fill = &(prim->fill); setlen(prim, 8); // Rectangle fill // FIXME: reportedly this command doesn't accept height values >511... - prim->code[5] = 0x02000000; - //prim->code[5] |= env->r0 | (env->g0 << 8) | (env->b0 << 16); - //prim->code[6] = env->clip.x; - //prim->code[6] |= env->clip.y << 16; - prim->code[5] |= *((const uint32_t *) &(env->isbg)) >> 8; - prim->code[6] = *((const uint32_t *) &(env->clip.x)); - prim->code[7] = env->clip.w; - prim->code[7] |= _min(env->clip.h, 0x1ff) << 16; + setFill_T(fill); + setColor0(fill, *((const uint32_t *) &(env->isbg)) >> 8); + setXY0(fill, env->clip.x, env->clip.y); + setWH(fill, env->clip.w, _min(env->clip.h, 0x1ff)); } return EnqueueDrawOp((void *) &DrawOTag2, (uint32_t) prim, 0, 0); diff --git a/libpsn00b/psxgpu/image.c b/libpsn00b/psxgpu/image.c index 09c7ab9..61ac484 100644 --- a/libpsn00b/psxgpu/image.c +++ b/libpsn00b/psxgpu/image.c @@ -1,6 +1,6 @@ /* * PSn00bSDK GPU library (image and VRAM transfer functions) - * (C) 2022 spicyjpeg - MPL licensed + * (C) 2022-2023 spicyjpeg - MPL licensed * * TODO: MoveImage() is currently commented out as it won't trigger a DMA IRQ, * making it unusable as a draw queue command. A way around this (perhaps using @@ -14,7 +14,7 @@ #include #define QUEUE_LENGTH 16 -#define DMA_CHUNK_LENGTH 8 +#define DMA_CHUNK_LENGTH 16 /* Internal globals */ @@ -38,6 +38,9 @@ static void _dma_transfer(const RECT *rect, uint32_t *data, int write) { length += DMA_CHUNK_LENGTH - 1; } + while (!(GPU_GP1 & (1 << 26))) + __asm__ volatile(""); + GPU_GP1 = 0x04000000; // Disable DMA request GPU_GP0 = 0x01000000; // Flush cache @@ -50,6 +53,9 @@ static void _dma_transfer(const RECT *rect, uint32_t *data, int write) { // Enable DMA request, route to GP0 (2) or from GPU_READ (3) GPU_GP1 = 0x04000002 | (write ^ 1); + while (DMA_CHCR(DMA_GPU) & (1 << 24)) + __asm__ volatile(""); + DMA_MADR(DMA_GPU) = (uint32_t) data; if (length < DMA_CHUNK_LENGTH) DMA_BCR(DMA_GPU) = 0x00010000 | length; @@ -103,6 +109,9 @@ void StoreImage2(const RECT *rect, uint32_t *data) { } void MoveImage2(const RECT *rect, int x, int y) { + while (!(GPU_GP1 & (1 << 26))) + __asm__ volatile(""); + GPU_GP0 = 0x80000000; //GPU_GP0 = rect->x | (rect->y << 16); GPU_GP0 = *((const uint32_t *) &(rect->x)); -- cgit v1.2.3 From a21e949c9aea98cb4b3feee48bb98579bbdfba70 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Sun, 22 Jan 2023 15:40:14 +0100 Subject: Fix VSync(), assert(), warnings and some examples --- examples/demos/n00bdemo/main.c | 14 +++----------- examples/graphics/billboard/billboard.c | 2 +- examples/io/system573/iso.xml | 8 +++++--- examples/system/childexec/child/child.c | 7 ++++--- examples/system/childexec/parent.c | 22 +++++++++------------- libpsn00b/include/assert.h | 8 +++++--- libpsn00b/psxcd/misc.c | 6 +++--- libpsn00b/psxgpu/common.c | 11 ++++++----- libpsn00b/psxgpu/image.c | 2 +- 9 files changed, 37 insertions(+), 43 deletions(-) (limited to 'libpsn00b/psxgpu') diff --git a/examples/demos/n00bdemo/main.c b/examples/demos/n00bdemo/main.c index 6d0be3c..c9ca3ce 100644 --- a/examples/demos/n00bdemo/main.c +++ b/examples/demos/n00bdemo/main.c @@ -624,7 +624,7 @@ void plasmastuff() { // Simple stripe transition effect void transition() { - int i,count,comp; + int count = 0; int bheight[16] = { 0 }; TILE *tile = (TILE*)nextpri; @@ -632,9 +632,9 @@ void transition() { while( 1 ) { - comp = 0; + int comp = 0; - for( i=0; i<16; i++ ) { + for( int i=0; i<16; i++ ) { if( bheight[i] > 0 ) { @@ -657,19 +657,11 @@ void transition() { if( bheight[count>>1] == 0 ) bheight[count>>1] = 1; - display(); count++; if( comp >= 16 ) break; - - /* - I haven't yet managed to figure out why this loop hangs on no$psx - if I comment out this completely useless call to puts(). Some - alignment or timing crap perhaps? -- spicyjpeg - */ - puts("."); } DrawSync(0); diff --git a/examples/graphics/billboard/billboard.c b/examples/graphics/billboard/billboard.c index ea98b28..1ddc4dc 100644 --- a/examples/graphics/billboard/billboard.c +++ b/examples/graphics/billboard/billboard.c @@ -152,7 +152,7 @@ int main() { setRGB0(quad, 128, 128, 128); // Set tpage - quad->tpage = getTPage(tim.mode&0x8, 0, tim.prect->x, tim.prect->y); + quad->tpage = getTPage(tim.mode, 0, tim.prect->x, tim.prect->y); // Set CLUT setClut(quad, tim.crect->x, tim.crect->y); diff --git a/examples/io/system573/iso.xml b/examples/io/system573/iso.xml index 2226089..c960b8a 100644 --- a/examples/io/system573/iso.xml +++ b/examples/io/system573/iso.xml @@ -19,9 +19,9 @@ The System 573 BIOS does not parse SYSTEM.CNF, it's instead hardcoded to look for an executable named PSX.EXE. Some modded or hacked BIOS variants may instead look for slightly altered - file names (QSY.DXD, SSW.BXF, TSV.AXG) as an obfuscation - measure, so it's recommended to have multiple copies of the - executable on the disc. + file names (QSY.DXD, SSW.BXF, TSV.AXG, GSE.NXX, NSE.GXX) as an + obfuscation measure, so it's recommended to have multiple + copies of the executable on the disc. Note that this behavior can be abused to make multi-system CDs with different executables for PS1 and 573 (i.e. have both @@ -33,6 +33,8 @@ + + diff --git a/examples/system/childexec/child/child.c b/examples/system/childexec/child/child.c index dcfbfaf..b52dd32 100644 --- a/examples/system/childexec/child/child.c +++ b/examples/system/childexec/child/child.c @@ -238,11 +238,12 @@ int main(int argc, const char *argv[]) { display(); } - + + DrawSync(0); StopPAD(); - + StopCallback(); + return 0; - } void init(void) { diff --git a/examples/system/childexec/parent.c b/examples/system/childexec/parent.c index 83d964c..79c81f1 100644 --- a/examples/system/childexec/parent.c +++ b/examples/system/childexec/parent.c @@ -285,31 +285,27 @@ void run_child(void) { // Copy child executable to its intended adddress memcpy((void*)exe->param.t_addr, child_exe+2048, exe->param.t_size); - - // Prepare for program execution and disable interrupts - //EnterCriticalSection(); - StopCallback(); - // Stop pads, enable auto acknowledge + // Prepare for program execution and disable interrupts + DrawSync(0); StopPAD(); - ChangeClearPAD(1); - ChangeClearRCnt(3, 1); + StopCallback(); + FlushCache(); // Execute child - printf("Child exec!\n"); + printf("Executing child...\n"); Exec(&exe->param, 3, args); - + // Restore interrupts for this PS-EXE RestartCallback(); - //ExitCriticalSection(); - + printf("Child returned\n"); + // Re-init and re-enable pads InitPAD(pad_buff[0], 34, pad_buff[1], 34); StartPAD(); ChangeClearPAD(0); - + // Set this program's display mode SetDispMask(0); PutDispEnv(&disp); - } diff --git a/libpsn00b/include/assert.h b/libpsn00b/include/assert.h index 1b2bda2..d18a56f 100644 --- a/libpsn00b/include/assert.h +++ b/libpsn00b/include/assert.h @@ -1,6 +1,6 @@ /* * PSn00bSDK assert macro and internal logging - * (C) 2022 spicyjpeg - MPL licensed + * (C) 2022-2023 spicyjpeg - MPL licensed * * Note that the _sdk_log() macro is used internally by PSn00bSDK to output * debug messages and warnings. @@ -32,9 +32,11 @@ void _assert_abort(const char *file, int line, const char *expr); ((expr) ? ((void) 0) : _assert_abort(__FILE__, __LINE__, #expr)) #ifdef SDK_LIBRARY_NAME -#define _sdk_log(fmt, ...) printf(SDK_LIBRARY_NAME ": " fmt, ##__VA_ARGS__) +#define _sdk_log(fmt, ...) \ + printf(SDK_LIBRARY_NAME ": " fmt __VA_OPT__(,) __VA_ARGS__) #else -#define _sdk_log(fmt, ...) printf(fmt, ##__VA_ARGS__) +#define _sdk_log(fmt, ...) \ + printf(fmt __VA_OPT__(,) __VA_ARGS__) #endif #endif diff --git a/libpsn00b/psxcd/misc.c b/libpsn00b/psxcd/misc.c index fc87676..851fea6 100644 --- a/libpsn00b/psxcd/misc.c +++ b/libpsn00b/psxcd/misc.c @@ -14,7 +14,7 @@ /* Unlock command strings */ -static char *_unlock_strings[] = { +static const char *_unlock_strings[] = { "", "Licensed by", "Sony", @@ -24,7 +24,7 @@ static char *_unlock_strings[] = { "" }; -static const char *_unlock_regions[] = { +static const char *const _unlock_regions[] = { "of America", // CdlRegionSCEA "(Europe)", // CdlRegionSCEE "World wide" // CdlRegionSCEW @@ -66,7 +66,7 @@ int CdDataSync(int mode) { return 0; } - _sdk_log("CdDataSync() timeout\n"); + _sdk_log("CdDataSync() timeout, CHCR=0x%08x\n", DMA_CHCR(DMA_CD)); return -1; } diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index c52ab8a..537f672 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -133,12 +133,13 @@ int VSync(int mode) { return delta; if (mode < 0) return _vblank_counter; - if (!mode) - mode = 1; // VSync(0) = wait for one vblank - // Wait for at least one vertical blank event since the last call to - // VSync() to occur. - for (uint32_t target = _last_vblank + mode; _vblank_counter < target;) { + // Wait for the specified number of vertical blank events since the last + // call to VSync() to occur (if mode >= 2) or just for a single vertical + // blank (if mode = 0). + uint32_t target = mode ? (_last_vblank + mode) : (_vblank_counter + 1); + + while (_vblank_counter < target) { uint32_t status = GPU_GP1; _vsync_halt_func(); diff --git a/libpsn00b/psxgpu/image.c b/libpsn00b/psxgpu/image.c index 61ac484..ba5c445 100644 --- a/libpsn00b/psxgpu/image.c +++ b/libpsn00b/psxgpu/image.c @@ -53,7 +53,7 @@ static void _dma_transfer(const RECT *rect, uint32_t *data, int write) { // Enable DMA request, route to GP0 (2) or from GPU_READ (3) GPU_GP1 = 0x04000002 | (write ^ 1); - while (DMA_CHCR(DMA_GPU) & (1 << 24)) + while ((DMA_CHCR(DMA_GPU) & (1 << 24)) || !(GPU_GP1 & (1 << 28))) __asm__ volatile(""); DMA_MADR(DMA_GPU) = (uint32_t) data; -- cgit v1.2.3 From 8e3a757d4d7d5dfc62f69ce4ede08f1cf79e3461 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Tue, 4 Apr 2023 00:46:20 +0200 Subject: Add IsIdleGPU(), tweak psxgpu.h, fix mdec/strvideo --- examples/demos/n00bdemo/main.c | 29 ++++++++++++------------- examples/mdec/strvideo/main.c | 19 +++++++++++------ libpsn00b/include/psxgpu.h | 48 +++++++++++++++++++++++++++--------------- libpsn00b/include/psxpress.h | 5 ++--- libpsn00b/psxgpu/common.c | 15 +++++++++++++ libpsn00b/psxpress/vlc2.c | 11 ++++------ 6 files changed, 78 insertions(+), 49 deletions(-) (limited to 'libpsn00b/psxgpu') diff --git a/examples/demos/n00bdemo/main.c b/examples/demos/n00bdemo/main.c index c9ca3ce..55dbbc4 100644 --- a/examples/demos/n00bdemo/main.c +++ b/examples/demos/n00bdemo/main.c @@ -358,10 +358,9 @@ void stencilstuff() { /* The stencil demo is achieved by utilizing the mask bit setting primitive GP0(E6h). The structure of this primitive is defined as - DR_MASK initialized and set by setDrawMask(). These are not available - in Sony's SDK by default. + DR_STP initialized and set by setDrawStp(). - The DR_MASK primitive controls mask bit operations for drawing + The DR_STP primitive controls mask bit operations for drawing primitives such as setting mask bits on every pixel drawn or mask bit test where pixels won't be drawn on pixels with the mask bit set. It applies to most graphics drawing primitives except VRAM fill. @@ -373,10 +372,10 @@ void stencilstuff() { bit operation disabled. The stencil effect featured in this demo is achieved by enabling set - mask bit with DR_MASK, drawing semi-transparent primitives using + mask bit with DR_STP, drawing semi-transparent primitives using additive blending but color is all zero to make it completely invisible but is enough to update the mask bits, disable mask set bit but enable - mask test with DR_MASK and then drawing a rectangle that fills the + mask test with DR_STP and then drawing a rectangle that fills the entire screen. Semi-transparency mask in textures must not be used when drawing the scene that will be 'below' the mask layer. */ @@ -384,7 +383,7 @@ void stencilstuff() { int spin=0; - DR_MASK *mask; + DR_STP *mask; TILE *rect; SC_OT s_ot; @@ -430,10 +429,10 @@ void stencilstuff() { // Sort mask primitive that enables setting mask bits - mask = (DR_MASK*)nextpri; - setDrawMask( mask, 1, 0 ); + mask = (DR_STP*)nextpri; + setDrawStp( mask, 1, 0 ); addPrim( ot[db]+20, mask ); - nextpri += sizeof(DR_MASK); + nextpri += sizeof(DR_STP); // Sort the stars @@ -465,10 +464,10 @@ void stencilstuff() { // Sort mask primitive that enables mask bit test - mask = (DR_MASK*)nextpri; - setDrawMask( mask, 0, 1 ); + mask = (DR_STP*)nextpri; + setDrawStp( mask, 0, 1 ); addPrim( ot[db]+18, mask ); - nextpri += sizeof(DR_MASK); + nextpri += sizeof(DR_STP); // Sort rectangle that fills the screen @@ -482,10 +481,10 @@ void stencilstuff() { // Clear all mask settings - mask = (DR_MASK*)nextpri; - setDrawMask( mask, 0, 0 ); + mask = (DR_STP*)nextpri; + setDrawStp( mask, 0, 0 ); addPrim( ot[db]+15, mask ); - nextpri += sizeof(DR_MASK); + nextpri += sizeof(DR_STP); // Sort overlay then display diff --git a/examples/mdec/strvideo/main.c b/examples/mdec/strvideo/main.c index 57cb6ef..853e0c2 100644 --- a/examples/mdec/strvideo/main.c +++ b/examples/mdec/strvideo/main.c @@ -189,8 +189,15 @@ void cd_sector_handler(void) { return; // If this sector is actually part of a new frame, validate the sectors - // that have been read so far and flip the bitstream data buffers. - if (sector_header.frame_id != str_ctx.frame_id) { + // that have been read so far and flip the bitstream data buffers. If the + // frame number is actually lower than the current one, assume the drive + // has started reading another .STR file and stop playback. + if ((int) sector_header.frame_id < str_ctx.frame_id) { + str_ctx.frame_ready = -1; + return; + } + + if ((int) sector_header.frame_id > str_ctx.frame_id) { // Do not set the ready flag if any sector has been missed. if (str_ctx.sector_count) str_ctx.dropped_frames++; @@ -263,11 +270,9 @@ void init_stream(void) { CdReadyCallback(&cd_event_handler); ExitCriticalSection(); - // Set the maximum amount of data DecDCTvlc() can output and copy the - // lookup table used for decompression to the scratchpad area. This is - // optional but makes the decompressor slightly faster. See the libpsxpress - // documentation for more details. - DecDCTvlcSize(0x8000); + // Copy the lookup table used for frame decompression to the scratchpad + // area. This is optional but makes the decompressor slightly faster. See + // the libpsxpress documentation for more details. DecDCTvlcCopyTableV3((VLC_TableV3 *) 0x1f800000); str_ctx.cur_frame = 0; diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h index 2329908..78d8342 100644 --- a/libpsn00b/include/psxgpu.h +++ b/libpsn00b/include/psxgpu.h @@ -83,7 +83,7 @@ typedef enum _GPU_VideoMode { (p)->u0 = (_u0), (p)->v0 = (_v0), \ (p)->u1 = (_u1), (p)->v1 = (_v1), \ (p)->u2 = (_u2), (p)->v2 = (_v2) - + #define setUV4(p, _u0, _v0, _u1, _v1, _u2, _v2, _u3, _v3) \ (p)->u0 = (_u0), (p)->v0 = (_v0), \ (p)->u1 = (_u1), (p)->v1 = (_v1), \ @@ -202,38 +202,48 @@ typedef enum _GPU_VideoMode { #define setTexWindow_T(p, r) \ (p)->code[0] = (0xe2000000 | \ - ((r)->w % 32) | \ - (((r)->h % 32) << 5) | \ - (((r)->x % 32) << 10) | \ - (((r)->y % 32) << 15) \ + ((r)->w & 0x1f) | \ + (((r)->h & 0x1f) << 5) | \ + (((r)->x & 0x1f) << 10) | \ + (((r)->y & 0x1f) << 15) \ ) #define setTexWindow(p, r) \ setlen(p, 1), setTexWindow_T(p, r) -#define setDrawArea_T(p, r) \ +#define setDrawAreaXY_T(p, _x0, _y0, _x1, _y1) \ (p)->code[0] = (0xe3000000 | \ - ((r)->x % 1024) | \ - (((r)->y % 1024) << 10) \ + ((_x0) & 0x3ff) | \ + (((_y0) & 0x3ff) << 10) \ ), \ (p)->code[1] = (0xe4000000 | \ - (((r)->x + (r)->w - 1) % 1024) | \ - ((((r)->y + (r)->h - 1) % 1024) << 10) \ + ((_x1) & 0x3ff) | \ + (((_y1) & 0x3ff) << 10) \ + ) +#define setDrawAreaXY(p, _x0, _y0, _x1, _y1) \ + setlen(p, 2), setDrawAreaXY_T(p, _x0, _y0, _x1, _y1) + +#define setDrawArea_T(p, r) \ + setDrawAreaXY_T(p, \ + (r)->x, \ + (r)->y, \ + (r)->x + (r)->w - 1, \ + (r)->y + (r)->h - 1 \ ) #define setDrawArea(p, r) \ setlen(p, 2), setDrawArea_T(p, r) #define setDrawOffset_T(p, _x, _y) \ (p)->code[0] = (0xe5000000 | \ - ((_x) % 1024) | \ - (((_y) % 1024) << 11) \ + ((_x) & 0x7ff) | \ + (((_y) & 0x7ff) << 11) \ ) #define setDrawOffset(p, _x, _y) \ setlen(p, 1), setDrawOffset_T(p, _x, _y) -#define setDrawMask_T(p, sb, mt) \ - (p)->code[0] = (0xe6000000 | (sb) | ((mt) << 1)) -#define setDrawMask(p, sb, mt) \ - setlen(p, 1), setDrawMask_T(p, sb, mt) +#define setDrawStp_T(p, pbw, mt) \ + (p)->code[0] = (0xe6000000 | (pbw) | ((mt) << 1)) +#define setDrawStp(p, pbw, mt) \ + setlen(p, 1), setDrawStp_T(p, pbw, mt) /* Primitive structure definitions */ @@ -469,7 +479,7 @@ _DEF_PRIM(DR_TWIN, _DEF_PRIM(DR_TPAGE, uint32_t code[1]; ) -_DEF_PRIM(DR_MASK, +_DEF_PRIM(DR_STP, uint32_t code[1]; ) @@ -481,6 +491,9 @@ _DEF_PRIM(DR_ENV, FILL_T fill; ) +#undef _DEF_PRIM +#undef _DEF_ALIAS + /* Structure definitions */ typedef struct _RECT { @@ -545,6 +558,7 @@ void PutDrawEnv(DRAWENV *env); void PutDrawEnvFast(DRAWENV *env); int GetODE(void); +int IsIdleGPU(int timeout); int VSync(int mode); void *VSyncHaltFunction(void (*func)(void)); void *VSyncCallback(void (*func)(void)); diff --git a/libpsn00b/include/psxpress.h b/libpsn00b/include/psxpress.h index c3b13f4..ea0c2ec 100644 --- a/libpsn00b/include/psxpress.h +++ b/libpsn00b/include/psxpress.h @@ -246,8 +246,6 @@ int DecDCToutSync(int mode); * can be different). If max_size = 0, the entire frame will always be decoded * in one shot. * - * Only bitstream version 2 is currently supported. - * * WARNING: InitGeom() must be called prior to using DecDCTvlcStart() for the * first time. Attempting to call this function with the GTE disabled will * result in a crash. @@ -411,7 +409,8 @@ void DecDCTvlcCopyTableV3(VLC_TableV3 *addr); * buffer can be different). If max_size = 0, the entire frame will always be * decoded in one shot. * - * Only bitstream version 2 is currently supported. + * This function only supports decoding version 1 and 2 bitstreams. Use + * DecDCTvlcStart() to decode a version 3 bitstream. * * @param ctx Pointer to VLC_Context structure (which will be initialized) * @param buf diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index 537f672..e354261 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -321,3 +321,18 @@ void DrawOTag2(const uint32_t *ot) { DMA_BCR(DMA_GPU) = 0; DMA_CHCR(DMA_GPU) = 0x01000401; } + +/* Queue pause/resume API */ + +int IsIdleGPU(int timeout) { + if (timeout <= 0) + timeout = 1; + + for (; timeout; timeout--) { + if (GPU_GP1 & (1 << 26)) + return 0; + } + + //_sdk_log("IsIdleGPU() timeout\n"); + return -1; +} diff --git a/libpsn00b/psxpress/vlc2.c b/libpsn00b/psxpress/vlc2.c index 24c54ce..7d9d9f3 100644 --- a/libpsn00b/psxpress/vlc2.c +++ b/libpsn00b/psxpress/vlc2.c @@ -123,7 +123,7 @@ int __attribute__((optimize(3))) DecDCTvlcContinue2( *output = (uint16_t) _get_bits_unsigned(22); _advance_window(22); } else if (window >> 24) { - // The first lookup table is for codes that not start with + // The first lookup table is for codes that do not start with // 00000000. value = _vlc_huffman_table2->ac[_get_bits_unsigned(13)]; _advance_window(value >> 16); @@ -136,12 +136,9 @@ int __attribute__((optimize(3))) DecDCTvlcContinue2( *output = (uint16_t) value; } } else { - // Parse the DC (first) coefficient for this block. Version 2 - // simply stores the signed 10-bit value as-is, while version 3 - // uses a delta encoding combined with a compression method similar - // to exp-Golomb. + // Parse the DC (first) coefficient for this block. if (is_v3) { - // TODO: version 3 is currently not supported. + // This implementation does not support version 3. return -1; } else { value = _get_bits_unsigned(10); @@ -161,7 +158,7 @@ int __attribute__((optimize(3))) DecDCTvlcContinue2( // time and processes each 16-bit word starting from the the MSB, so an // endianness conversion is necessary to preserve bit order when // reading 32 bits at a time. Also note that the PS1 CPU is not capable - // of shifting by more than 31 bits - it will shift by 0 bits instead! + // of shifting by >=31 bits - it will shift by (N % 32) bits instead! if (bit_offset < 0) { window = next_window << (-bit_offset); bit_offset += 32; -- cgit v1.2.3 From 870f4dca9d7b5e86544216d0e36863d17aefef62 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Tue, 4 Apr 2023 15:09:38 +0200 Subject: Add argument validation to most libpsn00b functions --- libpsn00b/include/assert.h | 19 +++++++++++++++++++ libpsn00b/psxcd/cdread.c | 2 ++ libpsn00b/psxcd/common.c | 10 ++++++++-- libpsn00b/psxcd/isofs.c | 13 +++++++++++++ libpsn00b/psxcd/misc.c | 8 ++++++++ libpsn00b/psxetc/dl.c | 13 +++++++++++-- libpsn00b/psxetc/interrupts.c | 19 +++++++------------ libpsn00b/psxgpu/common.c | 16 +++++++++++++++- libpsn00b/psxgpu/env.c | 15 +++++++++++++++ libpsn00b/psxgpu/font.c | 14 ++++++++++---- libpsn00b/psxgpu/image.c | 16 ++++++++++++++++ libpsn00b/psxpress/mdec.c | 6 ++++++ libpsn00b/psxspu/common.c | 10 +++++++++- 13 files changed, 139 insertions(+), 22 deletions(-) (limited to 'libpsn00b/psxgpu') diff --git a/libpsn00b/include/assert.h b/libpsn00b/include/assert.h index d18a56f..26b2023 100644 --- a/libpsn00b/include/assert.h +++ b/libpsn00b/include/assert.h @@ -25,6 +25,9 @@ void _assert_abort(const char *file, int line, const char *expr); #define assert(expr) #define _sdk_log(fmt, ...) +#define _sdk_assert(expr, fmt, ...) +#define _sdk_validate_args_void(expr) +#define _sdk_validate_args(expr, ret) #else @@ -39,6 +42,22 @@ void _assert_abort(const char *file, int line, const char *expr); printf(fmt __VA_OPT__(,) __VA_ARGS__) #endif +#define _sdk_assert(expr, ret, fmt, ...) \ + if (!(expr)) { \ + _sdk_log(fmt, __VA_ARGS__); \ + return ret; \ + } +#define _sdk_validate_args_void(expr) \ + if (!(expr)) { \ + _sdk_log("invalid args to %s() (%s)\n", __func__, #expr); \ + return; \ + } +#define _sdk_validate_args(expr, ret) \ + if (!(expr)) { \ + _sdk_log("invalid args to %s() (%s)\n", __func__, #expr); \ + return ret; \ + } + #endif #endif diff --git a/libpsn00b/psxcd/cdread.c b/libpsn00b/psxcd/cdread.c index d211a01..1adc255 100644 --- a/libpsn00b/psxcd/cdread.c +++ b/libpsn00b/psxcd/cdread.c @@ -89,6 +89,8 @@ static int _poll_retry(void) { /* Public API */ int CdReadRetry(int sectors, uint32_t *buf, int mode, int attempts) { + _sdk_validate_args((sectors > 0) && buf && (attempts > 0), -1); + if (CdReadSync(1, 0) > 0) { _sdk_log("CdRead() failed, another read in progress (%d sectors pending)\n", _pending_sectors); return 0; diff --git a/libpsn00b/psxcd/common.c b/libpsn00b/psxcd/common.c index c8f01d1..461ab91 100644 --- a/libpsn00b/psxcd/common.c +++ b/libpsn00b/psxcd/common.c @@ -244,6 +244,8 @@ int CdInit(void) { /* Low-level command API */ int CdCommandF(CdlCommand cmd, const void *param, int length) { + _sdk_validate_args(param || (length <= 0), -1); + const uint8_t *_param = (const uint8_t *) param; _last_command = (uint8_t) cmd; @@ -283,7 +285,7 @@ int CdCommandF(CdlCommand cmd, const void *param, int length) { __asm__ volatile(""); CD_REG(0) = 0; - for (; length; length--) + for (; length > 0; length--) CD_REG(2) = *(_param++); CD_REG(0) = 0; @@ -292,6 +294,8 @@ int CdCommandF(CdlCommand cmd, const void *param, int length) { } int CdCommand(CdlCommand cmd, const void *param, int length, uint8_t *result) { + _sdk_validate_args(param || (length <= 0), -1); + /*if (_ack_pending) { _sdk_log("CdCommand(0x%02x) failed, drive busy\n", cmd); return 0; @@ -329,8 +333,10 @@ int CdControlF(CdlCommand cmd, const void *param) { } else { // The command takes a mandatory parameter or no parameter. length = flags & 3; - if (length && !param) + if (length && !param) { + _sdk_log("CdControl() param is required for command 0x%02x\n", cmd); return -1; + } } return CdCommandF(cmd, param, length); diff --git a/libpsn00b/psxcd/isofs.c b/libpsn00b/psxcd/isofs.c index 0ac782b..5fd0536 100644 --- a/libpsn00b/psxcd/isofs.c +++ b/libpsn00b/psxcd/isofs.c @@ -450,6 +450,8 @@ static char* get_filename(char *name, const char *filename) CdlFILE *CdSearchFile(CdlFILE *fp, const char *filename) { + _sdk_validate_args(fp && filename, NULL); + int i,j,found_dir,num_dirs; int dir_len; char tpath_rbuff[128]; @@ -553,6 +555,8 @@ CdlFILE *CdSearchFile(CdlFILE *fp, const char *filename) CdlDIR *CdOpenDir(const char* path) { + _sdk_validate_args(path, NULL); + CdlDIR_INT* dir; int num_dirs; int i,found_dir; @@ -631,6 +635,8 @@ CdlDIR *CdOpenDir(const char* path) int CdReadDir(CdlDIR *dir, CdlFILE* file) { + _sdk_validate_args(dir && file, 0); + CdlDIR_INT* d_dir; ISO_DIR_ENTRY* dir_entry; @@ -683,6 +689,9 @@ int CdReadDir(CdlDIR *dir, CdlFILE* file) void CdCloseDir(CdlDIR *dir) { + if (!dir) + return; + CdlDIR_INT* d_dir; d_dir = (CdlDIR_INT*)dir; @@ -698,6 +707,8 @@ int CdIsoError() int CdGetVolumeLabel(char *label) { + _sdk_validate_args(label, -1); + int i, length = 31; ISO_DESCRIPTOR* descriptor; @@ -761,6 +772,8 @@ static void _scan_callback(CdlIntrResult status, unsigned char *result) int CdLoadSession(int session) { + _sdk_validate_args(session >= 0, -1); + CdlLOC *loc; CdlCB ready_oldcb; char scanbuff[2048]; diff --git a/libpsn00b/psxcd/misc.c b/libpsn00b/psxcd/misc.c index 851fea6..2f04821 100644 --- a/libpsn00b/psxcd/misc.c +++ b/libpsn00b/psxcd/misc.c @@ -33,6 +33,8 @@ static const char *const _unlock_regions[] = { /* Sector DMA transfer functions */ int CdGetSector(void *madr, int size) { + _sdk_validate_args(madr && (size > 0), 0); + //while (!(CD_REG(0) & (1 << 6))) //__asm__ volatile(""); @@ -47,6 +49,8 @@ int CdGetSector(void *madr, int size) { } int CdGetSector2(void *madr, int size) { + _sdk_validate_args(madr && (size > 0), 0); + //while (!(CD_REG(0) & (1 << 6))) //__asm__ volatile(""); @@ -170,6 +174,8 @@ int CdUnlock(CdlRegionCode region) { /* Misc. functions */ int CdGetToc(CdlLOC *toc) { + _sdk_validate_args(toc, 0); + uint8_t result[4]; if (!CdCommand(CdlGetTN, 0, 0, result)) @@ -199,6 +205,8 @@ int CdGetToc(CdlLOC *toc) { } int CdMix(const CdlATV *vol) { + _sdk_validate_args(vol, 0); + CD_REG(0) = 2; CD_REG(2) = vol->val0; CD_REG(3) = vol->val1; diff --git a/libpsn00b/psxetc/dl.c b/libpsn00b/psxetc/dl.c index ff712eb..06302e2 100644 --- a/libpsn00b/psxetc/dl.c +++ b/libpsn00b/psxetc/dl.c @@ -112,6 +112,8 @@ static uint32_t _elf_hash(const char *str) { /* Symbol map loading/introspection API */ int DL_InitSymbolMap(int num_entries) { + _sdk_validate_args(num_entries, -1); + if (_symbol_map.entries) DL_UnloadSymbolMap(); @@ -151,6 +153,8 @@ void DL_UnloadSymbolMap(void) { } void DL_AddMapSymbol(const char *name, void *ptr) { + _sdk_validate_args_void(name); + uint32_t hash = _elf_hash(name); int index = _symbol_map.index; _symbol_map.index = index + 1; @@ -168,6 +172,8 @@ void DL_AddMapSymbol(const char *name, void *ptr) { } int DL_ParseSymbolMap(const char *ptr, size_t size) { + _sdk_validate_args(ptr && size, 0); + int entries = 0; // Perform a quick scan over the entire map text and count the number of @@ -232,6 +238,8 @@ int DL_ParseSymbolMap(const char *ptr, size_t size) { } void *DL_GetMapSymbol(const char *name) { + _sdk_validate_args(name, 0); + if (!_symbol_map.entries) { _sdk_log("DL_GetMapSymbol() with no map loaded\n"); return 0; @@ -275,8 +283,7 @@ void *DL_SetResolveCallback(void *(*callback)(DLL *, const char *)) { /* Library loading and linking API */ DLL *DL_CreateDLL(DLL *dll, void *ptr, size_t size, DL_ResolveMode mode) { - if (!dll || !ptr) - return 0; + _sdk_validate_args(dll && ptr && size, 0); dll->ptr = ptr; dll->malloc_ptr = (mode & DL_FREE_ON_DESTROY) ? ptr : 0; @@ -463,6 +470,8 @@ void DL_DestroyDLL(DLL *dll) { } void *DL_GetDLLSymbol(const DLL *dll, const char *name) { + _sdk_validate_args(name, 0); + if (!dll) return DL_GetMapSymbol(name); //return _dl_resolve_callback(0, name); diff --git a/libpsn00b/psxetc/interrupts.c b/libpsn00b/psxetc/interrupts.c index 7c8b206..b7bbd07 100644 --- a/libpsn00b/psxetc/interrupts.c +++ b/libpsn00b/psxetc/interrupts.c @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -99,8 +100,7 @@ static void _global_dma_handler(void) { /* IRQ and DMA handler API */ void *InterruptCallback(IRQ_Channel irq, void (*func)(void)) { - if ((irq < 0) || (irq >= NUM_IRQ_CHANNELS)) - return 0; + _sdk_validate_args((irq >= 0) && (irq < NUM_IRQ_CHANNELS), 0); void *old_callback = _irq_handlers[irq]; _irq_handlers[irq] = func; @@ -116,15 +116,13 @@ void *InterruptCallback(IRQ_Channel irq, void (*func)(void)) { } void *GetInterruptCallback(IRQ_Channel irq) { - if ((irq < 0) || (irq >= NUM_IRQ_CHANNELS)) - return 0; + _sdk_validate_args((irq >= 0) && (irq < NUM_IRQ_CHANNELS), 0); return _irq_handlers[irq]; } void *DMACallback(DMA_Channel dma, void (*func)(void)) { - if ((dma < 0) || (dma >= NUM_DMA_CHANNELS)) - return 0; + _sdk_validate_args((dma >= 0) && (dma < NUM_DMA_CHANNELS), 0); void *old_callback = _dma_handlers[dma]; _dma_handlers[dma] = func; @@ -151,8 +149,7 @@ void *DMACallback(DMA_Channel dma, void (*func)(void)) { } void *GetDMACallback(DMA_Channel dma) { - if ((dma < 0) || (dma >= NUM_DMA_CHANNELS)) - return 0; + _sdk_validate_args((dma >= 0) && (dma < NUM_DMA_CHANNELS), 0); return _dma_handlers[dma]; } @@ -160,8 +157,7 @@ void *GetDMACallback(DMA_Channel dma) { /* DMA channel priority API */ int SetDMAPriority(DMA_Channel dma, int priority) { - if ((dma < 0) || (dma >= NUM_DMA_CHANNELS)) - return -1; + _sdk_validate_args((dma >= 0) && (dma < NUM_DMA_CHANNELS), -1); uint32_t dpcr = DMA_DPCR; uint32_t channel = dpcr >> (dma * 4); @@ -175,8 +171,7 @@ int SetDMAPriority(DMA_Channel dma, int priority) { } int GetDMAPriority(DMA_Channel dma) { - if ((dma < 0) || (dma >= NUM_DMA_CHANNELS)) - return -1; + _sdk_validate_args((dma >= 0) && (dma < NUM_DMA_CHANNELS), -1); uint32_t channel = DMA_DPCR >> (dma * 4); return (channel & 8) ? (channel & 7) : -1; diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index e354261..46f87bc 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -187,6 +187,8 @@ int EnqueueDrawOp( uint32_t arg2, uint32_t arg3 ) { + _sdk_validate_args(func, -1); + // If GPU DMA is currently busy, append the command to the queue instead of // executing it immediately. Note that interrupts must be disabled *prior* // to checking if DMA is busy; disabling them afterwards would create a @@ -263,6 +265,8 @@ void *DrawSyncCallback(void (*func)(void)) { /* OT and primitive drawing API */ void ClearOTagR(uint32_t *ot, size_t length) { + _sdk_validate_args_void(ot && length); + DMA_MADR(DMA_OTC) = (uint32_t) &ot[length - 1]; DMA_BCR(DMA_OTC) = length & 0xffff; DMA_CHCR(DMA_OTC) = 0x11000002; @@ -272,6 +276,8 @@ void ClearOTagR(uint32_t *ot, size_t length) { } void ClearOTag(uint32_t *ot, size_t length) { + _sdk_validate_args_void(ot && length); + // DMA6 only supports writing to RAM in reverse order (last to first), so // the OT has to be cleared in software here. This function is thus much // slower than ClearOTagR(). @@ -285,10 +291,14 @@ void ClearOTag(uint32_t *ot, size_t length) { } void AddPrim(uint32_t *ot, const void *pri) { + _sdk_validate_args_void(ot && pri); + addPrim(ot, pri); } -void DrawPrim(const uint32_t *pri) { +void DrawPrim(const uint32_t *pri) { + _sdk_validate_args_void(pri); + size_t length = getlen(pri); DrawSync(0); @@ -308,10 +318,14 @@ void DrawPrim(const uint32_t *pri) { } int DrawOTag(const uint32_t *ot) { + _sdk_validate_args(ot, -1); + return EnqueueDrawOp((void *) &DrawOTag2, (uint32_t) ot, 0, 0); } void DrawOTag2(const uint32_t *ot) { + _sdk_validate_args_void(ot); + GPU_GP1 = 0x04000002; // Enable DMA request, route to GP0 while (DMA_CHCR(DMA_GPU) & (1 << 24)) diff --git a/libpsn00b/psxgpu/env.c b/libpsn00b/psxgpu/env.c index b90a431..cfe09c0 100644 --- a/libpsn00b/psxgpu/env.c +++ b/libpsn00b/psxgpu/env.c @@ -4,6 +4,7 @@ */ #include +#include #include #include @@ -36,6 +37,8 @@ static inline uint32_t _get_window_mask(int size) { /* Drawing API */ DRAWENV *SetDefDrawEnv(DRAWENV *env, int x, int y, int w, int h) { + _sdk_validate_args(env && (w > 0) && (h > 0), 0); + env->clip.x = x; env->clip.y = y; env->clip.w = w; @@ -60,6 +63,8 @@ DRAWENV *SetDefDrawEnv(DRAWENV *env, int x, int y, int w, int h) { } int DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { + _sdk_validate_args(ot && env, -1); + // All commands are grouped into a single display list packet for // performance reasons using tagless primitives (the GPU does not care // about the grouping as the display list is parsed by the CPU). @@ -102,6 +107,8 @@ int DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { } void PutDrawEnv(DRAWENV *env) { + _sdk_validate_args_void(env); + DrawOTagEnv((const uint32_t *) 0x00ffffff, env); } @@ -109,6 +116,8 @@ void PutDrawEnv(DRAWENV *env) { // useful if the DRAWENV structure is never modified (which is the case most of // the time). void PutDrawEnvFast(DRAWENV *env) { + _sdk_validate_args_void(env); + if (!(env->dr_env.tag)) DrawOTagEnv((const uint32_t *) 0x00ffffff, env); else @@ -118,6 +127,8 @@ void PutDrawEnvFast(DRAWENV *env) { /* Display API */ DISPENV *SetDefDispEnv(DISPENV *env, int x, int y, int w, int h) { + _sdk_validate_args(env && (w > 0) && (h > 0), 0); + env->disp.x = x; env->disp.y = y; env->disp.w = w; @@ -136,6 +147,8 @@ DISPENV *SetDefDispEnv(DISPENV *env, int x, int y, int w, int h) { } void PutDispEnv(const DISPENV *env) { + _sdk_validate_args_void(env); + uint32_t h_range, v_range, mode, fb_pos; mode = _gpu_video_mode << 3; @@ -204,6 +217,8 @@ void PutDispEnv(const DISPENV *env) { /* Deprecated "raw" display API */ void PutDispEnvRaw(const DISPENV_RAW *env) { + _sdk_validate_args_void(env); + uint32_t h_range, v_range, fb_pos; h_range = 608 + env->vid_xpos; diff --git a/libpsn00b/psxgpu/font.c b/libpsn00b/psxgpu/font.c index b1c3c7a..c9d60f1 100644 --- a/libpsn00b/psxgpu/font.c +++ b/libpsn00b/psxgpu/font.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -23,6 +24,7 @@ uint16_t _font_clut; extern uint8_t _gpu_debug_font[]; void FntLoad(int x, int y) { + _sdk_validate_args_void((x >= 0) && (y >= 0) && (x < 1024) && (y < 1024)); RECT pos; TIM_IMAGE tim; @@ -66,7 +68,8 @@ void FntLoad(int x, int y) { } int FntOpen(int x, int y, int w, int h, int isbg, int n) { - + _sdk_validate_args((w > 0) && (h > 0) && (n > 0), -1); + int i; // Initialize a text stream @@ -98,7 +101,8 @@ int FntOpen(int x, int y, int w, int h, int isbg, int n) { } int FntPrint(int id, const char *fmt, ...) { - + _sdk_validate_args((id < _nstreams) && fmt, -1); + int n; va_list ap; @@ -124,7 +128,8 @@ int FntPrint(int id, const char *fmt, ...) { } char *FntFlush(int id) { - + _sdk_validate_args(id < _nstreams, 0); + char *opri; SPRT_8 *sprt; DR_TPAGE *tpage; @@ -226,7 +231,8 @@ char *FntFlush(int id) { } char *FntSort(uint32_t *ot, char *pri, int x, int y, const char *text) { - + _sdk_validate_args(ot && pri, 0); + DR_TPAGE *tpage; SPRT_8 *sprt = (SPRT_8*)pri; int i; diff --git a/libpsn00b/psxgpu/image.c b/libpsn00b/psxgpu/image.c index ba5c445..63bceb6 100644 --- a/libpsn00b/psxgpu/image.c +++ b/libpsn00b/psxgpu/image.c @@ -69,6 +69,8 @@ static void _dma_transfer(const RECT *rect, uint32_t *data, int write) { /* VRAM transfer API */ int LoadImage(const RECT *rect, const uint32_t *data) { + _sdk_validate_args(rect && data, -1); + int index = _next_saved_rect; _saved_rects[index] = *rect; @@ -83,6 +85,8 @@ int LoadImage(const RECT *rect, const uint32_t *data) { } int StoreImage(const RECT *rect, uint32_t *data) { + _sdk_validate_args(rect && data, -1); + int index = _next_saved_rect; _saved_rects[index] = *rect; @@ -97,18 +101,26 @@ int StoreImage(const RECT *rect, uint32_t *data) { } int MoveImage(const RECT *rect, int x, int y) { + _sdk_validate_args(rect, -1); + return EnqueueDrawOp((void *) &MoveImage2, (uint32_t) rect, x, y); } void LoadImage2(const RECT *rect, const uint32_t *data) { + _sdk_validate_args_void(rect && data); + _dma_transfer(rect, (uint32_t *) data, 1); } void StoreImage2(const RECT *rect, uint32_t *data) { + _sdk_validate_args_void(rect && data); + _dma_transfer(rect, data, 0); } void MoveImage2(const RECT *rect, int x, int y) { + _sdk_validate_args_void(rect); + while (!(GPU_GP1 & (1 << 26))) __asm__ volatile(""); @@ -131,6 +143,8 @@ void MoveImage2(const RECT *rect, int x, int y) { // returning pointers to them, which become useless once the .TIM file is // unloaded from main RAM. int GsGetTimInfo(const uint32_t *tim, GsIMAGE *info) { + _sdk_validate_args(tim && info, 1); + if ((*(tim++) & 0xffff) != 0x0010) return 1; @@ -157,6 +171,8 @@ int GsGetTimInfo(const uint32_t *tim, GsIMAGE *info) { } int GetTimInfo(const uint32_t *tim, TIM_IMAGE *info) { + _sdk_validate_args(tim && info, 1); + if ((*(tim++) & 0xffff) != 0x0010) return 1; diff --git a/libpsn00b/psxpress/mdec.c b/libpsn00b/psxpress/mdec.c index 394a0ce..b3aa837 100644 --- a/libpsn00b/psxpress/mdec.c +++ b/libpsn00b/psxpress/mdec.c @@ -111,6 +111,8 @@ void DecDCTPutEnv(const DECDCTENV *env, int mono) { } void DecDCTin(const uint32_t *data, int mode) { + _sdk_validate_args_void(data); + uint32_t header = *data; DecDCTinSync(0); @@ -128,6 +130,8 @@ void DecDCTin(const uint32_t *data, int mode) { // data length as an argument rather than parsing it from the first 4 bytes of // the stream. void DecDCTinRaw(const uint32_t *data, size_t length) { + _sdk_validate_args_void(data && length); + if ((length >= DMA_CHUNK_LENGTH) && (length % DMA_CHUNK_LENGTH)) { _sdk_log("input data length (%d) is not a multiple of %d, rounding\n", length, DMA_CHUNK_LENGTH); length += DMA_CHUNK_LENGTH - 1; @@ -157,6 +161,8 @@ int DecDCTinSync(int mode) { } void DecDCTout(uint32_t *data, size_t length) { + _sdk_validate_args_void(data && length); + DecDCToutSync(0); if ((length >= DMA_CHUNK_LENGTH) && (length % DMA_CHUNK_LENGTH)) { diff --git a/libpsn00b/psxspu/common.c b/libpsn00b/psxspu/common.c index 1275621..efe1d9e 100644 --- a/libpsn00b/psxspu/common.c +++ b/libpsn00b/psxspu/common.c @@ -164,12 +164,18 @@ void SpuInit(void) { } size_t SpuRead(uint32_t *data, size_t size) { + _sdk_validate_args(data && size, 0); + return _dma_transfer(data, size, 0) * 4; } size_t SpuWrite(const uint32_t *data, size_t size) { - if (_transfer_addr < WRITABLE_AREA_ADDR) + _sdk_validate_args(data && size, 0); + + if (_transfer_addr < WRITABLE_AREA_ADDR) { + _sdk_log("ignoring attempt to write to capture buffers at 0x%05x\n", _transfer_addr); return 0; + } // I/O transfer mode is not that useful, but whatever. if (_transfer_mode) @@ -179,6 +185,8 @@ size_t SpuWrite(const uint32_t *data, size_t size) { } size_t SpuWritePartly(const uint32_t *data, size_t size) { + //_sdk_validate_args(data && size, 0); + size_t _size = SpuWrite(data, size); _transfer_addr += (_size + 1) / 2; -- cgit v1.2.3 From ca6b54f3c15a7b00a5ede64ba452f2955a421a1e Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Thu, 11 May 2023 15:59:56 +0200 Subject: Add MoveImage(), SetDrawOpType(), GPU IRQ support --- libpsn00b/include/hwregs_c.h | 38 ++++++------ libpsn00b/include/psxgpu.h | 40 ++++++++----- libpsn00b/include/psxspu.h | 2 + libpsn00b/psxgpu/common.c | 139 ++++++++++++++++++++++++------------------- libpsn00b/psxgpu/env.c | 8 +-- libpsn00b/psxgpu/image.c | 3 + libpsn00b/psxspu/common.c | 8 +++ 7 files changed, 137 insertions(+), 101 deletions(-) (limited to 'libpsn00b/psxgpu') diff --git a/libpsn00b/include/hwregs_c.h b/libpsn00b/include/hwregs_c.h index 3fd5b2c..2152986 100644 --- a/libpsn00b/include/hwregs_c.h +++ b/libpsn00b/include/hwregs_c.h @@ -34,7 +34,7 @@ #define CD_DATA _MMIO8(IOBASE | 0x1802) #define CD_IRQ _MMIO8(IOBASE | 0x1803) -#define CD_REG(N) _MMIO8(IOBASE | 0x1800 + (N)) +#define CD_REG(N) _MMIO8((IOBASE | 0x1800) + (N)) /* SPU */ @@ -73,13 +73,13 @@ // These are not named SPU_VOICE_* to avoid name clashes with SPU attribute // flags defined in psxspu.h. -#define SPU_CH_VOL_L(N) _MMIO16(IOBASE | 0x1c00 + 16 * (N)) -#define SPU_CH_VOL_R(N) _MMIO16(IOBASE | 0x1c02 + 16 * (N)) -#define SPU_CH_FREQ(N) _MMIO16(IOBASE | 0x1c04 + 16 * (N)) -#define SPU_CH_ADDR(N) _MMIO16(IOBASE | 0x1c06 + 16 * (N)) -#define SPU_CH_ADSR1(N) _MMIO16(IOBASE | 0x1c08 + 16 * (N)) -#define SPU_CH_ADSR2(N) _MMIO16(IOBASE | 0x1c0a + 16 * (N)) -#define SPU_CH_LOOP_ADDR(N) _MMIO16(IOBASE | 0x1c0e + 16 * (N)) +#define SPU_CH_VOL_L(N) _MMIO16((IOBASE | 0x1c00) + (16 * (N))) +#define SPU_CH_VOL_R(N) _MMIO16((IOBASE | 0x1c02) + (16 * (N))) +#define SPU_CH_FREQ(N) _MMIO16((IOBASE | 0x1c04) + (16 * (N))) +#define SPU_CH_ADDR(N) _MMIO16((IOBASE | 0x1c06) + (16 * (N))) +#define SPU_CH_ADSR1(N) _MMIO16((IOBASE | 0x1c08) + (16 * (N))) +#define SPU_CH_ADSR2(N) _MMIO16((IOBASE | 0x1c0a) + (16 * (N))) +#define SPU_CH_LOOP_ADDR(N) _MMIO16((IOBASE | 0x1c0e) + (16 * (N))) /* MDEC */ @@ -91,11 +91,11 @@ // IMPORTANT: even though SIO_DATA is a 32-bit register, it should only be // accessed as 8-bit. Reading it as 16 or 32-bit works fine on real hardware, // but leads to problems in some emulators. -#define SIO_DATA(N) _MMIO8 (IOBASE | 0x1040 + 16 * (N)) -#define SIO_STAT(N) _MMIO16(IOBASE | 0x1044 + 16 * (N)) -#define SIO_MODE(N) _MMIO16(IOBASE | 0x1048 + 16 * (N)) -#define SIO_CTRL(N) _MMIO16(IOBASE | 0x104a + 16 * (N)) -#define SIO_BAUD(N) _MMIO16(IOBASE | 0x104e + 16 * (N)) +#define SIO_DATA(N) _MMIO8 ((IOBASE | 0x1040) + (16 * (N))) +#define SIO_STAT(N) _MMIO16((IOBASE | 0x1044) + (16 * (N))) +#define SIO_MODE(N) _MMIO16((IOBASE | 0x1048) + (16 * (N))) +#define SIO_CTRL(N) _MMIO16((IOBASE | 0x104a) + (16 * (N))) +#define SIO_BAUD(N) _MMIO16((IOBASE | 0x104e) + (16 * (N))) /* IRQ controller */ @@ -107,15 +107,15 @@ #define DMA_DPCR _MMIO32(IOBASE | 0x10f0) #define DMA_DICR _MMIO32(IOBASE | 0x10f4) -#define DMA_MADR(N) _MMIO32(IOBASE | 0x1080 + 16 * (N)) -#define DMA_BCR(N) _MMIO32(IOBASE | 0x1084 + 16 * (N)) -#define DMA_CHCR(N) _MMIO32(IOBASE | 0x1088 + 16 * (N)) +#define DMA_MADR(N) _MMIO32((IOBASE | 0x1080) + (16 * (N))) +#define DMA_BCR(N) _MMIO32((IOBASE | 0x1084) + (16 * (N))) +#define DMA_CHCR(N) _MMIO32((IOBASE | 0x1088) + (16 * (N))) /* Timers */ -#define TIMER_VALUE(N) _MMIO32(IOBASE | 0x1100 + 16 * (N)) -#define TIMER_CTRL(N) _MMIO32(IOBASE | 0x1104 + 16 * (N)) -#define TIMER_RELOAD(N) _MMIO32(IOBASE | 0x1108 + 16 * (N)) +#define TIMER_VALUE(N) _MMIO32((IOBASE | 0x1100) + (16 * (N))) +#define TIMER_CTRL(N) _MMIO32((IOBASE | 0x1104) + (16 * (N))) +#define TIMER_RELOAD(N) _MMIO32((IOBASE | 0x1108) + (16 * (N))) /* Memory/bus control */ diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h index 9b99fe9..2e9e4eb 100644 --- a/libpsn00b/include/psxgpu.h +++ b/libpsn00b/include/psxgpu.h @@ -44,6 +44,11 @@ typedef enum _GPU_VideoMode { MODE_PAL = 1 } GPU_VideoMode; +typedef enum _GPU_DrawOpType { + DRAWOP_TYPE_DMA = 1, + DRAWOP_TYPE_GPU_IRQ = 2 +} GPU_DrawOpType; + /* Structure macros */ #define setVector(v, _x, _y, _z) \ @@ -142,11 +147,11 @@ typedef enum _GPU_VideoMode { ((tge) ? (getcode_T(p) |= 1) : (getcode_T(p) &= ~1)) #define getTPage(tp, abr, x, y) ( \ - (((x) / 64) & 15) | \ - ((((y) / 256) & 1) << 4) | \ - (((abr) & 3) << 5) | \ - (((tp) & 3) << 7) | \ - ((((y) / 512) & 1) << 11) \ + (((x) & 0x3c0) >> 6) | \ + (((y) & 0x100) >> 4) | \ + (((y) & 0x200) << 2) | \ + (((abr) & 3) << 5) | \ + (((tp) & 3) << 7) \ ) #define getClut(x, y) (((y) << 6) | (((x) >> 4) & 0x3f)) @@ -210,8 +215,8 @@ typedef enum _GPU_VideoMode { #define setDrawTPage_T(p, dfe, dtd, tpage) \ (p)->code[0] = (0xe1000000 | \ (tpage) | \ - ((dtd) << 9) | \ - ((dfe) << 10) \ + (((dtd) & 1) << 9) | \ + (((dfe) & 1) << 10) \ ) #define setDrawTPage(p, dfe, dtd, tpage) \ setlen(p, 1), setDrawTPage_T(p, dfe, dtd, tpage) @@ -261,6 +266,11 @@ typedef enum _GPU_VideoMode { #define setDrawStp(p, pbw, mt) \ setlen(p, 1), setDrawStp_T(p, pbw, mt) +#define setDrawIRQ_T(p) \ + (p)->code[0] = 0x1f000000 +#define setDrawIRQ(p) \ + setlen(p, 1), setDrawIRQ_T(p) + /* Primitive structure definitions */ typedef struct _P_TAG_T { @@ -475,12 +485,11 @@ _DEF_PRIM(FILL, uint16_t w, h; ) -_DEF_PRIM(BLIT, +_DEF_PRIM(DR_MOVE, uint8_t p0, p1, p2, code; uint16_t x0, y0; uint16_t x1, y1; uint16_t w, h; - uint32_t pad[4]; ) _DEF_PRIM(DR_AREA, @@ -498,6 +507,9 @@ _DEF_PRIM(DR_TPAGE, _DEF_PRIM(DR_STP, uint32_t code[1]; ) +_DEF_PRIM(DR_IRQ, + uint32_t code[1]; +) _DEF_PRIM(DR_ENV, DR_TPAGE_T tpage; @@ -579,12 +591,8 @@ int VSync(int mode); void *VSyncHaltFunction(void (*func)(void)); void *VSyncCallback(void (*func)(void)); -int EnqueueDrawOp( - void (*func)(uint32_t, uint32_t, uint32_t), - uint32_t arg1, - uint32_t arg2, - uint32_t arg3 -); +void SetDrawOpType(GPU_DrawOpType type); +int EnqueueDrawOp(void (*func)(), uint32_t arg1, uint32_t arg2, uint32_t arg3); int DrawSync(int mode); void *DrawSyncCallback(void (*func)(void)); @@ -600,6 +608,8 @@ void ClearOTag(uint32_t *ot, size_t length); int DrawOTag(const uint32_t *ot); int DrawOTagEnv(const uint32_t *ot, DRAWENV *env); void DrawOTag2(const uint32_t *ot); +int DrawBuffer(const uint32_t *buf, size_t length); +void DrawBuffer2(const uint32_t *buf, size_t length); void DrawPrim(const uint32_t *pri); void AddPrim(uint32_t *ot, const void *pri); diff --git a/libpsn00b/include/psxspu.h b/libpsn00b/include/psxspu.h index d829821..b544952 100644 --- a/libpsn00b/include/psxspu.h +++ b/libpsn00b/include/psxspu.h @@ -156,7 +156,9 @@ size_t SpuRead(uint32_t *data, size_t size); size_t SpuWrite(const uint32_t *data, size_t size); size_t SpuWritePartly(const uint32_t *data, size_t size); SPU_TransferMode SpuSetTransferMode(SPU_TransferMode mode); +SPU_TransferMode SpuGetTransferMode(void); uint32_t SpuSetTransferStartAddr(uint32_t addr); +uint32_t SpuGetTransferStartAddr(void); int SpuIsTransferCompleted(int mode); #ifdef __cplusplus diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index 46f87bc..8af6c5b 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -10,9 +10,8 @@ #include #include -#define QUEUE_LENGTH 16 -#define DMA_CHUNK_LENGTH 16 -#define VSYNC_TIMEOUT 0x100000 +#define QUEUE_LENGTH 16 +#define VSYNC_TIMEOUT 0x100000 static void _default_vsync_halt(void); @@ -21,7 +20,7 @@ static void _default_vsync_halt(void); typedef struct { void (*func)(uint32_t, uint32_t, uint32_t); uint32_t arg1, arg2, arg3; -} QueueEntry; +} DrawOp; /* Internal globals */ @@ -31,10 +30,10 @@ static void (*_vsync_halt_func)(void) = &_default_vsync_halt; static void (*_vsync_callback)(void) = (void *) 0; static void (*_drawsync_callback)(void) = (void *) 0; -static volatile QueueEntry _draw_queue[QUEUE_LENGTH]; -static volatile uint8_t _queue_head, _queue_tail, _queue_length; -static volatile uint32_t _vblank_counter, _last_vblank; -static volatile uint16_t _last_hblank; +static volatile DrawOp _draw_queue[QUEUE_LENGTH]; +static volatile uint8_t _queue_head, _queue_tail, _queue_length, _drawop_type; +static volatile uint32_t _vblank_counter, _last_vblank; +static volatile uint16_t _last_hblank; /* Private interrupt handlers */ @@ -45,15 +44,16 @@ static void _vblank_handler(void) { _vsync_callback(); } -static void _gpu_dma_handler(void) { - if (GPU_GP1 & (1 << 24)) - GPU_GP1 = 0x02000000; // Reset IRQ +static void _process_drawop(void) { + int length = _queue_length; + if (!length) + return; - if (--_queue_length) { + if (--length) { int head = _queue_head; _queue_head = (head + 1) % QUEUE_LENGTH; - volatile QueueEntry *entry = &_draw_queue[head]; + volatile DrawOp *entry = &_draw_queue[head]; entry->func(entry->arg1, entry->arg2, entry->arg3); } else { GPU_GP1 = 0x04000000; // Disable DMA request @@ -61,17 +61,36 @@ static void _gpu_dma_handler(void) { if (_drawsync_callback) _drawsync_callback(); } + + _queue_length = length; +} + +static void _gpu_irq_handler(void) { + GPU_GP1 = 0x02000000; // Reset IRQ + + if (_drawop_type == DRAWOP_TYPE_GPU_IRQ) + _process_drawop(); +} + +static void _gpu_dma_handler(void) { + if (_drawop_type == DRAWOP_TYPE_DMA) + _process_drawop(); } /* GPU reset and system initialization */ void ResetGraph(int mode) { + _queue_head = 0; + _queue_tail = 0; + _queue_length = 0; + _drawop_type = 0; + // Perform some basic system initialization when ResetGraph() is called for // the first time. if (!ResetCallback()) { EnterCriticalSection(); InterruptCallback(IRQ_VBLANK, &_vblank_handler); - InterruptCallback(IRQ_GPU, &_gpu_dma_handler); + InterruptCallback(IRQ_GPU, &_gpu_irq_handler); DMACallback(DMA_GPU, &_gpu_dma_handler); _gpu_video_mode = (GPU_GP1 >> 20) & 1; @@ -80,15 +99,15 @@ void ResetGraph(int mode) { _sdk_log("setup done, default mode is %s\n", _gpu_video_mode ? "PAL" : "NTSC"); } - _queue_head = 0; - _queue_tail = 0; - _queue_length = 0; - - if (mode == 1) { + if (mode) { GPU_GP1 = 0x01000000; // Reset command buffer GPU_GP1 = 0x02000000; // Reset IRQ GPU_GP1 = 0x04000000; // Disable DMA request - return; + + if (mode == 1) + return; + } else { + GPU_GP1 = 0x00000000; // Reset GPU } SetDMAPriority(DMA_GPU, 3); @@ -96,20 +115,12 @@ void ResetGraph(int mode) { DMA_CHCR(DMA_GPU) = 0x00000201; // Stop DMA DMA_CHCR(DMA_OTC) = 0x00000200; // Stop DMA - if (mode) { - GPU_GP1 = 0x01000000; // Reset command buffer - GPU_GP1 = 0x02000000; // Reset IRQ - GPU_GP1 = 0x04000000; // Disable DMA request - } else { - GPU_GP1 = 0x00000000; // Reset GPU - } + TIMER_CTRL(0) = 0x0500; + TIMER_CTRL(1) = 0x0500; _vblank_counter = 0; _last_vblank = 0; _last_hblank = 0; - - TIMER_CTRL(0) = 0x0500; - TIMER_CTRL(1) = 0x0500; } /* VSync() API */ @@ -179,14 +190,11 @@ void *VSyncCallback(void (*func)(void)) { /* Command queue API */ -// This function is normally only used internally, but it is exposed for -// advanced use cases. -int EnqueueDrawOp( - void (*func)(uint32_t, uint32_t, uint32_t), - uint32_t arg1, - uint32_t arg2, - uint32_t arg3 -) { +void SetDrawOpType(GPU_DrawOpType type) { + _drawop_type = type; +} + +int EnqueueDrawOp(void (*func)(), uint32_t arg1, uint32_t arg2, uint32_t arg3) { _sdk_validate_args(func, -1); // If GPU DMA is currently busy, append the command to the queue instead of @@ -216,7 +224,7 @@ int EnqueueDrawOp( _queue_tail = (tail + 1) % QUEUE_LENGTH; _queue_length = length + 1; - volatile QueueEntry *entry = &_draw_queue[tail]; + volatile DrawOp *entry = &_draw_queue[tail]; entry->func = func; entry->arg1 = arg1; entry->arg2 = arg2; @@ -283,10 +291,10 @@ void ClearOTag(uint32_t *ot, size_t length) { // slower than ClearOTagR(). // https://problemkaputt.de/psx-spx.htm#dmachannels for (int i = 0; i < (length - 1); i++) - ot[i] = (uint32_t) &ot[i + 1] & 0x00ffffff; + ot[i] = (uint32_t) &ot[i + 1] & 0x7fffff; //setaddr(&ot[i], &ot[i + 1]); - ot[length - 1] = 0x00ffffff; + ot[length - 1] = 0xffffff; //termPrim(&ot[length - 1]); } @@ -296,27 +304,6 @@ void AddPrim(uint32_t *ot, const void *pri) { addPrim(ot, pri); } -void DrawPrim(const uint32_t *pri) { - _sdk_validate_args_void(pri); - - size_t length = getlen(pri); - - DrawSync(0); - GPU_GP1 = 0x04000002; // Enable DMA request, route to GP0 - - // NOTE: if length >= DMA_CHUNK_LENGTH then it also has to be a multiple of - // DMA_CHUNK_LENGTH, otherwise the DMA channel will get stuck waiting for - // more data indefinitely. - DMA_MADR(DMA_GPU) = (uint32_t) &pri[1]; - if (length < DMA_CHUNK_LENGTH) - DMA_BCR(DMA_GPU) = 0x00010000 | length; - else - DMA_BCR(DMA_GPU) = DMA_CHUNK_LENGTH | - ((length / DMA_CHUNK_LENGTH) << 16); - - DMA_CHCR(DMA_GPU) = 0x01000201; -} - int DrawOTag(const uint32_t *ot) { _sdk_validate_args(ot, -1); @@ -326,6 +313,7 @@ int DrawOTag(const uint32_t *ot) { void DrawOTag2(const uint32_t *ot) { _sdk_validate_args_void(ot); + SetDrawOpType(DRAWOP_TYPE_DMA); GPU_GP1 = 0x04000002; // Enable DMA request, route to GP0 while (DMA_CHCR(DMA_GPU) & (1 << 24)) @@ -336,6 +324,33 @@ void DrawOTag2(const uint32_t *ot) { DMA_CHCR(DMA_GPU) = 0x01000401; } +int DrawBuffer(const uint32_t *buf, size_t length) { + _sdk_validate_args(buf && length && (length <= 0xffff), -1); + + return EnqueueDrawOp((void *) &DrawBuffer2, (uint32_t) buf, length, 0); +} + +void DrawBuffer2(const uint32_t *buf, size_t length) { + _sdk_validate_args_void(buf && length && (length <= 0xffff)); + + SetDrawOpType(DRAWOP_TYPE_DMA); + GPU_GP1 = 0x04000002; // Enable DMA request, route to GP0 + + while (DMA_CHCR(DMA_GPU) & (1 << 24)) + __asm__ volatile(""); + + DMA_MADR(DMA_GPU) = (uint32_t) buf; + DMA_BCR(DMA_GPU) = 0x00000001 | (length << 16); + DMA_CHCR(DMA_GPU) = 0x01000201; +} + +void DrawPrim(const uint32_t *pri) { + _sdk_validate_args_void(pri); + + DrawSync(0); + DrawBuffer2(&pri[1], getlen(pri)); +} + /* Queue pause/resume API */ int IsIdleGPU(int timeout) { diff --git a/libpsn00b/psxgpu/env.c b/libpsn00b/psxgpu/env.c index cfe09c0..c9e6142 100644 --- a/libpsn00b/psxgpu/env.c +++ b/libpsn00b/psxgpu/env.c @@ -86,9 +86,7 @@ int DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { // Set drawing area setDrawArea_T(&(prim->area), &(env->clip)); setDrawOffset_T( - &(prim->offset), - env->clip.x + env->ofs[0], - env->clip.y + env->ofs[1] + &(prim->offset), env->clip.x + env->ofs[0], env->clip.y + env->ofs[1] ); if (env->isbg) { @@ -109,7 +107,7 @@ int DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { void PutDrawEnv(DRAWENV *env) { _sdk_validate_args_void(env); - DrawOTagEnv((const uint32_t *) 0x00ffffff, env); + DrawOTagEnv((const uint32_t *) 0xffffff, env); } // This function skips rebuilding the cached packet whenever possible and is @@ -119,7 +117,7 @@ void PutDrawEnvFast(DRAWENV *env) { _sdk_validate_args_void(env); if (!(env->dr_env.tag)) - DrawOTagEnv((const uint32_t *) 0x00ffffff, env); + DrawOTagEnv((const uint32_t *) 0xffffff, env); else DrawOTag((const uint32_t *) &(env->dr_env)); } diff --git a/libpsn00b/psxgpu/image.c b/libpsn00b/psxgpu/image.c index 63bceb6..a08e293 100644 --- a/libpsn00b/psxgpu/image.c +++ b/libpsn00b/psxgpu/image.c @@ -41,6 +41,7 @@ static void _dma_transfer(const RECT *rect, uint32_t *data, int write) { while (!(GPU_GP1 & (1 << 26))) __asm__ volatile(""); + SetDrawOpType(DRAWOP_TYPE_DMA); GPU_GP1 = 0x04000000; // Disable DMA request GPU_GP0 = 0x01000000; // Flush cache @@ -124,6 +125,8 @@ void MoveImage2(const RECT *rect, int x, int y) { while (!(GPU_GP1 & (1 << 26))) __asm__ volatile(""); + SetDrawOpType(DRAWOP_TYPE_GPU_IRQ); + GPU_GP0 = 0x80000000; //GPU_GP0 = rect->x | (rect->y << 16); GPU_GP0 = *((const uint32_t *) &(rect->x)); diff --git a/libpsn00b/psxspu/common.c b/libpsn00b/psxspu/common.c index efe1d9e..64c6d1b 100644 --- a/libpsn00b/psxspu/common.c +++ b/libpsn00b/psxspu/common.c @@ -198,6 +198,10 @@ SPU_TransferMode SpuSetTransferMode(SPU_TransferMode mode) { return mode; } +SPU_TransferMode SpuGetTransferMode(void) { + return _transfer_mode; +} + uint32_t SpuSetTransferStartAddr(uint32_t addr) { if (addr > 0x7ffff) return 0; @@ -206,6 +210,10 @@ uint32_t SpuSetTransferStartAddr(uint32_t addr) { return addr; } +uint32_t SpuGetTransferStartAddr(void) { + return _transfer_addr * 8; +} + int SpuIsTransferCompleted(int mode) { if (!mode) return ((SPU_STAT >> 10) & 1) ^ 1; -- cgit v1.2.3 From 2021cdfca29dc5c98e570a674ac97f92f47a1129 Mon Sep 17 00:00:00 2001 From: spicyjpeg Date: Thu, 11 May 2023 23:08:11 +0200 Subject: Add GPU IRQ variants of all display list APIs --- doc/drawing_queue.md | 105 ++++++++++++++++++++++++++++++++ libpsn00b/include/psxgpu.h | 5 ++ libpsn00b/psxgpu/common.c | 81 ------------------------- libpsn00b/psxgpu/drawing.c | 148 +++++++++++++++++++++++++++++++++++++++++++++ libpsn00b/psxgpu/env.c | 86 ++++++++++++++++---------- libpsn00b/psxgpu/image.c | 12 +++- 6 files changed, 323 insertions(+), 114 deletions(-) create mode 100644 doc/drawing_queue.md create mode 100644 libpsn00b/psxgpu/drawing.c (limited to 'libpsn00b/psxgpu') diff --git a/doc/drawing_queue.md b/doc/drawing_queue.md new file mode 100644 index 0000000..4fa83f7 --- /dev/null +++ b/doc/drawing_queue.md @@ -0,0 +1,105 @@ + +# GPU drawing queue + +`libpsxgpu` manages access to the GPU by implementing a software driven queue. +This queue, separate from the GPU's internal command FIFO, allows for high-level +management of GPU operations such as display list sending, VRAM image uploads +and framebuffer readback, in a similar way to the drawing queue system +implemented behind the scenes by the official SDK. + +The queue is managed internally by the library and can hold up to 16 drawing +operations ("DrawOps"). Each DrawOp is represented by a pointer to a function, +alongside any arguments to be passed to it. Whenever the GPU is idle, +`libpsxgpu` fetches a DrawOp from the queue and calls its respective function, +which should then proceed to actually send commands to the GPU or set up and +start a DMA transfer. `DrawSync()` can be called to wait for the queue to become +empty or get its current length, while `DrawSyncCallback()` may be used to +register a callback that will be invoked once the GPU is idle and no more +DrawOps are pending. + +Completion of each DrawOp (and transition of the GPU from busy to idle state) is +signalled through one of two means: + +- the DMA channel 2 IRQ, fired automatically by the DMA unit when a data + transfer such as a VRAM upload or a display list has finished executing; +- the GPU IRQ, triggered manually using the `GP0(0x1f)` command or the `DR_IRQ` + primitive. + +Note that the end of a DMA transfer does not necessarily imply that the GPU has +finished executing all commands; the last command issued may not yet be done, +hence the ability to use the GPU IRQ instead is provided as a more reliable way +to detect the completion of certain commands. + +## Built-in DrawOps + +The library includes a number of built-in DrawOps for the most common use cases. +The following APIs are wrappers around DrawOps: + +- `DrawBuffer()` and `DrawBufferIRQ()` queue a new DrawOp to start a DMA + transfer in chunked mode (sending one word at a time) with the specified + starting address and number of words. `DrawBuffer2()` and `DrawBufferIRQ2()` + are the underlying DrawOp functions respectively. +- `DrawOTag()` and `DrawOTagIRQ()` queue a new DrawOp to start a DMA transfer in + linked-list mode with the specified starting address, with `DrawOTag2()` and + `DrawOTagIRQ2()` being the respective DrawOp functions. +- `PutDrawEnv()`, `PutDrawEnvFast()`, `DrawOTagEnv()` and `DrawOTagEnvIRQ()` + insert drawing environment setup commands as the first (or only) item in a + display list, then proceed to pass it to `DrawOTag()`. The setup packet + linked into the display list is stored as part of the `DRAWENV` structure. +- `LoadImage()` and `StoreImage()` copy the provided coordinates into a + temporary buffer, then proceed to enqueue a DrawOp to actually start the VRAM + transfer. The synchronous variants of these APIs are `LoadImage2()` and + `StoreImage2()` respectively. +- `MoveImage()` saves the provided coordinates into a temporary buffer, then + enqueues a DrawOp that will issue a `GP0(0x80)` VRAM blitting command. As + this command is handled entirely by the GPU with no DMA transfers involved, + the GPU IRQ is used to detect its completion. + +## Custom DrawOps + +Unlike the official SDK, `libpsxgpu` exposes the drawing queue by providing a +way to enqueue arbitrary custom DrawOps. This can be useful for profiling +purposes or to work around specific GPU bugs (see the use cases section). + +Custom DrawOps can be pushed into the queue by calling `EnqueueDrawOp()` and +passing a pointer to the callback function in charge of issuing the DrawOp's +commands to the GPU, as well as up to 3 arguments to be passed through to it. +The function must: + +- call `SetDrawOpType()` to let the library know which type of IRQ it shall wait + for before moving onto the next DrawOp (either `DRAWOP_TYPE_DMA` or + `DRAWOP_TYPE_GPU_IRQ`); +- wait until the GPU is ready to accept commands by polling the status bits in + `GPU_STAT` and make sure DMA channel 2 is also idle before proceeding; +- issue any commands to the GPU's GP0 register and/or set up a DMA transfer, + terminating them with a `GP0(0x1f)` IRQ command if appropriate. + +Note that DrawOps are called from within the exception handler's context and +must thus not block for significant periods of time, manipulate COP0 registers +or wait for any IRQs to occur. They are also restricted from manipulating the +drawing queue by e.g. calling `EnqueueDrawOp()`, `DrawOTag()` or any other +function that enqueues a DrawOp. + +## Use cases + +### Scissoring commands + +The GPU provides commands to set the origin of all X/Y coordinates passed to it +as well as a scissoring region, all pixels outside of which are automatically +masked out during drawing. These commands are issued to the GP0 register and can +be inserted in a display list through the `DR_OFFSET` and `DR_AREA` primitives, +however they will *not* go through the GPU's command FIFO like most other +primitives. They will instead take effect immediately, resulting in graphical +glitches if the GPU is already busy processing a drawing command (i.e. if they +are not the very first commands in a display list). + +The software-driven drawing queue provides a way around this. By splitting up a +frame's display list into multiple chunks, one for each scissoring command +issued, it is possible to always place scissoring commands at the beginning of a +chunk. Each chunk can be terminated with a `DR_IRQ` primitive and queued for +drawing using `DrawOTagIRQ()` to ensure the GPU goes idle before the next chunk +is sent, preventing scissoring commands from being received by the GPU while +busy. + +----------------------------------------- +_Last updated on 2023-05-11 by spicyjpeg_ diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h index 2e9e4eb..d7f1ad5 100644 --- a/libpsn00b/include/psxgpu.h +++ b/libpsn00b/include/psxgpu.h @@ -606,10 +606,15 @@ void MoveImage2(const RECT *rect, int x, int y); void ClearOTagR(uint32_t *ot, size_t length); void ClearOTag(uint32_t *ot, size_t length); int DrawOTag(const uint32_t *ot); +int DrawOTagIRQ(const uint32_t *ot); int DrawOTagEnv(const uint32_t *ot, DRAWENV *env); +int DrawOTagEnvIRQ(const uint32_t *ot, DRAWENV *env); void DrawOTag2(const uint32_t *ot); +void DrawOTagIRQ2(const uint32_t *ot); int DrawBuffer(const uint32_t *buf, size_t length); +int DrawBufferIRQ(const uint32_t *buf, size_t length); void DrawBuffer2(const uint32_t *buf, size_t length); +void DrawBufferIRQ2(const uint32_t *buf, size_t length); void DrawPrim(const uint32_t *pri); void AddPrim(uint32_t *ot, const void *pri); diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index 8af6c5b..7e0758b 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -270,87 +270,6 @@ void *DrawSyncCallback(void (*func)(void)) { return old_callback; } -/* OT and primitive drawing API */ - -void ClearOTagR(uint32_t *ot, size_t length) { - _sdk_validate_args_void(ot && length); - - DMA_MADR(DMA_OTC) = (uint32_t) &ot[length - 1]; - DMA_BCR(DMA_OTC) = length & 0xffff; - DMA_CHCR(DMA_OTC) = 0x11000002; - - while (DMA_CHCR(DMA_OTC) & (1 << 24)) - __asm__ volatile(""); -} - -void ClearOTag(uint32_t *ot, size_t length) { - _sdk_validate_args_void(ot && length); - - // DMA6 only supports writing to RAM in reverse order (last to first), so - // the OT has to be cleared in software here. This function is thus much - // slower than ClearOTagR(). - // https://problemkaputt.de/psx-spx.htm#dmachannels - for (int i = 0; i < (length - 1); i++) - ot[i] = (uint32_t) &ot[i + 1] & 0x7fffff; - //setaddr(&ot[i], &ot[i + 1]); - - ot[length - 1] = 0xffffff; - //termPrim(&ot[length - 1]); -} - -void AddPrim(uint32_t *ot, const void *pri) { - _sdk_validate_args_void(ot && pri); - - addPrim(ot, pri); -} - -int DrawOTag(const uint32_t *ot) { - _sdk_validate_args(ot, -1); - - return EnqueueDrawOp((void *) &DrawOTag2, (uint32_t) ot, 0, 0); -} - -void DrawOTag2(const uint32_t *ot) { - _sdk_validate_args_void(ot); - - SetDrawOpType(DRAWOP_TYPE_DMA); - GPU_GP1 = 0x04000002; // Enable DMA request, route to GP0 - - while (DMA_CHCR(DMA_GPU) & (1 << 24)) - __asm__ volatile(""); - - DMA_MADR(DMA_GPU) = (uint32_t) ot; - DMA_BCR(DMA_GPU) = 0; - DMA_CHCR(DMA_GPU) = 0x01000401; -} - -int DrawBuffer(const uint32_t *buf, size_t length) { - _sdk_validate_args(buf && length && (length <= 0xffff), -1); - - return EnqueueDrawOp((void *) &DrawBuffer2, (uint32_t) buf, length, 0); -} - -void DrawBuffer2(const uint32_t *buf, size_t length) { - _sdk_validate_args_void(buf && length && (length <= 0xffff)); - - SetDrawOpType(DRAWOP_TYPE_DMA); - GPU_GP1 = 0x04000002; // Enable DMA request, route to GP0 - - while (DMA_CHCR(DMA_GPU) & (1 << 24)) - __asm__ volatile(""); - - DMA_MADR(DMA_GPU) = (uint32_t) buf; - DMA_BCR(DMA_GPU) = 0x00000001 | (length << 16); - DMA_CHCR(DMA_GPU) = 0x01000201; -} - -void DrawPrim(const uint32_t *pri) { - _sdk_validate_args_void(pri); - - DrawSync(0); - DrawBuffer2(&pri[1], getlen(pri)); -} - /* Queue pause/resume API */ int IsIdleGPU(int timeout) { diff --git a/libpsn00b/psxgpu/drawing.c b/libpsn00b/psxgpu/drawing.c new file mode 100644 index 0000000..161b2f7 --- /dev/null +++ b/libpsn00b/psxgpu/drawing.c @@ -0,0 +1,148 @@ +/* + * PSn00bSDK GPU library (drawing/display list functions) + * (C) 2022-2023 spicyjpeg - MPL licensed + */ + +#include +#include +#include +#include +#include + +/* Private utilities */ + +// This function is actually referenced in env.c as well, so it can't be static. +void _send_linked_list(GPU_DrawOpType type, const uint32_t *ot) { + SetDrawOpType(type); + GPU_GP1 = 0x04000002; // Enable DMA request, route to GP0 + + while (DMA_CHCR(DMA_GPU) & (1 << 24)) + __asm__ volatile(""); + + DMA_MADR(DMA_GPU) = (uint32_t) ot; + DMA_BCR(DMA_GPU) = 0; + DMA_CHCR(DMA_GPU) = 0x01000401; +} + +static void _send_buffer( + GPU_DrawOpType type, const uint32_t *buf, size_t length +) { + SetDrawOpType(type); + GPU_GP1 = 0x04000002; // Enable DMA request, route to GP0 + + while (DMA_CHCR(DMA_GPU) & (1 << 24)) + __asm__ volatile(""); + + DMA_MADR(DMA_GPU) = (uint32_t) buf; + DMA_BCR(DMA_GPU) = 0x00000001 | (length << 16); + DMA_CHCR(DMA_GPU) = 0x01000201; +} + +/* Buffer and primitive drawing API */ + +int DrawOTag(const uint32_t *ot) { + _sdk_validate_args(ot, -1); + + return EnqueueDrawOp( + (void *) &_send_linked_list, + (uint32_t) DRAWOP_TYPE_DMA, + (uint32_t) ot, + 0 + ); +} + +int DrawOTagIRQ(const uint32_t *ot) { + _sdk_validate_args(ot, -1); + + return EnqueueDrawOp( + (void *) &_send_linked_list, + (uint32_t) DRAWOP_TYPE_GPU_IRQ, + (uint32_t) ot, + 0 + ); +} + +int DrawBuffer(const uint32_t *buf, size_t length) { + _sdk_validate_args(buf && length && (length <= 0xffff), -1); + + return EnqueueDrawOp( + (void *) &DrawBuffer2, + (uint32_t) DRAWOP_TYPE_DMA, + (uint32_t) buf, + (uint32_t) length + ); +} + +int DrawBufferIRQ(const uint32_t *buf, size_t length) { + _sdk_validate_args(buf && length && (length <= 0xffff), -1); + + return EnqueueDrawOp( + (void *) &DrawBuffer2, + (uint32_t) DRAWOP_TYPE_GPU_IRQ, + (uint32_t) buf, + (uint32_t) length + ); +} + +void DrawOTag2(const uint32_t *ot) { + _sdk_validate_args_void(ot); + + _send_linked_list(DRAWOP_TYPE_DMA, ot); +} + +void DrawOTagIRQ2(const uint32_t *ot) { + _sdk_validate_args_void(ot); + + _send_linked_list(DRAWOP_TYPE_GPU_IRQ, ot); +} + +void DrawBuffer2(const uint32_t *buf, size_t length) { + _sdk_validate_args_void(buf && length && (length <= 0xffff)); + + _send_buffer(DRAWOP_TYPE_DMA, buf, length); +} + +void DrawBufferIRQ2(const uint32_t *buf, size_t length) { + _sdk_validate_args_void(buf && length && (length <= 0xffff)); + + _send_buffer(DRAWOP_TYPE_GPU_IRQ, buf, length); +} + +void DrawPrim(const uint32_t *pri) { + _sdk_validate_args_void(pri); + + DrawSync(0); + DrawBuffer2(&pri[1], getlen(pri)); +} + +/* Helper functions */ + +void ClearOTagR(uint32_t *ot, size_t length) { + _sdk_validate_args_void(ot && length); + + DMA_MADR(DMA_OTC) = (uint32_t) &ot[length - 1]; + DMA_BCR(DMA_OTC) = length & 0xffff; + DMA_CHCR(DMA_OTC) = 0x11000002; + + while (DMA_CHCR(DMA_OTC) & (1 << 24)) + __asm__ volatile(""); +} + +void ClearOTag(uint32_t *ot, size_t length) { + _sdk_validate_args_void(ot && length); + + // DMA6 only supports writing to RAM in reverse order (last to first), so + // the OT has to be cleared in software here. This function is thus much + // slower than ClearOTagR(). + // https://problemkaputt.de/psx-spx.htm#dmachannels + for (int i = 0; i < (length - 1); i++) + ot[i] = (uint32_t) &ot[i + 1] & 0x7fffff; + + ot[length - 1] = 0xffffff; +} + +void AddPrim(uint32_t *ot, const void *pri) { + _sdk_validate_args_void(ot && pri); + + addPrim(ot, pri); +} diff --git a/libpsn00b/psxgpu/env.c b/libpsn00b/psxgpu/env.c index c9e6142..236ae4b 100644 --- a/libpsn00b/psxgpu/env.c +++ b/libpsn00b/psxgpu/env.c @@ -34,37 +34,7 @@ static inline uint32_t _get_window_mask(int size) { return mask & 0x1f; } -/* Drawing API */ - -DRAWENV *SetDefDrawEnv(DRAWENV *env, int x, int y, int w, int h) { - _sdk_validate_args(env && (w > 0) && (h > 0), 0); - - env->clip.x = x; - env->clip.y = y; - env->clip.w = w; - env->clip.h = h; - - env->ofs[0] = 0; - env->ofs[1] = 0; - - env->tw.x = 0; - env->tw.y = 0; - env->tw.w = 256; - env->tw.h = 256; - - env->tpage = 0x0a; - env->dtd = 1; - env->dfe = 0; - env->isbg = 0; - setRGB0(env, 0, 0, 0); - - env->dr_env.tag = 0; - return env; -} - -int DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { - _sdk_validate_args(ot && env, -1); - +static const uint32_t *_build_drawenv_ot(const uint32_t *ot, DRAWENV *env) { // All commands are grouped into a single display list packet for // performance reasons using tagless primitives (the GPU does not care // about the grouping as the display list is parsed by the CPU). @@ -101,7 +71,59 @@ int DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { setWH(fill, env->clip.w, _min(env->clip.h, 0x1ff)); } - return EnqueueDrawOp((void *) &DrawOTag2, (uint32_t) prim, 0, 0); + return (const uint32_t *) prim; +} + +/* Drawing API */ + +void _send_linked_list(GPU_DrawOpType type, const uint32_t *ot); + +DRAWENV *SetDefDrawEnv(DRAWENV *env, int x, int y, int w, int h) { + _sdk_validate_args(env && (w > 0) && (h > 0), 0); + + env->clip.x = x; + env->clip.y = y; + env->clip.w = w; + env->clip.h = h; + + env->ofs[0] = 0; + env->ofs[1] = 0; + + env->tw.x = 0; + env->tw.y = 0; + env->tw.w = 256; + env->tw.h = 256; + + env->tpage = 0x0a; + env->dtd = 1; + env->dfe = 0; + env->isbg = 0; + setRGB0(env, 0, 0, 0); + + env->dr_env.tag = 0; + return env; +} + +int DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { + _sdk_validate_args(ot && env, -1); + + return EnqueueDrawOp( + (void *) &_send_linked_list, + (uint32_t) DRAWOP_TYPE_DMA, + (uint32_t) _build_drawenv_ot(ot, env), + 0 + ); +} + +int DrawOTagEnvIRQ(const uint32_t *ot, DRAWENV *env) { + _sdk_validate_args(ot && env, -1); + + return EnqueueDrawOp( + (void *) &_send_linked_list, + (uint32_t) DRAWOP_TYPE_GPU_IRQ, + (uint32_t) _build_drawenv_ot(ot, env), + 0 + ); } void PutDrawEnv(DRAWENV *env) { diff --git a/libpsn00b/psxgpu/image.c b/libpsn00b/psxgpu/image.c index a08e293..e02c3c2 100644 --- a/libpsn00b/psxgpu/image.c +++ b/libpsn00b/psxgpu/image.c @@ -104,7 +104,17 @@ int StoreImage(const RECT *rect, uint32_t *data) { int MoveImage(const RECT *rect, int x, int y) { _sdk_validate_args(rect, -1); - return EnqueueDrawOp((void *) &MoveImage2, (uint32_t) rect, x, y); + int index = _next_saved_rect; + + _saved_rects[index] = *rect; + _next_saved_rect = (index + 1) % QUEUE_LENGTH; + + return EnqueueDrawOp( + (void *) &MoveImage2, + (uint32_t) &_saved_rects[index], + (uint32_t) x, + (uint32_t) y + ); } void LoadImage2(const RECT *rect, const uint32_t *data) { -- cgit v1.2.3