Category : C Source Code
Archive   : CSWITCH.ZIP
Filename : CSWITCH.DOC

Output of file : CSWITCH.DOC contained in archive : CSWITCH.ZIP


Multitasking Functions for DOS Programs

Copyright 1990 by Herb Rose
All Rights Reserved

This file is an introduction to the Cswitch functions. These
functions provide limited multitasking capabilities for programs
running under MS-DOS or PC-DOS.

The term 'limited multitasking' is used because these routines do not
provide a multitasking user interface like DESQVIEW or WINDOWS. These
routines are programming tools that will enable a C programmer to
write applications that consist of independent, simultaneously executing

Using Cswitch, you can execute functions as independent tasks, which
execute concurrently in a time-slicing environment. You may also load
and execute programs separately. Cswitch provides you with a priority
driven scheduler and dispatcher, DOS interface, and task control
functions to implement true multitasking under your program's control.

Before I continue the explanation of Cswitch, please bear with me as
I detail the legalities of using these routines.


This software is distributed as is, with no warranty of any kind,
either expressed or implied. No warranty or guarantee is made that
the software is complete or error-free. All liability and risk
associated with the use of this software is assumed by the user.
Under no circumstances will ADEPT Software, its owners, or its
agents be responsible for any damages arising from the use, or the
inability to use this product. This applies to all damages and
remedies including, but not limited to, loss of or damage to property,
and loss of profits.

This software has been tested in many configurations, under various
conditions, and has consistently performed acceptably. To the best
of my knowledge, there are no errors or omissions in the software
that will pose a problem to the user.

The user must be aware, however, that this is a programmer's tool,
and by it's very nature will affect the software that controls the
user's computer. All reasonable precautions should be taken to
protect your computer from the affects of an errant operating system.

In short -

Shareware Distribution

CSWITCH is distributed as shareware. You may freely use and distribute
these routines under the following conditions :

1. All files on this diskette must be distributed as a set, and no
modifications or additions may be distributed as part of the
set unless specifically approved in writing by Herb Rose.

2. All copyright and legal notices must remain intact and unaltered.

3. Copying and distribution fees may not exceed $6, and must be
clearly specified as such.

4. You must register Cswitch if you want a copy of the source code,
or if you wish to distribute a software product that makes use
of any of the Cswitch functions.

Note that registration is not required if you simply want to use Cswitch
in the privacy of your own home, or just to see what it can do. As an
incentive to registration, I include C and assembly source code when you
register. The source code is easy to read and well documented. Make
files are included for Microsoft C and Microsoft Assembler.

If you plan to distribute any software product that makes use of Cswitch
functions, then registration is mandatory. Registration provides you
with an unlimited, nonexclusive binary distribution license, with no
royalties. This is only fair, I think. If you distribute a program,
you should pay for the tools you used to create that program.

Registration is for use on a single computer. Contact Herb Rose for site
licensing, or for multiple copy discounts.

Due to practical constraints, I cannot provide technical assistance
except to registered users. I operate a 24-hour Bulletin Board System
to handle technical assistance. Telephone support is also available,
but I prefer to handle non-emergency questions via the BBS.

Registration Form

Please use the following form, or a reasonable facsimile, when registering

Mail to : ADEPT Software
P.O. Box 2181
Woodbridge, Va. 22193

CSWITCH Registration ..................... ______ @ $35 = ______

Va. Residents add 4.5% sales tax ...................... = ______

Overseas Air Delivery ................................. = __4.00
(U.S. delivery and Overseas Surface delivery is free)

TOTAL ................................................. = ______

Please send check or money order, in U.S. funds.

Your Name : _____________________________________

Company Name : _____________________________________

Address : _____________________________________

City, State, Zip : _____________________________________
(or country)

Would you like to receive information on our other products ? ____

Introduction to Multitasking

