The ModulaTor logo 7KB

The ModulaTor

Oberon-2 and Modula-2 Technical Publication

The ModulaTor
Erlangen's First Independent Modula_2 Journal! Nr. 7, Aug-1994 
______________________________________________________________

An overview of PMOS 
by
P.J. Moylan (peter@ee.newcastle.edu.au) 
Department of Electrical and Computer Engineering 
peter@ee.newcastle.edu.au The University of Newcastle NSW 2308, Australia 

Date: Wed Apr 20 01:25:28 1994
From: peter@laplace.newcastle.edu.au (Peter Moylan)
Subject: PMOS - article for The ModulaTor
To: ea113@fim.uni-erlangen.de
Cc: peter@laplace.newcastle.edu.au
 

1. Introduction 

This document gives an overview description of the PMOS system, a large 
library of Modula-2 modules. The software is distributed at no cost for non-profit 
use; see later for access information. 

The PMOS software system is a set of modules, written in Modula-2, for use in 
real-time applications such as control systems. At the user level, it does not 
appear as an operating system in the conventional sense of having a command 
interpreter and facilities for running multiple independent programs in parallel. 
Rather, it is a set of library routines which can be incorporated into an 
applications program to allow for multitasking within that program. 

PMOS is designed to run on IBM-PC/AT and similar personal computers. One 
would normally use the computer's original operating system, usually MS-DOS, 
while performing the program development task of editing, compiling, and 
linking. Then, when the applications program is run, PMOS takes over the 
computer by substituting its own device drivers, etc., for those which were 
originally present. The keyboard controller is designed in such a way that the 
Ctrl/Alt/Del combination aborts the program and returns control to the original 
operating system. (Incidentally, a module called TerminationControl is provided 
to allow you to provide for graceful program termination, for cases where abrupt 
program abortion could be disastrous.) 

When PMOS is run under OS/2, it runs in a virtual DOS session rather than 
taking over the whole machine. (A complete takeover is neither sensible nor 
feasible without first shutting down OS/2.) In that case the Ctrl/Alt/Ins keyboard 
combination should be used, instead of Ctrl/Alt/Del, to terminate the program. 
When running under OS/2 there are also some restrictions on what the PMOS 
device drivers can do. In particular the hard disk driver will not work - it will act as 
if there were no hard disk available - although the floppy disk driver does work. 

2. Multiprogramming features 

The kernel of the PMOS system is contained in modules InnerKernel, 
TaskControl, Semaphores, and Timer. Many of the procedures in those 
modules may be considered as internal system details which are irrelevant to 
the general user. In this section, we focus attention on the procedures which are 
intended to be user-callable. 

When your program first starts running, there are just two tasks in the system: 
the task which is executing your program, and a "null task" which does nothing 
and whose only function is to give the processor something to do while no other 
task is runnable. Your program may then create a new parallel thread of 
execution by calling procedure CreateTask, in module TaskControl. 
(Meanwhile, PMOS has itself created a few more tasks, to look after device 
drivers and the like.) Further tasks may then be created, either by the original 
task or by any of the newly created tasks. 

When you call CreateTask, you give a procedure name, which becomes the 
starting address for the new task; a priority; and an abbreviated task name 
which is used only for testing. As a general guideline priority levels in the range 
1 to 7 should be used for user-level tasks, and levels 8 to 15 should be reserved 
for system-level tasks such as device drivers. Typically most tasks should have 
priority 1 (the lowest level). There may occasionally be a good reason for giving 
a high priority to a user-defined task of exceptional urgency, but this facility 
should be used with care. A careless use of high priorities can make your 
software run inefficiently, by interfering with the ability of the system to do work 
on your behalf. 

It is important to understand the distinction between a task and a procedure. A 
task is the actual thread of execution, which may pass through many 
procedures during its lifetime. More importantly, it is quite possible for several 
tasks to be executing the same procedure simultaneously. In this connection, it 
should be noted that the variables declared inside a procedure belong to an 
individual task, in the sense that a separate copy of those variables is created 
for each caller of the procedure. The variables declared at module level, on the 
other hand, are shared by all tasks which enter that module. Thus, you should 
avoid declaring global variables unless you intend to make them shared 
variables, in which case you must of course arrange for suitable critical section 
protection when accessing those variables. 

