mod context;

use core::arch::global_asm;
use riscv::register::{mtvec::TrapMode, scause::{self, Exception, Trap}, sie, stval, stvec};
use riscv::register::scause::Interrupt;

pub use context::TrapContext;
use crate::println;
use crate::syscall::syscall;
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next};
use crate::timer::set_next_trigger;



// 引入陷入保存寄存器需要的汇编代码
global_asm!(include_str!("trap.S"));

/// 初始化stvec 寄存器, 这个寄存器保存陷入时, 入口函数的地址
pub fn init() {
    extern "C" {
        fn __alltraps();
    }
    unsafe {
        stvec::write(__alltraps as usize, TrapMode::Direct);
    }
}

// 设置riscv 允许定时器中断
pub fn enable_timer_interrupt() {
    unsafe {
        sie::set_stimer();
    }
}

// 这个函数是 trap.S 中__alltraps 保存完所有寄存器在内核栈 之后会进入到这里
#[no_mangle]
pub fn trap_handler(trap_context: &mut TrapContext) -> &mut TrapContext {
    let scause = scause::read(); // trap 发生的原因
    let stval = stval::read(); // trap的附加信息

    // 根据发生的原因判断是那种类别
    match scause.cause() {
        // 用户态发出的系统调用
        Trap::Exception(Exception::UserEnvCall) => {
            trap_context.sepc += 4;  // +4 是因为我们需要返回 ecall指令的下一个指令
            trap_context.x[10] = syscall(trap_context.x[17], [trap_context.x[10], trap_context.x[11], trap_context.x[12]]) as usize;
        }
        // 访问不允许的内存
        Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::StorePageFault) => {
            println!("[kernel] PageFault in application, kernel killed it.");
            exit_current_and_run_next();
        }
        // 非法指令
        Trap::Exception(Exception::IllegalInstruction) => {
            println!("[kernel] IllegalInstruction in application, kernel killed it.");
            exit_current_and_run_next();
        }
        Trap::Interrupt(Interrupt::SupervisorTimer) => {
            // 发生时钟中断之后, 继续设置下一个时钟中断的发起时间
            set_next_trigger();
            // 暂停当前任务, 切换新的任务
            suspend_current_and_run_next();
        }
        // 未知错误
        _ => {
            panic!(
                "Unsupported trap {:?}, stval = {:#x}!",
                scause.cause(),
                stval
            );
        }
    }
    trap_context
}



