aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/psxetc/interrupts.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpsn00b/psxetc/interrupts.c')
-rw-r--r--libpsn00b/psxetc/interrupts.c62
1 files changed, 32 insertions, 30 deletions
diff --git a/libpsn00b/psxetc/interrupts.c b/libpsn00b/psxetc/interrupts.c
index 859209a..cc9d12c 100644
--- a/libpsn00b/psxetc/interrupts.c
+++ b/libpsn00b/psxetc/interrupts.c
@@ -53,44 +53,50 @@ static const struct JMP_BUF _isr_jmp_buf = {
/* Internal IRQ and DMA handlers */
static void _global_isr(void) {
- uint16_t stat = IRQ_STAT, mask = IRQ_MASK;
+ uint16_t stat = IRQ_STAT & IRQ_MASK;
- // Clear all IRQ flags in one shot. This is not the "proper" way to do it
- // but it's much faster than clearing one flag at a time.
- IRQ_STAT = ~mask;
+ for (; stat; stat = IRQ_STAT & IRQ_MASK) {
+ //for (int i = 0; i < NUM_IRQ_CHANNELS; i++) {
+ for (int i = 0, mask = 1; stat; i++, stat >>= 1, mask <<= 1) {
+ if (!(stat & 1))
+ continue;
- //for (int i = 0; i < NUM_IRQ_CHANNELS; i++) {
- for (int i = 0; stat; i++, stat >>= 1) {
- if (!(stat & 1))
- continue;
+ // Acknowledge the current IRQ. Note that clearing all IRQ flags in one
+ // shot would result in hard-to-debug race conditions (been there, done
+ // that).
+ IRQ_STAT = (uint16_t) (mask ^ 0xffff);
- if (_irq_handlers[i])
- _irq_handlers[i]();
+ if (_irq_handlers[i])
+ _irq_handlers[i]();
+ }
}
ReturnFromException();
}
static void _global_dma_handler(void) {
- uint32_t stat = DMA_DICR;
+ uint32_t dicr = DMA_DICR;
+ uint32_t stat = (dicr >> 24) & 0x7f;
+
+ for (; stat; dicr = DMA_DICR, stat = (dicr >> 24) & 0x7f) {
+ uint32_t base = dicr & 0x00ffffff;
- // Clear all DMA IRQ flags in one shot (note that flags are cleared by
- // writing 1 to them rather than 0).
- stat &= 0x7fff0000;
- DMA_DICR = stat;
- stat >>= 24;
+ //for (int i = 0; i < NUM_DMA_CHANNELS; i++) {
+ for (int i = 0, mask = (1 << 24); stat; i++, stat >>= 1, mask <<= 1) {
+ if (!(stat & 1))
+ continue;
- //for (int i = 0; i < NUM_DMA_CHANNELS; i++) {
- for (int i = 0; stat; i++, stat >>= 1) {
- if (!(stat & 1))
- continue;
+ // Acknowledge the current DMA channel's IRQ. For whatever reason
+ // DMA IRQ flags are cleared by writing 1 to them rather than 0.
+ DMA_DICR = base | mask;
- if (_dma_handlers[i])
- _dma_handlers[i]();
+ if (_dma_handlers[i])
+ _dma_handlers[i]();
+ }
}
}
-/* Callback registration API */
+/* IRQ and DMA handler API */
void *InterruptCallback(int irq, void (*func)(void)) {
if ((irq < 0) || (irq >= NUM_IRQ_CHANNELS))
@@ -127,12 +133,12 @@ void *DMACallback(int dma, void (*func)(void)) {
// the callback is being registered or removed. The main DMA IRQ dispatcher
// is also registered if this is the first DMA callback being configured,
// or disabled if it's the last one being removed.
- if (func) {
+ if (!old_callback && func) {
DMA_DICR |= (0x10000 << dma) | (1 << 23);
if (!(_num_dma_handlers++))
InterruptCallback(3, &_global_dma_handler);
- } else {
+ } else if (old_callback && !func) {
if (--_num_dma_handlers) {
DMA_DICR &= ~(0x10000 << dma);
} else {
@@ -158,7 +164,7 @@ int ResetCallback(void) {
return -1;
EnterCriticalSection();
- _saved_irq_mask = 1 << 3; // Enable DMA IRQ by default
+ _saved_irq_mask = 0;
_saved_dma_dpcr = 0x03333333;
_saved_dma_dicr = 0;
@@ -167,10 +173,6 @@ int ResetCallback(void) {
for (int i = 0; i < NUM_DMA_CHANNELS; i++)
_dma_handlers[i] = (void *) 0;
- // Set up the DMA IRQ handler. This handler shall *not* be overridden using
- // InterruptCallback().
- _irq_handlers[3] = &_global_dma_handler;
-
_96_remove();
RestartCallback();
return 0;