Email: Password: Remember Me | Create Account (Free)

Back to Subject List

Old thread has been locked -- no new posts accepted in this thread
???
09/24/08 16:51
Read: times


 
Msg Score: +1
 +1 Informative
#158575 - An alternative
Responding to: ???'s previous message
Maarten Brock said:
What usually goes hand-in-hand with a super-loop is the notion of state-machines. Every 'thread' in your mindset can be implemented as a state-machine. And all state-machines are called sequentially in the super-loop. Obviously the state-machines may not lock up. Together they perform a form of cooperative multitasking.

Or, you could just go ahead and use a real cooperative
multitasker.

To save somone else the trouble of pointing out the obvious, I am
fully aware that the 8051 architecture isn't the best for this
sort of thing.  I am also fully aware of the difficulties
surrounding the use of function pointers with some 8051 C
compilers.  That said, however, it can be done, as shown
below.  Whether or not it's a good idea in any particular
situation is a matter of engineering.

Please view what follows as a detailed sketch, and not as
ready-to-run code, for two reasons:

1.  A bit of it depends on symbols defined in header files that I
    have not supplied.

2.  A bit of it is compiler dependent.

-- Russ

-------------------------------------------------------------------

What Is 8051Task?
-----------------

    8051Task is a library of C functions that lets you run one or
    more C functions together in a priority-based, cooperative
    (non-preemptive) multitasking environment.  8051Task provides
    full support for intertask synchronization, intertask
    communication, and timed delays.

    8051Task provides a more capable scheduler and better
    intertask communication than do simple round-robin task
    switchers such as Wayne Conrad's MTASK or the system described
    in the October, 1988 issue of "Computer Language" magazine,
    while avoiding the complexity of a full-blown
    interrupt-driven, preepmtive system like Thomas Wagner's
    CTask.

What is Multitasking?
---------------------

    Multitasking is a way to divide a single program into several
    distinct threads of execution called "tasks" which execute
    concurrently under the control of a supervisory program called
    a "scheduler".  The tasks are usually somewhat independent of
    one another (although they can communicate among themselves)
    and appear to execute all at once.  On a single-CPU system, of
    course, only one task can be running at any given instant, but
    the scheduler passes control to the various tasks in a way
    that gives the illusion that they are all running at the same
    time.

    Multitasking as implemented by 8051Task is not to be confused
    with multiprogramming (as provided by Windows or an operating
    system like QNX), in which multiple -programs- appear to run
    at the same time, or with multiprocessing, which requires a
    computer with more than one CPU.  When you use 8051Task, you
    compile and link all your tasks together into a single program
    which works just like any other program.

Preemptive vs. Cooperative Multitasking
---------------------------------------

    In a "preemptive" multitasking system, the scheduler doles out
    CPU time to the tasks in chunks called "time slices".  Each
    task is allowed to execute for a specific time period, after
    which control is wrested from it by the timer interrupt and
    given to some other task by the scheduler.  The advantage of
    such a scheme is that tasks can be written with little or no
    awareness of the multitasking system.  The downside, however,
    is that it can be difficult to make sure that tasks are not
    interrupted at inappropriate times.  As an example, consider a
    task which has just made a call to the C runtime library.  If
    it is interrupted while it is within the library routine, and
    control is then passed to a different task which also calls
    the library, havoc will reign shortly if the library is not
    reentrant.

    In a "cooperative" multitasking environment, a task that is
    running continues to run until it explicitly relinquishes
    control by making a call to the multitasking system.  As a
    result, such tasks must be written so that they do in fact
    give up the CPU appropriately.  Concern about being preempted
    at a bad time is eliminated, however, because the tasks
    surrender control voluntarily at times of their own choosing
    rather than having the rug jerked from under them by an
    interrupt.

    8051Task implements cooperative multitasking.

How Does 8051Task Work?
-----------------------

    8051Task tasks are written as independent C functions which
    are linked together with functions from the 8051Task library
    (and possibly other libraries) to form the complete
    application.  The tasks are rarely (if ever) called explicitly
    by user-written code.  Instead, each task is identified to the
    8051Task scheduler, usually when the application is first
    started, by a call to create_task().  From then on, the
    scheduler takes care of placing the tasks into execution,
    based on events within the application and upon the tasks'
    relative priorities.

    A key concept in understanding how the scheduler works is that
    of a task's "state".  At any instant, each task is in exactly
    one of the following states:

    RUNNING     The RUNNING task is the one currently executing.
                There can obviously be only one RUNNING task,
                since there is only one CPU.
                        
    READY       READY tasks are those which are not currently
                blocked from execution waiting for some external
                event.  At each scheduling period, the READY task
                with the highest priority is made the RUNNING task
                and is placed into execution.  If several READY
                tasks all have the same priority, they will run in
                a round-robin fashion.

    SLEEPING    SLEEPING tasks are those blocked from execution
                until some specified future time.  SLEEPING tasks
                are made READY when the specified time arrives.

    WAITING     WAITING tasks are those waiting for some event to
                occur, such as a message from another task,
                availability of a character from the keyboard, or
                some interrupt or other.  WAITING tasks may also
                have a timeout associated with them, in which case
                they will wait only for a specified time before
                being made READY again.

    When a task is first created, it is placed in the READY state,
    and it will begin to execute as soon as all other
    higher-priority tasks become blocked (either SLEEPING or
    WAITING).  It will continue to run until it actively
    relinquishes control, either explicitly by a call to yield()
    or suspend(), or implicitly by a call to one of the other
    8051Task functions.