Multitasking is not a new concept. Computers have been programmed to
execute multiple tasks concurrently for many years. It was observed
a long time ago that most programs spent the majority of their time
waiting for data input. Since users and most peripheral devices are
notoriously slow compared to the execution speeds of modern computers,
most programs simply sit there 'twiddling their thumbs' waiting for data.
It was observed that the computer's computational capabilities could be
used more efficiently if another program were allowed to use the processor
while a program that was just waiting was put into a dormant state.
Carrying this observation one step farther, we find true multitasking.
Multitasking is the ability to have several programs loaded into the
computers memory, and apparently executing simultaneously. In reality,
there is a controlling program, called the Kernel, or Scheduler, that
causes the computer to execute some of each programs instructions in
a round-robin fashion. Programs that are waiting for something to
happen, such as waiting for a keystroke, or for a disk transfer to
complete, are normally placed into a 'waiting' state, and do not get
placed back into execution until the event they are waiting for has
actually happened.

Notice that I said programs were 'apparently' running simultaneously.
It is impossible for a microcomputer, such as the PC, to execute more
that one program simultaneously. Other computers may have several micro-
processors, and can actually execute more than one task simultaneously.
When there is only one microprocessor, the tasks must be executed one
at a time. The term 'time-slicing' is used to describe the operation
of a multi-tasking operating system, meaning that the processor will
execute each task's instructions for a short time, then will execute
another task's instructions for a while, eventually allowing all the
tasks to get some of their work done in a round-robin fashion. If the
amount of time given to each task is very large, say one or two seconds,
there could be a significant amount of time for each task to remain
idle while it awaited it's turn to execute. If the amount of time
given to each task (called the 'time slice') is too short, then too
much of the computer's time will be spent saving and restoring task
context information, diminishing the efficiency of the system. A
good time slice setting is one that allows many different tasks to
execute in a short period of time, without noticably affecting the
efficiency of the system. Cswitch uses the timer interrupt for the
PC to trigger a task switch. This occurs approximately 18 times per
second, or once every 55 milliseconds. Since a normal time slice on
a real multitasking operating system is usually around 50 microseconds,
our time slice is actually very large. In fact, 55 milliseconds is
way too big for a real-time operating system, which has to react to
external stimulus as quickly as possible. With 18 tasks loaded into
Cswitch, each task will only get to run once per second, for about
1/18 second. If some of the tasks are higher priority than others,
the lower priority tasks may not run at all for several seconds. This
is clearly not acceptable for programs that must react to external

The 55 millisecond time slice, however, is fine for running one or
two tasks at a high priority, and allowing several other tasks to
run at a lower priority. This gives the effect of 'background'
processes running.

If you find the 55 millisecond time slice inadequate, you should
consider altering the timer chip's programming so that it provides
a timer interrupt more often. Your interrupt handler will have to
count the number of interrupts, and activate the DOS time-of-day
routine (the normal destination of interrupt vector 8) once every
55 milliseconds, otherwise the computer's internal date and time
settings will not be accurate.

Now we will focus on how you get multiple tasks to cooperate so that a
given job will get done. Basically, there are only 2 areas that we are
concerned with -

1. Passing information from one task to another, and
2. Preventing another task from interfering with a resource you are using.

The first area is handled in Cswitch via Message Queues. A queue is
simply a list of messages. In this case, Cswitch has 64 queues, numbered
0-63. Any task may place a message onto any of the queues, and any
task may fetch a message from any of the queues. If there is more than
one message waiting on a queue, they will be fetched sequentially, on a
first-in, first-out basis.

A message may be any format, containing any information that may be needed
by another task. When placing a message on a queue, you must tell Cswitch
which queue to put it on, how long the message is (in bytes), and the
address of the first byte of the message. Cswitch will copy as many
bytes as you have told it into a temporary buffer, and will place a
pointer to that buffer into the message list for that queue.

When you fetch a message from a queue, you must tell Cswitch which queue
to read a message from, the maximum number of bytes you are willing to
receive, and where to put the message. After Cswitch has copied the
message (or as much of the message as you will allow) into your memory,
the temp buffer is released, and the message pointer is deleted from
the queue's list.

