home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Black Box 4
/
BlackBox.cdr
/
progc
/
cpptask.arj
/
TSKMAIN.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1991-08-21
|
9KB
|
388 lines
/*
CPPTask - A Multitasking Kernel For C++
Version 1.0 08-12-91
Ported by Rich Smith from:
Public Domain Software written by
Thomas Wagner
Patschkauer Weg 31
D-1000 Berlin 33
West Germany
TSKMAIN.CPP - Main routines for task handling.
Subroutines:
timer_main
int9
tasker_class::tasker_class
tasker_class::~tasker_class
tasker_class::preempt_on
tasker_class::preempt_off
tasker_class::tsk_ena_preempt
tasker_class::tsk_dis_preempt
tasker_class::schedule
tasker_class::c_schedule
asm_remove_tasker
*/
#include <stdio.h>
#include "task.hpp"
#include "tsklocal.hpp"
#define STACKSIZE 512
/*
The task queues:
All tasks using a timeout, either through "t_delay" or an event wait,
are enqueued into the "tsk_timer" 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".
*/
tlinkptr tsk_timer = NULL;
timer null_timer_node(0, NULL, TKIND_TASK, 0);
tcbptr tsk_eligible;
tcbptr 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 tsk_preempt;
byte tsk_pretick;
byte 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 processes the entries
in the timeout queue.
int9_tcb is the tcb for the int9 chain task.
This task waits for the tsk_int9_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.
*/
counter tsk_timer_counter;
local char timer_stack [STACKSIZE];
local void far timer_main (void);
local task timer_tcb((funcptr) timer_main, (byteptr) timer_stack,
STACKSIZE, PRI_TIMER, NULL);
local task main_tcb(NULL, NULL, STACKSIZE, PRI_TIMER, NULL);
#if (IBM)
counter tsk_int9_counter;
local void far int9 (void);
local char int9_stack [STACKSIZE];
local task int9_tcb((funcptr) int9, (byteptr) int9_stack,
STACKSIZE, PRI_INT9, NULL);
#endif
resource alloc_resource;
#if (DOS)
resource lower_dos;
resource upper_dos;
flag critical;
#endif
word keyboardbuffer[80];
wpipe key_avail((farptr) keyboardbuffer, sizeof(keyboardbuffer));
tasker_class tasker(0, 0);
#if (CLOCK_MSEC)
double tick_factor;
#endif
word ticks_per_sec;
/*
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.
*/
funcptr v24_remove_func = NULL;
funcptr prt_remove_func = NULL;
/* --------------------------------------------------------------------- */
#pragma check_stack(off)
/*
The timer_main task handles all timeouts.
It maintains a single timer queue, which contains elements of the
"tlink" structure. No other task is allowed to manipulate this queue,
except for the insertion of new elements at the queue head. This allows
stepping through the queue with interrupts enabled.
CAUTION: This assumes that the operation of loading a far pointer
is indivisible!
*/
local void far timer_main (void)
{
tlinkptr curr;
tlinkptr last;
byte state;
CRITICAL;
while (1)
{
tsk_timer_counter.wait_counter_set (0L);
last = (tlinkptr) &tsk_timer;
while ((curr = last->next) != NULL)
{
/* Enter critical section for access to state and timeout
variables. The timer action is also critical.
*/
C_ENTER;
if ((state = curr->tstate) >= TSTAT_COUNTDOWN)
if (!--curr->timeout)
{
if (state == TSTAT_COUNTDOWN)
state = (byte) TSTAT_REMOVE;
else
curr->timeout = curr->reload;
curr->tsk_timer_action();
}
if (state == (byte) TSTAT_REMOVE)
{
last->next = curr->next;
curr->tstate = TSTAT_IDLE;
if (curr->tkind & TKIND_TEMP)
delete curr;
curr = last;
}
C_LEAVE;
last = curr;
}
}
}
/*
int9 is the timer interrupt chain task.
*/
#if (IBM)
local void far int9 (void)
{
while (1)
{
tsk_int9_counter.wait_counter_set (0L);
tsk_chain_timer ();
}
}
#endif
/* --------------------------------------------------------------------- */
/*
tasker_class
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.
*/
tasker_class::tasker_class (byte varpri, int speedup)
{
word divisor, sys_ticks;
tsk_current = &main_tcb;
tsk_eligible = NULL;
tsk_timer = &null_timer_node;
tsk_preempt = 1;
tsk_pretick = 0;
tsk_var_prior = varpri;
timer_tcb.start_task ();
#if (IBM)
int9_tcb.start_task ();
if (speedup <= 0 || speedup > 8)
{
divisor = 0;
sys_ticks = 1;
}
else
{
divisor = 0x8000 >> (speedup - 1);
sys_ticks = 1 << speedup;
}
ticks_per_sec = 18 * sys_ticks; /* rough number only */
#if (CLOCK_MSEC)
tick_factor = (65536.0 / (double)sys_ticks) / 1193.18;
#endif
tsk_install_timer (divisor, sys_ticks);
tsk_install_kbd ();
#endif
#if (AT_BIOS)
// tsk_install_bios ();
#endif
#if (DOS)
tsk_install_dos ();
#endif
}
/*
~tasker_class
Calls the interrupt handler un-install routines.
*/
tasker_class::~tasker_class (void)
{
tsk_preempt = 0;
#if (AT_BIOS)
// tsk_remove_bios ();
#endif
#if (IBM)
if (v24_remove_func != NULL)
v24_remove_func ();
if (prt_remove_func != NULL)
prt_remove_func ();
/* Allow all stored clock ticks to be processed */
int9_tcb.set_priority (0xffff);
while (tsk_int9_counter.check_counter ())
schedule();
tsk_remove_timer ();
tsk_remove_kbd ();
#endif
#if (DOS)
tsk_remove_dos ();
#endif
}
/*
preempt_off
Turns off task preemption (will stay off until explicitly enabled).
*/
void far tasker_class::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 tasker_class::preempt_on (void)
{
tsk_preempt = 0;
tsk_cli ();
if (tsk_pretick)
schedule ();
tsk_sti ();
}
/*
tsk_ena_preempt
Resets temporary task preemption flag. If preemption is pending,
the scheduler is called.
*/
void far tasker_class::tsk_ena_preempt (void)
{
tsk_cli ();
if (!(tsk_preempt &= ~2))
if (tsk_pretick)
schedule ();
tsk_sti ();
}
/*
tsk_dis_preempt
Sets temporary task preemption flag.
*/
void far tasker_class::tsk_dis_preempt (void)
{
tsk_preempt |= 2;
}
void far tasker_class::schedule(void)
{
asm_schedule();
}
void far tasker_class::c_schedule(void)
{
asm_c_schedule();
}
void far asm_remove_tasker(void)
{
tasker.tasker_class::~tasker_class();
}