8051Task Function Overviews
---------------------------

    This section gives a brief functional description of each
    8051Task call, and is organized with related functions grouped
    together.  Complete details are to be found in the source
    code.

Overhead Functions
------------------
          
    start_8051Task()    Initializes the 8051Task software.  This
                        function must be called before any other
                        8051Task functions are used.

Task Manipulation
-----------------
          
    create_task()       Establishes a specified C function as an
                        8051Task task and makes it READY.

    kill_task()         Kills a specified task.

    yield()             Makes the calling task READY and then
                        invokes the scheduler.  Use of this call
                        is the key to the "cooperative" nature of
                        8051Task scheduling.  If a task plans to
                        run for a long time without either waiting
                        for an event or going to sleep, it should
                        call yield periodically to give tasks with
                        higher priority a chance to use the CPU.

    suspend()           Suspends execution of the calling task for
                        a specified time.

Mailbox Functions
-----------------

    Tasks can send messages of unspecified format to each other
    using mailboxes.  Mailboxes provide a FIFO queue of messages
    which is initially empty.  Messages sent to a mailbox are
    simply added to the queue, while tasks reading from the
    mailbox wait until a message is present and get the oldest
    such message.

    create_mailbox()    Creates a mailbox.

    send_mail()         Sends a message to a mailbox.

    get_mail()          Waits until a message is present at a
                        mailbox, and returns it to the caller.  A
                        timeout may be specified.
                
    check_mail()        Checks if a mailbox contains anything or
                        not.

Semaphore Functions
-------------------

    Tasks can synchronize with each other for resource sharing and
    so on using semaphores.  Once created, each semaphore
    maintains a count.  Tasks signalling the semaphore increase
    the count by one, and whenever a task waiting at the semaphore
    is made READY, the count is decreased by one.  The count can
    be initialized to any desired value when the semaphore is
    created.

    create_sem()        Creates a semaphore.

    signal_sem()        Signals a semaphore.

    wait_sem()          Waits for a semaphore.  A timeout may be
                        specified.

    check_sem()         Returns the value of a semaphore's count.

Miscellaneous Functions
-----------------------

    get_status()        Returns the termination status (either
                        TIMEOUT or OKAY) of the most recent call
                        to suspend(), get_mail(), wait_key(), or
                        wait_sem().

/* ////////////////////////////////////////////////////////////////////////////
                                  8051task.h
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    This is the external interface to the 8051 multitasking system.

REVISIONS:      12 Jun 05 - RAC - Genesis
//////////////////////////////////////////////////////////////////////////// */

#ifndef TASK8051_H
#define TASK8051_H

/* ////////////////////////////////////////////////////////////////////////////
                               Names for Numbers
//////////////////////////////////////////////////////////////////////////// */

#define STANDARD_PRIORITY       100     /* Priority of Joe Average task */
enum { OKAY, TIMEOUT };                 /* Wait return status values */

/* ////////////////////////////////////////////////////////////////////////////
                          Data Structure Definitions
//////////////////////////////////////////////////////////////////////////// */

#define TDESC   struct task_descriptor

TDESC {                                 /* The Task Descriptor */
    char        taskName[20];           /* Text task name for debugging */
    TDESC       *prevWait;              /* Links to other tasks waiting at a */
    TDESC       *nextWait;              /*  mailbox or semaphore */
    TDESC       *prevTime;              /* Links to other tasks waiting on */
    TDESC       *nextTime;              /*  the timer */
    long        timeout;                /* Timer expiration time */
    unsigned    priority;               /* 0 = lowest, 0xFFFF = highest */
    char        taskNumber;             /* My task number */
    unsigned    mySP;                   /* Saved SP register */
    unsigned    waitStatus;             /* Status of last wait operation, or */
                                        /*  semaphore count */
    void        *msg;                   /* Pointer to place to put mail, or */
                                        /*  to oldest queued mailbox message */
    union {
        unsigned   *mailSize;           /* Pointer to place to put mail size */
        unsigned   semCount;
        } tt;
    };

