#include <cstdint>

#include "bios.h"
#include "interrupts.h"

namespace {

constexpr uint32_t kMstatusMieMask = 1 << 3;
constexpr uint32_t kMieExternalInterruptMask = 1 << 11;
constexpr uint32_t kExternalInterruptCause = 0x0b;
constexpr uint32_t kInterruptCauseMask = 0xff;

Isr external_handler = nullptr;

__attribute__((interrupt))
void TrapHandler() {
    uint32_t mcause;
    uint32_t mip;
    asm volatile("csrr %0, mcause" : "=r"(mcause));
    asm volatile("csrr %0, mip" : "=r"(mip));

    // check for external interrupt
    if ((mcause & kInterruptCauseMask) == kExternalInterruptCause) {
        if (external_handler != nullptr) {
            external_handler();
        }

        mip &= ~(kMieExternalInterruptMask);
        asm volatile("csrw mip, %0" :: "r"(mip));
    } else {
        BiosWozmon();
    }
}

}  // namespace

void SetExternalInterruptHandler(Isr handler) {
    external_handler = handler;
}

void EnableExternalInterrupts() {
    uint32_t mie;
    Isr trap = TrapHandler;

    asm volatile("csrr %0, mie" : "=r"(mie));
    asm volatile("csrw mtvec, %0" :: "r"(trap));
    mie |= kMieExternalInterruptMask;
    asm volatile("csrw mie, %0" :: "r"(mie));
}

bool EnableInterrupts(bool on) {
    uint32_t mstatus;
    bool was_on;
    asm volatile("csrr %0, mstatus" : "=r"(mstatus));

    was_on = (mstatus & kMstatusMieMask) > 0;

    if (on) {
        mstatus |= kMstatusMieMask;
    } else {
        mstatus &= ~kMstatusMieMask;
    }

    asm volatile("csrw mstatus, %0" :: "r"(mstatus));
    return was_on;
}