The ModulaTor logo 7KB

The ModulaTor

Oberon-2 and Modula-2 Technical Publication

The ModulaTor
Erlangen's First Independent Modula_2 Journal! Nr. 0/Jan-1992 

The MCS Modula-2 Compiler System V4.4 

by Dieter J~ager.

This overview describes the components of the MCS Modula-2 program development 
system under Unix. MCS is a configurable host/target system in that it is capable to 
compile programs on any supported host for any supported target. 

Supported platforms include the Unix-platforms: 

PC/AT (Interactive, SCO, ESIX, LynxOS, ... - I'386 & I'486, flat memory model), 

HP 9000 series 300 & 400 and other systems that use the Motorola 680x0 processors 
under Unix and OS-9. 

SUN Sparc-1, 2 and 10 under Solaris V1 and V2.x. 

1. The MCS Compiler and its Environment  

The MCS Modula-2 System consists of the following separate programs and libraries: 

mc Modula-2 compiler. 

The compiler is a fast two-pass compiler and implements the full Modula-2 language as 
defined in Niklaus Wirth's book Programming in Modula-2 (3rd ed.). Additionaly the 
compiler features some ISO-Modula-2 as well as system specific language extensions. 

ml Modula-2 linker. 

The linker connects and relocates the single compiled modules to an executable 
program. It is an optimizing linker in that those procedures which are not referenced 
within the program are located and extracted. This keeps the executable image file 
small. If the linker detects references to "foreign" languages like C or FORTRAN the 
system linker is called automatically to finish the task of building the complete program. 

mu the Modula-2 make utility. 

The make utility reads a list of the modules that build the program. It checks the 
dependencies between the modules and automatically compiles those modules that 
were changed or that directly or indirectly dependent on them. 

mar the Modula-2 library archive manager. 

The archive manager program handles the inclusion of separately compiled modules in a 
libray file. 

mlib the MCS Modula-2 standard library. 

This library is very similar to the standard C library and constitutes the basis of producing 
truely portable programs. 

clib the interface library to the operating system. 

Because the C library as defined by the X-Open group is the interface to the UNIX 
operating system, this library contains a collection of so called interface modules which 
allows the access to the functions of the C library. 

1.1. Windows-Environment  

Additionally an integrated windows-oriented environment MCS/W is available. MCS/W 
consists of the following programs: 

md the Modula-2 source level run time debugger. 

me the Modula-2 editor. 

maw the windows server program for ascii terminals. 

mxw the windows server program for the X-Windows system. 

mtc an interactive tool for configuration purpose. 

1.2. The Configuration File  

All programs developed with MCS are configured by a so called configuration file. This is 
a small text file that contains the basic parameters, compiler and linker options to 
configure the system for a specific task. It allows for example the production of programs 
for other than the host operating system or processor. 

For example, the following file project.cfg under OS-9 describes some project-specific 

*Version 4.2 for os9/68020                                      
* Specification of some compiler and linker internal values, e.g. 
hash table size. 

mtemp        'RAM'                                               
mlprocs     1200                                                 
mlmods      150                                                  

* path to the library to use:                                    
modlib       l:/dd/lib/Modula/os9/mlib.68020,\                   

* the path to the sources of the libraries, used by md           
srclib       a:/dd/srclib/os9/mlib,\                             

* predefined basic compiler and linker switches:                 
mcoption     v+,pic+,lm-,ld-,varopt+,codeopt+,cpu:20             
mloption     v+,op+,s+                                           

* register layout to use:                                        
reguse       fp:a5,pd:a4,sb:a6,lastd:d0,lastf:f0,lasta:a0,\      

* memory layout for the final program:                           
layout       sboff:$8000,segl:1,\                                

Example a the development cycle:

                      configuration file                         
             mc sourcefile  -----> ".lnk"-file                   
             ml ".lnk"-file -----> executable program            

mc testprg -od:rels project
  (* output directory: rels, configuration file: project.cfg *)
ml rels/testprg project
  (* configuration file: project *)

If there is no configuration file specified in the command line, the file default.cfg is 
searched. (First in the current directory and, if not found, in the directory designated by 
the environment variable M2ENV.) 

1.3. The MCS Standard Library mlib  

A basic requirement for writing truely portable programs is a standardized library. MCS 
provides mlib with identical definitions (layers 1 to 3) across all supported host and 
target systems. 

The overall structure of the library separates the different modules into four layers: 

                  |                     |                        
   layer 3        |    utility layer    |  e.g. forking         
                  |                     |                       

                  | system independent  |                       
   layer 2        |                     |  e.g. TextIO FileIO   
                  |       modules       |                       

                  |  system dependent   |                       
   layer 1        |                     |  e.g. BasicIO Storage 
                  |       modules       |                       

                  |                     |                       
   layer 0        |  system interface   |  e.g. Systemcalls     
                  |                     |                       

layer 0: This layer contains the modules directly interfacing the operating system. It is 
completely operating system dependent. 

layer 1: This layer represents the Modula-2 interface to the operating system. Their 
definition modules are unique under all operating systems supported by MCS. Only the 
implementation modules are system specific. An example for this layer is module 
BasicIO. BasicIO provides a stream data type STREAM with associated operations such 
as connecting to a stream, reading and writing. 

layer 2: This layer is the core of the library and is completely operating system 
independent. An example for this layer is the module FileIO which provides the data type 
FILE with associated procedures for unformatted, buffered I/O. A file is connected 
internally to a stream. If a buffer gets filled-up or flushed for example, it calls the 
appropriate procedures from BasicIO. 

layer 3: This layer is a collection of so-called utility modules. Module Forking for example 
provides procedures to create and execute new processes. This layer is some to some 
degree operating system dependent, i.e. it isn't possible for example to create processes 
on single user single tasking operating systems. 

1.4 The Interface Modules  

