home *** CD-ROM | disk | FTP | other *** search
- //--------------------------------------------------------------------------
- //
- // COROUTINE.CPP: body of DOS coroutine library.
- // Copyright (c) J.English 1993.
- // Author's address: je@unix.brighton.ac.uk
- //
- // Permission is granted to use copy and distribute the
- // information contained in this file provided that this
- // copyright notice is retained intact and that any software
- // or other document incorporating this file or parts thereof
- // makes the source code for the library of which this file
- // is a part freely available.
- //
- //--------------------------------------------------------------------------
- //
- // Note: this library is highly DOS specific and hence non-portable.
- // It also involves the use of assembly language and interrupt
- // functions, so it is also very compiler-specific and will need
- // modification for use with compilers other than Borland C++.
- //
- // Revision history:
- // 1.0 April 1993 Initial coding
- // 1.1 June 1993 Added wait, terminate; tidied private data
- // Minor changes to schedule.
- //
- //--------------------------------------------------------------------------
-
- #include "coroutine.h"
- #include <dos.h>
-
-
- //--------------------------------------------------------------------------
- //
- // Constants.
- //
- const unsigned MIN_STACK = 512; // minimum stack size
-
-
- //--------------------------------------------------------------------------
- //
- // Global (static) variables.
- //
- static CoroutineManager* current = 0; // current coroutine
- static CoroutineManager* nextentry = 0; // current coroutine
- static Coroutine* mainitem = 0; // coroutine for execution of "main"
- static unsigned mainsetup = 0; // flag set when constructing "main"
- static volatile unsigned count = 0; // number of active coroutines
-
-
- //--------------------------------------------------------------------------
- //
- // Class MainCoroutine.
- //
- // A minimal derivation of Coroutine used for the main task.
- //
- class MainCoroutine : public Coroutine
- {
- public:
- MainCoroutine () : Coroutine (MIN_STACK) { }
-
- protected:
- virtual void main () { }
- };
-
- //--------------------------------------------------------------------------
- //
- // Class CoroutineManager.
- //
- // This is a support class used for maintaining the coroutine queue.
- // The queue is represented as a circular list of CoroutineManagers.
- //
- class CoroutineManager
- {
- public:
- CoroutineManager (Coroutine* t);
- ~CoroutineManager () { delete stack; }
-
- CoroutineManager* next; // next entry in list
- CoroutineManager* prev; // previous entry in list
- Coroutine* coroutine; // coroutine for this entry
- char* stack; // coroutine stack area
- unsigned sp,ss; // coroutine stack pointer
-
- static void far start (Coroutine* t); // execute coroutine and then die
-
- static void create (); // register coroutine creation
- static void destroy (); // register coroutine destruction
-
- static void interrupt schedule (); // schedule next coroutine
- };
-
-
- //--------------------------------------------------------------------------
- //
- // CoroutineManager::CoroutineManager.
- //
- // Constructor for class CoroutineManager.
- //
- inline CoroutineManager::CoroutineManager (Coroutine* t)
- {
- next = prev = this;
- coroutine = t;
- stack = 0;
- }
-
-
- //--------------------------------------------------------------------------
- //
- // CoroutineManager::start.
- //
- // This is used to execute a coroutine by executing "main" and
- // then terminating it.
- //
- void far CoroutineManager::start (Coroutine* t)
- {
- t->main ();
- t->terminate ();
- }
-
- //--------------------------------------------------------------------------
- //
- // CoroutineManager::create.
- //
- // Register the creation of a coroutine and initialise the system
- // if it is the first coroutine to be created.
- //
- void CoroutineManager::create ()
- {
- //--- increment the number of coroutines & initialise if necessary
- if (count++ == 0)
- {
- //--- create the main coroutine (bodged using "mainsetup")
- mainsetup = 1;
- mainitem = new MainCoroutine;
- mainsetup = 0;
- mainitem->state = Coroutine::RUNNING;
-
- //--- select main coroutine as the current one
- current = mainitem->entry;
- }
- }
-
-
- //--------------------------------------------------------------------------
- //
- // CoroutineManager::destroy.
- //
- // Register the destruction of a coroutine and stop scheduling if
- // the last coroutine is being destroyed.
- //
- void CoroutineManager::destroy ()
- {
- //--- decrement coroutine count
- count--;
-
- //--- terminate main coroutine when no others left
- if (count == 0)
- { current = 0;
- mainitem->state = Coroutine::TERMINATED;
- delete mainitem;
- }
- }
-
- //--------------------------------------------------------------------------
- //
- // CoroutineManager::schedule.
- //
- // Save the current coroutine and restore another one.
- //
- void interrupt CoroutineManager::schedule ()
- {
- //--- don't schedule if no coroutines present
- if (current == 0)
- return;
-
- //--- switch context to "nextentry"
- asm { cli; }
- _SP = _BP;
- current->sp = _SP;
- current->ss = _SS;
- current = nextentry;
- _SS = current->ss;
- _SP = current->sp;
- _BP = _SP;
- asm { sti; }
- }
-
- //--------------------------------------------------------------------------
- //
- // Coroutine::Coroutine.
- //
- // Construct a new coroutine. All new coroutines are kept in limbo
- // until they are explicitly started using "run".
- //
- Coroutine::Coroutine (unsigned stacksize)
- : entry (new CoroutineManager (this)),
- state (TERMINATED)
- {
- //--- leave coroutine terminated if coroutine manager not allocated
- if (entry == 0)
- return;
-
- //--- set up new coroutine (for all but main coroutine)
- entry->stack = 0;
- if (!mainsetup)
- {
- //--- allocate coroutine stack area
- if (stacksize < MIN_STACK)
- stacksize = MIN_STACK;
- entry->stack = new char [stacksize];
- if (entry->stack == 0)
- return;
-
- //--- register coroutine creation
- CoroutineManager::create ();
-
- //--- create coroutine stack
- unsigned* stk = (unsigned*)(entry->stack + stacksize);
- *--(Coroutine**)stk = this; // "this" pointer for call to "start"
- stk -= 2; // dummy return address from "start"
- *--stk = _FLAGS; // flags
- *--stk = FP_SEG (&CoroutineManager::start);
- *--stk = FP_OFF (&CoroutineManager::start);
- stk -= 5; // ax, bx, cx, dx, es
- *--stk = _DS; // ds
- stk -= 2; // si, di
- *--stk = _BP; // bp
- entry->ss = FP_SEG (stk);
- entry->sp = FP_OFF (stk);
-
- //--- set coroutine state
- state = CREATED;
- }
- }
-
- //--------------------------------------------------------------------------
- //
- // Coroutine::~Coroutine.
- //
- // Wait for current coroutine to terminate, and then destroy it.
- //
- Coroutine::~Coroutine ()
- {
- //--- wait for coroutine to terminate
- wait ();
-
- //--- register coroutine destruction (normal coroutines only)
- if (this != mainitem)
- CoroutineManager::destroy ();
-
- //--- delete associated structures
- delete entry;
- }
-
-
- //--------------------------------------------------------------------------
- //
- // Coroutine::run.
- //
- // Start a new coroutine running.
- //
- int Coroutine::run ()
- {
- //--- don't start coroutine if not newly created
- if (state != CREATED)
- return 0;
-
- //--- link coroutine into queue
- entry->next = current->next;
- entry->prev = current;
- entry->prev->next = entry;
- entry->next->prev = entry;
-
- //--- start coroutine running
- state = RUNNING;
- nextentry = entry;
- CoroutineManager::schedule ();
- return 1;
- }
-
- //--------------------------------------------------------------------------
- //
- // Coroutine::terminate.
- //
- // This terminates a coroutine by removing it from the queue. If
- // a coroutine calls it to terminate itself, the next coroutine
- // is selected and then scheduled.
- //
- void Coroutine::terminate ()
- {
- //--- check if coroutine is running and reset its state
- int running = (state == RUNNING);
- state = TERMINATED;
- if (!running)
- return;
-
- //--- select next coroutine to run if necessary
- if (entry == current)
- nextentry = current->next;
-
- //--- detach coroutine from the queue
- entry->next->prev = entry->prev;
- entry->prev->next = entry->next;
-
- //--- schedule next coroutine if necessary
- if (entry == current)
- CoroutineManager::schedule ();
- }
-
-
- //--------------------------------------------------------------------------
- //
- // Coroutine::wait.
- //
- // Wait for a coroutine to terminate. If it's the current coroutine
- // waiting for itself to terminate, grant its wish by terminating it.
- // If it hasn't been started yet, terminate it immediately.
- //
- void Coroutine::wait ()
- {
- if (current == entry)
- terminate ();
- if (state == CREATED)
- state = TERMINATED;
- while (state != TERMINATED)
- pause ();
- }
-
-
- //--------------------------------------------------------------------------
- //
- // Coroutine::pause.
- //
- // Schedule the next available coroutine.
- //
- void Coroutine::pause ()
- {
- nextentry = current->next;
- CoroutineManager::schedule ();
- }
-