It is up to the individual tasks to coordinate which queue numbers will
be used to pass messages back and forth. In the Falken BBS, a multi-
line Bulletin Board System program written with Cswitch, the main
BBS program assigns input and output queues to each task when it is
started. Queue number 1 is reserved, and is used to send the queue
assignments to the tasks. In other words, when a task first runs
under control of Falken, it must read a message from queue number 1.
That message will tell it which queue it must read messages from, and
which queue it can write messages to, for the rest of the time it is

The second area of concern is to prevent another task from interfering
with a resource that you are using. A resource may be almost anything,
from a disk file to a memory buffer, to a peripheral device. Cswitch
uses a semaphore system to control access to these resources.

You must know in advance which resources must be controlled with
semaphores. If you are going to allow several tasks to access a
memory area simultaneously, it may be a good idea to control it, since
one task may try to write new information to the memory while another
task is trying to read the old information.

Semaphores work like this -
A semaphore has an owner and a waiting list. If your task attempts to
'attach' to the semaphore, but another task already 'owns' the semaphore,
your task gets placed onto the 'waiting list', and does not get to run
anymore (it is taken out of the ready queue). Your task will remain
dormant until it is the 'owner' of the semaphore.

When the owner of the semaphore 'releases' it, the first task in the
'waiting list' is made the new owner, and is allowed to run again.

Note : When you 'attach' to a semaphore, you will become the owner of
the semaphore before you are allowed to continue. Your
program remains dormant until it is the owner, so you must release the
semaphore when you are done with it, to allow other programs to run,
which may be waiting for the semaphore.

If you attempt to 'attach' to a semaphore, and it is unowned, your
task becomes the owner and is allowed to continue running immediately.
You must still release the semaphore when you are done.

Cswitch Functions

Cswitch allows tasks to perform the following functions :

- All normal DOS and BIOS functions
- Send information between tasks with Message Queues
- Control system resources with Semaphores
- Alter task priority
- Suspend / Resume task execution
- Delay for a time period (sleep)
- Start new tasks, either sharing existing code, or loading a new
program file from disk.

To use Cswitch, you must link your main program with the CSWITCH1.OBJ,
CSWITCH2.OBJ and LMTC.OBJ files. These files provide interrupt level
control for task switching, and the operating system functions, such as
message passing and semaphore control. In addition, programs which
will be loaded and executed under control of Cswitch must be linked
with LMTC.OBJ or SMTC.OBJ (for large or small model, respectively).
These files contain interface routines for calling system services.

Your main program must call the function START_MT().

START_MT initializes the Cswitch data areas and enables multitasking.
It intercepts several interrupt vectors, and builds Task Control
Blocks for your main program and for an 'idle task'. The idle task
runs at a low priority, and checks the delay and termination queues
regularly to service tasks waiting for those functions.
After the call to START_MT(), multitasking is fully operational,
and you may spawn tasks and load programs. Note that your main
program has a Task Control Block (TCB) and gets scheduled for time-
slicing just as every other task does. Your main program runs at a
priority of 1 (the highest priority allowed).

Before terminating, your main program MUST call the function END_MT().
END_MT restores the interrupt vectors, releases all unnecessary
memory, and halts all multitasking. Essentially, it resores the
computer to the same state it was in prior to the START_MT call.


If you allow your program to terminate without calling END_MT, almost
anything can happen, since the interrupt vectors are still pointing
to Cswitch code which may or may not be overwritten by DOS. At a
minimum, your system will lock up. A worst-case scenario could
involve unrecoverable damage to disk files.

It is highly recommended that you test all your programs that use
multitasking on a separate machine, to prevent loss of valuable
data or programs. At a minimum, make sure all your important files
are backed up before testing new programs that use Cswitch routines.

Executing Tasks

There are 2 methods of starting new tasks under Cswitch. The first
method is to SPAWN a new task that will execute a portion of your code
independently. Normally, this facility will be used to execute a
function as a separate task. The new task will be given it's own
stack and TCB, and will be scheduled for time-slicing normally. It
will begin executing code at the address you specify (normally a
function name). It may call other functions, and may manipulate
global data normally. any automatic (local) variables will reside
on the new tasks stack, so there will not be any interference when
a function is called by 2 different tasks. In fact, you can spawn

