//! 中断服务例程
//!
//! # 中断处理总流程
//!
//! * 中断发生时，CPU首先完成以下工作：
//!     * 设置rsp的值（Ring3->0 或 DoubleFault，在TSS中设置）
//!     * 向栈中push一些值（Ring0时3项，Ring3时5项，某些中断还有错误码）
//!     * 跳转到`vector{i}`符号（位于`vector.asm`）
//! * 进入中断服务例程后，完成以下工作：
//!     * `vector{i}`向栈中push空错误码（如CPU没有做）和中断号
//!     * 跳转到`__alltraps`（位于`trap.asm`）
//!     * `__alltraps`向栈中push全部寄存器的值
//!     * 调用`rust_trap`，并把此时的rsp传过来，也就是`TrapFrame`的指针
//! * `rust_trap`完成以下工作：
//!     * 根据tf中的中断号，再次分发到具体的中断处理函数。
//!       一些函数可能会调用switch()，切换到其它线程执行，在某一时刻再切换回来。
//!     * 如果需要，执行进程调度
//! * `__alltraps`完成以下工作：
//!     * 检查tf中的cs段，如果是用户态，向TSS中设置下一次中断时重置rsp的值，使其指向该用户线程的内核栈
//!     * 从栈中pop全部寄存器的值，pop中断号和错误码
//!     * 执行iret，CPU从栈中pop一些值（若其中的CS是Ring0，3项，否则5项），重置rip和rsp（Ring3时）
//!
//! ``` mermaid
//! sequenceDiagram
//!	activate CPU
//!	Note right of CPU: set rsp (user)
//!	Note right of CPU: push something
//!	deactivate CPU
//!
//!	CPU ->> +ASM: jmp vector_i
//!	Note right of ASM: push error_code & trap_num
//!
//!	ASM ->> ASM: jmp alltraps
//!	Note right of ASM: push registers
//!
//!	ASM ->> +Rust: call rust_trap(rsp)
//!    Note right of Rust: handle the trap
//!
//!	opt schedule
//!	Rust ->> +NewThread: switch
//!	Note left of NewThread: forkret:
//!    NewThread -->> -ASM: ret
//!    Note left of ASM: trapret:
//!    ASM -->> +NewThread: from another trap
//!    NewThread ->> -Rust: switch
//!	end
//!
//!	Rust -->> -ASM: ret
//!    Note left of ASM: trapret:
//!
//!    opt CS is user mode
//!	ASM ->> +Rust: call set_return_rsp
//!	Note right of Rust: set in TSS
//!	Rust -->> -ASM: ret
//!	end
//!
//!	Note right of ASM: pop registers
//!	Note right of ASM: pop error_code & trap_num
//!	ASM -->> -CPU: iret
//!
//!	activate CPU
//!	Note right of CPU: pop something
//!	Note right of CPU: set rsp (user)
//!	deactivate CPU
//! ```

use super::consts::*;
use super::TrapFrame;
use log::*;
use bitflags::*;
use crate::drivers::DRIVERS;

global_asm!(include_str!("trap.asm"));
global_asm!(include_str!("vector.asm"));

#[allow(non_upper_case_globals)]
#[no_mangle]
pub extern fn rust_trap(tf: &mut TrapFrame) {
    trace!("Interrupt: {:#x} @ CPU{}", tf.trap_num, super::super::cpu::id());
    // Dispatch
    match tf.trap_num as u8 {
        Breakpoint => breakpoint(),
        DoubleFault => double_fault(tf),
        PageFault => page_fault(tf),
        IRQ0...63 => {
            let irq = tf.trap_num as u8 - IRQ0;
            super::ack(irq); // must ack before switching
            match irq {
                Timer => crate::trap::timer(),
                Keyboard => keyboard(),
                COM1 => com1(),
                COM2 => com2(),
                IDE => ide(),
                _ => {
                    for driver in DRIVERS.read().iter() {
                        if driver.try_handle_interrupt(Some(irq.into())) == true {
                            debug!("driver processed interrupt");
                            return;
                        }
                    }
                    warn!("unhandled external IRQ number: {}", irq);
                },
            }
        }
        Syscall32 => syscall32(tf),
        InvalidOpcode => invalid_opcode(tf),
        DivideError | GeneralProtectionFault => error(tf),
        _ => panic!("Unhandled interrupt {:x}", tf.trap_num),
    }
}

