home *** CD-ROM | disk | FTP | other *** search
Text File | 1989-12-24 | 209.6 KB | 5,311 lines |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- CTask
-
- A Multitasking Kernel for C
-
-
-
- Version 2.0 Released 89-12-21
-
-
-
-
- Public Domain Software written by
-
- Thomas Wagner
-
- Ferrari electronic GmbH
-
-
-
- Contents
-
-
- About this Manual 1
-
- Introduction 1
- An Example 1
- Switching the Context 2
- You have Mail 2
- Reentrancy and Resources 3
- DOS Access 3
- Handling the Keyboard 4
- Serial I/O and Timeouts 4
- Priorities 5
- Change to your liking 5
-
- General Notes 6
- What can CTask NOT be used for? 6
- What is required to use CTask? 6
- Do I have to pay for using CTask? 7
- What support can I expect? 8
- About this Release 9
-
- Multitasking Basics 10
- Tasks 10
- Events 10
- Reentrancy 11
- Deadlocks 12
-
- Using CTask 14
- Configuration Options 14
- Memory Allocation 16
- Snapshot 17
- Task Stacks 17
- Drivers 18
- Things to remember 18
- Priority Handling 19
-
- Multitasking and DOS 20
- Spawning and CTask TSR's 21
- Task Groups 22
-
- How does CTask work 24
- Queues 24
- The Scheduler 25
- Events 25
- Resources 25
- Flags 26
- Counters 26
- Mailboxes and Pipes 26
- Serial and Printer Drivers 27
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Contents 1
-
-
- CTask Data Types 29
- Typedefs used for simplified type specifications 30
- Error return values for event wait functions 30
- Queues 31
- The timer/watch control block 32
- The name link structure 36
- The task control block structure 36
- Task states 38
- Task flags 39
- The Group Control Block 40
- The event control blocks 40
- The Ticker structure 41
- The flag event structure 41
- The counter event structure 42
- The resource event structure 42
- The mailbox event structure 43
- The pipe and word pipe event structure 43
- The buffer event structure 44
-
- CTask Routines 45
- Installation and Removal 45
- Searching for names 47
- Miscellaneous 48
- Task Operations 50
- Timer Operations 52
- Event wait Timeouts 53
- "Tickers" 53
- Delays 54
- Timed Events and Watch Events 54
- Event Operations 59
- Resources 59
- Flags 61
- Counters 62
- Mailboxes 64
- Pipes 65
- Buffers 67
- The Keyboard Handler 69
- The Serial I/O handler 69
- The Printer Output Driver 75
-
- Some notes on potential trouble spots 77
- Turbo C console output 77
- The timer tick EOI 77
- Debugging 77
-
- Changes from Previous Versions 79
- Changes for CTask 1.2 to 2.0 79
- Interface Changes 79
- Changes for CTask 1.1b to 1.2 81
- Changes for CTask 1.1 to 1.1b 83
- Changes for CTask 0.1 to 1.1 83
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Contents 2
-
-
- About this Manual
-
- If you are new to CTask, I would suggest reading all chapters
- before attempting to build your first application. One chapter
- you can skip is the CTask Data Types section, since you are not
- required to know about the innards of CTask's structures. You can
- also ignore the List of Changes.
-
- If you are updating from an older release, the List of Changes
- at the end is for you. You can safely skip the Introduction, and
- the section on Multitasking Basics. In the General Notes, you
- should read the section on support and the info about this
- release. In the Using CTask section, check the new configuration
- options, and the chapter on Memory Allocation. Check the Routine
- descriptions, and, if you are using internal data structures
- directly, the Data Type descriptions, for changes.
-
-
- Finally, a note to German speaking readers:
-
- Dieses Handbuch, wie auch die Kommentare im Quellcode, ist in
- Englisch abgefaßt, da dies die universelle Sprache für Computer-
- benutzer weltweit ist. Es gibt keine deutsche Version, und es
- wird auch keine geben (sofern nicht jemand bereit ist einen
- angemessenen Betrag für eine Übersetzung auf den Tisch des Hauses
- zu legen). Selbstverständlich bleibt es Ihnen unbenommen, das
- Handbuch selbst zu übersetzen und diese Version auch zu
- vertreiben, oder mir zum Vertrieb anzubieten. Wie die Software
- ist auch dieses Handbuch Public Domain.
-
-
-
- Introduction
-
- CTask is a set of routines that allow your C program to execute
- functions in parallel, without you having to build in sophisti-
- cated polling and switching schemes. CTask handles the switching
- of processor time with a priority based, preemptive scheduler,
- and provides a fairly complete set of routines for inter-task
- communication, event signalling, and task interlocking. CTask
- also includes a number of drivers for MS-DOS that build on the
- basic functions to allow you to include serial I/O, printer
- buffering, and concurrent access to DOS functions into your
- programs with little programming effort.
-
-
- An Example
-
- To illustrate one possible use of CTask, let me elaborate on the
- following example. Say you just finished your nifty telecommuni-
- cations program, complete with download protocols, scripts, and
- everything. But wouldn't it be nice to be able to print the file
- you just downloaded while receiving the next, and edit a comment
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 1
-
-
- to some previous message without interrupting the transmission?
- So you take those editor routines from a previous project, plug
- them in, and - oops, how do you switch back and forth between
- editing, communication and printing? The answer to this is CTask.
- CTask allows your C program to do many different things at the
- same time by switching the processor between the tasks you de-
- fine. And since most of the time your program is waiting for some
- slow device (like the human hand) to provide feedback, this
- switching is completely transparent, and will not noticeably slow
- your program down.
-
-
- Switching the Context
-
- So what is needed to allow the user to edit a file while at the
- same time downloading another and printing a third? First, you
- have to have some form of "context switching". This means that
- you have to be able to interrupt the processing of the download
- when the user presses a key, process the key, and return to the
- download "task" at the exact same point it was interrupted. One
- solution to this would be to include a poll for the keyboard at
- several points in the download routine, and call the editor task
- when a key is available. But apart from cluttering your code
- with lots of unrelated calls, there is another problem. What if
- the operation the user requested is more involved than just
- putting the character on the screen, like writing the file to
- disk? This might take so long that your download times out. There
- must be a way to pass control back and forth between the two
- tasks, such that no task is delayed for an extended period of
- time, and also to activate the print spooler task at some defined
- interval to output the data to the printer. This context
- switching is called "scheduling" in CTask. The "scheduler" is
- invoked on every system timer tick, and will save the context of
- the current task. The scheduler then takes the first element from
- the queue of tasks that are eligible to be run, and restores the
- context of this task, returning to the point where the task was
- interrupted. This switching is completely automatic, and requires
- no special programming in the tasks itself. All you have to do is
- to tell CTask that there are three tasks, the download task, the
- spooler task, and the editor task.
-
-
- You have Mail
-
- All you have to do for context switching, that is. There's a bit
- more to multitasking than meets the eye. How do you tell the
- spooler task what files to spool, and the download task what
- files to download? You can't call a task like an ordinary func-
- tion, so what you need for this is "inter-task communication".
- There must be a way to pass a message containing the filename to
- be printed to the spooler task, and there are several in CTask,
- one of them the "mailbox". The spooler can use a CTask call,
- wait_mail, to wait for a message to arrive at its mailbox. As
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 2
-
-
- long as nothing arrives, the spooler task will no longer be
- scheduled, so if there is nothing to print, it will not use any
- processor time. When you send a message with send_mail to the
- mailbox, the spooler will wake up, and process the file. You can
- also send more file name messages to the spooler while it still
- prints a file, leaving the messages in the mailbox until the
- spooler is ready to process the next file.
-
-
- Reentrancy and Resources
-
- This last example seems innocent enough, but there's a big stumb-
- ling block hidden in it. You allocate the file name messages in
- the controlling task with malloc, and you free them in the
- spooler with free, no problem, right? Wrong, there is a big
- problem, "reentrancy". Reentrancy means that you can re-enter a
- routine while another task is already using it, and that this
- will not disturb the operation of the interrupted task. But
- malloc and free share and modify global data, the chain of free
- memory blocks. Imagine the following: You just called malloc from
- the controlling task. Malloc has loaded the address of a free
- element into a local variable, and is about to write back the
- pointer to the next free element into the last. At exactly this
- moment, the timer ticks, and the spooler is activated. It has
- just finished printing, so it calls free. Free steps through the
- chain of free blocks to find the right place to insert the block.
- According to Murphy's law, it will find just the place where
- malloc is about to write back the pointer. Free coerces the
- elements, points the next pointer to the element malloc just
- wants to take off the chain, and returns. Malloc writes its next
- pointer into the middle of the newly coerced block, and now re-
- turns an element which is still in the free list. Compared to the
- job of finding this kind of bug, stepping in for Tantalus may
- feel like a vacation. This kind of problem code is called a
- "critical region". There must be a way to make sure that no two
- tasks simultaneously enter such a region, and, you guessed it,
- CTask provides one, the "resource". When you request a resource
- in one task, all other tasks trying to request the same resource
- after that are put to sleep until you call release_resource. Only
- then will the highest priority task that waits for the resource
- wake up, and get access to the protected region. So you would
- have to substitute malloc and free calls in your routines with
- calls to functions that first request a resource, execute the
- function, and then release the resource.
-
-
- DOS Access
-
- But, you might ask, isn't there another reentrancy problem in
- this example, since both the spooler and the download task might
- simultaneously call DOS to do their file-I/O, and DOS is not
- reentrant? Do I have to substitute all my calls to fread and
- fwrite, too? The answer to this, luckily, is no. CTask traps all
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 3
-
-
- your DOS calls, and automatically encloses them in the necessary
- resource request and release calls, so you don't have to worry
- about trashing your disk by simultaneous DOS requests. The
- limited multitasking capabilities of DOS are exploited to allow
- some parallel processing in DOS, and CTask will also detect and
- handle DOS calls by resident background programs like the DOS
- PRINT utility.
-
-
- Handling the Keyboard
-
- CTask also allows you to circumvent DOS for keyboard input, so
- that waiting for the keyboard will not block other tasks from
- access to DOS functions. Previous versions of CTask used a "pipe"
- to store all keyboard input. Starting with version 1.2, CTask
- uses a "flag" to signal that keyboard input might be available. A
- "flag" is another form of inter-task communication that just sig-
- nals that some event occurred, without passing any specific data.
- The reason for using flags in the keyboard handler is compati-
- bility to TSR's. If a keyboard interrupt occurs, the interrupt
- handler just sets a flag, and passes on the interrupt. The key-
- board routines wait for this flag to be set, and then check the
- keyboard buffer if a character has arrived. If the keyboard buf-
- fer is empty, the flag is again cleared, and the keyboard rou-
- tines put the waiting task to sleep again, so the processor is
- free to do more interesting things than to loop waiting for the
- user to press a key.
-
- Serial I/O and Timeouts
-
- The "pipe" is similar to the mailbox in that you can wait for
- items to be sent to a pipe. But unlike mailboxes, pipes use their
- own buffer to store the items (which are limited to bytes and
- words), so you don't have to allocate mail blocks for each item.
- When waiting on the pipe, your task is put to sleep, freeing the
- processor. Pipes are used for the serial I/O handler included
- with CTask that makes some of the work you've put into your
- communications package obsolete. When outputting data to the
- serial port via the CTask routines, the data is buffered in a
- pipe, and incoming data is also placed in a pipe. All interrupt
- handling, and the processing of modem status and XON/XOFF proto-
- cols, is done by CTask, so you can concentrate on implementing
- the higher level protocols. Since CTask allows all calls that
- wait for pipes, mail, and other events, to specify a timeout that
- is based on the system tick, you do not have to resort to timed
- waiting loops to detect communication line faults. You simply
- give a time limit on the wait call, and if that limit expires,
- the wait routine will return with an error indication.
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 4
-
-
- Priorities
-
- If the protocol you implement requires fast responses to incoming
- blocks, you can influence the response of CTask to your comm
- task's needs by giving this task a higher priority. CTask allows
- 65535 different priority levels, and tasks having higher priority
- are scheduled before tasks with lower priority. Also, high prio-
- rity tasks will get access to mail, pipes, and resources, before
- other tasks. It might even be sensible to split the comm task
- into two separate tasks, one of high priority that assembles the
- incoming bytes into blocks and handles the protocol, and a lower
- priority task that reads the received blocks from a mailbox and
- stores them on the disk. In extremely time critical applications,
- you can even turn off task preemption, so the timer tick will no
- longer cause a task switch.
-
-
- Change to your liking
-
- CTask provides all basic building blocks for implementing
- concurrent programs in an easy and comprehensible way. Since
- CTask is mainly implemented in C, it may not be the fastest
- possible system, but due to the straightforward design which uses
- few shortcuts, modifying the sources to suit your needs and taste
- can be done without weeks of studying assembler code that
- squeezes every microsecond from the processor. CTask is public
- domain code, and there are no restrictions on its use. It is
- distributed in source form, so you are free to change all aspects
- of the package. Multitasking programs, especially in embedded
- applications, tend to be very diverse in their needs for specific
- constructs. So although CTask is ready to run under DOS, and is
- easily adaptable for embedded applications, you should see CTask
- more as a starting point for your own thoughts, and as a toolbox
- from which you can pick the instruments you need, than as a
- finished and fixed block of code you simply plug into your
- application.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 5
-
-
- General Notes
-
-
- What can CTask NOT be used for?
-
- CTask is not intended to provide for multitasking on the command
- level of MS-DOS. Although version 1.2 of CTask added the ability
- to TSR and spawn other programs, and to communicate between
- multiple copies of CTask, a full DOS-process management (which
- would have to include keeping track of memory allocation and DOS
- process control blocks) is not included. Adding this functio-
- nality would not be trivial (although certainly worthwhile).
-
- CTask also is not a true "Real-Time" multitasking system. Due to
- the completely dynamic structure of tasks and events, and the
- minimal restrictions on what interrupt handlers may do, it is
- nearly impossible to calculate a maximum interrupt or task switch
- latency. If you have critical timing requirements, you should
- consider getting a professional package like AMX. CTask has been
- used successfully in embedded control applications, and if your
- timing requirements are not that critical, or you're ready to
- take up the task of measuring and calculating latencies with your
- specific task setup, CTask may be useful even for Real-Time
- applications. However, you're more or less on your own in this
- field.
-
- And, there is no warranty that CTask does perform without errors,
- or does exactly what you or I intended. So CTask should not be
- used in applications in which malfunction of routines of this
- package would result in damage to property or health of any
- person without *very* extensive testing under all kinds of loads.
- In using CTask, you do so at your own risk. I have tested CTask
- extensively, but with a complex system like CTask, where timing
- might make a big difference, you can't be completely sure that
- all will work as intended.
-
-
- What is required to use CTask?
-
- To compile CTask, Microsoft C 5.1 or later, or Turbo C 2.0 or
- later are required. Microsoft MASM 5.1 or later, or TASM 1.0 or
- later is required for the assembler parts. Conversion to other
- compilers is possible if they conform to the new ANSI (draft)
- standard. Conversion of the assembler parts to other Assembler
- versions requires substitution of the simplified model directives
- by explicit segment definitions, and adding the DGROUP to offsets
- referencing data.
-
- CTask will add 14k-25k of code to your program, depending on
- options and installed drivers. The minimum static data used by
- CTask is approximately 4k. Non-DOS versions use less memory.
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 6
-
-
- Converting CTask for stand-alone operation requires few changes.
- Mainly, the timer interrupt handler (in "tsktim.asm") has to be
- rewritten, and the initialization code (in "tskmain.c") may have
- to be changed. Changes to other modules (naturally except the
- optional hardware drivers) should not be necessary. The "DOS"
- configuration flag in tskconf.h can be disabled to eliminate most
- system-dependent features of CTask.
-
- Another requirement is a good debugger. If you never before wrote
- multitasking applications, you're in for some surprises. The
- normal debugging tools (Symdeb, Codeview) are of only limited
- use, since they use DOS calls for their I/O, and thus may
- conflict with your background tasks. One safety measure is to
- first thoroughly test your program with preemption disabled,
- possibly inserting some schedule() calls, and only allow task
- preemption if you found most major bugs. I personally recommend
- Periscope for debugging, since it can be made resident and so is
- always available, and because it does not use DOS. Periscope IV
- is the most expensive solution, and the best tool you can
- imagine, but the less costly versions will also help a lot.
-
-
- Do I have to pay for using CTask?
-
- No. One reason for writing CTask was to provide a free, no
- strings attached, utility, instead of the usual "for personal use
- only" restriction. Writing a multitasking application for
- personal use only doesn't seem too interesting to me. CTask is
- completely free, and there is no restriction on its use. You may
- incorporate all or parts of CTask in your programs, and redistri-
- bute it in source or binary form by any means. I also do not
- restrict the use of CTask in commercial applications. Since
- trying to distribute the unmodified CTask for money will only
- give you a bad name, you may even do that if you find someone
- dumb enough to buy it. Naturally, if you make a bundle from it,
- or simply like CTask, I would not reject a donation. However,
- this is not required, and it will not give you any special
- support.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 7
-
-
- What support can I expect?
-
- I will try my best to eliminate any bugs reported to me, and to
- incorporate suggested enhancements and changes. However, my spare
- time is limited, so I can not guarantee continued or individual
- support. (But since I'm one of the owners of a consulting firm,
- you can always hire me to do it...). Please address all reports
- or questions to my business address:
-
- Ferrari electronic GmbH
- attn: Thomas Wagner
- Beusselstrasse 27
- D-1000 Berlin 21, West Germany
-
- Phone: (49-30) 396 50 21
- Fax: (49-30) 396 80 20
-
- BIX: twagner
- UUCP: oeschi@netmbx.UUCP (attn: Thomas Wagner)
-
- But, please, if at all possible, do it in writing. Please do not
- phone unless it is absolutely vital (or you have a business
- proposal). I like to hear about any applications for CTask, and
- if you are visiting Berlin, I also invite you to drop by for a
- talk. But I am usually not that happy when I am interrupted in my
- paid work by a phone call requesting support for a free product.
-
- I will try to answer all letters and Faxes I receive. However, I
- am usually not the fastest in this respect, so please be patient.
- The preferred, and the fastest, method to reach me is through
- BIX.
-
- BIX (tm) is the BYTE Information Exchange, an electronic confe-
- rencing system created by McGraw-Hill, the publishers of the well
- renowned BYTE magazine. BIX can be (and is) accessed from all
- parts of the world. Although accessing BIX from outside the US
- isn't exactly cheap (don't ask me what I have to pay each month),
- the wealth of information available there, and the fast and
- extensive help the other members can give you on all kinds of
- hard- and software problems, makes it worth every Mark, Peseta,
- Franc, or Ruble you have to spend. New versions and updates of
- CTask will first appear on BIX.
-
- At the time of this writing, I am one of the moderators of the
- IBM exchange on BIX, moderating the "ibm.other" conference. I
- have created a support topic for CTask, where all suggested
- enhancements and changes, plus bug reports, can be posted. Just
- join "ibm.other", topic "ctask". You can also report problems of
- limited general interest via BIXmail to "twagner". Unless I am
- not able to reach the keyboard for some reason, I log on at least
- once per day, so you can expect relatively fast responses to your
- queries.
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 8
-
-
- To get more info on joining BIX, call the BIX Customer Service at
- 800-227-2983 (U.S. and Canada), or 603-924-7681 (New Hampshire
- and outside the U.S.) from 8:30 to 23:00 Eastern Time (-5 GMT).
- BIX access currently is $39 for three months (flat fee, no extra
- charges for connect time), plus the applicable telecomm charges
- (Tymnet in the U.S. and Canada, your local PTT's Packet Net
- charges from outside the U.S.). If you're calling from the US,
- you can subscribe by dialling BIX direct at 617-861-9767. Hit the
- return key, and enter "bix" at the 'login (enter "bix")' prompt.
- At the 'Name?' prompt, enter "bix.flatfee". International users
- need an account (NUI) with their local packet net. Please enquire
- at your post/telecomm office for details. If you already own a
- NUI, enter the BIX international network address (NUA),
- "310690157800", and enter "bix.flatfee" at the 'Name?' prompt.
-
-
- About this Release
-
- Since the Beta release of CTask in March 1988, CTask has found
- widespread distribution through several channels. I have heard
- from some users, and their suggestions have been implemented in
- this version as far as possible. Special thanks go to Kent J.
- Quirk, Peter Heinrich, Stephen Worthington, Burt Bicksler, Tron
- Hvaring, Joe Urso, and Dave Goodwin, for their bug reports,
- suggestions, and enhancements. Bug reports also came in from
- others, thanks to all who wrote or called. The serial code in
- TSKSIO.C was enhanced by S. Worthington.
-
- This release is the long awaited successor to version 1.1.
- Although I did announce a "version 1.2", and some users even
- received beta copies of it, 1.2 was never officially released.
- This release is called 2.0 because of the major changes to the
- main CTask data structures, which significantly impact the kernel
- routines. Version 1.2 added most of the new features, so you will
- see references to 1.2 throughout the manual. If you've been using
- (and modifying) 1.1 or a 1.2 pre-release, and the changes to the
- queue structure cause trouble for your application, the final
- version of 1.2 is available on special request.
-
- Again, please notify me of your CTask application, report bugs,
- or suggest changes and enhancements. If I know you're using
- CTask, I can notify you of possible new releases (although none
- is currently planned), and of possible severe bugs.
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 9
-
-
- Multitasking Basics
-
- Tasks
-
- In CTask, a "task" is defined as a (far) C function. The number
- of tasks is not limited, and one function may be used for several
- tasks. There is little difference between a task function and a
- normal function. The usual form of a task function is
-
- void far my_task (farptr arg)
- {
- one-time initialization code
- while (TRUE)
- {
- processing code
- }
- }
-
- A task function is (usually) never called directly. Rather, it is
- specified in the call to the create_task routine, and started by
- start_task. It will then continue to run, sharing the processor
- time with all other tasks, until it is "killed" by kill_task.
- Returning from the routine will have the same effect as a kill.
- The sharing of processor time is accomplished by "preempting" the
- tasks. Preemption means that the task is interrupted in the
- middle of some statement by a hardware interrupt (usually the
- timer), and is *not* immediately restarted when the interrupt
- handler returns. Instead, the next task that is able to run is
- activated by the "scheduler", with the interrupted task
- continuing its duty at some (normally unpredictable) later time.
- You can also have multi-tasking without preemption, and CTask
- supports this, too, but this requires full cooperation of all
- tasks in the system, such that no task continues to run for an
- extended period of time without passing control to other tasks by
- an explicit scheduling request, or by waiting for an event.
-
- The optional argument to the task function may be used if one
- function is to be used for more than one task, for example to
- pass a pointer to a static data area for use by this specific
- instance of the function.
-
-
- Events
-
- Tasks alone would be of limited use. If you have several routines
- which just do number crunching or sorting or such, making them
- into parallel tasks would be sensible only on a multiprocessor
- system. Normally, at least some of your tasks will wait for some
- outside "event" to happen, be it the user pressing a key, or a
- character arriving from the modem. Then there may be tasks which
- have to wait until another task finishes processing on some piece
- of data before they can continue. For this synchronization, there
- are a number of constructs in CTask, which I summarize under the
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 10
-
-
- name "event". Common to all events are the operations of waiting
- for an event to happen, and signalling that the event has
- happened. Using a CTask event is much more efficient than looping
- while waiting on some shared data location to change state, and
- also eliminates concurrency problems inherent in such a simple
- approach. Tasks waiting for an event are taken off the scheduler
- queue, so they no longer use processor time.
-
-
- Reentrancy
-
- One of the biggest problem with multitasking in general, and C on
- the PC in particular, is reentrancy. Reentrancy means that you
- can use a routine, be it you own, or one of the C run-time
- library, from different tasks at the same time. When writing your
- own code, you can easily avoid problems, but when using the run-
- time library routines, you often can only guess if the routines
- are reentrant or not.
-
- A routine is NOT reentrant if it modifies static data. This can
- be illustrated by the following nonsense example:
-
- int non_reentrant (int val)
- { static int temp;
- temp = val;
- return temp * 2;
- }
-
- Now take two tasks, which call this routine. Task1 calls it with
- val=3, Task2 with val=7. What will be the return value for both
- tasks? You never know. There are three possible outcomes:
-
- 1) The tasks execute sequentially. Task1 will get 6, and Task2
- 14 as a result. This is what one normally expects.
-
- 2) Task1 runs up to "temp = val", then is interrupted by the
- timer. Task2 executes, and gets 14 as result. Then Task1
- continues. Return for Task1 is 14.
-
- 3) Task2 runs up to "temp = val", then is interrupted by the
- timer. Task1 executes, and gets 6 as result. Then Task2
- continues. Return for Task2 is 6.
-
- add to this the effects of optimization, and a loop, and the
- outcome is completely random.
-
- Most routines in the C library will not explicitly do something
- like this, but all functions that
-
- - do file I/O (read, write, printf, scanf, etc.) or
- - change memory allocation (malloc, free, etc.)
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 11
-
-
- have to use some static data to do buffering, or to store the
- chain of memory blocks in. Interrupting such an operation may
- have disastrous effects. The most devilish aspect of non-
- reentrancy is that the effects are unpredictable. Your program
- may run 1000 times without any error, and then on the 1001th time
- crash the system so completely that only the big red switch will
- help.
-
- So what can you do about it? A lot. There are several ways to
- protect "critical regions" from being entered in parallel. The
- most simple method is to disable interrupts. This, however,
- should be used only for *very* short periods of time, and not for
- rou-tines that might themselves re-enable them (like file-I/O). A
- better method is to temporarily disable task preemption. The
- routines tsk_dis_preempt and tsk_ena_preempt allow this form of
- short-term task switch disable. Interrupts may still be
- processed, but tasks will not be preempted. The best way is to
- use a "resource". A resource is a special kind of event, which
- only one task can possess at a time. Requesting the resource
- before entering the routine, and releasing it afterwards, will
- protect you from any other task simultaneously entering the
- critical region (assuming that this task also requests the
- resource).
-
- It is also reasonably safe to use file-I/O without protection if
- it goes to different files. The C file control blocks for
- different files are distinct, so there will be no conflict
- between tasks. Since DOS access is automatically protected by
- CTask, concurrent file I/O is possible.
-
- What you may NEVER do is to use a non-reentrant routine that is
- not protected by an interrupt disable from an interrupt handler.
- An interrupt handler is not a task, and so can not safely request
- a resource or disable task preemption. This is the reason why the
- CTask routines generally disable interrupts before manipulating
- the internal queues rather than only disabling task preemption.
-
-
- Deadlocks
-
- One thing to watch out for when using resources or similar event
- mechanisms is not to get into a situation where Task 1 waits for
- a resource that Task 2 has requested, while at the same time Task
- 2 waits for a resource that Task 1 already has. This situation is
- called a deadlock (or, more picturesque, deadly embrace), and it
- can only be resolved with outside help (e.g. waking up one of the
- tasks forcibly). To illustrate, consider the following example:
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 12
-
-
- void far task_1 ()
- {
- ...
- request_resource (&rsc1, 0L);
- request_resource (&rsc2, 0L);
- ...
- }
-
- void far task_2 ()
- {
- ...
- request_resource (&rsc2, 0L);
- request_resource (&rsc1, 0L);
- ...
- }
-
- Since interrupts are always enabled on return from a task switch,
- even if the statements are enclosed in a critical region, there
- is no guarantee that the request_resource calls will be executed
- without interruption. In this example, the problem is obvious,
- but in a more complex application, where resource requests or
- other waits might be buried in some nested routine, you should
- watch out for similar situations. One way to avoid problems would
- be in this example to change task_2 to
-
- void far task_2 ()
- {
- int again;
- ...
- do {
- request_resource (&rsc2, 0L);
- if (again = c_request_resource (&rsc1))
- {
- release_resource (&rsc2);
- delay (2L);
- }
- } while (again);
- ...
- }
-
- Note that this is only one of many possible approaches, and that
- this approach favors task_1 over task_2.
-
- You should also take care not to kill tasks that currently own a
- resource. CTask will not detect this, and the resource will never
- be freed.
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 13
-
-
- Using CTask
-
- CTask comes archived with both source and binaries. The binary
- version is compiled in the large model, but since the precompiled
- kernel routines don't use any functions from the C library, you
- can use all functions in small or other model programs (except
- Turbo C's Tiny and Huge models). The include files provided
- specify all model dependencies, so you don't have to use the
- large model for your application, but always remember to include
- "tsk.h" for the type definitions and function prototypes.
-
- The C source files will work without changes for both Microsoft
- and Turbo C. The library files are not compatible, so use
- "ctaskms.lib" for Microsoft, and "ctasktc.lib" for Turbo C.
-
- In the distributed configuration (i.e. dynamic allocation of
- control blocks enabled), the file TSKALLOC.C must be added to the
- library or separately linked after compiling it *in the same
- model as the main program*. This file uses C-library routines,
- and thus must match the main program's memory model. The same
- goes for TSKSNAP.C, an optional snapshot-dump utility, and
- CONOUT.C, the sample console output handler. The provided
- "ctsupms.lib" and "ctsuptc.lib" files have been compiled in the
- large model, and may only be used with large model programs.
-
- Turbo C's huge model uses a different segment setup for data
- segments. This requires using a special data segment for all
- CTask data, and compilation of the CTask kernel in huge model.
- For the assembler files, the symbol TC_HUGE must be defined
- during assembly, the C files must be compiled with the Data-
- Segment and BSS-Segment naming options. Make-files for Turbo C
- Huge model are included.
-
-
- Configuration Options
-
- The file TSKCONF.H contains a number of #define's that allow you
- to configure some CTask features. In general, you should
- recompile all of CTask when changing one of the flags. You must
- also take care to modify the corresponding equate in the
- assembler-include TSK.MAC.
-
- The entries are
-
- TSK_DYNAMIC
- If 1, you can let CTask dynamically create task and event
- control blocks, task stacks, and pipe buffers, by passing
- NULL as the block address. Since this requires the C runtime
- allocation calls, it is not suitable for non-DOS
- applications (except if you provide your own memory
- allocation routines).
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 14
-
-
- This option is normally enabled (1).
-
- TSK_NAMEPAR
- If 1, all create_xxx calls accept an additional parameter,
- the name of the created control block. This name should con-
- tain only printeable characters, and should not be longer
- than 8 characters plus the zero terminator. This option is
- used together with TSK_NAMED (see below).
-
- This option is normally enabled (1).
- It must be enabled for DOS.
-
- TSK_NAMED
- If 1, all control blocks (except timer control blocks) are
- named and linked on creation. This allows the snapshot dump
- routine to display the system state. TSK_NAMEPAR must be
- defined for this option to work. Since it may be desirable
- to switch off the handling of the names after the debugging
- phase, without having to change all create_xxx calls to omit
- the name parameter, the name parameter is ignored if
- TSK_NAMED is undefined, but TSK_NAMEPAR is defined.
-
- This option is normally enabled (1). It must be enabled for
- DOS.
-
- CLOCK_MSEC
- If 1, all timeouts are specified in milliseconds instead of
- timer ticks. This allows programming of delays and timeouts
- independent of the speedup-parameter or the system tick
- rate. Since timeout calculations use floating point
- operations if this option is enabled, a floating point
- library is needed. This precludes model independence of the
- CTask kernel.
-
- This option is normally disabled (0).
-
- PRI_TIMER
- Specifies the priority of the timer task. Normally the timer
- task should have a higher priority than any other task in
- the system.
-
- The value is normally 0xf000.
-
- PRI_STD
- This value may be used in your programs as the standard
- priority of user tasks. Its value is arbitrary. It is not
- used in the kernel except for the definition of PRI_INT9
- (see below).
-
- The value is normally 100 (decimal).
-
- PRI_INT8
- Determines the priority of the "int8"-task. This task chains
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 15
-
-
- to the previous interrupt vector for the sytem timer tick,
- and thus may activate resident TSR's. Since TSR's normally
- use polling when accessing the keyboard and other devices,
- the priority of the int8-task should be equal to or lower
- than normal user-defined tasks to allow your program to con-
- tinue to run while the TSR is active. You can tune this
- value so that some tasks are blocked by the TSR to avoid
- trashing the screen.
-
- The value is normally PRI_STD.
-
- IBM
- DOS
- Both IBM and DOS are more or less of informative value only,
- to point out those areas in the kernel that need attention
- when converting to non-IBM or non-DOS environments.
- Disabling one or both of this options requires the
- substitution of your own routines for installation, timer,
- and keyboard handling.
-
- Both options must normally be enabled (1).
-
- GROUPS
- If enabled, task groups, i.e. multiple invocations of CTask,
- are supported.
-
- This option is normally enabled (1).
- It should be enabled for DOS.
-
- SINGLE_DATA
- If enabled, only a single global data block is used. This
- speeds up processing, but prohibits linkage of multiple
- groups. Useful for dedicated systems, and also if your
- program is known to never interact with another copy of
- CTask. Should be left 0 if you're not so sure, must be 0 if
- GROUPS is enabled.
-
- AT_BIOS
- If enabled, the AT BIOS wait/post handler is installed. May
- be disabled when compatibility problems arise.
-
- This option is normally enabled (1).
- You can also disable installation of this handler with an
- install flag in the install_tasker call.
-
-
-
- Memory Allocation
-
- TSKALLOC.C is needed if TSK_DYNAMIC is enabled in tskconf.h to
- handle the allocation and free calls. If you want to use dynamic
- allocation in your own tasks, you should also use the functions
- tsk_alloc and tsk_free to avoid the reentrancy problems mentioned
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 16
-
-
- in the introduction. If you should need other forms of alloc
- (like calloc) the recommended way is to add those functions to
- TSKALLOC.C, requesting the resource alloc_resource before calling
- the C memory-allocation function.
-
- Joe Urso provided the following tip on how to force all memory
- allocation to pass through the resource handling functions:
-
- "I have found that some MS Library functions call the allocate
- and free library functions, namely the first call to fread(),
- fopen(), and fclose().
-
- To fix this situation I have
-
- - extracted FMALLOC from the llibce library,
-
- - renamed the functions within that object (via the Norton
- Utilities) to the upper case spelling of the functions
- (FREE, MALLOC, _FFREE and _FMALLOC) and replaced the file in
- the lib. I also did the same for object EXPAND because of
- the function REALLOC.
-
- - then modified TSKALLOC.C to contain the lower case spelling
- of malloc, free, and expand. These functions aquire the
- alloc resource, then call the upper case spelling of the
- name.
-
- At link time I now link with /NOIGNORECASE and /NODEFAULTLIB."
-
-
- Snapshot
-
- TSKSNAP.C is only needed if you want to include the snapshot dump
- into your program. Note that you can *not* use snapshot if you
- disable TSK_NAMED in tskconf.h.
-
-
- Task Stacks
-
- When compiling your application, turn stack checking off. The
- standard stack check is of little use with task stacks, and may
- interfere with CTask's operation. The stack area for tasks should
- be allocated on the main program's stack, not in the static or
- heap data space. The reason for this is that some of the C
- library routines check for stack overflow regardless of your
- compile-time switches, and will crash your application if you use
- stacks outside the normal stack. The stack allocation parameter
- with LINK (MS-C), or the _stacksize variable (Turbo C) have to be
- increased to reflect the additional stack space. When calculating
- task stack sizes, keep in mind that library routines (esp.
- printf) allocate a lot of space on the stack for temporary
- variables. A minimum of 1k for tasks using library routines is
- recommended, 2k puts you on the safe side. Tasks not using C
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 17
-
-
- library routines may use a smaller stack, about 256 bytes at a
- minimum, plus space for any local variables and nested routines.
- Then add up all task stacks, and add space for the main task (the
- function calling install_tasker), with this size also dependent
- on what you will do in the main task while CTask is active.
- Stacks for tasks that do not use C library routines may be
- allocated anywhere.
-
-
- Drivers
-
- The keyboard and DOS handlers are always installed with CTask.
- Using the serial I/O and printer drivers is optional, so you have
- to install them separately, but only *after* installing CTask.
- When using the serial driver, include "sio.h" in your modules,
- when using the printer driver, include "prt.h".
-
- Another driver that is automatically installed is the BIOS
- wait/post handler for the IBM AT. The AT BIOS contains some
- multitasking hooks to avoid busy waiting in the BIOS. If you
- experience problems with disk accesses or printer output through
- BIOS (not through the CTask printer driver), you should disable
- installation of this driver by setting AT_BIOS to zero in
- tskconf.h, or not specifying the IFL_INT15 flag in
- install_tasker. Normally, no problems should arise with this
- driver even in XT type machines.
-
-
- Things to remember
-
- Remember that tasks are not automatically started after creation.
- Use start_task to allow a created task to run.
-
- Always use create_xxx on resources, pipes, etc. Using the event
- routines without doing so will have unpredictable results.
-
- Before exiting the program, all installed drivers and CTask
- should be explicitly removed. Although the DOS handler traps the
- terminate call and automatically calls remove_tasker, you should
- make sure that all tasks are completed properly, and call
- remove_tasker yourself.
-
- Deleting events before exiting the program is not mandatory, but
- recommended to kill all tasks waiting for the event. You should
- be careful not to kill tasks while they are active in DOS. The
- kill_task routine should be reserved for fatal error handling.
- The best way is to let the tasks kill themselves depending on
- some global variable or event. If a task is killed while waiting
- for input in DOS, DOS operation may be severely impaired. If you
- use the C console input routines, make sure that the task returns
- from DOS before it is killed, if necessary by requesting the user
- to press a key.
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 18
-
-
- Priority Handling
-
- CTask provides for prioritized task execution and event
- processing. This means that a task that has a higher priority
- will be run before any other tasks having lower priority. Also, a
- higher priority task will gain access to resources, counters,
- pipes, and mail, before lower priority tasks. With fixed
- priorities, this means that a high priority task can monopolize
- CPU time, even if it calls schedule() explicitly. Variable
- priority increases each eligible task's priority by one on each
- scheduler call, so that lower priority tasks will slowly rise to
- the head of the queue until they get executed. The priority will
- be reset to the initial priority when a task is run.
-
- Since variable priority increases processing time in the critical
- region of the scheduler, it is not recommended for systems in
- which a larger number of tasks is expected to be eligible
- simultaneously.
-
- Usually, all tasks in a system should have the same priority,
- with only very few exceptions for non-critical background
- processing (low priority) or very time-critical tasks (high
- priority). High priority tasks should be written in such a way
- that they either reduce their priority when processing is
- completed, or that they wait for an event. Busy waiting in a high
- priority task will severely impair system operation with variable
- priority enabled, and will stop the system until the task is
- placed in a waiting state with fixed priority.
-
- The (automatically created) main task is started with the highest
- possible priority below the timer task, so that it can process
- all initialisations before other tasks start running. To allow
- the system to operate, the priority of the main task must be
- reduced, or the main task must wait for an event or a timeout.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 19
-
-
- Multitasking and DOS
-
-
- CTask includes (and automatically installs) a routine which traps
- all DOS calls, and makes sure that no two tasks simultaneously
- enter DOS. This is accomplished using the resource mechanism,
- with special provisions for the limited multitasking capabilities
- provided by DOS. There are a few calls, namely those with
- function codes <= 0x0c, which allow functions with codes > 0x0c
- to be executed while DOS is waiting for an external device
- (generally the keyboard) to get ready.
-
- This, however, limits the use of some C library functions, namely
- scanf and fread, for console input. Both these functions use
- handle input, and thus can not be interrupted. When writing
- routines for handling user input, keyboard read functions should
- either use the low-level calls getch and gets, or, better yet,
- the direct entries into the keyboard handler, t_read_key and
- t_keyhit, and then process the string with sscanf if desired.
-
- The keyboard handler (contained in tskkbd.asm) traps all keyboard
- interrupts, setting a flag when a key is hit. If a task reads
- from the keyboard, it is automatically made waiting if there is
- no key to read. Using getch and gets is less desirable since they
- use polling instead of event waiting, and thus degrade system
- performance. Also, the t_read_key and t_keyhit functions do not
- use DOS, so DOS functions <= 0C can be executed concurrently.
-
- The BIOS interrupts 10 (Video), 13 (Disk), and 16 (Keyboard) are
- protected by CTask. Using other BIOS interrupts directly should
- generally be avoided, unless you are absolutely sure they will
- not be used by other routines via DOS, or you provide your own
- critical region handling. Protection of INT 10 is optional, and
- should only be used if you expect to use BIOS level video output
- in parallel to other tasks. Since INT 10 calls occur extremely
- frequent in most programs, the overhead for reserving and
- releasing the resource can slow down the system noticeably.
-
- Starting with version 1.2, the DOS handling has been extended to
- automatically save and restore the DOS variable context on a task
- switch. This is done by directly saving the critical variables of
- the DOS data area in the task control block. An undocumented DOS
- call is used to determine location and length of this area. NOTE
- that this call is not available in DOS versions prior to 3.1, and
- may not be available in MS-DOS clones or emulators. This context
- save allows multiple DOS applications to run concurrently.
-
- The DOS access module has been tested to work with PC-DOS and MS-
- DOS versions 3.20, 3.30, 4.00, and 4.01. There were also no
- problems with the DOS PRINT program running in the background.
- Special provisions are built into the DOS module to detect
- background DOS calls. Using Sidekick also hasn't lead to any
- problems, although you may trash Sidekick's screen display by
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 20
-
-
- writing to the screen while Sidekick is active (note that your
- application *continues* to run while Sidekick or other pop-ups
- are active). I can not guarantee complete compatibility with all
- background and pop-up programs under all versions of DOS.
- Specifically, DOS versions prior to 3.1 have significant problems
- with multitasking, and do not support the variable save call
- necessary for full DOS multitasking. Upgrading to a newer DOS
- version is recommended if you are still using DOS < 3.2 (DOS 3.1
- has some bugs in other areas).
-
- Also starting with version 1.2, CTask is now compatible with MS-
- Windows. It has been tested to work with Windows 286 1.2 and 2.1,
- but the mechanism used should work with other non-preemptive
- versions, too. CTask can be installed as a background TSR before
- starting Windows, or you may spawn Windows from CTask.
- Windows/386 is a special virtual-mode operating system and can
- generally not be combined with other multitaskers.
-
- Preemptive multitaskers like DesqView will most likely not
- coexist peacefully with CTask.
-
- Critical errors and Control C occurring while concurrent DOS
- access takes place may be fatal. Using the ctrlbrk() and
- harderr() functions of Turbo C, or their equivalents in MS C, to
- trap critical errors and Control C is highly recommended. Error
- handlers should be installed before CTask is installed.
-
-
- Spawning and CTask TSR's
-
- Starting with Version 1.2, CTask programs can now spawn other
- programs, and Terminate and Stay Resident (TSR). The DOS context
- switch, plus the saving and restoring of interrupts 21-24, on
- every task switch, makes it possible to have multiple, nearly
- completely independent, programs running under DOS. But please
- note that CTask is not intended as a replacement for true DOS-
- multitaskers like DesqView. Implementation of such a system would
- surely be possible, using CTask as a base, but would require
- keeping track of all DOS control blocks. This is left as an
- exercise to the reader. The new features are intended to allow
- you to easily write background data aquisition or communication
- modules that run in parallel to other DOS applications.
-
- Since the spawned program might itself be a CTask program,
- Version 1.2 automatically checks for the presence of a background
- copy of CTask. If one is found, the secondary invocation will
- link to the global data of the first CTask copy, making it
- possible to easily communicate between independent instances of
- CTask programs. The naming mechanism has been slightly changed to
- make it easier to find a task or data structure by name.
-
- For example, you could write a communications program in two
- separate parts. One part, the resident part, contains interrupt
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 21
-
-
- handlers and the protocol routines. The other part, the transient
- portion, contains the driving program, with editor, menus, script
- language and so on. If the resident part is started, it first
- spawns the transient part, which handles setup, and starts
- communications. Both parts can communicate using all CTask
- mechanisms, i.e. pipes, buffers, mailboxes etc., with the
- transient part obtaining the pointers to the CTask structures
- defined in the resident part via the structure name. Now if an
- up- or download is started, the transient part can exit to DOS,
- and the resident part can spawn a copy of command.com. Voila -
- background up/download with minimum hassle.
-
-
- Task Groups
-
- Each invocation of the CTask kernel (i.e. each call of the
- install_tasker routine) creates a new "Task Group". This group
- chains all tasks and other control structures created by this
- program together, so they can be killed if the group terminates
- (through a DOS call or the remove_tasker routine). Although it is
- recommended to kill all structures before terminating, there are
- a number of situations where this is hard to control under DOS.
- So CTask tries to take a few safety measures to make sure the
- system does not crash due to pointers chaining into nowhere.
-
- But please note that care should be taken not to "pull the rug
- under a group's feet". CTask will attempt to clean up memory when
- a group terminates. However, if that group has spawned another
- non-CTask program, CTask doesn't know about it, so an orphan will
- be left in memory, and you might even manage to get the system
- into an unstable state.
-
- And, there are a few restrictions. Although it is possible to
- create multiple groups running, and terminating, independently in
- parallel, those groups must be created by separate tasks from one
- "home group". If one task creates a group, and the same task, or
- another task from this group, creates a second, then it is not
- possible to terminate the first group without also killing the
- second. You can view this like a tree, where you can cut off
- branches, but all sub-branches of that branch will come down with
- it. The root of that tree, the primary invocation of CTask, can
- not be killed without killing the whole tree.
-
- Also, it is mandatory that you at least try to call remove_tasker
- on termination. CTask can trap a program's termination, but this
- happens at a time when all the program's memory has already been
- released by DOS. This can lead to extremely dangerous situations
- if other tasks, or TSR's, allocate and modify this memory before
- CTask finishes it's cleanup. CTask will stop preemption while it
- is removing the group, but it still is definitely not recommended
- to rely on this mechanism.
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 22
-
-
- A sample for a task group structure follows:
-
- -Root-
- home
- level
- branch -----+
- ^ ^ |
- | | |
- | | v
- -Group1- | | -Group2-
- home ------+ +----- home
- level <------------- level
- branch -----+ branch
- ^ ^ |
- | | |
- | | v
- -GroupA- | | -GroupB-
- home -----+ +----- home
- level <------------ level
- branch branch
-
-
- In this sample, Groups 1 and 2 were created by different tasks in
- the Root group, Groups A and B by different tasks in Group 1.
-
- Three pointers are used in chaining groups. The 'branch' points
- to the first element in an unordered list of task groups on the
- same "branch level" one level "up" the tree. Those groups are
- chained by the 'level' pointer. To allow finding the home group
- when going "down" the tree (towards the root), all groups on the
- same level point there through the 'home' pointer. In this
- sample, you could kill Group2 without affecting Group1 or it's
- subgroups. Killing Group1 will terminate GroupA and GroupB.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 23
-
-
- How does CTask work
-
- Queues
-
- CTask uses priority queues for most of its functions. A priority
- queue differs from the usual FIFO (first in, first out) queues in
- the insertion of elements into the queue. Rather than just
- placing new elements after the last queue member, the priority of
- the task determines its place in the queue. A task is enqueued
- after all queue members with higher or equal priority, but before
- any member with lower priority. Removal from the queue takes
- place at the first element. These queues are used with all
- operations in which a task is made waiting, be it waiting to get
- run, or waiting for an event. The advantage of this is the
- simplicity of implementing priorities. Other schemes might rely
- on arrays of queues, or searching through lists, to determine the
- highest priority waiting task. With priority queues, the most
- time critical function, scheduling, is very fast, since it can
- simply take the first task from the queue of eligible functions.
- The disadvantage is the time required to step through all greater
- or equal priority members of a queue to find the right place to
- insert the task. But since the number of tasks simultaneously
- enqueued in the same queue is normally relatively small with most
- multitasking systems, this disadvantage is more than offset by
- the simple, and thus easy to implement efficiently, scheme.
-
- Version 2.0 generalizes the queueing scheme, and uses doubly
- linked lists for all internal chains (except the ticker chain).
- The introduction of the "yield" function, which inserts a task at
- the end of the eligible queue, but resets the priority, mandated
- searching through the queue from the back end. The previous
- scheme, which walked the queue from the front end, could lead to
- task starvation in a system with polling tasks when yielding.
- Also, the timer algorithm was changed such that the timer task
- didn't have to step through all timeout elements, but rather to
- only decrement the first queue element ticker. This is
- implemented by storing the timeout as a tick difference to the
- next queue element rather than as an absolute value. Doubly
- linked lists make insertion and removal of elements at arbitrary
- points very simple, and eliminate the time needed to find an
- element in a queue.
-
- To alleviate the additional overhead associated with managing
- dual pointers, the main queueing routines were coded in
- assembler. Especially when operating with multiple far pointers,
- the code generated by the C compilers even with maximum
- optimization is not that impressive. The corresponding C code is
- included as a reference, and to aid porting.
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 24
-
-
- The Scheduler
-
- In a CTask system, there is at least one essential queue: the
- eligible queue, which contains all tasks waiting to run. Each
- time the scheduler is invoked, it will first save all processor
- registers in the current task control block, and then enqueue the
- current task into the appropriate queue. Normally, this will be
- the eligible queue. Then the first element in the eligible queue
- is removed from the queue, the stack is switched to the stack of
- this task, and processor registers are restored from the new TCB,
- returning to the point where the new task was interrupted. If the
- eligible queue is empty the scheduler will loop with interrupts
- enabled. The queue head pointer of the task control block of the
- current running task determines what will happen to the task when
- the scheduler is invoked. If it points to an event control block,
- the task will be enqueued as waiting for this event. If it is
- NULL, the task will not be enqueued in any queue, effectively
- stopping it. If it points to the eligible queue, the task will be
- enqueued there. In any case, the scheduler does not care what
- queue the task is to be enqueued in, since all queues use the
- same priority based ordering. If a task is no longer in the
- eligible queue, it can only be returned to this queue by an
- external event.
-
-
- Events
-
- External events can take several forms, which are relatively
- similar on the inside. All CTask events use a control block, with
- at least one queue for tasks waiting for the event. Although it
- would have been possible to summarize all events under a global
- structure, this approach was not used in CTask, the main reasons
- being execution speed and ease of use. So if you scan through the
- source files for CTask events, you will see many very similar
- routines, which only differ in the types of data and the names of
- queues they process. In a class based language like C++, one
- would certainly define all events as instances or derivations of
- one class. In plain C, the necessary type casting, and the
- different handling of certain cases, would clobber the code to
- the point of illegibility.
-
-
- Resources
-
- The event types "resource", "flag", and "counter" differ only in
- the kind of wait operations and the handling of the state
- variable. The resource is mainly for use in interlocking critical
- regions of code, to protect access to non-reentrant resources.
- Only one task can "own" a resource, all other tasks requesting
- the resource are delayed until the owning task releases it. For
- added protection, CTask stores the task control block address of
- the current owner of the resource in the resource control block,
- so no other task can erroneously release it.
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 25
-
-
- Flags
-
- Flags, on the other hand, can be set and cleared by any task, and
- also by interrupt handlers. Tasks can wait on either state of the
- flag, and all tasks waiting for a state are simultaneously
- activated when the flag is changed to this state. This makes
- flags suitable for use as a global signalling mechanism.
-
-
- Counters
-
- Counters are a variation on the flag concept. A counter can be
- incremented and cleared by any task, and by interrupt handlers.
- Tasks can wait for a counter to be zero or nonzero. Like flags,
- all tasks waiting for the zero state are activated
- simultaneously. But unlike flags, only the first task waiting for
- the nonzero state of a counter will be activated on incrementing
- the counter, and the counter will be automatically decremented by
- one. The counter is used inside CTask to handle timer interrupts.
- The timer counter will be incremented on each timer tick,
- activating the timer task. If for any reason the timer task is
- unable to complete its run until the next tick, this tick will
- not be lost, since the counter is incremented, and the timer task
- will continue to run the next time it calls the counter wait
- request.
-
-
- Mailboxes and Pipes
-
- The "mailbox" and "pipe" events can be used for inter-task
- communication, and for the communication between interrupt
- handlers and tasks.
-
- Mailboxes can hold an unlimited number of mail blocks. Mail
- blocks have no fixed structure, but the first doubleword in each
- block passed to a mailbox routine is used as a chain pointer.
- Mail blocks are chained into a mailbox in FIFO order. Tasks can
- wait for mail to arrive, or can conditionally read mail if a
- block is available. Tasks and interrupt handlers can write blocks
- to a mailbox. Since mailboxes don't need any copying of data,
- they are suited for high speed exchange of larger amounts of data
- between tasks. The disadvantage of sending mail this way is that
- you have to make sure that a mail block is not re-used before it
- has been read out of the box and processed by the receiving task.
- When exchanging fixed length messages, you can build a free mail
- block chain by using a mailbox to hold the available blocks.
-
- Buffered message exchange is possible using pipes. When creating
- a pipe, you specify a buffer area and its length, and the pipe
- routines will buffer all data written to the pipe in this area.
- This implies that writing to a pipe may cause a task to be
- delayed until there is space available in the pipe. To allow
- interrupt handlers to write to pipes, there is a conditional
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 26
-
-
- write request, which will simply return if the pipe is full.
- Tasks can wait for data to arrive in a pipe, and for the pipe to
- be emptied. A conditional read request is also provided. The
- disadvantage of pipes is that they are slightly slower than
- mailboxes due to the necessary copying, and that you can only
- place word or byte sized items in a pipe. When there is more than
- one reader or writer task, you can not rely on the bytes in a
- pipe being in any specific order. A "buffer" construct is
- provided in CTask that expands pipes to allow arbitrary length
- messages to be written and read. This is implemented using a
- resource for reading and writing to the pipe associated with the
- buffer, so the message is always guaranteed to be written and
- read in one piece. But since resources can not be used in
- interrupt handlers, using such buffers is not allowed from
- interrupts.
-
-
- Serial and Printer Drivers
-
- There are a number of routines included in the CTask package for
- PC-specific tasks. Although they will be of limited use for
- embed-ded applications, studying the serial I/O and printer
- interface routines will give you some hints on how to use the
- CTask kernel for implementing your own device drivers. For PC
- based appli-cations, the routines should be usable with no or
- little changes for implementing complete communications packages.
-
- The serial I/O handler uses interrupts for both input and output
- of data, and supports both XON/XOFF and RTS/CTS handshake
- methods. The modem inputs can selectively be enabled to control
- data transmission. Pipes are used for transmit and receive data,
- with the buffer and its size specified on initialization. Both
- COM1 and COM2 can be active simultaneously, and other ports can
- be supported by editing a table in the source code or defining
- ports on-line. Since CTask allows the access to all events in
- interrupt handlers, writing interrupt based I/O drivers is
- relatively simple. On receiving a character, the character and
- any associated errors are placed in the receive pipe, and the
- transmit pipe is read on transmit ready interrupt. Note that
- conditional reads and writes have to be used in the interrupt
- handler, since you can't safely delay an interrupt handler.
-
- The printer output driver supports both polling and interrupt
- output. Again, a pipe is used for the output characters.
- However, since polling is supported, and because of the somewhat
- unreliable interrupt structure of the printer ports, a driver
- task is required. This task is automatically created on
- installing the driver. The task first waits on the pipe for a
- character to be written to the printer. When polling is enabled,
- it tries to output the character directly, using a short busy-
- waiting loop. So as not to load the system too heavily by the
- polling, the task will yield if it can't output the character
- after a small number of loops. When interrupts are enabled, the
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 27
-
-
- process is essentially the same at the start, but after the
- character is written to the port, the task will wait on a flag to
- be set. The interrupt handler will set the flag on interrupt,
- enabling the task to get the next character from the pipe. Since
- interrupts are unreliable with most printers due to the usually
- very short pulses on the acknowledge line, the task uses a
- timeout on the flag wait, so it does not hang if an interrupt is
- missed. Studying the printer driver will also give you an idea
- what the optional parameter on task creation can be used for. In
- the printer driver, this parameter is used to pass the address of
- the printer control block, which contains the pipe, the flag, and
- the hardware info, to the task. This allows the printer driver to
- be simultaneously installed for any number of printers without
- having to write separate printer tasks.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 28
-
-
- CTask Data Types
-
-
- Note that you do not have to know the innards of the structures.
- All structure fields are filled by the "create_xxx" routines and
- modified by the CTask functions. You should NEVER modify a field
- in one of the structures directly. The structures are explained
- here shortly only for those wanting to modify the routines.
-
- NOTE: When modifying CTask structures, take care to modify the
- equivalent definitions in the assembler include file "tsk.mac".
- Some of the assembler routines have to use field offsets into
- pointers, so having different offsets in C and assembler will
- crash the system.
-
-
- If you only want to use the routines, you should simply include
- the file "tsk.h" in your source, and define variables of the
- types
-
- tcb - for task control blocks
- tcbptr - for far pointers to tcbs
-
- flag - for flag events
- flagptr - for far pointers to flags
-
- resource - for resource events
- resourceptr - for far pointers to resources
-
- counter - for counter events
- counterptr - for far pointers to counters
-
- mailbox - for mailbox events
- mailboxptr - for far pointers to mailboxes
-
- pipe - for pipe events
- pipeptr - for far pointers to pipes
-
- wpipe - for word pipe events
- wpipeptr - for far pointers to word pipes
-
- buffer - for buffer events
- bufferptr - for far pointers to buffers
-
- tlink - for timeout and watch control blocks
- tlinkptr - for far pointers to timeout control blocks
-
- namerec - for control block names
- nameptr - for name pointers
-
- without caring what's behind them.
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 29
-
-
- Additionally, you may use the types
-
- byte - for unsigned characters
- word - for unsigned short integers
- dword - for unsigned long integers
-
- funcptr - for pointers to task functions
- farptr - for far pointers to anything
- byteptr - for far pointers to byte arrays
- wordptr - for far pointers to word arrays
-
- in defining or typecasting items to be passed as parameters to
- CTask functions.
-
-
- Typedefs used for simplified type specifications
-
- typedef unsigned char byte;
- typedef unsigned short word;
- typedef unsigned long dword;
- typedef void (cdecl far *funcptr)();
- typedef void far *farptr;
- typedef byte far *byteptr;
- typedef word far *wordptr;
-
-
-
- Error return values for event wait functions
-
-
- #define TTIMEOUT ((farptr) -1L)
- #define TIMEOUT (-1)
-
- -1 is returned if a timeout occurred while waiting for
- an event.
-
- #define TWAKE ((farptr) -2L)
- #define WAKE (-2)
-
- -2 is returned if the task was waked up while waiting
- for an event.
-
- #define TWATCH ((farptr) -3L)
- #define WATCH (-3)
-
- -3 is returned if the task was waked up during a wait
- by a watchpoint action.
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 30
-
-
- Queues
-
- The 'queue' structure is a dual link for linking task control
- blocks and timer blocks. The first three fields are used both for
- the queue head, and for elements to be inserted in a queue.
-
- CAUTION: Do not change the order of the first three fields in
- either queue or queue_head! Those two structures are used
- interchangeably in various places in the CTask kernel. Changing
- one of them would have catastrophic results.
-
- The queue element ("queue") contains the forward and backward
- pointer, a structure kind field, plus the priority (when used in
- a TCB) or the timeout value (when used in a tlink structure). The
- kind field is used to identify the type of structure the queue
- element is a part of, and also to identify the "end" of the
- queue. To simplify insertion and removal, all queues are circular
- in nature, i.e. there is no NULL-pointer at the head or tail. The
- queue head, and thus the end of a queue, is identified by a zero
- kind field.
-
- typedef struct {
- word prior;
- word ini_prior;
- } qelem_pri;
-
- typedef struct queue_rec far *queptr;
-
- typedef struct queue_rec {
- queptr volatile next;
- queptr volatile prev;
- byte kind;
- union {
- qelem_pri pri;
- dword ticks;
- } el;
- } queue;
-
- The queue head ("queue_head") also contains the forward and
- backward pointer, and the structure kind field. The kind field is
- 0 in true queue heads. The same structure is also used to chain
- name blocks, since those do not need the priority/tick field
- present in normal queue elements.
-
- typedef struct {
- queptr volatile first;
- queptr volatile last;
- byte kind;
- } queue_head;
-
- typedef queue_head far *queheadptr;
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 31
-
-
- The timer/watch control block
-
- The timer control block is included in every task control block.
- It may also be created as a separate entity to specify special
- actions to be executed after or every n timer ticks. It may also
- be used to specify memory or port locations to be watched for a
- change or a certain value on every timer tick.
-
- The "link" field links the active structures into the timer
- queue.
-
- "strucp" points to the structure to be acted upon, "kind"
- specifies the kind of structure:
-
- #define TKIND_TASK 1
-
- Strucp points to the task control block of which the
- element is a member. The task will be awakened when the
- timeout expires.
-
- #define TKIND_WAKE 2
-
- Strucp points to a task control block. Otherwise same
- as TKIND_TASK.
-
- #define TKIND_PROC 3
-
- Strucp contains the address of a function to be called
- on timeout.
-
- #define TKIND_FLAG 4
-
- Strucp points to a flag that should be set on timeout.
-
- #define TKIND_COUNTER 5
-
- Strucp points to a counter that should be increased on
- timeout.
-
- The timeout structure is used for both timeouts and memory/port
- watchpoints. The union "elem" contains either the tick reload value,
- or the specs for the memory or port location to watch. "elkind"
- specifies the kind of structure used in the upper nibble:
-
- #define TELEM_TIMER 0x10
-
- This is a timeout element.
-
- #define TELEM_MEM 0x20
-
- This is a memory watch element.
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 32
-
-
- #define TELEM_PORT 0x30
-
- This is a port watch element.
-
- The lower nibble of "elkind" is used for watchpoints, it
- specifies the comparison operation:
-
- #define TCMP_EQ 1
-
- Activate watch if value read is equal to "compare".
-
- #define TCMP_NE 2
-
- Activate watch if value read is not equal to "compare".
-
- #define TCMP_GE 3
-
- Activate watch if value read is >= "compare"
- (unsigned).
-
- #define TCMP_LE 4
-
- Activate watch if value read is <= "compare"
- (unsigned).
-
- #define TCMP_GES 5
-
- Activate watch if value read is >= "compare" (signed).
-
- #define TCMP_LES 6
-
- Activate watch if value read is <= "compare" (signed).
-
- #define TCMP_CHG 7
-
- Activate watch if value read is not equal to "compare".
- Additionally, store the value read into the "compare"
- field. Only useful for continuous watchpoints.
-
-
- For timeouts, "elem.time" contains the "reload" value used for
- repetitive timeout actions to reload the original timeout value.
- The actual timeout value is part of the link structure.
-
- For port watches, "elem.port" contains the values "port", "mask",
- "compare" and "in_word". "port" holds the port number to read
- from. The value read is masked with "mask", and compared with
- "compare". A word access is used if "in_word" is nonzero.
-
- Memory watches are similar to port watches, with "elem.mem"
- containing "address", the memory address to watch, and "mask" and
- "compare" as above.
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 33
-
-
- "tstate" contains the state of the element:
-
- #define TSTAT_REMOVE 0xff
-
- The element should be deleted.
-
- #define TSTAT_IDLE 0
-
- The element is not enqueued.
-
- #define TSTAT_WATCH 1
-
- The element is a watchpoint (port or memory).
- It will be removed when the watch is activated.
-
- #define TSTAT_CONTWATCH 2
-
- The element is a watchpoint (port or memory).
- It will not be removed on activation.
-
- #define TSTAT_COUNTDOWN 3
-
- The timeout counter is counted down. If the timeout is
- reached, the element is removed from the queue.
-
- #define TSTAT_REPEAT 4
-
- The timeout counter is counted down. If the timeout is
- reached, the timeout count is reloaded.
-
- The "flags" field holds two bits:
-
- #define TFLAG_BUSY 0x01
-
- If this bit is set, the timer task is busy processing
- this element. It may not be deleted or enqueued.
-
- #define TFLAG_TEMP 0x80
-
- If this bit is set, this is a temporary element, which
- will be deleted if the timeout is reached, or the watch
- condition is met.
-
- The "user_parm" field may be used to pass pointers or other
- values to timeout functions. It is not modified by the CTask
- kernel routines.
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 34
-
-
- typedef struct tlink_rec far *tlinkptr;
-
- struct telem_memwatch {
- wordptr address;
- word mask;
- word compare;
- };
-
- struct telem_portwatch {
- word port;
- word mask;
- word compare;
- byte in_word;
- };
-
- struct telem_timeout {
- dword reload;
- };
-
- struct tlink_rec {
- queue link;
- farptr strucp;
- dword user_parm;
- union {
- struct telem_memwatch mem;
- struct telem_portwatch port;
- struct telem_timeout time;
- } elem;
- byte elkind;
- byte tstate;
- byte flags;
- };
-
- typedef struct tlink_rec tlink;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 35
-
-
- The name link structure
-
- If TSK_NAMED is enabled, all structures except the timer control
- block contain a name link. All control blocks are linked and
- named via this element.
-
- "strucp" points to the head of the structure the name link is an
- element of, with "kind" in the list field specifying the type of
- structure:
-
- #define TYP_TCB 1 task control block
- #define TYP_FLAG 2 flag event
- #define TYP_RESOURCE 3 resource event
- #define TYP_COUNTER 4 counter event
- #define TYP_MAILBOX 5 mailbox event
- #define TYP_PIPE 6 byte pipe
- #define TYP_WPIPE 7 word pipe
- #define TYP_BUFFER 8 buffer
-
- The head element of the name list (normally the group control
- block) has its kind field set to zero.
-
- The "name" field contains an up to 8-character name plus a zero
- terminator.
-
- #define NAMELENGTH 9
-
- typedef struct name_rec far *nameptr;
-
- struct name_rec {
- queue_head list;
- farptr strucp;
- char name [NAMELENGTH];
- };
-
- typedef struct name_rec namerec;
-
-
- The task control block structure
-
-
- The "cqueue" field links the TCB into one of the queues. The
- "qhead" pointer points to the head of the queue the task is
- enqueued in, or will be enqueued in on the next schedule request
- in the case of the current running task.
-
- The "prior" field in the cqueue structure contains the tasks
- current priority, with 0xffff the highest possible priority, and
- 0 the lowest. The "ini_prior" field initially contains the same
- value. If variable priority is enabled, "prior" is incremented on
- each scheduler call, and reset to "ini_prior" when the task is
- activated.
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 36
-
-
- "stack" contains the saved task stack pointer (offset and
- segment) if the task is not running. The field "stkbot" contains
- the bottom address of the task stack. It is set by create_task,
- but is currently not used anywhere else. Stack checking routines
- might use this value to test for stack overflow and/or stack
- usage. The "t_ax", "t_cx", ..., "t_ds" fields hold the register
- contents while a task is inactive.
-
- "state" and "flags" contain the tasks state and flags.
-
- "timerq" is a tlink structure used to chain the tcb into the
- timer queue if the task is waiting for a timeout, or into the
- watch queue if the task is waiting for a watch event. See above
- for a description of the tlink structure.
-
- The fields "retptr" and "retsize" are used in event handling.
- They are used when a task is waiting for an event by the task
- activating the event, and also by timeout and wake to indicate
- error returns. The use of these pointers eliminates the need to
- loop for an event, which requires slightly more code in the event
- handling routines, but reduces the need for task switching.
-
- The "save_func" and "rest_func" pointers can optionally point to
- task-switch save and restore functions. In special applications,
- it may be necessary to save and restore global variables or other
- items when a task is deactivated. The save function is called
- when a task is deactivated, the restore function when a task is
- activated. Both functions should be defined as
-
- void far funcname (tcbptr tcb);
-
- The functions are called with a pointer to the current TCB, DS
- and ES are set up to point to the task's current data segment.
- Please note that the data segment may be different from the
- default data segment if the task switch occurred while the task
- executed a DOS or BIOS function. Interrupts are disabled, and
- should not be re-enabled. The stack is the scheduler's local
- stack. Calling CTask-routines that might cause the scheduler to
- be activated should be avoided.
-
- The "user_ptr" field can hold any pointer or other value to be
- used by the save/restore functions or to point to task-related
- items. This field is not modified by the CTask routines.
-
- The "group" and "homegroup" pointers link a task with it's
- current task group, and with the base group that created the task
- if the task spawned a secondary invocation of CTask.
-
- The DOS-related fields "indos", "new", "base_psp", "psp_sssp",
- and "swap_area" are used to save critical information on the
- state of DOS. They should never be tampered with.
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 37
-
-
- The "name" namerec structure is present only if TSK_NAMED is
- enabled. It may be used in debugging (see tsksnap.c for an
- example), and in applications where the address of the structure
- can not be passed directly.
-
-
- typedef struct tcb_rec far *tcbptr;
-
- struct tcb_rec {
- tcbptr next;
- tqueptr queue;
- byteptr stack;
- byteptr stkbot;
- word prior;
- word initprior;
- byte state;
- byte flags;
- dlink timerq;
- farptr retptr;
- int retsize;
- };
-
- typedef struct tcb_rec tcb;
-
-
-
- Task states
-
-
- #define ST_KILLED 0
-
- The task has been killed. Restarting the task is not
- possible. Queue pointers are invalid.
-
- #define ST_STOPPED 1
-
- The task is not enqueued in any queue. To be in-cluded
- in scheduling, it has to be explicitly started. The
- queue head pointer is NULL.
-
- #define ST_DELAYED 2
-
- The task is enqueued in the timer queue only. When the
- timer expires, it is placed in the eligible queue. The
- queue head pointer is NULL.
-
- #define ST_WAITING 3
-
- The task is waiting for an event to happen. It can
- also be chained into the timer queue if a timeout was
- specified in the call. The queue head pointer points
- to the queue head in the event control block for the
- event the process is waiting on.
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 38
-
-
- #define ST_ELIGIBLE 4
-
- The task is enqueued in the queue of processes eligible
- for running. It can not be chained in the timer queue.
- The queue head pointer points to the eligible queue.
-
- #define ST_RUNNING 5
-
- The task is the current running process. Although it is
- not enqueued in any queue, the queue head pointer in
- its control block is valid and points to the queue the
- process will be enqueued in on the next schedule
- request.
-
-
- Possible state transitions and their reasons:
-
- stopped -> eligible by start_task ()
-
- delayed -> killed by kill_task ()
- -> eligible by timer task, or wake_task ()
-
- eligible -> killed by kill_task ()
- -> running by scheduler
-
- running -> killed by kill_task ()
- -> stopped by delay (0)
- -> delayed by delay (n != 0)
- -> eligible by scheduler
- -> waiting by wait_xxx ()
-
- waiting -> killed by kill_task ()
- -> eligible by event happening, timeout,
- or wake_task()
-
-
-
- Task flags
-
- System flags:
-
- #define F_TEMP 0x80
-
- This tcb was allocated automatically, and must be
- free'd on task kill.
-
- #define F_STTEMP 0x40
-
- The tasks stack was allocated automatically, and must
- be free'd on task kill.
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 39
-
-
- User changeable flags:
-
- #define F_CRIT 0x01
-
- This task may not be preempted. It will run until it
- clears this flag, delays itself, calls the scheduler
- explicitly, or waits for an event.
-
-
-
- The Group Control Block
-
- Each invocation of CTask owns a group control block, gcb. This
- structure holds all information relevant to this invocation only,
- and chains all data structures created in this group.
-
- The "home", "level", and "branch" fields interconnect the groups
- (see the chapter on groups for more information).
-
- The "creator" field identifies the creating task.
-
- The "exit_addr", "create_psp", "save_psp", and "save_sssp" fields
- hold the information necessary to allow spawning CTask
- applications.
-
- The "namelist" field contains the name of the group itself, and
- links all structures created within this group.
-
-
- struct group_rec {
- gcbptr home;
- gcbptr level;
- gcbptr branch;
- tcbptr creator;
- dword exit_addr;
- word create_psp;
- word save_psp;
- dword save_sssp;
- namerec namelist;
- };
-
-
-
- The event control blocks
-
- All event control blocks have two optional fields:
-
- "name" is only present if TSK_NAMED is enabled. It is a namerec
- structure as explained above.
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 40
-
-
- "flags" is only present if TSK_DYNAMIC is enabled. It contains
-
- F_TEMP If this control block was allocated
- automatically, and must be free'd on delete.
-
- F_STTEMP If the buffer for this event was allocated auto-
- matically, and must be free'd on delete (pipes
- and buffers only).
-
-
-
- The Ticker structure
-
- The ticker is not an event structure in the true sense, since
- there are no operations that wait on a ticker. It is the only
- control structure linked by a single forward pointer. It contains
- this "next" pointer, a doubleword "ticks" count that is counted
- down towards zero on every hardware clock tick, and the optional
- "flags" field. It is permissible to access and modify the "ticks"
- field directly.
-
- typedef struct ticker_rec far *tick_ptr;
-
- typedef struct ticker_rec {
- tick_ptr next;
- dword ticks;
- byte flags; (if TSK_DYNAMIC)
- } ticker;
-
-
-
- The Flag event structure
-
- Contains two queues for processes waiting on a flag state (clear
- or set), plus the flag state (0 = clear, 1 = set).
-
- typedef struct {
- queue_head wait_set;
- queue_head wait_clear;
- int state;
- byte flags; (if TSK_DYNAMIC)
- namerec name; (if TSK_NAMED)
- } flag;
-
- typedef flag far *flagptr;
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 41
-
-
- The counter event structure
-
- Similar to a flag, but contains a doubleword state counter.
-
- typedef struct {
- queue_head wait_set;
- queue_head wait_clear;
- dword state;
- byte flags; (if TSK_DYNAMIC)
- namerec name; (if TSK_NAMED)
- } counter;
-
- typedef counter far *counterptr;
-
-
- The resource event structure
-
- Contains a queue for the tasks waiting for access to the
- resource, a pointer to the current owner of the resource (to
- check for illegal "release_resource" and nested request calls),
- and the resource request counter (0 = free, <> 0 = in use).
-
- typedef struct {
- queue_head waiting;
- queue_head owner;
- word count;
- byte flags; (if TSK_DYNAMIC)
- namerec name; (if TSK_NAMED)
- } resource;
-
- typedef resource far *resourceptr;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 42
-
-
- The mailbox event structure
-
- The msgptr type is only used internally to chain mail blocks into
- the mailbox. The mailbox type contains a queue of the tasks
- waiting for mail, and a first and last pointer for the chain of
- mail blocks.
-
- struct msg_header {
- struct msg_header far *next;
- };
-
- typedef struct msg_header far *msgptr;
- typedef mailbox far *mailboxptr;
-
- typedef struct {
- queue_head waiting;
- msgptr mail_first;
- msgptr mail_last;
- byte flags; (if TSK_DYNAMIC)
- namerec name; (if TSK_NAMED)
- } mailbox;
-
-
-
- The pipe and word pipe event structure
-
- Contains queues of the tasks waiting to read or write to the
- pipe, indices for reading (outptr) and writing (inptr) into the
- buffer, the buffer size, the number of bytes or words currently
- in the pipe ("filled"), and the pointer to the buffer. A word
- pipe is handled exactly the same as a byte pipe, the only
- difference being the element size placed in the buffer. With
- normal pipes, characters are buffered, with word pipes, words.
-
- typedef struct {
- queue_head wait_read;
- queue_head wait_write;
- queue_head wait_clear;
- word bufsize;
- word filled;
- word inptr;
- word outptr;
- byteptr contents;
- byte flags; (if TSK_DYNAMIC)
- namerec name; (if TSK_NAMED)
- } pipe;
-
- typedef pipe far *pipeptr;
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 43
-
-
- typedef struct {
- queue_head wait_read;
- queue_head wait_write;
- queue_head wait_clear;
- word bufsize;
- word filled;
- word inptr;
- word outptr;
- wordptr wcontents;
- byte flags; (if TSK_DYNAMIC)
- namerec name; (if TSK_NAMED)
- } wpipe;
-
- typedef wpipe far *wpipeptr;
-
-
- The buffer event structure
-
- Contains resources for read and write access, the word pipe used
- for buffering, and a message counter.
-
- typedef struct {
- resource buf_write;
- resource buf_read;
- wpipe pip;
- word msgcnt;
- byte flags; (if TSK_DYNAMIC)
- namerec name; (if TSK_NAMED)
- } buffer;
-
- typedef buffer far *bufferptr;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 44
-
-
- CTask Routines
-
-
- Installation and Removal
-
- int install_tasker (int varpri, int speedup,
- word flags, byteptr name);
-
-
- Installs the multitasker. Must be called prior to any other
- routine. The calling routine is defined as the main task,
- and assigned the highest priority. To allow other tasks to
- execute, the main task must have its priority reduced, be
- delayed, or wait on an event.
-
- Returns 1 if CTask was already present in the system, i.e.
- this is a secondary invocation. Returns 0 if this is the
- first installation of CTask.
-
- "varpri" Enables variable priority if nonzero.
-
- "speedup" is defined for the IBM PC/XT/AT as the clock tick
- speedup factor. The timer tick frequency will be
- set to
-
- speedup ticks/sec msecs/tick
- 0 18.2 54.9 (normal clock)
- 1 36.4 27.5
- 2 72.8 13.7
- 3 145.6 6.9
- 4 291.3 3.4
-
- Note that all timeouts are specified in tick
- units, so changing the speedup parameter will
- influence timeouts and delays. You can enable
- CLOCK_MSEC in tskconf.h to allow timeouts to be
- specified in milliseconds. The system clock will
- not be disturbed by changing the speed. Using
- values above 2 can lead to interrupt overruns on
- slower machines and is not recommended.
-
- "flags" controls various installation flags through the
- bitwise OR of the following flags defined in
- TSK.H:
-
- IFL_VIDEO Install INT 10 access resource if set. No
- protection of Video interrupt if clear.
- Installation of the Video resource is safer,
- but may slow down the system noticeably. It is
- not necessary if you don't do screen-I/O
- through the BIOS from your CTask program.
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 45
-
-
- IFL_DISK Install INT 13 access resource if set. No
- protection of Disk interrupt if clear.
- Installation of INT 13 is recommended for
- better system safety. The resource will allow
- parallel access to floppy and harddisk, but
- might be incompatible with some special
- programs (though none have yet been reported).
-
- IFL_INT8_DIR Call original INT 8 directly if set.
- This changes the timing for the timer inter-
- rupt. If this flag is clear, the original
- timer interrupt is chained to through a task,
- so it can be preempted. If it is set, the
- original interrupt is chained to directly, at
- the start of timer interrupt processing. This
- avoids compatibility problems with certain
- TSR's and Networks, but may impair CTask
- operations while those TSR's are active.
-
- IFL_PRINTER Install INT 17 handler if set.
- Installation of the printer interrupt handler
- avoids lengthy busy waiting on printer output,
- but might be incompatible with certain printer
- redirectors.
-
- IFL_INT15 Install IBM-AT INT 15 handler if set.
- The IBM INT 15 handler allows avoiding some
- busy waiting loops. It may, however, be
- incompatible with some non-100% clones, and
- with debuggers like Periscope IV. If you're
- not sure, don't set this flag.
-
- IFL_NODOSVARS
- Do not copy the internal DOS variable area if
- set. This flag may be used for incompatible
- versions of DOS or it's clones, but background
- DOS access will not work if this flag is set.
- Use only for known incompatible versions, and
- only if you do not intend to TSR or Spawn.
-
- "name" is the name of the created task group.
-
-
- void remove_tasker (void);
-
- Uninstalls the multitasker. Must only be called from the
- main task, and should be called before exiting the program.
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 46
-
-
- int ctask_resident (void);
-
- This routine allows checking if another copy of CTask is
- already resident. It should be called before install_tasker
- is invoked.
-
- Returns 1 if CTask is resident, 0 otherwise. It always
- returns 1 after install_tasker has been called.
-
-
-
- Searching for names
-
-
- farptr find_name (byteptr name, int kind);
-
- This routine searches for a task, group, or event structure
- from the current task's group downward to the base group.
-
- Returns a pointer to the structure, or a pointer to the name
- record within the structure (depending on kind) if the name
- was found, NULL otherwise.
-
- This routine may be called before "install_tasker" has been
- invoked to search names in resident copies.
-
- "name" pointer to a zero-terminated, up to eight
- character, name. Case is significant in names.
-
- "kind" is the kind of structure to search for:
-
- If -1, all structures are searched, and the first
- structure with the given name is returned. In
- this case, the returned pointer points to the
- name record within the structure, to allow
- identification of the structure. The "strucp"
- pointer within that record points to the start of
- the structure, the "kind" field in the link
- structure identifies the type of the structure.
-
- If kind is >= 0, the returned pointer is a
- pointer to the start of the structure:
-
- TYP_GROUP - Search for Groups only
- TYP_FLAG - Search for Flags only
- TYP_RESOURCE - Search for Resources only
- TYP_COUNTER - Search for Counters only
- TYP_MAILBOX - Search for Mailboxes only
- TYP_PIPE - Search for Pipes only
- TYP_WPIPE - Search for Word Pipes only
- TYP_BUFFER - Search for Buffers only
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 47
-
-
- farptr find_group_name (gcbptr group, byteptr name, int kind);
-
- Looks for a name in the specified group only.
-
- Return value and parameters match those of find_group_name,
- except
-
- "group" pointer to a group control block.
-
-
-
- Miscellaneous
-
-
- void preempt_off (void);
-
- Disables task preemption. This is the default after instal-
- lation. With preemption turned off, only delays, event
- waits, and explicit scheduler calls will cause a task
- switch.
-
-
- void preempt_on (void);
-
- Enables task preemption. Task switches will occur on timer
- ticks.
-
-
- void schedule (void);
-
- Explicit scheduling request. The highest priority eligible
- task will be made running.
-
-
- void c_schedule (void);
-
- Conditional scheduling request. Scheduling will take place
- only if preemption is allowed.
-
-
- void yield (void);
-
- Explicit scheduling request, with task priority temporarily
- set to zero. If a task has to wait for some external event
- in a polling loop, this call may be used to allow tasks of
- lower priority to execute.
-
-
- void tsk_dis_preempt (void)
-
- Temporarily disable task preemption. Preemption will be re-
- enabled by tsk_ena_preempt or an unconditional scheduler
- call.
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 48
-
-
- void tsk_ena_preempt (void)
-
- Re-enable task preemption. Note that tsk_dis_preempt and
- tsk_ena_preempt do not change the global preemption state
- set by preempt_off and preempt_on.
-
-
- int tsk_dis_int (void)
-
- Disable interrupts. Returns the state of the interrupt flag
- prior to this call (1 if interrupts were enabled).
-
-
- void tsk_ena_int (int state)
-
- Enables interrupts if "state" is nonzero. Normally used in
- conjunction with tsk_dis_int.
-
-
- The routines tsk_dis_int and tsk_ena_int may be used in a simpli-
- fied scheme with the defines
-
- CRITICAL; Declares "int crit_intsav;".
- C_ENTER; Expands to "crit_intsav = tsk_dis_int ();"
- C_LEAVE; Expands to "tsk_ena_int (crit_intsav);".
-
-
- void tsk_cli (void)
-
- Disables interrupts (intrinsic function).
-
-
- void tsk_sti (void)
-
- Unconditionally enables interrupts (intrinsic function).
-
-
- void tsk_outp (int port, byte b)
-
- Outputs the value "b" to hardware-port "port" (intrinsic
- function).
-
- byte tsk_inp (int port)
-
- Returns the value read from port "port" (intrinsic
- function).
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 49
-
-
- The following entry points may be used from assembler routines:
-
- extrn _tsk_scheduler: far
-
- Direct entry into the scheduler. The stack must be set up as
- for an interrupt handler, e.g.
-
- pushf
- cli
- call _tsk_scheduler
-
-
- extrn _sched_int: far
-
- Conditional scheduling call. The stack must be set up as for
- an interrupt handler.
-
-
-
- Task Operations
-
-
- tcbptr create_task (tcbptr task, funcptr func, byteptr stack,
- word stksz, word prior, farptr arg
- [, byteptr name]);
-
- Initialises a task. Must be called prior to any other opera-
- tions on this task. The task is in the stopped state after
- creation. It must be started to be able to run.
-
- Returns a pointer to the created tcb, or NULL on error.
-
- "task" is a pointer to a tcb (NULL for automatic allo-
- cation).
-
- "func" is a pointer to a void far function, with a
- single dword sized parameter.
-
- "stack" is a pointer to a stack area for the task (NULL
- for automatic allocation).
-
- "stksz" is the size of the stack area in bytes.
-
- "prior" is the tasks priority (0 lowest, 0xffff highest).
-
- "arg" is an argument to the created task. It may be
- used to differentiate between tasks when using
- the same function in different tasks.
-
- "name" is the name of the task, up to eight characters,
- plus zero-terminator. This parameter is defined
- only if TSK_NAMEPAR is enabled in tskconf.h.
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 50
-
-
- void kill_task (tcbptr task);
-
- Kills a task. The task can no longer be used.
-
-
- int start_task (tcbptr task);
-
- Starts a task, i.e. makes it eligible for running.
-
- Returns 0 if task was started, -1 if the task was not
- stopped. A value of NULL for "task" will start the "main
- task".
-
-
- int stop_task (tcbptr task);
-
- Stops a task, i.e. removes it from all queues. A value of
- NULL for "task" will stop the "main task".
-
- Returns 0 on success.
-
-
- int wake_task (tcbptr task);
-
- Prematurely wakes a delayed or waiting task. If the task was
- waiting for an event, it will be removed from the waiting
- queue, with the operation terminating with an error return
- value.
-
- Returns 0 if task was waked, -1 if the task was not delayed
- or waiting. A value of NULL for "task" will wake the "main
- task".
-
-
- void set_priority (tcbptr task, word prior)
-
- Sets the priority of the specified task to "prior". Note
- that you should NOT modify the priority field in the tcb
- structure directly. A value of NULL for "task" will set the
- priority of the "main task".
-
-
- word get_priority (tcbptr task)
-
- Returns the current priority of the specified task. A value
- of NULL for "task" will get the priority of the "main task".
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 51
-
-
- void set_task_flags (tcbptr task, byte flags)
-
- Sets the flags of the task to "flags". Currently, the only
- flag that can be changed is
-
- F_CRIT the task can not be preempted if set.
-
- Note that you may NOT modify the flag field in the tcb
- structure directly. A value of NULL for "task" will set the
- flags of the "main task".
-
-
- void set_funcs (tcbptr task, funcptr save, funcptr rest);
-
- Sets the save and restore functions for the specified task.
- The save function is called whenever the task is deacti-
- vated, the restore function is called whenever the task is
- scheduled. This allows saving of critical system states upon
- a task switch. Both functions are called with a far call,
- the pointer to the task control block is passed as
- parameter. A value of NULL for "task" will set the functions
- of the "main task". To deactivate one or both functions,
- pass NULL as function parameter.
-
-
- farptr set_user_ptr (tcbptr task, farptr uptr);
-
- Sets the user pointer in the specified TCB, and returns it's
- previous value. The user pointer may be used to point to
- memory blocks local to a task, or to hold other task-
- specific values. It is never accessed or modified by the
- CTask kernel. A value of NULL for "task" will set the user
- pointer of the "main task".
-
-
- farptr get_user_ptr (tcbptr task);
-
- Returns the user pointer of the specified TCB. A value of
- NULL for "task" will get the user pointer of the "main
- task".
-
-
-
- Timer Operations
-
-
- Timeouts are normally specified in timer ticks. If CLOCK_MSEC is
- enabled, timeouts will be converted to the nearest number of
- timer ticks automatically. The global word variable
- "ticks_per_sec" contains a rough estimate of the number of ticks
- per second even if CLOCK_MSEC is not enabled.
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 52
-
-
- Event wait Timeouts
-
- When waiting for an event, a timeout may be specified. The
- operation will terminate with an error return value if the
- timeout is reached before the event occurs. NOTE that the timeout
- parameter is not optional. To specify no timeout, use the value 0.
-
-
- "Tickers"
-
- It sometimes is desirable to measure timing intervals or global
- timeouts without creating special tasks, or while waiting for
- external events in polling loops. A low overhead way of doing
- this is the "ticker" structure. When this structure is linked
- into the ticker chain, the doubleword "ticks" field is decre-
- mented on every timer tick (in the timer interrupt itself), until
- it reaches zero.
-
-
- tick_ptr create_ticker (tick_ptr elem, dword val);
-
- Links a ticker element into the ticker chain. The ticks
- field is initialized to "val", and will be decremented to
- zero. If the elem pointer is NULL, a new ticker element will
- be created.
-
- Returns the ticker element pointer, NULL on error.
-
-
- void delete_ticker (tick_ptr elem);
-
- Unlinks a ticker element from the ticker chain. Countdown
- will stop. If the element was allocated dynamically, it will
- be deleted.
-
-
- void set_ticker (tick_ptr elem, dword val);
-
- Sets the ticks field to the given value. If the ticks field
- is modified directly instead of through this routine,
- interrupts should be disabled when writing a doubleword
- value.
-
-
- dword get_ticker (tick_ptr elem)
-
- Returns the current ticker value. If the ticks field is read
- directly instead of through this routine, interrupts should
- be disabled when reading the doubleword value.
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 53
-
-
- Delays
-
-
- int t_delay (dword ticks);
-
- Delay the current task for "ticks" clock ticks/milliseconds.
- If ticks is 0, the task is stopped.
-
- Returns TIMEOUT if the delay expired, WAKE if the task was
- activated by wake_task.
-
-
-
- Timed Events and Watch Events
-
- You can create timer control blocks and memory/port watch blocks
- to
- - set a flag
- - increment a counter
- - wake up a task
- - call a function
-
- after a specified timeout interval, or when a certain condition
- is met. The operation may optionally be repeated.
-
- Watch control blocks are checked on every timer tick. They allow
- memory locations and I/O ports to be checked for a certain value,
- or for a change in value. When the specified condition is met,
- the given action is executed.
-
- For calling functions on a timeout or watch, the following must
- be noted:
-
- - Timeout/watch functions should be as short as possible,
- since they run at the highest priority.
- - The stack and data area is the area of the timer task. Be
- careful when referencing variables.
- - Timeout/watch functions may not use any functions that could
- cause the timer task to be made waiting.
-
- Since version 2.0, timeout/watch functions are called with
- interrupts enabled. The definition of the timeout function is
-
- void far funcname (dword user_parm);
-
- The function is called with the "userpar" parameter specified
- with the create_timer, create_memory_watch, and create_port_watch
- calls.
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 54
-
-
- tlinkptr create_timer (tlinkptr elem, dword tout, farptr strucp,
- byte kind, byte rept [,dword userpar]);
-
- Create a timer control block.
-
- Returns the address of the control block, NULL on error.
-
- "elem" is the control block to initialise (NULL for
- auto-matic allocation).
-
- "tout" specifies the timeout interval.
-
- "strucp" points to the structure to be used. This is a
- flagptr for setting a flag, a counterptr for
- increasing counters, a tcbptr for waking a task,
- or a funcptr for calling a function.
-
- "kind" gives the kind of structure "strucp" points to.
- It must be one of
-
- TKIND_WAKE for task-wakeup
- TKIND_PROC for function call
- TKIND_FLAG for flag set
- TKIND_COUNTER for counter increment.
-
- "rept" if nonzero, the action will be repeated on every
- "tout" timeout interval.
-
- "userpar" is a doubleword value that is passed to the
- timeout function if kind is TKIND_PROC. This
- parameter is optional.
-
- NOTE: Timer control blocks can not be named.
-
-
- void change_timer (tlinkptr elem, dword tout, byte rept);
-
- Changes the timeout value and/or the repeat flag for an
- existing timer control block. If "tout" is zero, the call
- has the same effect as delete_timer.
-
-
- void delete_timer (tlinkptr elem);
-
- Deletes a timer control block, and removes it from the
- timeout queue.
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 55
-
-
- tlinkptr create_memory_watch (tlinkptr elem, farptr address,
- word mask, word compare,
- byte cmpkind, farptr strucp,
- byte kind, byte rept
- [, dword userpar]);
-
- Creates a memory watch control block. Memory watches compare
- a byte or word value at the specified address against a
- comparison value. The value loaded is always a word, the
- "mask" parameter may be used to restrict comparison to a
- byte.
-
- Returns the watch control block pointer, or NULL on error.
-
- "elem" is the control block to initialise (NULL for
- automatic allocation).
-
- "address" is a pointer to the memory location to watch.
-
- "mask" is a bitmask to mask out irrelevant bits before
- comparison.
-
- "compare" is the value the memory contents are to be
- compared with.
-
- "cmpkind" is the kind of comparison to be used:
-
- TCMP_EQ (*address & mask) == compare
- TCMP_NE (*address & mask) != compare
- TCMP_GE (*address & mask) >= compare (unsigned)
- TCMP_LE (*address & mask) <= compare (unsigned)
- TCMP_GES (*address & mask) >= compare (signed)
- TCMP_LES (*address & mask) <= compare (signed)
-
- TCMP_CHG Change in value:
- (*address & mask) != compare
- compare = *address & mask
-
- "strucp" points to the structure to be used. This is a
- flagptr for setting a flag, a counterptr for
- increasing counters, a tcbptr for waking a task,
- or a funcptr for calling a function.
-
- "kind" gives the kind of structure "strucp" points to.
- It must be one of
-
- TKIND_WAKE for task-wakeup
- TKIND_PROC for function call
- TKIND_FLAG for flag set
- TKIND_COUNTER for counter increment.
-
- "rept" if nonzero, the action will be repeated whenever
- the condition is met.
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 56
-
-
- "userpar" is a doubleword value that is passed to the watch
- function if kind is TKIND_PROC. This parameter is
- optional.
-
- NOTE: Watch control blocks can not be named.
-
-
- int wait_memory (farptr address,
- word mask, word compare, byte cmpkind)
-
- Delays the current task until the specified condition is
- met. Parameters are the same as in create_memory_watch. No
- timeout can be specified.
-
- Returns WATCH if the watch condition was met, WAKE if the
- task was activated by wake_task.
-
-
- tlinkptr create_port_watch (tlinkptr elem,
- word port, byte in_word,
- word mask, word compare,
- byte cmpkind, farptr strucp,
- byte kind, byte rept
- [, dword userpar]);
-
- Creates a port watch control block. Port watches compare a
- byte or word value read from the specified port address
- against a comarison value. The value read is either a word
- or a byte, depending on the "in_word" parameter.
-
- Returns the watch control block pointer, or NULL on error.
-
- "elem" is the control block to initialise (NULL for
- automatic allocation).
-
- "port" is the port address to read.
-
- "in_word" Specifies whether a word or byte input
- instruction is to be used. If zero, a byte is
- input, if nonzero, a word is read from the port.
-
- "mask" is a bitmask to mask out irrelevant bits before
- comparison.
-
- "compare" is the value the port contents are to be compared
- with.
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 57
-
-
- "cmpkind" is the kind of comparison to be used:
-
- TCMP_EQ (in(port) & mask) == compare
- TCMP_NE (in(port) & mask) != compare
- TCMP_GE (in(port) & mask) >= compare (unsigned)
- TCMP_LE (in(port) & mask) <= compare (unsigned)
- TCMP_GES (in(port) & mask) >= compare (signed)
- TCMP_LES (in(port) & mask) <= compare (signed)
-
- TCMP_CHG Change in value:
- (in(port) & mask) != compare
- compare = in(port) & mask
- (NOTE: The port is read only once.)
-
- "strucp" points to the structure to be used. This is a
- flagptr for setting a flag, a counterptr for
- increasing counters, a tcbptr for waking a task,
- or a funcptr for calling a function.
-
- "kind" gives the kind of structure "strucp" points to.
- It must be one of
-
- TKIND_WAKE for task-wakeup
- TKIND_PROC for function call
- TKIND_FLAG for flag set
- TKIND_COUNTER for counter increment.
-
- "rept" if nonzero, the action will be repeated whenever
- the condition is met.
-
- "userpar" is a doubleword value that is passed to the watch
- function if kind is TKIND_PROC. This parameter is
- optional.
-
- NOTE: Watch control blocks can not be named.
-
-
-
- int wait_port (word port, byte in_word,
- word mask, word compare, byte cmpkind)
-
- Delays the current task until the specified condition is
- met. Parameters are the same as in create_port_watch. No
- timeout can be specified.
-
- Returns WATCH if the watch condition was met, WAKE if the
- task was activated by wake_task.
-
-
- void delete_watch (tlinkptr elem);
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 58
-
-
- The specified watch element is removed from the watch queue.
-
-
- NOTE: delete_timer, delete_watch, and change_timer should not be
- used on automatically allocated timer/watch control blocks. Since
- such blocks are deallocated once the timeout expires or the
- condition is met (except for repeat operations), the validity of
- the pointer is not guaranteed.
-
-
-
- Event Operations
-
- Resources
-
- A Resource is either in use or free, the default state is free.
- If a task has requested a resource, its state is in use, and the
- requesting task is said to "own" the resource. Tasks requesting
- a resource while it is in use will be made waiting. If the
- resource is released, the highest priority waiting task is made
- eligible, and is assigned the resource. Interrupt handlers may
- not use resource functions other than "check_resource".
-
- To facilitate use of resources in nested routines, CTask allows a
- resource to be requested by a Task already owning it. Different
- calls may be used to request resources, depending on how such
- nested calls should be handled. The standard request_resource and
- c_request_resource calls just mark the resource as in use by
- setting the owner count to 1, and the first nested routine that
- releases the resource will cause the resource to be freed. The
- request_cresource and c_request_cresource calls increment the
- owner count, so the resource will only be freed upon a matching
- count of requests and releases.
-
-
- resourceptr create_resource (resourceptr rsc [, byteptr name]);
-
- This initialises a resource. Must be used prior to any other
- operations on a resource.
-
- Returns the address of the control block, NULL on error.
-
- "rsc" is the resource control block (NULL for automatic
- allocation).
-
- "name" is the name of the resource, up to eight
- characters, plus zero-terminator. This parameter
- is defined only if TSK_NAMEPAR is enabled in
- tskconf.h.
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 59
-
-
- void delete_resource (resourceptr rsc);
-
- Calling this routine is optional. It will kill all tasks
- waiting for the resource.
-
-
- void release_resource (resourceptr rsc);
-
- Decrement the owner count, and release the resource if it is
- zero. If the task does not own the resource, the call is
- ignored. If the resource is released, and tasks are waiting
- for access, the highest priority task will gain access to
- the resource.
-
-
- int request_resource (resourceptr rsc, dword timeout);
-
- Requests the resource. If it is not available, the task is
- suspended. A timeout may be specified.
-
- Returns 0 if resource was allocated, TIMEOUT if a timeout
- occurred, WAKE on wake.
-
- This call is ignored (returns a 0) if the calling task
- already owns the resource.
-
-
- int request_cresource (resourceptr rsc, dword timeout);
-
- Requests the resource. If it is not available, the task is
- suspended. A timeout may be specified.
-
- Returns 0 if resource was allocated, TIMEOUT if a timeout
- occurred, WAKE on wake.
-
- This call increments the owner count, and returns 0, if the
- calling task already owns the resource.
-
-
- int c_request_resource (resourceptr rsc);
-
- Requests the resource only if it is available.
-
- Returns 0 if resource was allocated, -1 if unavailable.
-
-
- int check_resource (resourceptr rsc);
-
- Returns 0 if resource is allocated, 1 if free.
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 60
-
-
- Flags
-
- A Flag can be either on or off, the default state is off (0).
- Tasks can wait on either state of the flag. If the state is
- changed, all tasks waiting for the state are made eligible.
- Interrupt handlers may use the "set_flag", "clear_flag", and
- "check_flag" functions.
-
-
- flagptr create_flag (flagptr flg [, byteptr name]);
-
- This initialises a flag. Must be used prior to any other
- operations on a flag. The state is set to 0.
-
- Returns the address of the control block, NULL on error.
-
- "flg" is the flag control block (NULL for automatic
- allocation).
-
- "name" is the name of the flag, up to eight characters,
- plus zero-terminator. This parameter is defined
- only if TSK_NAMEPAR is enabled in tskconf.h.
-
-
- void delete_flag (flagptr flg);
-
- Calling this routine is optional. It will kill all tasks
- waiting for the flag.
-
-
- void set_flag (flagptr flg);
-
- This sets the flag. All tasks waiting for the set state will
- be made eligible for running.
-
-
- void clear_flag (flagptr flg);
-
- This clears the flag. All tasks waiting for the clear state
- will be made eligible for running.
-
-
- int wait_flag_set (flagptr flg, dword timeout);
-
- Waits for the set state of the flag. If the flag is not set,
- the task is suspended. A timeout may be specified.
-
- Returns 0 if the flag was set, TIMEOUT on timeout, WAKE on
- wake.
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 61
-
-
- int wait_flag_clear (flagptr flg, dword timeout);
-
- Waits for the clear state of the flag. If the flag is not
- clear, the task is suspended. A timeout may be specified.
-
- Returns 0 if the flag was cleared, TIMEOUT on timeout, WAKE
- on wake.
-
-
- int clear_flag_wait_set (flagptr flg, dword timeout)
-
- Combines the operations clear_flag and wait_flag_set.
-
- Returns 0 if the flag was set, TIMEOUT on timeout, WAKE on
- wake.
-
-
- int check_flag (flagptr flg);
-
- Returns 0 if flag clear, 1 if set.
-
-
-
- Counters
-
- A Counter can have any value from 0L to 0xffffffffL, the default
- value is 0. Tasks can wait for a counter being zero or non-zero.
- If the counter is cleared or decremented to zero, all tasks wai-
- ting for the zero condition are made eligible. If the counter is
- incremented, the highest priority task waiting for non-zero is
- made eligible, and the counter is decremented by one. Interrupt
- handlers may use the "clear_counter", "inc_counter",
- "set_counter", and "check_counter" functions.
-
-
- counterptr create_counter (counterptr cnt [, byteptr name]);
-
- This initialises a counter. Must be used prior to any other
- operations on a flag. The value is set to 0.
-
- Returns the address of the control block, NULL on error.
-
- "cnt" is the counter control block (NULL for automatic
- allocation).
-
- "name" is the name of the counter, up to eight
- characters, plus zero-terminator. This parameter
- is defined only if TSK_NAMEPAR is enabled in
- tskconf.h.
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 62
-
-
- void delete_counter (counterptr cnt);
-
- Calling this routine is optional. It will kill all tasks
- waiting for the counter.
-
-
- void clear_counter (counterptr cnt);
-
- Clears the counter to zero. All tasks waiting for the zero
- state will be made eligible for running.
-
-
- int wait_counter_set (counterptr cnt, dword timeout);
-
- Waits for the counter having a nonzero value. If the value
- is zero, the task is suspended. The value is decremented
- when the task gets access to the counter. A timeout may be
- specified.
-
- Returns 0 if the counter was nonzero, TIMEOUT on timeout,
- WAKE on wake.
-
-
- int wait_counter_clear (counterptr cnt, dword timeout);
-
- Waits for the counter having a zero value. If the value is
- nonzero, the task is suspended. A timeout may be specified.
-
- Returns 0 if the counter was zero, TIMEOUT on timeout, WAKE
- on wake.
-
-
- void inc_counter (counterptr cnt);
-
- Increments the counter. If tasks are waiting for the nonzero
- state, the highest priority task is given access to the
- counter.
-
-
- void set_counter (counterptr cnt, dword val);
-
- Sets the counter to the given value. If "val" is nonzero,
- and tasks are waiting for the set state, the highest
- priority tasks are made eligible, and "val" is decremented,
- until either "val" reaches zero, or there are no more tasks
- waiting. If val is, or is counted down to, zero, all tasks
- waiting for the zero state are made eligible.
-
-
- dword check_counter (counterptr cnt);
-
- Returns the current value of the counter.
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 63
-
-
- Mailboxes
-
- A Mailbox can hold any number of mail blocks. Tasks can send mail
- to a mailbox and wait for mail to arrive. A mail block is
- assigned to the highest priority waiting task. Care must be
- exercised not to re-use a mail block until it has been processed
- by the receiving task. The mail block format is user defineable,
- with the first doubleword in a block reserved for the tasking
- system. Interrupt handlers may use the "send_mail",
- "c_wait_mail", and "check_mailbox" functions.
-
- Note that mailboxes are well suited to provide the mechanism for
- managing a chain of (equally sized) free mail blocks. On
- initialization, all free blocks would be "sent" to the manager
- box. Requesting a free block would use "wait_mail", and freeing a
- used block would again "send" it to the manager mailbox.
-
-
- mailboxptr create_mailbox (mailboxptr box [, byteptr name]);
-
- This initialises a mailbox. Must be used prior to any other
- operations on a mailbox.
-
- Returns the address of the control block, NULL on error.
-
- "box" is the mailbox control block (NULL for automatic
- allocation).
-
- "name" is the name of the mailbox, up to eight
- characters, plus zero-terminator. This parameter
- is defined only if TSK_NAMEPAR is enabled in
- tskconf.h.
-
-
- void delete_mailbox (mailboxptr box);
-
- Calling this routine is optional. It will kill all tasks
- waiting for mail.
-
-
- void send_mail (mailboxptr box, farptr msg);
-
- Sends a message to the specified mailbox. If tasks are
- waiting for mail, the highest priority task will get it.
-
-
- farptr wait_mail (mailboxptr box, dword timeout);
-
- Waits for mail. If no mail is available, the task is
- suspended. A timeout may be specified.
-
- Returns the pointer to the received mail block, TTIMEOUT on
- timeout, TWAKE on wake.
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 64
-
-
- farptr c_wait_mail (mailboxptr box);
-
- Reads mail only if mail is available.
-
- Returns NULL if there is no mail, else a pointer to the
- received message.
-
-
- int check_mailbox (mailboxptr box);
-
- Returns 0 if mailbox is empty, 1 otherwise.
-
-
-
- Pipes
-
- A Pipe has a buffer to hold character or word sized items. An
- item may be written to a pipe if there is space in the buffer.
- Otherwise, the writing task will be made waiting. A reading task
- will be made waiting if the buffer is empty. If an item has been
- read, the highest priority task waiting to write to the pipe will
- be allowed to write. Interrupt handlers may only use pipe
- functions "check_pipe", "c_write_pipe", and "c_read_pipe".
-
- Note that the values -1 and -2 (0xffff and 0xfffe) should be
- avoided when writing to word pipes. These values are used to mark
- timeout and wake when reading, or pipe empty when checking a word
- pipe, and thus may lead to erroneous operation of your routines.
-
-
- pipeptr create_pipe (pipeptr pip, farptr buf, word bufsize
- [, byteptr name]);
- wpipeptr create_wpipe (wpipeptr pip, farptr buf, word bufsize
- [, byteptr name]);
-
- This initialises a pipe. Must be used prior to any other
- operations on a pipe. "bufsize" specifies the buffer size in
- bytes. With word pipes, the buffer size should be divisible
- by two.
-
- Returns the address of the control block, NULL on error.
-
- "pip" is the pipe/wpipe control block (NULL for
- automatic allocation).
-
- "name" is the name of the pipe, up to eight characters,
- plus zero-terminator. This parameter is defined
- only if TSK_NAMEPAR is enabled in tskconf.h.
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 65
-
-
- void delete_pipe (pipeptr pip);
- void delete_wpipe (wpipeptr pip);
-
- Calling this routine is optional. It will kill all tasks
- waiting to read or write messages.
-
-
- int read_pipe (pipeptr pip, dword timeout);
- word read_wpipe (wpipeptr pip, dword timeout);
-
- Read an item from a pipe. If no item is available, the task
- is suspended. A timeout may be specified.
-
- Returns the item, or TIMEOUT on timeout, WAKE on wake.
-
-
- int c_read_pipe (pipeptr pip);
- word c_read_wpipe (wpipeptr pip);
-
- Reads an item from a pipe only if one is available.
-
- Returns the received item, or -1 if none is available.
-
-
- int write_pipe (pipeptr pip, byte ch, dword timeout);
- int write_wpipe (wpipeptr pip, word ch, dword timeout);
-
- Writes an item to a pipe. If the buffer is full, the task is
- suspended. A timeout may be specified. If tasks are waiting
- to read, the item will be assigned to the highest priority
- waiting task.
-
- Returns 0 on success, or TIMEOUT on timeout, WAKE on wake.
-
-
- int c_write_pipe (pipeptr pip, byte ch);
- int c_write_wpipe (wpipeptr pip, word ch);
-
- Writes an item to a pipe only if enough space is available.
-
- Returns 0 on success, or -1 if no space available.
-
-
- int wait_pipe_empty (pipeptr pip, dword timeout);
- int wait_wpipe_empty (wpipeptr pip, dword timeout);
-
- Waits for the pipe to be emptied. If the pipe is already
- empty on entry, the task continues to run.
-
- Returns 0 on empty, TIMEOUT on timeout, WAKE on wake.
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 66
-
-
- int check_pipe (pipeptr pip);
- word check_wpipe (pipeptr pip);
-
- Returns -1 if the pipe is empty, else the first item in the
- pipe. The item is not removed from the pipe.
-
-
- word pipe_free (pipeptr pip);
- word wpipe_free (wpipeptr pip);
-
- Returns the number of free items available in the pipe.
-
-
- void flush_pipe (pipeptr pip)
- void flush_wpipe (wpipeptr pip)
-
- Clears the pipe. Tasks waiting for the empty state are made
- eligible.
-
-
- Buffers
-
- A Buffer has a buffer to hold message strings. A message may be
- written to a buffer if it fits into the buffer. Otherwise, the
- writing task will be made waiting. A reading task will be made
- waiting if the buffer is empty. If a message has been read, the
- highest priority task waiting to write to the buffer will be
- allowed to write if its message fits into the available space.
- Interrupt handlers may not use buffer functions other than
- "check_buffer".
-
- The buffer routines are implemented using resources and pipes,
- and thus are not part of the true "kernel" routines.
-
-
- bufferptr create_buffer (bufferptr buf, farptr pbuf, word bufsize
- [, byteptr name]);
-
- This initialises a buffer. Must be used prior to any other
- operations on a buffer.
-
- The minimum buffer size is the length of the longest
- expected message plus two.
-
- Returns the address of the control block, NULL on error.
-
- "buf" is the buffer control block (NULL for automatic
- allocation).
-
- "name" is the name of the buffer, up to eight
- characters, plus zero-terminator. This parameter
- is defined only if TSK_NAMEPAR is enabled in
- tskconf.h.
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 67
-
-
- void delete_buffer (bufferptr buf);
-
- Calling this routine is optional. It will kill all tasks
- waiting to read or write messages.
-
-
- int read_buffer (bufferptr buf, farptr msg, int size,
- dword timeout);
-
- Read a message from a buffer. If no message is available,
- the task is suspended. A timeout may be specified.
-
- The message will be copied to the buffer "buf", with a
- maximum length of "size" bytes.
-
- Returns the length of the received message, TIMEOUT on
- timeout, WAKE on wake.
-
-
- int c_read_buffer (bufferptr buf, farptr msg, int size);
-
- Reads a message from a buffer only if one is available.
-
- Returns the length of the received message, or -1 if none is
- available.
-
-
- int write_buffer (bufferptr buf, farptr msg, int size,
- dword timeout);
-
- Writes a message from "msg" with length "size" to a buffer.
- If not enough space for the message is available, the task
- is suspended. A timeout may be specified. If tasks are
- waiting for messages, the highest priority task will get it.
-
- Returns the length of the message, TIMEOUT on timeout, WAKE
- on wake, -3 on error (length < 0 or length > buffer size).
-
-
- int c_write_buffer (bufferptr buf, farptr msg, int size);
-
- Writes a message to a buffer only if enough space is
- available.
-
- Returns the length of the message, -1 if no space available,
- or -3 on error (length < 0 or length > buffer size).
-
-
- word check_buffer (bufferptr buf);
-
- Returns the current number of bytes (not messages) in the
- buffer, including the length words.
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 68
-
-
- The Keyboard Handler
-
-
- word t_read_key (void)
-
- Waits for a key to be entered. Returns the ASCII-code in the
- lower byte, and the scan-code in the upper byte, or WAKE
- (0xfffe) on wake.
-
-
- word t_wait_key (dword timeout)
-
- Waits for a key to be entered. Returns the ASCII-code in the
- lower byte, and the scan-code in the upper byte, or TIMEOUT
- (0xffff) on timeout, WAKE (0xfffe) on wake.
-
-
- word t_keyhit (void)
-
- Returns -1 (0xffff) if no key was entered, else the value of
- the key. The key is not removed.
-
-
-
- The Serial I/O handler
-
- The serial I/O handler provides full duplex interrupt driven I/O
- on the serial ports. Support for COM1 and COM2 is included,
- adding other ports is possible by changing the source and/or
- defining ports on-line.
-
-
- int v24_define_port (int base, byte irq, byte vector)
-
- Defines a new COM-port. This routine can only be used if
- TSK_DYNAMIC is enabled.
-
- "base" is the port base I/O address.
-
- "irq" is the IRQ-line number (0-7 for XT, 0-15 for AT)
- the port uses for interrupts.
-
- "vector" is the interrupt vector number (0-0xff).
-
- The return value is the internal port number for use with
- v24_install. -1 is returned on error (memory full).
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 69
-
-
- sioptr v24_install (int port, int init,
- farptr rcvbuf, word rcvsize,
- farptr xmitbuf, word xmitsize);
-
- Installs the handler for the specified port. Currently,
- ports 0 (COM1) and 1 (COM2) are supported by default. Both
- ports may be used simultaneously, the buffer areas for the
- ports must not be shared.
-
- "rcvbuf" is a word pipe buffer area for received
- characters. May be NULL for automatic allocation.
-
- "rcvsize" specifies the receive buffer size in bytes. Note
- that the buffer will hold rcvsize / 2 received
- characters.
-
- "xmitbuf" is a byte pipe buffer for characters waiting for
- transmission. May be NULL for automatic
- allocation.
-
- "xmitsize" gives the transmit buffer size in bytes.
-
- "port" Port number to install (0: COM1, 1: COM2).
-
- If the port number is ORed with 0x80, the port is
- *relative*. This means that the entry in the BIOS
- table for COM-Ports is used to search the tables
- internal to the driver for the port information,
- instead of using the table entry directly. If the
- port address cannot be found, the driver returns
- with an error code. Note that ports are numbered
- from 0, so to specify relative COM1, pass 0x80 as
- parameter.
-
- "init" if non-zero, the port is initialised with the
- default values specified in the source. If the
- parameter is zero, the control registers and baud
- rates on entry are not modified.
-
- The return value is a pointer to the sio control block for
- use with the other driver routines. NULL is returned on
- error (invalid port number, bad buffer sizes, nonexistent
- hardware, out of memory).
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 70
-
-
- void v24_remove (sioptr sio, int restore);
-
- Removes the driver for the specified control block. Should
- be called for all ports installed before exiting the
- program.
-
- If the "restore" parameter is nonzero, the control registers
- and the baud rate that were set before installation of the
- driver are restored. If the parameter is zero, the values
- are not changed.
-
-
- void v24_remove_all (void)
-
- Removes all installed serial i/o drivers. This routine is
- automatically called on remove_tasker if drivers were
- installed. Note that you can not specify a restore
- parameter, the default restore parameter is used (constant
- in tsksio.c).
-
-
- void v24_change_rts (sioptr sio, int on);
-
- Changes the state of the RTS output line. A nonzero value
- for "on" will turn the output on.
-
-
- void v24_change_dtr (sioptr sio, int on);
-
- Changes the state of the DTR output line. A nonzero value
- for "on" will turn the output on.
-
-
- void v24_change_baud (sioptr sio, long rate);
-
- Changes the baud rate. The following baud rates are suppor-
- ted:
-
- 50, 75, 110, 134, 150, 300, 600,
- 1200, 1800, 2000, 2400, 3600, 4800, 7200, 9600,
- 19200, 38400.
-
- Note that baud rates above 9600 may cause problems on slow
- machines.
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 71
-
-
- void v24_change_parity (sioptr sio, int par);
-
- Changes parity. The parameter must be one of the following
- values defined in "sio.h":
-
- PAR_NONE no parity checks
- PAR_EVEN even parity
- PAR_ODD odd parity
- PAR_MARK mark parity
- PAR_SPACE space parity
-
-
- void v24_change_wordlength (sioptr sio, int len);
-
- Changes word length. Values 5, 6, 7, 8 may be given.
-
-
- void v24_change_stopbits (sioptr sio, int n);
-
- Changes Stopbits. Values 1 and 2 are allowed.
-
-
- void v24_watch_modem (sioptr sio, byte flags);
-
- Watches the modem status lines to control transmission.
-
- "flags" Specifies the input lines that must be active to
- allow transmission. Transmission will stop if one
- of the lines goes inactive. The parameter may be
- combined from the following values defined in
- "sio.h":
-
- CTS to watch clear to send
- DSR to watch data set ready
- RI to watch ring indicator
- CD to watch carrier detect
-
- A value of zero (the default) will allow transmission
- regardless of modem status.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 72
-
-
- void v24_protocol (sioptr sio, int prot,
- word offthresh, word onthresh);
-
- Sets the handshake protocol to use.
-
- "prot" specifies the protocol. This parameter may be
- combined from the following values:
-
- XONXOFF to enable XON/XOFF (DC1/DC3) handshake
- RTSCTS to enable RTS/CTS handshake
-
- "offthresh" specifies the minimum number of free items in
- the receive buffer. If this threshold is reached,
- an XOFF is transmitted and/or the RTS line is
- inactivated.
-
- "onthresh" specifies the minimum number of items that must
- be free before XON is transmitted and/or the RTS
- line is re-activated.
-
- Enabling XONXOFF will remove all XON and XOFF characters
- from the input stream. Transmission will be disabled when
- XOFF is received, and re-enabled when XON is received.
-
- Enabling RTSCTS will stop transmission when the CTS modem
- input line is inactive.
-
-
- int v24_send (sioptr sio, byte ch, dword timeout);
-
- Transmits the character "ch". A timeout may be specified.
-
- Returns TIMEOUT on timeout, WAKE on wake, else 0.
-
-
- int v24_receive (sioptr sio, dword timeout);
-
- Waits for a received character. A timeout may be specified.
-
- Returns TIMEOUT on timeout, WAKE on wake, else the character
- in the lower byte, plus an error code in the upper byte. The
- error code is the combination of
-
- 0x02 overrun error
- 0x04 parity error
- 0x08 framing error
- 0x10 break interrupt.
-
-
- int v24_check (sioptr sio);
-
- Returns -1 if no receive character is available, else the
- next character from the pipe. The character is not removed.
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 73
-
-
- int v24_overrun (sioptr sio);
-
- Checks for receive pipe overrun. Returns 1 if an overrun
- occurred, 0 otherwise. Clears the overrun flag.
-
-
- int v24_modem_status (sioptr sio);
-
- Returns the current modem status word. The CTS, DSR, RI, and
- CD defines may be used to extract the modem input line
- status.
-
-
- int v24_complete (sioptr sio);
-
- Returns 1 if all characters in the transmit pipe have been
- sent, else 0.
-
-
- int v24_wait_complete (sioptr sio, dword timeout);
-
- Waits for the transmit pipe to be empty. Returns TIMEOUT on
- timeout, WAKE on wake, else 0.
-
-
- void v24_flush_receive (sioptr sio);
-
- Flushes the receive pipe.
-
-
- void v24_flush_transmit (sioptr sio)
-
- Flushes the transmit pipe. Tasks waiting for transmit
- completion are made eligible.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 74
-
-
- The Printer Output Driver
-
-
- The printer output driver provides for buffered output to up to
- three printer ports (more can be added by editing the source).
- Interrupt or polling may be selected. Due to the usual hardware
- implementation of printers and the printer interface, using
- polling is recommended. When using interrupts, you should not
- simultaneously install both port 0 and port 1 with interrupt
- enabled, since both ports share the same interrupt line.
-
-
- int prt_install (int port, byte polling, word prior,
- farptr xmitbuf, word xmitsize);
-
- Installs the printer driver for the specified port. Ports 0
- (LPT1), 1 (LPT2), and 2 (LPT3) are supported.
-
- "port" The output port to use.
-
- If the port number is ORed with 0x80, the port is
- relative. This means that the entry in the BIOS
- table for LPT-Ports is used to search the tables
- internal to the driver for the port information,
- instead of using the table entry directly. If the
- port address cannot be found, the driver returns
- with an error code. Note that ports are numbered
- from 0, so to specify LPT1, pass 0x80 as
- parameter.
-
- "polling" specifies polling output when nonzero, interrupt
- output when zero.
-
- "prior" sets the priority of the printer output task.
-
- "xmitbuf" is a buffer area for the printer output buffer.
- May be NULL for automatic allocation.
-
- "xmitsize" specifies the output buffer size in bytes.
-
- The return value is the internal port number for use with
- the other driver routines. -1 is returned on error (invalid
- port number, bad buffer sizes, nonexistent hardware).
-
-
- void prt_remove (int port);
-
- Removes the printer driver for the specified port.
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 75
-
-
- void prt_remove_all (void)
-
- Removes all installed printer ports. This routine is
- automatically called on remove_tasker if ports were
- installed.
-
-
- void prt_change_control (int port, byte control);
-
- Changes the printer control output lines of the port. The
- value for "control" may be combined from the following
- values defined in "prt.h":
-
- AUTOFEED will enable printer auto feed if set
- INIT will initialise (prime) the printer if clear
- SELECT will select the printer if set
-
-
- int prt_write (int port, byte ch, dword timeout);
-
- Write a byte to the printer. A timeout may be given.
-
- Returns 0 on success, TIMEOUT on timeout, WAKE on wake.
-
-
- int prt_status (int port);
-
- Returns the current printer status lines, combined from the
- values
-
- BUSY Printer is busy when 0
- ACK Acknowledge (pulsed 0)
- PEND Paper End detected when 0
- SELIN Printer is selected (on line) when 1
- ERROR Printer error when 0
-
-
- int prt_complete (int port);
-
- Returns 1 if the printer buffer has been completely
- transmitted, 0 otherwise.
-
-
- int prt_wait_complete (int port, dword timeout);
-
- Waits for printer output to complete.
-
- Returns 0 on success, else TIMEOUT on timeout, WAKE on wake.
-
- void prt_flush (int port)
-
- Flushes the output pipe of the printer. Tasks waiting for
- print completion are made eligible.
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 76
-
-
- Some notes on potential trouble spots
-
- Turbo C console output
-
- As mentioned in the manual, console output should generally be
- done only from one task, or be protected with resources. This is
- especially true for Turbo C (Version 1.5 and later), because the
- putch function is NOT REENTRANT. Since other console output also
- uses putch in the end, you have to be extremely careful not to
- crash your system by entering the putch routine concurrently. You
- should use the "conout" module provided with CTask to channel
- console output through a buffer. If you own the Turbo C library
- source code, you could replace the "_VideoInt" routine in file
- "crtinit.cas" with an (inline) assembler equivalent that pushes
- BP on the stack instead of storing it into a static variable.
-
-
- The timer tick EOI
-
- To allow CTask to run concurrently with other background programs
- that might steal the timer tick interrupt, and to enable high
- priority tasks to override timer ticks, the tick interrupt
- handler issues an EOI before calling the scheduler. Later, the
- tick task will (possibly indirectly) call the original BIOS tick
- handler, which again issues an EOI for an interrupt that is no
- longer pending. Although this should practically not pose any
- problems, since no other interrupts could normally be pending at
- this time, and the EOI is not stored, it is theoretically
- possible for interrupts to be falsely acknowledged (at least
- that's what the books say).
-
- If you experience problems in special applications (especially if
- interrupt priorities have been reprogrammed), you may use the
- IFL_INT8_DIR installation flag. This flag changes the logic in
- the timer interrupt handler such that the original interrupt is
- called first, and no additional EOI is issued. You could also
- replace the timer-chaining logic with a routine that substitutes
- the BIOS timekeeping (use the BIOS listing as a reference). This,
- however, would preclude chaining to TSR's that have hooked the
- timer tick.
-
-
- Debugging
-
- Debugging CTask programs can be surprising. The debugger can't
- know about the task structure of your program, and you can easily
- sit and wonder for hours why the debugger is behaving so
- strangely until it finally sinks down: the debugger itself can be
- preempted.
-
- The keyboard and timer interrupts are the main culprits. Both can
- hit during execution of debugger routines, so that data may be
- different from what you expect it to be. For example, if you are
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 77
-
-
- tracing through code that reads or writes a global variable that
- is also modified by another task, you may find that this variable
- has a completely different value when you dump it from the value
- that was just loaded into a register on the last instruction
- trace. If you are using Periscope, be sure to let Periscope
- restore the INT 8 and INT 9 entries. This is not exactly pleasant
- when you're using a foreign keyboard, but it can avoid a lot of
- trouble. If you're debugging at the application level, those
- precautions may not be necessary, but if you want to trace into
- the kernel routines, you must be extremely careful not to trash
- the debugger.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 78
-
-
- Changes from Previous Versions
-
-
- When upgrading from previous versions, you have to recompile all
- modules that use any CTask functions or data structures. Due to
- the major changes in the control blocks, just re-linking is not
- sufficient.
-
-
- Changes for CTask 1.2 to 2.0
-
- CTask 2.0 unifies the queue concept, and uses doubly-linked lists
- for all queues, including the task and timer queues. Although the
- overhead is slightly higher when inserting elements, the overall
- logic is greatly simplified, and removing elements from arbitrary
- points in a queue is a snap. To avoid a timing penalty for the
- new functionality, all low-level queue handling code was
- implemented in assembler.
-
- The new concept allows better support for the yield() operation,
- and it also allows an improved handling of timeout elements. With
- the 1.1/1.2 algorithm, it was not completely safe to process the
- timeout queue with interrupts enabled, and changes to the queue
- required great care. The new handling of the timeout queue, which
- is now sorted, and stores the tick difference to the previous
- element instead of an absolute count, decreases the amount of
- time spent in the timeout loop. Watch elements, which still
- require stepping through all queue elements, have been separated
- from timeouts. While the processing of the queue still has to be
- done with interrupts disabled, the timeout/watch action now is
- completely uncritical.
-
- The changes to the queue structures required changes in nearly
- all modules. The main changes were in the tsksub, tsktimer, and
- tskasm modules, the tskque module was added.
-
-
- Interface Changes
-
- Version 2.0 introduces some changes in the interface. Since the
- changes only affect installation and previously unavailable
- functions, the impact on your programs should be minimal. If
- you're using a 1.2 pre-release, watch out for the changes in the
- name search and ticker functions. The affected routines are
-
- install_tasker
- Two new parameters added. Late 1.2 pre-releases
- implemented but ignored the "flags" parameter,
- and recommended setting it to zero. This is no
- longer true.
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 79
-
-
- ctask_resident
- New routine to check if CTask is already
- resident.
-
- find_name
- Name changed from tsk_find_name, values for
- "kind" parameter changed.
-
- find_group_name
- New routine for 1.1, in 1.2 this was called
- tsk_find_group_name. Searches names within a
- group. The "kind" parameter values changed.
-
- yield
- New routine, schedules with minimal priority.
- Version 1.2 also had this, but using it could
- lead to task starvation under certain conditions.
-
- get_priority
- set_funcs
- set_user_ptr
- get_user_ptr
- New routines to access fields in the TCB.
-
- create_ticker
- delete_ticker
- set_ticker
- get_ticker
- New routines for a simplistic time counter. Late
- versions of 1.2 had similar functions with
- different names, and different parameters.
-
- create_timer
- New optional parameter.
-
- create_memory_watch
- create_port_watch
- New routines to create memory/port watch entries.
- Some pre-release versions of 1.2 don't support
- the last, optional, user pointer parameter.
-
- wait_memory
- wait_port
- New routines to wait for memory/port changes.
-
- delete_watch
- New routine to delete watch element. Pre-release
- versions of 1.2 equated this to delete_timer,
- version 2.0 requires delete_watch to be
- different.
-
- set_counter
- New routine to set counter to given value.
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 80
-
-
- v24_flush_transmit
- New routine in serial handler to flush transmit
- pipe.
-
- prt_flush
- New routine in printer handler to flush output
- pipe.
-
-
- timout functions
- The timeout function now is passed a parameter,
- and is called with interrupts enabled.
-
- assembler interface
- All entry points now start with the usual
- underline, since the extended language-specific
- procedure definitions are used. The "scheduler"
- entry was renamed to _tsk_scheduler to avoid
- catastrophic results of typos.
-
- internal functions
- Previous versions defined most internal CTask
- functions as far. This allowed calling them from
- outside the CTask kernel (although that was never
- recommended). The new version no longer allows
- this, since internal functions are near relative
- to the common CTask code segment.
-
-
- Changes for CTask 1.1b to 1.2
-
- The never released version 1.2 added the concept of task groups,
- and the save/restore of internal DOS variables, to support
- spawning and TSR'ing CTask programs. The TSKDOS module went
- through several changes in the pre-release copies.
-
- All relevant global variables of CTask were grouped into a single
- structure, and are indirectly accessed through a pointer. This
- allows linkage between multiple copies of CTask, with automatic
- detection of other copies.
-
- This change required adding new structures, and changing the task
- control block to accommodate the new group linkage and the space
- for the DOS variable swap.
-
- Also new in 1.2 was the addition of a stack switch on entry to
- all interrupt handlers. This change was required to eliminate
- problems with TSR's, especially networks, and to support spawned
- programs that supply only a minimal amount of stack. CTask now
- allocates local stacks from a stack pool to hardware and software
- interrupt handlers. On entry to the scheduler, the task registers
- are no longer pushed on the stack, but instead are stored in the
- task control block. This again mandated a change to the TCB.
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 81
-
-
- Support for memory and port watches that check the state of a
- memory location or an I/O port on every tick was added.
-
- A "ticker" structure that allows simple timeouts in polling tasks
- was introduced.
-
- Task switch save/restore functions were added.
-
- The printer output driver was reworked, an an INT 17 interface
- driver has been added. The INT 15 printer output support was
- dropped.
-
- Resources to protect concurrent access to INT 10 and INT 13 were
- added in the TSKDOS module.
-
- The timer tick interrupt now supports both early and late INT 8
- chaining (and the INT9 stuff has finally been renamed to INT8).
- This change was required to support network software that would
- time out without really having waited long enough if the INT 8
- task was postponed, and ticked a number of ticks at once. It can
- also avoid incompatibilities with other software that does
- strange things in the timer interrupt. Installation of the new
- INT 8 handling is optional.
-
- Installation flags now allow on-line customization of some
- functions, especially the installation of some of the BIOS
- interrupts, and the INT 8 algorithm.
-
- The name-searching functions were augmented to better support
- searching local to groups, and searching groups.
-
- The make-files were cleaned up, and the Turbo C make-files
- changed to work with Borland's make.
-
- Support for the Turbo C Huge model was added, assembler files now
- load the DS register when necessary.
-
- The placement of external definitions in the assembler files was
- changed to avoid the fixup errors that appeared when assembling
- with TASM instead of MASM (thanks for H.J. Haug for pointing this
- out).
-
- All CTask routines are now allocated in a common code segment.
- This allows calling local functions with a near call.
-
- All files were polished a bit, with a version number and change
- date at the top. Comments have again been added. No warnings
- remain for Turbo and Microsoft with all warnings enabled.
-
- All files were affected, and some new files were added.
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 82
-
-
- Changes for CTask 1.1 to 1.1b
-
- Release 1.1b fixed a minor bug in tskmain.c which prevented the
- main task from being delayed.
-
- Thanks to Kent J. Quirk for reporting the bug.
-
- Release 1.1a was necessary to fix three severe and some minor
- bugs in release 1.1. The bugs corrected in 1.1a were
-
- - tskasm.asm: Interrupts enabled too early in the scheduler
- (severe)
- - tskdos.asm: Registers swapped in DOS version test (severe)
- - tsksio.c: Incorrect loop variable in v24_sio_initialise
- (severe)
-
- - tskmain.c: Function tsk_dis_preempt missing
- - tsksnap.c: Incorrect format specification for continuation
- line
- - tskasm.asm: Task state incorrect for eligible tasks
-
- Also some minor changes to eliminate warnings, and changes to the
- test files so they no longer use concurrent console output.
-
- A console output task was added ("conout.c" and "conout.h") as a
- sample for channeling console output through a single task.
-
- Some changes suggested by Stephen Worthington were incorporated
- (pipe flush functions, better SIO transmit interrupt handling).
-
- Thanks to Peter Heinrich, Stephen Worthington, and Burt Bicksler
- for reporting the bugs.
-
-
- Changes for CTask 0.1 to 1.1
-
- Thanks to Peter Heinrich, Tron Hvaring, and Dave Goodwin for
- their suggestions and the bug reports.
-
- The main changes were
-
- - Support for named control blocks
- - Support for dynamic allocation of control blocks and
- buffers
- - Task state display support (snapshot dump)
- - More flexible timer handling, interrupt disable times
- reduced
- - SIO support extended for shared IRQ's and on-line
- definition of ports; save and restore of control
- registers
- - Bug fixes for known bugs
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 83
-
-
- Affected files:
-
- TSKCONF.H - new file, contains configuration options.
-
- TSK.H - additional optional fields in most control
- structures
- - timer queue structure changed completely
- - additional #defines and types for name and timer
- control blocks
- - tsk_inp, tsk_outp, tsk_dis_int, and tsk_ena_int
- functions defined as intrinsics.
-
- TSKMAIN.C - timeout queue handling, and support for timeout
- blocks
- - system tick interrupt chaining separated from
- timeout
- - minor changes to accommodate named and dynamic
- structures.
-
- TSKSUB.C - changes to the timeout-functions
- - new routines for managing named structures.
-
- TSKSIO.C - support for shared IRQ lines an dynamic definition
- of ports was added
- - complete status of the port is saved before
- initialising, and optionally restored on removal of
- the driver
- - relative port number support added.
-
- TSKPRT.C - relative port number support added.
-
- TSKBUF.C - bug corrections.
-
- TSKALLOC.C - new file, interface to memory allocation functions.
-
- TSKSNAP.C - new file, provides snapshot dump of CTask state.
-
- TSKASM.ASM - Partly runs with interrupts enabled, idle task
- removed.
-
- TSKTIM.ASM - cleanup, and changes to accommodate changes in
- timeout logic.
-
- TSKBIOS.ASM - new file, handles the AT BIOS INT 15 wait/post
- calls.
-
- Minor changes in other files to accommodate named and dynamic
- control blocks.
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Page 84
-
-
- Index
-
-
- AT_BIOS 16
- BIOS 20
- buffer 44
- bufferptr 44
- byte 30
- byteptr 30
- change_timer 55
- check_buffer 68
- check_counter 63
- check_flag 62
- check_mailbox 65
- check_pipe 67
- check_resource 60
- check_wpipe 67
- clear_counter 63
- clear_flag 61
- clear_flag_wait_set 62
- CLOCK_MSEC 15
- CLOCK_MSEC 52
- console 20
- counter 42
- counterptr 42
- create_buffer 67
- create_counter 62
- create_flag 61
- create_mailbox 64
- create_memory_watch 56
- create_pipe 65
- create_port_watch 57
- create_resource 59
- create_task 50
- create_ticker 53
- create_timer 55
- create_wpipe 65
- CRITICAL 49
- ctask_resident 47
- C_ENTER 49
- C_LEAVE 49
- c_read_buffer 68
- c_read_pipe 66
- c_read_wpipe 66
- c_request_resource 60
- c_schedule 48
- c_wait_mail 65
- c_write_buffer 68
- c_write_pipe 66
- c_write_wpipe 66
- delete_buffer 68
- delete_counter 63
- delete_flag 61
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Index 1
-
-
- delete_mailbox 64
- delete_pipe 66
- delete_resource 60
- delete_ticker 53
- delete_timer 55
- delete_watch 58
- delete_wpipe 66
- DOS 16
- DOS 20
- dword 30
- farptr 30
- find_group_name 48
- find_name 47
- flag 41
- flagptr 41
- flush_pipe 67
- flush_wpipe 67
- funcptr 30
- F_CRIT 40
- F_STTEMP 39
- F_TEMP 39
- get_priority 51
- get_ticker 53
- get_user_ptr 52
- GROUPS 16
- group_rec 40
- IBM 16
- IFL_DISK 46
- IFL_INT15 46
- IFL_INT8_DIR 46
- IFL_NODOSVARS 46
- IFL_PRINTER 46
- IFL_VIDEO 45
- inc_counter 63
- installation flags 45
- install_tasker 45
- keyboard 20
- kill_task 51
- mailbox 43
- mailboxptr 43
- msgptr 43
- msg_header 43
- NAMELENGTH 36
- nameptr 36
- namerec 36
- pipe 43
- pipeptr 43
- pipe_free 67
- preempt_off 48
- preempt_on 48
- PRI_INT8 15
- PRI_STD 15
- PRI_TIMER 15
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Index 2
-
-
- prt_change_control 76
- prt_complete 76
- prt_flush 76
- prt_install 75
- prt_remove 75
- prt_remove_all 76
- prt_status 76
- prt_wait_complete 76
- prt_write 76
- qelem_pri 31
- queheadptr 31
- queptr 31
- queue 31
- queue_head 31
- read_buffer 68
- read_pipe 66
- read_wpipe 66
- release_resource 60
- remove_tasker 46
- request_cresource 60
- request_resource 60
- resource 42
- resourceptr 42
- restore function 37
- save function 37
- schedule 48
- sched_int 50
- send_mail 64
- set_counter 63
- set_flag 61
- set_funcs 52
- set_priority 51
- set_task_flags 52
- set_ticker 53
- set_user_ptr 52
- SINGLE_DATA 16
- spawn 21
- start_task 51
- stop_task 51
- ST_DELAYED 38
- ST_ELIGIBLE 39
- ST_KILLED 38
- ST_RUNNING 39
- ST_STOPPED 38
- ST_WAITING 38
- tcb 38
- tcbptr 38
- TCMP_CHG 33
- TCMP_EQ 33
- TCMP_GE 33
- TCMP_GES 33
- TCMP_LE 33
- TCMP_LES 33
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Index 3
-
-
- TCMP_NE 33
- TELEM_MEM 32
- telem_memwatch 35
- TELEM_PORT 33
- telem_portwatch 35
- telem_timeout 35
- TELEM_TIMER 32
- TFLAG_BUSY 34
- TFLAG_TEMP 34
- ticker 41
- ticker 53
- tick_ptr 41
- TIMEOUT 30
- Timeout/watch functions 54
- Timeouts 52
- TKIND_COUNTER 32
- TKIND_FLAG 32
- TKIND_PROC 32
- TKIND_TASK 32
- TKIND_WAKE 32
- tlink 35
- tlinkptr 35
- tsk_cli 49
- tsk_dis_int 49
- tsk_dis_preempt 48
- TSK_DYNAMIC 14
- tsk_ena_int 49
- tsk_ena_preempt 49
- tsk_inp 49
- TSK_NAMED 15
- TSK_NAMEPAR 15
- tsk_outp 49
- tsk_scheduler 50
- tsk_sti 49
- TSR 21
- TSTAT_CONTWATCH 34
- TSTAT_COUNTDOWN 34
- TSTAT_IDLE 34
- TSTAT_REMOVE 34
- TSTAT_REPEAT 34
- TSTAT_WATCH 34
- TTIMEOUT 30
- TWAKE 30
- TWATCH 30
- TYP_BUFFER 36
- TYP_COUNTER 36
- TYP_FLAG 36
- TYP_MAILBOX 36
- TYP_PIPE 36
- TYP_RESOURCE 36
- TYP_TCB 36
- TYP_WPIPE 36
- t_delay 54
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Index 4
-
-
- t_keyhit 69
- t_read_key 69
- t_wait_key 69
- user pointer 37
- v24_change_baud 71
- v24_change_dtr 71
- v24_change_parity 72
- v24_change_rts 71
- v24_change_stopbits 72
- v24_change_wordlength 72
- v24_check 73
- v24_complete 74
- v24_define_port 69
- v24_flush_receive 74
- v24_flush_transmit 74
- v24_install 70
- v24_modem_status 74
- v24_overrun 74
- v24_protocol 73
- v24_receive 73
- v24_remove 71
- v24_remove_all 71
- v24_send 73
- v24_wait_complete 74
- v24_watch_modem 72
- wait_counter_clear 63
- wait_counter_set 63
- wait_flag_clear 62
- wait_flag_set 61
- wait_mail 64
- wait_memory 57
- wait_pipe_empty 66
- wait_port 58
- wait_wpipe_empty 66
- WAKE 30
- wake_task 51
- WATCH 30
- word 30
- wordptr 30
- wpipe 44
- wpipeptr 44
- wpipe_free 67
- write_buffer 68
- write_pipe 66
- write_wpipe 66
- yield 48
-
-
-
-
-
-
-
-
-
- Ctask Manual - Version 2.0 - 89-12-21 - Index 5
-