several new tasks which execute the same function if you like.

When the function being executed returns to the caller, the task will
be terminated. Any functions that are called by the spawned task will
return normally, of course. Only when the function being spawned
returns will the task terminate.

The second method of starting new tasks is to load a program from
disk and execute it. The function LOADPGM() is used to load an EXE
or COM from disk and execute it. You must supply 2 parameters to the
LOADPMG function - the command line to run the program and the priority
to be assigned to the task.

For instance, to load and run the program MYPROG.EXE with the parameter
'myfile.dat', youwould normally enter a DOS command line like this :

C:>myprog myfile.dat

To load and run the program under Cswitch, you would call the LOADPGM
function like this :

loadpgm("myprog myfile.dat",4);

This loads and runs 'myprog.exe', gives it the command line parameter
'myfile.dat', and allows it to run at priority 4.

The .EXE extension is assumed if no extension is provided. You can
load and execute .COM files by specifying the .COM extension. You
must specify the full path of the program to be executed, if it is not
in the current default directory. Cswitch will not search your PATH
for the program.


The Cswitch scheduler maintains a list of tasks that are waiting to
execute, called the 'ready queue'. When a task switch occurs, the
TCB at the head of the ready queue is fetched, and allowed to run.
Normally, the TCB that was just executing is inserted back into the
ready queue. In some cases, such as waiting for semaphore, delaying,
etc. the task will not be re-inserted into the ready queue.

Tasks are inserted into the ready queue according to their relative
priority. Each task is assigned a 'base priority' from 1 to 10.
1 is the highest priority, and 10 is the lowest. Each task also has
a 'current priority', which is used to insert TCBs into the ready
queue. Each time a task is fetched from the ready queue, the 'current
priority' of all the other TCBs on the ready queue is decremented
by 1. When a task is re-inserted into the ready list, it's current
priority is set equal to it's base priority, then it is inserted
into the ready queue so that it is in front of all tasks whose current
priority is lower. In this way, tasks with a higher priority will
be allowed to execute more often than tasks of lower priority. The
tasks with lower priority will remain on the ready queue until their
current priority increases to the point that the other tasks get
inserted behind them, and they work their way to the head of the queue.

Cswitch System Services

Cswitch must replace some of the DOS system services with it's own
services. In every case, the calling sequence and register setup
is identical do the DOS call. Return values are also identical
to DOS return values. In short, if your program ran OK under DOS,
it should run OK under Cswitch.

Cswitch replaces the following DOS (interrupt 21) services :

48h : allocate memory

Cswitch controls memory allocation for all tasks. DOS uses
a 'first fit' algorithm for allocating memory, meaning that
it allocates a chunk of memory from the first block that
is large enough to fulfill the request. This leads to memory
fragmentation, limiting the number of tasks that can run.
Cswitch uses a 'best fit' algorithm, searching all the
available memory blocks to find a block that is exactly the
size needed, or is closest to it. This way, memory fragmentation
is kept to a minimum.

Note : starting with DOS 3.3, you can alter the memory
allocation strategy that DOS uses, but 'first fit' is still
the default.

49h : release memory

Cswitch must also handle the memory release function. When a
block of memory is released, all memory is re-combined as much
as possible to limit memory fragmentation.

4ah : set memory block size

This DOS function is used to adjust the size of a memory block.
Since DOS tasks expect to be the only task running, they assume
that a memory block can be altered by simply increasing the size
of the block when they need more memory. Since several tasks
may occupy memory blocks adjacent to the block to be modified,
this is not always possible under Cswitch. Cswitch will attempt
to fulfill the request by looking for a free block adjacent to
the specified block, and combining them to form a larger block.
If unsuccessful, it will return an error.

Note that programs that allocate memory dynamically with CALLOC
or MALLOC calls may run into problems with this limitation.
The problem is that the library routines for allocating memory
(MALLOC, et al) use a table of structures to control the memory
blocks they obtain from DOS. Normally, this table will only
hold 20 entries (this is true for Microsoft C and QuickC).
Since Cswitch will return an error on most of the 'set memory
block' calls, the library routines will execute an 'allocate
memory' call every time you execute MALLOC. After 19 calls,
the table is full, and you can't allocate any more memory!

