Your own kernel
Combine the course: programs are generators that make system calls by yielding { sys: "write" }, "sleep", "spawn" or "yield"; your kernel keeps a process list, runs each for at most a quantum of steps (preemption), puts sleepers aside until their wake time, and skips the clock ahead when everyone is asleep.
The kernel's data structures
A tiny kernel needs only a few structures. A process table maps pids to processes, each with a name, its generator, its state and a wake time. A ready queue holds pids waiting for the CPU. A sleep list holds pids with their wake times. The clock counts ticks, and an output log records what processes wrote.
- spawn(name, program) — new pid, state ready, push onto the ready queue.
- Running: resume the generator up to quantum times.
- { sys: "sleep", ticks } — move to the sleep list until clock + ticks.
- { sys: "spawn", … } — create a child process.
- Generator finished — mark terminated and drop it.
Write a tiny program that only writes and finishes first; get that working before sleep and spawn.