home *** CD-ROM | disk | FTP | other *** search
/ Frostbyte's 1980s DOS Shareware Collection / floppyshareware.zip / floppyshareware / DOOG / CTASK.ZIP / TSKMAIN.C < prev    next >
C/C++ Source or Header  |  1989-12-19  |  19KB  |  896 lines

  1. /*
  2.     --- Version 2.0 89-12-17 21:20 ---
  3.  
  4.    TSKMAIN.C - CTask - Main routines for task handling.
  5.  
  6.    CTask - a Multitasking Kernel for C
  7.  
  8.    Public Domain Software written by
  9.       Thomas Wagner
  10.       Patschkauer Weg 31
  11.       D-1000 Berlin 33
  12.       West Germany
  13.  
  14.    No rights reserved.
  15.  
  16.       Changes from V1.1: - Support for task groups and secondary 
  17.                            invocations added, moved global vars into
  18.                            structure.
  19.                          - stop_task function and other goodies added.
  20.                          - timer functions moved to separate module.
  21. */
  22.  
  23. #include <stdio.h>
  24.  
  25. #include "tsk.h"
  26. #include "tsklocal.h"
  27.  
  28. #define STACKSIZE 512
  29.  
  30. ctask_globvars _Near tsk_glob_rec;
  31. globvarptr _Near tsk_global = NULL;
  32. word _Near tsk_instflags;
  33. #if (GROUPS)
  34. char _Near tsk_version [8] = VERSTRING;
  35. #endif
  36.  
  37. /*
  38.    The tcb's of the standard tasks.
  39.  
  40.       timer_tcb   is the tcb for the timer task.
  41.                   This task waits for the tsk_timer_counter, which is
  42.                   increased on every timer tick. It processes the entries
  43.                   in the timeout queue.
  44.  
  45.       int8_tcb    is the tcb for the int8 chain task.
  46.                   This task waits for the tsk_int8_counter, which is
  47.                   increased on every system timer tick. It then chains to
  48.                   the previous timer interrupt entry.
  49.  
  50.       main_tcb    is the "main" task which called "install_tasker". This
  51.                   task has no separate stack, rather the stack on entry
  52.                   to the scheduler is used. Since main_tcb is not used
  53.                   if this is a secondary invocation of CTask, "main_ptr"
  54.                   points to the main_tcb for a primary invocation, and
  55.                   to the underlying task for a secondary invocation.
  56. */
  57.  
  58. local tcb _Near timer_tcb;
  59. local tcb _Near main_tcb;
  60. local tcbptr _Near main_ptr = &main_tcb;
  61.  
  62. local char timer_stack [STACKSIZE];
  63.  
  64. counter  _Near tsk_timer_counter;
  65.  
  66. #if (IBM)
  67. counter  _Near tsk_int8_counter;
  68. local tcb int8_tcb;
  69. local char int8_stack [STACKSIZE];
  70. #endif
  71.  
  72. /*
  73.    Un-Install-Function pointers for the optional serial and printer 
  74.    drivers. If ports are installed, the driver inserts the address
  75.    of a remove-function here, to be called on removal of the main
  76.    tasker.
  77. */
  78.  
  79. #if (DOS)
  80. funcptr_void v24_remove_func = NULL;
  81. funcptr_void prt_remove_func = NULL;
  82. #endif
  83.  
  84. /* --------------------------------------------------------------------- */
  85.  
  86. typedef struct {
  87.                word  int20;
  88.                word  mem_end;
  89.                byte  reserved1;
  90.                byte  syscall [5];
  91.                dword exit_addr;        /* Where DOS jumps to on EXIT */
  92.                dword ctl_c_addr;
  93.                dword crit_err_addr;
  94.                word  parent_psp;
  95.                byte  open_files [20];
  96.                word  env_segment;
  97.                dword caller_stack;     /* SS:SP of caller for SPAWN */
  98.                word  filetab_len;      /* Number of entries in file table */
  99.                dword filetab_ptr;      /* file table pointer */
  100.                dword nested_psp;
  101.                } psp;
  102.  
  103. typedef psp far *pspptr;
  104.  
  105. /* --------------------------------------------------------------------- */
  106.  
  107. #pragma check_stack(off)
  108.  
  109. /*
  110.    Killretn kills the current active task. It is used internally, but
  111.    can also be called from outside.
  112. */
  113.  
  114. void far killretn (void)
  115. {
  116.    tsk_cli ();
  117.    tsk_kill (GLOBDATA current_task);
  118.    GLOBDATA current_task = NULL;
  119.    schedule ();
  120. }
  121.  
  122. /* ---------------------------------------------------------------------- */
  123.  
  124. /*
  125.    create_task
  126.       Initialises a tcb. The task is in stopped state initially.
  127. */
  128.  
  129. tcbptr far create_task (tcbptr task,
  130.                         funcptr func,
  131.                         byteptr stack,
  132.                         word stksz,
  133.                         word prior,
  134.                         farptr arg
  135. #if (TSK_NAMEPAR)
  136.                         ,byteptr name
  137. #endif
  138.                         )
  139.  
  140. {
  141.    struct task_stack far *stk;
  142.  
  143. #if (TSK_DYNAMIC)
  144.    if (task == NULL)
  145.       {
  146.       if ((task = (tcbptr) tsk_alloc (sizeof(tcb))) == NULL)
  147.          return NULL;
  148.       task->flags = F_TEMP;
  149.       }
  150.    else
  151.       task->flags = 0;
  152.  
  153.    if (stack == NULL)
  154.       {
  155.       if ((stack = (byteptr) tsk_alloc (stksz)) == NULL)
  156.          {
  157.          if (task->flags & F_TEMP)
  158.             tsk_free (task);
  159.          return NULL;
  160.          }
  161.       task->flags |= F_STTEMP;
  162.       }
  163. #else
  164.    task->flags = 0;
  165. #endif
  166.  
  167.    stk = (struct task_stack far *)(stack + stksz - sizeof (struct task_stack));
  168.    stk->r_ds = task->t_es = tsk_dseg ();
  169.    stk->r_flags = tsk_flags ();
  170.    stk->retn = func;
  171.    stk->dummyret = killretn;
  172.    stk->arg = arg;
  173.    task->t_bp = 0;
  174.  
  175.    task->stkbot = stack;
  176.    task->stack = (byteptr) stk;
  177.    task->cqueue.kind = TYP_TCB;
  178.    task->cqueue.next = NULL;
  179.    task->qhead = NULL;
  180.    task->state = ST_STOPPED;
  181.    task->cqueue.el.pri.prior = task->cqueue.el.pri.ini_prior = prior;
  182.    task->timerq.link.el.ticks = task->timerq.elem.time.reload = 0;
  183.    task->timerq.link.kind = TKIND_WAKE;
  184.    task->timerq.link.next = NULL;
  185.    task->timerq.strucp = (farptr) task;
  186.    task->timerq.tstate = TSTAT_IDLE;
  187.    task->timerq.flags = 0;
  188.    task->save_func = task->rest_func = NULL;
  189.     task->user_ptr = NULL;
  190.  
  191. #if (GROUPS)
  192.    task->group = task->homegroup = GLOBDATA current_task->group;
  193. #endif
  194. #if (DOS)
  195.    task->indos = 0;
  196.    task->new = 1;
  197.    task->base_psp = task->group->create_psp;
  198. #endif
  199.  
  200. #if (TSK_NAMED)
  201.    tsk_add_name (&task->name, name, TYP_TCB, task);
  202. #endif
  203.  
  204.    return task;
  205. }
  206.  
  207.  
  208. /*
  209.    kill_task
  210.       Removes a task from the system.
  211. */
  212.  
  213. void far kill_task (tcbptr task)
  214. {
  215.    byte st;
  216.    CRITICAL;
  217.  
  218.    C_ENTER;
  219.    if ((st = task->state) != ST_RUNNING)
  220.       {
  221.       if (st == ST_KILLED)
  222.          return;
  223.       else
  224.          tsk_dequeue (&task->cqueue);
  225.       }
  226.  
  227.    tsk_kill (task);
  228.    if (st == ST_RUNNING)
  229.       {
  230.       GLOBDATA current_task = NULL;
  231.       schedule ();
  232.       }
  233.    C_LEAVE;
  234. }
  235.  
  236.  
  237. /*
  238.    start_task
  239.       Starts a stopped task. Returns -1 if the task was not stopped.
  240. */
  241.  
  242. int far start_task (tcbptr task)
  243. {
  244.    CRITICAL;
  245.  
  246.    if (task == NULL)
  247.       task = main_ptr;
  248.  
  249.    if (task->state == ST_STOPPED)
  250.       {
  251.       task->state = ST_ELIGIBLE;
  252.       C_ENTER;
  253.       tsk_runable (task);
  254.       C_LEAVE;
  255.       return 0;
  256.       }
  257.    return -1;
  258. }
  259.  
  260.  
  261. /*
  262.    wake_task
  263.       Restarts a task waiting for an event or timeout. 
  264.       Returns -1 if the task was not waiting or stopped.
  265. */
  266.  
  267. int far wake_task (tcbptr task)
  268. {
  269.    CRITICAL;
  270.  
  271.    if (task == NULL)
  272.       task = main_ptr;
  273.  
  274.    C_ENTER;
  275.    if (task->state >= ST_ELIGIBLE)
  276.       {
  277.       C_LEAVE;
  278.       return -1;
  279.       }
  280.  
  281.    task->retptr = TWAKE;
  282.    tsk_runable (task);
  283.    C_LEAVE;
  284.    return 0;
  285. }
  286.  
  287.  
  288. /*
  289.    stop_task
  290.       Sets a task to the stopped state.
  291. */
  292.  
  293. int far stop_task (tcbptr task)
  294. {
  295.    CRITICAL;
  296.  
  297.    if (task == NULL)
  298.       task = main_ptr;
  299.  
  300.    if (task->state < ST_STOPPED)
  301.       return -1;
  302.  
  303.    C_ENTER;
  304.    task->state = ST_STOPPED;
  305.    tsk_dequeue (&task->cqueue);
  306.    task->qhead = NULL;
  307.    tsk_deqtimer (&task->timerq.link);
  308.    C_LEAVE;
  309.  
  310.    return 0;
  311. }
  312.  
  313.  
  314.  
  315. /*
  316.    get_priority
  317.       Returns the priority of a task.
  318. */
  319.  
  320. word far get_priority (tcbptr task)
  321. {
  322.    if (task == NULL)
  323.       task = main_ptr;
  324.  
  325.    return task->cqueue.el.pri.prior;
  326. }
  327.  
  328.  
  329. /*
  330.    set_priority
  331.       Changes the priority of a task. If the task is enqueued in a
  332.       queue, its position in the queue is affected.
  333. */
  334.  
  335. void far set_priority (tcbptr task, word prior)
  336. {
  337.    queheadptr que;
  338.    CRITICAL;
  339.  
  340.    if (task == NULL)
  341.       task = main_ptr;
  342.  
  343.    C_ENTER;
  344.    task->cqueue.el.pri.prior = task->cqueue.el.pri.ini_prior = prior;
  345.  
  346.    if ((task->state != ST_RUNNING) && ((que = task->qhead) != NULL))
  347.       {
  348.       tsk_dequeue (&task->cqueue);
  349.       tsk_enqueue (que, &task->cqueue);
  350.       }
  351.    C_LEAVE;
  352. }
  353.  
  354. /*
  355.    set_task_flags
  356.       Changes the user modifiable flags of the task.
  357. */
  358.  
  359. void far set_task_flags (tcbptr task, byte flags)
  360. {
  361.    CRITICAL;
  362.  
  363.    if (task == NULL)
  364.       task = main_ptr;
  365.  
  366.    C_ENTER;
  367.    task->flags = (task->flags & FL_SYSM) | (flags & FL_USRM);
  368.    C_LEAVE;
  369. }
  370.  
  371.  
  372. /*
  373.    set_funcs
  374.       Set the save/restore functions in the TCB.
  375. */
  376.  
  377. void far set_funcs (tcbptr task, funcptr save, funcptr rest)
  378. {
  379.    CRITICAL;
  380.  
  381.    if (task == NULL)
  382.       task = main_ptr;
  383.  
  384.    C_ENTER;
  385.    task->save_func = save;
  386.    task->rest_func = rest;
  387.    C_LEAVE;
  388. }
  389.  
  390.  
  391. /*
  392.    set_user_ptr
  393.       Set the user pointer in the TCB.
  394. */
  395.  
  396. farptr far set_user_ptr (tcbptr task, farptr uptr)
  397. {
  398.    farptr old;
  399.    CRITICAL;
  400.  
  401.    if (task == NULL)
  402.       task = main_ptr;
  403.  
  404.    C_ENTER;
  405.    old = task->user_ptr;
  406.    task->user_ptr = uptr;
  407.    C_LEAVE;
  408.  
  409.    return old;
  410. }
  411.  
  412.  
  413. /*
  414.    get_user_ptr
  415.       Returns the user pointer from the TCB.
  416. */
  417.  
  418. farptr far get_user_ptr (tcbptr task)
  419. {
  420.    if (task == NULL)
  421.       task = main_ptr;
  422.  
  423.    return task->user_ptr;
  424. }
  425.  
  426.  
  427. /*
  428.    curr_task
  429.       Returns TCB of current running task.
  430. */
  431.  
  432. tcbptr far curr_task (void)
  433. {
  434.    return GLOBDATA current_task;
  435. }
  436.  
  437.  
  438. /* --------------------------------------------------------------------- */
  439.  
  440. #if(GROUPS)
  441.  
  442. local void near tsk_create_group (gcbptr group, byteptr name)
  443. {
  444.    pspptr pspp;
  445.    CRITICAL;
  446.  
  447. #if (DOS)
  448.    pspp = MK_FP(_psp, 0);
  449. #endif
  450.  
  451.    tsk_init_qhead (&group->namelist.list);
  452.    tsk_copy_name (&group->namelist, name);
  453.    group->creator = GLOBDATA current_task;
  454.    group->branch = NULL;
  455.    group->home = GLOBDATA current_task->group;
  456.  
  457.    C_ENTER;
  458. #if (DOS)
  459.    group->exit_addr = pspp->exit_addr;
  460.     group->create_psp = _psp;
  461.    pspp->exit_addr = (dword)tsk_emergency_exit;
  462. #endif
  463.    group->level = group->home->branch;
  464.    group->home->branch = group;
  465.    GLOBDATA current_task->group = group;
  466.    C_LEAVE;
  467. }
  468.  
  469.  
  470. int far ctask_resident (void)
  471. {
  472.     globvarptr gv;
  473.    
  474.     gv = tsk_resident ();
  475.    if (gv != NULL && tsk_global == NULL)
  476.         tsk_global = gv;
  477.  
  478.    return (gv != NULL);
  479. }
  480.  
  481.  
  482. #endif
  483.  
  484.  
  485. /*
  486.    install_tasker
  487.       Installs the Ctask system. The internal tasks are created,
  488.       the queues are initialised, and the interrupt handler installation
  489.       routines are called. Task preemption is initially off.
  490.  
  491.       Handling of the speedup parameter is system dependent.
  492. */
  493.  
  494. int far install_tasker (byte varpri, int speedup, word flags, byteptr name)
  495. {
  496. #if (DOS)
  497.    word divisor, sys_ticks;
  498. #endif
  499. #if (GROUPS)
  500.    int i;
  501.    tcbptr ct;
  502. #endif
  503.  
  504.    tsk_instflags = flags;
  505.  
  506. #if (GROUPS)
  507.  
  508.     for (i = 0; i < 8; i++)
  509.       tsk_glob_rec.id [i] = tsk_version [i];
  510.  
  511.     if (ctask_resident ())
  512.         {
  513.       main_ptr = ct = GLOBDATA current_task;
  514.       tsk_create_group (&tsk_glob_rec.group, name);
  515.  
  516. #if (DOS)
  517.       tsk_glob_rec.group.save_psp = ct->base_psp;
  518.       tsk_glob_rec.group.save_sssp = ct->psp_sssp;
  519.       ct->base_psp = _psp;
  520. #endif
  521.  
  522. #if (TSK_DYNAMIC)
  523.        create_resource (&alloc_resource
  524. #if (TSK_NAMEPAR)
  525.                         , "ALLOCRSC"
  526. #endif
  527.                         );
  528. #endif
  529.         return 1;
  530.         }
  531. #endif
  532.  
  533.    tsk_global = &tsk_glob_rec;
  534.    tsk_glob_rec.current_task = &main_tcb;
  535. #if (GROUPS)
  536.    main_tcb.group = &tsk_glob_rec.group;
  537. #endif
  538.  
  539.    tsk_init_qhead (&tsk_glob_rec.eligible_queue);
  540.    tsk_init_qhead (&tsk_glob_rec.timer_queue);
  541.    tsk_init_qhead (&tsk_glob_rec.watch_queue);
  542.    tsk_glob_rec.preempt = 1;
  543.    tsk_glob_rec.pretick = 0;
  544.    tsk_glob_rec.var_prior = varpri;
  545.    tsk_glob_rec.in_sched = 0;
  546.    tsk_glob_rec.ticker_chain = NULL;
  547.  
  548. #if (GROUPS)
  549.    tsk_glob_rec.group.branch = NULL;
  550.    tsk_create_group (&tsk_glob_rec.group, name);
  551.    tsk_glob_rec.group.branch = NULL;
  552.    tsk_glob_rec.group.home = NULL;
  553.  
  554. #if (DOS)
  555.    tsk_glob_rec.group.save_psp = 0;
  556.  
  557.    tsk_install_dos ();
  558. #endif
  559. #else
  560. #if (TSK_NAMED)
  561.    tsk_init_qhead (&tsk_glob_rec.name_list);
  562.    tsk_glob_rec.name_list.name [0] = 0;
  563. #endif
  564. #endif
  565.  
  566.    /* 
  567.       Call create_task to initialise the main task's TCB.
  568.       Note that the function pointer and stack parameters are
  569.       insignificant, but must be valid pointers (the stack is
  570.       initialised, but not used).
  571.    */
  572.  
  573.    create_task (&main_tcb, tsk_timer, timer_stack, STACKSIZE, PRI_TIMER - 1, NULL
  574. #if (TSK_NAMEPAR)
  575.                 , "-MAIN-"
  576. #endif
  577.                 );
  578.  
  579.    main_tcb.qhead = &tsk_glob_rec.eligible_queue;
  580.    main_tcb.state = ST_RUNNING;
  581.  
  582. #if (TSK_DYNAMIC)
  583.    create_resource (&alloc_resource
  584. #if (TSK_NAMEPAR)
  585.                        , "ALLOCRSC"
  586. #endif
  587.                        );
  588. #endif
  589.  
  590.    create_task (&timer_tcb, tsk_timer, timer_stack, STACKSIZE, PRI_TIMER, NULL
  591. #if (TSK_NAMEPAR)
  592.                 , "-TIMER-"
  593. #endif
  594.                 );
  595.    create_counter (&tsk_timer_counter
  596. #if (TSK_NAMEPAR)
  597.                 , "TIMCOUNT"
  598. #endif
  599.                 );
  600.  
  601.    start_task (&timer_tcb);
  602.  
  603. #if (IBM)
  604.    create_task (&int8_tcb, tsk_int8, int8_stack, STACKSIZE, PRI_INT8, NULL
  605. #if (TSK_NAMEPAR)
  606.                 , "-INT8-"
  607. #endif
  608.                 );
  609.    create_counter (&tsk_int8_counter
  610. #if (TSK_NAMEPAR)
  611.                 , "INT8CNT"
  612. #endif
  613.                 );
  614.    start_task (&int8_tcb);
  615.  
  616.    if (speedup <= 0 || speedup > 8)
  617.       {
  618.       divisor = 0;
  619.       sys_ticks = 1;
  620.       }
  621.    else
  622.       {
  623.       divisor = 0x8000 >> (speedup - 1);
  624.       sys_ticks = 1 << speedup;
  625.       }
  626.  
  627.    tsk_glob_rec.ticks_per_sec = 18 * sys_ticks;  /* rough number only */
  628.  
  629. #if (CLOCK_MSEC)
  630.    tsk_glob_rec.tick_factor = (65536.0 / (double)sys_ticks) / 1193.18;
  631. #endif
  632.  
  633.    tsk_install_timer (divisor, sys_ticks);
  634.    tsk_install_kbd ();
  635.    if (flags & IFL_PRINTER)
  636.       tsk_install_int17 ();
  637. #endif
  638. #if (AT_BIOS)
  639.    if (flags & IFL_INT15)
  640.       tsk_install_bios ();
  641. #endif
  642.  
  643. #if (!DOS)
  644.    tsk_install_timer (0, 0);
  645. #endif
  646.    return 0;
  647. }
  648.  
  649.  
  650. /*
  651.    tsk_remove_tasker
  652.       Calls the interrupt handler un-install routines.
  653. */
  654.  
  655. local void near tsk_remove_tasker (void)
  656. {
  657.    tsk_glob_rec.preempt = 0;
  658.  
  659. #if (DOS)
  660.    if (v24_remove_func != NULL)
  661.       v24_remove_func ();
  662.    if (prt_remove_func != NULL)
  663.       prt_remove_func ();
  664. #endif
  665.  
  666. #if (AT_BIOS)
  667.    if (tsk_instflags & IFL_INT15)
  668.       tsk_remove_bios ();
  669. #endif
  670. #if (IBM)
  671.  
  672.    /* Allow all stored clock ticks to be processed */
  673.  
  674.    if (!(tsk_instflags & IFL_INT8_DIR))
  675.       {
  676.       set_priority (&int8_tcb, 0xffff);
  677.       while (check_counter (&tsk_int8_counter))
  678.          schedule();
  679.       }
  680.  
  681.    if (tsk_instflags & IFL_PRINTER)
  682.       tsk_remove_int17 ();
  683.  
  684.    tsk_remove_timer ();
  685.    tsk_remove_kbd ();
  686. #endif
  687.  
  688. #if (DOS)
  689.    tsk_remove_dos ();
  690. #endif
  691. }
  692.  
  693. /*
  694.    tsk_kill_group
  695.       Kills all tasks in the current group and unlinks all structures
  696.       from the name list.
  697. */
  698.  
  699. #if (GROUPS)
  700.  
  701. local void near tsk_kill_group (gcbptr group)
  702. {
  703.    nameptr curr;
  704.    tcbptr tsk;
  705. #if (DOS)
  706.    pspptr pspp;
  707. #endif
  708.  
  709.    tsk_dis_preempt ();
  710.  
  711. #if (DOS)
  712.    pspp = MK_FP (group->create_psp, 0);
  713.    pspp->exit_addr = group->exit_addr;
  714.    if (group->save_psp)
  715.       {
  716.       group->creator->base_psp = group->save_psp;
  717.       group->creator->psp_sssp = group->save_sssp;
  718.       pspp = MK_FP (group->save_psp, 0);
  719.       pspp->caller_stack = group->save_sssp;
  720.       }
  721.  
  722.    if (v24_remove_func != NULL)
  723.       {
  724.       v24_remove_func ();
  725.       v24_remove_func = NULL;
  726.       }
  727.    if (prt_remove_func != NULL)
  728.       {
  729.       prt_remove_func ();
  730.       prt_remove_func = NULL;
  731.       }
  732. #endif
  733.  
  734.    group->creator->group = group->home;
  735.    curr = (nameptr)group->namelist.list.first;
  736.  
  737.    while (curr->list.kind)
  738.       {
  739.       tsk_del_name (curr);
  740.       if (curr->list.kind == TYP_TCB)
  741.          {
  742.          tsk = (tcbptr)curr->strucp;
  743.          if (tsk != GLOBDATA current_task &&
  744.              tsk != &int8_tcb &&
  745.              tsk != &timer_tcb)
  746.             kill_task (tsk);
  747.          }
  748.  
  749.       curr = (nameptr)group->namelist.list.first;
  750.       }
  751.  
  752.    tsk_ena_preempt ();
  753. }
  754.  
  755. #endif
  756.  
  757. /*
  758.    tsk_remove_group
  759.       The current group is unlinked from the group chain, then
  760.       all tasks in the current group, and in all subgroups,
  761.       are killed via "tsk_kill_group".
  762.  
  763.       Returns  0 on normal completion,
  764.                1 when last group was removed (including the tasker),
  765.               -1 when the group structure was messed up.
  766.  
  767.       CAUTION: This routine is for use by the TSKDOS and remove_tasker
  768.                modules ONLY! Do not call it directly.
  769. */
  770.  
  771. #if (GROUPS)
  772.  
  773. int near tsk_remove_group (gcbptr group, int freemem)
  774. {
  775.     CRITICAL;
  776.     gcbptr curr, last;
  777.  
  778.    C_ENTER;
  779.    while (group->branch != NULL)
  780.       tsk_remove_group (group->branch, 1);
  781.  
  782.    if (group->home == NULL)
  783.       {
  784.       tsk_kill_group (group);
  785.       tsk_remove_tasker ();
  786.       return 1;
  787.       }
  788.  
  789.    last = NULL;
  790.    curr = group->home->branch;
  791.  
  792.    while (curr != NULL && curr != group)
  793.       {
  794.       last = curr;
  795.       curr = curr->level;
  796.       }
  797.    if (curr == NULL)
  798.       return -1;
  799.  
  800.    if (last == NULL)
  801.       group->home->branch = group->level;
  802.    else
  803.       last->level = group->level;
  804.  
  805.     GLOBDATA current_task->group = group->home;
  806.  
  807.    tsk_kill_group (group);
  808. #if (DOS)
  809.    if (freemem)
  810.       tsk_free_mem (group->create_psp);
  811. #endif
  812.  
  813.     C_LEAVE;
  814.    if (!freemem)
  815.       preempt_on ();
  816.    return 0;
  817. }
  818.  
  819. #endif
  820.  
  821.  
  822. /*
  823.    remove_tasker
  824.       Front-end for remove_group/tsk_remove_tasker.
  825.       Kills the current task group, and/or un-installs CTask.
  826. */
  827.  
  828. void far remove_tasker (void)
  829. {
  830. #if (GROUPS)
  831.     tsk_remove_group (GLOBDATA current_task->group, 0);
  832. #else
  833.    tsk_remove_tasker ();
  834. #endif
  835. }
  836.  
  837.  
  838. /* --------------------------------------------------------------------- */
  839.  
  840.  
  841. /*
  842.    preempt_off
  843.       Turns off task preemption (will stay off until explicitly enabled).
  844. */
  845.  
  846. void far preempt_off (void)
  847. {
  848.    GLOBDATA preempt = 1;
  849. }
  850.  
  851.  
  852. /*
  853.    preempt_on
  854.       Resets permanent and temporary task preemption flag. If 
  855.       preemption is pending, the scheduler is called.
  856. */
  857.  
  858. void far preempt_on (void)
  859. {
  860.    GLOBDATA preempt = 0;
  861.    tsk_cli ();
  862.    if (GLOBDATA pretick)
  863.       schedule ();
  864.    tsk_sti ();
  865. }
  866.  
  867.  
  868. /*
  869.    tsk_ena_preempt
  870.       Resets temporary task preemption flag. If preemption is pending,
  871.       the scheduler is called.
  872. */
  873.  
  874. void far tsk_ena_preempt (void)
  875. {
  876.    tsk_cli ();
  877.    if (!(GLOBDATA preempt &= ~2))
  878.       if (GLOBDATA pretick)
  879.          schedule ();
  880.    tsk_sti ();
  881. }
  882.  
  883.  
  884. /*
  885.    tsk_dis_preempt
  886.       Sets temporary task preemption flag.
  887. */
  888.  
  889. void far tsk_dis_preempt (void)
  890. {
  891.    GLOBDATA preempt |= 2;
  892. }
  893.  
  894.  
  895.  
  896.