Modula-2 for VAX OpenVMS 7.3
My VAXstations now have Modula-2: the Hamburg VAX-11 Modula-2 compiler, as a kit for OpenVMS VAX 7.3, which compiles itself. It is Niklaus Wirth’s language in the implementation of the DBPL group at the University of Hamburg (after the ETH Zurich compiler for the PDP-11): a native compiler that is written in Modula-2, produces ordinary VAX object files for the linker, and comes with foreign definition modules for the VMS system services, RMS, the run-time library and SMG. The kit is a BACKUP saveset with a DCL installer and a DCL uninstaller; it needs nothing but the DEC tools every VMS has (LINK and BACKUP on the target; MACRO and LIBRARIAN only to rebuild it).
Claude Code assisted, which is to say, did much of the hard work.
Downloads, with SHA-256 sums in SHA256SUMS.TXT:
- MODULA2_VAX_20261011.BCK (2.9 MB): the kit to install.
- MODULA2_INSTALL.COM and MODULA2_UNINSTALL.COM.
- README.TXT, which says everything below about installing in more detail (and the licence situation below, in the same words), and SHA256SUMS.TXT.
- Source kit (438 KB): the 154 text files of the DECUS V3.2 distribution exactly as shipped, where they came from with hashes, the one patch, the build and self-hosting procedures, the host tools, and the recipe to recreate the kit from scratch.
- Examples and tests: the programs shown below, their inputs, and expected output.
- The file list of the saveset: MODULA2_VAX_20261011_LIST.LIS.
Credits
The Hamburg compiler’s files carry the authors’ names, J. Koch, M. Mall, P. Putfarken, H. Eckhardt, for the group of J. W. Schmidt, and the grant that paid for it (DFG, Schm 450/2-1). The project’s own page once said stable systems for VAX/VMS were available on request, and bitsavers.org and the DECUS VAX SIG library have carried the files for decades.
Everything that is
mine (the installer, the uninstaller, the setup procedure, the examples,
the build procedures) is marked as mine; the one change to a Hamburg file
is a diff of the MODULA command definition, in the source kit.
Which Modula-2
Modula-2 on the VAX is a story of several compilers, and most of them were products. What I could actually get, and run on VMS, was one:
- The Hamburg compiler, version 3.1 on a 1983 tape image at bitsavers
(the tape has the sources and the object code), and version 3.2 in the
DECUS VAX SIG library, a 1989 VMSINSTAL kit holding the same system as
released in 1985-1988. The compiler’s banner reads
VAX-11 Modula-2 V3.2-25. - DEC’s own Western Research Lab Modula-2 was announced for VMS “soon” in 1990; what exists is for Ultrix and BSD. ModulaWare’s MVR was a product. MOCKA is for Linux. Modula-3 is another language.
This kit is the V3.2 distribution. The V3.1 tape is there as a baseline: I read it on Linux and on the simulator, and its compiler runs too.
What you get
$ @SYS$COMMON:[SYSLIB.MODULA2]MODULA2_SETUP
Modula-2 (Hamburg VAX-11 Modula-2 V3.2-25) is ready: MODULA, PRETTY, EBNF
$ MODULA HELLO
$ LINK HELLO
$ RUN HELLO
Hello from Modula-2 on OpenVMS VAX
MODULAis a DCL verb (fromMODULA.CLD) with/LIST,/LOG,/CROSS_REFERENCE,/DEBUG,/MACHINE_CODE,/NOCHECK,/QUERY. A definition module compiles to a.SYMfile (the compiler reads these for every import), an implementation or main module to a.OBJ.- The run-time library
MODULA.OLBis found throughLNK$LIBRARY, so a plainLINKworks.InOut,Terminal,FileSystem,Storage,Conversions,MathLib0..3,DateTime,Measures,RMSFiles, and Wirth’sProcessScheduler. - Foreign definition modules (
%FOREIGN DEFINITION MODULE) describe system services and run-time library routines, with parameter modes%IMMED,%REFand%STDESCR. The kit hasVMS(system services),RMS, the...Definitionsconstant sets,ScreenManagementProcedures(SMG),StringHandlingProcedures,CommonInputOutputProceduresand more. Writing your own takes ten lines (below). PRETTYis a Modula-2 source pretty printer andEBNFa grammar checker, both written in Modula-2, as is the compiler.- Reserved words and standard identifiers are upper case, identifiers are case sensitive, and the first nine letters of a module name are its file name.
It compiles itself
The compiler’s sources are in the distribution. Compiling all of them, with the compiler that is shipped (as object code), takes 22 seconds on the simulator; the result is a compiler that I can use to compile all of them again:
| Stage | MODULA.EXE | differs from the previous stage |
|---|---|---|
| The shipped object code, relinked | 180,736 bytes | – |
| 1: compiled by the shipped compiler | 180,224 bytes | many blocks (older compiler versions made the shipped objects) |
| 2: compiled by stage 1 | 180,224 bytes | 8 bytes, in the image header (link time) |
| 3: compiled by stage 2 | 180,224 bytes | 6 bytes, in the image header |
Stages 1, 2 and 3 are the same program. Every stage is 0 failed compilations of 137. The object and symbol files of two stages differ in a few records each (0 to 15 for an object, 1 to 4 for a symbol file: they carry compilation dates), but the linked images are identical but for the header, so none of those records is code. The kit holds stage 3.
The kit
- 267 files, 4,855 blocks (512 bytes blocks) installed in
SYS$COMMON:[SYSLIB.MODULA2]: the compiler, the library, the 87 symbol files, the 87 definition modules and 59 sources of the system, the user guide and four other documents, the examples. It is installed from a 2.9 MB BACKUP saveset in 8 seconds on the simulator and 30 on the VAXstation 4000/96. - Nothing outside that directory changes: no startup file, no
SYSUAF, no DCL table, no system logical name, and notSTARLET(the 1989 VMSINSTAL kit inserts the library intoSTARLET; this one has its ownMODULA.OLB). Every user runsMODULA2_SETUP.COM; it definesMOD$SYSTEM, theMODULAcommand and the first freeLNK$LIBRARY. - The installer writes a manifest with the size and revision date of every
file, runs a smoke test in a subprocess (compile, link and run a
program), and refuses to install over an existing tree without
REPLACE. The uninstaller deletes only files the manifest lists and that are still unchanged (a file you edited, and anything you added, stays), then empty directories, then the manifest. A lifecycle test does all of that: install, refuse, check, change and add files, uninstall (kept, exit status 4),REPLACEstops, clean, uninstall, reinstall,REPLACE: 20 checks, all pass.
Installing
You need OpenVMS VAX 7.3, SYSTEM (or SYSPRV), and about 12,500 free
blocks on the system disk while installing. No compiler.
Put the saveset and the two
.COMfiles into one directory on the VAX. Transfer modes matter: the.BCKin binary, the.COMfiles in ASCII.A binary transfer loses the saveset’s record attributes; the installer puts them back itself:
$ SET FILE/ATTRIBUTE=(RFM:FIX,LRL:32256,MRS:32256,RAT:NONE) - MODULA2_VAX_20261011.BCKAs
SYSTEM:$ @MODULA2_INSTALLEach user, at the prompt or in
LOGIN.COM:$ @SYS$COMMON:[SYSLIB.MODULA2]MODULA2_SETUP
@MODULA2_UNINSTALL CHECK says what an uninstall would remove, and
@MODULA2_UNINSTALL does it.
Programs
All of these are in the examples archive, each with a DCL test; 8 of 8 pass as an ordinary user, in 3 seconds on the simulator (13 on the VAXstation 4000/96), including 8 compilations.
A foreign definition module of your own. VMSINFO asks VMS who and where
it is. The library’s VMS module declares the process name of $GETJPI as a
string descriptor, but “the calling process” means passing nothing, so
VMSINFO declares what it needs:
%FOREIGN DEFINITION MODULE VmsX;
FROM SYSTEM IMPORT WORD, ADDRESS;
EXPORT QUALIFIED SYS$GETJPIW, SYS$GETSYIW;
PROCEDURE SYS$GETJPIW( %IMMED efn : WORD;
VAR pidadr: WORD;
%IMMED prcnam: ADDRESS;
%IMMED itmlst: ADDRESS;
%IMMED iosb : ADDRESS;
%IMMED astadr: ADDRESS;
%IMMED astprm: WORD): CARDINAL;
The item list is a RECORD of length-and-code, buffer, return length; the
program fills six of them (JPI$_USERNAME, JPI$_PID, JPI$_IMAGNAME,
JPI$_CPUTIM, …) and calls the service. Its test compares the output with
DCL’s own F$GETJPI and F$GETSYI.
RMS. PHONEBK is a telephone book in an indexed RMS file with a key on
the name (an XABKEY), through the library’s record-level interface: create
or open, keyed Get, Put, Update, Delete, a sequential list in key
order, and “first key greater or equal” for prefix search. The file is
Indexed, Prolog: 3 and the second run finds what the first left.
Coroutines. SIEVE is the sieve of Eratosthenes as a pipeline: one
coroutine generates 2, 3, 4, … and every prime found adds a filter
coroutine. A coroutine is a PROCESS made with NEWPROCESS(procedure, workspace, size, p) and entered with TRANSFER(me, other); the 46 primes
below 200 take 47 of them, each with 4 KB from ALLOCATE.
Four floating point formats. The VAX has F, D, G and H floating
point. REAL is F; D_FLOATING, G_FLOATING and H_FLOATING come from
SYSTEM, with all the operators and no implicit conversions. Summing the
series for e in each:
e = sum of 1/n!, 40 terms
F_floating 2.718282
D_floating 2.718281828459045
G_floating 2.71828182845905
H_floating 2.71828182845904523536028747135266
(The test compares the leading digits with Python’s decimal.)
Separate compilation. CALC is a recursive descent calculator
(^ * / %, variables A-Z) with its scanner Tokens in a definition and an
implementation module: compile TOKENS.DEF, TOKENS.MOD, CALC.MOD, link
CALC,TOKENS. Its expected output was computed by a separately written
Python evaluator, as were the Life, sieve and phone book outputs.
Issues
/LISTis the compiler’s default in a batch job, and it crashes (%SYSTEM-F-INTDIV), also the shipped 1988MODULA.EXE. The kit’sMODULAcommand does not default to it; use/NOLISTotherwise.- The library must be compiled without range checks.
InOut.WriteTextcomputesn-LEN(s)+1inCARDINAL;WriteCard(c, 0)then raises%MODULA-F-SUBVALERR. The shipped objects were built that way; so are ours. Your own programs keep the checks. - Files made by DCL
OPEN/WRITEare VFC with print carriage control and the compiler reads the two header bytes of each record as source. Edit with a real editor, orCONVERTthem. - The bitsavers zip holds only the sources; the object code is on the tape image, which is why a “just compile it” path did not exist: the compiler is written in the language it compiles, so you need the object code to start.
What I did not check
- Real hardware, mostly. The kit was built and tested on a simulated
MicroVAX 3900 (Open SIMH). I then installed it on one real machine, my
VAXstation 4000/96, system-wide, and
RUN_TESTSpassed 8 of 8 there as an ordinary user. I have not run it on my 4000/60 or any other VAX, at least not yet. Simulator times say nothing about a real VAX except ratios. - Programs larger than the examples (the compiler itself is 21,559 lines of Modula-2 in 27 modules and works, which is the best test I have).
- Interactive use of SMG, DEBUG with Modula-2 symbols,
/MACHINE_CODEand/CROSS_REFERENCEoutput (they run; I have not read them closely). HELP MODULA: DCL’sHELPdoes not find the kit’s help library on 7.3; the kit definesM2HELP MODULAinstead.
Rebuilding
REBUILD.md in the source kit has every command, in order, from the DECUS
modula32.zip (hashes given) to a tested kit, with the expected result and
the measured time of each step: reading the V3.2 saveset (278 files),
relinking the compiler from the shipped objects (1.2 s), three
self-hosting stages (95 s on the simulator), making the kit (6.5 s). The
only host-side tools are three small Python 3 scripts.
How this was done
As with Emacs, Perl and Poplog: Claude sessions with written briefs, on a VAXstation and a simulator, with the rule that nothing counts until a test shows it, and the transcripts kept. Every number above comes from one of those transcripts.
References
- N. Wirth, Programming in Modula-2, Springer, 1982.
- J. Koch, M. Mall, P. Putfarken, H. Eckhardt, VAX-11 Modula-2 User’s Guide (DBPL Memo 041-83) and Installing the VAX-11 Modula-2 Compiler (Memo 021-83), Universitaet Hamburg, 1983: on the media.
- bitsavers,
bits/DEC/vax/vms/Hamburg_Modula2/(tape image, log, zip). - DECUS VAX SIG library,
modula32.zip(kit MODULA032, Frankfurt/Hamburg versions, 1989).
Credits: the DBPL research project of the University of Hamburg (J. W. Schmidt; J. Koch, M. Mall, P. Putfarken, H. Eckhardt) and Niklaus Wirth’s group at ETH Zurich, who made the language and its first compilers; the DECUS VAX SIG library and bitsavers, who kept the files for decades; and Claude Code (Anthropic’s Claude models) for much of the building, debugging, testing and writing.