MCS is mainly a Unix/Modula-2 development system. Especially under UNIX all 
important libraries are written in C. One example is the X-Windows library Xlib. Hence it 
is necessary to interface Modula-2 programs to libraries written in so-called foreign 
languages. This is accomplished through so-called interface modules. 

Example: How to use the function getenv from the C library in Modula-2. 

getenv is defined in C as follows: 

char *getenv(name)
char *name;

Its interface module to Modula-2 has the following definition:

INTERFACE MODULE CGetEnv;                                        

  TYPE namptr = POINTER TO ARRAY[0..1000] OF CHAR; (* dummy high bound *) 

  PROCEDURE GetEnv EXTERNAL getenv(name:ARRAY OF CHAR):namptr;   
  (* GetEnv name in Modula world, getenv name in C world. *)     

END CGetEnv.                                                     

It is a fact that most programming languages under Unix have their own rules for 
parameter passing (i.e. different procedure calling conventions). To be able to call 
procedures written in foreign programming languages from Modula-2 in a comfortable 
way, MCS provides parameterisation of its Modula-2 compiler. The specification of the 
procedure calling and parameter passing mechanism is contained in a so-called 
language description file LDF. LDF configures the compiler to produce code, stack and 
register layout the way required by the foreign language. This includes different 
automatic type conversions if required. Hence, in the example module TestEnv below, it 
is posible to handle imported foreign objects as it would be Modula-2 objects: 

MODULE TestEnv;                                                  

  FROM CGetEnv IMPORT namptr, GetEnv;                                       
  FROM StdIO IMPORT Write, NL;                                              

VAR nam : namptr;                                            
  Write("s","TERM="); Write("s",nam^); NL;                       
END TestEnv.                                                     

Now compile cgetenv.def and testenv.mod:

mc cgetenv.def
(* if not otherwise specified, the language description file default.fld is used *)
mc testenv

Then link testenv:

ml testenv -llib:-lc

The Modula-2 linker links the Modula-2 part. If the linker detects foreign references it 
produces a relocatible output file and calls the ld program (under UNIX) to finish the task. 
The option -lc (standard C library) is passed to ld. 

1.5 Debugger Support  

The Modula-2 linker supports the format of the debbugers of the certain operating 
systems like sdb/adb under UNIX including the source level debugging facilities if there 
are any and if the manufacturer documents them. But because these debuggers do not 
support the Modula-2 language in a sufficient way MCS provides its own Modula-2 
source-level debugger which is part of MCS/W. 

1.6 The MCS/W Environment 

1.6.1 The Windows-System  

The above mentioned debugger together with the Modula-2 editor are building the MCS 
integrated environment. This environment is windows-orientied and operates on both 
ASCII-Terminals and under the X-Windows system. For this purpose there are two 
different windows-server programs called maw and mxw. Wenn working on an 
ASCII-terminal, one starts the maw program that simulates a windows-user-interface 
with almost the same functionality of an X-Windows system including the facility to have 
concurrently running shells in different windows. Working on X-Windows displays one 
starts the mxw program to integrate the debugger and editor in the X-Windows system. 

1.6.2. The Run-Time Debugger  

The Debugger is a run-time debugger having different windows for the source code, the 
disassembly, and for displaying the values of symbolic variables. Breakpoints can be set 
in both the source and the assembly-code window and one can single step statement- 
by-statement through the Modula-2 source code or instruction-by-instruction through the 
associated assembly-code. With the sync instruction of the debugger one can switch 
from the source window to the appropriate location in the assembler window and vice 
versa. With the print instruction one can display the values of variables, as well as whole 
structures (arrays and records). Subscription or dereferencing of arrays and pointers is 
also supported. Example: to examine a field of a record that is the i-th element of an 
array whose address is stored in pointer ptr one has to type ptr^[i].field. The debugger 
even allows to display linked lists and other dynamically allocated structures. 

1.6.3. The Multi-Windows Editor  

The editor is a multi-windows editor and is fully integrated with the compiler and linker. If 
the compiler encounters an error the cursor will automatically be positioned to the 
location of the first error in the source code. 

1.7 The MCS Implementation of Modula-2  

Version 4.3 of MCS already supports some of the emerging ISO-Modula-2 language 
extensions (refered to as the Standard below) as agreed upon at the 6th ISO-Modula-2 
SC22/WG13 meeting in 1991. What's not yet available is marked by \240 (not yet 
implemented) below. 

1.7.1. The Standard reduced the number of simple ordinal types. The only types left are 
INTEGER and CARDINAL, usually implemented as four byte words. Because this is not 
very efficient on for example 16 bit architectures the Standard permits that an 
implementation of subrange types may choose an alternate appropriate byte length. 

1.7.2. The Standard defines the set of pervasive (standard) functions and procedures 
precisely. Standard functions are now also allowed in constant expressions if their result 
may be computed at compile time. 

1.7.3. The SYSTEM module has been changed. A new type called LOC is defined in 
SYSTEM. This type represents the smallest accessable unit of the processor. The 
ARRAY OF LOC now plays the role of the formerly ARRAY OF WORD as the universal 
formal parameter. The existence of the type WORD is implementation defined. 

As in the Standard there is the type ADDRESS defined as POINTER TO LOC which is 
compatible to any pointer type. The Standard defines that this type is no longer 
compatible with CARDINAL type and no arithmetic operators are defined on it. Three 
functions ADDADR, SUBADR and DIFADR from SYSTEM must be used to perform 
address arithmetic. 

Also the Standard features a new binary or packed-set concept. For this purpose the 
new keyword PACKEDSET\240 is defined. With such a packed-set it is guaranteed that for 
example including element 5 means setting bit 5 in a variable of this type. Additionally 
there are new procedures SHIFT and ROTATE in SYSTEM operating on packed-sets. 
The predefined type BITSET is such a binary set type. 

