home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 362_01 / rmaxtask.doc < prev    next >
Text File  |  1991-12-12  |  54KB  |  1,104 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.                                    RMAXTask
  22.  
  23.                    A Multitasking Environment for C and C++
  24.  
  25.                                   Version 1.0
  26.  
  27.                                       ---
  28.  
  29.                               Copyright (c) 1991
  30.                             RMAX Development Group
  31.                          1033 East Coral Gables Drive
  32.                             Phoenix, Arizona 85022
  33.  
  34.                                                                Page 1
  35.  
  36.           TABLE OF CONTENTS
  37.           -----------------
  38.  
  39.           LEGAL INFORMATION . . . . . . . . . . . . . . . . . . .  2
  40.  
  41.           INTRODUCTION  . . . . . . . . . . . . . . . . . . . . .  3
  42.  
  43.              What is RMAXTask?  . . . . . . . . . . . . . . . . .  3
  44.              What is Multitasking?  . . . . . . . . . . . . . . .  3
  45.              Why Do I Need Multitasking?  . . . . . . . . . . . .  4
  46.  
  47.           RMAXTask TECHNICAL INFORMATION  . . . . . . . . . . . .  8
  48.  
  49.              How Does RMAXTask Work?  . . . . . . . . . . . . . .  8
  50.              RMAXTask Function Overviews  . . . . . . . . . . . .  9
  51.  
  52.           RMAXTask FUNCTION REFERENCE . . . . . . . . . . . . . . 12
  53.  
  54.              check_mail() . . . . . . . . . . . . . . . . . . . . 12
  55.              check_sem()  . . . . . . . . . . . . . . . . . . . . 12
  56.              create_mailbox() . . . . . . . . . . . . . . . . . . 12
  57.              create_sem() . . . . . . . . . . . . . . . . . . . . 12
  58.              create_task(). . . . . . . . . . . . . . . . . . . . 13
  59.  
  60.              fake_key() . . . . . . . . . . . . . . . . . . . . . 14
  61.              get_mail() . . . . . . . . . . . . . . . . . . . . . 14
  62.              get_status() . . . . . . . . . . . . . . . . . . . . 15
  63.              key_hit()  . . . . . . . . . . . . . . . . . . . . . 15
  64.              kill_task()  . . . . . . . . . . . . . . . . . . . . 15
  65.  
  66.              RMAX_time()  . . . . . . . . . . . . . . . . . . . . 16
  67.              send_mail()  . . . . . . . . . . . . . . . . . . . . 16
  68.              signal_sem() . . . . . . . . . . . . . . . . . . . . 16
  69.              start_RMAXTask() . . . . . . . . . . . . . . . . . . 17
  70.              stop_RMAXTask()  . . . . . . . . . . . . . . . . . . 17
  71.  
  72.              suspend()  . . . . . . . . . . . . . . . . . . . . . 17
  73.              wait_key() . . . . . . . . . . . . . . . . . . . . . 18
  74.              wait_sem() . . . . . . . . . . . . . . . . . . . . . 18
  75.              yield()  . . . . . . . . . . . . . . . . . . . . . . 19
  76.  
  77.           RMAXTask QUESTIONS AND ANSWERS  . . . . . . . . . . . . 20
  78.  
  79.           APPENDIX A - IBM PC EXTENDED KEYBOARD CODES . . . . . . 22
  80.  
  81.           APPENDIX B - RMAXTask REVISION HISTORY  . . . . . . . . 24
  82.  
  83.  
  84.                                                                Page 2
  85.  
  86.           LEGAL INFORMATION
  87.           -----------------
  88.  
  89.           This is the RMAXTask multitasking library for C.  This
  90.           library is copyrighted, but you may use and distribute the
  91.           shareware version of the library under the following
  92.           conditions:
  93.  
  94.           1.  By using the library, you agree that you do so at your
  95.               own risk, and to hold both RMAX Development Group and the
  96.               authors of these programs harmless from any liability for
  97.               loss or damage of any kind suffered by you arising from
  98.               your use of these programs.
  99.  
  100.           2.  You may freely copy and distribute the shareware files,
  101.               provided that you distribute them all together in their
  102.               original form, without any changes, additions or
  103.               deletions.  No fee may be charged for distributing the
  104.               files, except that you may charge not more than a $10.00
  105.               disk copying fee for each physical copy you distribute.
  106.  
  107.           3.  You may not create programs intended for sale using the
  108.               RMAXTask library unless you have registered your copy,
  109.               nor may you continue to use the shareware files for more
  110.               than 30 days after receiving them without registering.
  111.               Upon registration, you will receive the full source code
  112.               to the library, pre-compiled libraries for all the memory
  113.               models for both C and C++, and a license allowing
  114.               unrestricted use of the library routines in your own
  115.               programs.
  116.  
  117.           To register your copy of RMAXTask, simply fill out the form
  118.           found in the file REGISTER.TXT and return it along with your
  119.           registration fee.
  120.  
  121.  
  122.                                                                Page 3
  123.  
  124.           WHAT IS RMAXTask?
  125.           -----------------
  126.  
  127.           RMAXTask is a library of C functions that lets you run one or
  128.           more C functions together in a priority-based, cooperative
  129.           (non-preemptive) multitasking environment.  RMAXTask
  130.           provides full support for intertask synchronization and
  131.           communication, timed delays, and access to the PC's keyboard.
  132.  
  133.           RMAXTask provides a more capable scheduler and better
  134.           intertask communication than do simple round-robin task
  135.           switchers such as Wayne Conrad's MTASK or the system
  136.           described in the October, 1988 issue of "Computer Language"
  137.           magazine, while avoiding the complexity of a full-blown
  138.           interrupt-driven, preepmtive system like Thomas Wagner's
  139.           CTask.
  140.  
  141.  
  142.           WHAT IS MULTITASKING?
  143.           ---------------------
  144.  
  145.           Multitasking is a way to divide a single program into several
  146.           distinct threads of execution called "tasks" which execute
  147.           concurrently under the control of a supervisory program
  148.           called a "scheduler".  The tasks are usually somewhat
  149.           independent of one another (although they can communicate
  150.           among themselves) and appear to execute all at once.  On a
  151.           single-CPU system, of course, only one task can be running at
  152.           any given instant, but the scheduler passes control to the
  153.           various tasks in a way that gives the illusion that they are
  154.           all running at the same time.
  155.  
  156.           Multitasking as implemented by RMAXTask is not to be confused
  157.           with multiprogramming (as provided by Windows or an operating
  158.           system like QNX), in which multiple -programs- appear to run
  159.           at the same time, or with multiprocessing, which requires a
  160.           computer with more than one CPU.  When you use RMAXTask, you
  161.           compile and link all your tasks together into a single
  162.           program which appears (and is) to the operating system just
  163.           like any other program.
  164.  
  165.  
  166.           PREEMPTIVE VS. COOPERATIVE MULTITASKING
  167.           ---------------------------------------
  168.  
  169.           In a "preemptive" multitasking system, the scheduler doles
  170.           out CPU time to the tasks in chunks called "time slices".
  171.           Each task is allowed to execute for a specific time period,
  172.           after which control is wrested from it by the timer interrupt
  173.           and given to some other task by the scheduler.  The advantage
  174.           of such a scheme is that tasks can be written with little or
  175.           no awareness of the multitasking system.  The downside,
  176.           however, is that it can be difficult to make sure that tasks
  177.                                                                Page 4
  178.  
  179.           are not interrupted at inappropriate times.  As an example,
  180.           consider a task which has just made a DOS call to write to a
  181.           file.  If it is interrupted while it is within DOS and
  182.           control is then passed to a different task which also calls
  183.           DOS, havoc will reign shortly because DOS is not re-entrant.
  184.  
  185.           In a "cooperative" multitasking environment, a task once
  186.           running continues to run until it explicitly relinquishes
  187.           control by making a call to the multitasking system.  As a
  188.           result, such tasks must be written so that they do in fact
  189.           give up the CPU appropriately.  Concern about being preempted
  190.           at a bad time is eliminated, however, because the tasks
  191.           surrender control voluntarily at times of their own choosing
  192.           rather than having the rug jerked from under them by an
  193.           interrupt.
  194.  
  195.           RMAXTask implements cooperative multitasking.
  196.  
  197.  
  198.           WHY DO I NEED MULTITASKING?
  199.           ---------------------------
  200.  
  201.           To see how multitasking can be useful, imagine that you are
  202.           designing an equipment monitoring program that is supposed to
  203.           do the following:
  204.  
  205.           1.  Receive and act on user input from the keyboard.
  206.  
  207.           2.  Periodically issue queries to several remote data-
  208.               gathering computers via the serial port.  Then wait for
  209.               the replies and process them.
  210.  
  211.           3.  Whenever one of the remote computers reports a "bad
  212.               status" condition, record it at the end of an error log
  213.               file on the hard disk.
  214.  
  215.           You decide to set up each of the program's three functions as
  216.           a separate subroutine and then call them in a round-robin
  217.           fashion from a main program that looks like this:
  218.  
  219.           void main() {
  220.             initialize everything
  221.             for (;;) {                  /* Repeat forever */
  222.               service_keyboard();       /* Check on the human */
  223.               poll_remotes();           /* Check on remote computers */
  224.               log_status();             /* Keep status log current */
  225.               }                         /* End 'repeat forever' */
  226.             }                           /* End main() */
  227.  
  228.           You then figure out the innards of the three subroutines and
  229.           begin to code the program, testing each piece as it's built.
  230.           You use a stub for poll_remotes() because the remote
  231.           computers are not yet ready for testing, and everything seems
  232.                                                                Page 5
  233.  
  234.           to work pretty well.  You have service_keyboard() completely
  235.           debugged, and log_status() is successful at recording the
  236.           simulated "bad status" condition that is generated by your
  237.           poll_remotes() stub.  You even find time to debug the real
  238.           version of poll_remotes(), using a second PC to simulate the
  239.           action of one of the remote computers.
  240.  
  241.           When the real remote computers are finally available, you
  242.           expect testing to go fairly smoothly as a result of all the
  243.           preliminary work you have done.  Soon into it, however, you
  244.           discover that the response to the keyboard is noticeably
  245.           sluggish.  The problem is that the real version of
  246.           poll_remotes() takes a lot longer to run than the stub did,
  247.           and service_keyboard() isn't being called frequently enough.
  248.           You try to fix the problem by adding a second call to
  249.           service_keybaord() in the main loop, like this:
  250.  
  251.             for (;;) {                  /* Repeat forever */
  252.               service_keyboard();       /* Check on the human */
  253.               poll_remotes();           /* Check on remote computers */
  254.               service_keyboard();       /* Check on the human again */
  255.               log_status();             /* Keep status log current */
  256.               }                         /* End 'repeat forever' */
  257.  
  258.           But that doesn't improve things enough and you find you have
  259.           to call service_keyboard() a couple of times from -within-
  260.           poll_remotes() to make the keyboard acceptably responsive.
  261.           This strikes you as a bit of a kludge, but at least it works.
  262.  
  263.           The system is finally put into service and appears to run
  264.           well until one day after a storm the remote computers report
  265.           an unusually large number of "bad status" conditions.
  266.           log_status() is now running for an unusually long time.
  267.           Respone at the keyboard is again sluggish and you find you
  268.           have to add a call to service_keyboard() inside of
  269.           log_status() to straighten things out again.
  270.  
  271.           Everything works fine after that, but you have a nagging
  272.           feeling that it could have been done better.  It somehow
  273.           bothers you to have calls to service_keyboard() stuffed away
  274.           in every corner of the program.  You also realize that
  275.           logging the errors to the disk doesn't have to happen
  276.           immediately when they're discovered and you wonder how you
  277.           might set it up so log_status() only operates when there's
  278.           nothing more important going on.
  279.  
  280.  
  281.           THERE IS A BETTER WAY
  282.           ---------------------
  283.  
  284.           The solution to these problems is to write each of the three
  285.           subroutines as independent tasks and then let them operate
  286.           concurrently in a multitasking environment such as that
  287.                                                                Page 6
  288.  
  289.           provided by RMAXTask.  Let's look at how each task would be
  290.           organized, starting with the task that handles the user
  291.           interface:
  292.  
  293.           The Keyboard Task
  294.           -----------------
  295.  
  296.           void keyboard_task() {
  297.             for (;;) {                  /* Repeat forever */
  298.               wait_key();               /* Wait for next keystroke */
  299.               /* Process keystroke here */
  300.               }                         /* End 'repeat forever' */
  301.             }                           /* End keyboard_task() */
  302.  
  303.           This task would be created as a high priority task, meaning
  304.           that it would be run first if other tasks of lower priority
  305.           were also waiting to use the CPU.  As shown, the task is
  306.           written as a never-ending loop that repeatedly waits for
  307.           input from the keyboard and then processes it one keystroke
  308.           at a time.  Note here that wait_key() is a function provided
  309.           by the multitasking system that suspends the calling task
  310.           until the next time a key is pressed.  Thus, when wait_key()
  311.           is called, keyboard_task() is suspended and other tasks are
  312.           allowed to execute until the next key is pressed.  At that
  313.           time, keyboard_task() competes again with the other tasks for
  314.           the CPU and it will always win since it has higher priority
  315.           than the other tasks.  The net effect is that keyboard_task()
  316.           is suspended most of the time within wait_key(), but that it
  317.           processes the individual keystrokes very quickly as they are
  318.           received.
  319.  
  320.           The Polling Task
  321.           ----------------
  322.  
  323.           The task that polls the remote computers would be given a
  324.           lower priority than keyboard_task(), but a higher priority
  325.           than the task that does the status logging.  It might look
  326.           something like this:
  327.  
  328.           void polling_task() {
  329.             for (;;) {                    /* Repeat forever */
  330.               for each remote {           /* Do each remote computer */
  331.                 suspend(POLL_INTERVAL);   /* Wait until time to poll */
  332.                 send_query();             /* Send query to remote */
  333.                 while (no_response_yet()) /* Wait for response to */
  334.                   yield();                /*  come back */
  335.                 process_response();       /* Process the response */
  336.                 if (bad_status())         /* Signal errors to the */
  337.                   notify_logging_task();  /* logging task */
  338.                 }                         /* End 'for each remote' */
  339.               }                           /* End 'repeat forever' */
  340.             }                             /* End polling_task() */
  341.  
  342.                                                                Page 7
  343.  
  344.           Here, suspend() and yield() are functions provided by the
  345.           multitasking system.  suspend() suspends the calling task for
  346.           a specified time period during which other tasks are allowed
  347.           to run.  yield() relinquishes control momentarily to give
  348.           tasks with higher priority a chance to run, but returns
  349.           immediately if there are no such tasks.
  350.  
  351.           As shown, polling_task() polls each remote computer in turn,
  352.           at regular time intervals established by the suspend() call.
  353.           Between polls, suspend() allows other tasks, regardless of
  354.           the priority to run.  When it is time to poll, polling_task()
  355.           sends a query to the remote device and then yields to
  356.           higher-priority tasks while it is waiting for the response.
  357.           When the response is received, polling_task() processes it
  358.           and sends a message to the status logging task if the status
  359.           was bad.  Messages are sent from one task to another using a
  360.           mechanism called a "mailbox" which is provided as part of the
  361.           multitasking system.
  362.  
  363.           The Status Logging Task
  364.           -----------------------
  365.           
  366.           Since there's no particular hurry about updating the bad
  367.           status log, the status logging task is given a low priority
  368.           so that it does not inhibit the operation of either of the
  369.           other tasks.
  370.  
  371.           void log_status() {
  372.             for (;;) {                  /* Repeat forever
  373.               await message from poller /* Wait for bad news */
  374.               append record to file     /* Write it on the log file */
  375.               }                         /* End 'repeat forever' */
  376.             }                           /* End log status */
  377.  
  378.           Here, "await message from poller" is implemented by a call to
  379.           the get_mail() function of the multitasking system, which
  380.           waits for a message to appear at the mailbox to which the
  381.           polling task is sending bad status information.  Higher
  382.           priority tasks are allowed to run if they want to each time
  383.           this function is called, so even when there is work for
  384.           log_status() to do, it only gets to run if no higher priority
  385.           tasks are ready to run.
  386.  
  387.           By now you should have a feel for the kinds of problems that
  388.           RMAXTask can solve, and a rough idea for how tasks are
  389.           written and how they fit together.  The next sections tell
  390.           just a little about the internal workings of RMAXTask and
  391.           then give detailed explanations of each of the RMAXTask
  392.           function calls.
  393.                                                                Page 8
  394.  
  395.           HOW DOES RMAXTask WORK?
  396.           -----------------------
  397.  
  398.           As shown in the example above, RMAXTask tasks are written as
  399.           independent C functions which are linked together with
  400.           functions from the RMAXTask library (and possibly other
  401.           libraries) to form the complete application.  The tasks are
  402.           rarely (if ever) called explicitly by user-written code.
  403.           Instead, each task is identified to the RMAXTask scheduler,
  404.           usually when the application is first started, by a call to
  405.           create_task().  From then on, the scheduler takes care of
  406.           placing the tasks into execution, based on events within the
  407.           application and upon the tasks' relative priorities.
  408.  
  409.           A key concept in understanding how the scheduler works is
  410.           that of a task's "state".  At any instant, each task is in
  411.           exactly one of the following states:
  412.  
  413.           RUNNING       The RUNNING task is the one currently
  414.                         executing.  There can obviously be only one
  415.                         RUNNING task, since there is only one CPU.
  416.                         
  417.           READY         READY tasks are those which are not currently
  418.                         blocked from execution waiting for some
  419.                         external event.  At each scheduling period, the
  420.                         READY task with the highest priority is made
  421.                         the RUNNING task and is placed into execution.
  422.                         If several READY tasks all have the same
  423.                         priority, they will run in a round-robin
  424.                         fashion.
  425.  
  426.           SLEEPING      SLEEPING tasks are those blocked from execution
  427.                         until some specified future time.  SLEEPING
  428.                         tasks are made READY when the specified time
  429.                         arrives.
  430.  
  431.           WAITING       WAITING tasks are those waiting for some
  432.                         event to occur, such as a message from another
  433.                         task, availability of a character from the
  434.                         keyboard, or some interrupt or other.  WAITING
  435.                         tasks may also have a timeout associated with
  436.                         them, in which case they will wait only for a
  437.                         specified time before being made READY again.
  438.  
  439.           When a task is first created, it is placed in the READY
  440.           state, and it will begin to execute as soon as all other
  441.           higher-priority tasks become blocked (either SLEEPING or
  442.           WAITING).  It will continue to run until it actively
  443.           relinquishes control, either explicitly by a call to yield()
  444.           or suspend(), or implicitly by a call to one of the other
  445.           RMAXTask functions.
  446.                                                                Page 9
  447.  
  448.           RMAXTask FUNCTION OVERVIEWS
  449.           ---------------------------
  450.  
  451.           This section gives a brief functional description of each RMAXTask
  452.           call, and is organized with related functions grouped
  453.           together.  The next section gives very detailed information
  454.           about each call, and is arranged alphabetically by function
  455.           name.
  456.  
  457.           Overhead Functions
  458.           ------------------
  459.           
  460.                 start_RMAXTask() - Initializes the RMAXTask software.
  461.                                    This function must be called before
  462.                                    any other RMAXTask functions are
  463.                                    used.
  464.  
  465.                 stop_RMAXTask()  - Cleans up after the RMAXTask
  466.                                    software.  Should be called before
  467.                                    the application returns to DOS.
  468.  
  469.           Task Manipulation
  470.           -----------------
  471.           
  472.                 create_task()    - Establishes a specified C function
  473.                                    as an RMAXTask task and makes it
  474.                                    READY.
  475.  
  476.                 kill_task()      - Kills a specified task.
  477.  
  478.                 yield()          - Makes the calling task READY and
  479.                                    then invokes the scheduler.  Use of
  480.                                    this call is the key to the
  481.                                    "cooperative" nature of RMAXTask
  482.                                    scheduling.  If a task plans to run
  483.                                    for a long time without either
  484.                                    waiting for an event or going to
  485.                                    sleep, it should call yield
  486.                                    periodically to give tasks with
  487.                                    higher priority a chance to use the
  488.                                    CPU.
  489.  
  490.                 suspend()        - Suspends execution of the calling
  491.                                    task for a specified time.
  492.                                                                Page 10
  493.  
  494.           Mailbox Functions
  495.           -----------------
  496.  
  497.           Tasks can send messages of unspecified format to each other
  498.           using mailboxes.  Mailboxes provide a FIFO queue of messages
  499.           which is initially empty.  Messages sent to a mailbox are
  500.           simply added to the queue, while tasks reading from the
  501.           mailbox wait until a message is present and get the oldest
  502.           such message.
  503.  
  504.                 create_mailbox() - Creates a mailbox.
  505.  
  506.                 send_mail()      - Sends a message to a mailbox.
  507.  
  508.                 get_mail()       - Waits until a message is present at
  509.                                    a mailbox, and returns it to the
  510.                                    caller.  A timeout may be specified.
  511.                 
  512.                 check_mail()     - Checks if a mailbox contains
  513.                                    anything or not.
  514.  
  515.  
  516.           Semaphore Functions
  517.           -------------------
  518.  
  519.           Tasks can synchronize with each other for resource sharing
  520.           and so on using semaphores.  Once created, each semaphore
  521.           maintains a count.  Tasks signalling the semaphore increase
  522.           the count by one, and whenever a task waiting at the
  523.           semaphore is made READY, the count is decreased by one.  The
  524.           count can be initialized to any desired value when the
  525.           semaphore is created.
  526.  
  527.                 create_sem()     - Creates a semaphore.
  528.  
  529.                 signal_sem()     - Signals a semaphore.
  530.  
  531.                 wait_sem()       - Waits for a semaphore.  A timeout
  532.                                    may be specified.
  533.  
  534.                 check_sem()      - Returns the value of a semaphore's
  535.                                    count.
  536.  
  537.  
  538.           Keyboard Interface Functions
  539.           ----------------------------
  540.  
  541.                 wait_key()       - Waits for a key from the keyboard.
  542.                                    Returns its value, or TIMEOUT if we
  543.                                    wait too long.
  544.  
  545.                 key_hit()        - Tells if a keystroke is available
  546.                                    from wait_key() or not.
  547.                                                                Page 11
  548.           
  549.                 fake_key()       - Causes a task suspended in
  550.                                    wait_key() to receive a specified
  551.                                    character, just as though it had
  552.                                    been typed at the keyboard.
  553.  
  554.           Miscellaneous Functions
  555.           -----------------------
  556.  
  557.                 get_status()     - Returns the termination status
  558.                                    (either TIMEOUT or OKAY) of the most
  559.                                    recent call to suspend(),
  560.                                    get_mail(), wait_key(), or
  561.                                    wait_sem().
  562.  
  563.                  RMAX_time()     - Returns the value of a counter that
  564.                                    is initialized to zero when the
  565.                                    program first starts and is
  566.                                    incremented 18.2 times per second
  567.                                    forever thereafter.
  568.                                                                Page 12
  569.           
  570.           RMAXTask FUNCTION REFERENCE
  571.           ---------------------------
  572.  
  573.           -------------------------------------------------------------
  574.           check_mail()
  575.           -------------------------------------------------------------
  576.           Syntax:       int check_mail(TDESC *mb_ptr);
  577.  
  578.           Description:  Tells whether or not there is mail waiting to
  579.                         be received at the mailbox pointed to by
  580.                         'mb_ptr'.
  581.  
  582.           Returns:      1 if there is mail waiting at 'mailbox', or 0
  583.                         otherwise.
  584.  
  585.           See also:     create_mailbox, send_mail, get_mail
  586.  
  587.  
  588.           -------------------------------------------------------------
  589.           check_sem()
  590.           -------------------------------------------------------------
  591.           Syntax:       int check_sem(TDESC *sem_ptr);
  592.  
  593.           Description:  Returns the count associated with the semaphore
  594.                         pointed to by 'sem_ptr'.
  595.  
  596.           See also:     create_sem, signal_sem, wait_sem
  597.  
  598.  
  599.           -------------------------------------------------------------
  600.           create_mailbox()
  601.           -------------------------------------------------------------
  602.           Syntax:       TDESC *create_mailbox(void);
  603.  
  604.           Description:  Creates a mailbox.
  605.  
  606.           Returns:      A pointer to the new mailbox if successful, or
  607.                         NULL otherwise.
  608.  
  609.           See also:     send_mail, get_mail, check_mail
  610.  
  611.  
  612.           -------------------------------------------------------------
  613.           create_sem()
  614.           -------------------------------------------------------------
  615.           Syntax:       TDESC *create_sem(int initial_count);
  616.  
  617.           Description:  Creates a semaphore and sets its count to
  618.                         'initial count'.
  619.  
  620.           Returns:      A pointer to the new semaphore if successful,
  621.                         or NULL otherwise.
  622.  
  623.           See also:     signal_sem, wait_sem, check_sem
  624.                                                                Page 13
  625.  
  626.           -------------------------------------------------------------
  627.           create_task()
  628.           -------------------------------------------------------------
  629.           Syntax:       TDESC *create_task(char *name, void (*fn)(),
  630.                                            unsigned priority, unsigned
  631.                                            stack_size);
  632.  
  633.           Description:  Sets up 'fn' as an RMAXTask task and places it
  634.                         in the READY state.  The task is given a
  635.                         priority of 'priority' and 'stack_size' bytes
  636.                         are reserved for its stack.  'name' is a text
  637.                         string that is stored as part of the task's
  638.                         descriptor for debugging purposes only.
  639.  
  640.                         'priority' can be any number from 0 (the lowest
  641.                         priority) to 0xFFFF (the highest priority).
  642.                         However, it probably never makes sense to give
  643.                         a task within the application a priority higher
  644.                         than 0xF000, which is the fixed priority of
  645.                         RMAXTask's internal timer task.  The symbol
  646.                         STANDARD_PRIORITY is defined in RMAXTASK.H at
  647.                         100.
  648.  
  649.                         The stack size appropriate for a given task
  650.                         depends on how many automatic variables are
  651.                         declared by it and its callees, as well as the
  652.                         depth to which function calls are made.  The
  653.                         symbol STANDARD_STACK is set in RMAXTASK.H at
  654.                         2000, and that is a more-than-generous size for
  655.                         most tasks.  
  656.  
  657.                         You should try increasing the stack size for
  658.                         tasks which exhibit errant behavior for no
  659.                         apparent reason.  On the other hand, if you're
  660.                         short on memory you may want to decrease the
  661.                         stack size until the task is observed to
  662.                         misbehave to see just how much it's really
  663.                         using.  You can also inspect the tasks' stacks
  664.                         using a debugger to see how much of the
  665.                         allocated space has been used, and then set the
  666.                         requested stack sizes accordingly.
  667.  
  668.                         Note that Borland's floating point emulator
  669.                         uses about 500 bytes at the bottom of the stack
  670.                         to store the state of the simulated 80x87, and
  671.                         so if your program uses EMU.LIB, the task
  672.                         stacks need to be that much larger than they
  673.                         otherwise would be.
  674.                         
  675.           Returns:      A pointer to the tasks's internal descriptor if
  676.                         the task was created successfully, and NULL
  677.                         otherwise.
  678.                                                                Page 14
  679.  
  680.           -------------------------------------------------------------
  681.           fake_key()
  682.           -------------------------------------------------------------
  683.           Syntax:       void fake_key(int key);
  684.  
  685.           Description:  Causes a task suspended in wait_key() to
  686.                         receive the character 'key', just as though it
  687.                         had been typed at the keyboard.
  688.  
  689.           See also:     wait_key, key_hit
  690.  
  691.  
  692.           -------------------------------------------------------------
  693.           get_mail()
  694.           -------------------------------------------------------------
  695.           Syntax:       void get_mail(void *msg, unsigned *msg_size,
  696.                                       TDESC *mailbox, long timeout,
  697.                                       int reverse);
  698.  
  699.           Description:  Causes the calling task to wait to receive a
  700.                         message at 'mailbox'.  When a message becomes
  701.                         available, it is copied into the memory at
  702.                         'msg', and its size is placed in '*msg_size'.
  703.                         If 'timeout' is specified as 0, get_mail() will
  704.                         wait forever for a message.  If a non-zero
  705.                         'timeout' value is given, get_mail() will
  706.                         return after that many 18.2 Hz BIOS clock ticks
  707.                         even if no message is received.  After
  708.                         get_mail() returns, the calling task can call
  709.                         get_status() to determine whether a message was
  710.                         received or the timeout period expired.
  711.  
  712.                         Tasks waiting for mail at a mailbox are placed
  713.                         in a FIFO queue, and the 'reverse' parameter
  714.                         specifies whether the caller of get_mail() will
  715.                         be placed at the head or the tail of the queue.
  716.                         If reverse is '0' (as it should be normally),
  717.                         the task will be placed at the tail of the
  718.                         queue and will have to wait there until all
  719.                         other waiting tasks have received their
  720.                         messages.  If reverse is '1', the calling task
  721.                         will be placed at the head of the queue and
  722.                         will receive the next message sent to
  723.                         'mailbox'.
  724.  
  725.           See also:     create_mailbox, send_mail, check_mail,
  726.                         get_status
  727.                                                                Page 15
  728.           
  729.           -------------------------------------------------------------
  730.           get_status()
  731.           -------------------------------------------------------------
  732.           Syntax:       int get_status(void);
  733.  
  734.           Description:  Returns the termination status of the most
  735.                         recent call to suspend(), get_mail(),
  736.                         wait_key(), or wait_sem().
  737.  
  738.           Returns:      OKAY if the function terminated normally, or
  739.                         TIMEOUT if it timed out before the awaited
  740.                         event occurred.  (get_status() always returns
  741.                         TIMEOUT following a call to suspend().)
  742.  
  743.           See also:     suspend, get_mail, wait_key, wait_sem
  744.  
  745.  
  746.           -------------------------------------------------------------
  747.           key_hit()
  748.           -------------------------------------------------------------
  749.           Syntax:       int key_hit(void);
  750.  
  751.           Description:  Tells if a keystroke is available from
  752.                         wait_key() or not.  Use the function in place
  753.                         of the equivalent function from your compiler's
  754.                         library if you are using wait_key() to get
  755.                         keybord characters.
  756.  
  757.           Returns:      1 if a keystroke is available from wait_key(),
  758.                         and 0 otherwise.
  759.  
  760.           See also:     wait_key, fake_key
  761.  
  762.  
  763.           -------------------------------------------------------------
  764.           kill_task()
  765.           -------------------------------------------------------------
  766.           Syntax:       void kill_task(TDESC *victim);
  767.  
  768.           Description:  Renders 'victim' effectively dead by removing
  769.                         it from the RMAXTask's task lists and freeing
  770.                         its internal descriptor and stack space.
  771.                         'victim' is the pointer returned by create_task
  772.                         when the task was created.
  773.  
  774.                         WARNING: kill_task() must be used with extreme
  775.                         care to avoid killing tasks that have open
  776.                         files, dynamic memory allocated that has not
  777.                         yet been free()'d, or any other loose ends that
  778.                         need tying off.
  779.  
  780.           See also:     create_task
  781.                                                                Page 16
  782.  
  783.           -------------------------------------------------------------
  784.           RMAX_time()
  785.           -------------------------------------------------------------
  786.           Syntax:       long RMAX_time(void);
  787.  
  788.           Description:  Returns the value of a 32-bit counter that is
  789.                         initialized to zero when the program first
  790.                         starts and is incremented 18.2 times per second
  791.                         forever thereafter.  This counter is sometimes
  792.                         more useful than the counter maintained by the
  793.                         BIOS because it doesn't wrap around at
  794.                         midnight.
  795.  
  796.  
  797.           -------------------------------------------------------------
  798.           send_mail()
  799.           -------------------------------------------------------------
  800.           Syntax:       int send_mail(void *msg, unsigned size,
  801.                                       TDESC *mailbox);
  802.  
  803.           Description:  Sends the 'size' bytes starting at 'msg' as a
  804.                         message to '*mailbox'.
  805.  
  806.           Returns:      1 if the function is successful, or 0 if there
  807.                         is an error.  
  808.  
  809.           See also:     create_mailbox, get_mail, check_mail
  810.  
  811.  
  812.           -------------------------------------------------------------
  813.           signal_sem()
  814.           -------------------------------------------------------------
  815.           Syntax:       void signal_sem(TDESC *semaphore);
  816.         
  817.           Description:  Sends a signal to '*semaphore'.
  818.  
  819.           See also:     create_sem, wait_sem, check_sem
  820.                                                                Page 17
  821.           
  822.           -------------------------------------------------------------
  823.           start_RMAXTask()
  824.           -------------------------------------------------------------
  825.           Syntax:       int start_RMAXTask(void);
  826.  
  827.           Description:  Initializes the RMAXTask multitasking system.
  828.                         This function must be called before any other
  829.                         RMAXTask functions are used.  WARNING: after
  830.                         start_RMAXTask() is called, the program must
  831.                         call stop_RMAXTask() before it returns to DOS
  832.                         in order to unhook interrupts and free memory
  833.                         used by RMAXTask().
  834.  
  835.           Returns:      1 if the function is successful, and 0
  836.                         otherwise.
  837.  
  838.           See also:     stop_RMAXTask
  839.  
  840.  
  841.           -------------------------------------------------------------
  842.           stop_RMAXTask()
  843.           -------------------------------------------------------------
  844.           Syntax:       void stop_RMAXTask(void);
  845.  
  846.           Description:  Unhooks interrupts and frees memory used by the
  847.                         RMAXTask software.  Must be called before
  848.                         returning to DOS from a program that has called
  849.                         start_RMAXTask().
  850.  
  851.           See also:     start_RMAXTask()
  852.  
  853.  
  854.           -------------------------------------------------------------
  855.           suspend()
  856.           -------------------------------------------------------------
  857.           Syntax:       void suspend(long ticks);
  858.  
  859.           Description:  Suspends execution of the calling task for
  860.                         'ticks' counts of the 18.2 Hz BIOS clock and
  861.                         allows other tasks to run during that time.
  862.  
  863.           See also:     yield
  864.                                                                Page 18
  865.  
  866.           -------------------------------------------------------------
  867.           wait_key()
  868.           -------------------------------------------------------------
  869.           Syntax:       int wait_key(long timeout);
  870.  
  871.           Description:  Causes the calling task to wait for the next
  872.                         keystroke from the keyboard.  If 'timeout' is
  873.                         specified as 0, wait_key() will wait forever
  874.                         for a keystroke.  If a non-zero 'timeout' value
  875.                         is given, wait_key() will return after that
  876.                         many 18.2 Hz BIOS clock ticks even if no key
  877.                         has been pressed.  After wait_key() returns,
  878.                         the calling task can call get_status() to
  879.                         determine whether a keystroke was received or
  880.                         the timeout period expired.
  881.  
  882.           Returns:      The extended keyboard code for the key pressed.
  883.                         The extended keyboard codes are listed in
  884.                         Appendix A of this document.  For keystrokes
  885.                         which correspond to a standard ASCII character,
  886.                         the ASCII code appears in the least-significant
  887.                         byte of the extended keyboard code returned by
  888.                         wait_key().
  889.  
  890.           See also:     key_hit, fake_key, Appendix A
  891.  
  892.  
  893.           -------------------------------------------------------------
  894.           wait_sem()
  895.           -------------------------------------------------------------
  896.           Syntax:       void wait_sem(TDESC *semaphore, long timeout);
  897.  
  898.           Description:  Causes the calling task to wait for a signal at
  899.                         '*semaphore'.  If 'timeout' is specified as 0,
  900.                         wait_sem() will wait forever.  If a non-zero
  901.                         'timeout' value is given, wait_sem() will
  902.                         return after that many 18.2 Hz BIOS clock ticks
  903.                         even if the semaphore is not signalled.  After
  904.                         wait_sem() returns, the calling task can call
  905.                         get_status() to determine whether a semaphore
  906.                         signal was received or the timeout period
  907.                         expired.
  908.  
  909.           See also:     create_sem, check_sem, signal_sem, get_status
  910.                                                                Page 19
  911.           
  912.           -------------------------------------------------------------
  913.           yield()
  914.           -------------------------------------------------------------
  915.           Syntax:       void yield(void);
  916.  
  917.           Description:  Makes the calling task READY and then invokes
  918.                         the scheduler.  Use of this call is the key to
  919.                         the "cooperative" nature of RMAXTask
  920.                         scheduling.  If a task plans to run for a long
  921.                         time without either waiting for an event or
  922.                         suspending itself, it should call yield()
  923.                         periodically to give other tasks with higher
  924.                         priority a chance to use the CPU.
  925.  
  926.           See also:     suspend
  927.                                                                Page 20
  928.           
  929.           RMAXTask QUESTIONS AND ANSWERS
  930.           ------------------------------
  931.  
  932.       Q.  How can I contact the author of RMAXTask?
  933.  
  934.       A.  You can write to 
  935.  
  936.               RMAX Development Group
  937.               1033 East Coral Gables Drive
  938.               Phoenix, Arizona 85022-3750.
  939.  
  940.           You can also leave electronic mail for Russ Cooper on the
  941.           Chandler C Connection BBS at (602) 759-7789.
  942.  
  943.  
  944.       Q.  Will RMAXTask work with programs that use the CXL windowing
  945.           library?
  946.           
  947.       A.  Yes.  If you modify the CXL function getxch() to access the
  948.           keyboard through the RMAXTask routines wait_key() and
  949.           key_hit() instead of through the BIOS routines, RMAXTask and
  950.           CXL work perfectly together.  
  951.  
  952.  
  953.       Q.  Can I write C++ programs that use RMAXTask?
  954.  
  955.       A.  Yes.  However, since the library on the shareware
  956.           distribution disk was compiled with a C compiler, it does not
  957.           contain C++ mangled names, and you will have to use the
  958.           extern "C" declaration to inform your C++ compiler not to
  959.           mangle the names of the RMAXTask functions.  This is most
  960.           easily done as follows at the point your program #includes
  961.           RMAXTASK.H:
  962.  
  963.               extern "C" {
  964.               #include  "rmaxtask.h"
  965.               };
  966.  
  967.           When you register RMAXTask, you will receive libraries
  968.           compiled both with and without mangled names.  Then this
  969.           problem disappears and you will also gain the benefit of the
  970.           additional type checking that C++ provides.
  971.  
  972.  
  973.       Q.  My parents don't understand me.  What should I do?
  974.  
  975.       A.  Show them this documentation.  They won't understand it,
  976.           either, and then at least you won't be alone.
  977.  
  978.  
  979.       Q.  Why doesn't RMAXTask support the mouse in the same way it
  980.           supports the keyboard?
  981.  
  982.       A.  Mouse support is planned for a future version of RMAXTask.
  983.                                                                Page 21
  984.  
  985.       Q.  What good is RMAX_time()?  Why not just use the BIOS timer
  986.           functions?
  987.  
  988.       A.  RMAX_time() is simpler to use than the BIOS timer because the
  989.           internal counter referenced by RMAX_time() doesn't get reset
  990.           to zero every night at midnight.
  991.  
  992.  
  993.       Q.  Does RMAXTask "hook" any interrupts?
  994.  
  995.       A.  Yes.  RMAXTask uses the timer tick interrupt 1CH to establish
  996.           all its internal timing.  Your program should only use
  997.           interrupt 1CH with the greatest of care to make sure that the
  998.           RMAXTask timer tick handler still gets called and that the
  999.           interrupt vector itself contains an appropriate value when
  1000.           your program terminates.  (Probably the easiest way to do
  1001.           this would be to add your code to the Int 1CH handler already
  1002.           supplied as part of RMAXTask.)
  1003.  
  1004.  
  1005.       Q.  I can't get RMAXTask to work using the Small memory model.
  1006.           What am I doing wrong?
  1007.  
  1008.       A.  Nothing.  Because of the way RMAXTask allocates stack space
  1009.           for the tasks, it can only be used with the Compact, Large,
  1010.           and Huge memory models.
  1011.                                                                Page 22
  1012.  
  1013.           APPENDIX A - IBM PC EXTENDED KEYBOARD CODES
  1014.           -------------------------------------------
  1015.           
  1016.            Key         Normal        Shift          Control        Alt
  1017.           -------------------------------------------------------------
  1018.            '           2827          2822           N/A            N/A
  1019.            ,           332C          333C           N/A            N/A
  1020.            -           0C2D          0C5F           0C1F           8200
  1021.            .           342E          343E           N/A            N/A
  1022.            /           352F          353F           N/A            N/A
  1023.            0           0B30          0B29           N/A            8100
  1024.            1           0231          0221           N/A            7800
  1025.            2           0332          0340           0300           7900
  1026.            3           0433          0423           N/A            7A00
  1027.            4           0534          0524           N/A            7B00
  1028.            5           0635          0625           N/A            7C00
  1029.            5           N/A           4C35           N/A            N/A
  1030.            6           0736          075E           071E           7D00
  1031.            7           0837          0826           N/A            7E00
  1032.            8           0938          092A           N/A            7F00
  1033.            9           0A39          0A28           N/A            8000
  1034.            ;           273B          273A           N/A            N/A
  1035.            =           0D3D          0D2B           N/A            8300
  1036.            A           1E61          1E41           1E01           1E00
  1037.            B           3062          3042           3002           3000
  1038.            C           2E63          2E43           2E03           2E00
  1039.            D           2064          2044           2004           2000
  1040.            E           1265          1245           1205           1200
  1041.            F           2166          2146           2106           2100
  1042.            G           2267          2247           2207           2200
  1043.            H           2368          2348           2308           2300
  1044.            I           1769          1749           1709           1700
  1045.            J           246A          244A           240A           2400
  1046.            K           256B          254B           250B           2500
  1047.            L           266C          264C           260C           2600
  1048.            M           326D          324D           320D           3200
  1049.            N           316E          314E           310E           3100
  1050.            O           186F          184F           180F           1800
  1051.            P           1970          1950           1910           1900
  1052.            Q           1071          1051           1011           1000
  1053.            R           1372          1352           1312           1300
  1054.            S           1F73          1F53           1F13           1F00
  1055.            T           1474          1454           1414           1400
  1056.            U           1675          1655           1615           1600
  1057.            V           2F76          2F56           2F16           2F00
  1058.            W           1177          1157           1117           1100
  1059.            X           2D78          2D58           2D18           2D00
  1060.            Y           1579          1559           1519           1500
  1061.            Z           2C7A          2C5A           2C1A           2C00
  1062.            [           1A5B          1A7B           1A1B           N/A
  1063.            \           2B5C          2B7C           2B1C           N/A
  1064.            ]           1B5D          1B7D           1B1D           N/A
  1065.            `           2960          297E           N/A            N/A
  1066.                                                                Page 23
  1067.  
  1068.            Key         Normal        Shift          Control        Alt
  1069.           -------------------------------------------------------------
  1070.            BkSpc       0E08          0E08           0E7F           N/A
  1071.            Del         5300          532E           N/A            N/A
  1072.            DownArrow   5000          5032           N/A            N/A
  1073.            End         4F00          4F31           7500           N/A
  1074.            Enter       1C0D          1C0D           1C0A           N/A
  1075.            Esc         011B          011B           011B           N/A
  1076.            Grey +      4E2B          4E2B           N/A            N/A
  1077.            Grey -      4A2D          4A2D           N/A            N/A
  1078.            Home        4700          4737           7700           N/A
  1079.            Ins         5200          5230           N/A            N/A
  1080.            LeftArrow   4B00          4B34           7300           N/A
  1081.            PgDn        5100          5133           7600           N/A
  1082.            PgUp        4900          4939           8400           N/A
  1083.            PrtSc       372A          N/A            7200           N/A
  1084.            RightArrow  4D00          4D36           7400           N/A
  1085.            Spacebar    3920          3920           3920           3920
  1086.            Tab         0F09          0F00           N/A            N/A
  1087.            UpArrow     4800          4838           N/A            N/A
  1088.            F1          3B00          5400           5E00           6800
  1089.            F10         4400          5D00           6700           7100
  1090.            F2          3C00          5500           5F00           6900
  1091.            F3          3D00          5600           6000           6A00
  1092.            F4          3E00          5700           6100           6B00
  1093.            F5          3F00          5800           6200           6C00
  1094.            F6          4000          5900           6300           6D00
  1095.            F7          4100          5A00           6400           6E00
  1096.            F8          4200          5B00           6500           6F00
  1097.            F9          4300          5C00           6600           7000
  1098.                                                                Page 24
  1099.  
  1100.           APPENDIX B - RMAXTask REVISION HISTORY
  1101.           --------------------------------------
  1102.  
  1103.           Version 1.0 - 12 DEC 91 - Original shareware release
  1104.