global long_mode_start
global asm_printchar
extern rust_interrupt_handler
extern rust_handle_keyboard
extern rust_handle_timer

section .text
bits 64

long_mode_start:
    ; print "OKAY"
    call setup_SSE
    call setup_IDT
    extern rust_setup_PIT
    call rust_setup_PIT
    ;call setup_PIT
    extern rust_setup_PIC
    call rust_setup_PIC
    extern rust_main
    call rust_main
    ; rust main returned, print `OS returned!`
    mov rax, 0x4f724f204f534f4f
    mov [0xb8000], rax
    mov rax, 0x4f724f754f744f65
    mov [0xb8008], rax
    mov rax, 0x4f214f644f654f6e
    mov [0xb8010], rax
    hlt

spin_loop_64:
    mov rcx, 0xffff_ff
    .loop:
    dec rcx
    cmp rcx, 0
    jne spin_loop_64.loop
    ret

error:
    mov rbx, 0x4f4f4f524f524f45
    mov [0xb8000], rbx
    mov rbx, 0x4f204f204f3a4f52
    mov [0xb8008], rbx
    mov byte [0xb800e], al
    hlt
    jmp error


setup_SSE:
    mov rax, 0x1
    cpuid
    test edx, 1 << 25
    jz .no_SSE

    ;enable SSE
    mov rax, cr0
    and ax, 0xfffb
    or ax, 0x2
    mov cr0, rax
    mov rax, cr4
    or ax, 3 << 9
    mov cr4, rax
    ret
    .no_SSE:
    mov al, "a"
    jmp error


; ----------- IDT section ---------------------

%define PIC1_COMMAND 0x20
%define PIC1_DATA 0x21
%define PIC2_COMMAND 0xa0
%define PIC2_DATA 0xa1

%define END_OF_INTERRUPT 0x20

%macro interrupt_handler_preamble 0
    push rax
    push rcx
    push rdx
    push r8
    push r9
    push r10
    push r11
    pushfq
%endmacro

%macro interrupt_handler_final 0
    popfq
    pop r11
    pop r10
    pop r9
    pop r8
    pop rdx
    pop rcx
    pop rax
    iretq
%endmacro

handle_keyboard:
    interrupt_handler_preamble

    in al, 0x60
    shl al, 8
    in al, 0x61
    push ax
    cld
    call rust_handle_keyboard
    pop ax

    mov al, END_OF_INTERRUPT
    out PIC1_COMMAND, al
    out PIC2_COMMAND, al

    interrupt_handler_final


handle_timer:
    interrupt_handler_preamble

    call rust_handle_timer

    mov al, END_OF_INTERRUPT
    out PIC1_COMMAND, al
    out PIC2_COMMAND, al

    interrupt_handler_final

handle_double_fault:
    interrupt_handler_preamble
    cli
    hlt
    interrupt_handler_final

;expects name of label, offset in table
%macro load_IDT_entry 2
%define IDT_SIZE 16
    mov rax, %1
    mov [idt64 + %2*IDT_SIZE], ax
    mov word [idt64 + %2*IDT_SIZE + 2], cs
    mov word [idt64 + %2*IDT_SIZE + 4], 0x8e00
    shr rax, 16
    mov [idt64 + %2*IDT_SIZE + 6], ax
    shr rax, 16
    mov [idt64 + %2*IDT_SIZE + 8], eax
%endmacro

setup_IDT:
    lidt [idt64.pointer]

    load_IDT_entry handle_double_fault, 8
    load_IDT_entry handle_timer, 32
    load_IDT_entry handle_keyboard, 33

    ret

idt64:
    resb 50*16
.pointer:
    dw .pointer - idt64
    dq idt64

; --------- end of IDT section --------