A task normally terminates by reaching the last statement in the procedure in 
which it started execution. If you want the task to terminate before that, you can 
call procedure TaskExit in module TaskControl. 

When the "main program" part of your Modula-2 program completes its 
execution, the entire program terminates, even if there are tasks still in 
execution. This might or might not correspond to what you want; so it may be 
necessary to insert tests at the end of the main program which wait until all 
subsidiary tasks have completed their work. 

3. Semaphores 

Module Semaphores contains procedures CreateSemaphore, Wait, and Signal, 
whose use will be obvious to anyone familiar with semaphores. Those who are 
unfamiliar with the concept should consult an operating systems textbook, since 
an adequate description would take more space than is available here. 

Given that semaphores are used for inter-task communication and protection, 
some programmers become confused about who should be the "owner" of a 
semaphore, and how a semaphore created by one task can be passed to 
another. In fact, this is the wrong way to look at the idea of ownership. It makes 
more sense to say that a semaphore belongs to a module (not to a task), that it 
is created in the initialisation code of that module, and that it is then available for 
use by tasks which call procedures in that module. 

Procedure TimedWaitT in module Semaphores is not intended for general use. 
A better alternative exists in module Timer. 

4. Locks and priority inheritance 

Module TaskControl also defines a data type Lock. A Lock is like a binary 
semaphore, and can be used for protecting critical sections. That is, you can 
use code of the form 

Obtain (L); 

protected section; 

Release (L); 

to ensure that, while the statements in the protected section are being executed, 
no other task can gain entry to any section of code which is also protected by 
the Lock called L. This is a little more efficient than using a semaphore, but is 
more restricted: a Lock may only be used for critical section protection (unlike a 
general semaphore, which may be used for things like intertask 
synchronisation); and the Obtain/Release pairs must be used in a properly 
nested fashion. 

In addition, any section protected by a Lock must be free of any operations - 
except for a nested Obtain/Release on another Lock - which could block the 
task. That is, it is not legal to have a Sleep or a semaphore Wait inside such a 
protected section. (Watch out in particular for procedure calls, where the called 
procedure might perform one of the forbidden operations.) If this condition 
cannot be satisfied, then you must use a semaphore rather than a Lock to 
protect your critical section. 

A special feature of the Obtain/Release mechanism is that it provides for priority 
inheritance. If a high-priority task is blocked while trying to obtain a Lock, then 
the holder of that Lock is promoted in priority, to ensure that it quickly reaches 
the point where it releases the Lock. In the case where a task holds several 
Locks, its priority becomes the maximum of the priorities of the tasks it is 
blocking. This promotion is temporary; the task's priority is dropped again when 
it is no longer blocking a higher-priority task. 

5. Timed Operations 

Most of the work done by module Timer is invisible to the user, since the main 
function of this module is to look after internal system functions such as 
time-slicing. There are, however, two useful user-callable procedures. 
Procedure Sleep provides a timed delay, putting the caller to sleep (and making 
the processor available to other tasks) for a specified number of milliseconds. 
Procedure TimedWait adds a time-out facility to semaphore operations, to allow 
for recovery in the case that an expected event never happened. 

Inside the implementation module, there is a constant MillisecondsPerTick 
which controls the frequency of timer interrupts. Its value can be decreased if it 
is necessary to increase the precision of timed operations. You are warned, 
however, that decreasing the value will increase the time spent by the processor 
on system overheads. 

