pub fn spawn(&mut self, f: fn()) {
        let available = self
            .tasks
            .iter_mut()
            .find(|t| t.state == State::Available)
            .expect("no available task.");

        let size = available.stack.len();
        unsafe {
            let s_ptr = available.stack.as_mut_ptr().offset(size as isize);

            // make sure our stack itself is 8 byte aligned - it will always
            // offset to a lower memory address. Since we know we're at the "high"
            // memory address of our allocated space, we know that offsetting to
            // a lower one will be a valid address (given that we actually allocated)
            // enough space to actually get an aligned pointer in the first place).
            let s_ptr = (s_ptr as usize & !7) as *mut u8;

            available.ctx.x1 = guard as u64;  //ctx.x1  is old return address
            available.ctx.nx1 = f as u64;     //ctx.nx2 is new return address
            available.ctx.x2 = s_ptr.offset(-32) as u64; //cxt.x2 is sp

        }
        available.state = State::Ready;
    }
/// While `yield` is the logically interesting function I think this the technically most interesting.
///
/// When we spawn a new task we first check if there are any available tasks (tasks in `Parked` state).
/// If we run out of tasks we panic in this scenario but there are several (better) ways to handle that.
/// We keep things simple for now.
///
/// When we find an available task we get the stack length and a pointer to our u8 bytearray.
///
/// The next part we have to use some unsafe functions. First we write an address to our `guard` function
/// that will be called if the function we provide returns. Then we set the address to the function we
/// pass inn.
///
/// Third, we set the value of `sp` which is the stack pointer to the address of our provided function so we start
/// executing that first when we are scheuled to run.
///
/// Lastly we set the state as `Ready` which means we have work to do and is ready to do it.
While `yield` is the logically interesting function I think this the technically most interesting.
When we spawn a new task we first check if there are any available tasks (tasks in `Parked` state).
If we run out of tasks we panic in this scenario but there are several (better) ways to handle that.
We keep things simple for now.

When we find an available task we get the stack length and a pointer to our u8 bytearray.

The next part we have to use some unsafe functions. First we write an address to our `guard` function
that will be called if the function we provide returns. Then we set the address to the function we
pass inn.

Third, we set the value of `sp` which is the stack pointer to the address of our provided function so we start
executing that first when we are scheuled to run.

Lastly we set the state as `Ready` which means we have work to do and is ready to do it.
While ` yield ` is the logically interesting function I think this the technically most interesting . When we spawn a new task we first check if there are any available tasks ( tasks in ` Parked ` state ) . If we run out of tasks we panic in this scenario but there are several ( better ) ways to handle that . We keep things simple for now . When we find an available task we get the stack length and a pointer to our u8 bytearray . The next part we have to use some unsafe functions . First we write an address to our ` guard ` function that will be called if the function we provide returns . Then we set the address to the function we pass inn . Third we set the value of ` sp ` which is the stack pointer to the address of our provided function so we start executing that first when we are scheuled to run . Lastly we set the state as ` Ready ` which means we have work to do and is ready to do it .
pub fn spawn(&mut self, f: fn()) {
        let available = self
            .tasks
            .iter_mut()
            .find(|t| t.state == State::Available)
            .expect("no available task.");
        let size = available.stack.len();
        unsafe {
            let s_ptr = available.stack.as_mut_ptr().offset(size as isize);
            // make sure our stack itself is 8 byte aligned - it will always
            // offset to a lower memory address. Since we know we're at the "high"
            // memory address of our allocated space, we know that offsetting to
            // a lower one will be a valid address (given that we actually allocated)
            // enough space to actually get an aligned pointer in the first place).
            let s_ptr = (s_ptr as usize & !7) as *mut u8;
            available.ctx.x1 = guard as u64;  //ctx.x1  is old return address
            available.ctx.nx1 = f as u64;     //ctx.nx2 is new return address
            available.ctx.x2 = s_ptr.offset(-32) as u64; //cxt.x2 is sp
        }
        available.state = State::Ready;
    }
pub fn spawn ( & mut self , f : fn ( ) ) { let available = self . tasks . iter_mut ( ) . find ( | t | t . state == State :: Available ) . expect ( " " ) ; let size = available . stack . len ( ) ; unsafe { let s_ptr = available . stack . as_mut_ptr ( ) . offset ( size as isize ) ; // make sure our stack itself is 8 byte aligned - it will always // offset to a lower memory address. Since we know we're at the "high" // memory address of our allocated space, we know that offsetting to // a lower one will be a valid address (given that we actually allocated) // enough space to actually get an aligned pointer in the first place). let s_ptr = ( s_ptr as usize & ! 7 ) as * mut u8 ; available . ctx . x1 = guard as u64 ; //ctx.x1  is old return address available . ctx . nx1 = f as u64 ; //ctx.nx2 is new return address available . ctx . x2 = s_ptr . offset ( - 32 ) as u64 ; //cxt.x2 is sp } available . state = State :: Ready ; }
While `yield` is the logically interesting function I think this the technically most interesting.
While ` yield ` is the logically interesting function I think this the technically most interesting .
&mut self
f
fn()
&mut self
f
fn()