/src/task.rs
use core::ptr;
use crate::mpu::{self, MemoryRegion, MemoryPermission, MemoryRange};

extern "C" {
    static _appram: u32;
    static _appram_end: u32;
    pub fn launch(entry: u32, sp: u32, data_base: u32) -> !;
}

global_asm!(r#"
    .global launch
    .type launch,function
launch:
    // Move entry address out of the way
    mov r3, r0
    // set up PSP and drop privileges
    mov r0, 0x3
    msr CONTROL, r0
    // set stack pointer and pointer to data region info
    mov sp, r1
    mov r0, r1
    // set up memory region pointer
    mov r9, r2
    isb
    dsb
    bx r3
"#);

#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Task {
    text: MemoryRegion,
    data: MemoryRegion,
    stack: MemoryRegion,

    pc: u32,
    sp: u32,
}

impl Task {
    pub fn default_layout_full(entry: unsafe extern "C" fn() -> u32) -> Task {
        let appram_base = unsafe { &_appram as *const u32 as u32 };
        let stack_start = appram_base + 0x10000 + 0xe000;
        let stack_size = 0x2000;
        let stack_end = stack_start + stack_size;

        Task {
            text: MemoryRegion {
                start: 0x00000000,
                length: 0,
                permissions: MemoryPermission::None,
                xn: false,
            },
            data: MemoryRegion {
                start: appram_base,
                length: 0x10000,
                permissions: MemoryPermission::RW,
                xn: true,
            },
            stack: MemoryRegion {
                start: stack_start,
                length: stack_size,
                permissions: MemoryPermission::RW,
                xn: true,
            },

            pc: entry as *const () as u32,
            sp: stack_end,
        }
    }

    pub fn run(&self) -> ! {
        mpu::add_region(&self.text);
        mpu::add_region(&self.data);
        mpu::add_region(&self.stack);

        unsafe {
            let sp = self.sp as *mut MemoryRange;
            let sp = sp.sub(1);
            ptr::write(sp, MemoryRange {
                start: self.data.start,
                length: self.data.length - self.stack.length,
            });
            let sp = sp as u32;

            launch(self.pc, sp, self.data.start)
        }
    }

}