/* ////////////////////////////////////////////////////////////////////////////
                             Function Prototypes
//////////////////////////////////////////////////////////////////////////// */

void        Start8051Task(void);
void        Suspend(long);
char        CreateTask(char *, void (*)(), unsigned);
void        Yield(void);
void        KillTask(char);
int         SendMail(void *, unsigned, TDESC*);
void        GetMail(void *, unsigned *, TDESC*, long, int);
int         CheckMail(TDESC *);
int         GetStatus(void);
void        SignalSem(TDESC *);
void        WaitSem(TDESC *, long);
unsigned    CheckSem(TDESC *);
long        GetTicks(void);
long        GetTicksI(void);

#endif  /* End #ifndef TASK8051_H */

/* ////////////////////////////////////////////////////////////////////////////
                                  8051task.c
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    This is the implementation of the 8051 multitasking system.

REVISIONS:      12 Jun 05 - RAC - Genesis
//////////////////////////////////////////////////////////////////////////// */

#include <stdlib.h>
#include <string.h>
#include "8051task.h"

/* ////////////////////////////////////////////////////////////////////////////
                               Names for Numbers
//////////////////////////////////////////////////////////////////////////// */

/*  This is the maximum number of tasks that can be in the system at one time.
    Note that main() counts as a task unless and until it returns.  Change this
    number and recompile to suit the application.  (Each task uses about 300
    bytes of external RAM, so it's worthwhile to minimize NUM_TASKS if RAM is
    in short supply.)  */

#define NUM_TASKS       10

/* ////////////////////////////////////////////////////////////////////////////
                          Data Structure Definitions
//////////////////////////////////////////////////////////////////////////// */

#define MQE struct msg_queue_entry

MQE {
    MQE         *nextMsg;               /* Link to next message in mailbox */
    unsigned    msgSize;                /* Size of this message */
    char        *msgText;               /* Pointer to message text */
    };    

/* ////////////////////////////////////////////////////////////////////////////
                         External Function Prototypes
//////////////////////////////////////////////////////////////////////////// */

void SaveStack(char *);                 /* These are defined in stackers.s03 */
void RestoreStack(char *);
unsigned Fixit(void (*)());

/* ////////////////////////////////////////////////////////////////////////////
                           Local Function Prototypes
//////////////////////////////////////////////////////////////////////////// */

static void TimerLink(TDESC *);
static void Schedule(void);
static void WaitUnlink(TDESC *);
static void TimeUnlink(TDESC *);
static void ReadyLink(TDESC *);
static void AutoKill(void);

/* ////////////////////////////////////////////////////////////////////////////
                               Module Variables
//////////////////////////////////////////////////////////////////////////// */

TDESC           rlh;                    /* Dummy READY list header */
TDESC           tlh;                    /* Dummy timer list head */
TDESC           tasks[NUM_TASKS];       /* The task descriptors */
char            stacks[NUM_TASKS][256]; /* Task stack images */
TDESC           *currentTask;           /* Ptr to the RUNNING task */
long            gnow;                   /* Ticks since timers started */
unsigned        tickCounter;            /* Count clock ticks here */

/* ////////////////////////////////////////////////////////////////////////////
                                Start8051Task()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Initializes the multitasker.  This function must be called
                before any other 8051task functions are used.

REVISIONS:      12 Jun 05 - RAC - Adapted from the similar RMAXTask routine
//////////////////////////////////////////////////////////////////////////// */

void Start8051Task() {

    char        j;                              /* A generic integer */

    rlh.priority = 0xFFFF;                      /* Keep READY header first */
    for (j=0; j<NUM_TASKS; j++) {               /* Mark all the tasks as */
        tasks[j].taskNumber = 255;              /*  available */
        }
    strcpy(tasks[0].taskName, "main");          /* Set up the main task */
    tasks[0].priority = STANDARD_PRIORITY;      /*  Standard user priority */
    tasks[0].taskNumber = 0;                    /*  Its descriptor is in use */
    currentTask = tasks + 0;                    /* Make it the current task */    
    }                                           /* End Start8051Task() */

/* ////////////////////////////////////////////////////////////////////////////
                                   Suspend()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Suspends a tasks for a given period of time.  The time is given
                in clock ticks, which occur at a rate determined by the timer
                interrupt handler.

REVISIONS:      12 Jun 05 - RAC - Adapted from the similar RMAXTask function
//////////////////////////////////////////////////////////////////////////// */

void Suspend(
    long        ticks                   /* Sleep for <ticks> clock ticks */
    ) {
    currentTask->timeout = ticks + gnow;        /* Timer expiration time */
    TimerLink(currentTask);                     /* Stick in timer list */
    Schedule();                                 /* Go do something else */
    }                                           /* End suspend() */

