home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / CPROG / CSWITCH.ZIP / CSWITCH.DOC next >
Text File  |  1990-08-14  |  47KB  |  1,280 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21.                           C S W I T C H
  22.                         -----------------
  23.  
  24.  
  25.  
  26.  
  27.  
  28.                Multitasking Functions for DOS Programs
  29.  
  30.                     Copyright 1990 by Herb Rose
  31.                         All Rights Reserved
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38.  
  39.  
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55.  
  56.               Version 1.0 release date : Aug 10, 1990
  57.  
  58.  
  59. This file is an introduction to the Cswitch functions.  These
  60. functions provide limited multitasking capabilities for programs
  61. running under MS-DOS or PC-DOS.
  62.  
  63. The term 'limited multitasking' is used because these routines do not
  64. provide a multitasking user interface like DESQVIEW or WINDOWS.  These
  65. routines are programming tools that will enable a C programmer to
  66. write applications that consist of independent, simultaneously executing
  67. programs.
  68.  
  69. Using Cswitch, you can execute functions as independent tasks, which
  70. execute concurrently in a time-slicing environment.  You may also load
  71. and execute programs separately.  Cswitch provides you with a priority
  72. driven scheduler and dispatcher, DOS interface, and task control
  73. functions to implement true multitasking under your program's control.
  74.  
  75. Before I continue the explanation of Cswitch, please bear with me as
  76. I detail the legalities of using these routines.
  77.  
  78.  
  79. WARRANTY
  80. --------
  81.  
  82. This software is distributed as is, with no warranty of any kind,
  83. either expressed or implied.  No warranty or guarantee is made that
  84. the software is complete or error free.  All liability and risk
  85. associated with the use of this software is assumed by the user.
  86. Under no circumstances will ADEPT Software, its owners, or its
  87. agents be responsible for any damages arising from the use, or the
  88. inability to use this product.  This applies to all damages and
  89. remedies including, but not limited to, loss of or damage to property,
  90. and loss of profits.
  91.  
  92. This software has been tested in many configurations, under various
  93. conditions, and has consistently performed acceptably.  To the best
  94. of my knowledge, there are no errors or omissions in the software
  95. that will pose a problem to the user.
  96.  
  97. The user must be aware, however, that this is a programmer's tool,
  98. and by it's very nature will affect the software that controls the
  99. user's computer.  All reasonable precautions should be taken to
  100. protect your computer from the affects of an errant operating system.
  101.  
  102. In short -
  103. BACK UP YOUR DISKS, AND BE CAREFUL WHEN TESTING NEW PROGRAMS THAT
  104. USE CSWITCH!!!!!
  105.  
  106.  
  107.  
  108. Shareware Distribution
  109. ----------------------
  110.  
  111. CSWITCH is distributed as shareware.  You may freely use and distribute
  112. these routines under the following conditions :
  113.  
  114.     1. All files on this diskette must be distributed as a set, and no
  115.        modifications or additions may be distributed as part of the
  116.        set unless specifically approved in writing by Herb Rose.
  117.  
  118.     2. All copyright and legal notices must remain intact and unaltered.
  119.  
  120.     3. Copying and distribution fees may not exceed $6, and must be
  121.        clearly specified as such.
  122.  
  123.     4. You must register Cswitch if you want a copy of the source code,
  124.        or if you wish to distribute a software product that makes use
  125.        of any of the Cswitch functions.
  126.  
  127. Note that registration is not required if you simply want to use Cswitch
  128. in the privacy of your own home, or just to see what it can do.  As an
  129. incentive to registration, I include C and assembly source code when you
  130. register.  The source code is easy to read and well documented.  Make
  131. files are included for Microsoft C and Microsoft Assembler.
  132.  
  133. Registered users are informed of new releases of the software, and may
  134. download new releases (including source code) from the INFO*SHARE BBS.
  135.  
  136. If you plan to distribute any software product that makes use of Cswitch
  137. functions, then registration is mandatory.  Registration provides you
  138. with an unlimited, nonexclusive binary distribution license, with no
  139. royalties.  This is only fair, I think.  If you distribute a program,
  140. you should pay for the tools you used to create that program.
  141.  
  142. Registration is for use on a single computer.  Contact ADEPT Software
  143. for site licensing, or for multiple copy discounts.
  144.  
  145. Due to practical constraints, I cannot provide telephone assistance
  146. except to registered users.  I operate a 24-hour Bulletin Board System
  147. to handle technical assistance.  Telephone support is available for
  148. registered users, but I prefer to handle non-emergency questions via
  149. the BBS.  The number is  (703) 803-8000.
  150.  
  151.  
  152.  
  153. Registration Form
  154. -----------------
  155.  
  156. Please use the following form, or a reasonable facsimile, when
  157. registering Cswitch.
  158.  
  159.  
  160.             Mail to :    ADEPT Software
  161.                          P.O. Box 2181
  162.                          Woodbridge, Va.  22193
  163.  
  164.  
  165.     CSWITCH Registration ..................... ______ @ $30 = ______
  166.  
  167.     Virginia Residents add 4.5% sales tax ................. = ______
  168.  
  169.     Overseas Air Delivery ...............................$5 = ______
  170.       (U.S. delivery and Overseas Surface delivery is free)
  171.  
  172.     TOTAL ................................................. = ______
  173.  
  174.         Please send check or money order, in U.S. funds.
  175.  
  176.  
  177.         Your Name :        _____________________________________
  178.  
  179.         Company Name :     _____________________________________
  180.  
  181.         Address :          _____________________________________
  182.  
  183.         City, State, Zip : _____________________________________
  184.         (or country)
  185.  
  186.  
  187.  
  188.  
  189.  
  190.  
  191. Introduction to Multitasking
  192. ----------------------------
  193.  
  194. Multitasking is not a new concept.  Computers have been programmed to
  195. execute multiple tasks concurrently for many years.  It was observed
  196. a long time ago that most programs spent the majority of their time
  197. waiting for data input or output.  Since users and most peripheral devices
  198. are notoriously slow compared to the execution speeds of modern computers,
  199. most programs simply sit there 'twiddling their thumbs' waiting for data.
  200. It was observed that the computer's computational capabilities could be
  201. used more efficiently if another program were allowed to use the processor
  202. while a program that was just waiting was put into a dormant state.
  203. Carrying this observation one step farther, we find true multitasking.
  204. Multitasking is the ability to have several programs loaded into the
  205. computers memory, and apparently executing simultaneously.  In reality,
  206. there is a controlling program, called the Kernel, or Scheduler, that
  207. causes the computer to execute some of each programs instructions in
  208. a round-robin fashion.  Programs that are waiting for something to
  209. happen, such as waiting for a keystroke, or for a disk transfer to
  210. complete, are normally placed into a 'waiting' state, and do not get
  211. placed back into execution until the event they are waiting for has
  212. actually happened.
  213.  
  214. Notice that I said programs were 'apparently' running simultaneously.
  215. It is impossible for a microcomputer, such as the PC, to execute more
  216. that one program simultaneously.  Other computers may have several micro-
  217. processors, and can actually execute more than one task simultaneously.
  218. When there is only one microprocessor, the tasks must be executed one
  219. at a time.  The term 'time-slicing' is used to describe the operation
  220. of a multi-tasking operating system, meaning that the processor will
  221. execute each task's instructions for a short time, then will execute
  222. another task's instructions for a while, eventually allowing all the
  223. tasks to get some of their work done in a round-robin fashion.  If the
  224. amount of time given to each task is very large, say one or two seconds,
  225. there could be a significant amount of time for each task to remain
  226. idle while it awaited it's turn to execute.  If the amount of time
  227. given to each task (called the 'time slice') is too short, then too
  228. much of the computer's time will be spent saving and restoring task
  229. context information, diminishing the efficiency of the system.  A
  230. good time slice setting is one that allows many different tasks to
  231. execute in a short period of time, without noticably affecting the
  232. efficiency of the system.  Cswitch uses the timer interrupt for the
  233. PC to trigger a task switch.  This occurs approximately 18 times per
  234. second, or once every 55 milliseconds.  Since a normal time slice on
  235. a real multitasking operating system is usually around 50 microseconds,
  236. our time slice is actually very large.  In fact, 55 milliseconds is
  237. way too big for a real-time operating system, which has to react to
  238. external stimulus as quickly as possible.  With 18 tasks loaded into
  239. Cswitch, each task will only get to run once per second, for about
  240. 1/18 second.  If some of the tasks are higher priority than others,
  241. the lower priority tasks may not run at all for several seconds.  This
  242. is clearly not acceptable for programs that must react to external
  243. stimulus.
  244.  
  245. The 55 millisecond time slice, however, is fine for running one or
  246. two tasks at a high priority, and allowing several other tasks to
  247. run at a lower priority.  This gives the effect of 'background'
  248. processes running.
  249.  
  250. If you find the 55 millisecond time slice inadequate, you should
  251. consider altering the timer chip's programming so that it provides
  252. a timer interrupt more often.  Your interrupt handler will have to
  253. count the number of interrupts, and activate the DOS time-of-day
  254. routine (the normal destination of interrupt vector 8) once every
  255. 55 milliseconds, otherwise the computer's internal date and time
  256. settings will not be accurate.
  257.  
  258. Now we will focus on how you get multiple tasks to cooperate so that a
  259. given job will get done.  Basically, there are only 2 areas that we are
  260. concerned with -
  261.  
  262. 1. Passing information from one task to another, and
  263. 2. Preventing another task from interfering with a resource you are using.
  264.  
  265.  
  266. Moving Data Between Tasks
  267. -------------------------
  268.  
  269. The first area is handled in Cswitch via Message Queues.  A queue is
  270. simply a list of messages.  In this case, Cswitch has 64 queues, numbered
  271. 0-63.  Any task may place a message onto any of the queues, and any
  272. task may fetch a message from any of the queues.  If there is more than
  273. one message waiting on a queue, they will be fetched sequentially, on a
  274. first-in, first-out basis.
  275.  
  276. A message may be any format, containing any information that may be needed
  277. by another task.  When placing a message on a queue, you must tell Cswitch
  278. which queue to put it on, how long the message is (in bytes), and the
  279. address of the first byte of the message.  Cswitch will copy as many
  280. bytes as you have told it into a temporary buffer, and will place a
  281. pointer to that buffer into the message list for that queue.
  282.  
  283. When you fetch a message from a queue, you must tell Cswitch which queue
  284. to read a message from, the maximum number of bytes you are willing to
  285. receive, and where to put the message.  After Cswitch has copied the
  286. message (or as much of the message as you will allow) into your memory,
  287. the temp buffer is released, and the message pointer is deleted from
  288. the queue's list.  Note : if the message contained more bytes than
  289. you received (by specifying a maximum byte count lower than the actual
  290. size of the message), the message will still be removed from the
  291. queue.  You should make certain you allow enough room to receive any
  292. message your task is likely to receive.
  293.  
  294. It is up to the individual tasks to coordinate which queue numbers will
  295. be used to pass messages back and forth.  In the Falken BBS, a multi-
  296. line Bulletin Board System program written with Cswitch, the main
  297. BBS program assigns input and output queues to each task when it is
  298. started.  Queue number 1 is reserved, and is used to send the queue
  299. assignments to the tasks.  In other words, when a task first runs
  300. under control of Falken, it must read a message from queue number 1.
  301. That message will tell it which queue it must read messages from, and
  302. which queue it can write messages to, for the rest of the time it is
  303. running.
  304.  
  305.  
  306. Semaphores
  307. ----------
  308.  
  309. The second area of concern is to prevent another task from interfering
  310. with a resource that you are using.  A resource may be almost anything,
  311. from a disk file to a memory buffer, to a peripheral device.  Cswitch
  312. uses a semaphore system to control access to these resources.
  313.  
  314. You must know in advance which resources must be controlled with
  315. semaphores.  If you are going to allow several tasks to access a
  316. memory area simultaneously, it may be a good idea to control it, since
  317. one task may try to write new information to the memory while another
  318. task is trying to read the old information.
  319.  
  320. Semaphores work like this -
  321. A semaphore has an owner and a waiting list.  If your task attempts to
  322. 'attach' to the semaphore, but another task already 'owns' the semaphore,
  323. your task gets placed onto the 'waiting list', and does not get to run
  324. anymore (it is taken out of the ready queue).  Your task will remain
  325. dormant until it is the 'owner' of the semaphore.
  326.  
  327. When the owner of the semaphore 'releases' it, the first task in the
  328. 'waiting list' is made the new owner, and is allowed to run again.
  329.  
  330. Note : When you 'attach' to a semaphore, you will become the owner of
  331. the semaphore before you are allowed to continue.  Your
  332. program remains dormant until it is the owner, and you must release the
  333. semaphore when you are done with it, to allow other programs to run,
  334. which may be waiting for the semaphore.
  335.  
  336. If you attempt to 'attach' to a semaphore, and it is unowned, your
  337. task becomes the owner and is allowed to continue running immediately.
  338. You must still release the semaphore when you are done.
  339.  
  340.  
  341. Cswitch Functions
  342. -----------------
  343.  
  344. Cswitch allows tasks to perform the following functions :
  345.  
  346.     - All normal DOS and BIOS functions
  347.     - Send information between tasks with Message Queues
  348.     - Control system resources with Semaphores
  349.     - Alter task priority
  350.     - Suspend / Resume task execution
  351.     - Delay for a time period (sleep)
  352.     - Start new tasks, either sharing existing code, or loading a new
  353.       program file from disk.
  354.     - Load small programs into expanded memory to allow more programs
  355.       to execute simultaneously.
  356.  
  357. To use Cswitch, you must link your main program with the CSWITCH1.OBJ,
  358. CSWITCH2.OBJ and LMTC.OBJ files.  These files provide interrupt level
  359. control for task switching, and the operating system functions, such as
  360. message passing and semaphore control.  In addition, programs which
  361. will be loaded and executed under control of Cswitch must be linked
  362. with LMTC.OBJ or SMTC.OBJ (for large or small model, respectively).
  363. These files contain interface routines for calling system services.
  364.  
  365. Your main program must call the function START_MT().
  366.  
  367. START_MT() initializes the Cswitch data areas and enables multitasking.
  368. It intercepts several interrupt vectors, and builds Task Control
  369. Blocks for your main program and for an 'idle task'.  The idle task
  370. runs at a low priority, and checks the delay and termination queues
  371. regularly to service tasks waiting for those functions.
  372. After the call to START_MT(), multitasking is fully operational,
  373. and you may spawn tasks and load programs.  Note that your main
  374. program has a Task Control Block (TCB) and gets scheduled for time-
  375. slicing just as every other task does. Your main program runs at a
  376. priority of 1 (the highest priority allowed).
  377.  
  378. Before terminating, your main program MUST call the function END_MT().
  379. END_MT restores the interrupt vectors, releases all unnecessary
  380. memory, and halts all multitasking.  Essentially, it restores the
  381. computer to the same state it was in prior to the START_MT() call.
  382.  
  383. ** WARNING ** WARNING ** WARNING ** WARNING ** WARNING ** WARNING **
  384.  
  385. If you allow your program to terminate without calling END_MT, almost
  386. anything can happen, since the interrupt vectors are still pointing
  387. to Cswitch code which may or may not be overwritten by DOS.  At a
  388. minimum, your system will lock up.  A worst-case scenario could
  389. involve unrecoverable damage to disk files.
  390.  
  391. It is highly recommended that you test all your programs that use
  392. multitasking on a 'test' computer, a separate machine that does not
  393. contain any critical files or programs, to prevent loss of valuable
  394. data or programs.  At a minimum, make sure all your important files
  395. are backed up before testing new programs that use Cswitch routines.
  396.   
  397.  
  398. Executing Tasks
  399. ---------------
  400.  
  401. There are 2 methods of starting new tasks under Cswitch.  The first
  402. method is to SPAWN a new task that will execute a portion of your code
  403. independently.  Normally, this facility will be used to execute a
  404. function as a separate task.  The new task will be given it's own
  405. stack and TCB, and will be scheduled for time-slicing normally.  It
  406. will begin executing code at the address you specify (normally a
  407. function name).  It may call other functions, and may manipulate
  408. global data normally.  Any automatic (local) variables will reside
  409. on the new tasks stack, so there will not be any interference when
  410. a function is called by 2 different tasks.  In fact, you can spawn
  411. several new tasks which execute the same function if you like.
  412.  
  413. When the function being executed returns to the caller, the task will
  414. be terminated.  Any functions that are called by the spawned task will
  415. return normally, of course.  Only when the function being spawned
  416. returns will the task terminate.
  417.  
  418. The second method of starting new tasks is to load a program from
  419. disk and execute it.  The function LOADTASK() is used to load an EXE
  420. or COM file from disk and execute it.  You must supply 3 parameters
  421. to the LOADTASK function - the command line to run the program and
  422. the priority to be assigned to the task, and a flag indicating whether
  423. the task may be loaded into expanded memory or not.
  424.  
  425. For instance, to load and run the program MYPROG.EXE with the parameter
  426. 'myfile.dat', you would normally enter a DOS command line like this :
  427.  
  428. C:>myprog myfile.dat
  429.  
  430. To load and run the program under Cswitch, you would call the LOADTASK
  431. function like this :
  432.  
  433.     loadtask("myprog myfile.dat",4,1);
  434.  
  435. This loads and runs 'myprog.exe', gives it the command line parameter
  436. 'myfile.dat', and allows it to run at priority 4, and allows it to be
  437. loaded into expanded memory, if possible.
  438.  
  439. The .EXE extension is assumed if no extension is provided.  You can
  440. load and execute .COM files by specifying the .COM extension.  You
  441. must specify the full path of the program to be executed, if it is not
  442. in the current default directory.  Cswitch will not search your PATH
  443. for the program.
  444.  
  445.  
  446. Scheduling
  447. ----------
  448.  
  449. The Cswitch scheduler maintains a list of tasks that are waiting to
  450. execute, called the 'ready queue'.  When a task switch occurs, the
  451. TCB at the head of the ready queue is fetched, and allowed to run.
  452. Normally, the TCB that was just executing is inserted back into the
  453. ready queue.  In some cases, such as waiting for semaphore, delaying,
  454. etc. the task will not be re-inserted into the ready queue.
  455.  
  456. Tasks are inserted into the ready queue according to their relative
  457. priority.  Each task is assigned a 'base priority' from 1 to 10.
  458. 1 is the highest priority, and 10 is the lowest.  Each task also has
  459. a 'current priority', which is used to insert TCBs into the ready
  460. queue. Each time a task is fetched from the ready queue, the 'current
  461. priority' of all the other TCBs on the ready queue is decremented
  462. by 1.  When a task is re-inserted into the ready list, it's current
  463. priority is set equal to it's base priority, then it is inserted
  464. into the ready queue so that it is in front of all tasks whose current
  465. priority is lower.  In this way, tasks with a higher priority will
  466. be allowed to execute more often than tasks of lower priority.  The
  467. tasks with lower priority will remain on the ready queue until their
  468. current priority increases to the point that the other tasks get
  469. inserted behind them, and they work their way to the head of the queue.
  470.  
  471.  
  472. Using Expanded Memory
  473. ---------------------
  474.  
  475. Cswitch can use expanded memory to load and run tasks that require less
  476. than 64K.  By calling START_SWAPPING(), you tell Cswitch to start using
  477. expanded memory.  Any task that is loaded after calling START_SWAPPING()
  478. is  eligible to be loaded into expanded memory.
  479.  
  480.  
  481. Cswitch System Services
  482. -----------------------
  483.  
  484. Cswitch must replace some of the DOS system services with it's own
  485. services.  In every case, the calling sequence and register setup
  486. is identical do the DOS call.  Return values are also identical
  487. to DOS return values.  In short, if your program ran OK under DOS,
  488. it should run OK under Cswitch.
  489.  
  490. Cswitch replaces the following DOS (interrupt 21) services :
  491.  
  492.     48h : allocate memory
  493.  
  494.         Cswitch controls memory allocation for all tasks.  DOS uses
  495.         a 'first fit' algorithm for allocating memory, meaning that
  496.         it allocates a chunk of memory from the first block that
  497.         is large enough to fulfill the request.  This leads to memory
  498.         fragmentation, limiting the number of tasks that can run.
  499.         Cswitch uses a 'best fit' algorithm, searching all the
  500.         available memory blocks to find a block that is exactly the
  501.         size needed, or is closest to it.  This way, memory fragmentation
  502.         is kept to a minimum.
  503.  
  504.         Note : starting with DOS 3.3, you can alter the memory
  505.         allocation strategy that DOS uses, but 'first fit' is still
  506.         the default.
  507.  
  508.     49h : release memory
  509.  
  510.         Cswitch must also handle the memory release function.  When a
  511.         block of memory is released, all memory is re-combined as much
  512.         as possible to limit memory fragmentation.
  513.  
  514.     4ah : set memory block size
  515.  
  516.         This DOS function is used to adjust the size of a memory block.
  517.         Since DOS tasks expect to be the only task running, they assume
  518.         that a memory block can be altered by simply increasing the size
  519.         of the block when they need more memory.  Since several tasks
  520.         may occupy memory blocks adjacent to the block to be modified,
  521.         this is not always possible under Cswitch.  Cswitch will attempt
  522.         to fulfill the request by looking for a free block adjacent to
  523.         the specified block, and combining them to form a larger block.
  524.         If unsuccessful, it will return an error.
  525.  
  526.         Note that programs that allocate memory dynamically with CALLOC
  527.         or MALLOC calls may run into problems with this limitation.
  528.         The problem is that the library routines for allocating memory
  529.         (MALLOC, et al) use a table of structures to control the memory
  530.         blocks they obtain from DOS.  Normally, this table will only
  531.         hold 20 entries (this is true for Microsoft C and QuickC).
  532.         Since Cswitch will return an error on some of the 'set memory
  533.         block' calls, the library routines may execute an 'allocate
  534.         memory' call every time you execute MALLOC.  After 20 calls,
  535.         the table is full, and you can't allocate any more memory!
  536.  
  537.         You can avoid problems by using the EXEMOD program to change
  538.         the minimum memory requirement of your task, so that enough
  539.         memory will be reserved when your task is loaded to handle
  540.         dynamic memory allocation demands without having to request
  541.         more memory from DOS.
  542.  
  543.         Or, you can avoid using the MALLOC/CALLOC routines wherever
  544.         possible, and instead call the DOS allocate memory call
  545.         directly.  Be careful, though, because Cswitch can only
  546.         control a total of 512 memory blocks.
  547.  
  548.         When a task in expanded memory wants to extend the memory block,
  549.         it is much easier to do, provided the limit of 64K is not exceeded.
  550.         Since each task loaded into expanded memory is allocated the
  551.         entire 64K expanded memory region to work in, it is usually
  552.         possible to extend the memory allocation to the desired size.
  553.  
  554.     4ch : terminate task
  555.  
  556.         Cswitch handles task termination internally. It will release
  557.         memory held by the task, close all open files, and release the
  558.         Task Control Block.
  559.  
  560.     1ah : set new DTA area
  561.  
  562.         Cswitch records the address of the tasks new DTA in the TCB, then
  563.         passes this request to DOS normally.  This function is normally
  564.         handled by the library routines, and is seldom called explicitly
  565.         by a programmer.
  566.  
  567.     2fh : get DTA address
  568.  
  569.         Cswitch returns the DTA address from the tasks TCB.
  570.  
  571.  
  572. Cswitch Functions
  573. -----------------
  574.  
  575. The following functions are provided in the file CSWITCH1.C, and may
  576. only be called by the main program. i.e. these functions may only be
  577. called by the program that is linked with Cswitch1 and Cswitch2.
  578.  
  579.     START_MT()
  580.  
  581.         Parameters :
  582.                 none
  583.  
  584.         Description :
  585.                 Starts the multitasking kernel, and enables all system
  586.                 functions.  This function must be called once, and only
  587.                 once, before executing any of the other functions or
  588.                 system services.
  589.  
  590.         Return Value :
  591.                 none
  592.  
  593.         Notes/Comments :
  594.  
  595.  
  596.     START_SWAPPING()
  597.  
  598.         Parameters :
  599.                 none
  600.  
  601.         Description :
  602.                 This function sets Cswitch up to swap tasks out to
  603.                 expanded memory.
  604.  
  605.         Return Value :
  606.                 The amount of expanded memory available is returned,
  607.  
  608.         Notes/Comments :
  609.                 All tasks started prior to this call are memory-resident,
  610.                 and all spawned tasks are memory resident.  This routine
  611.                 may only be called once, and there is no way to turn
  612.                 off usage of expanded memory  once it is started.
  613.  
  614.  
  615.     END_MT()
  616.  
  617.         Parameters :
  618.                 none
  619.  
  620.         Description :
  621.                 Ends multitasking.  The program that called START_MT()
  622.                 must call END_MT() before terminating.
  623.  
  624.         Return Value :
  625.                 none
  626.  
  627.         Notes/Comments :
  628.                 See the warning above concerning the dire consequences
  629.                 of terminating without restoring the interrupt vectors
  630.                 and turning off multitasking.
  631.  
  632.  
  633.  
  634.  
  635.     LOADPRG(cmd_string,pri,expmemflag)
  636.     char *cmd_string;
  637.     int pri;
  638.     int expmemflag;
  639.  
  640.         Parameters :
  641.                 cmd_string is a character pointer to a command string
  642.                 to load the program, including command line parameters
  643.                 just as you would type in to load the program under
  644.                 DOS.
  645.  
  646.                 pri is the base priority of the new task
  647.  
  648.                 if expmemflag is true, the task is eligible to be loaded
  649.                 into expanded memory.  if set to 0, the task may not be
  650.                 loaded into expanded memory, even if START_SWAPPING()
  651.                 has been called previously.
  652.  
  653.         Description :
  654.                 Loads and executes an .EXE or .COM file from disk.
  655.                 The full path to the program file must be specified,
  656.                 and command line parameters may be used.
  657.  
  658.         Return Value :
  659.                  0 = no error
  660.                 -1 = not enough memory to create new task stack
  661.                 -2 = no free task control blocks
  662.                 -3 = no free memory control blocks
  663.  
  664.         Notes/Comments :
  665.                 This function performs the same service as LOADTASK()
  666.                 below.  The difference is that this service is called
  667.                 directly as a function.  LOADTASK is invoked via the
  668.                 INT 62H.  Functionally, there is no difference, since
  669.                 LOADTASK() eventually calls LOADPRG to do the work.
  670.  
  671.  
  672. Cswitch Global Variables
  673. ------------------------
  674.  
  675. The following variables are available to the main program.  These are
  676. provided for information only.
  677.  
  678.     int dos48, dos49, dos4a, dos4c;
  679.  
  680.         These integers represent a running count of how many DOS calls
  681.         (INT 21H) have been made for these function codes.
  682.  
  683.     unsigned int base_mem_segment;
  684.  
  685.         This is the segment value of the memory segment controlled by
  686.         Cswitch.
  687.  
  688.     int idlecount;
  689.  
  690.         This is a running count of how many times the idle task has
  691.         run.  The idle task is spawned when START_MT() is called, and
  692.         handles the tasks that are delaying and terminating.
  693.  
  694.     int swap_count;
  695.  
  696.         This is a running count of how many task switches have taken
  697.         place.  When the scheduler is called, this count is incremented
  698.         even if no task switch takes place due to a task locking out
  699.         switching or some other reason.
  700.  
  701.     int extmem_pages;
  702.  
  703.         This is the total number of expanded memory pages available
  704.         for task swapping. This variable is loaded after a successful
  705.         call to START_SWAPPING().
  706.  
  707.     int exmemfree;
  708.  
  709.         This is the number of free pages of expanded memory
  710.         left in the system.  Pages are allocated as needed by memory
  711.         allocation calls, so this number may vary somewhat during
  712.         operation.
  713.  
  714.  
  715. Cswitch Internal Services
  716. -------------------------
  717.  
  718. The following functions are provided in the file LMTC.OBJ and SMTC.OBJ,
  719. and may be called by any task executing under Cswitch control.  These
  720. functions allow access to the semaphore, messaging, and task control
  721. functions of Cswitch.
  722.  
  723. These functions are actually invoked via an INT 62H call.  The parameters
  724. are loaded into registers, and the INT 62H transfers control to an
  725. assembly language routine.  The registers are then pushed onto the stack,
  726. and the C function for system calls is executed.
  727.  
  728. Most of the work done by Cswitch is done by C routines, with assembly
  729. language used only for interrupt control.
  730.  
  731. All of these functions are of type INT, although some do not have return
  732. values assigned.
  733.  
  734.  
  735.  
  736.     RELINQ()
  737.  
  738.         Parameters :
  739.                 none
  740.  
  741.         Description :
  742.                 Gives up the rest of the tasks time slice.
  743.                 This is graceful way of not wasting processor time if
  744.                 your task has nothing else to do for a short while.
  745.  
  746.         Return Value :
  747.                 none
  748.  
  749.         Notes/Comments :
  750.                 none
  751.  
  752.  
  753.  
  754.     SPAWN(func_addr,pri)
  755.     int *func_addr();
  756.     int pri;
  757.  
  758.         Parameters :
  759.                 func_addr is the address of a function or subroutine
  760.                 that is to be executed as a separate task
  761.  
  762.                 pri is the base priority for the new task (1-10)
  763.  
  764.         Description :
  765.                 Causes the specified function or subroutine to be
  766.                 executed as a separate task.  A new stack is allocated
  767.                 for the task, allowing it to have exclusive access to
  768.                 local variables.  It will still share global variables
  769.                 with the calling task.
  770.  
  771.                 STDIN, STDOUT, and STDERR are inherited from the caller.
  772.  
  773.                 The function can be spawned more than once, allowing
  774.                 multiple tasks to execute the same code thread.
  775.  
  776.         Return Value :
  777.                 >0 = the tcb number of the new task, indicating no error
  778.                 -1 = not enough memory to create new task stack
  779.                 -2 = no free task control blocks
  780.                 -3 = no free memory control blocks
  781.  
  782.         Notes/Comments :
  783.                 A task that is swappable to expanded memory (i.e. one
  784.                 that was loaded after a call to START_SWAPPING() )
  785.                 cannot spawn new tasks.
  786.  
  787.  
  788.  
  789.     LOADTASK(cmd_string,pri,expmemflag)
  790.     char *cmd_string;
  791.     int pri;
  792.     int expmemflag;
  793.  
  794.         Parameters :
  795.                 cmd_string is a character pointer to a command string
  796.                 to load the program, including command line parameters
  797.                 just as you would type in to load the program under
  798.                 DOS.
  799.  
  800.                 pri is the base priority of the new task
  801.  
  802.                 if expmemflag is non-zero, the task may be loaded into
  803.                 expanded memory, if it is enabled.  If exmemflag is
  804.                 0, the task will not be loaded into expanded memory.
  805.  
  806.         Description :
  807.                 Loads and executes an .EXE or .COM file from disk.
  808.                 The full path to the program file must be specified,
  809.                 and command line parameters may be used.
  810.  
  811.                 STDIN, STDOUT, and STDERR are inherited from the caller.
  812.  
  813.         Return Value :
  814.                  0 = task loader queue is full
  815.                  1 = no error
  816.  
  817.         Notes/Comments :
  818.                 This call actually places a load request onto a queue
  819.                 to be executed by the 'idle' task.  The status of the
  820.                 load may be checked with the TEST_LOAD() call.
  821.  
  822.                 .COM files are given exactly 64K memory.  .EXE files
  823.                 are sized according to the information in the file
  824.                 header.  The MINALLOC parameter is used to determine
  825.                 how much memory, in addition to the task size and
  826.                 data space, is needed for the program.
  827.  
  828.  
  829.  
  830.     SLEEP(seconds)
  831.     int sleep;
  832.  
  833.         Parameters :
  834.                 seconds is the number of seconds the task is to remain
  835.                 alseep.
  836.  
  837.         Description :
  838.                 puts the calling task into a sleeping state, where it
  839.                 will not execute at all, for a specified number of
  840.                 seconds.
  841.  
  842.         Return Value :
  843.                 none
  844.  
  845.         Notes/Comments :
  846.  
  847.  
  848.  
  849.  
  850.     SEND_MD_MSG(queue,message_addr,count)
  851.     int queue;
  852.     char *message_addr;
  853.     int count;
  854.  
  855.         Parameters :
  856.                 queue is the message queue to place a message on
  857.                 message_addr is a pointer to the data to be placed on queue
  858.                 count is the number of bytes to place on the queue
  859.  
  860.         Description :
  861.                 places the specified number of bytes onto a message
  862.                 queue.  queues are numbered 0-63.
  863.  
  864.         Return Value :
  865.                 -1 = bad queue number
  866.                 other wise, returns number of bytes transferred
  867.  
  868.         Notes/Comments :
  869.  
  870.  
  871.  
  872.  
  873.     TESTMSG(queue)
  874.     int queue;
  875.  
  876.         Parameters :
  877.                 queue is the queue number to test
  878.  
  879.         Description :
  880.                 tests whether there are any messages waiting on the
  881.                 specified queue.
  882.  
  883.         Return Value :
  884.                 -1 = bad queue number
  885.                  0 = nothing on queue
  886.                  else, returns the number of bytes contained in the
  887.                  first message on the queue
  888.  
  889.         Notes/Comments :
  890.  
  891.  
  892.  
  893.  
  894.     RECVMSG(queue,message_addr,count)
  895.     int queue;
  896.     char *message_addr;
  897.     int count;
  898.  
  899.         Parameters :
  900.                 queue is the message queue to place a message on
  901.                 message_addr is a pointer a data area to receive the data
  902.                 count is the maximum number of bytes to transfer
  903.  
  904.         Description :
  905.                 reads the first message from the specified queue
  906.                 into the data area pointed to by message_addr. only
  907.                 the first 'count' bytes of the message are transferred.
  908.                 if more than 'count' bytes are in the message, the
  909.                 extra is lost.
  910.  
  911.                 If there are no messages on the specified queue, the
  912.                 task is placed into a dormant state until a message is
  913.                 put on the queue.  If you do not want your task to remain
  914.                 dormant if the queue is empty, you should call TESTMSG()
  915.                 to make sure something is available before calling this
  916.                 function.
  917.  
  918.         Return Value :
  919.                 -1 = bad queue number
  920.                  0 = no message was on queue
  921.                  else, returns the number of bytes transferred
  922.  
  923.         Notes/Comments :
  924.  
  925.  
  926.  
  927.  
  928.     SEM_ATTACH(semaphore_number)
  929.     int semaphore_number;
  930.  
  931.         Parameters :
  932.                 semaphore number is the number of the semaphore to
  933.                 attach (0-63)
  934.  
  935.         Description :
  936.                 attaches to the semaphore.  see semaphore description
  937.                 elsewhere in documentation
  938.  
  939.         Return Value :
  940.                 -1 = invalid semaphore number
  941.                 all other values = success
  942.  
  943.         Notes/Comments :
  944.  
  945.  
  946.  
  947.  
  948.     SEM_RELEASE(semaphore_number)
  949.     int semaphore_number;
  950.  
  951.         Parameters :
  952.                 semaphore number is the number of the semaphore to
  953.                 be released (0-63)
  954.  
  955.         Description :
  956.                 releases the semaphore.  see semaphore description
  957.                 elsewhere in documentation
  958.  
  959.         Return Value :
  960.                 -1 = invalid semaphore number
  961.                 all other values = success
  962.  
  963.  
  964.  
  965.  
  966.  
  967.     SEM_TEST(semaphore_number)
  968.     int semaphore_number;
  969.  
  970.         Parameters :
  971.                 semaphore number is the number of the semaphore to
  972.                 test (0-63)
  973.  
  974.         Description :
  975.                 tests to see if the semaphore is already owned by
  976.                 another task. see semaphore description
  977.                 elsewhere in documentation
  978.  
  979.         Return Value :
  980.                 -1 = semaphore is unowned
  981.                 all other values = semaphore is owned
  982.  
  983.  
  984.  
  985.  
  986.  
  987.     SETPRI(new_priority)
  988.     int new_priority;
  989.  
  990.         Parameters :
  991.                 new_priority is the new base priority of the calling
  992.                 task.
  993.  
  994.         Description :
  995.                 allows a task to change its priority, affecting how
  996.                 often the task gets to execute.
  997.  
  998.         Return Value :
  999.                 none
  1000.  
  1001.         Notes/Comments :
  1002.                 only values 1-10 are legal.  the lower the number, the
  1003.                 more often a task will run.  the main program is assigned
  1004.                 a prioroty of 1, and usually should be the only task with
  1005.                 such a low priority.
  1006.  
  1007.  
  1008.  
  1009.  
  1010.     SUSPEND()
  1011.  
  1012.         Parameters :
  1013.                 none
  1014.  
  1015.         Description :
  1016.                 suspends the task until it is awakened by a WAKEUP()
  1017.                 call.
  1018.  
  1019.         Return Value :
  1020.                 none
  1021.  
  1022.         Notes/Comments :
  1023.  
  1024.  
  1025.  
  1026.  
  1027.     WAKEUP(tcb_number)
  1028.     int tcb_number;
  1029.  
  1030.         Parameters :
  1031.                 tcb_number is the tcb identifier for the task that is
  1032.                 to be awakened.
  1033.  
  1034.         Description :
  1035.                 allows a task that was suspended with a SUSPEND() call
  1036.                 to continue processing.
  1037.  
  1038.         Return Value :
  1039.                 none
  1040.  
  1041.         Notes/Comments :
  1042.                 This is the ONLY way to wake up a SUSPENDed task.
  1043.                 This call will also wake up a task that is in a
  1044.                 SLEEP state (see SLEEP() above).  In this way, a task
  1045.                 may go into a sleep state, and another task can cause
  1046.                 it to start executing again before the sleep delay is
  1047.                 over.
  1048.  
  1049.  
  1050.  
  1051.  
  1052.     HOG()
  1053.  
  1054.         Parameters :
  1055.                 none
  1056.  
  1057.         Description :
  1058.                 prevents this task from being swapped out until a
  1059.                 corresponding NOHOG() is issued.  In effect, this
  1060.                 halts multitasking, allowing the caller to run
  1061.                 forever, if desired.
  1062.  
  1063.         Return Value :
  1064.                 none
  1065.  
  1066.         Notes/Comments :
  1067.                 Use this function when your task must not be interrupted.
  1068.                 DOS functions like I/O and other critical functions are
  1069.                 already protected, so this function should not be used
  1070.                 too often.
  1071.  
  1072.  
  1073.  
  1074.  
  1075.     NOHOG()
  1076.  
  1077.         Parameters :
  1078.                 none
  1079.  
  1080.         Description :
  1081.                 allows multitasking to continue after being halted by
  1082.                 a HOG() call.
  1083.  
  1084.         Return Value :
  1085.                 none
  1086.  
  1087.         Notes/Comments :
  1088.  
  1089.  
  1090.  
  1091.  
  1092.     SPAWN_EXIT()
  1093.  
  1094.         Parameters :
  1095.                 none
  1096.  
  1097.         Description :
  1098.                 halts execution of a SPAWNed task.
  1099.  
  1100.         Return Value :
  1101.                 none
  1102.  
  1103.         Notes/Comments :
  1104.                 this is the preferred method of halting execution of a
  1105.                 spawned task.  When the spawned task is finished, it
  1106.                 should call this function to terminate.
  1107.  
  1108.  
  1109.  
  1110.  
  1111.  
  1112.     GET_TCB_INFO(pointer_address)
  1113.     struct tcb_rec far **pointer_address;
  1114.  
  1115.         Parameters :
  1116.                 pointer_address is the address of a pointer into
  1117.                 which the address of the calling tasks TCB will be placed.
  1118.  
  1119.         Description :
  1120.                 this function is used to access your Task Control Block
  1121.                 you pass in the address of a pointer, and the address
  1122.                 of your TCB is placed into the pointer.  The pointer
  1123.                 must be a LONG pointer, as supported in the LARGE
  1124.                 memory model.
  1125.  
  1126.         Return Value :
  1127.                 the TCB identifier (an index into the TCB array) is
  1128.                 returned.
  1129.  
  1130.  
  1131.         Notes/Comments :
  1132.                 Normally, tasks do not need access to their TCB.  Since
  1133.                 TCBs contain critical task control information, great
  1134.                 care must be taken when accessing the TCB.
  1135.  
  1136.  
  1137.     GET_TCB_ADDRESS(pointer_address)
  1138.     struct tcb_rec far **pointer_address;
  1139.  
  1140.         Parameters :
  1141.                 pointer_address is the address of a pointer into
  1142.                 which the address of the TCB array will be placed.
  1143.  
  1144.         Description :
  1145.                 similar to 'GET_TCB_INFO()' above, but this call
  1146.                 returns the address of the TCB array in memory, to
  1147.                 allow your task to access any given TCB, based on
  1148.                 the task number.
  1149.  
  1150.         Return Value :
  1151.                 the TCB identifier (an index into the TCB array) of
  1152.                 the calling task returned.
  1153.  
  1154.  
  1155.         Notes/Comments :
  1156.                 Normally, tasks do not need access to their TCB.  Since
  1157.                 TCBs contain critical task control information, great
  1158.                 care must be taken when accessing the TCB.
  1159.  
  1160.  
  1161.  
  1162.  
  1163.     GET_LOAD_STATUS()
  1164.  
  1165.         Parameters :
  1166.                 none
  1167.  
  1168.         Description :
  1169.                 returns the status of the last 'LOAD_TASK()' call
  1170.                 made by your program.
  1171.  
  1172.         Return Value :
  1173.                  0 = load in progress (not done yet)
  1174.                 -1 = cannot open file
  1175.                 -2 = insufficient memory
  1176.                 -3 = no TCBs available
  1177.                 -4 = no Memory Control Blocks available
  1178.  
  1179.                 otherwise, the TCB number (index into the TCB array)
  1180.                 of the new task is returned.
  1181.  
  1182.         Notes/Comments :
  1183.  
  1184.  
  1185.  
  1186.  
  1187. Sample Programs
  1188. ---------------
  1189.  
  1190. 2 sample program are included, with full source code.
  1191. MTTEST.C will load and execute the programs TEST1.EXE, TEST2.EXE,
  1192. TEST3.EXE, TEST4.EXE and TEST5.EXE.  These 5 programs read
  1193. and write a disk file.  Each time they finish reading and writing
  1194. the file, they send a message to MTTEST.  MTTEST receives and
  1195. displays the messages.  It's not particularly impressive to
  1196. watch, but it does illustrate that several programs can perform
  1197. concurrent I/O, and that programs can easily be loaded and executed
  1198. from disk.
  1199.  
  1200. MT.C is another sample program.  It demonstrates one use of the
  1201. semaphores, and the sleep() function.
  1202.  
  1203. The source files for these programs, and a make file for Microsoft
  1204. C is included.
  1205.  
  1206.  
  1207. Writing Programs With CSWITCH
  1208. -----------------------------
  1209.  
  1210. When you write your multitasking program, there are several things
  1211. to remember :
  1212.  
  1213. 1. You must use the LARGE memory model.  Programs that are linked with
  1214.    SMTC, or that are simply loaded and run, may be small model.  But
  1215.    your main program must be LARGE model.
  1216.  
  1217. 2. You must compile your main program, and any program that will SPAWN
  1218.    tasks, with the /Gs option to turn off stack checking.  This is
  1219.    imperative, and without it, your program will not run.
  1220.  
  1221. 3. C library routines are not re-entrant.  Therefore, you cannot
  1222.    do a PRINTF() from your main program, and one from a SPAWNED task
  1223.    at the same time.  In general, if a spawned task needs to do I/O,
  1224.    do it with DOS interrupts rather than with the library calls.  If
  1225.    this is impractical or impossible, then make the spawned function
  1226.    a separate program and execute it with LOADTASK().
  1227.  
  1228.    If several different programs perform simultaneous I/O there is no
  1229.    problem.  It is only when the same PRINTF or FOPEN routines are
  1230.    called from 2 different places in the same program at the same time
  1231.    that there is a problem.
  1232.  
  1233.    This probably applies to other languages as well.
  1234.  
  1235.    Also, most library routines perform stack checking upon entry.  Since
  1236.    a SPAWNED task is assigned a stack from free memory, the stack check
  1237.    will often fail, causing SPAWNED tasks to abort with a false stack
  1238.    overflow error.  If SPAWNed tasks are going to be performing complex
  1239.    functions and making a lot of library calls, they probably should
  1240.    be made into separate programs and executed with LOADTASK().
  1241.  
  1242.  
  1243. Language Support
  1244. ----------------
  1245.  
  1246. Currently, the only language directly supported is C.  Most languages
  1247. can be made to mimic C's function calls, so there should not be a
  1248. problem with linking these routines to other languages.
  1249.  
  1250. Soon, I will be adding interface modules for Turbo Pascal and QuickBasic.
  1251.  
  1252. Identifiers are prefixed with an underscore ( _ ) by the C compiler, so
  1253. if your compiler does not automatically add the underscore, you will have
  1254. to use it explicitly in the module names, and in the global data names.
  1255.  
  1256.  
  1257. Cswitch Limitations
  1258. -------------------
  1259.  
  1260. Cswitch routines will not be appropriate in every situation requiring
  1261. multitasking.  In some cases, products like Desqview, Omniview, Windows,
  1262. OS/2, and Unix are more appropriate.  Cswitch does give a programmer
  1263. an option, however.  Writing programs that multitask in the DOS
  1264. environment is a clean solution to many problems.  In general, if your
  1265. application must somehow do several things all at once, then writing
  1266. your application as a set of cooperating tasks is a lot easier than
  1267. trying to build every function into a single task.  Let your
  1268. imagination take it from there.
  1269.  
  1270. As stated previously, Cswitch does not attempt to provide a multi-
  1271. tasking user interface.  You must prevent tasks from writing to the
  1272. monitor or reading the keyboard simultaneously.  This can easily be
  1273. done with semaphores or I/O redirection.
  1274.  
  1275. Tasks that are created using the SPAWN call inherit their owners STDIN,
  1276. STDOUT and STDERR, as do tasks loaded from disk.  Normally these handles
  1277. refer to the CON device - the keyboard and monitor.  No other file
  1278. handles are inherited.
  1279.  
  1280.