1.7.4. The definition of coroutines and priority expression in the module header was 
changed dramatically in the Standard. Now there is a second compiler-internal module 
called COROUTINES, exporting procedures for creating and transfering of coroutines. 
There is a predefined enumeration type PRIORITY with at least two constants named 
INTERRUPTIBLE and NONINTERRUPTIBLE. Additionally two standard procedures 
ENTER and LEAVE are defined which are called automatically at procedures entry 
(begin) and exit (end) if the module header has a priority expression. Further it is 
possible to implement user defined ENTER and LEAVE procedures, which overlay the 
predefined functions. 

1.7.5. The Standard now permits multi-dimensonional open-array parameters. The 
pervasive function HIGH serves to get the dimensions of open-array parameters only. 

1.7.6. The Standard provides structured constants, value constructors, exception 
handling, module termination and structured function results.\240 

1.7.7. The Standard defines a rich I/O and conversions and string library.\240 

1.7.8. The Standard defines two new types COMPLEX and LONGCOMPLEX with 
associated operators and mathematical functions library.\240 

To achieve a certain degree of upward-compatibility the basic language modifications of 
the Standard are provided and most of the extensions are not yet implemented. The next 
major release of MCS will support the Standard library modules to be able to write 
portable Standard programs using a subset of ISO-Modula-2. Next is to add the 
language extensions step-by-step. 

2. Standard Functions and Procedures  

This chapter lists those procedures and functions that are predefined in Modula-2. 
Previously they were called standard function procedures. The Standard calls them 
pervasives. Most of these procedures are known as generic procedures, i.e. they are 
defined on different parameter types. In this context, the term "scalar types" used below 
UNSIGNED_16, UNSIGNED_32, pointer, enumeration and subrange types. In some 
cases the Standard is more restrictive than the MCS implementation. Two compilation 
options serve in forcing the compiler to flag non-Standard usage of pervasive functions, 
namley stdfunc+ or stdaddr+. 

INC(x), DEC(x) and 