/* ////////////////////////////////////////////////////////////////////////////
                                  TimerLink()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Internal routine to add entries to the timer list.

REVISIONS:      12 Jun 05 - RAC - Adapted from the similar RMAXTask function
//////////////////////////////////////////////////////////////////////////// */

static void TimerLink(TDESC *neu) {

    TDESC       *before;                        /* Temp TDESC pointers */
    TDESC       *after;

    before = &tlh;                              /* Start at timer header */
    after = before->nextTime;
    while (after != NULL &&                     /* Search for where to put */
           after->timeout < neu->timeout) {     /*  the new entry */
        before = after;
        after = after->nextTime;
        }
    neu->prevTime = before;                     /* Add the new guy to the */
    neu->nextTime = after;                      /*  list */
    before->nextTime = neu;
    if (after != NULL)
        after->prevTime = neu;    
    }                                           /* End TimerLink() */

/* ////////////////////////////////////////////////////////////////////////////
                                 TimeUnlink()
                                 WaitUnlink()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Internal routines to remove entries from the timer queue or one
                of the wait queues.

REVISIONS:      12 Jun 05 - RAC - Adapted from the similar RMAXTask functions
//////////////////////////////////////////////////////////////////////////// */

#define A B->prevTime
#define C B->nextTime

static void TimeUnlink(TDESC *B) {              /* The entry to remove */
    if (A != NULL) {                            /* If it is in fact queued, */
        A->nextTime = C;                        /*  wire pointers around */
        if (C != NULL) C->prevTime = A;         /*  it, and then */
        A = C = NULL;                           /*  unlink it completely */
        }                                       /* End 'it is queued' */
    }                                           /* End TimeUnlink() */  

#undef A
#undef C

#define A B->prevWait
#define C B->nextWait

/* ///////////////////////////////////////////////////////////////////////// */

static void WaitUnlink(TDESC *B) {              /* The entry to remove */
    if (A != NULL) {                            /* If it is in fact queued, */
        A->nextWait = C;                        /*  wire pointers around */
        if (C != NULL) C->prevWait = A;         /*  it, and then */
        A = C = NULL;                           /*  unlink it completely */
        }                                       /* End 'it is queued' */
    }                                           /* End WaitUnlink() */  

#undef A
#undef C

/* ////////////////////////////////////////////////////////////////////////////
                                  ReadyLink()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Internal routine to add entries to the READY list.

REVISIONS:      12 Jun 05 - RAC - Adapted from the similar RMAXTask function
//////////////////////////////////////////////////////////////////////////// */

static void ReadyLink(TDESC *neu) {

    TDESC       *before;                        /* Temp TDESC pointers */
    TDESC       *after;

    before = &rlh;                              /* Start at READY header */
    after = before->nextWait;
    while (after != NULL &&                     /* Search for where to put */
           after->priority >= neu->priority) {  /*  the new entry */
        before = after;
        after = after->nextWait;
        }
    neu->prevWait = before;                     /* Link new guy into the */
    neu->nextWait = after;                      /*  READY list */
    before->nextWait = neu;
    if (after != NULL)
        after->prevWait = neu;    
    }                                           /* End ready_link() */

/* ////////////////////////////////////////////////////////////////////////////
                                  Schedule()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Where the rubber meets the road.  This routine does some
                housekeeping related to the timer, saves the context of the
                RUNNING task, then activates the READY task with the highest
                priority.

REVISIONS:      12 Jun 05 - RAC - Adapted from the similar RMAXTask function
//////////////////////////////////////////////////////////////////////////// */

static void Schedule() {

    unsigned    tc;                             /* Copy of tick counter */
    TDESC       *top;                           /* Ptr to 1st timer q entry */

    do {                                        /* Do 'til task is READY */

    /*  If a recent timer tick has occurred, make READY all the tasks in the
        timer queue whose time has expired as a result of the recent tick.  */

        if (tickCounter) {                      /* If a tick has happened */
            ET0 = 0;                            /* Timer 0 interrupt off */
            tc = tickCounter;                   /* Read and reset the "real" */
            tickCounter = 0;                    /*  tick counter */
            ET0 = 1;                            /* Timer interrupt back on */

            gnow += tc;                         /* Update local time */
            for (;;) {                          /* We'll break from this */
                top = tlh.nextTime;             /* 1st timer queue entry */
                if ((top == NULL) ||            /* If no entry, or it has */
                    (top->timeout > gnow)) {    /*  not yet timed out, we're */
                    break;                      /*  done */
                    }
                TimeUnlink(top);                /* Yank task from timer list */
                WaitUnlink(top);                /*  and wait list, too */
                top->waitStatus = TIMEOUT;      /* It timed out */
                ReadyLink(top);                 /* Add to READY list */
                }                               /* End 'for expired timers' */
            }                                   /* End 'a tick has happened' */
        } while (rlh.nextWait == NULL);         /* End 'do til task READY' */


/*  Save the current task's stack and stack pointer */

    SaveStack(&(stacks[currentTask->taskNumber][0]));
    currentTask->mySP = SP;

/*  Switch to the new task  */

    currentTask = rlh.nextWait;                 /* The new current task */
    rlh.nextWait = currentTask->nextWait;       /* Unlink it from READY list */
    if (currentTask->nextWait != NULL) {
        currentTask->nextWait->prevWait = &rlh;
        }
    currentTask->prevWait = NULL;               /* Unlink it completely */
    currentTask->nextWait = NULL;

/*  Restore the new task's stack and stack pointer */

    SP = currentTask->mySP;
    RestoreStack(&(stacks[currentTask->taskNumber][0]));
    }                                           /* End Schedule() */

/* ////////////////////////////////////////////////////////////////////////////
                                   AutoKill()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Internal function that kills tasks that return from their main
                level functions.

REVISIONS:      26 Jun 05 - RAC - Adapted from the similar RMAXTask function.
//////////////////////////////////////////////////////////////////////////// */

void AutoKill() {
    currentTask->taskNumber = 255;              /* Mark descr as available */
    Schedule();                                 /* Press on */
    }                                           /* End AutoKill() */

/* ////////////////////////////////////////////////////////////////////////////
                                  KillTask()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Allows one task to murder another.

NOTES:          This function must be used with extreme care to make sure that
                the task being killed has his affairs in order.  He should not
                "own" any dynamic memory, he should not have any open files,
                etc.
                                                                             
                A task should NOT use this function to kill itself.  Tasks
                wishing to commit suicide should do so by simply returning from
                themselves, either by an explicit return or by simply running
                off the end of themselves.  Tasks contemplating suicide should
                also make sure their affairs are in order as noted above.

REVISIONS:      26 Jun 05 - RAC - Adapted from the similar RMAXTask function.
//////////////////////////////////////////////////////////////////////////// */

void KillTask(char victim) {
    TimeUnlink(tasks + victim);
    WaitUnlink(tasks + victim);
    tasks[victim].taskNumber = 255;             /* Mark descr as available */
    }                                           /* End KillTask() */

/* ////////////////////////////////////////////////////////////////////////////
                                 CreateTask()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Establishes a specified C function as an 8051Task task and
                makes it READY.

REVISIONS:      16 Jun 05 - RAC - Adapted from the similar RMAXTask function.
//////////////////////////////////////////////////////////////////////////// */

char CreateTask (                       /* New task number, or 255 if error */
    char        *name,                  /* Pointer to text name for task */
    void        (*fn)(),                /* Ptr to the code for the task */
    unsigned    priority                /* Desired task priority */
    ) {

    char        j;                      /* A generic integer */
    unsigned    u;                      /* A generic unsigned */

/*  Find an available task descriptor, or die trying */

    j = 0;                                      /* Start with the first task */
    while (1) {                                 /* We'll break from this */
        if (tasks[j].taskNumber==255) {         /* Abandon loop as soon as */
            break;                              /*  we find an available */
            }                                   /*  descriptor */
        if (++j == NUM_TASKS) {                 /* Abandon ship if there are */
            return 255;                         /*  no available descriptors */
            }
        }                                       /* End 'while (1)' */

/*  Now just set up the descriptor and stick it in the READY list.  */

    strcpy(tasks[j].taskName, name);
    tasks[j].priority = priority;               /* Caller-specified priority */
    tasks[j].taskNumber = j;                    /* Task # = tasks[] index */
    tasks[j].mySP = 11;                         /* Room for two return addrs */

    u = Fixit(AutoKill);                        /* Get pointer to AutoKill */
    stacks[j][8] = u & 0xFF;                    /* Set up to kill task if it */
    stacks[j][9] = (u >> 8) & 0xFF;             /*  ever returns */

    u = Fixit(fn);
    stacks[j][10] = u & 0xFF;                   /* Set up so we can just */
    stacks[j][11] = (u >> 8) & 0xFF;            /* return to the new task */
    ReadyLink(tasks+j);                         /* Put in the ready list */    
    return j;                                   /* Return the task number */
    }                                           /* End CreateTask() */

/* ////////////////////////////////////////////////////////////////////////////
                                    Yield()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Makes the calling task READY and then invokes the scheduler.
                If a task plans to run for a long time without either waiting
                for an event or going to sleep, it should call this routine
                periodically to allow tasks with equal or higher priority the
                chance to run.

REVISIONS:      16 Jun 05 - RAC - Adapted from the similar RMAXTask function.
//////////////////////////////////////////////////////////////////////////// */

void Yield() {
    ReadyLink(currentTask);
    Schedule();
    }                                           /* End yield() */

