home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The World of Computer Software
/
World_Of_Computer_Software-02-387-Vol-3of3.iso
/
m
/
mar93.zip
/
1103028A
< prev
next >
Wrap
Text File
|
1992-11-16
|
21KB
|
948 lines
/****************************************************/
/* */
/* SSX.C - stack swap executive */
/* */
/* By Tom Green and Dennis Cronin */
/* 10/19/92 */
/* */
/****************************************************/
/* turn on inline asm */
#pragma inline
#include <alloc.h>
#include <dos.h>
#include <string.h>
#include <setjmp.h>
#include "ssx.h"
#include "ssx_conf.h"
/* Task Control Block */
typedef struct tcb {
/* task chain forward ptr */
struct tcb *forw;
/* task chain backward ptr */
struct tcb *back;
/* delay chain forward ptr */
struct tcb *dforw;
/* delay chain backward ptr */
struct tcb *dback;
/* pointer to task code */
fptr task_ptr;
/* pointer to start of allocated stack */
unsigned int *stack;
/* task's current stack pointer */
unsigned int *stack_ptr;
/* delay counter */
long timeout;
/* flag for task timed out */
unsigned char timedout;
/* flag for TCB in use */
unsigned char active;
/* status flags */
unsigned char status;
/* task priority */
unsigned char priority;
/* task ID */
unsigned char id;
/* for storing extra task context */
char context[CNTXT_SZ];
} tcb;
/* misc. defines */
#define TRUE 1
#define FALSE 0
/* background task defines */
#define BG_TASK_ID 0xff
#define BG_TASK_PRI 0xff
/* make data and code local to this file */
#define LOCAL static
/* flags for the TCB status word */
#define T_READY 0 /* ready to run */
#define T_WAITING 1 /* waiting on wait_q */
#define T_DELAYED 2 /* delay timer running */
/* local function prototypes */
LOCAL tcb *get_tcb(void);
LOCAL void free_tcb(tcb *tbp);
LOCAL void put_ready(tcb *tbp);
LOCAL void rotate_tasks(tcb *tbp);
LOCAL void put_delay(long timeout);
LOCAL void run_new_task(void);
LOCAL void bg_task(void);
LOCAL void stack_swap(unsigned int **old_stack_ptr,
unsigned int **new_stack_ptr);
LOCAL int disable_ints(void);
/* local variables */
LOCAL long sys_time; /* system timer */
LOCAL unsigned char slice_cnt;
LOCAL int running;
LOCAL int initd;
LOCAL jmp_buf jbuf;
LOCAL int switch_lock;
/* task control */
LOCAL tcb t_pool[MAX_TASKS + 1]; /* pool of TCBs */
LOCAL tcb t_ready; /* head of ready task queue */
LOCAL tcb t_null; /* the NULL task */
LOCAL tcb *t_free; /* head of free queue */
LOCAL tcb *t_current; /* pointer to current task */
/* delay control */
LOCAL tcb d_chain; /* q head for delayed tasks */
/* MACROS to unlink from task and delay queues */
/* t_unlink - must be used w/ interrupts off */
#define t_unlink(tbp) \
{ \
(tbp)->back->forw = (tbp)->forw; \
if((tbp)->forw != NULL) \
(tbp)->forw->back = (tbp)->back; \
}
/* d_unlink - must be used w/ interrupts off */
#define d_unlink(tbp) \
{ \
(tbp)->dback->dforw = (tbp)->dforw; \
if((tbp)->dforw != NULL) \
(tbp)->dforw->dback = (tbp)->dback; \
}
/*
* ssx_init - init ssx data
*/
int
ssx_init(void)
{
int i;
tcb *tcbp;
if(initd)
return(INIT_ERROR);
memset(&d_chain,0,sizeof(d_chain));
/* init TCB free queue links */
for(i=0,tcbp=t_pool;i < MAX_TASKS-1;i++,tcbp++){
tcbp->forw = &t_pool[i+1];
}
t_pool[i].forw = NULL;
for(i = 0;i < MAX_TASKS;i++){
t_pool[i].active=FALSE;
}
t_current = NULL;
t_free = t_pool;
t_ready.forw = NULL;
switch_lock = 0;
/* set up background task */
if((ssx_task_create(BG_TASK_PRI,BG_TASK_ID,
bg_task,0x200,"bg_task"))
!= SUCCESS)
return(INIT_ERROR);
initd = TRUE;
return(SUCCESS);
}
/*
* sx_run - this starts executive
*/
void
ssx_run(void)
{
int val;
val = setjmp(jbuf);
if(val != 0)
return;
slice_cnt = 0;
sys_time = 0;
/* make current task ptr point to dummy tcb so
* beginning of time stack pointer save will
* have a safe place to save to.
*/
t_current = &t_null;
/* mark SSX as active */
running = TRUE;
/* this will start the first task rolling */
ssx_switch();
}
/*
* sx_stop - this stops executive
*/
void
ssx_stop(void)
{
int i;
int_state_var istate;
ints_off(istate);
/* free any allocated stacks */
for(i = 0; i < MAX_TASKS; i++){
if(t_pool[i].stack != NULL){
free(t_pool[i].stack);
t_pool[i].stack = NULL;
}
}
initd = FALSE;
running = FALSE;
restore_ints(istate);
longjmp(jbuf,1);
}
/*
* ssx_task_create - create task and set up tcb
*/
int
ssx_task_create(unsigned char task_pri,
unsigned char task_id,fptr task_ptr,
unsigned int stack_size,char *name)
{
unsigned int i;
tcb *tbp;
int_state_var istate;
ints_off(istate);
if(task_id == 0) {
restore_ints(istate);
return(TID_ERR);
}
/* check for TID already in use */
for(i = 0,tbp = t_pool;i < MAX_TASKS;i++,tbp++) {
if(tbp->active && tbp->id == task_id) {
restore_ints(istate);
return(TID_ERR);
}
}
if((tbp = get_tcb()) == NULL) { /* get a tcb */
restore_ints(istate);
return(TCB_ERR);
}
/* allocate stack for this task */
if((tbp->stack = (unsigned int *)
malloc(stack_size)) == NULL){
restore_ints(istate);
return(STACK_ERR);
}
/* fill in the blanks */
strncpy(tbp->context,name,CNTXT_SZ);
tbp->priority = task_pri;
tbp->id = task_id;
tbp->status = T_READY;
tbp->timedout = FALSE;
tbp->timeout = 0L;
tbp->forw = tbp->back =
tbp->dforw = tbp->dback = NULL;
tbp->task_ptr = task_ptr;
tbp->stack_ptr = (unsigned int *)(tbp->stack +
(stack_size / 2));
/* setup task stack to have address of start up
* routine and fake di, si, bp registers to pop.
* This part is not portable. the stack looks
* like this:
*
* |-------------------------| high
* |address of run_new_task |
* |-------------------------|
* |bp |
* |-------------------------|
* |si |
* |-------------------------|
* |di |
* |-------------------------| low
*
*/
*(--tbp->stack_ptr) = (unsigned int)run_new_task;
*(--tbp->stack_ptr) = 0; /* fake BP,SI,DI */
*(--tbp->stack_ptr) = 0; /* on stack */
*(--tbp->stack_ptr) = 0;
/* put on active chain */
rotate_tasks(tbp);
ssx_switch();
restore_ints(istate);
return(SUCCESS);
}
/*
* ssx_task_delay - cause task to delay for number
* of ticks
*/
void
ssx_task_delay(long timeout)
{
int_state_var istate;
ints_off(istate);
if(timeout == 0) {
ssx_switch();
restore_ints(istate);
return;
}
put_delay(timeout); /* put current task on */
/* delay queue */
t_unlink(t_current); /* take off ready queue */
ssx_switch();
restore_ints(istate);
}
/*
* ssx_task_delete - delete a task and remove from
* queues
*/
int
ssx_task_delete(unsigned char task_id)
{
unsigned int i;
tcb *tp;
int_state_var istate;
ints_off(istate);
/* look for background task ID */
if(task_id == BG_TASK_ID){
restore_ints(istate);
return(TID_ERR);
}
/* look for 'self' form */
if(task_id == 0) {
if(t_current) {
/* get current task's id */
task_id = t_current->id;
}
}
/* brute force, search all TCBs for matching ID */
for(i = 0,tp = t_pool;i < MAX_TASKS;i++,tp++) {
if(tp->active && tp->id == task_id) {
break;
}
}
/* see if found match */
if(i == MAX_TASKS){
restore_ints(istate);
return(TID_ERR);
}
switch(tp->status & (T_DELAYED | T_WAITING)) {
case T_DELAYED:
d_unlink(tp); /* remove from delay q */
break;
case T_WAITING:
t_unlink(tp); /* remove from some */
/* wait_q */
break;
case T_DELAYED | T_WAITING:
t_unlink(tp); /* remove from some */
/* wait_q */
d_unlink(tp); /* remove from delay q */
break;
case T_READY:
t_unlink(tp); /* remove from ready q */
break;
}
free(tp->stack); /* free allocated stack */
tp->stack = NULL;
free_tcb(tp); /* free up the TCB */
ssx_switch();
restore_ints(istate);
return(SUCCESS);
}
/*
* ssx_change_priority - change priority for currently
* running task,
* don't immediately reschedule
* returns old priority
*/
unsigned char
ssx_change_priority(unsigned char new_priority)
{
unsigned char old_priority;
int_state_var istate;
ints_off(istate);
old_priority = t_current->priority;
t_current->priority = new_priority;
t_unlink(t_current);
rotate_tasks(t_current);
restore_ints(istate);
return(old_priority);
}
/*
* ssx_wait - wait on wait_q. reschedule later
*/
void
ssx_wait(wait_q *wqptr)
{
tcb *tp;
tcb *t_cur;
int_state_var istate;
ints_off(istate);
/* check for message flag already set */
if(wqptr->mesg_flg){
wqptr->mesg_flg = FALSE;
restore_ints(istate);
return;
}
t_cur = t_current;
t_unlink(t_cur); /* take off ready queue */
tp = (tcb *)&wqptr->task_ptr;
/*
* find where to insert waiting task into
* wait queue
*/
while((tp->forw) != NULL) {
if(t_cur->priority <= tp->forw->priority)
break;
tp = tp->forw;
}
/* insert into queue */
if((t_cur->forw = tp->forw) != NULL)
t_cur->forw->back = t_cur;
tp->forw = t_cur;
t_cur->back = tp;
t_cur->status = T_WAITING;
ssx_switch();
restore_ints(istate);
}
/*
* ssx_wait_with alarm - wait on wait_q with alarm.
* reschedule now
*/
int
ssx_wait_with_alarm(wait_q *wqptr,long timeout)
{
tcb *tp;
tcb *t_cur;
int_state_var istate;
ints_off(istate);
/* check for message flag already set */
if(wqptr->mesg_flg){
wqptr->mesg_flg = FALSE;
restore_ints(istate);
return(SUCCESS);
}
t_cur = t_current;
t_unlink(t_cur); /* take off ready queue */
tp = (tcb *)&wqptr->task_ptr;
/*
* find where to insert waiting task into
* wait queue
*/
while((tp->forw) != NULL) {
if(t_cur->priority <= tp->forw->priority)
break;
tp = tp->forw;
}
/* insert into queue */
if((t_cur->forw = tp->forw) != NULL)
t_cur->forw->back = t_cur;
tp->forw = t_cur;
t_cur->back = tp;
t_cur->status = T_WAITING;
/*
* if there is timeout value, put task on
* delay queue
*/
if(timeout)
put_delay(timeout);
ssx_switch();
/*
* we were sheduled back in so
* see if task timed out and return error
*/
if(t_cur->timedout){
t_cur->timedout = FALSE;
restore_ints(istate);
return(TO_ERR);
}
restore_ints(istate);
/* task did not time out, so return success */
return(SUCCESS);
}
/*
* ssx_alert - alert wait_q. reshedule if task
* alerted is equal or higher
* priority than current task
*/
int
ssx_alert(wait_q *wqptr)
{
tcb *np;
tcb *oldtcb;
int_state_var istate;
ints_off(istate);
/* check for message waiting */
if(wqptr->mesg_flg){
restore_ints(istate); /* cannot alert if */
return(MW_ERR); /* messgae is waiting */
}
np=(tcb *)wqptr->task_ptr;
/* check if there is a task waiting on wait_q */
if(np != NULL){
t_unlink(np);
if(np->status & T_DELAYED)
d_unlink(np);
np->status &= ~(T_WAITING | T_DELAYED);
put_ready(np);
/*
* switch to waiting task if it is equal
* or higher priority
*/
if(np->priority <= t_current->priority){
oldtcb = t_current;
t_current = np;
/*
* check and see if scheduling is
* disabled
*/
if(switch_lock == 0)
stack_swap(&oldtcb->stack_ptr
,&np->stack_ptr);
}
restore_ints(istate);
return(SUCCESS);
}
/* fell thru, simply leave message in wait_q */
wqptr->mesg_flg = TRUE;
restore_ints(istate);
return(SUCCESS);
}
/*
* ssx_clock_tick - call this to update ssx clock from
* timer interrupt handler
*/
void
ssx_clock_tick(void)
{
tcb *tp;
int_state_var istate;
ints_off(istate);
if(running == FALSE){
restore_ints(istate);
return;
}
sys_time++;
/* do time updates */
tp = (tcb *)&d_chain;
/* check for timed out tasks */
while((tp = tp->dforw) != NULL) {
if((sys_time - tp->timeout) >= 0) {
d_unlink(tp); /* this one's ready */
tp->timedout = TRUE;
if(tp->status & T_WAITING)
t_unlink(tp);
tp->status = T_READY;
/* put task on ready queue */
rotate_tasks(tp);
}
else
break; /* passed the ready ones */
}
/* round robin rotation */
if((++slice_cnt) == TIME_SLICE){
slice_cnt = 0;
/* if task is running and was left ready */
if(t_current && t_current->status
== T_READY) {
/* remove from ready queue */
t_unlink(t_current);
/* puts at back of pri group */
rotate_tasks(t_current);
}
}
ssx_switch();
restore_ints(istate);
}
/*
* ssx_set_time - this sets SSX system time
*/
void
ssx_set_time(long time)
{
int_state_var istate;
ints_off(istate);
sys_time = time;
restore_ints(istate);
}
/*
* ssx_get_time - this returns SSX system time
*/
long
ssx_get_time(void)
{
return(sys_time);
}
/*
* ssx_lock - disable task switching
*/
void
ssx_lock(void)
{
int_state_var istate;
ints_off(istate);
switch_lock++;
restore_ints(istate);
}
/*
* ssx_unlock - enable task switching
*/
void
ssx_unlock(void)
{
int_state_var istate;
ints_off(istate);
/* call ssx_switch if we are not nested */
if(--switch_lock == 0)
ssx_switch();
restore_ints(istate);
}
/*
* ssx_switch - run next ready task
*
* notes: there must always be a runnable task w/
* SSX. a background task is created by ssx_init
* and this task can never wait or do anything
* that would remove it from the active queue.
* this saves checks in this routine, making
* it more efficient.
*/
void
ssx_switch(void)
{
tcb *oldtcb;
if(!running)
return;
oldtcb = t_current;
/* switch tasks */
t_current = t_ready.forw; /* get next */
/* ready task */
/*
* if new task is same as old, do not
* bother with switch
*/
if(t_current == oldtcb)
return;
/* check and see if scheduling is disabled */
if(switch_lock == 0){
/* we have a new task so do a task switch */
stack_swap(&oldtcb->stack_ptr,
&t_current->stack_ptr);
}
}
/*
* get_tcb - get task control block
*/
LOCAL tcb *
get_tcb(void)
{
tcb *tbp;
if(t_free == NULL)
return(NULL);
tbp = t_free;
t_free = tbp->forw;
tbp->active = TRUE;
return(tbp);
}
/*
* free_tcb - free task control block
*/
LOCAL void
free_tcb(tcb *tbp)
{
/* '****' for debug TCB not in use */
tbp->timeout = 0x2a2a2a2aL;
tbp->active = FALSE;
tbp->forw = t_free;
t_free = tbp;
}
/*
* put_ready - put task at head of ready queue
*/
LOCAL void
put_ready(tcb *tbp)
{
tcb *tp,*np;
unsigned int priority;
/* get priority of task to be inserted in chain */
priority = tbp->priority;
/* put on the active chain */
tp = (tcb *)&t_ready;
/*
* sort in order of decreasing priority, put at
* head of pri group
*/
while((np = tp->forw) != NULL && np->priority
< priority)
tp = np;
/* link in */
tbp->forw = np;
tbp->back = tp;
tp->forw = tbp;
if(np != NULL)
np->back = tbp;
}
/*
* rotate_tasks - put task at back of ready queue
*/
LOCAL void
rotate_tasks(tcb *tbp)
{
tcb *tp,*np;
unsigned int priority;
/* get priority of task to be inserted in chain */
priority = tbp->priority;
/* put on the active chain */
tp = (tcb *)&t_ready;
/*
* sort in order of decreasing priority,
* put at back of pri group
*/
while((np = tp->forw) != NULL && np->priority
<= priority)
tp = np;
/* link in */
tbp->forw = np;
tbp->back = tp;
tp->forw = tbp;
if(np != NULL)
np->back = tbp;
}
/*
* put_delay - put task on delay queue
*/
LOCAL void
put_delay(long timeout)
{
tcb *tp,*np;
t_current->timeout =
timeout + sys_time; /* actual time ready */
t_current->timedout=FALSE;
t_current->status |= T_DELAYED;
tp = (tcb *)&d_chain;
/* sort in order increasing target time */
/* trick to solve wrap of sys_time */
while((np = tp->dforw) != NULL) {
if(timeout <= np->timeout - sys_time)
/* hit a more future one */
break;
tp = np;
}
/* link in */
t_current->dforw = np;
t_current->dback = tp;
tp->dforw = t_current;
if(np != NULL)
np->dback = t_current;
}
/*
* run_new_task - starts up a new task making sure
* interrupts are enabled
*/
LOCAL void
run_new_task(void)
{
ints_on();
(t_current->task_ptr)();
}
/*
* bg_task - must have a background task
*/
LOCAL void
bg_task(void)
{
while(1);
}
/*
* WARNING - routines from here on are not portable
*/
/*
* stack_swap - switch from stack of curent task to
* stack of new task
*/
LOCAL void
stack_swap(unsigned int **old_stack_ptr,
unsigned int **new_stack_ptr)
{
asm or di,di /* fool compiler into saving */
asm or si,si /* di and si registers */
/* save stack pointer of old task from sp reg */
*old_stack_ptr = (unsigned int *)_SP;
/* load sp reg with stack pointer of new task */
_SP=(unsigned int)*new_stack_ptr;
}
/*
* disable_ints - disable interrupts and return state
* of interrupts before before they
* were disabled. Returns positive
* integer if they were enabled
*/
LOCAL int
disable_ints(void)
{
asm pushf /* save flags to get */
/* interupt status */
asm cli /* interrupts off */
asm pop ax /* get interrupt state from */
/* flags that were pushed */
asm and ax,0200h /* and flags to get interrupt */
/* status */
return(_AX);
}