To avoid confusion, it should be mentioned that the hardware provides more 
than one timer. Module Timer uses one specific hardware timer which simply 
interrupts at a fixed frequency. There is a separate time-of-day clock, which 
keeps track of the date and time even when the computer is powered down, and 
that is looked after by module TimeOfDay. (A module called Directories uses the 
time-of-day clock to know when a file is created; and the time-of-day clock is 
also useful in software testing, when you want to know how long a program 
section takes to execute. At present, there is no facility for timing one specific 
task.) Depending on the hardware configuration, there may be other timers in 
the system which are used for more specialised purposes - see, for example, 
module AnalogueIO - but these are of no concern to the Timer module. 

6. Screen Output 

In the majority of applications, the best procedures to use for screen output are 
those in module Windows. These procedures provide for character and string 
output and input, and a variety of cursor-positioning and similar operations. 

At a lower level, module GlassTTY provides low-overhead but very crude output 
procedures. It is used by some system procedures for error message output, 
but is probably unsuitable for other applications. At the very lowest level, module 
TextVideo performs some of the most primitive hardware operations. 

For numeric input and output, use modules NumericIO and RealIO. The module 
called Conversions might also be of use here. 

It is sometimes useful to have a program produce diagnostic information which 
does not normally appear on the screen, but which can be viewed by the user 
on demand. This facility is provided by a module called MaintenancePages, 
which demonstrates one way to use multiple virtual screens. Similar 
applications can be designed around the features provided by MultiScreen. 

For graphical rather than text modes, use module GWindows. Utility procedures 
to do graph-plotting may be found in module WGraph. The low-level graphics 
functions are in the modules called Screen and Graphics. There is also a 
module called ScreenGeometry whose primary function is to define data types 
"Point" and "Rectangle". 

Module Tiles, which looks after the issues associated with overlapping graphics 
windows, is not intended to be called by the end user. 

7. Keyboard Input 

In "normal" applications, the best keyboard input routines are those, such as 
ReadChar, supplied in module Windows. However, module Keyboard provides 
a way to read a character without going through the Windows module. The 
essential difference is this: the input procedures in Windows prompt the user, 
e.g. with a blinking cursor, and may echo the input on the screen if specified. By 
going directly to module Keyboard, you avoid the prompt. There are some cases 
where this is a more desirable behaviour. 

Both Windows and Keyboard provide a method for reading a character without 
consuming it - a highly desirable facility, since there are many applications 
where one has to overshoot by one character before detecting the end of an 
input string. The Keyboard module does this by providing an "un-read" 
operation called PutBack. The solution in Windows is slightly different, see 
function LookaheadCharacter. The reason for using different approaches in the 
two modules is somewhat technical and not easily explained in an overview 
such as this, but you will soon find that the conventions are well adapted to a 
"natural" style of programming. 

The keyboard is actually handled by two modules. Module Keyboard, already 
mentioned, provides the higher-level interface which most users will find 
appropriate. Those who need more detailed control over keyboard operations 
can use the lower-level module KBdriver, which allows access to such 
information as whether several keys have been pressed simultaneously. 

8. Dealing with a mouse 

The module called Mouse provides functions for things like getting the current 
mouse position, and it also provides for installing a user-supplied procedure to 
be called on any of a defined set of mouse events. 

Internally, this module does its job by importing from either SerialMouse or 
Mouse33. The reason for having two distinct mouse drivers is that not all mice 
are the same, and there are situations where one driver will work and the other 
will not. Mouse33 works by using INT 33H calls to a pre-loaded driver, so this is 
potentially the more portable version; but the vendor-supplied mouse driver for 
at least one popular mouse is incompatible with PMOS because of the way it 
takes over the timer interrupt. Module SerialMouse talks directly to the mouse 
via a serial port; this makes it less portable in principle, but it does at least 
bypass the shortcomings of vendor-supplied drivers. 

To specify which mouse driver to use, edit the definition module for the module 
called ConfigurationOptions. 

There is also a module called UserInterface which lets you use a mouse to 
move windows around on the screen. 

9. Screen Editing 

