home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / progc / cpptask.arj / TSKMAIN.CPP < prev    next >
C/C++ Source or Header  |  1991-08-21  |  9KB  |  388 lines

  1. /*
  2.    CPPTask - A Multitasking Kernel For C++
  3.  
  4.    Version 1.0 08-12-91
  5.  
  6.    Ported by Rich Smith from:
  7.  
  8.    Public Domain Software written by
  9.       Thomas Wagner
  10.       Patschkauer Weg 31
  11.       D-1000 Berlin 33
  12.       West Germany
  13.  
  14.    TSKMAIN.CPP - Main routines for task handling.
  15.  
  16.    Subroutines:
  17.         timer_main
  18.         int9
  19.         tasker_class::tasker_class
  20.         tasker_class::~tasker_class
  21.         tasker_class::preempt_on
  22.         tasker_class::preempt_off
  23.         tasker_class::tsk_ena_preempt
  24.         tasker_class::tsk_dis_preempt
  25.         tasker_class::schedule
  26.         tasker_class::c_schedule
  27.         asm_remove_tasker
  28.  
  29. */
  30.  
  31. #include <stdio.h>
  32.  
  33. #include "task.hpp"
  34. #include "tsklocal.hpp"
  35.  
  36. #define STACKSIZE 512
  37.  
  38. /*
  39.    The task queues:
  40.       All tasks using a timeout, either through "t_delay" or an event wait,
  41.       are enqueued into the "tsk_timer" queue, using the "timerq" link.
  42.       All tasks eligible for running are enqueued in "tsk_eligible".
  43.       The tcb-address of the current running task is stored in "tsk_current".
  44. */
  45.  
  46. tlinkptr   tsk_timer = NULL;
  47. timer      null_timer_node(0, NULL, TKIND_TASK, 0);
  48. tcbptr     tsk_eligible;
  49. tcbptr     tsk_current;
  50.  
  51. /*
  52.    System flags:
  53.       tsk_preempt is zero if preemption is allowed.
  54.                   Bit 0 is set if preemption has been disabled globally.
  55.                   Bit 1 is set for temporary disabling preemption.
  56.                   Temporary preemption is automatically removed by the
  57.                   scheduler.
  58.  
  59.       tsk_pretick is nonzero if a schedule request from an interrupt handler
  60.                   was rejected due to tsk_preempt nonzero. This allows
  61.                   an immediate scheduling whenever tsk_preempt is set to 0.
  62.  
  63.       tsk_var_prior Can be set nonzero to enable variable priority.
  64.                     Variable priority will increase the priority of
  65.                     eligible tasks on each scheduler call while they 
  66.                     are waiting to be executed, so that low priority 
  67.                     tasks will slowly get to the head of the eligible
  68.                     queue, getting a chance to be run. With variable
  69.                     priority off, lower priority tasks will never be
  70.                     executed while higher priority tasks are eligible.
  71.                     
  72. */
  73.  
  74. byte  tsk_preempt;
  75. byte  tsk_pretick;
  76. byte  tsk_var_prior;
  77.  
  78. /* --------------------------------------------------------------------- */
  79.  
  80. /*
  81.    The tcb's of the standard tasks.
  82.  
  83.       timer_tcb   is the tcb for the timer task.
  84.                   This task waits for the tsk_timer_counter, which is
  85.                   increased on every timer tick. It processes the entries
  86.                   in the timeout queue.
  87.  
  88.       int9_tcb    is the tcb for the int9 chain task.
  89.                   This task waits for the tsk_int9_counter, which is
  90.                   increased on every system timer tick. It then chains to
  91.                   the previous timer interrupt entry.
  92.  
  93.       main_tcb    is the "main" task which called "install_tasker". This
  94.                   task has no separate stack, rather the stack on entry
  95.                   to the scheduler is used.
  96.  
  97. */
  98.  
  99. counter tsk_timer_counter;
  100. local char timer_stack [STACKSIZE];
  101. local void far timer_main (void);
  102. local task timer_tcb((funcptr) timer_main, (byteptr) timer_stack,
  103.                      STACKSIZE, PRI_TIMER, NULL);
  104.  
  105. local task main_tcb(NULL, NULL, STACKSIZE, PRI_TIMER, NULL);
  106.  
  107. #if (IBM)
  108. counter tsk_int9_counter;
  109. local void far int9 (void);
  110. local char int9_stack [STACKSIZE];
  111. local task int9_tcb((funcptr) int9, (byteptr) int9_stack,
  112.                     STACKSIZE, PRI_INT9, NULL);
  113. #endif
  114.  
  115. resource alloc_resource;
  116.  
  117. #if (DOS)
  118. resource lower_dos;
  119. resource upper_dos;
  120. flag critical;
  121. #endif
  122.  
  123. word keyboardbuffer[80];
  124. wpipe key_avail((farptr) keyboardbuffer, sizeof(keyboardbuffer));
  125.  
  126. tasker_class tasker(0, 0);
  127.  
  128. #if (CLOCK_MSEC)
  129. double  tick_factor;
  130. #endif
  131.  
  132. word  ticks_per_sec;
  133.  
  134.  
  135. /*
  136.    Un-Install-Function pointers for the optional serial and printer 
  137.    drivers. If ports are installed, the driver inserts the address
  138.    of a remove-function here, to be called on removal of the main
  139.    tasker.
  140. */
  141.  
  142. funcptr v24_remove_func = NULL;
  143. funcptr prt_remove_func = NULL;
  144.  
  145.  
  146. /* --------------------------------------------------------------------- */
  147.  
  148. #pragma check_stack(off)
  149.  
  150. /*
  151.    The timer_main task handles all timeouts.
  152.    It maintains a single timer queue, which contains elements of the
  153.    "tlink" structure. No other task is allowed to manipulate this queue,
  154.    except for the insertion of new elements at the queue head. This allows
  155.    stepping through the queue with interrupts enabled.
  156.    CAUTION: This assumes that the operation of loading a far pointer
  157.             is indivisible!
  158. */
  159.  
  160. local void far timer_main (void)
  161. {
  162.    tlinkptr curr;
  163.    tlinkptr last;
  164.    byte state;
  165.    CRITICAL;
  166.  
  167.    while (1)
  168.       {
  169.       tsk_timer_counter.wait_counter_set (0L);
  170.  
  171.       last = (tlinkptr) &tsk_timer;
  172.  
  173.       while ((curr = last->next) != NULL)
  174.          {
  175.          /* Enter critical section for access to state and timeout
  176.             variables. The timer action is also critical.
  177.          */
  178.          C_ENTER;
  179.          if ((state = curr->tstate) >= TSTAT_COUNTDOWN)
  180.             if (!--curr->timeout)
  181.                {
  182.                if (state == TSTAT_COUNTDOWN)
  183.                   state = (byte) TSTAT_REMOVE;
  184.                else
  185.                   curr->timeout = curr->reload;
  186.  
  187.                curr->tsk_timer_action();
  188.                }
  189.          if (state == (byte) TSTAT_REMOVE)
  190.             {
  191.             last->next = curr->next;
  192.             curr->tstate = TSTAT_IDLE;
  193.  
  194.             if (curr->tkind & TKIND_TEMP)
  195.                delete curr;
  196.  
  197.             curr = last;
  198.             }
  199.          C_LEAVE;
  200.          last = curr;
  201.          }
  202.       }
  203. }
  204.  
  205. /*
  206.    int9 is the timer interrupt chain task.
  207. */
  208.  
  209. #if (IBM)
  210.  
  211. local void far int9 (void)
  212. {
  213.    while (1)
  214.       {
  215.       tsk_int9_counter.wait_counter_set (0L);
  216.       tsk_chain_timer ();
  217.       }
  218. }
  219.  
  220. #endif
  221.  
  222. /* --------------------------------------------------------------------- */
  223.  
  224.  
  225. /*
  226.    tasker_class
  227.       Installs the Ctask system. The internal tasks are created,
  228.       the queues are initialised, and the interrupt handler installation
  229.       routines are called. Task preemption is initially off.
  230.  
  231.       Handling of the speedup parameter is system dependent.
  232. */
  233.  
  234. tasker_class::tasker_class (byte varpri, int speedup)
  235. {
  236.    word divisor, sys_ticks;
  237.  
  238.    tsk_current = &main_tcb;
  239.    tsk_eligible = NULL;
  240.    tsk_timer = &null_timer_node;
  241.    tsk_preempt = 1;
  242.    tsk_pretick = 0;
  243.    tsk_var_prior = varpri;
  244.  
  245.    timer_tcb.start_task ();
  246.  
  247. #if (IBM)
  248.    int9_tcb.start_task ();
  249.  
  250.    if (speedup <= 0 || speedup > 8)
  251.       {
  252.       divisor = 0;
  253.       sys_ticks = 1;
  254.       }
  255.    else
  256.       {
  257.       divisor = 0x8000 >> (speedup - 1);
  258.       sys_ticks = 1 << speedup;
  259.       }
  260.  
  261.    ticks_per_sec = 18 * sys_ticks;  /* rough number only */
  262.  
  263. #if (CLOCK_MSEC)
  264.    tick_factor = (65536.0 / (double)sys_ticks) / 1193.18;
  265. #endif
  266.  
  267.      tsk_install_timer (divisor, sys_ticks);
  268.      tsk_install_kbd ();
  269. #endif
  270.  
  271. #if (AT_BIOS)
  272. //   tsk_install_bios ();
  273. #endif
  274.  
  275. #if (DOS)
  276.      tsk_install_dos ();
  277. #endif
  278. }
  279.  
  280.  
  281. /*
  282.    ~tasker_class
  283.       Calls the interrupt handler un-install routines.
  284. */
  285.  
  286. tasker_class::~tasker_class (void)
  287. {
  288.    tsk_preempt = 0;
  289.  
  290. #if (AT_BIOS)
  291. //   tsk_remove_bios ();
  292. #endif
  293. #if (IBM)
  294.  
  295.    if (v24_remove_func != NULL)
  296.       v24_remove_func ();
  297.    if (prt_remove_func != NULL)
  298.       prt_remove_func ();
  299.  
  300.    /* Allow all stored clock ticks to be processed */
  301.    int9_tcb.set_priority (0xffff);
  302.    while (tsk_int9_counter.check_counter ())
  303.       schedule();
  304.  
  305.      tsk_remove_timer ();
  306.      tsk_remove_kbd ();
  307. #endif
  308.  
  309. #if (DOS)
  310.      tsk_remove_dos ();
  311. #endif
  312. }
  313.  
  314.  
  315. /*
  316.    preempt_off
  317.       Turns off task preemption (will stay off until explicitly enabled).
  318. */
  319.  
  320. void far tasker_class::preempt_off (void)
  321. {
  322.    tsk_preempt = 1;
  323. }
  324.  
  325.  
  326. /*
  327.    preempt_on
  328.       Resets permanent and temporary task preemption flag. If 
  329.       preemption is pending, the scheduler is called.
  330. */
  331.  
  332. void far tasker_class::preempt_on (void)
  333. {
  334.    tsk_preempt = 0;
  335.    tsk_cli ();
  336.    if (tsk_pretick)
  337.       schedule ();
  338.    tsk_sti ();
  339. }
  340.  
  341.  
  342. /*
  343.    tsk_ena_preempt
  344.       Resets temporary task preemption flag. If preemption is pending,
  345.       the scheduler is called.
  346. */
  347.  
  348. void far tasker_class::tsk_ena_preempt (void)
  349. {
  350.    tsk_cli ();
  351.    if (!(tsk_preempt &= ~2))
  352.       if (tsk_pretick)
  353.          schedule ();
  354.    tsk_sti ();
  355. }
  356.  
  357.  
  358. /*
  359.    tsk_dis_preempt
  360.       Sets temporary task preemption flag.
  361. */
  362.  
  363. void far tasker_class::tsk_dis_preempt (void)
  364. {
  365.    tsk_preempt |= 2;
  366. }
  367.  
  368.  
  369. void far tasker_class::schedule(void)
  370. {
  371.    asm_schedule();
  372. }
  373.  
  374.  
  375.  
  376. void far tasker_class::c_schedule(void)
  377. {
  378.    asm_c_schedule();
  379. }
  380.  
  381.  
  382. void far asm_remove_tasker(void)
  383. {
  384.     tasker.tasker_class::~tasker_class();
  385. }
  386.  
  387.  
  388.