diff --git a/qemu/examples/spawn2.rs b/qemu/examples/spawn2.rs
new file mode 100644
index 0000000000000000000000000000000000000000..782cb89934f36656885f5102495b25ce5b5fee86
--- /dev/null
+++ b/qemu/examples/spawn2.rs
@@ -0,0 +1,184 @@
+//! examples/spawn2.rs
+
+#![no_main]
+#![no_std]
+#![feature(const_fn)]
+#![feature(type_alias_impl_trait)]
+
+use core::cell::Cell;
+use core::cell::UnsafeCell;
+use core::future::Future;
+use core::mem;
+use core::mem::MaybeUninit;
+use core::pin::Pin;
+use core::ptr;
+use core::ptr::NonNull;
+use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering};
+use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
+
+use cortex_m_semihosting::{debug, hprintln};
+use panic_semihosting as _;
+
+#[rtic::app(device = lm3s6965)]
+mod app {
+    #[init]
+    fn init(_c: init::Context) -> init::LateResources {
+        foo::spawn(true).unwrap();
+        bar::spawn(true).unwrap();
+
+        init::LateResources {}
+    }
+
+    #[task]
+    fn foo(_c: foo::Context, spawn: bool) {
+        // BEGIN BOILERPLATE
+        type F = impl Future + 'static;
+        fn create() -> F {
+            task()
+        }
+
+        static mut TASK: Task<F> = Task::new();
+
+        unsafe {
+            if spawn {
+                TASK.spawn(create);
+            }
+            TASK.poll(|| {
+                // it's OK if the task fails to spawn because it's already queued.
+                // if it's awakened 2 times we just need to run it once.
+                let _ = foo::spawn(false);
+            });
+        }
+        // END BOILERPLATE
+
+        async fn task() {
+            loop {
+                hprintln!("foo");
+                please_yield().await;
+            }
+        }
+    }
+
+    #[task]
+    fn bar(_c: bar::Context, spawn: bool) {
+        // BEGIN BOILERPLATE
+        type F = impl Future + 'static;
+        fn create() -> F {
+            task()
+        }
+
+        static mut TASK: Task<F> = Task::new();
+
+        unsafe {
+            if spawn {
+                TASK.spawn(create);
+            }
+            TASK.poll(|| {
+                // it's OK if the task fails to spawn because it's already queued.
+                // if it's awakened 2 times we just need to run it once.
+                let _ = bar::spawn(false);
+            });
+        }
+        // END BOILERPLATE
+
+        async fn task() {
+            let mut nr = 0;
+
+            loop {
+                nr += 1;
+                hprintln!("bar {}", nr);
+                if nr == 3 {
+                    debug::exit(debug::EXIT_SUCCESS);
+                }
+                please_yield().await;
+            }
+        }
+    }
+
+    // RTIC requires that unused interrupts are declared in an extern block when
+    // using software tasks; these free interrupts will be used to dispatch the
+    // software tasks.
+    extern "C" {
+        fn SSI0();
+    }
+}
+
+//=============
+// Waker
+
+static WAKER_VTABLE: RawWakerVTable =
+    RawWakerVTable::new(waker_clone, waker_wake, waker_wake, waker_drop);
+
+unsafe fn waker_clone(p: *const ()) -> RawWaker {
+    RawWaker::new(p, &WAKER_VTABLE)
+}
+
+unsafe fn waker_wake(p: *const ()) {
+    let f: fn() = mem::transmute(p);
+    f();
+}
+
+unsafe fn waker_drop(_: *const ()) {
+    // nop
+}
+
+//============
+// Task
+
+enum Task<F: Future + 'static> {
+    Idle,
+    Running(F),
+    Done(F::Output),
+}
+
+impl<F: Future + 'static> Task<F> {
+    const fn new() -> Self {
+        Self::Idle
+    }
+
+    fn spawn(&mut self, future: impl FnOnce() -> F) {
+        *self = Task::Running(future());
+    }
+
+    unsafe fn poll(&mut self, wake: fn()) {
+        match self {
+            Task::Idle => {}
+            Task::Running(future) => {
+                let future = Pin::new_unchecked(future);
+                let waker_data: *const () = mem::transmute(wake);
+                let waker = Waker::from_raw(RawWaker::new(waker_data, &WAKER_VTABLE));
+                let mut cx = Context::from_waker(&waker);
+
+                match future.poll(&mut cx) {
+                    Poll::Ready(r) => *self = Task::Done(r),
+                    Poll::Pending => {}
+                };
+            }
+            Task::Done(_) => {}
+        }
+    }
+}
+
+//=============
+// Yield
+
+struct Yield {
+    done: bool,
+}
+
+impl Future for Yield {
+    type Output = ();
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        if self.done {
+            Poll::Ready(())
+        } else {
+            cx.waker().wake_by_ref();
+            self.done = true;
+            Poll::Pending
+        }
+    }
+}
+
+fn please_yield() -> Yield {
+    Yield { done: false }
+}
diff --git a/qemu/examples/spawn3.rs b/qemu/examples/spawn3.rs
new file mode 100644
index 0000000000000000000000000000000000000000..82ca0b230c7a10cd9bd0f41b7806bc0882bd60e5
--- /dev/null
+++ b/qemu/examples/spawn3.rs
@@ -0,0 +1,203 @@
+//! examples/spawn2.rs
+
+#![no_main]
+#![no_std]
+#![feature(const_fn)]
+#![feature(type_alias_impl_trait)]
+
+// use core::cell::Cell;
+// use core::cell::UnsafeCell;
+use core::future::Future;
+use core::mem;
+// use core::mem::MaybeUninit;
+use core::pin::Pin;
+// use core::ptr;
+// use core::ptr::NonNull;
+// use core::sync::atomic::{AtomicPtr, AtomicU32, Ordering};
+use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
+
+use cortex_m_semihosting::{debug, hprintln};
+use panic_semihosting as _;
+
+#[rtic::app(device = lm3s6965)]
+mod app {
+    #[resources]
+    struct Resources {
+        // A resource
+        #[init(0)]
+        counter: u32,
+    }
+
+    #[init]
+    fn init(_c: init::Context) -> init::LateResources {
+        foo::spawn(true).unwrap();
+        bar::spawn(true).unwrap();
+
+        init::LateResources {}
+    }
+
+    #[task(resources = [counter])]
+    fn foo(c: foo::Context, spawn: bool) {
+        // BEGIN BOILERPLATE
+        type F = impl Future + 'static;
+        fn create(c: foo::Context<'static>) -> F {
+            task(c)
+        }
+
+        static mut TASK: Task<F> = Task::new();
+
+        unsafe {
+            if spawn {
+                // TODO I have no idea if this transmute is actually OK
+                TASK.spawn(|| create(mem::transmute(c)));
+            }
+            TASK.poll(|| {
+                // it's OK if the task fails to spawn because it's already queued.
+                // if it's awakened 2 times we just need to run it once.
+                let _ = foo::spawn(false);
+            });
+        }
+        // END BOILERPLATE
+
+        async fn task(mut c: foo::Context<'static>) {
+            loop {
+                c.resources.counter.lock(|c| {
+                    *c += 1;
+                    if *c > 10 {
+                        debug::exit(debug::EXIT_SUCCESS);
+                    }
+                    hprintln!("foo {}", *c).ok();
+                });
+                please_yield().await;
+            }
+        }
+    }
+
+    #[task(resources = [counter])]
+    fn bar(c: bar::Context, spawn: bool) {
+        // BEGIN BOILERPLATE
+        type F = impl Future + 'static;
+        fn create(c: bar::Context<'static>) -> F {
+            task(c)
+        }
+
+        static mut TASK: Task<F> = Task::new();
+
+        unsafe {
+            if spawn {
+                // TODO I have no idea if this transmute is actually OK
+                TASK.spawn(|| create(mem::transmute(c)));
+            }
+            TASK.poll(|| {
+                // it's OK if the task fails to spawn because it's already queued.
+                // if it's awakened 2 times we just need to run it once.
+                let _ = bar::spawn(false);
+            });
+        }
+        // END BOILERPLATE
+
+        async fn task(mut c: bar::Context<'static>) {
+            loop {
+                c.resources.counter.lock(|c| {
+                    *c += 1;
+                    hprintln!("bar {}", *c).ok();
+                });
+                rt_task::spawn();
+                please_yield().await;
+            }
+        }
+    }
+
+    #[task(resources = [counter], priority = 2)]
+    fn rt_task(cx: rt_task::Context) {
+        hprintln!("counter {}", cx.resources.counter).ok();
+    }
+
+    // RTIC requires that unused interrupts are declared in an extern block when
+    // using software tasks; these free interrupts will be used to dispatch the
+    // software tasks.
+    extern "C" {
+        fn SSI0();
+        fn UART0();
+    }
+}
+
+//=============
+// Waker
+
+static WAKER_VTABLE: RawWakerVTable =
+    RawWakerVTable::new(waker_clone, waker_wake, waker_wake, waker_drop);
+
+unsafe fn waker_clone(p: *const ()) -> RawWaker {
+    RawWaker::new(p, &WAKER_VTABLE)
+}
+
+unsafe fn waker_wake(p: *const ()) {
+    let f: fn() = mem::transmute(p);
+    f();
+}
+
+unsafe fn waker_drop(_: *const ()) {
+    // nop
+}
+
+//============
+// Task
+
+enum Task<F: Future + 'static> {
+    Idle,
+    Running(F),
+    Done(F::Output),
+}
+
+impl<F: Future + 'static> Task<F> {
+    const fn new() -> Self {
+        Self::Idle
+    }
+
+    fn spawn(&mut self, future: impl FnOnce() -> F) {
+        *self = Task::Running(future());
+    }
+
+    unsafe fn poll(&mut self, wake: fn()) {
+        match self {
+            Task::Idle => {}
+            Task::Running(future) => {
+                let future = Pin::new_unchecked(future);
+                let waker_data: *const () = mem::transmute(wake);
+                let waker = Waker::from_raw(RawWaker::new(waker_data, &WAKER_VTABLE));
+                let mut cx = Context::from_waker(&waker);
+
+                match future.poll(&mut cx) {
+                    Poll::Ready(r) => *self = Task::Done(r),
+                    Poll::Pending => {}
+                };
+            }
+            Task::Done(_) => {}
+        }
+    }
+}
+
+//=============
+// Yield
+
+struct Yield {
+    done: bool,
+}
+
+impl Future for Yield {
+    type Output = ();
+    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+        if self.done {
+            Poll::Ready(())
+        } else {
+            cx.waker().wake_by_ref();
+            self.done = true;
+            Poll::Pending
+        }
+    }
+}
+
+fn please_yield() -> Yield {
+    Yield { done: false }
+}