You can avoid problems by using the EXEMOD program to change
the minimum memory requirement of your task, so that enough
memory will be reserved when your task is loaded to handle
dynamic memory allocation demands without having to request
more memory from DOS.

Or, you can avoid using the MALLOC/CALLOC routines wherever
possible, and instead call the DOS allocate memory call
directly. Be careful, though, because Cswitch can only
control a total of 512 memory blocks.

4ch : terminate task

Cswitch handles task termination internally. It will release
memory held by the task, close all open files, and release the
Task Control Block.

1ah : set new DTA area

Cswitch records the address of the tasks new DTA in the TCB, then
passes this request to DOS normally. This function is normally
handled by the library routines, and is seldom called explicitly
by a programmer.

2fh : get DTA address

Cswitch returns the DTA address from the tasks TCB.

Cswitch Functions

The following functions are provided in the file CSWITCH1.C, and may
only be called by the main program. I.e. these functions may only be
called by the program that is linked with Cswitch1 and Cswitch2.


Parameters :

Description :
Starts the multitasking kernel, and enables all system
functions. This function must be called once, and only
once, before executing any of the other functions or
system services.

Return Value :

Notes/Comments :


Parameters :

Description :
Ends multitasking. The program that called START_MT()
must call END_MT() before terminating.

Return Value :

Notes/Comments :
See the warning above concerning the dire consequences
of terminating without restoring the interrupt vectors
and turning off multitasking.

char *cmd_string;
int pri;

Parameters :
cmd_string is a character pointer to a command string
to load the program, including command line parameters
just as you would type in to load the program under

pri is the base priority of the new task

Description :
Loads and executes an .EXE or .COM file from disk.
The full path to the program file must be specified,
and command line parameters may be used.

Return Value :
0 = no error
-1 = not enough memory to create new task stack
-2 = no free task control blocks
-3 = no free memory control blocks

Notes/Comments :
This function performs the same service as LOADTASK()
below. The difference is that this service is called
directly as a function. LOADTASK is invoked via the
INT 62H. Functionally, there is no difference, since
LOADTASK() eventually calls LOADPRG to do the work.

Cswitch Global Variables

The following variables are available to the main program. These are
provided for information only.

int dos48, dos49, dos4a, dos4c;

These integers represent a running count of how many DOS calls
(INT 21H) have been made for these function codes.

unsigned int base_mem_segment;

This is the segment value of the memory segment controlled by

int idlecount;

This is a running count of how many times the idle task has
run. The idle task is spawned when START_MT() is called, and
handles the tasks that are delaying and terminating.

int swap_count;

This is a running count of how many task switches have taken
place. When the scheduler is called, this count is incremented
even if no task switch takes place due to a task locking out
switching or some other reason.

Cswitch Internal Services

The following functions are provided in the file LMTC.OBJ and SMTC.OBJ,
and may be called by any task executing under Cswitch control. These
functions allow access to the semaphore, messaging, and task control
functions of Cswitch.

These functions are actually invoked via an INT 62H call. The parameters
are loaded into registers, and the INT 62H transfers control to an
assembly language routine. The registers are then pushed onto the stack,
and the C function for system calls is executed.

Most of the work done by Cswitch is done by C routines, with assembly
language used only for interrupt control.

All of these functions are of type INT, although some do not have return
values assigned.


Parameters :

Description :
Gives up the rest of the tasks time slice.
This is graceful way of not wasting processor time if
your task has nothing else to do for a short while.

Return Value :

Notes/Comments :

int *func_addr();
int pri;

Parameters :
func_addr is the address of a function or subroutine
that is to be executed as a separate task

pri is the base priority for the new task (1-10)

Description :
Causes the specified function or subroutine to be
executed as a separate task. A new stack is allocated
for the task, allowing it to have exclusive access to
local variables. It will still share global variables
with the calling task.

