home *** CD-ROM | disk | FTP | other *** search
- /*
- --- 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;
- }
-
-
-
-