In part 1, I talked a bit about simple cooperative multitasking, such as might be implemented in an imbedded pic. Each process had to maintain all of it's own context, and the scheduler did not much of anything. In part two here, I'll talk about adding some context switching to the scheduler. Someone has already posted a snippet of pseudocode where the direct calls to each task are replaced by a loop with indirect calls through a table of processes. Good - I won't do that part If you're going to have a scheduler that saves and restores a process's context for it, the first thing you need to do is define just what it is that makes up that context. A process is ... something running on a CPU that thinks it is independent of any other similar processes. So, minimally, the context includes the set of registers that a process can see (including the program counter.) For most languages, the context includes the stack. For large systems, the context probably includes something like a pager map, but we'll skip that for now. Note that we're already pretty much out of the realm of small PICs here - since a pic doesn't have a manipulable stack, and doesn't really have much in the way of registers. Still, one can imagine that a C compile might simulate all of that for us, assuming there's memory left over for code. The scheduler will have to save a process's context to run some other process, and restore it later. Logically speaking, the place where the context is saved is part of the process as well. Now, the scheduler itself has its own context as well, which will normally run in between "user" processes. A simple scheme that implements this uses two functions suspend() and resume() as the primitives for doing the context switch. In general, these will need to be written in assembler and with internal knowlege of the compiler operations, but they can be the ONLY such functions - pretty much everything else can be written the high level langauge you're using (C, here.) The basic pseudo-code looks like: suspend: push all ; Save all registers on process stack move context.sp, sp ; Save our stack pointer in context area. cli ; disable interrupts move sp, context.schedsp ; get the schedulers stack pointer sti ; we can have interrupts again pop all ; restore all registers [note: this restores ; the scheduler's register from the ; scheduler's stack!] ret ; return (to scheduler!) resume: push all ; Save schedulers registers on sched stack move context.schedsp, sp ; Save scheduler stack pointer cli move sp, context,sp ; get process stack sti pop all ; restore process registers ret ; return (to process!) This assumes that there is a structure called "context" somewhere that implements the "process controll block", something like: typedef struct context_t { mempointer schedsp; /* place to save sched sp while running. */ mempointer sp; /* place to save MY sp while suspended. */ mempointer stack; /* beginning of stack */ int stacksize; /* size of stack. */ int proc_id; /* something to identify us */ }; You can use the same cell for schedsp and sp if you're short on memory. Note that we don't need storage in the context for regsiters or PC, since we save those on the appropriate stacks. Given these primitives, your process can make a function call to suspend() at any point that it decides it can't do any more work, regardless of the "stack depth" of the call. Consider our translating port again: process translate() { char c; c = getcharacter(); putcharacter(translate(c)); } char getcharacter() { while ((uart->status & RXRDY) == 0) { suspend(); } return(uart->data); } putcharacter (char x) { while ((uart->status & TXREADY) == 0) { suspend(); } uart->data = x; } Here we can return to the scheduler when either we can't write or can't read from the uart. (there's no buffer in between now, so it "doesn't matter" if we miss input characters while we're suspended waiting to transmit them - there wasn't anything to do with them anyway!) More in part three - making better choices about which process to run. BillW