aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/psxgpu
diff options
context:
space:
mode:
authorspicyjpeg <thatspicyjpeg@gmail.com>2023-05-11 23:42:43 +0200
committerGitHub <noreply@github.com>2023-05-11 23:42:43 +0200
commit04d7728350cbd04dd86cd894e906c98673e3f9a7 (patch)
tree08e8c7dd495d1c4c6fcf5f7ba6b4b10693dc42f6 /libpsn00b/psxgpu
parenteaec942f56ceec9c14de5c4185a02602abadd50a (diff)
parent58a8306d24fe29d965aa8b40ddc37c3163c0a2f9 (diff)
downloadpsn00bsdk-04d7728350cbd04dd86cd894e906c98673e3f9a7.tar.gz
Merge pull request #70 from Lameguy64/v0.23-wip
Header cleanups, PCDRV, more safety checks, libc and mkpsxiso fixes (v0.23)
Diffstat (limited to 'libpsn00b/psxgpu')
-rw-r--r--libpsn00b/psxgpu/common.c199
-rw-r--r--libpsn00b/psxgpu/drawing.c148
-rw-r--r--libpsn00b/psxgpu/env.c154
-rw-r--r--libpsn00b/psxgpu/font.c14
-rw-r--r--libpsn00b/psxgpu/image.c64
5 files changed, 391 insertions, 188 deletions
diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c
index e41bd31..7e0758b 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 <stdint.h>
@@ -10,9 +10,8 @@
#include <psxgpu.h>
#include <hwregs_c.h>
-#define QUEUE_LENGTH 16
-#define DMA_CHUNK_LENGTH 8
-#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;
-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,16 +44,16 @@ static void _vblank_handler(void) {
_vsync_callback();
}
-static void _gpu_dma_handler(void) {
- //while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(2) & (1 << 24)))
- while (!(GPU_GP1 & (1 << 26)))
- __asm__ volatile("");
+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
@@ -62,16 +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_irq_handler);
DMACallback(DMA_GPU, &_gpu_dma_handler);
_gpu_video_mode = (GPU_GP1 >> 20) & 1;
@@ -80,28 +99,27 @@ void ResetGraph(int mode) {
_sdk_log("setup done, default mode is %s\n", _gpu_video_mode ? "PAL" : "NTSC");
}
- if (mode == 3) {
+ if (mode) {
GPU_GP1 = 0x01000000; // Reset command buffer
- return;
- }
-
- DMA_DPCR |= 0x0b000b00; // Enable DMA2 and DMA6
- DMA_CHCR(2) = 0x00000201; // Stop DMA2
- DMA_CHCR(6) = 0x00000200; // Stop DMA6
+ GPU_GP1 = 0x02000000; // Reset IRQ
+ GPU_GP1 = 0x04000000; // Disable DMA request
- if (mode == 1) {
- GPU_GP1 = 0x01000000; // Reset command buffer
- return;
+ if (mode == 1)
+ return;
+ } else {
+ GPU_GP1 = 0x00000000; // Reset GPU
}
- GPU_GP1 = 0x00000000; // Reset GPU
+ SetDMAPriority(DMA_GPU, 3);
+ SetDMAPriority(DMA_OTC, 3);
+ DMA_CHCR(DMA_GPU) = 0x00000201; // Stop DMA
+ DMA_CHCR(DMA_OTC) = 0x00000200; // Stop DMA
+
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;
}
@@ -127,10 +145,13 @@ int VSync(int mode) {
if (mode < 0)
return _vblank_counter;
- uint32_t status = GPU_GP1;
+ // 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);
- // Wait for at least one vertical blank event to occur.
- do {
+ while (_vblank_counter < target) {
+ uint32_t status = GPU_GP1;
_vsync_halt_func();
// If interlaced mode is enabled, wait until the GPU starts displaying
@@ -139,9 +160,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;
}
@@ -167,14 +190,13 @@ 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
// executing it immediately. Note that interrupts must be disabled *prior*
// to checking if DMA is busy; disabling them afterwards would create a
@@ -202,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;
@@ -225,7 +247,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("");
}
@@ -248,88 +270,17 @@ void *DrawSyncCallback(void (*func)(void)) {
return old_callback;
}
-/* 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;
+/* Queue pause/resume API */
- while (DMA_CHCR(6) & (1 << 24))
- __asm__ volatile("");
-}
+int IsIdleGPU(int timeout) {
+ if (timeout <= 0)
+ timeout = 1;
-void ClearOTag(uint32_t *ot, size_t 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] & 0x00ffffff;
-
- ot[length - 1] = 0x00ffffff;
-}
-
-void AddPrim(uint32_t *ot, const void *pri) {
- addPrim(ot, pri);
-}
-
-void DrawPrim(const uint32_t *pri) {
- size_t length = getlen(pri);
-
- DrawSync(0);
- GPU_GP1 = 0x04000002;
-
- // 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];
- if (length < DMA_CHUNK_LENGTH)
- DMA_BCR(2) = 0x00010000 | length;
- else
- DMA_BCR(2) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16);
-
- DMA_CHCR(2) = 0x01000201;
-}
-
-int DrawOTag(const uint32_t *ot) {
- return EnqueueDrawOp((void *) &DrawOTag2, (uint32_t) ot, 0, 0);
-}
-
-void DrawOTag2(const uint32_t *ot) {
- GPU_GP1 = 0x04000002;
-
- while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(2) & (1 << 24)))
- __asm__ volatile("");
-
- DMA_MADR(2) = (uint32_t) ot;
- DMA_BCR(2) = 0;
- DMA_CHCR(2) = 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);
-}
+ for (; timeout; timeout--) {
+ if (GPU_GP1 & (1 << 26))
+ return 0;
+ }
-void SetDispMask(int mask) {
- GPU_GP1 = 0x03000000 | (mask ? 0 : 1);
+ //_sdk_log("IsIdleGPU() timeout\n");
+ return -1;
}
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 <stdint.h>
+#include <assert.h>
+#include <psxetc.h>
+#include <psxgpu.h>
+#include <hwregs_c.h>
+
+/* 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 8784947..236ae4b 100644
--- a/libpsn00b/psxgpu/env.c
+++ b/libpsn00b/psxgpu/env.c
@@ -1,9 +1,10 @@
/*
* PSn00bSDK GPU library (DRAWENV/DISPENV functions)
- * (C) 2022 spicyjpeg - MPL licensed
+ * (C) 2022-2023 spicyjpeg - MPL licensed
*/
#include <stdint.h>
+#include <assert.h>
#include <psxgpu.h>
#include <hwregs_c.h>
@@ -33,9 +34,53 @@ static inline uint32_t _get_window_mask(int size) {
return mask & 0x1f;
}
+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).
+ DR_ENV *prim = &(env->dr_env);
+ setaddr(prim, ot);
+ setlen(prim, 5);
+
+ // Texture page (reset active page and set dither/mask bits)
+ setDrawTPage_T(&(prim->tpage), env->dfe & 1, env->dtd & 1, env->tpage);
+
+ // Texture window
+ //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...
+ 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 (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;
@@ -60,69 +105,41 @@ 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).
- 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;
+ _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
+ );
+}
- // 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;
+int DrawOTagEnvIRQ(const uint32_t *ot, DRAWENV *env) {
+ _sdk_validate_args(ot && env, -1);
- if (env->isbg) {
- 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;
- }
-
- return EnqueueDrawOp((void *) &DrawOTag2, (uint32_t) prim, 0, 0);
+ return EnqueueDrawOp(
+ (void *) &_send_linked_list,
+ (uint32_t) DRAWOP_TYPE_GPU_IRQ,
+ (uint32_t) _build_drawenv_ot(ot, env),
+ 0
+ );
}
void PutDrawEnv(DRAWENV *env) {
- DrawOTagEnv((const uint32_t *) 0x00ffffff, env);
+ _sdk_validate_args_void(env);
+
+ DrawOTagEnv((const uint32_t *) 0xffffff, env);
}
// This function skips rebuilding the cached packet whenever possible and is
// 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);
+ DrawOTagEnv((const uint32_t *) 0xffffff, env);
else
DrawOTag((const uint32_t *) &(env->dr_env));
}
@@ -130,6 +147,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;
@@ -148,6 +167,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;
@@ -216,6 +237,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;
@@ -233,3 +256,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/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 <stdint.h>
+#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
@@ -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 fc018a4..e02c3c2 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
@@ -9,11 +9,12 @@
#include <stdint.h>
#include <assert.h>
+#include <psxetc.h>
#include <psxgpu.h>
#include <hwregs_c.h>
#define QUEUE_LENGTH 16
-#define DMA_CHUNK_LENGTH 8
+#define DMA_CHUNK_LENGTH 16
/* Internal globals */
@@ -37,6 +38,10 @@ static void _dma_transfer(const RECT *rect, uint32_t *data, int write) {
length += DMA_CHUNK_LENGTH - 1;
}
+ while (!(GPU_GP1 & (1 << 26)))
+ __asm__ volatile("");
+
+ SetDrawOpType(DRAWOP_TYPE_DMA);
GPU_GP1 = 0x04000000; // Disable DMA request
GPU_GP0 = 0x01000000; // Flush cache
@@ -49,18 +54,24 @@ 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;
+ while ((DMA_CHCR(DMA_GPU) & (1 << 24)) || !(GPU_GP1 & (1 << 28)))
+ __asm__ volatile("");
+
+ 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 */
int LoadImage(const RECT *rect, const uint32_t *data) {
+ _sdk_validate_args(rect && data, -1);
+
int index = _next_saved_rect;
_saved_rects[index] = *rect;
@@ -75,6 +86,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;
@@ -88,22 +101,53 @@ int StoreImage(const RECT *rect, uint32_t *data) {
);
}
+int MoveImage(const RECT *rect, int x, int y) {
+ _sdk_validate_args(rect, -1);
+
+ 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) {
+ _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) {
+void MoveImage2(const RECT *rect, int x, int y) {
+ _sdk_validate_args_void(rect);
+
+ 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));
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 */
@@ -112,6 +156,8 @@ void StoreImage2(const RECT *rect, uint32_t *data) {
// 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;
@@ -138,6 +184,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;