STDIN, STDOUT, and STDERR are inherited from the caller.

The function can be spawned more than once, allowing
multiple tasks to execute the same code thread.

Return Value :
0 = no error
-1 = not enough memory to create new task stack
-2 = no free task control blocks
-3 = no free memory control blocks

Notes/Comments :

char *cmd_string;
int pri;

Parameters :
cmd_string is a character pointer to a command string
to load the program, including command line parameters
just as you would type in to load the program under

pri is the base priority of the new task

Description :
Loads and executes an .EXE or .COM file from disk.
The full path to the program file must be specified,
and command line parameters may be used.

STDIN, STDOUT, and STDERR are inherited from the caller.

Return Value :
0 = no error
-1 = not enough memory to create new task stack
-2 = no free task control blocks
-3 = no free memory control blocks

Notes/Comments :
.COM files are given exactly 64K memory. .EXE files
are sized according to the information in the file
header. The MINALLOC parameter is used to determine
how much memory, in addition to the task size and
data space, is needed for the program.

int sleep;

Parameters :
seconds is the number of seconds the task is to remain

Description :
puts the calling task into a sleeping state, where it
will not execute at all, for a specified number of

Return Value :

Notes/Comments :

int queue;
char *message_addr;
int count;

Parameters :
queue is the message queue to place a message on
message_addr is a pointer to the data to be placed on queue
count is the number of bytes to place on the queue

Description :
places the specified number of bytes onto a message
queue. queues are numbered 0-63.

Return Value :
-1 = bad queue number
other wise, returns number of bytes transferred

Notes/Comments :

int queue;

Parameters :
queue is the queue number to test

Description :
tests whether there are any messages waiting on the
specified queue.

Return Value :
-1 = bad queue number
0 = nothing on queue
else, returns the number of bytes contained in the
first message on the queue

Notes/Comments :

int queue;
char *message_addr;
int count;

Parameters :
queue is the message queue to place a message on
message_addr is a pointer a data area to receive the data
count is the maximum number of bytes to transfer

Description :
reads the first message from the specified queue
into the data area pointed to by message_addr. only
the first 'count' bytes of the message are transferred.
if more than 'count' bytes are in the message, the
extra is lost.

If there are no messages on the specified queue, the
task is placed into a dormant state until a message is
put on the queue. If you do not want your task to remain
dormant if the queue is empty, you should call TESTMSG()
to make sure something is available before calling this

Return Value :
-1 = bad queue number
0 = no message was on queue
else, returns the number of bytes transferred

Notes/Comments :

int semaphore_number;

Parameters :
semaphore number is the number of the semaphore to
attach (0-63)

Description :
attaches to the semaphore. see semaphore description
elsewhere in documentation

Return Value :
-1 = invalid semaphore number
all other values = success

Notes/Comments :

int semaphore_number;

Parameters :
semaphore number is the number of the semaphore to
be released (0-63)

Description :
releases the semaphore. see semaphore description
elsewhere in documentation

Return Value :
-1 = invalid semaphore number
all other values = success

int semaphore_number;

Parameters :
semaphore number is the number of the semaphore to
test (0-63)

Description :
tests to see if the semaphore is already owned by
another task. see semaphore description
elsewhere in documentation

Return Value :
-1 = semaphore is unowned
all other values = semaphore is owned

int new_priority;

Parameters :
new_priority is the new base priority of the calling

Description :
allows a task to change its priority, affecting how
often the task gets to execute.

Return Value :

Notes/Comments :
only values 1-10 are legal. the lower the number, the
more often a task will run. the main program is assigned
a priroty of 1, and usually should be the only task with
such a low priority.


Parameters :

Description :
suspends the task until it is awakened by a WAKEUP()

Return Value :

Notes/Comments :

int tcb_number;

Parameters :
tcb_number is the tcb identifier for the task that is
to be awakened.

Description :
allows a task that was suspended with a SUSPEND() call
to continue processing.

Return Value :

Notes/Comments :
This is the ONLY way to wake up a SUSPENDed task.
This call will also wake up a task that is in a
SLEEP state (see SLEEP() above). In this way, a task
may go into a sleep state, and another task can cause
it to start executing again before the sleep delay is