/* ////////////////////////////////////////////////////////////////////////////
                                  SendMail()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Sends a message to a mailbox.

REVISIONS:      26 Jun 05 - RAC - Adapted from the similar RMAXTask function.
//////////////////////////////////////////////////////////////////////////// */

int SendMail (                          /* 1 if all is well, 0 if error */
    void        *msg,                   /* Pointer to message to send */
    unsigned    size,                   /* Size of message to send */
    TDESC       *mailbox                /* Pointer to destination mailbox */
    ) {

    TDESC       *wt;                            /* Ptr to waiting task */
    MQE         *mqep;                          /* Temp message queue ptr */

    if ((wt = mailbox->nextWait) != NULL) {     /* There is a task waiting! */
        memcpy(wt->msg,                         /* Copy message to that */
               msg,                             /*  task's buffer */
               size);
        *(wt->tt.mailSize) = size;              /* Return size as well */
        TimeUnlink(wt);                         /* Yank task from timer list */
        WaitUnlink(wt);                         /*  and wait list, too */
        wt->waitStatus = OKAY;                  /* The read was successful */
        ReadyLink(wt);                          /* Add to READY list */
        }                                       /* End 'task is waiting */
    else {                                      /* No task waiting */
        if (mailbox->msg == NULL) {             /* This is 1st message */
            mailbox->msg = calloc(1, sizeof(MQE)); /* Make space for new */
            if (mailbox->msg == NULL)              /*  message, or die */
                return 0;                          /*  trying */
            mqep = (MQE *)mailbox->msg;         /* Go to new message */
            }                                   /* End '1st message' */
        else {                                  /* Not 1st message */
            mqep = (MQE *)mailbox->msg;         /* Point at 1st message */
            while (mqep->nextMsg != NULL)       /* Find end of list of */
                mqep = mqep->nextMsg;           /*  messages in this mailbox */
            mqep->nextMsg = (MQE *)calloc(1, sizeof(MQE)); /* Make space */
            if (mqep->nextMsg == NULL)          /*  for new message, or die */
                return 0;                       /*  trying */
            mqep = mqep->nextMsg;               /* Success ... go to new msg */
            }                                   /* End 'not 1st message' */
        mqep->nextMsg = NULL;                   /* Terminate chain */
        mqep->msgSize = size;                   /* Set up message size */
        mqep->msgText = (char *)calloc(1, size); /* Allocate space for */
        if (mqep->msgText == NULL)              /*  message text, or die */
            return 0;                           /*  trying */
        memcpy(mqep->msgText,                   /* Copy actual message to */
               msg,                             /*  the list */
               size);   
        }                                       /* End 'no task waiting' */    
    return 1;                                   /* Success */
    }                                           /* End send_mail() */

/* ////////////////////////////////////////////////////////////////////////////
                                   GetMail()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Waits at a mailbox for mail or a timeout.                    *

REVISIONS:      26 Jun 05 - RAC - Adapted from the similar RMAXTask function.
//////////////////////////////////////////////////////////////////////////// */

void GetMail(
    void        *recipMsg,              /* Where to put result */
    unsigned    *mailSize,              /* Where to put size of result */
    TDESC       *mailbox,               /* Pointer to mailbox to use */
    long        timeout,                /* Give up this many ticks from now */
    int         reverse                 /* Reverse normal queueing order */
    ) {

    MQE         *mqep;                          /* Temp message queue ptr */

    mqep = (MQE *) mailbox->msg;
    if (mqep != NULL) {                         /* There is mail */
        memcpy(recipMsg,                        /* Copy it to recipient */
               mqep->msgText,
               mqep->msgSize);
        *mailSize = mqep->msgSize;              /* Return size as well */
        mailbox->msg = mqep->nextMsg;           /* Unlink the mail just read */
        free(mqep->msgText);                    /* Free message text */
        free(mqep);                             /* Free message queue entry */
        currentTask->waitStatus = OKAY;         /* The read was successful */
        }                                       /* End 'there is mail' */
    else {                                      /* There is no mail */
        if (!reverse)
            while (mailbox->nextWait != NULL)   /* Find end of list of tasks */
                mailbox = mailbox->nextWait;    /*  waiting on this mailbox, */
        currentTask->nextWait = mailbox->nextWait; /* Terminate the list */
        mailbox->nextWait = currentTask;        /*  and add the current task */
        currentTask->prevWait = mailbox;        /* Complete linkage */
        if (currentTask->nextWait != NULL)      /* RAC 041292 */
            (currentTask->nextWait)->prevWait   /* RAC 041292 */
                = currentTask;                  /* RAC 041292 */
        currentTask->msg = recipMsg;            /* Note where the result */
        currentTask->tt.mailSize = mailSize;    /*  should be put eventually */
        if (timeout) {                          /* If timeout was given, */
            currentTask->timeout =              /*  figure out when timer */
                timeout + gnow;                 /*  should expire */
            TimerLink(currentTask);             /* Stick in timer list */
            }
        Schedule();                             /* Go do something else */
        }                                       /* End 'no mail' */
    }                                           /* End get_mail() */    