fn breakpoint() {
    error!("\nEXCEPTION: Breakpoint");
}

fn double_fault(tf: &TrapFrame) {
    error!("\nEXCEPTION: Double Fault\n{:#x?}", tf);
    loop {}
}

fn page_fault(tf: &mut TrapFrame) {
    let addr: usize;
    unsafe { asm!("mov %cr2, $0" : "=r" (addr)); }

    bitflags! {
        struct PageError: u8 {
            const PRESENT = 1 << 0;
            const WRITE = 1 << 1;
            const USER = 1 << 2;
            const RESERVED_WRITE = 1 << 3;
            const INST = 1 << 4;
        }
    }
    let code = PageError::from_bits(tf.error_code as u8).unwrap();

    if crate::memory::handle_page_fault(addr) {
        return;
    }
    error!("\nEXCEPTION: Page Fault @ {:#x}, code: {:?}", addr, code);
    error(tf);
}

fn keyboard() {
    use crate::arch::driver::keyboard;
    use pc_keyboard::{DecodedKey, KeyCode};
    trace!("\nInterupt: Keyboard");
    if let Some(key) = keyboard::receive() {
        match key {
            DecodedKey::Unicode(c) => crate::trap::serial(c),
            DecodedKey::RawKey(code) => {
                let s = match code {
                    KeyCode::ArrowUp => "\u{1b}[A",
                    KeyCode::ArrowDown => "\u{1b}[B",
                    KeyCode::ArrowRight => "\u{1b}[C",
                    KeyCode::ArrowLeft => "\u{1b}[D",
                    _ => "",
                };
                for c in s.chars() {
                    crate::trap::serial(c);
                }
            }
        }
    }
}

fn com1() {
    use crate::arch::driver::serial::*;
    trace!("\nInterupt: COM1");
    crate::trap::serial(COM1.lock().receive() as char);
}

fn com2() {
    use crate::arch::driver::serial::*;
    trace!("\nInterupt: COM2");
    COM2.lock().receive();
}

fn ide() {
    trace!("\nInterupt: IDE");
}

#[no_mangle]
pub extern "C" fn syscall(tf: &mut TrapFrame) {
    trace!("\nInterupt: Syscall {:#x?}", tf.rax);
    let ret = crate::syscall::syscall(tf.rax, [tf.rdi, tf.rsi, tf.rdx, tf.r10, tf.r8, tf.r9], tf);
    tf.rax = ret as usize;
}

fn syscall32(tf: &mut TrapFrame) {
    trace!("\nInterupt: Syscall {:#x?}", tf.rax);
    let ret = crate::syscall::syscall(tf.rax, [tf.rdx, tf.rcx, tf.rbx, tf.rdi, tf.rsi, 0], tf);
    tf.rax = ret as usize;
}

/// Support `syscall` instruction
fn invalid_opcode(tf: &mut TrapFrame) {
    let opcode = unsafe { (tf.rip as *mut u16).read() };
    const SYSCALL_OPCODE: u16 = 0x05_0f;
    if opcode == SYSCALL_OPCODE {
        tf.rip += 2;    // must before syscall
        syscall(tf);
    } else {
        crate::trap::error(tf);
    }
}

fn error(tf: &TrapFrame) {
    crate::trap::error(tf);
}

#[no_mangle]
pub unsafe extern fn set_return_rsp(tf: *const TrapFrame) {
    use crate::arch::gdt::Cpu;
    Cpu::current().set_ring0_rsp(tf.add(1) as usize);
}
