home *** CD-ROM | disk | FTP | other *** search
/ Shareware Overload / ShartewareOverload.cdr / progm / ctask.zip / TSKMAIN.C < prev    next >
C/C++ Source or Header  |  1988-03-01  |  12KB  |  486 lines

  1. /*
  2.    TSKMAIN.C - CTask - Main routines for task handling.
  3.  
  4.    CTask - a Multitasking Kernel for C
  5.  
  6.    Public Domain Software written by
  7.       Thomas Wagner
  8.       Patschkauer Weg 31
  9.       D-1000 Berlin 33
  10.       West Germany
  11.  
  12.    No rights reserved.
  13. */
  14.  
  15. #include <stdio.h>
  16.  
  17. #include "tsk.h"
  18. #include "tsklocal.h"
  19.  
  20. #define STACKSIZE 512
  21.  
  22. /*
  23.    struct task_stack describes the contents of a tasks stack after creation.
  24.    The first 10 words are the registers to be restored by the scheduler.
  25.    Only the segment registers are significant initially.
  26.    The next three words contain the function address plus the CPU flags
  27.    setup as if an interrupt had occurred at the function's entry address.
  28.  
  29.    This setup is the same as the stack of an interrupted task after
  30.    scheduling.
  31.  
  32.    The following two words contain a dummy return address, which points
  33.    to the routine "killretn". Thus, if the task main function should ever
  34.    return, the task is automatically killed. The last doubleword is
  35.    used for the optional argument to the task.
  36. */
  37.  
  38. struct task_stack {
  39.                   word     r_es;
  40.                   word     r_ds;
  41.                   word     r_di;
  42.                   word     r_si;
  43.                   word     r_bp;
  44.                   word     r_sp;
  45.                   word     r_bx;
  46.                   word     r_dx;
  47.                   word     r_cx;
  48.                   word     r_ax;
  49.                   funcptr  retn;
  50.                   word     r_flags;
  51.                   funcptr  dummyret;
  52.                   farptr   arg;
  53.                   };
  54.  
  55. /*
  56.    The task queues:
  57.       All tasks using a timeout, either through "t_delay" or an event wait,
  58.       are enqueued into the "tsk_timer" dual link queue, using the "timerq"
  59.       link.
  60.       All tasks eligible for running are enqueued in "tsk_eligible".
  61.       The tcb-address of the current running task is stored in "tsk_current".
  62. */
  63.  
  64. dlink  _Near tsk_timer;
  65. tcbptr _Near tsk_eligible;
  66. tcbptr _Near tsk_current;
  67.  
  68. /*
  69.    System flags:
  70.       tsk_preempt is zero if preemption is allowed.
  71.                   Bit 0 is set if preemption has been disabled globally.
  72.                   Bit 1 is set for temporary disabling preemption.
  73.                   Temporary preemption is automatically removed by the
  74.                   scheduler.
  75.  
  76.       tsk_pretick is nonzero if a schedule request from an interrupt handler
  77.                   was rejected due to tsk_preempt nonzero. This allows
  78.                   an immediate scheduling whenever tsk_preempt is set to 0.
  79.  
  80.       tsk_var_prior Can be set nonzero to enable variable priority.
  81.                     Variable priority will increase the priority of
  82.                     eligible tasks on each scheduler call while they 
  83.                     are waiting to be executed, so that low priority 
  84.                     tasks will slowly get to the head of the eligible
  85.                     queue, getting a chance to be run. With variable
  86.                     priority off, lower priority tasks will never be
  87.                     executed while higher priority tasks are eligible.
  88.                     
  89. */
  90.  
  91. byte _Near tsk_preempt;
  92. byte _Near tsk_pretick;
  93. byte _Near tsk_var_prior;
  94.  
  95. /* --------------------------------------------------------------------- */
  96.  
  97. /*
  98.    The tcb's of the standard tasks.
  99.  
  100.       timer_tcb   is the tcb for the timer task.
  101.                   This task waits for the tsk_timer_counter, which is
  102.                   increased on every timer tick. It then chains to the
  103.                   previous timer interrupt entry. Next the timeout queue
  104.                   is scanned.
  105.  
  106.       main_tcb    is the "main" task which called "install_tasker". This
  107.                   task has no separate stack, rather the stack on entry
  108.                   to the scheduler is used.
  109.  
  110.       idle_tcb    is the idle task. This task is handled in a special way 
  111.                   by the scheduler. It is not enqueued in any queue, and 
  112.                   is only activated when no other task is able to run. 
  113.                   This task currently does nothing, but might be extended 
  114.                   to do some housekeeping if desired (although it may
  115.                   never use any functions which might cause the idle task
  116.                   to be made waiting).
  117. */
  118.  
  119. tcb      _Near idle_tcb;
  120. counter  _Near tsk_timer_counter;
  121.  
  122. local tcb timer_tcb;
  123. local tcb main_tcb;
  124.  
  125. local char idle_stack [STACKSIZE];
  126. local char timer_stack [STACKSIZE];
  127.  
  128. /* --------------------------------------------------------------------- */
  129.  
  130. #pragma check_stack(off)
  131.  
  132. /*
  133.    Killretn kills the current active task. It is used internally, but
  134.    can also be called from outside.
  135. */
  136.  
  137. void far killretn (void)
  138. {
  139.    tsk_cli ();
  140.    tsk_current->queue = NULL;
  141.    tsk_current->state = ST_KILLED;
  142.    schedule ();
  143. }
  144.  
  145. /*
  146.    Idle is the idle task. It's tcb may never be manipulated.
  147. */
  148.  
  149. local void far idle (void)
  150. {
  151.    while (1)
  152.       schedule ();
  153. }
  154.  
  155.  
  156. /*
  157.    The timer task handles all timeouts.
  158. */
  159.  
  160. local void far timer (void)
  161. {
  162.    dlinkptr curr;
  163.    dlinkptr nxt;
  164.    tcbptr task;
  165.    byte st;
  166.    CRITICAL;
  167.  
  168.    while (1)
  169.       {
  170.       wait_counter_set (&tsk_timer_counter, 0L);
  171.       tsk_chain_timer ();
  172.  
  173.       C_ENTER;
  174.       curr = tsk_timer.follow;
  175.       while (curr->tcbp != NULL)
  176.          {
  177.          nxt = curr->follow;
  178.          if (!--curr->timeout)
  179.             {
  180.             task = curr->tcbp;
  181.             st = task->state;
  182.  
  183.             if (st == ST_WAITING || st == ST_DELAYED)
  184.                {
  185.                task->retptr = TTIMEOUT;
  186.                tsk_wakeup (task);
  187.                }
  188.             else
  189.                tsk_unqtimer (task);
  190.             }
  191.          curr = nxt;
  192.          }
  193.       C_LEAVE;
  194.       }
  195. }
  196.  
  197. /* ---------------------------------------------------------------------- */
  198.  
  199. /*
  200.    create_task
  201.       Initialises a tcb. The task is in stopped state initially.
  202. */
  203.  
  204. void far create_task (tcbptr task,
  205.                       funcptr func,
  206.                       byteptr stack,
  207.                       word stksz,
  208.                       word prior,
  209.                       farptr arg)
  210. {
  211.    struct task_stack far *stk;
  212.  
  213.    stk = (struct task_stack far *)(stack + stksz - sizeof (struct task_stack));
  214.    stk->r_ds = stk->r_es = tsk_dseg ();
  215.    stk->r_bp = 0;
  216.    stk->r_flags = tsk_flags ();
  217.    stk->retn = func;
  218.    stk->dummyret = killretn;
  219.    stk->arg = arg;
  220.  
  221.    task->stkbot = stack;
  222.    task->stack = (byteptr) stk;
  223.    task->next = NULL;
  224.    task->queue = NULL;
  225.    task->state = ST_STOPPED;
  226.    task->flags = 0;
  227.    task->prior = task->initprior = prior;
  228.    task->timerq.timeout = 0;
  229.    task->timerq.tcbp = task;
  230. }
  231.  
  232.  
  233. /*
  234.    kill_task
  235.       Removes a task from the system.
  236. */
  237.  
  238. void far kill_task (tcbptr task)
  239. {
  240.    CRITICAL;
  241.  
  242.    C_ENTER;
  243.    if (task->state == ST_RUNNING)
  244.       {
  245.       task->queue = NULL;
  246.       task->state = ST_KILLED;
  247.       schedule ();
  248.       }
  249.    else
  250.       {
  251.       tsk_unqueue (task);
  252.       tsk_unqtimer (task);
  253.       task->state = ST_KILLED;
  254.       }
  255.    C_LEAVE;
  256. }
  257.  
  258.  
  259. /*
  260.    start_task
  261.       Starts a stopped task. Returns -1 if the task was not stopped.
  262. */
  263.  
  264. int far start_task (tcbptr task)
  265. {
  266.    CRITICAL;
  267.  
  268.    if (task == NULL)
  269.       task = &main_tcb;
  270.  
  271.    if (task->state == ST_STOPPED)
  272.       {
  273.       task->state = ST_ELIGIBLE;
  274.       C_ENTER;
  275.       tsk_enqueue (task, &tsk_eligible);
  276.       C_LEAVE;
  277.       return 0;
  278.       }
  279.    return -1;
  280. }
  281.  
  282.  
  283. /*
  284.    wake_task
  285.       Restarts a task waiting for an event or timeout. 
  286.       Returns -1 if the task was not waiting or stopped.
  287. */
  288.  
  289. int far wake_task (tcbptr task)
  290. {
  291.    CRITICAL;
  292.  
  293.    if (task == NULL)
  294.       task = &main_tcb;
  295.  
  296.    C_ENTER;
  297.    if (task->state >= ST_ELIGIBLE)
  298.       {
  299.       C_LEAVE;
  300.       return -1;
  301.       }
  302.  
  303.    task->retptr = TWAKE;
  304.    tsk_wakeup (task);
  305.    C_LEAVE;
  306.    return 0;
  307. }
  308.  
  309.  
  310. /*
  311.    set_priority
  312.       Changes the priority of a task. If the task is enqueued in a
  313.       queue, its position in the queue is affected.
  314. */
  315.  
  316. void far set_priority (tcbptr task, word prior)
  317. {
  318.    tqueptr que;
  319.    CRITICAL;
  320.  
  321.    if (task == NULL)
  322.       task = &main_tcb;
  323.  
  324.    C_ENTER;
  325.    task->prior = task->initprior = prior;
  326.  
  327.    if ((task->state != ST_RUNNING) && ((que = task->queue) != NULL))
  328.       {
  329.       tsk_unqueue (task);
  330.       tsk_enqueue (task, que);
  331.       }
  332.    C_LEAVE;
  333. }
  334.  
  335. /*
  336.    set_task_flags
  337.       Changes the user modifiable flags of the task.
  338. */
  339.  
  340. void far set_task_flags (tcbptr task, byte flags)
  341. {
  342.    CRITICAL;
  343.  
  344.    if (task == NULL)
  345.       task = &main_tcb;
  346.  
  347.    C_ENTER;
  348.    task->flags = (task->flags & FL_SYSM) | (flags & FL_USRM);
  349.    C_LEAVE;
  350. }
  351.  
  352.  
  353. /* --------------------------------------------------------------------- */
  354.  
  355.  
  356. /*
  357.    t_delay
  358.       delay the current task by "ticks" clock ticks.
  359.       If ticks is zero, the task is stopped.
  360. */
  361.  
  362. int far t_delay (dword ticks)
  363. {
  364.    tsk_cli ();
  365.    tsk_current->queue = NULL;
  366.    if (ticks)
  367.       {
  368.       tsk_current->state = ST_DELAYED;
  369.       tsk_enqtimer (tsk_current, ticks);
  370.       }
  371.    else
  372.       tsk_current->state = ST_STOPPED;
  373.  
  374.    schedule ();
  375.    return (int)tsk_current->retptr;
  376. }
  377.  
  378.  
  379. /* --------------------------------------------------------------------- */
  380.  
  381.  
  382. /*
  383.    install_tasker
  384.       Installs the Ctask system. The internal tasks are created,
  385.       the queues are initialised, and the interrupt handler installation
  386.       routines are called. Task preemption is initially off.
  387.  
  388.       Handling of the speedup parameter is system dependent.
  389. */
  390.  
  391. void far install_tasker (byte varpri, int speedup)
  392. {
  393.    word divisor, sys_ticks;
  394.  
  395.    tsk_current = &main_tcb;
  396.    tsk_eligible = NULL;
  397.    tsk_timer.follow = tsk_timer.prev = &tsk_timer;
  398.    tsk_timer.tcbp = NULL;
  399.    tsk_preempt = 1;
  400.    tsk_pretick = 0;
  401.    tsk_var_prior = varpri;
  402.  
  403.    create_task (&idle_tcb, idle, idle_stack, STACKSIZE, 0, NULL);
  404.    create_task (&timer_tcb, timer, timer_stack, STACKSIZE, 0xffff, NULL);
  405.    create_counter (&tsk_timer_counter);
  406.  
  407.    main_tcb.prior = main_tcb.initprior = 0xfffe;
  408.    main_tcb.queue = &tsk_eligible;
  409.    main_tcb.flags = F_CRIT;
  410.    main_tcb.state = ST_RUNNING;
  411.    start_task (&timer_tcb);
  412.  
  413.    if (speedup <= 0 || speedup > 8)
  414.       {
  415.       divisor = 0;
  416.       sys_ticks = 1;
  417.       }
  418.    else
  419.       {
  420.       divisor = 0x8000 >> (speedup - 1);
  421.       sys_ticks = 1 << speedup;
  422.       }
  423.  
  424.    tsk_install_timer (divisor, sys_ticks);
  425.    tsk_install_dos ();
  426.    tsk_install_kbd ();
  427. }
  428.  
  429.  
  430. /*
  431.    preempt_off
  432.       Turns off task preemption (will stay off until explicitly enabled).
  433. */
  434.  
  435. void far preempt_off (void)
  436. {
  437.    tsk_preempt = 1;
  438. }
  439.  
  440.  
  441. /*
  442.    preempt_on
  443.       Resets permanent and temporary task preemption flag. If 
  444.       preemption is pending, the scheduler is called.
  445. */
  446.  
  447. void far preempt_on (void)
  448. {
  449.    tsk_preempt = 0;
  450.    tsk_cli ();
  451.    if (tsk_pretick)
  452.       schedule ();
  453.    tsk_sti ();
  454. }
  455.  
  456.  
  457. /*
  458.    remove_tasker
  459.       Calls the interrupt handler un-install routines.
  460. */
  461.  
  462. void far remove_tasker (void)
  463. {
  464.    tsk_preempt = 0;
  465.    tsk_remove_timer ();
  466.    tsk_remove_dos ();
  467.    tsk_remove_kbd ();
  468. }
  469.  
  470.  
  471. /*
  472.    tsk_ena_preempt
  473.       Resets temporary task preemption flag. If preemption is pending,
  474.       the scheduler is called.
  475. */
  476.  
  477. void far tsk_ena_preempt (void)
  478. {
  479.    tsk_cli ();
  480.    if (!(tsk_preempt &= ~2))
  481.       if (tsk_pretick)
  482.          schedule ();
  483.    tsk_sti ();
  484. }
  485.  
  486.