/* ////////////////////////////////////////////////////////////////////////////
                                  CheckMail()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Tells if a particular mailbox has mail or not.

REVISIONS:      26 Jun 05 - RAC - Adapted from the similar RMAXTask function.
//////////////////////////////////////////////////////////////////////////// */

int CheckMail(                          /* 1 if there is mail, else 0 */
    TDESC       *mailbox                /* Ptr to mailbox of interest */
    ) {
    return (mailbox->msg == NULL) ? 0 : 1;
    }

/* ////////////////////////////////////////////////////////////////////////////
                                  GetStatus()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    For the current task, returns the status of the most recent
                call to Suspend(), GetMail(), or WaitSem().

NOTE:           This routine will always return TIMEOUT following a call to
                Suspend().

REVISIONS:      26 Jun 05 - RAC - Adapted from the similar RMAXTask function
//////////////////////////////////////////////////////////////////////////// */

int GetStatus(void) {                   /* TIMEOUT or OKAY */
    return currentTask->waitStatus;
    }

/* ////////////////////////////////////////////////////////////////////////////
                                  SignalSem()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Signals a semaphore.

REVISIONS:      26 Jun 05 - RAC - Adapted from the similar RMAXTask function.
//////////////////////////////////////////////////////////////////////////// */

void SignalSem (
    TDESC       *semaphore              /* Ptr to semaphore of interest */
    ) {
    
    TDESC       *wt;                            /* Ptr to waiting task */

    if ((wt = semaphore->nextWait) != NULL) {   /* There is a task waiting! */
        wt->waitStatus = OKAY;                  /* The read was successful */
        TimeUnlink(wt);                         /* Yank task from timer list */
        WaitUnlink(wt);                         /*  and wait list, too */
        ReadyLink(wt);                          /* Add to READY list */
        }                                       /* End 'task is waiting' */
    else {                                      /* No task waiting */
        semaphore->tt.semCount += 1;            /* Bump count */
        }                                       /* End 'no task waiting' */    
    }                                           /* End SignalSem() */

/* ////////////////////////////////////////////////////////////////////////////
                                   WaitSem()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Waits for a semaphore.

REVISIONS:      26 Jun 05 - RAC - Adapted from the similar RMAXTask function.
//////////////////////////////////////////////////////////////////////////// */

void WaitSem (
    TDESC       *semaphore,             /* Ptr to semaphore of interest */
    long        timeout                 /* Give up this many ticks from now */
    ) {

    if (semaphore->tt.semCount) {               /* Semaphore is set */
         semaphore->tt.semCount -= 1;           /* Decrement count */
        currentTask->waitStatus = OKAY;         /* The read was successful */
        }                                       /* End 'semaphore is set' */
    else {                                      /* Semaphore clear */
        while (semaphore->nextWait != NULL)     /* Find end of list of tasks */
            semaphore = semaphore->nextWait;    /*  waiting on this semaphore, */
        semaphore->nextWait = currentTask;      /*  and add the current task */
        currentTask->nextWait = NULL;           /* Terminate the list */
        currentTask->prevWait = semaphore;      /* Complete linkage */
        if (timeout) {                          /* If timeout was given, */
            currentTask->timeout =              /*  figure out when timer */
                timeout + gnow;                 /*  should expire */
            TimerLink(currentTask);             /* Stick in timer list */
            }
        Schedule();                             /* Go do something else */
        }                                       /* End 'semaphore clear' */
    }                                           /* End WaitSem() */     

/* ////////////////////////////////////////////////////////////////////////////
                                   CheckSem()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    Returns a semaphore count.

REVISIONS:      26 Jun 05 - RAC - Adapted from the similar RMAXTask function.
//////////////////////////////////////////////////////////////////////////// */

unsigned CheckSem (                     /* The semaphore count */
    TDESC       *semaphore              /* Ptr to semaphore of interest */
    ) {
    return semaphore->tt.semCount;      /* Return count */
    }    

/* ////////////////////////////////////////////////////////////////////////////
                                  GetTicks()
                                  GetTicksI()
///////////////////////////////////////////////////////////////////////////////
DESCRIPTION:    GetTicks() returns the value of a counter that is set to zero
                at boot time and incremented at TICKS_PER_SECOND Hz forever
                thereafter.  If TICKS_PER_SECOND is 40 (as it probably is;
                check mtc.h to make sure), the counter will run for about 621
                days before overflowing.

                GetTicksI() is exactly the same, to be used within ISRs to
                avoid re-entrancy problems.

REVISIONS:       5 Mar 07 - RAC - Genesis
                19 Apr 07 - RAC - Added GetTicksI()
//////////////////////////////////////////////////////////////////////////// */

