home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Frostbyte's 1980s DOS Shareware Collection
/
floppyshareware.zip
/
floppyshareware
/
DOOG
/
CTASK.ZIP
/
TSKMAIN.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-12-19
|
19KB
|
896 lines
/*
--- Version 2.0 89-12-17 21:20 ---
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.
Changes from V1.1: - Support for task groups and secondary
invocations added, moved global vars into
structure.
- stop_task function and other goodies added.
- timer functions moved to separate module.
*/
#include <stdio.h>
#include "tsk.h"
#include "tsklocal.h"
#define STACKSIZE 512
ctask_globvars _Near tsk_glob_rec;
globvarptr _Near tsk_global = NULL;
word _Near tsk_instflags;
#if (GROUPS)
char _Near tsk_version [8] = VERSTRING;
#endif
/*
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 processes the entries
in the timeout queue.
int8_tcb is the tcb for the int8 chain task.
This task waits for the tsk_int8_counter, which is
increased on every system timer tick. It then chains to
the previous timer interrupt entry.
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. Since main_tcb is not used
if this is a secondary invocation of CTask, "main_ptr"
points to the main_tcb for a primary invocation, and
to the underlying task for a secondary invocation.
*/
local tcb _Near timer_tcb;
local tcb _Near main_tcb;
local tcbptr _Near main_ptr = &main_tcb;
local char timer_stack [STACKSIZE];
counter _Near tsk_timer_counter;
#if (IBM)
counter _Near tsk_int8_counter;
local tcb int8_tcb;
local char int8_stack [STACKSIZE];
#endif
/*
Un-Install-Function pointers for the optional serial and printer
drivers. If ports are installed, the driver inserts the address
of a remove-function here, to be called on removal of the main
tasker.
*/
#if (DOS)
funcptr_void v24_remove_func = NULL;
funcptr_void prt_remove_func = NULL;
#endif
/* --------------------------------------------------------------------- */
typedef struct {
word int20;
word mem_end;
byte reserved1;
byte syscall [5];
dword exit_addr; /* Where DOS jumps to on EXIT */
dword ctl_c_addr;
dword crit_err_addr;
word parent_psp;
byte open_files [20];
word env_segment;
dword caller_stack; /* SS:SP of caller for SPAWN */
word filetab_len; /* Number of entries in file table */
dword filetab_ptr; /* file table pointer */
dword nested_psp;
} psp;
typedef psp far *pspptr;
/* --------------------------------------------------------------------- */
#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_kill (GLOBDATA current_task);
GLOBDATA current_task = NULL;
schedule ();
}
/* ---------------------------------------------------------------------- */
/*
create_task
Initialises a tcb. The task is in stopped state initially.
*/
tcbptr far create_task (tcbptr task,
funcptr func,
byteptr stack,
word stksz,
word prior,
farptr arg
#if (TSK_NAMEPAR)
,byteptr name
#endif
)
{
struct task_stack far *stk;
#if (TSK_DYNAMIC)
if (task == NULL)
{
if ((task = (tcbptr) tsk_alloc (sizeof(tcb))) == NULL)
return NULL;
task->flags = F_TEMP;
}
else
task->flags = 0;
if (stack == NULL)
{
if ((stack = (byteptr) tsk_alloc (stksz)) == NULL)
{
if (task->flags & F_TEMP)
tsk_free (task);
return NULL;
}
task->flags |= F_STTEMP;
}
#else
task->flags = 0;
#endif
stk = (struct task_stack far *)(stack + stksz - sizeof (struct task_stack));
stk->r_ds = task->t_es = tsk_dseg ();
stk->r_flags = tsk_flags ();
stk->retn = func;
stk->dummyret = killretn;
stk->arg = arg;
task->t_bp = 0;
task->stkbot = stack;
task->stack = (byteptr) stk;
task->cqueue.kind = TYP_TCB;
task->cqueue.next = NULL;
task->qhead = NULL;
task->state = ST_STOPPED;
task->cqueue.el.pri.prior = task->cqueue.el.pri.ini_prior = prior;
task->timerq.link.el.ticks = task->timerq.elem.time.reload = 0;
task->timerq.link.kind = TKIND_WAKE;
task->timerq.link.next = NULL;
task->timerq.strucp = (farptr) task;
task->timerq.tstate = TSTAT_IDLE;
task->timerq.flags = 0;
task->save_func = task->rest_func = NULL;
task->user_ptr = NULL;
#if (GROUPS)
task->group = task->homegroup = GLOBDATA current_task->group;
#endif
#if (DOS)
task->indos = 0;
task->new = 1;
task->base_psp = task->group->create_psp;
#endif
#if (TSK_NAMED)
tsk_add_name (&task->name, name, TYP_TCB, task);
#endif
return task;
}
/*
kill_task
Removes a task from the system.
*/
void far kill_task (tcbptr task)
{
byte st;
CRITICAL;
C_ENTER;
if ((st = task->state) != ST_RUNNING)
{
if (st == ST_KILLED)
return;
else
tsk_dequeue (&task->cqueue);
}
tsk_kill (task);
if (st == ST_RUNNING)
{
GLOBDATA current_task = NULL;
schedule ();
}
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_ptr;
if (task->state == ST_STOPPED)
{
task->state = ST_ELIGIBLE;
C_ENTER;
tsk_runable (task);
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_ptr;
C_ENTER;
if (task->state >= ST_ELIGIBLE)
{
C_LEAVE;
return -1;
}
task->retptr = TWAKE;
tsk_runable (task);
C_LEAVE;
return 0;
}
/*
stop_task
Sets a task to the stopped state.
*/
int far stop_task (tcbptr task)
{
CRITICAL;
if (task == NULL)
task = main_ptr;
if (task->state < ST_STOPPED)
return -1;
C_ENTER;
task->state = ST_STOPPED;
tsk_dequeue (&task->cqueue);
task->qhead = NULL;
tsk_deqtimer (&task->timerq.link);
C_LEAVE;
return 0;
}
/*
get_priority
Returns the priority of a task.
*/
word far get_priority (tcbptr task)
{
if (task == NULL)
task = main_ptr;
return task->cqueue.el.pri.prior;
}
/*
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)
{
queheadptr que;
CRITICAL;
if (task == NULL)
task = main_ptr;
C_ENTER;
task->cqueue.el.pri.prior = task->cqueue.el.pri.ini_prior = prior;
if ((task->state != ST_RUNNING) && ((que = task->qhead) != NULL))
{
tsk_dequeue (&task->cqueue);
tsk_enqueue (que, &task->cqueue);
}
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_ptr;
C_ENTER;
task->flags = (task->flags & FL_SYSM) | (flags & FL_USRM);
C_LEAVE;
}
/*
set_funcs
Set the save/restore functions in the TCB.
*/
void far set_funcs (tcbptr task, funcptr save, funcptr rest)
{
CRITICAL;
if (task == NULL)
task = main_ptr;
C_ENTER;
task->save_func = save;
task->rest_func = rest;
C_LEAVE;
}
/*
set_user_ptr
Set the user pointer in the TCB.
*/
farptr far set_user_ptr (tcbptr task, farptr uptr)
{
farptr old;
CRITICAL;
if (task == NULL)
task = main_ptr;
C_ENTER;
old = task->user_ptr;
task->user_ptr = uptr;
C_LEAVE;
return old;
}
/*
get_user_ptr
Returns the user pointer from the TCB.
*/
farptr far get_user_ptr (tcbptr task)
{
if (task == NULL)
task = main_ptr;
return task->user_ptr;
}
/*
curr_task
Returns TCB of current running task.
*/
tcbptr far curr_task (void)
{
return GLOBDATA current_task;
}
/* --------------------------------------------------------------------- */
#if(GROUPS)
local void near tsk_create_group (gcbptr group, byteptr name)
{
pspptr pspp;
CRITICAL;
#if (DOS)
pspp = MK_FP(_psp, 0);
#endif
tsk_init_qhead (&group->namelist.list);
tsk_copy_name (&group->namelist, name);
group->creator = GLOBDATA current_task;
group->branch = NULL;
group->home = GLOBDATA current_task->group;
C_ENTER;
#if (DOS)
group->exit_addr = pspp->exit_addr;
group->create_psp = _psp;
pspp->exit_addr = (dword)tsk_emergency_exit;
#endif
group->level = group->home->branch;
group->home->branch = group;
GLOBDATA current_task->group = group;
C_LEAVE;
}
int far ctask_resident (void)
{
globvarptr gv;
gv = tsk_resident ();
if (gv != NULL && tsk_global == NULL)
tsk_global = gv;
return (gv != NULL);
}
#endif
/*
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.
*/
int far install_tasker (byte varpri, int speedup, word flags, byteptr name)
{
#if (DOS)
word divisor, sys_ticks;
#endif
#if (GROUPS)
int i;
tcbptr ct;
#endif
tsk_instflags = flags;
#if (GROUPS)
for (i = 0; i < 8; i++)
tsk_glob_rec.id [i] = tsk_version [i];
if (ctask_resident ())
{
main_ptr = ct = GLOBDATA current_task;
tsk_create_group (&tsk_glob_rec.group, name);
#if (DOS)
tsk_glob_rec.group.save_psp = ct->base_psp;
tsk_glob_rec.group.save_sssp = ct->psp_sssp;
ct->base_psp = _psp;
#endif
#if (TSK_DYNAMIC)
create_resource (&alloc_resource
#if (TSK_NAMEPAR)
, "ALLOCRSC"
#endif
);
#endif
return 1;
}
#endif
tsk_global = &tsk_glob_rec;
tsk_glob_rec.current_task = &main_tcb;
#if (GROUPS)
main_tcb.group = &tsk_glob_rec.group;
#endif
tsk_init_qhead (&tsk_glob_rec.eligible_queue);
tsk_init_qhead (&tsk_glob_rec.timer_queue);
tsk_init_qhead (&tsk_glob_rec.watch_queue);
tsk_glob_rec.preempt = 1;
tsk_glob_rec.pretick = 0;
tsk_glob_rec.var_prior = varpri;
tsk_glob_rec.in_sched = 0;
tsk_glob_rec.ticker_chain = NULL;
#if (GROUPS)
tsk_glob_rec.group.branch = NULL;
tsk_create_group (&tsk_glob_rec.group, name);
tsk_glob_rec.group.branch = NULL;
tsk_glob_rec.group.home = NULL;
#if (DOS)
tsk_glob_rec.group.save_psp = 0;
tsk_install_dos ();
#endif
#else
#if (TSK_NAMED)
tsk_init_qhead (&tsk_glob_rec.name_list);
tsk_glob_rec.name_list.name [0] = 0;
#endif
#endif
/*
Call create_task to initialise the main task's TCB.
Note that the function pointer and stack parameters are
insignificant, but must be valid pointers (the stack is
initialised, but not used).
*/
create_task (&main_tcb, tsk_timer, timer_stack, STACKSIZE, PRI_TIMER - 1, NULL
#if (TSK_NAMEPAR)
, "-MAIN-"
#endif
);
main_tcb.qhead = &tsk_glob_rec.eligible_queue;
main_tcb.state = ST_RUNNING;
#if (TSK_DYNAMIC)
create_resource (&alloc_resource
#if (TSK_NAMEPAR)
, "ALLOCRSC"
#endif
);
#endif
create_task (&timer_tcb, tsk_timer, timer_stack, STACKSIZE, PRI_TIMER, NULL
#if (TSK_NAMEPAR)
, "-TIMER-"
#endif
);
create_counter (&tsk_timer_counter
#if (TSK_NAMEPAR)
, "TIMCOUNT"
#endif
);
start_task (&timer_tcb);
#if (IBM)
create_task (&int8_tcb, tsk_int8, int8_stack, STACKSIZE, PRI_INT8, NULL
#if (TSK_NAMEPAR)
, "-INT8-"
#endif
);
create_counter (&tsk_int8_counter
#if (TSK_NAMEPAR)
, "INT8CNT"
#endif
);
start_task (&int8_tcb);
if (speedup <= 0 || speedup > 8)
{
divisor = 0;
sys_ticks = 1;
}
else
{
divisor = 0x8000 >> (speedup - 1);
sys_ticks = 1 << speedup;
}
tsk_glob_rec.ticks_per_sec = 18 * sys_ticks; /* rough number only */
#if (CLOCK_MSEC)
tsk_glob_rec.tick_factor = (65536.0 / (double)sys_ticks) / 1193.18;
#endif
tsk_install_timer (divisor, sys_ticks);
tsk_install_kbd ();
if (flags & IFL_PRINTER)
tsk_install_int17 ();
#endif
#if (AT_BIOS)
if (flags & IFL_INT15)
tsk_install_bios ();
#endif
#if (!DOS)
tsk_install_timer (0, 0);
#endif
return 0;
}
/*
tsk_remove_tasker
Calls the interrupt handler un-install routines.
*/
local void near tsk_remove_tasker (void)
{
tsk_glob_rec.preempt = 0;
#if (DOS)
if (v24_remove_func != NULL)
v24_remove_func ();
if (prt_remove_func != NULL)
prt_remove_func ();
#endif
#if (AT_BIOS)
if (tsk_instflags & IFL_INT15)
tsk_remove_bios ();
#endif
#if (IBM)
/* Allow all stored clock ticks to be processed */
if (!(tsk_instflags & IFL_INT8_DIR))
{
set_priority (&int8_tcb, 0xffff);
while (check_counter (&tsk_int8_counter))
schedule();
}
if (tsk_instflags & IFL_PRINTER)
tsk_remove_int17 ();
tsk_remove_timer ();
tsk_remove_kbd ();
#endif
#if (DOS)
tsk_remove_dos ();
#endif
}
/*
tsk_kill_group
Kills all tasks in the current group and unlinks all structures
from the name list.
*/
#if (GROUPS)
local void near tsk_kill_group (gcbptr group)
{
nameptr curr;
tcbptr tsk;
#if (DOS)
pspptr pspp;
#endif
tsk_dis_preempt ();
#if (DOS)
pspp = MK_FP (group->create_psp, 0);
pspp->exit_addr = group->exit_addr;
if (group->save_psp)
{
group->creator->base_psp = group->save_psp;
group->creator->psp_sssp = group->save_sssp;
pspp = MK_FP (group->save_psp, 0);
pspp->caller_stack = group->save_sssp;
}
if (v24_remove_func != NULL)
{
v24_remove_func ();
v24_remove_func = NULL;
}
if (prt_remove_func != NULL)
{
prt_remove_func ();
prt_remove_func = NULL;
}
#endif
group->creator->group = group->home;
curr = (nameptr)group->namelist.list.first;
while (curr->list.kind)
{
tsk_del_name (curr);
if (curr->list.kind == TYP_TCB)
{
tsk = (tcbptr)curr->strucp;
if (tsk != GLOBDATA current_task &&
tsk != &int8_tcb &&
tsk != &timer_tcb)
kill_task (tsk);
}
curr = (nameptr)group->namelist.list.first;
}
tsk_ena_preempt ();
}
#endif
/*
tsk_remove_group
The current group is unlinked from the group chain, then
all tasks in the current group, and in all subgroups,
are killed via "tsk_kill_group".
Returns 0 on normal completion,
1 when last group was removed (including the tasker),
-1 when the group structure was messed up.
CAUTION: This routine is for use by the TSKDOS and remove_tasker
modules ONLY! Do not call it directly.
*/
#if (GROUPS)
int near tsk_remove_group (gcbptr group, int freemem)
{
CRITICAL;
gcbptr curr, last;
C_ENTER;
while (group->branch != NULL)
tsk_remove_group (group->branch, 1);
if (group->home == NULL)
{
tsk_kill_group (group);
tsk_remove_tasker ();
return 1;
}
last = NULL;
curr = group->home->branch;
while (curr != NULL && curr != group)
{
last = curr;
curr = curr->level;
}
if (curr == NULL)
return -1;
if (last == NULL)
group->home->branch = group->level;
else
last->level = group->level;
GLOBDATA current_task->group = group->home;
tsk_kill_group (group);
#if (DOS)
if (freemem)
tsk_free_mem (group->create_psp);
#endif
C_LEAVE;
if (!freemem)
preempt_on ();
return 0;
}
#endif
/*
remove_tasker
Front-end for remove_group/tsk_remove_tasker.
Kills the current task group, and/or un-installs CTask.
*/
void far remove_tasker (void)
{
#if (GROUPS)
tsk_remove_group (GLOBDATA current_task->group, 0);
#else
tsk_remove_tasker ();
#endif
}
/* --------------------------------------------------------------------- */
/*
preempt_off
Turns off task preemption (will stay off until explicitly enabled).
*/
void far preempt_off (void)
{
GLOBDATA preempt = 1;
}
/*
preempt_on
Resets permanent and temporary task preemption flag. If
preemption is pending, the scheduler is called.
*/
void far preempt_on (void)
{
GLOBDATA preempt = 0;
tsk_cli ();
if (GLOBDATA pretick)
schedule ();
tsk_sti ();
}
/*
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 (!(GLOBDATA preempt &= ~2))
if (GLOBDATA pretick)
schedule ();
tsk_sti ();
}
/*
tsk_dis_preempt
Sets temporary task preemption flag.
*/
void far tsk_dis_preempt (void)
{
GLOBDATA preempt |= 2;
}