| ??? 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
|
| Topic | Author | Date |
| 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 |



