
/* MSM.c, Copyright (C) 1992 by Jon Shemitz.

   A MSM engine and not much else -- this code
   has been slashed to minimize magazine space:
   Features and *error checking* have been
   stripped out and made "exercises for the reader".
*/

#include "msm.h"

// The TaskList is privately declared. Encapsulation
// the "old fashioned" way.

enum OnOff {On, Off};

struct TaskList {
       Byte      Length;
       TaskToken List[NTasks];

       TaskList()               {Length = 0;}
       void Append(TaskToken T) {List[Length++] = T;}
       void Delete(TaskToken T);
       int ContainsP(TaskToken T)
         {return IndexOf(T) < Length;}

       private:
         int IndexOf(TaskToken T);
	 // Returns Length iff not found
       };
typedef TaskList* PTaskList;

void TaskList::Delete(TaskToken T)
{
  int Index = IndexOf(T);
  if (Index < Length) Length--;
  for ( ; Index < Length; Index++)
    List[Index] = List[Index+1];
};

int TaskList::IndexOf(TaskToken T)
{
  int Index;
  for (Index = 0;
       Index < Length && List[Index] != T;
       Index++) ;
  return Index;
};

struct MSM {
       OnOff      Tasking;
       TaskPtr	  Ptr[NTasks];
       TaskList	  Foreground;
       TaskList   Background;

       MSM(); // Guarantee initialization
       } Taskz;

// Tasks' member functions

Tasks::Tasks(Handler S, Word W, Priorities P)
{
  State    = S;	// Starting state
  Waits    = W; // Starting waitmap
  Priority = P; // Starting priority

  TaskToken N; // Find 1st TaskToken
  for (N = 0; Taskz.Ptr[N] != 0; N++) ;
  Number       = N;    // Point task to slot
  Taskz.Ptr[N] = this; // Point slot to task

  if (W == 0) Activate(); // Add to active task list(s)
};

Tasks::~Tasks()
{
  if (Waits == 0) Deactivate();
  // Remove from active task lists
  Taskz.Ptr[Number] = 0;
  // Clear the task slot
};

void Tasks::SetPriority(Priorities P)
{
  int Moved = ((Priority == 0) != (P == 0)) &&
  	      (Waits == 0);
  // Has the task moved from Foreground to Background
  // or vice versa?
  if (Moved) Deactivate(); // Remove from current list
  Priority = P;            // Set new priority
  if (Moved) Activate();   // Place on new list
};

void Tasks::Activate()
{
  PTaskList List = (Priority == 0)  ?
                   & Taskz.Background :
		   & Taskz.Foreground ;
  if (!List->ContainsP(Number)) // If not in list
    List->Append(Number);       // then add it
};

void Tasks::Deactivate()
{
  PTaskList List = (Priority == 0)  ?
  		   & Taskz.Background :
		   & Taskz.Foreground ;
  List->Delete(Number); // Delete it iff present
};

// MSM 'engine' code

TaskPtr ThisTask = 0;

MSM::MSM() // Initialize MSM 'engine'
{
  for (TaskToken Idx = 0;
       Idx < NTasks;
       Taskz.Ptr[Idx++] = 0) ;
  // Clear all slots
};

void MSM_Run() // Run the 'engine'
{
  Taskz.Tasking = On;
  while (Taskz.Tasking == On)
    if (Taskz.Foreground.Length != 0) {
      // Run foreground tasks
      Priorities Level;
      int        Idx, NextLevel;


      for (NextLevel = 1, Level = 1;
           NextLevel;
	   Level++) {
	NextLevel = 0; // No GetPriority() > Level yet
        for (Idx = Taskz.Foreground.Length - 1;
	     Idx >= 0; Idx--) {
          // Execute most-recently-awoken tasks first
          ThisTask =
	    Taskz.Ptr[Taskz.Foreground.List[Idx]];
          if (ThisTask->GetPriority() >= Level)
	    (*ThisTask->GetState())();
    	    // Execute state handler
          NextLevel = NextLevel ||
	              ThisTask->GetPriority() > Level;
          // NextLevel == 1 as soon any Priority>Level
          };
        };
      } else {
      // Run background tasks
      int Idx = Taskz.Background.Length - 1;
      while (Idx >= 0) {
        ThisTask =
	  Taskz.Ptr[Taskz.Background.List[Idx--]];
        (*ThisTask->GetState())(); // Execute handler
        };
      };
};

void MSM_Stop() // Handlers can call to stop the engine
{
  Taskz.Tasking = Off;
};

TaskPtr Task(TaskToken TT)
{
  return Taskz.Ptr[TT];
}
WRAP_EOF

