.altmacro

.global __x86_64_apic_vectors

.set IRQ_REG_RAX, 0 * 8
.set IRQ_REG_RCX, 1 * 8
.set IRQ_REG_RDX, 2 * 8
.set IRQ_REG_RBX, 3 * 8
.set IRQ_REG_RSI, 4 * 8
.set IRQ_REG_RDI, 5 * 8
.set IRQ_REG_RBP, 6 * 8
.set IRQ_REG_R8, 7 * 8
.set IRQ_REG_R9, 8 * 8
.set IRQ_REG_R10, 9 * 8
.set IRQ_REG_R11, 10 * 8
.set IRQ_REG_R12, 11 * 8
.set IRQ_REG_R13, 12 * 8
.set IRQ_REG_R14, 13 * 8
.set IRQ_REG_R15, 14 * 8

// 15 registers + stack align word if needed
.set IRQ_STATE_SIZE, 15 * 8

.macro IRQ_SWAPGS_IF_NEEDED, cs_off
    cmpq $0x08, \cs_off(%rsp)
    je 1f
    swapgs
1:
.endm

.macro IRQ_SAVE_STATE
    // Save state
    subq $IRQ_STATE_SIZE, %rsp

    movq %rax, IRQ_REG_RAX(%rsp)
    movq %rcx, IRQ_REG_RCX(%rsp)
    movq %rdx, IRQ_REG_RDX(%rsp)
    movq %rbx, IRQ_REG_RBX(%rsp)
    movq %rsi, IRQ_REG_RSI(%rsp)
    movq %rdi, IRQ_REG_RDI(%rsp)
    movq %rbp, IRQ_REG_RBP(%rsp)
    movq %r8,  IRQ_REG_R8(%rsp)
    movq %r9,  IRQ_REG_R9(%rsp)
    movq %r10, IRQ_REG_R10(%rsp)
    movq %r11, IRQ_REG_R11(%rsp)
    movq %r12, IRQ_REG_R12(%rsp)
    movq %r13, IRQ_REG_R13(%rsp)
    movq %r14, IRQ_REG_R14(%rsp)
    movq %r15, IRQ_REG_R15(%rsp)

    // Save current stack into %rbp
    movq %rsp, %rbp

    // Force correct stack alignment
    orq $0xF, %rsp
    xorq $0xF, %rsp
.endm

.macro IRQ_RESTORE_STATE
    // Restore the stack pointer
    movq %rbp, %rsp

    // Restore state
    movq IRQ_REG_RAX(%rsp), %rax
    movq IRQ_REG_RCX(%rsp), %rcx
    movq IRQ_REG_RDX(%rsp), %rdx
    movq IRQ_REG_RBX(%rsp), %rbx
    movq IRQ_REG_RSI(%rsp), %rsi
    movq IRQ_REG_RDI(%rsp), %rdi
    movq IRQ_REG_RBP(%rsp), %rbp
    movq IRQ_REG_R8(%rsp),  %r8
    movq IRQ_REG_R9(%rsp),  %r9
    movq IRQ_REG_R10(%rsp), %r10
    movq IRQ_REG_R11(%rsp), %r11
    movq IRQ_REG_R12(%rsp), %r12
    movq IRQ_REG_R13(%rsp), %r13
    movq IRQ_REG_R14(%rsp), %r14
    movq IRQ_REG_R15(%rsp), %r15

    addq $IRQ_STATE_SIZE, %rsp
.endm

.macro IRQ_VECTOR, n
irq_vector_\n:
    // %rsp + 0: %rip
    // %rsp + 8: %cs
    IRQ_SWAPGS_IF_NEEDED 8
    IRQ_SAVE_STATE

    // Force correct segment registers
    mov $0x10, %ax
    mov %ax, %ss
    mov %ax, %ds
    mov %ax, %es

    mov $\n, %rdi
    mov %rbp, %rsi
    call {irq_handler}

    IRQ_RESTORE_STATE
    IRQ_SWAPGS_IF_NEEDED 8

    iretq
.endm

.macro IRQ_VECTOR_ENTRY, n
.quad irq_vector_\n
.endm

.macro IRQ_VECTORS, start, end
.set i, 0
.rept \end - \start
    IRQ_VECTOR %i
    .set i, i+1
.endr
.endm

.macro IRQ_VECTOR_ENTRIES, start, end
.set i, 0
.rept \end - \start
    IRQ_VECTOR_ENTRY %i
    .set i, i+1
.endr
.endm

.macro MSI_VECTOR, n
msi_vector_\n:
    // %rsp + 0: %rip
    // %rsp + 8: %cs
    IRQ_SWAPGS_IF_NEEDED 8
    IRQ_SAVE_STATE

    // Force correct segment registers
    mov $0x10, %ax
    mov %ax, %ss
    mov %ax, %ds
    mov %ax, %es

    mov $\n, %rdi
    mov %rbp, %rsi
    call {msi_handler}

    IRQ_RESTORE_STATE
    IRQ_SWAPGS_IF_NEEDED 8

    iretq
.endm

.macro MSI_VECTOR_ENTRY, n
.quad msi_vector_\n
.endm

.macro MSI_VECTORS, start, end
.set i, 0
.rept \end - \start
    MSI_VECTOR %i
    .set i, i+1
.endr
.endm

.macro MSI_VECTOR_ENTRIES, start, end
.set i, 0
.rept \end - \start
    MSI_VECTOR_ENTRY %i
    .set i, i+1
.endr
.endm

.macro FILL_EMPTY_SPACE, start, end
.set i, 0
.rept \end - \start
.quad dummy_vector
.endr
.endm

.section .text
local_timer_vector:
    IRQ_SWAPGS_IF_NEEDED 8
    IRQ_SAVE_STATE

    mov %rbp, %rdi
    call {local_timer_irq_handler}

    IRQ_RESTORE_STATE
    IRQ_SWAPGS_IF_NEEDED 8
    iretq

ipi_vector:
    IRQ_SWAPGS_IF_NEEDED 8
    IRQ_SAVE_STATE

    call {ipi_handler}
    jmp .

dummy_vector:
    IRQ_SWAPGS_IF_NEEDED 8
    IRQ_SAVE_STATE

    call {dummy_irq_handler}
    jmp .

IRQ_VECTORS {irq_vector_offset}, {irq_vector_offset} + {irq_vector_count}
MSI_VECTORS {msi_vector_offset}, {msi_vector_offset} + {msi_vector_count}

.pushsection .data.rel
// 224 vectors: 256 - 32 (exceptions)
.p2align 4
.type __x86_64_apic_vectors, @object
__x86_64_apic_vectors:
    // Local timer IRQ: 0
    .quad local_timer_vector
    // Dummy entries (currently): 1..=2
    .quad dummy_vector
    .quad dummy_vector
    // IPI vector: 3
    .quad ipi_vector
    // Regular IRQ vectors: 4..207
    IRQ_VECTOR_ENTRIES {irq_vector_offset}, {irq_vector_offset} + {irq_vector_count}
    // MSI vectors: 207..223
    MSI_VECTOR_ENTRIES {msi_vector_offset}, {msi_vector_offset} + {msi_vector_count}
    // Spurious interrupt vector: 223
    .quad dummy_vector
.size __x86_64_apic_vectors, . - __x86_64_apic_vectors
.popsection // .data.rel
