Most implementations of Modula-2 for UNIX depend on the C-library
to access system calls.
Ulm's Modula-2 compiler neither supports nor needs an
access to system calls via foreign libraries (i.e. the standard
C-library /lib/libc.a).
Instead, SYSTEM exports three function procedures
which interface all system calls:
PROCEDURE UNIXCALL(syscall: CARDINAL; VAR r0, r1: INTEGER; ...) : BOOLEAN; PROCEDURE UNIXFORK(VAR pid: CARDINAL) : BOOLEAN; PROCEDURE UNIXSIGNAL(signo: CARDINAL; p: PROCEDURE; VAR old: PROCEDURE; VAR result: CARDINAL) : BOOLEAN;
UNIXCALL interfaces all system calls with the exception of fork(2) and sigaction(2) which have a different calling scheme (on the machine instruction level). The parameter syscall must be a constant number (see Sys(3) or /usr/include/sys/syscall.h for valid numbers). Some system calls expect under some UNIX-variants one or two parameters in some specific registers or, more often, return some values in these registers:
Platform | OS Release | registers |
Concurrent 3200 Series | XELOS R02 | registers 0 and 1 |
Nixdorf Targon/31 | System V.3 | d0 and d1 |
Sun-3 Workstation | SunOS 4.1.x | d0 and d1 |
SPARC architecture | Solaris 2.x | o0 and o1 |
Any number of additional parameters may be passed as additional arguments. Note, however, that the compiler does not care about compatibility issues here. Strings and buffers, for example, must be passed by address and not as dynamic arrays. UNIXCALL returns FALSE in case of errors. All UNIX-architectures we know of (including those platforms which has been listed above) return the error code (see errno(2)) in the first register (VAR-parameter r0). In case of success, some results may be returned into the VAR-parameters r0 and r1. read(2), for example, returns the number of bytes read in r0, and pipe(2) returns (on many platforms but not all) both file descriptors in r0 and r1.
Following example is taken from the associated library and shows the implementation of SysWrite on the Solaris 2.x system (which runs in identical form on all other platforms mentioned above):
IMPLEMENTATION MODULE SysWrite; FROM Errno IMPORT errno; FROM Sys IMPORT write; FROM SYSTEM IMPORT UNIXCALL, ADDRESS; PROCEDURE Write(fd: CARDINAL; ptr: ADDRESS; VAR bytecount: CARDINAL) : BOOLEAN; VAR r0, r1: CARDINAL; BEGIN IF UNIXCALL(write, r0, r1, fd, ptr, bytecount) THEN bytecount := r0; RETURN TRUE ELSE errno := r0; bytecount := 0; RETURN FALSE END; END Write; END SysWrite.
UNIXFORK is already interfaced by SysFork and should not be used anywhere else. Note that in case of failures the error code is returned in pid. In case of success, pid will contain the process id of the son (for the father process) or 0 (in the child process).
Similarly, UNIXSIGNAL is interfaced by SysSignal
and should not be called in other modules. The parameters
signo and p specify which parameterless procedure
is to be called on which signal number. The old signal
handler is returned in old and, in case of failures,
the error code is returned in result together with
a return value of FALSE.
Note that UNIXSIGNAL does not offer all facilities
of sigaction(2) (under Solaris 2.x) in their full generality
but at least UNIXSIGNAL and SysSignal work on
all our implementations without changes.