INC(x,n), DEC(x,n); "Increment" and "decrement". For x, variables of all scalar types are 
 allowed. If n is not specified, the default is n=1. The expression n may be of the 
 signed or unsigned type. There is no test as to whether the range has been 
 exceeded.\240 For example, INC ("charvariable", "addressvariable"); causes the 
 "charvariable" to be incremented by the value "addressvariable MOD 100h" 
 (stdfunc+ x may be not a pointer type. stdaddr+ x and n may be not of type 

NEW(x),DISPOSE(x) and 

NEW(x,tag1,tag2,..),(DISPOSE(x,tag1,tag2,..); The compiler substitutes the call of NEW 
 or DISPOSE in the call of procedures ALLOCATE or DEALLOCATE, i.e. the use of 
 NEW and DISPOSE requires the visibility of the two procedures (mostly an IMPORT 
 of a storage module). The variable x is any type of pointer. If it is a pointer to a variant 
 record, then the tag fields can be specified as parameters, so that only the storage 
 locations actually required are allocated to the heap. 

CAP(x : CHAR) : CHAR; If 'a' <= x <= 'z', the corresponding capital letter is returned, 
 otherwise x itself. 

CHR(x) : CHAR; synonym for VAL(CHAR,x) 

ORD(x) : CARDINAL; synonym for VAL(CARDINAL,x) 

INT(x) : INTEGER; synonym for VAL(INTEGER,x) 

VAL("scalar+real type",x) : "scalar+real type expression"); General conversion function. 
 (stdaddr+ ADDRESS not allowed. stdfunc+ see VAL) 

FLOAT(x : scalar type+real type) : SHORTREAL; Conversion -> SHORTREAL. 

LFLOAT(x : scalar type+real type) : LONGREAL; Conversion -> LONGREAL. 

TRUNC(x : any real type) : CARDINAL; Conversion real type -> CARDINAL. 

ABS(x : "ordinal+real type") : "ordinal+real type" The absolute value of x is returned. 

ODD(x : scalar type) : BOOLEAN; TRUE is returned if x is odd (not an even number). 
 (stdaddr+ ADDRESS not allowed. stdfunc+ only signed and unsigned type allowed ) 

HIGH("arraytype") : LONGINT; The upper limit of an array is returned. If high is applied 
 to an open array parameter, the upper limit is always zero-normalized to the lower 
 limit. For multi-dimensional open-array parameter HIGH gives delivers each single 
 dimension as follows: first dimension = HIGH(x); second dimension = HIGH(x[0]); 
 third dimension = HIGH(x[0,0]); etc. 

INCL(x : "anysettype", basictype"); and 

EXCL(x : "anysettype", basictype"); "basictype" is any valid expression of the basic type 
 of the set. The corresponding element is removed from or inserted into the quantity x. 

SIZE(variable OR type):LONGINT; returns the size of a variable or type in units of LOC. 
 The variable may be not subscripted or dereferenced. SIZE is not allowed for open 

MIN(type): LONGINT or real type; 

MAX(type): LONGINT or real type; type may be any scalar or real type. Returned is the 
 minimum (maximum) value of that type. 

HALT; This procedure forces a program termination. 

PRIO():PRIORITY; returns the current priority. 

ENTER(PRIORITY); enters the specified priority. 

LEAVE(PRIORITY); restores the priority. The parameter must match the parameter of 
 the previous ENTER call. 

3. The Module SYSTEM  

The SYSTEM module is a pseudo-module, since it is an internal module known by the 
compiler. The objects exported by SYSTEM are magic in that they have to be imported 
from SYSTEM and can't be provided by regular module. This method was used because 
system-dependent data types and procedures are combined in SYSTEM. A program 
which imports from SYSTEM is processor and/or operating system-dependent. 

Data types exported by module SYSTEM: 


Function Procedures: 

INLINE(<constant expression> , <constant expression> , ...); This procedure serves for 
 machine-code programming. 

SETREG(num:CARDINAL; val:UNSIGNED_32); The processor register num is loaded 
 with val. The register number is processor dependent. 

REGISTER(num : CARDINAL) : UNSIGNED_32; The contents of the process register 
 "num" are returned, "num" being specified in exactly the same way as in SETREG. 

LABEL(name); sets an named label that may be referenced in a CONDBRANCH or 

CONDBRANCH(branchcode, label); By help of this procedure you can construct 
 conditional branch instructions more comfortable then with an INLINE call. The 
 branchcode is the code of the processors branch instruction in such a form the 
 compiler would create as result of compare or test statements without branch 
 optimizing. This means for example for the 680x0 processor a two byte sequence 
 describing a conditional branch with two byte offset. If the branch optimizer is on the 
 compiled branch as a result of the CONDBRANCH call is optimized too, means 
 converted to a short form, if possible. 

UNCONDBRANCH(branchcode, label); The same for unconditional branches. 

REFERENCE(variable or procedure, offset); By help of this procedure you can reference 
 procedures or global variables whose address is known not before link time. The 
 optional second argument is a constant expression, that creates an offset added to 
 the address of the variable or procedure. For example: 

        CONST   jsr=<code of subroutine call>   
        INLINE(jsr); REFERENCE(proc1);

ADR(variable) : ADDRESS; Supplies the memory address of "variable". 

ADDADR(ADDRESS-expression, offset: LONGINT) : ADDRESS; 

SUBADR(ADDRESS-expression, offset: LONGINT) : ADDRESS; Two of the three 
 functions to perform address arithmetics. The value of the address expression is 
 incremented or decremented by offset and the result is returned. 

DIFADR(ADDRESS-expression, ADDRESS-expression) : LONGINT; The two address 
 expressions are subtracted; the result is converted to LONGINT and returned. 


TSIZE(type,tag1,tag2,...) : LONGINT; "type" can be any type name. The storage 
 requirements are returned in units of LOC. In the event of the "type" describing a 
 variant record, the second form is appicable, which causes the total storage 
 requirements of the type specified by the given tag fields to be returned. 

CAST(any type, any expression); The type transfer function. 

SHIFT(binary set variable, shiftcount); The bits of the binary set variable are shifted 
 "shiftcount" places to the left if shiftcount > 0, otherwise to the right. 

ROTATE(binary set variable, shiftcount); The same like SHIFT but rotation of the bits. 

If you set the compiler switch stdsys- (default state) then it is not necessary to import 
you may. 

4. The Module MATHLIB  

There is a further compiler-build-in module called MATHLIB. MATHLIB exports the 
following transcendental functions: 

SIN, COS, TAN the basic transcendental functions 

ASIN, ACOS, ATAN and their reverse functions 

SINH, COSH, TANH the hyperbolic functions 

ASINH, ACOSH, ATANH and their reverse functions 

LG the logarithm to the basis ten 

LN the natural logarithm 

EXP the exponential function 

SQRT the sqrt function 

POWER POWER(x,y) -> x**y 

The exported functions are generic, that means that you can use them with any of the 
three real types: 

  FROM MATHLIB IMPORT                                            
      SIN, SQRT;                                                 

      r1,r2 : REAL;                                              
      b1,b2 : BCD;                                               

END ...                                                          

We have choosen this construction to allow the code generator to produce inline code if 
the hardware supports that. If there is no hardware then the code generator produces 
calls to the appropriate run time modules rMATHLIB, lMATHLIB and bMATHLIB which 
do a software emulation of the above functions. 

5. Library Modules  

The definition modules supplied with MCS are listed below. (Editors note: The format of 
the procedure parameters and comments was changed to fit the publication format 
requirements of The ModulaTor) 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 



PROCEDURE ALLOCATE (VAR ptr:ADDRESS;size:LONGCARD); (* allocates on word 
 boundaries aligned space of the amount of size. ExtendHeap is called automatically, if there 
 is not enough memory. *) 

PROCEDURE DEALLOCATE(VAR ptr:ADDRESS;size:LONGCARD); (* frees the space on 
 which ptr points, never free memory, that isn't allocated. *) 

VAR HeapTrap : PROC; (* user may supply this variable with his own trap routine. *) 


END Storage. 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 



(* Module BasicIO is the lowest level in the hierarchy of I/O modules, its implementation must 
 handle two tasks: the first is to establish the chain to the I/O procedures of the operating 
 system. In UNIX this is very easy, because the procedures of BasicIO are di- rectly 
 supported. In some other operating systems, like CP/M-68k there is to add some code, 
 because e.g. file I/O is done only in units of 128 bytes. The second task that must be done by 
 BasicIO is to allow to create your own devices, which you can use in the same manner as the 
 files and devices supported by the operating system, that means you can use all the 
 procedures of the I/O library to read and to write from/to the newly created device. You can 
 use this ability not only to integrate a new physical device, but also to create a new logical 
 device, which uses a device that is supported by the operating system in a different manner. 
 For example you can create a logical device "protocol" that writes both to the standard output 
 and to a file. *) 

TYPE FileMode = (Create,R,W,RW,Append,Execute,Exclusive, Dir,Xpath); (* os9 special 
 meanings: Execute: execution file attribute is set. Exclusive: file is opened for non sharable 
 use. Dir: a directory file is opened or created. Xpath: if the pathname is not absolute, then the 
 relative pathname goes up from the current execution directory *) 

OpenMode = SET OF FileMode; 




readfct = PROCEDURE(STREAM,ADDRESS,LONGCARD): LONGCARD; writefct = readfct; 






DevFunctions = RECORD open : openfct; close : closefct; read : readfct; write : writefct; ioctl : 
 ioctlfct; seeklong : seeklongfct; getpos : getposfct; lookup : lookupfct; remove : removefct; 

stream = RECORD channel : ADDRESS; next : STREAM; func : DevFunctions; END; 

(* STREAM is returned by a sucessful Open, Open allocates first a stream record on the heap, 
 then the list of all known devices is scanned for a name match, if the name parameter of Open 
 holds a device extension. If not, it is assumed that name is a device/file that is known by the 
 operating system. A pointer to the corresponding Dev- Functions Record is stored in 
 stream.func. Next the procedure is called and their return value is stored 
 in On a not sucessful open NIL must be returned. In this case all allocated 
 records are deallocated and STREAM(NIL) is returned and the errorcell in the processfield is 
 set to SystemError. *) 

VAR In,Out,Err : STREAM; (* the three standard channels opened by the UNIX system normally 
 assigned to the terminal. *) 

CONST BSChar = 10C; (* backspace character *) 

EOFChar = 33C; (* end of file character *) 

EOL1st = 15C; (* return *) 

EOL2nd = 377C; (* 0xff means: in this operating system line termination is done by a single 
 character (EOL1st) *) 

BlockSize = 256; (* Block size in block orientied devices *) 

Ok = 0; SystemError = 1; UseError = 2; (* error constants *) 

(* all procedures in this module set the errors according to error constants. Ok is set on sucess *) 


PROCEDURE Close(VAR x:STREAM); (* The name parameter of Open has the following 
 form: "['devicename'/]unitname". The device extension is optional and must be a known 
 device created by CreateDevice. If there is no extension then it is assumed, that name holds a 
 device or file that is known by the operating system. Openmode sets the access rights of the 
 channel. NOT(Create IN mode) means the file must exist and if Create IN mode then the file 
 is created or if already existing he is truncated. "R" stands for read "W" for write and "RW" 
 for both permissions. Append means the file is opened and the file- pointer is adjusted at the 
 end of the file. If mode contains nonsense like OpenMode{Create, R,RW} the Open will fail 
 and UseError is set. For example: 

stream1:=Open("/usr/modlib/new",OpenMode{Create, RW}); file /usr/modlib/new is created 
 with read and write permissions. 

stream2:=Open("'ramdisk'/xyz",OpenMode{R}); ramdisk must have been created by 
 CreateDevice, and the existing unit xyz is opened for reading. 

stream3:=Open("'serialport'",OpenMode{RW}); the device serialport, created by CreateDevice is 
 opened, there is no unitname passed, because it is not a muliple file device. 

stream4:=Open("serialport",OpenMode{Create,RW}); a file named serialport is created. *) 

TYPE errsrc = (readerr, writeerr, closeerr); 

errorhandler = PROCEDURE(STREAM, errsrc); 

VAR ErrorHandler : errorhandler; (* Whenever an error occurs if you are operating on a 
 STREAM, then the errorhandler is activated. BasicIO assigns the default error handler, but 
 you can alter this by assignment to ErrorHandler. *) 


 bytes are read from/written to the buffer buf. The amount of the really read or written bytes 
 are returned. *) 

TYPE OffsetMode = (FromCurrentDown,FromCurrentUp,FromBegin); 

PROCEDURE Seek(x:STREAM;pos:LONGCARD;offset:OffsetMode); 

PROCEDURE SeekLong(x:STREAM;block,byte:LONGCARD); 

PROCEDURE GetPos(x:STREAM;VAR block,byte:LONGCARD); (*Block is always measured 
 in units of BlockSize. *) 

 control the behavior of the devices, for details see your operating system manuals. 
 Additonally this procedure is defined for all devices with func=NIL. In this case param is set 
 to TRUE if the device is a character special device. This is important, when you create your 
 own devices with CreateDevice. *) 

PROCEDURE CreateDevice (name : ARRAY OF CHAR; open : openfct; close : closefct; read : 
 readfct; write : writefct; ioctl : ioctlfct; seeklong : seeklongfct; getpos : getposfct; lookup : 
 lookupfct; remove : removefct ); 

(* used to create your own logical devices or to integrate new physical devices. If created they 
 can be used in the same manner as the files and devices supported by operating system. *) 

PROCEDURE LookupFile( name:ARRAY OF CHAR):BOOLEAN; (* returns true if a device or 
 file is existing. *) 

PROCEDURE RemoveFile( name:ARRAY OF CHAR); (* the device or file name is deleted. 
 The name parameter in LookupFile and RemoveFile obeys the same rules as the name 
 parameter in Open. *) 

TYPE TerminalMode = (StringEcho,CharEcho,StringBlank, CharBlank); 

PROCEDURE SetTerminal(s:STREAM;mode:TerminalMode); (* if the stream is connected with 
 a terminal then the specific mode is set. "Blank" means input characters are not echoed, if 
 "String" is chosen then a whole string until <cr> is read in by ReadBytes, "Char" means 
 ReadBytes returns always after a single character input. *) 

END BasicIO. 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 

DEFINITION MODULE FileIO; (* buffered unformatted I/O *) 



CONST Ok = 0; SystemError = 1; UseError = 2; 

(* all procedures of this module set the results in the same manner like the module BasicIO: 
 Ok,SystemError,UseError. *) 


VAR StdIn,StdOut,StdErr : FILE; 

(* FILE indicates the buffered I/O channel, a FILE is always connected with an underlaying 
 STREAM. StdIn, StdOut, StdErr are opened automatically and connected with In, Out, Err. 

TYPE FileMode = BasicIO.FileMode; 

OpenMode = BasicIO.OpenMode; 

PROCEDURE FOpen(name:ARRAY OF CHAR; mode:OpenMode; 

PROCEDURE FClose(VAR f:FILE); (* used to open and close a buffered I/O channel, buffersize 
 is the length of the buffer in units of BasicIO.BlockSize. If buffersize=0 then the the data are 
 read/written directly from/to the underlaying stream. *) 


PROCEDURE FReadWord(f:FILE; VAR ch:WORD); (* get or put the smallest accessible unit 
 of the target processor. *) 


 or write n WORD's to the buffer, the really read or written amount of WORD's is returned. *) 


PROCEDURE FWriteRec(f:FILE; rec:ARRAY OF WORD); (* read or write any variable 
 BasicIO. EOFChar is returned, when reading over the end of File. *) 

PROCEDURE UngetWord(f:FILE; ch:WORD); (* ch is written back to the file, so that a 
 following read operation returns this character. *) 

TYPE OffsetMode = BasicIO.OffsetMode; 

PROCEDURE FSeek(f:FILE; position:LONGCARD; offset:OffsetMode); 

PROCEDURE FSeekLong(f:FILE; blocks,bytes:LONGCARD); 

PROCEDURE FGetPos(f:FILE; VAR blocks,bytes:LONGCARD); 

PROCEDURE Reset(f:FILE; mode:OpenMode); (* When you open a file with reading and 
 writing permissions then there is some overhead, because it must be possible to read and 
 write in mixed order. If your problem allows, then it is more efficient to open the file first 
 either for reading or writing. To change the filemode and to adjust the internal filepointer to 
 the beginning of the file for a second run, you can use the Reset procedure. *) 

PROCEDURE FEOF(f:FILE):BOOLEAN; (* returns TRUE if a previous read will have read 
 over the physical end of file. *) 

PROCEDURE FGetStream(f:FILE):BasicIO.STREAM; (* returns the STREAM belonging to the 
 FILE f. Only for special purpose. Be carefull, you will get a great confusion, if you try to 
 access the FILE and the underlaying STREAM both with their specific read and write 
 procedures in a mixed order. *) 

PROCEDURE Flush(f:FILE); (* the buffer is immediately written to the underlaying stream. 
 Only for special use. *) 

PROCEDURE FSkipLine(f:FILE); (* skips the rest of the input line if and only if the FILE is 
 associated with a terminal. There is no direct use of this procedure, but the I/O layers above 
 like TextIO needs it to avoid strange results on terminal IO. *) 

END FileIO. 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 

DEFINITION MODULE ETextIO; (* buffered formatted I/O on FILE *) 



(* Error constants returned by ErrorSystem.Result *) CONST Ok = 0; SystemError = 1; UseError 
 = 2; FormatError = 3; 



PROCEDURE UngetCh(f:FILE;ch:CHAR); (* ch is written back to the file, so that a following 
 read operation returns this character. *) 

PROCEDURE FReadLn( f:FILE;VAR buf:ARRAY OF CHAR); (* buf is filled with chars read 
 in from FILE until EOL1st or EOFChar is detected or more characters then HIGH(buf) have 
 been read in, in this case rest of line is skipped. All characters < ' ' except ^I are ignored. *) 

PROCEDURE FNL(f:FILE); (* EOL1st and also EOL2nd, if EOL2nd#377B, is written to FILE. 


 synthax of the formatstring is as follows: "type,b=number,w=number,d=number" Type must 
 always be specified, b(base), w(field- width) and d(decplaces) are optional and are set to 
 default values, if they are not given. 

type: s : strings, f : real numbers in floating point format, r : real numbers in fixpoint format, i : 
 integers, longints, c : cardinals, longcards, a : address, bf: bcd numbers in floating point 
 format, br: bcd numbers in fixpoint format, 

base : base of numbersystem 2 < base < 36, only used if type is integer,address or cardinal 

width: field-width of output, if width > 0 : output is right adjusted, if width < 0 : output is left 
 adjusted, if width = 0 : output field is as big as necessary. 

If used for input then all characters are get from input stream until a character <=' ' is encountered 

decplaces : decimal places for float, real; If zero they are printed in integer format. Amount of 
 digits for int, card, addr: leading '0' characters are added if necessary. If decplaces=0 then 
 only the significant digits are printed. 

decplaces is used for str and for input 

default values : b=10, w=0, d=0 for integer, address and cardinal. d=7 for float. d=4 for real 

NOTE: because FRead skips all leading characters <=' ' its not possible to read an empty string. In 
 this case use procedure FReadLn. *) 

PROCEDURE CloseText(f:FILE); (* because some operating systems mark the end of a text file 
 with a special EOF character, this procedure is necessary. It writes EOFChar's from the 
 current position of the internal filepointer up to the current block end. After this FClose is 
 called. Be carefull if you have used FSeek, the EOFChars are written up from the current file 
 position. *) 



(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 

DEFINITION MODULE EStdIO; (* formatted output and input on StdOut and StdIn *) 


PROCEDURE ReadCh(VAR ch:CHAR); (* FReadCh(StdIn,ch) *) 

PROCEDURE ReadLn(VAR buf:ARRAY OF CHAR); (* FReadLn(StdIn,buf) *) 

 FWrite(StdIn,format,z) *) 

PROCEDURE WriteCh(ch:CHAR); (* FWriteCh(StdOut,ch) *) 

PROCEDURE NL; (* FNL(StdOut) *) 

 FWrite(StdOut,format,z) *) 




(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 

DEFINITION MODULE ErrorSystem; (* reentrant error system, errors are stored in 
 process field *) 

PROCEDURE Result():CARDINAL; (* returns the error *) 

PROCEDURE SetResult(x:CARDINAL); (* set the error *) 


PROCEDURE SetSysResult(number:CARDINAL); (* if your operating system returns errors on 
 a not sucessful systemcall, you can get the errornumber with SysResult. *) 

END ErrorSystem. 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 



PROCEDURE Compare(string1,string2:ARRAY OF CHAR):INTEGER; (* compares 'string1' 
 and 'string2' and returns 0 if 'string1' = 'string2', 1 if 'string1' > 'string2'. -1 if 'string1' < 
 'string2' *) 

PROCEDURE Concat(string1,string2:ARRAY OF CHAR; VAR result:ARRAY OF CHAR); (* 
 adds string1 and string2, result may be also string1 *) 

 CHAR); (* copies length characters from string[at] to result *) 

PROCEDURE Delete(VAR string:ARRAY OF CHAR; at,length:CARDINAL); (* deletes length 
 characters beginning at string[at] *) 

 at:CARDINAL); (* NOTE: Insert("xyz",x,Length(x)) adds "xyz" at the end of string *) 


 at:CARDINAL); (* adds addstr at position at, rest of str is deleted *) 

 pos:CARDINAL):BOOLEAN; (* same as SearchPos for searching characters *) 

