// src/main.rs
#![no_std]
#![no_main]
#![feature(naked_functions)]

use core::panic::PanicInfo;

mod heap;
mod trap;
mod uart;

#[naked]
#[no_mangle]
#[link_section = ".text.init"]
unsafe extern "C" fn _enter() -> ! {
    // TODO see if possible to replace this somehow...
    use core::arch::asm;
    asm!(
        // load hartid into t0; if t0 =/= 0, then jump to busy loop
        "csrr t0, mhartid",
        "bnez t0, 3f",

        // before we use the `la` pseudo-instruction for the first time,
        // we need to set `gp` (look up linker relaxation)
        ".option push", // pushes the current option stack
        ".option norelax", // disable relaxation so we can properly set `gp`
        // this instruction compiles to:
        //     auipc gp, %pcrel_hi(_global_pointer)
        //     addi  gp, gp, %pcrel_lo(1b)
        // if relaxation were on, this would simply be `mv gp, gp` or `addi gp, gp, 0`
        "la gp, _global_pointer",
        ".option pop", // pops the current option stack

        // // delegate all traps to S-mode trap handler
        // "li t5, 0xffff",
        // "csrw medeleg, t5", // delegate all machine exceptions
        // "csrw mideleg, t5", // delegate all machine interrupts

        // set the stack pointer
        "la sp, _init_stack_top",

        // // Make sure machine mode is set, and enable coarse interrupts
        // "li t0, (0b11 << 11) | (1 << 7) | (1 << 3)",
        // "csrw mstatus, t0",
        
        // // Set mtvec to the location of our trap handler function
        // "la t1, {trap_vector}",
        // "csrw mtvec, t1",

        // // Set MSIE, MTIE, and MEIE on machine interrupt enable CSR:
        // //   MSIE to enable machine-/M-mode software interrupts
        // //   MTIE to enable M-mode timer interrupts
        // //   MEIE to enable M-mode external interrupts
        // "li t2, (1 << 3) | (1 << 7) | (1 << 11)",
        // "csrw mie, t2",

        // clear the BSS
        "la t0, _bss_start",
        "la t1, _bss_end",
        "bgeu t0, t1, 2f",
    "1:",
        "sb zero, 0(t0)",
        "addi t0, t0, 1",
        "bne t0, t1, 1b",
    "2:",
        "j 4f",
        // BSS is clear!
    
    // busy loop if hartid =/= 0
    "3:",
        "wfi", // wait for interrupt (or nop)
        "j 3b",
    
    "4:",
        // "tail-call" to {start} (call without saving a return address)
        "tail {start}",
        start = sym start, // {start} refers to the function [start] below
        //trap_vector = sym trap::trap_handler, // {trap_vector} refers to function [trap::trap_handler]
        options(noreturn) // we must handle "returning" from assembly
    );
}

extern "C" fn start() -> ! {
    // UNSAFE: Called exactly once, right here.
    unsafe { heap::init() };

    // Now we're free to use dynamic allocation!
    // {
    //     extern crate alloc;
    //     use alloc::boxed::Box;
    //     let _my_heap_pointer = Box::new(10);
    //     // _my_heap_pointer lives on the heap!
    // }

    // println!("This should not print because the console is not initialised.");
    unsafe { uart::init_console(0x1000_0000) };
    println!("Hello, world!");

    // print current hartid
    println!("hartid: {}", riscv::register::mhartid::read());

    // poll console for input and print characters back
    loop {
        let c = uart::CONSOLE.lock().as_mut().and_then(uart::Device::get);
        if let Some(c) = c { print!("{}", c as char); }
    }
}

#[no_mangle]
extern "C" fn eh_personality() {}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    // print panic info and abort
    println!("{}", info);
    abort();
}

#[no_mangle]
extern "C" fn abort() -> ! {
    use core::arch::asm;
    loop { unsafe { asm!("wfi"); } }
}

// TODO unit testing