The design and implementation of operator interfaces can be a tedious and 
time-consuming part of software development, so PMOS contains some 
features to ease this problem. Module Menus displays user-defined menus on 
the screen, and looks after displaying a menu, scrolling when necessary, 
handling the keystrokes by which the keyboard user selects an option, and 
returning the result to the caller. Module ScreenEditor does a similar job where a 
display of variable values, of a mixture of types, must be created with an option 
for the keyboard user to modify some or all of the values. For more complex 
applications, it might be necessary to use the procedures in modules FieldEditor 
and ListEditor. The procedures in module RowEditor, which works in 
collaboration with this group, will not normally be needed to be called directly. 

There is a module Calculator which, when invoked, displays a calculator window 
on the screen and allows the user to perform simple calculations. (The 
calculator procedure returns when the user types the Esc key or clicks on the 
"close" button with the mouse.) Use this when you want the user to be able to 
drop out of your program temporarily to perform some calculations. 

10. Miscellaneous utilities 

The module called LowLevel provides a variety of low-level functions: bitwise 
logic operations, shifts and rotations, pointer decomposition, and the like. More 
low-level procedures can be found in MiscPMOS. 

Random numbers are available from modules RandCard (for a cardinal-valued 
result) and Random (for a real-valued result). 

To implement queues, see modules CircularBuffers, Queues, and 
LossyQueues. Module Queues handles the most general case, but 
CircularBuffers can be more efficient when it is applicable. LossyQueues is for 
the situation, very common in real-time applications, where it is better to lose 
data than to have to wait for buffer space to become available. 

Queue-handling operations are also supplied in Mailboxes. This module is 
intended as a convenient mechanism for message-passing between tasks. 

Module Conversions deals with things like string-to-numeric conversions. 

A date and time procedure may be found in module TimeOfDay. 

11. Sound output 

Module SoundEffects deals, at a low level, with output to the speaker. You can, 
for example, call procedure Beep to produce an audible alarm. For more 
ambitious sound effects, see modules Music and Piano. 

The module called MusicDemonstration has as its only function the playing of a 
demonstration piece of music. If you import it into your program, the music will 
play in parallel with whatever your program is doing. 

Sorting is provided in QuickSortModule and FileSort. QuickSortModule supplies 
a procedure for in-memory sorting, and FileSort does in-place sorting of a file. 

12. File I/O 

The standard operations of opening and closing a file, and of reading and 
writing, are in module Files. 

Note: in many operating systems, you have to specify when opening a file 
whether you are opening it for reading or for writing. PMOS takes a different 
approach: you can mix read and write operations, but you have to specify when 
opening a file whether it is supposed to be a new file or an existing file. Unlike 
MS-DOS, PMOS does not allow you to open a new file with the same name as 
an existing file. This helps to avoid accidental file deletions. 

The modules called Directories, FileNames, IOErrorCodes, and Devices are 
parts of the file system which are not normally called by the end user. 

When using the Files module to access disk files, you must explicitly import 
modules HardDisk and/or Floppy (and the import declarations should come 
before the importation of Files), even though you will not explicitly call 
procedures in those modules. The file system does not have direct access to the 
device drivers; it simply makes use of whatever device drivers have (in their 
initialisation code) called module Devices to declare their presence. 

Although the PMOS file system works well with floppy disks, it does not always 
handle hard disk files satisfactorily. The problem areas are: 

(a) The HardDisk driver does not support all known disk interfaces. It does not 
handle SCSI disks, for example. 

(b) OS/2 will not permit hard disk operations, so under OS/2 the HardDisk 
module acts as if there is no hard disk present. 

(c) At program termination, MS-DOS does not detect the fact that it is holding an 
obsolete copy of the directories and file allocation table, so it will not see newly 
created files - and it will attempt to write over them - unless the machine is 
rebooted. 

To circumvent these problems, an alternative module FileSys provides the 
same features as the Files module, but it implements them via system calls 
rather than by dealing directly with the device drivers. This loses some of the 
advantages of multitasking, but provides greater program portability. 

13. Other device drivers 

Module Printer supports a dot matrix printer connected to the parallel port. At 
present, this module is not part of the file system, so the PrintChar procedure 
must be called directly. 