PROCEDURE SearchPos(subString,string:ARRAY OF CHAR; at:CARDINAL; VAR 
 pos:CARDINAL):BOOLEAN; (* starts at position at, pos holds the position of the first 
 occurence of subString if there is a match, if not then pos holds the first position after the 
 string end and FALSE is returned. *) 

END Strings. 


(* Modula-2 Cross System V4.0 

Author: Dieter J~ager, date: 6/1988 *) 


(* string arguments cannot have any leading or trailing blanks, 2 <= base <= 36 ==> possible 
 digits '0'..'9', 'A'..'Z'*) 




 VAR sucess:BOOLEAN); 

END ConNum. 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 


PROCEDURE RealToStr( real : REAL; decPlaces : INTEGER; VAR str : ARRAY OF CHAR); 
 (* if decplaces negative then scientific notation, if zero no decimal point. *) 

PROCEDURE StrToReal( str : ARRAY OF CHAR; VAR real : REAL; VAR success : 

END ConReal. 


(* Modula-2 Cross System V4.2 

Author: Stefan Wertgen, date: 6/1988 *) 


PROCEDURE LRealToStr( lreal : LONGREAL; decPlaces : INTEGER; VAR str : ARRAY OF 
 CHAR); (* if decplaces negative then scientific notation, if zero no decimal point. *) 

PROCEDURE StrToLReal( str : ARRAY OF CHAR; VAR lreal : LONGREAL; VAR success : 

END ConLReal. 


(* Modula-2 Cross System V4.2 

Author: Stefan Wertgen, date: 6/1988 *) 


PROCEDURE BCDToStr( bcd : BCD; decPlaces : INTEGER; VAR str : ARRAY OF CHAR); (* 
 if decplaces negative then scientific notation, if zero no decimal point. *) 

PROCEDURE StrToBCD( str : ARRAY OF CHAR; VAR bcd : BCD; VAR success : 



(* Modula-2 Cross System V4.0 

Author: Dieter J~ager, date: 6/1988 *) 

DEFINITION MODULE RegExpression; (* string - regular expression comparision. 
 The same algorithm for regular expressions as in the UNIX shell is used. *) 

PROCEDURE Match(expression,string:ARRAY OF CHAR):BOOLEAN; (* 'Match' returns 
 TRUE if there is a match between the regular expression 'expression' and the string 'string'. 
 There are the following meta characters with special meaning: '?' matches any single 
 character, '*' matches any string, '[' begin group, ']' end group, '!' if the first character after '[' 
 then there will be a match for all characters excluding the group characters, '-' until character. 

Some examples for a legal group: [ab] matches 'a' or 'b' in string, [a-nz] matches all characters 
 between 'a' and 'n' and 'z' in string. 

'\' escape character for the meta characters. Examples: 

'*' matches any string, '???' matches any three character string, 'a*b' matches any string that 
 begins with 'a' and ends with 'b', '\\*[\*-\-]' matches any string beginning with '\' and ending 
 with '*' until '-' ('*' or '+' or ',' or '-'), '*[*--]' illegal regular expression Match returns FALSE, 
 '[]' matches no character. 

There is one special case because of the fact that the procedure match is used in the first instance 
 to compare a regular expression with a path name: '/' this character is never included in a '*' 
 string, so that there can't be an overrun over the single directory elements of a whole path 

For example: /dd/* matches not /dd/xx/yy, /dd/????? matches not /dd/xx/yy. This rule does not 
 apply for a '**' string. 

For example: **.lst will match any file with the ending string '.lst'. *) 

END RegExpression. 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 


 CHAR):BOOLEAN; (* get the string value of an environment variable *) 

END Environ. 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 

DEFINITION MODULE Files; (* this module is in certain aspects dependent of the operating 
 system. This is the version for OS9. *) 


TYPE Attributes = (read,write,exec,pread,pwrite,pexec, nonshared,dir); 

AttrSet = SET OF Attributes; 

FileName = ARRAY[0..27] OF CHAR; 

Date = RECORD year,month,day,hour,minute : CARDINAL; END; 

FileInfo = RECORD CASE accessible : BOOLEAN OF TRUE : Attr : AttrSet; OwnerID : 
 CARDINAL; Modify : Date; Size : LONGCARD; | FALSE : END; END; 

Entry = RECORD name : FileName; status : FileInfo; END; 

DirInfo = POINTER TO ARRAY[0..10000] OF Entry; (* dummy high bound *) 

PROCEDURE ReadFileInfo(s:STREAM; VAR info:FileInfo); (* get the info from the opened 
 STREAM s *) 

PROCEDURE GetFileInfo(filename : ARRAY OF CHAR; VAR info : FileInfo); (* info holds 
 the status information of filename *) 

PROCEDURE GetDirInfo(dirname : ARRAY OF CHAR; VAR found : BOOLEAN; VAR info : 
 DirInfo; VAR size : LONGCARD); (* if found then info holds a pointer to an array of 
 entries, allocated on heap of the size of the size parameter. The highest index of this Array is 
 size DIV TSIZE(Entry) -1. If the directory is empty info=NIL and size=0 is returned. If the 
 array is not longer used, don't forget to free the heap space with DEALLOCATE(info,size). 

PROCEDURE ExtendPath(path,name:ARRAY OF CHAR; VAR newpath:ARRAY OF CHAR); 
 (* connects path and name to the newpath. For example: "lib/modlib" and "basicio.mod" 
 gives "lib/modlib/basicio.mod". This procedure is very useful, if you will recursively scan a 
 file structure.*) 

END Files. 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 

DEFINITION MODULE forking; (* this module is in certain aspects dependent of the 
 operating system. This is the version for OS9. *) 


TYPE ForkType = (fork,chain); 

VAR ShellName : ARRAY[0..19] OF CHAR; (* default set to "shell", can be altered so that other 
 shell programs like the UNIX compatible "sh" can be forked, if calling the system procedure. 

PROCEDURE Load(modname:ARRAY OF CHAR):BOOLEAN; (* Tries to load modname. IF 
 it is not an absolute pathname then Load tries to load from the current execution directory, if 
 this fails, then the pathes in environment variable PATH are searched. On sucess TRUE is 
 returned and modname is changed to the name of the loaded module. *) 

PROCEDURE ForkArg(type : ForkType; name : ARRAY OF CHAR; argv, envp : ADDRESS; 
 addMem : LONGCARD; priority, nbrPath : CARDINAL):CARDINAL; (* The first form to 
 fork/chain programs. "name" is the modul name or file name of the called program. "argv" is 
 a pointer to an array to pointers to null terminated argument strings. argv^[0] is reserved for 
 the program name, but must not be supplied, because it is automatically assigned to "name". 
 The last not used pointer must be a null pointer. envp is like argv, but for the environment 
 strings. Normally EntryExit.environ is passed through. The last four parameters are identical 
 to those of the F$Fork system call. If the F$Fork system call is not sucessfull, then the pathes 
 in the environment variable PATH are used in trying to load the program. The child id is 
 returned or 0ffffh on error. *) 

PROCEDURE ForkStr(type : ForkType; name : ARRAY OF CHAR; paramstr : ARRAY OF 
 CHAR; addMem : LONGCARD; priority, nbrPath : CARDINAL):CARDINAL; (* The same 
 as ForkArg but with the difference of passing a whole argument string. The characters "'" or 
 '"' can be used to pass a whole string as one parameter. The amount of passed arguments is 
 limited to a number of 20. *) 

PROCEDURE System(commandstr:ARRAY OF CHAR):CARDINAL; (* A shell is forked to 
 execute the commandstr. After this a F$Wait systemcall is made. 

NOTE: all passed strings must be null terminated!! *) 

END forking. 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 

DEFINITION MODULE Termcap; (* used to get the entries of the termcap file *) 


VAR Baudrate : CARDINAL; (* default value is 9600. You can get the used baud rate from the 
 the returned baud rate code by the IGetStt systemcall. *) 

PadChar : CHAR; (* default value is 0C. This Character is send to construct a needed delay time. 

PROCEDURE OpenTermcap(TermcapFile,EntryName: ARRAY OF CHAR):INTEGER; (* The 
 first step is to call OpenTermcap. TermcapFile is the name of the termcap file and 
 EntryName is the name of your terminal. You can get this names with the procedure 
 Environ.GetEnv from the environment variables "TERM" and "TERMCAP". OpenTermcap 
 searches the corresponding Entry and stores the capability strings for the later calls of 
 TGetString. The "tc" entry is managed automatically. OpenTermcap returns a -1 if the 
 termcap file is not found, a 0 on success and a 1 if there was a format error. *) 

 num:CARDINAL):BOOLEAN; (* the cap parameter must be supplied with the requested 
 capability string. TGetString returns TRUE if the entry was found and additionally in 
 CapString or num the value if the capability is of type string or numeric. If the actual 
 parameter for CapString is too short, then TGetString returns also FALSE. Store the returned 
 values for a later use with procedures TGoto and TPut. *) 

PROCEDURE CloseTermcap; (* must to be called after extracting all needed values with 
 procedure TGetString for file closing and Heap recovery. *) 

PROCEDURE TGoto(f:FILE; cm:ARRAY OF CHAR; x,y:CARDINAL); (* used for direct 
 cursor motion to position x,y. cm is the returned CapString by TGetString when called with 
 "cm" for the cap parameter. f is the file on which the controlstring is written to, usually 
 StdOut. *) 

PROCEDURE TPut(f:FILE; AffectedLines:CARDINAL; in:ARRAY OF CHAR); (* the same 
 like TGoto for the other capabilities. The parameter AffectedLines is used for sending the 
 correct amount of pad characters if the delay time of the capability is dependent of the 
 amount of affected lines. *) 

END Termcap. 


(* Modula-2 Cross System V4.2 

Author: Dieter J~ager, date: 6/1988 *) 

DEFINITION MODULE Screen; (* high level module for crt control purpose, based on 
 module Termcap. The initialisation of Screen reads the termcap file from the path specified 
 in the environment variable TERMCAP or if not set from /dd/sys/termcap. This module 
 offers the mainly needed screen control functions in a more comfortable way as the basic 
 module Termcap. Details: is, ti and te capabilities automatically supported, for this 
 EntryExit.Deiniz is used. is and te is send to StdErr, assumed that it is not redirected. If your 
 Terminal needs delay times then you must set Termcap.Baudrate to the correct value. *) 


TYPE Capabilities = (ClrEol, (* clear to end of line *) ClrEos, (* clear to end of screen *) 
 ClrScreen, (* cursor home and clear *) inverse, (* begin stand out mode *) normal, (* end 
 stand out mode *) cm); (* cursor movement, Gotoxy *) 

CapSet = SET OF Capabilities; 

Controls = [ ClrEol..normal ]; 

ErrorCode = (Ready,NoTERM,NoTermcapFile,NoEntry); 

VAR Lines, (* number of lines of your Terminal *) 

Columns, (* number of columns *) 

BlChars : CARDINAL; (* number of blank characters left by se or so (normal, inverse) *) 

Supported : CapSet; (* supported capabilities of your Term. *) 

Error : ErrorCode; (* possible Error in reading termcap. All this variables are supplied by 
 initialisation of Screen. *) 

OutFile : FILE; (* the file on which Gotoxy and Send will write to. Default set to StdOut *) 

PROCEDURE Gotoxy(x,y:CARDINAL); (* direct cursor movement *) 

PROCEDURE Send(code:Controls); (* for the other capabilities *) 

END Screen. 


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, 14-Jul-1998