aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/include/psxetc.h
diff options
context:
space:
mode:
Diffstat (limited to 'libpsn00b/include/psxetc.h')
-rw-r--r--libpsn00b/include/psxetc.h196
1 files changed, 180 insertions, 16 deletions
diff --git a/libpsn00b/include/psxetc.h b/libpsn00b/include/psxetc.h
index fcfec06..ae4611e 100644
--- a/libpsn00b/include/psxetc.h
+++ b/libpsn00b/include/psxetc.h
@@ -3,20 +3,45 @@
* (C) 2019-2022 Lameguy64, spicyjpeg - MPL licensed
*/
+/**
+ * @file psxetc.h
+ * @brief Interrupt management library header
+ *
+ * @details This library provides basic facilities (such as interrupt handling)
+ * used by all other PSn00bSDK libraries, as well as some additional
+ * functionality including a dynamic linker (whose API is however defined in a
+ * separate header).
+ */
+
#ifndef __PSXETC_H
#define __PSXETC_H
-/* Macros */
+/* IRQ and DMA channel definitions */
-// This macro is used internally by PSn00bSDK to log debug messages to a buffer
-// which is then printed to stdout when calling VSync().
-#ifdef NDEBUG
-#define _sdk_log(...)
-#define _sdk_dump_log()
-#else
-#define _sdk_log(...) _sdk_log_inner(__VA_ARGS__)
-#define _sdk_dump_log() _sdk_dump_log_inner()
-#endif
+typedef enum _IRQ_Channel {
+ IRQ_VBLANK = 0,
+ IRQ_GPU = 1,
+ IRQ_CD = 2,
+ IRQ_DMA = 3,
+ IRQ_TIMER0 = 4,
+ IRQ_TIMER1 = 5,
+ IRQ_TIMER2 = 6,
+ IRQ_SIO0 = 7,
+ IRQ_SIO1 = 8,
+ IRQ_SPU = 9,
+ IRQ_GUN = 10,
+ IRQ_PIO = 10
+} IRQ_Channel;
+
+typedef enum _DMA_Channel {
+ DMA_MDEC_IN = 0,
+ DMA_MDEC_OUT = 1,
+ DMA_GPU = 2,
+ DMA_CD = 3,
+ DMA_SPU = 4,
+ DMA_PIO = 5,
+ DMA_OTC = 6
+} DMA_Channel;
/* Public API */
@@ -24,16 +49,155 @@
extern "C" {
#endif
-void _sdk_log_inner(const char *fmt, ...);
-void _sdk_dump_log_inner(void);
+/**
+ * @brief Sets a callback for an interrupt.
+ *
+ * @details Registers a function to be called whenever the specified interrupt
+ * is fired. A previously registered callback can be removed by passing a null
+ * pointer instead. The IRQ controller is automatically configured to only
+ * enable interrupts for which a callback is registered.
+ *
+ * The callback will run in the exception handler's context, so it should be as
+ * fast as possible and shall not call any function that relies on interrupts
+ * being enabled. Each interrupt is acknowledged automatically before the
+ * callback is invoked.
+ *
+ * The following interrupt channels are available (the ones already used
+ * internally by libraries shall not be overridden to avoid conflicts):
+ *
+ * | ID | Channel | Used by |
+ * | --: | :--------------- | :-------------------------------------- |
+ * | 0 | IRQ_VBLANK | psxgpu (use VSyncCallback() instead) |
+ * | 1 | IRQ_GPU | |
+ * | 2 | IRQ_CD | psxcd (use CdReadyCallback() instead) |
+ * | 3 | IRQ_DMA | psxetc (use DMACallback() instead) |
+ * | 4 | IRQ_TIMER0 | |
+ * | 5 | IRQ_TIMER1 | |
+ * | 6 | IRQ_TIMER2 | |
+ * | 7 | IRQ_SIO0 | |
+ * | 8 | IRQ_SIO1 | psxsio (use SIO_ReadCallback() instead) |
+ * | 9 | IRQ_SPU | |
+ * | 10 | IRQ_GUN, IRQ_PIO | |
+ *
+ * WARNING: even though interrupts are acknowledged automatically at the IRQ
+ * controller side, most IRQ channels (1, 2, 3, 7, 8, 9) additionally require
+ * acknowledging at the device side, which must be done by the callback. The
+ * exact way to acknowledge interrupts varies for each device, however it
+ * usually involves setting or clearing a bit in a register. See the nocash
+ * documentation for more details.
+ *
+ * @param irq
+ * @param func
+ * @return Previously set callback for the channel or NULL
+ */
+void *InterruptCallback(IRQ_Channel irq, void (*func)(void));
+
+/**
+ * @brief Gets the callback for an interrupt.
+ *
+ * @details Returns a pointer to the callback currently registered to handle
+ * the specified interrupt, or a null pointer if none is set.
+ *
+ * @param irq
+ * @return Currently set callback for the channel or NULL
+ *
+ * @see InterruptCallback()
+ */
+void *GetInterruptCallback(IRQ_Channel irq);
-void *InterruptCallback(int irq, void (*func)(void));
-void *GetInterruptCallback(int irq);
-void *DMACallback(int dma, void (*func)(void));
-void *GetDMACallback(int dma);
+/**
+ * @brief Sets a callback for a DMA interrupt.
+ *
+ * @details Registers a function to be called whenever the specified DMA
+ * channel goes from busy to idle, i.e. when a transfer is completed. A
+ * previously registered callback can be removed by passing a null pointer
+ * instead. The DMA controller is automatically configured to only enable DMA
+ * interrupts for which a callback is registered.
+ *
+ * This function uses InterruptCallback() to register a "master handler" for
+ * DMA interrupts, which then dispatches the IRQ to depending on the channel
+ * that triggered it.
+ *
+ * The callback will run in the exception handler's context, so it should be as
+ * fast as possible and shall not call any function that relies on interrupts
+ * being enabled. Each interrupt is acknowledged automatically before the
+ * callback is invoked.
+ *
+ * The following DMA channels are available (the ones already used internally
+ * by libraries shall not be overridden to avoid conflicts):
+ *
+ * | ID | Channel | Used by |
+ * | --: | :----------- | :-------------------------------------- |
+ * | 0 | DMA_MDEC_IN | |
+ * | 1 | DMA_MDEC_OUT | |
+ * | 2 | DMA_GPU | psxgpu (use DrawSyncCallback() instead) |
+ * | 3 | DMA_CD | |
+ * | 4 | DMA_SPU | |
+ * | 5 | DMA_PIO | |
+ * | 6 | DMA_OTC | |
+ *
+ * @param dma
+ * @param func
+ * @return Previously set callback for the channel or NULL
+ */
+void *DMACallback(DMA_Channel dma, void (*func)(void));
+/**
+ * @brief Gets the callback for a DMA interrupt.
+ *
+ * @details Returns a pointer to the callback currently registered to handle
+ * the specified DMA interrupt, or a null pointer if none is set.
+ *
+ * @param dma
+ * @return Currently set callback for the channel or NULL
+ *
+ * @see DMACallback()
+ */
+void *GetDMACallback(DMA_Channel dma);
+
+/**
+ * @brief Initializes the interrupt dispatcher.
+ *
+ * @details Sets up the interrupt handling system, hooks the BIOS to dispatch
+ * interrupts to the library and clears all registered callbacks. This function
+ * must be called once at the beginning of the program, prior to registering
+ * any IRQ or DMA callbacks.
+ *
+ * ResetCallback() is called by psxgpu's ResetGraph(), so invoking it manually
+ * is usually not required. Calling ResetCallback() after ResetGraph() will
+ * actually result in improper initialization, as ResetGraph() registers
+ * several callbacks used internally by psxgpu.
+ *
+ * @return 0 or -1 if the was already initialized
+ */
int ResetCallback(void);
+
+/**
+ * @brief Restores the interrupt dispatcher.
+ *
+ * @details Restores the IRQ and DMA controller state saved by StopCallback()
+ * and reinstalls BIOS hooks for interrupt dispatching. All callbacks
+ * previously set before StopCallback() was called are preserved.
+ *
+ * @see StopCallback()
+ */
void RestartCallback(void);
+
+/**
+ * @brief Temporarily disables the interrupt dispatcher.
+ *
+ * @details Saves the state of the IRQ and DMA controllers, then disables them
+ * and removes BIOS hooks. This function must be called prior to launching a
+ * new executable or DLL that calls ResetCallback() or ResetGraph(), or an
+ * executable not built with PSn00bSDK that uses its own interrupt handling
+ * subsystem (such as a retail game). The saved state can be restored after the
+ * executable returns using RestartCallback().
+ *
+ * Note that interrupts are (obviously) disabled until RestartCallback() is
+ * called.
+ *
+ * @see RestartCallback()
+ */
void StopCallback(void);
#ifdef __cplusplus