Context Switching in Xinu

Tom Kelliher, CS42

Feb. 23, 1998

The following is from the Xinu system, designed by Douglas Comer.

/* declarations */

#define QUANTUM         10             /* clock ticks until preemption */
#define SP              6              /* reg. 6 is stack pointer */
#define PC              7              /* reg. 7 is program counter */
#define PS              8              /* proc. status stored in 8th loc. */

#define NPROC           10             /* max number of processes */

#define PRCURR          0x01           /* process is running */
#define PRFREE          0x02           /* table entry is free */
#define PRREADY         0x03           /* process is on ready queue */
#define PRRECV          0x04           /* process is waiting for message */
#define PRSLEEP         0x05           /* process is sleeping */
#define PRSUSP          0x06           /* process is suspended */
#define PRWAIT          0x07           /* process is on semaphore queue */

#define PNREGS          9              /* number of CPU registers */
#define PNMLEN          8              /* process name length */


/* process table entry */

struct   pentry \{
         char  pstate;                 /* state of process */
         short pprio;                  /* process priority */
         short pregs[PNREGS];          /* saved registers: R0-R5, SP, PC, PS */
         short psem;                   /* semaphore process waiting on */
         short pmsg;                   /* message sent to process */
         short phasmsg;                /* nonzero iff pmsg is valid */
         short pbase;                  /* base of runtime stack */
         short pstklen;                /* stack length */
         short plimit;                 /* lowest extent of stack */
         char name[PNMLEN];            /* process name */
         short pargs;                  /* number of arguments */
         short paddr;                  /* initial code address */
\};


extern struct pentry    proctab[NPROC];
extern int              currpid;       /* currently executing process */
extern int              rdyhead, rdytail;
extern int              preempt;


/*-----------------------------------------------------------------------------
 *  resched -- reschedule processor to highest priority ready process
 *
 *  Notes:     Upon entry, currpid gives current process id.
 *             Proctab[currpid].pstate gives correct NEXT state for current
 *             process if other than PRCURR.
 *-----------------------------------------------------------------------------
 */
int resched() \{
   register struct pentry        *optr;   /* pointer to old process entry */
   register struct pentry        *nptr;   /* pointer to new process entry */

   /* no switch needed if current process priority HIGHER than next */

   if ( ( (optr = &proctab[currpid])->pstate == PRCURR) &&
        (lastkey(rdytail) < optr->pprio) )
      return(OK);

   /* force context switch */

   if (optr->pstate == PRCURR) \{
      optr->pstate = PREADY;
      insert(currpid, rdyhead, optr->pprio);
   \}

   /* remove highest priority process at end of ready list */

   nptr = &proctab[ (currpid = getlast(rdytail) ) ];
   nptr->pstate = PRCURR;
   preempt = QUANTUM;                     /* reset preemption counter */
   ctxsw(optr->pregs, nptr->pregs);       /* do context switch */

   /* the OLD process returns here when resumed */

   return(OK);
\}


/*-----------------------------------------------------------------------------
 *  ctxsw -- assembler routine for performing context switch,
 *  saving/loading registers
 *
 *  The stack contains three items upon entry to this routine:
 *
 *  SP+4 => address of 9 word save area with new registers + PS
 *  SP+2 => address of 9 word save area for old registers + PS
 *  SP   => return address
 *
 *  The saved state consists of: the values of R0-R5 upon entry, SP+2, PC
 *  equal to the return address, and the PS (i.e., the PC and SP are saved
 *  as if the calling process had returned to its caller.
 *-----------------------------------------------------------------------------
 */
         .globl _ctxsw        /* declare name global */
_ctxsw:                       /* entry point to proc. */
         mov   r0,*2(sp)      /* save old R0 in old register area */
         mov   2(sp),r0       /* get address of old register area in R0 */
         add   $2,r0          /* increment to saved pos. of R1 */
         mov   r1,(r0)+       /* save R1-R5 in successive locations of old
         mov   r2,(r0)+       /*  process register save are */
         mov   r3,(r0)+
         mov   r4,(r0)+
         mov   r5,(r0)+
         add   $2,sp          /* move SP beyond the return address, as if a */
                              /*  return had occurred */
         mov   sp,(r0)+       /* save stack pointer */
         mov   -(sp),(r0)+    /* save caller's return address as PC */
         mfps  (r0)           /* save processor status beyond registers */
         mov   4(sp),r0       /* get address of start of new register area */
                              /* ready to load registers for the new process */
                              /*  and abandon the old stack */
         mov   2(r0),r1       /* load R1-R5 and SP from the new area */
         mov   4(r0),r2
         mov   6(r0),r3
         mov   8.(r0),r4      /* dot following a number makes it decimal; */
         mov   10.(r0),r5     /*  otherwise it is octal */
         mov   12.(r0),sp     /* have switched stacks now */
         mov   16.(r0),-(sp)  /* push new process PS on new process stack */
         mov   14.(r0),-(sp)  /* push new process PC on new process stack */
         mov   (r0),r0        /* load R0 from new area */
         rtt                  /* load PC, PS, and reset SP all at once */



Thomas P. Kelliher
Mon Feb 23 08:16:36 EST 1998
Tom Kelliher