--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+
+/* TODO:
+
+* Added multi-threading
+* Add message passing
+
+*/
+
+typedef struct Task_T {
+ long* stack_top; // top of coroutine's stack
+ long* stack_base; // allocated memory for stack
+ struct Task_T* next; // pointer to next task
+ int id;
+} Task_T;
+
+static struct {
+ Task_T* curr;
+ Task_T* head;
+ Task_T* tail;
+ Task_T* dead;
+} Queue = { 0 };
+
+extern void Task_Switch(Task_T* prev, Task_T* next);
+asm (
+".global Task_Switch\n"
+"Task_Switch:\n"
+" push %rdi\n"
+" push %rbp\n"
+" push %rbx\n"
+" push %r12\n"
+" push %r13\n"
+" push %r14\n"
+" push %r15\n"
+" mov %rsp,(%rdi)\n"
+" mov (%rsi),%rsp\n"
+" pop %r15\n"
+" pop %r14\n"
+" pop %r13\n"
+" pop %r12\n"
+" pop %rbx\n"
+" pop %rbp\n"
+" pop %rdi\n"
+" ret\n"
+ );
+
+void Enqueue(Task_T* task)
+{
+ /* enqueue the currently running task */
+ if (task && task != Queue.dead)
+ {
+ if (Queue.tail)
+ {
+ Queue.tail->next = task;
+ task->next = NULL;
+ }
+ Queue.tail = task;
+ if (!Queue.head)
+ {
+ Queue.head = Queue.tail;
+ }
+ }
+}
+
+void Task_Yield(void)
+{
+ if (Queue.head)
+ {
+ /* unload the current task */
+ Task_T* curr = Queue.curr;
+ Enqueue(curr);
+ Queue.curr = NULL;
+
+ /* now pick the next task and start it */
+ Queue.curr = Queue.head;
+ Queue.head = Queue.head->next;
+ Queue.curr->next = NULL;
+ if (!Queue.head)
+ {
+ Queue.tail = NULL;
+ }
+
+ /* run the selected task */
+ Task_Switch(curr, Queue.curr);
+
+ /* clean up the dead task if we have one */
+ if (Queue.dead)
+ {
+ printf("destroying task %d\n", Queue.dead->id);
+ free(Queue.dead->stack_base);
+ free(Queue.dead);
+ Queue.dead = NULL;
+ }
+ }
+}
+
+void Task_Exit(void)
+{
+ printf("exiting task %d\n", Queue.curr->id);
+ Queue.dead = Queue.curr;
+ if (Queue.head)
+ {
+ Task_Yield();
+ }
+ else
+ {
+ exit(0);
+ }
+}
+
+void Task_Create(void (*task_fn)(void*), void *arg, int stacksize)
+{
+ static int i = 0;
+ if (stacksize == 0) { stacksize = 1024*1024; }
+ Task_T* task = calloc(1, sizeof(Task_T));
+ task->id = i++;
+ task->stack_base = calloc(stacksize/sizeof(long), sizeof(long));
+ task->stack_top = &task->stack_base[stacksize/sizeof(long)-1]; // top of stack
+ *(--task->stack_top) = (long)Task_Exit; // coroutine cleanup
+ *(--task->stack_top) = (long)task_fn; // user's function to run (rop style!)
+ *(--task->stack_top) = (long)arg; // user's function argument (rdi)
+ for (int saved = 0; saved < 6; saved++)
+ {
+ *(--task->stack_top) = 0xdeadbeef; // initial values for saved registers
+ }
+
+ /* enqueue the task */
+ if (!Queue.curr)
+ {
+ Queue.curr = task;
+ }
+ else
+ {
+ Enqueue(task);
+ }
+ printf("created task %d\n", task->id);
+ Task_Yield();
+}
+
+void Task(void)
+{
+ /* create a task object for main */
+ Task_Create(0, 0, 0);
+}
+
+/***************************************
+MAIN
+***************************************/
+
+void task_sub(void *arg)
+{
+ for (int i = 0; i < 5; i++)
+ {
+ Task_Yield();
+ printf(" Inside task %d\n", Queue.curr->id);
+ }
+}
+
+void task_main(void *arg)
+{
+ for (int i = 0; i < 3; i++)
+ {
+ printf("spawning task %d\n", i);
+ Task_Create(task_sub, 0, 0);
+ }
+}
+
+int main(int argc, char** argv) {
+ if (argc != 2) { return 1; }
+ Task();
+
+ int count = atoi(argv[1]);
+ for (int i = 0; i < count; i++)
+ {
+ printf("spawning task %d\n", i+1);
+ int* id = calloc(1, sizeof(int));
+ *id = i;
+ Task_Create(task_sub, id, 0);
+ }
+ Task_Exit();
+ printf("This is unreachable!\n");
+ return 0;
+}
\ No newline at end of file