Parameters :

Description :
prevents this task from being swapped out until a
corresponding NOHOG() is issued. In effect, this
halts multitasking, allowing the caller to run
forever, if desired.

Return Value :

Notes/Comments :
Use this function when your task must not be interrupted.
DOS functions like I/O and other critical functions are
already protected, so this function should not be used
too often.


Parameters :

Description :
allows multitasking to continue after being halted by
a HOG() call.

Return Value :

Notes/Comments :


Parameters :

Description :
halts execution of a SPAWNed task.

Return Value :

Notes/Comments :
this is the preferred method of halting execution of a
spawned task. When the spawned task is finished, it
should call this function to terminate.

struct tcb_rec far **pointer_address;

Parameters :
pointer_address is the address of a pointer into
which the address of the calling tasks TCB will be placed.

Description :
this function is used to access your Task Control Block
you pass in the address of a pointer, and the address
of your TCB is placed into the pointer. The pointer
must be a LONG pointer, as supported in the LARGE
memory model.

Return Value :
the TCB identifier (an index into the TCB array) is

Notes/Comments :
Normally, tasks do not need access to their TCB. Since
TCBs contain critical task control information, great
care must be taken when accessing the TCB.

Sample Programs

A sample program is included. MTTEST.C will load and execute
the programs TEST1.EXE and TEST2.EXE. These 2 programs read
and write a disk file. Each time they finish reading and writing
the file, they send a message to MTTEST. MTTEST receives and
displays the messages. It's not particularly impressive to
watch, but it does illustrate that several programs can perform
concurrent I/O, and that programs can easily be loaded and executed
from disk.

MT.C is another sample program. It demonstrates one use of the
semaphores, and the sleep() function.

The source files for these programs, and a make file for Microsoft
C is included.

Writing Programs With CSWITCH

When you write your multitasking program, there are several things
to remember :

1. You must use the LARGE memory model. Programs that are linked with
SMTC, or that are simply loaded and run, may be small model. But
your main program must be LARGE model.

2. You must compile your main program, and any program that will SPAWN
tasks, with the /Gs option to turn off stack checking. This is
imperative, and without it, your program will not run.

3. C library routines are not re-entrant. Therefore, you cannot
do a PRINTF() from your main program, and one from a SPAWNED task
at the same time. In general, if a spawned task needs to do I/O,
do it with DOS interrupts rather than with the library calls. If
this is impractical or impossible, then make the spawned function
a separate program and execute it with LOADTASK().

If several different programs perform simultaneous I/O there is no
problem. It is only when the same PRINTF or FOPEN routines are
called from 2 different places in the same program that there is a

This probably applies to other languages as well.

Language Support

Currently, the only language directly supported is C. Most languages
can be made to mimic C's function calls, so there should not be a
problem with linking these routines to other languages.

Soon, I will be adding interface modules for Turbo Pascal and QuickBasic.

Identifiers are prefixed with an underscore (_) by the C compiler, so if
your compiler does not automatically add the underscore, you will have
to use it explicitly in the module names, and in the global data names.

Cswitch Limitations

As stated previously, Cswitch does not attempt to provide a multi-
tasking user interface. You must prevent tasks from writing to the
monitor or reading the keyboard simultaneously. This can easily be
done with semaphores.

Tasks that are created using the SPAWN call inherit their owners STDIN,
STDOUT and STDERR, as do tasks loaded from disk. Normally these handles
refer to the CON device - the keyboard and monitor. No other file
handles are inherited.

Currently, Cswitch is limited by the size of your conventional memory
for loading and executing tasks. Soon, there will be a page-swapping
function included to swap tasks to and from expanded memory.

Cswitch may not be able to load and execute programs that have been
PACKED with Microsoft's EXEPACK program (/E option on the LINK).
If a program is to be loaded under Cswitch, do not pack it.

  3 Responses to “Category : C Source Code
Archive   : CSWITCH.ZIP
Filename : CSWITCH.DOC

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: