Listing 1 (exec.h)

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

#ifndef STRATOM_H
#include "stratom.h"
#endif

#ifndef YES
#define YES 1
#define NO 0
typedef int BOOL;
#endif

#ifndef BIT
#define BIT(i) (1 << (i))
#endif

/*      different perfixes for EXEC_ID
 */
#define PORT_PREFIX         BIT(15)
#define SEMAPHORE_PREFIX    BIT(14)
#define TIMER_PREFIX        BIT(13)
#define MONOSTABLE_PREFIX   BIT(12)
#define TRIGGER_PREFIX      BIT(11)

#define BAD_EXEC_ID 0

typedef int            EXEC_ID;
typedef unsigned char  THREAD_ID;

typedef unsigned int   PORT;
typedef unsigned int   SEMAPHORE;

/*      process type.  Not used yet
 */
typedef enum
    { NORMAL_PROC = 10, HALTABLE_PROC, INHIBITABLE_PROC }
              PROCESS_TYPE;

void *ExecAlloc(unsigned int size);
THREAD_ID ExecCreateThread(PROCESS_TYPE procType,
       void (*pFunction)(void), unsigned int stackSize,
       unsigned long arg0, unsigned long arg1);
THREAD_ID ExecGetThreadID(void);
void ExecInit(void);
void ExecStart(void);

void MsgClearSemaphore(STRING_ATOM semaphoreAtom);
BOOL MsgConnectPorts(STRING_ATOM sourceAtom,
       STRING_ATOM destinationAtom);
void MsgDeleteTimer(EXEC_ID watchID);
EXEC_ID MsgGetMsg(void);
BOOL MsgInhibitPorts(STRING_ATOM sourceAtom,
       STRING_ATOM destinationAtom);
void *MsgOpenPort(STRING_ATOM nameAtom, PORT *pPortValue);
PORT MsgPeekPort(STRING_ATOM, PORT *);
void MsgSetSemaphore(STRING_ATOM semaphoreAtom);
BOOL MsgSuppressPorts(STRING_ATOM sourceAtom,
       STRING_ATOM destinationAtom);
EXEC_ID MsgWatchPort(STRING_ATOM portAtom, PORT *pPortValue);
EXEC_ID MsgWatchSemaphore(STRING_ATOM semaphoreAtom, BOOL detectOn);
EXEC_ID MsgWatchTimer(unsigned int interval);
void MsgWriteToPort(STRING_ATOM portAtom, PORT portValue);

#define MsgWatchTrigger(portAtom) MsgWatchPort(portAtom, NULL)

void SysLEDs(int i);

typedef unsigned
#ifdef HC11
int
#else
long
#endif
EXEC_ARG;

extern EXEC_ARG ExecArg0, ExecArg1;
/*- exec.c -
 */
#include <stdlib.h>
#include <string.h>
#include "exec.h"
#include "execpriv.h"

/*      arguments to threads
 */
EXEC_ARG                ExecArg0;
EXEC_ARG                ExecArg1;

/*      number of possible threads
 */
#ifndef NUMBER_OF_THREADS
#define NUMBER_OF_THREADS  34
#endif

/*      process state
 */
typedef enum
   { INIT_STATE = 20, RUNNING_STATE, WAIT_STATE }
             PROCESS_STATE;

/*      context for thread
 */
typedef struct
    {
    THREAD_ID       threadID;
    void            (*pFunction)(void);
    PROCESS_TYPE    procType;
    PROCESS_STATE   procState;
    jmp_buf         exitContext;
    jmp_buf         switchContext;
    char            *stackTop;
    char            *stackBottom;
    EXEC_ARG        arg0;
    EXEC_ARG        arg1;
    } THREAD_CONTEXT;

/*       file static variables
 */
static BOOL                  hasCreatedThreads;
static THREAD_CONTEXT        threadTable[NUMBER_OF_THREADS];
static THREAD_ID             execCurrentThreadID;

static void execRunNewThread(void);

/*      allocate a block of memory, zeroed out
 */
void *ExecAlloc(unsigned int size)
    {
    void *p;

    p = calloc(1, size);
       if (!p)
              {
#ifndef HC11
              printf("no memory\n");
#endif
              exit(1);
              }
    return (p);
    }

/*       thread function returns here, jump to exit context
 */
void ExecCallReturn(void)
    {

    longjmp(threadTable[execCurrentThreadID].exitContext,
              execCurrentThreadID);
    }

/*       create a thread context
 */
THREAD_ID ExecCreateThread(PROCESS_TYPE procType, void (*pFunction)(void),
    unsigned int stackSize, unsigned long arg0, unsigned long arg1)
    {
       char *stack;
    THREAD_ID i;

       /*       find the next free entry in table
        */
    for (i = 0; i < NUMBER_OF_THREADS && threadTable[i].procState; ++i)
       ;
    if (i == NUMBER_OF_THREADS)
       return (BAD_EXEC_ID);
       hasCreatedThreads = YES;
       /*      clear entry and allocate stack
        */
       memset(&threadTable[i], '\0', sizeof (THREAD_CONTEXT));
       /*      stack space used by the executive, i.e. by the functions MsgGetMsg()
        *      and ExecSwitch()
        */
       stackSize &= ~Ox3;      /* quad byte aligned */
#define EXEC_STACK_SIZE 40
       stackSize += EXEC_STACK_SIZE;
       stack = SysGetStack(stackSize);
       threadTable[i].procType = procType;
       threadTable[i].pFunction = pFunction;
       threadTable[i].stackBottom = stack;
       threadTable[i].stackTop = stack + stackSize;
       threadTable[i].procState = INIT_STATE;
       /*      memorize the initail arguments to function
        */
       threadTable[i].arg0 = arg0;
       threadTable[i].arg1 = arg1;
       /*      put this on the runnable queue
        */
       MsgEnQueue(i, i);
       return (i);
    }
/*      return a new id
 */
EXEC_ID execExecID(unsigned int prefix)
    {
    static unsigned int i;

    return (prefix | (++i));
    }

/*       return the current thread id
 */
THREAD_ID ExecGetThreadID(void)
    {

    return (execCurrentThreadID);
    }

/*       initialize the system
 */
void ExecInit(void)
    {
       SysInit();
    }
/*       take the first thread off the runnable queue and give execution to it
 */
static
void execRunNewThread(void)  
       {
       EXEC_ID returnID;
       PROCESS_STATE procState;
       
       /*      take the first thread off the queue
        */
       execCurrentThreadID = MsgDeQueue(&returnID);
       SysLEDs(execCurrentThreadID);
       /*      set process state
        */
       procState = threadTable[execCurrentThreadID].procState;
       threadTable[execCurrentThreadID].procState = RUNNING_STATE;
       /*      procState == WAIT_STAT - use longjmp() to switch back to the old
        *      execution context
        */
       if (procState != INIT_STATE)
              longjmp(threadTable[execCurrentThreadID].switchContext, returnID);
       else
              {
              /*      initial call to a thread. Put thread arguments in the external
               *      variables and call an assembly routine to call the thread
               */
              if (setjmp(threadTable[execCurrentThreadID].exitContext) == 0)
                     {
                     ExecArg0 = threadTable[execCurrentThreadID].arg0;
                     ExecArg1 = threadTable[execCurrentThreadID].arg1;
                     ExecCall(threadTable[execCurrentThreadID].pFunction,
                                   threadTable[execCurrentThreadID].stackTop);
                     }
              /*       thread dies, reclaim thread table entry (but not stack space)
               *       and run the next available thread
               */
              threadTable[execCurrentThreadID].procState = 0;
              /*       need to delete from all queues and ports
               */
              execRunNewThread();
              }
       }
/*      start the system.
 */
void ExecStart(void)
   {

       if (hasCreatedThreads)
              execRunNewThread();
   }
/*      switch to a new thread
 */
EXEC_ID ExecSwitch(void)
   {
   EXEC_ID watchID;
   
       /*       check for stack overflow
        */
       if ((char *)&watchID < threadTable[execCurrentThreadID].stackBottom)
              {
#ifndef HC11
              printf("stack overflow\n");
#endif
              exit(1);
              }
    /*  set up our return context and switch
   */
    if ((watchID = setjmp(threadTable[execCurrentThreadID].switchContext)) == 0)
       {
              threadTable[execCurrentThreadID].procState = WAIT_STATE;
              execRunNewThread();
              }
    return (watchID);
    }

/* End of File */