home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Overload
/
ShartewareOverload.cdr
/
progm
/
ctask.zip
/
TSKMAIN.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-03-01
|
12KB
|
486 lines
/*
TSKMAIN.C - CTask - Main routines for task handling.
CTask - a Multitasking Kernel for C
Public Domain Software written by
Thomas Wagner
Patschkauer Weg 31
D-1000 Berlin 33
West Germany
No rights reserved.
*/
#include <stdio.h>
#include "tsk.h"
#include "tsklocal.h"
#define STACKSIZE 512
/*
struct task_stack describes the contents of a tasks stack after creation.
The first 10 words are the registers to be restored by the scheduler.
Only the segment registers are significant initially.
The next three words contain the function address plus the CPU flags
setup as if an interrupt had occurred at the function's entry address.
This setup is the same as the stack of an interrupted task after
scheduling.
The following two words contain a dummy return address, which points
to the routine "killretn". Thus, if the task main function should ever
return, the task is automatically killed. The last doubleword is
used for the optional argument to the task.
*/
struct task_stack {
word r_es;
word r_ds;
word r_di;
word r_si;
word r_bp;
word r_sp;
word r_bx;
word r_dx;
word r_cx;
word r_ax;
funcptr retn;
word r_flags;
funcptr dummyret;
farptr arg;
};
/*
The task queues:
All tasks using a timeout, either through "t_delay" or an event wait,
are enqueued into the "tsk_timer" dual link queue, using the "timerq"
link.
All tasks eligible for running are enqueued in "tsk_eligible".
The tcb-address of the current running task is stored in "tsk_current".
*/
dlink _Near tsk_timer;
tcbptr _Near tsk_eligible;
tcbptr _Near tsk_current;
/*
System flags:
tsk_preempt is zero if preemption is allowed.
Bit 0 is set if preemption has been disabled globally.
Bit 1 is set for temporary disabling preemption.
Temporary preemption is automatically removed by the
scheduler.
tsk_pretick is nonzero if a schedule request from an interrupt handler
was rejected due to tsk_preempt nonzero. This allows
an immediate scheduling whenever tsk_preempt is set to 0.
tsk_var_prior Can be set nonzero to enable variable priority.
Variable priority will increase the priority of
eligible tasks on each scheduler call while they
are waiting to be executed, so that low priority
tasks will slowly get to the head of the eligible
queue, getting a chance to be run. With variable
priority off, lower priority tasks will never be
executed while higher priority tasks are eligible.
*/
byte _Near tsk_preempt;
byte _Near tsk_pretick;
byte _Near tsk_var_prior;
/* --------------------------------------------------------------------- */
/*
The tcb's of the standard tasks.
timer_tcb is the tcb for the timer task.
This task waits for the tsk_timer_counter, which is
increased on every timer tick. It then chains to the
previous timer interrupt entry. Next the timeout queue
is scanned.
main_tcb is the "main" task which called "install_tasker". This
task has no separate stack, rather the stack on entry
to the scheduler is used.
idle_tcb is the idle task. This task is handled in a special way
by the scheduler. It is not enqueued in any queue, and
is only activated when no other task is able to run.
This task currently does nothing, but might be extended
to do some housekeeping if desired (although it may
never use any functions which might cause the idle task
to be made waiting).
*/
tcb _Near idle_tcb;
counter _Near tsk_timer_counter;
local tcb timer_tcb;
local tcb main_tcb;
local char idle_stack [STACKSIZE];
local char timer_stack [STACKSIZE];
/* --------------------------------------------------------------------- */
#pragma check_stack(off)
/*
Killretn kills the current active task. It is used internally, but
can also be called from outside.
*/
void far killretn (void)
{
tsk_cli ();
tsk_current->queue = NULL;
tsk_current->state = ST_KILLED;
schedule ();
}
/*
Idle is the idle task. It's tcb may never be manipulated.
*/
local void far idle (void)
{
while (1)
schedule ();
}
/*
The timer task handles all timeouts.
*/
local void far timer (void)
{
dlinkptr curr;
dlinkptr nxt;
tcbptr task;
byte st;
CRITICAL;
while (1)
{
wait_counter_set (&tsk_timer_counter, 0L);
tsk_chain_timer ();
C_ENTER;
curr = tsk_timer.follow;
while (curr->tcbp != NULL)
{
nxt = curr->follow;
if (!--curr->timeout)
{
task = curr->tcbp;
st = task->state;
if (st == ST_WAITING || st == ST_DELAYED)
{
task->retptr = TTIMEOUT;
tsk_wakeup (task);
}
else
tsk_unqtimer (task);
}
curr = nxt;
}
C_LEAVE;
}
}
/* ---------------------------------------------------------------------- */
/*
create_task
Initialises a tcb. The task is in stopped state initially.
*/
void far create_task (tcbptr task,
funcptr func,
byteptr stack,
word stksz,
word prior,
farptr arg)
{
struct task_stack far *stk;
stk = (struct task_stack far *)(stack + stksz - sizeof (struct task_stack));
stk->r_ds = stk->r_es = tsk_dseg ();
stk->r_bp = 0;
stk->r_flags = tsk_flags ();
stk->retn = func;
stk->dummyret = killretn;
stk->arg = arg;
task->stkbot = stack;
task->stack = (byteptr) stk;
task->next = NULL;
task->queue = NULL;
task->state = ST_STOPPED;
task->flags = 0;
task->prior = task->initprior = prior;
task->timerq.timeout = 0;
task->timerq.tcbp = task;
}
/*
kill_task
Removes a task from the system.
*/
void far kill_task (tcbptr task)
{
CRITICAL;
C_ENTER;
if (task->state == ST_RUNNING)
{
task->queue = NULL;
task->state = ST_KILLED;
schedule ();
}
else
{
tsk_unqueue (task);
tsk_unqtimer (task);
task->state = ST_KILLED;
}
C_LEAVE;
}
/*
start_task
Starts a stopped task. Returns -1 if the task was not stopped.
*/
int far start_task (tcbptr task)
{
CRITICAL;
if (task == NULL)
task = &main_tcb;
if (task->state == ST_STOPPED)
{
task->state = ST_ELIGIBLE;
C_ENTER;
tsk_enqueue (task, &tsk_eligible);
C_LEAVE;
return 0;
}
return -1;
}
/*
wake_task
Restarts a task waiting for an event or timeout.
Returns -1 if the task was not waiting or stopped.
*/
int far wake_task (tcbptr task)
{
CRITICAL;
if (task == NULL)
task = &main_tcb;
C_ENTER;
if (task->state >= ST_ELIGIBLE)
{
C_LEAVE;
return -1;
}
task->retptr = TWAKE;
tsk_wakeup (task);
C_LEAVE;
return 0;
}
/*
set_priority
Changes the priority of a task. If the task is enqueued in a
queue, its position in the queue is affected.
*/
void far set_priority (tcbptr task, word prior)
{
tqueptr que;
CRITICAL;
if (task == NULL)
task = &main_tcb;
C_ENTER;
task->prior = task->initprior = prior;
if ((task->state != ST_RUNNING) && ((que = task->queue) != NULL))
{
tsk_unqueue (task);
tsk_enqueue (task, que);
}
C_LEAVE;
}
/*
set_task_flags
Changes the user modifiable flags of the task.
*/
void far set_task_flags (tcbptr task, byte flags)
{
CRITICAL;
if (task == NULL)
task = &main_tcb;
C_ENTER;
task->flags = (task->flags & FL_SYSM) | (flags & FL_USRM);
C_LEAVE;
}
/* --------------------------------------------------------------------- */
/*
t_delay
delay the current task by "ticks" clock ticks.
If ticks is zero, the task is stopped.
*/
int far t_delay (dword ticks)
{
tsk_cli ();
tsk_current->queue = NULL;
if (ticks)
{
tsk_current->state = ST_DELAYED;
tsk_enqtimer (tsk_current, ticks);
}
else
tsk_current->state = ST_STOPPED;
schedule ();
return (int)tsk_current->retptr;
}
/* --------------------------------------------------------------------- */
/*
install_tasker
Installs the Ctask system. The internal tasks are created,
the queues are initialised, and the interrupt handler installation
routines are called. Task preemption is initially off.
Handling of the speedup parameter is system dependent.
*/
void far install_tasker (byte varpri, int speedup)
{
word divisor, sys_ticks;
tsk_current = &main_tcb;
tsk_eligible = NULL;
tsk_timer.follow = tsk_timer.prev = &tsk_timer;
tsk_timer.tcbp = NULL;
tsk_preempt = 1;
tsk_pretick = 0;
tsk_var_prior = varpri;
create_task (&idle_tcb, idle, idle_stack, STACKSIZE, 0, NULL);
create_task (&timer_tcb, timer, timer_stack, STACKSIZE, 0xffff, NULL);
create_counter (&tsk_timer_counter);
main_tcb.prior = main_tcb.initprior = 0xfffe;
main_tcb.queue = &tsk_eligible;
main_tcb.flags = F_CRIT;
main_tcb.state = ST_RUNNING;
start_task (&timer_tcb);
if (speedup <= 0 || speedup > 8)
{
divisor = 0;
sys_ticks = 1;
}
else
{
divisor = 0x8000 >> (speedup - 1);
sys_ticks = 1 << speedup;
}
tsk_install_timer (divisor, sys_ticks);
tsk_install_dos ();
tsk_install_kbd ();
}
/*
preempt_off
Turns off task preemption (will stay off until explicitly enabled).
*/
void far preempt_off (void)
{
tsk_preempt = 1;
}
/*
preempt_on
Resets permanent and temporary task preemption flag. If
preemption is pending, the scheduler is called.
*/
void far preempt_on (void)
{
tsk_preempt = 0;
tsk_cli ();
if (tsk_pretick)
schedule ();
tsk_sti ();
}
/*
remove_tasker
Calls the interrupt handler un-install routines.
*/
void far remove_tasker (void)
{
tsk_preempt = 0;
tsk_remove_timer ();
tsk_remove_dos ();
tsk_remove_kbd ();
}
/*
tsk_ena_preempt
Resets temporary task preemption flag. If preemption is pending,
the scheduler is called.
*/
void far tsk_ena_preempt (void)
{
tsk_cli ();
if (!(tsk_preempt &= ~2))
if (tsk_pretick)
schedule ();
tsk_sti ();
}