long GetTicks() { return gnow; }
long GetTicksI() { return gnow; }

;------------------------------------------------------------------------------
;                                stackers.s03
;------------------------------------------------------------------------------
; DESCRIPTION:  This module contains a couple of assembly language routines to
;               move copies of the stack back and forth from internal RAM to
;               buffers in external RAM.
;
; REVISIONS     15 Jun 05 - RAC - Genesis
;------------------------------------------------------------------------------

;------------------------------------------------------------------------------
;       External Variable Definitions
;------------------------------------------------------------------------------

        EXTERN  _R                      ; REGISTER BANK (LINKER SETS THIS UP)

        RSEG    RCODE

;------------------------------------------------------------------------------
;                                 SaveStack()
;                                RestoreStack()
;------------------------------------------------------------------------------
; DESCRIPTION:  SaveStack() moves the stack image from internal RAM to a buffer
;               in external RAM whose address is passed by the caller.  The
;               address is passed in R6 (low byte) and R7 (high byte).
;               RestoreStack() is identical except it moves the stack image
;               from the caller's buffer to internal RAM.
;
; REVISIONS     15 Jun 05 - RAC - Genesis
;------------------------------------------------------------------------------

        PUBLIC  SaveStack
SaveStack:
                MOV     DPH,R6          ; Move external RAM pointer from R6, R7
                MOV     DPL,R7          ;  to DPTR
                MOV     R0,#0           ; Use R0 to index the internal RAM
Sloop:
                MOV     A,@R0           ; Get byte from internal RAM
                MOVX    @DPTR,A         ; Stuff into external RAM buffer
                INC     DPTR            ; Bump external RAM pointer
                INC     R0              ; Bump interanl RAM index
                MOV     A,R0            ; RAM index to accumulator for compare
                CJNE    A,SP,Sloop      ; Spin until top of the stack
                RET                     ; Done

;------------------------------------------------------------------------------

        PUBLIC  RestoreStack
RestoreStack:
                MOV     DPH,R6          ; Move external RAM pointer from R6, R7
                MOV     DPL,R7          ;  to DPTR
                MOV     R0,#0           ; Use R0 to index the internal RAM
RLoop:
                MOVX    A,@DPTR         ; Get byte from external RAM buffer
                MOV     @R0,A           ; Stuff into internal RAM
                INC     DPTR            ; Bump external RAM pointer
                INC     R0              ; Bump interanl RAM index
                MOV     A,R0            ; RAM index to accumulator for compare
                INC     A
                CJNE    A,SP,RLoop      ; Spin until top of the stack
                RET     

;------------------------------------------------------------------------------
;                                   Fixit()
;------------------------------------------------------------------------------
; DESCRIPTION:  Grabs the address of a task being created from a pointer to it
;               that is also in the code segment, and therefore difficult to
;               access from C.  Or so it seems.
;
; REVISIONS:    16 Jun 05 - RAC - Genesis
;------------------------------------------------------------------------------

        PUBLIC  Fixit
Fixit:
                MOV     DPH,R6          ; Get pointer to task address
                MOV     DPL,R7
                CLR     A
                MOVC    A,@A+DPTR       ; Get high byte of address
                MOV     R6,A            ; Save for return
                MOV     A,#1
                MOVC    A,@A+DPTR       ; Get low byte of address
                MOV     R7,A            ; Save for return
                RET

                END




List of 22 messages in thread
TopicAuthorDate
Multi Threading in C for 89S8253            01/01/70 00:00      
   effective?            01/01/70 00:00      
   Protothreads            01/01/70 00:00      
   What do you mean?            01/01/70 00:00      
   What actually you are looking for            01/01/70 00:00      
      Seek Help ! for Serial Communication            01/01/70 00:00      
         start a new thread            01/01/70 00:00      
            Good reply...            01/01/70 00:00      
               hehe...            01/01/70 00:00      
                  purpose            01/01/70 00:00      
                     What tasks?            01/01/70 00:00      
                        tasks            01/01/70 00:00      
                           Superloop            01/01/70 00:00      
                              ok...one more thing            01/01/70 00:00      
                                 If Key == '1' Then            01/01/70 00:00      
                                    statemachine            01/01/70 00:00      
                                       An alternative            01/01/70 00:00      
                                          why not in FAQ or personal page?            01/01/70 00:00      
                                             Yes            01/01/70 00:00      
   An alternative ?            01/01/70 00:00      
      Your Mileage May Vary            01/01/70 00:00      
   thanks alot            01/01/70 00:00      

Back to Subject List