/* Copyright (C) 2021,2022 fef <owo@fef.moe>.  All rights reserved. */

#pragma once
#define _ARCH_INTERRUPT_H_

/** @brief Total number of interrupt lines on x86. */
#define X86_INTR_COUNT 256

/*
 * These are all reserved interrupt/exception vector numbers for x86 Protected
 * and Long Mode.  Taken from the Intel SDM vol 3, sec 6.3.1, tbl 6-1.
 */

/** @brief Divide Error */
#define X86_VECT_DE	0
/** @brief Debug Exception */
#define X86_VECT_DB	1
/** @brief Nonmaskable Interrupt */
#define X86_VECT_NMI	2
/** @brief Breakpoint */
#define X86_VECT_BP	3
/** @brief Overflow */
#define X86_VECT_OF	4
/** @brief BOUND Range Exceeded */
#define X86_VECT_BR	5
/** @brief Invalid Opcode (Undefined Opcode) */
#define X86_VECT_UD	6
/** @brief Device Not Available (No Math Coprocessor) */
#define X86_VECT_NM	7
/** @brief Double Fault */
#define X86_VECT_DF	8
/* 9 is reserved (Coprocessor Segment Overrun) */

/** @brief Invalid TSS */
#define X86_VECT_TS	10
/** @brief Segment Not Present */
#define X86_VECT_NP	11
/** @brief Stack-Segment Fault */
#define X86_VECT_SS	12
/** @brief General Protection */
#define X86_VECT_GP	13
/** @brief Page Fault */
#define X86_VECT_PF	14
/* 15 is reserved */

/** @brief x87 FPU Floating Point Error (Math Fault) */
#define X86_VECT_MF	16
/** @brief Alignment Check */
#define X86_VECT_AC	17
/** @brief Machine Check */
#define X86_VECT_MC	18
/** @brief SIMD Floating-Point Exception */
#define X86_VECT_XM	19
/** @brief Virtualization Exception */
#define X86_VECT_VE	20
/** @brief Control Protection Exception */
#define X86_VECT_CP	21
/* 22-31 are Intel reserved, IRQs start at 32 */

/** @brief First interrupt vector number for IRQs */
#define X86_VECT_IRQ_BASE 32
/** @brief Interrupt vector number for IRQ `n` */
#define X86_VECT_IRQ(n) (X86_VECT_IRQ_BASE + (n))

#ifndef _ASM_SOURCE

#include <gay/cdefs.h>
#include <gay/types.h>

#ifdef __x86_64__
#include <amd64/interrupt.h>
#else
#include <i386/interrupt.h>
#endif

/**
 * @brief A single entry in the Interrupt Descriptor Table as laid out in hardware.
 * Luckily, this is not quite as deranged as the GDT layout.
 */
struct x86_idt_entry {
	u16 offset0;		/**< @brief ptr to handler, bits 0:15 */
	u16 selector;		/**< @brief GDT selector, use kernel code segment */
	u8 _rsvd0;		/**< @brief always 0 */
	u8 attr;
#define X86_IDT_GATE_TASK32		0x5u
#define X86_IDT_GATE_INTR16		0x6u
#define X86_IDT_GATE_TRAP16		0x7u
#define X86_IDT_GATE_INTR32		0xeu
#define X86_IDT_GATE_TRAP32		0xfu
#define X86_IDT_STORAGE			(1u << 4) /* 0 for interrupt/trap, 1 for system gate */
#define X86_IDT_DPL(x)			(((x) & 3u) << 5)
#define X86_IDT_PRESENT			(1u << 7)
	u16 offset1;		/**< @brief ptr to handler, bits 16:31 */
#ifdef __x86_64__
	u32 offset2;
	u32 _rsvd1;		/**< @brief always zero */
#endif
} __packed;

extern struct x86_idt_entry x86_idt[X86_INTR_COUNT];

/**
 * @brief Set a gate handler in the Interrupt Descriptor Table.
 *
 * @param vector Vector number in the IDT
 * @param handler Address of the handler function
 * @param flags Flags for the IDT entry
 */
void x86_set_gate(u8 vector, void (*handler)(void), u8 flags);

/**
 * @brief Set a trap gate handler in the Interrupt Descriptor table.
 *
 * @param vector Vector number in the IDT
 * @param isr Address of the interrupt service routine
 */
void x86_set_trap_gate(u8 vector, void (*isr)(void));

void x86_set_intr_gate(u8 vector, void (*isr)(void));

/** @brief Set up the IDT and load it into IDTR. */
void x86_setup_interrupts(void);

/** @brief Internal routine performing the actual LIDT instruction. */
extern void x86_load_idt(void);

#endif /* not _ASM_SOURCE */