For I/O through the serial ports, use module SerialIO. 

Module AnalogueIO provides an interface to analogue-to-digital hardware. 
Normally the A/D software will run in a "periodic sampling" mode, which is 
explained in the comments in the definition module. Note that the support is for 
one specific type of A/D interface board, and will have to be rewritten if the 
hardware is changed. 

Due to the special nature of A/D operations, there is no intention to support 
analogue operations within the file system. 

14. Writing new device drivers 

To add a new device driver to the system, it is necessary to understand how to 
use procedures CreateInterruptTask and WaitForInterrupt in module 
TaskControl. It will probably also be necessary to use the procedures in 
modules LowLevel, DMA, and MiscPMOS. In this connection, it should be noted 
that the ROM routines in the computer's BIOS (Basic Input/Output System) are 
sometimes incompatible with multitasking and may have to be avoided. It is 
advisable to study the listings of existing device drivers, to see what techniques 
are needed. 

To incorporate a new device driver into the file system, use module Devices. 
The file system does not in itself have access to any device driver, but indirectly 
it can access any driver which makes itself known to module Devices. 

15. Other modules 

Module Trace is an aid to program testing and debugging; its use will be obvious 
after reading the module listing. Module KTrace is a lower-level version, which 
will rarely be needed by the average programmer. 

The module called TerminationControl provides a means by which any 
user-written module can declare a "clean-up" routine to be invoked when the 
program terminates (either normally, or on an error). Multiple calls to this 
module are possible, so it is quite possible for each module to have its own 
separate termination procedure. If a termination handler itself installs another 
termination handler, this forces an extra pass through the termination handling. 

Module Bounce is for demonstration and testing: it shows a text-mode bouncing 
ball on the screen. 

The "module" called Skeleton is not in fact a module. It is simply a file which may 
be copied to simplify the typing effort when creating a new module. 

16. Disclaimer 

The PMOS system is an experimental package which is under active 
development, and there is no guarantee of upward compatibility. The 
information herein is accurate at the time of writing, but changes are likely to 
occur in future versions. If you run into version compatibility problems, check the 
source file of the current version in the PMOS library. 

While all care has been taken to avoid software errors, no responsibility can be 
accepted for reliability of the software. 

It would be appreciated if any errors found could be reported to the author of this 
document. 

17. Distribution information 

PMOS is available for anonymous ftp at address ee.newcastle.edu.au, in 
directory pub/PMOS. An alternative ftp site is ftp.psg.com, directory 
pub/modula-2/code. 

If you don't have ftp access, try one of the following: 

- Fat City Software BBS, San Diego (USA); supports anonymous "gopher" type 
access. Send mail saying "HELP" in the subject line to fileserv@fatcity.com. 
Also available via FREQ on FidoNet, RIME, DOVE-Net and other networks. 
Contact bruceb@fatcity.com for more information or call the BBS direct at (619) 
484-7683. ZyXEL 19.2k. 

- Cix, a subscription conferencing system in London (England); supports mail, 
Telnet, zmodem transfer. Conference modula.2/jpi. Conference moderator is 
Don Milne, mpack@cix.compulink.co.uk. 

- PMI BBS, 414-465-1656 14.4k baud v.32bis v.42bis. 

PMOS is free for non-profit use. Further information may be found in the 
README file which is distributed with the software. 

________________________________________________________________________ 

IMPRESSUM: The ModulaTor is an unrefereed journal. Technical papers are to be taken as working papers and personal rather than organizational statements. Items are printed at the discretion of the Editor based upon his judgement on the interest and relevancy to the readership. Letters, announcements, and other items of professional interest are selected on the same basis. Office of publication: The Editor of The ModulaTor is Guenter Dotzel; he can be reached by tel/fax: [removed due to abuse] or by mailto:[email deleted due to spam]
  ModulaWare home page   The ModulaTor download    [Any browser]

Webdesign by www.otolo.com/webworx, 14-Jul-1998