#include "intc.h"

// this interrupt controller is tied to the pol0 design
#include "pol0.h"

namespace intc {
namespace {

struct IntC {
    volatile uint32_t ISR;
    volatile uint32_t IPR;
    volatile uint32_t IER;
    volatile uint32_t IAR;
    volatile uint32_t SIE;
    volatile uint32_t CIE;
    volatile uint32_t IVR;
    volatile uint32_t MER;
    volatile uint32_t IMR;
    volatile uint32_t ILR;

    // the rest is not enabled
};

IntC* intc = reinterpret_cast<IntC*>(INTC_BASE);
Isr isrs[NIRQ] = {};

}  // namespace

bool SetIrqEnabled(uint8_t irqn, bool enabled) {
    uint32_t mask = 1 << irqn;
    uint32_t ier = intc->IER;
    bool was_enabled = (ier & (~mask)) > 0;
    if (enabled) {
        intc->IER = ier | mask;
    } else {
        intc->IER = ier & (~mask);
    }

    return was_enabled;
}

void SetIsr(uint8_t irqn, Isr isr) { isrs[irqn] = isr; }

void EnableInterrupts() { intc->MER = 0x3; }

void InterruptHandler() {
    uint32_t ipr = intc->IPR;

    for (int i = 0; i < NIRQ; i++) {
        uint32_t mask = 1 << i;

        if ((ipr & mask) > 0) {
            // interrupt pending
            if (isrs[i] != nullptr) {
                isrs[i]();
            }
            intc->IAR = mask;  // ack
        }
    }
}

}  // namespace intc
