I haven't changed any of the sources and you will even see the original timestamps of the files as I archived them at the end of this project. This means that the emulator and its associated tools can no longer be compiled with recent C compilers and even if you manage to get rid of all the K&R style within the C sources, it won't work because of huge portability problems. These utilities were just developed to serve in the development of a Modula-2 compiler for the Perkin-Elmer 3220 architecture and were no longer needed once this goal was achieved.
Following compressed tar archives are available for download:
Please note that the Lilith emulator, the M-Code decoder, and the M-Code linker are available under the terms of the GPL. The Multipass Modula-2 compiler for the Lilith architecture, however, was developed by a team under the direction of Niklaus Wirth at the Institut für Informatik at ETH Zürich. We received this software under a license agreement which allows us to redistribute it. You have to consent to this licence before you download it.
The package historic-lilith-binaries.tar.gz provides the binaries lilith, mcd, and mcl that were compiled from the sources of the first three packages in 1984. Unfortunately they cannot be reproduced using the simulation system (see below) as the C compiler shipped with the provided instance of UNIX Edition 7 has bugs we hadn't on our system running at that time.
If you intend to play with these tools, you will need to setup an environment that is close enough to our system we used at that time. I recommend to use the most recent simulator of the Computer History Simulation Project along with a copy of Unix Edition 7 for the Interdata architecture which is available under a license provided by Caldera Corporation. The Interdata is a predecessor of the Perkin-Elmer architecture we had. This is no problem for the Lilith emulator and the compiler running on it but for our Modula-2 compiler for that architecture which took advantage of instructions the Interdata didn't have.
Here is a step-by-step instruction how this can be set up:
clonard$ mv historic-lilith-emulator lilith clonard$ mv historic-mcode-decoder mcd clonard$ mv historic-mcode-linker mcl clonard$ mv historic-multipass-modula2-compiler-lilith mll clonard$ mv historic-lilith-binaries bin clonard$ tar cf modula.tar lilith mcd mcl mll bin
set ttp ena set pas dev=12 att -e dp0 iu7_dp0.dsk att -e dp1 iu7_dp1.dsk att -e dp2 modula.tar set console brk=3 boot dp0
(The set console command configures the console to send a BREAK whenever CTRL-c is typed in.)
clonard$ id32 Interdata 32b simulator V3.8-0 Boot : dsk(1,0)unix Memory = 248.0 K # ^D Restricted rights: Use, duplication, or disclosure is subject to restrictions stated in your contracts with Western Electric Company, Inc. and the University of Wollongong. Fri Jan 2 18:12:58 EST 1970 login: root Password: root #
(Please note that ^D represents CTRL-d and that the root password is not echoed.)
# cd /dev # /etc/mknod dr2 b 0 4 # /etc/mknod rdr2 c 2 4 # cd /tmp # tar xf /dev/rdr2 Tar: blocksize = 20 tar: lilith/ - cannot create tar: lilith/src/ - cannot create tar: mcd/ - cannot create tar: mcd/src/ - cannot create tar: mcl/ - cannot create tar: mcl/src/ - cannot create tar: mll/ - cannot create tar: mll/src/ - cannot create tar: bin/ - cannot create tar: bin/bin/ - cannot create #(The error messages of tar can be ignored.)
# PATH=$PATH:/tmp/bin/bin # export PATH # mkdir /tmp/hello # cd /tmp/hello # ed HelloWorld.m2 ?HelloWorld.m2 a MODULE HelloWorld; FROM Terminal IMPORT WriteString, WriteLn; BEGIN WriteString("Hello, world!"); WriteLn; END HelloWorld. . w 128 q # for x in /tmp/mll/src/C18.* > do ln $x `basename $x` > done # ln /tmp/mll/src/mc mc # ln /tmp/mll/src/SYM . # mc HelloWorld.m2 source file> HelloWorld.m2 p1 Terminal: Terminal.sy p2 p3 p4 end compilation #
# mcd HelloWorld.o decode of `HelloWorld.o' : codekey = 3 module name: HelloWorld datasize = 12 key 0 0 0 import Terminal is # 1 HelloWorld is # 2 data, relative to G 1: 0B 2: 0B procedure # 0 at 2 bytes relative to F data, relative to G 3: 44145B 4: 66154B 5: 67454B 6: 20167B 7: 67562B 10: 66144B 11: 20400B code at F + 1 words 1 2 353 ENTR 0 2 4 25 LGA 1 3 6 224 TS 3 7 32 JPFC [ 2] -> 12 4 11 354 RTN 5 12 25 LGA 3 6 14 122 SGW2 6 15 355 CX 1 0 10 20 102 LGW2 10 21 14 LI12 11 22 355 CX 1 6 12 25 355 CX 1 5 14 30 354 RTN 14 31 336 NOP fixups at 26 23 16 #
The module body begins with a test whether this module was already initialized by loading the address of the first global variable (LGA 1) and performing a test-and-set operation on it (TS). If this flag was false, we jump to position 12, continuing the body, otherwise we return (RTN) immediately. Next follows the storage of a pointer to the hello world string in the global variable 2 (LGA 3 and SGW2). Next follows the invocation of the module body of Terminal (CX 1 0, i.e. call external module 1, procedure 0). Then we are ready to invoke WriteString and WriteLn. An open array parameter requires two values to be passed, the address of the array (LGW2 = load global word 2) and its length (LI12 = load immediate constant 12). Finally, we return. The NOP instruction is used to fill up to a word boundary of 16 bits. Some of the instructions require one byte only (like RTN), others are longer (CX, for example, requires three bytes).
# ar x /tmp/mll/src/OBJECTS Terminal.o # mcl -o HelloWorld HelloWorld.o Terminal.o # lilith HelloWorld Hello, world! #
You might wonder why no other library modules were needed or why the source for Terminal.m2 is missing. It may be interesting to decode Terminal.o:
# mcd Terminal.o decode of `Terminal.o' : codekey = 3 module name: Terminal datasize = 3 key 123373 745 152654 import Terminal is # 1 data, relative to G 1: 0B 2: 0B procedure # 1 at 16 bytes relative to F code at F + 7 words 7 16 246 SVC 74 10 20 354 RTN 10 21 336 NOP procedure # 2 at 22 bytes relative to F code at F + 11 words 11 22 246 SVC 75 12 24 354 RTN 12 25 336 NOP procedure # 3 at 26 bytes relative to F code at F + 13 words 13 26 246 SVC 76 14 30 354 RTN 14 31 336 NOP procedure # 4 at 32 bytes relative to F code at F + 15 words 15 32 246 SVC 77 16 34 354 RTN 16 35 336 NOP procedure # 5 at 36 bytes relative to F code at F + 17 words 17 36 246 SVC 100 20 40 354 RTN 20 41 336 NOP procedure # 6 at 42 bytes relative to F code at F + 21 words 21 42 246 SVC 101 22 44 354 RTN 22 45 336 NOP procedure # 0 at 46 bytes relative to F code at F + 23 words 23 46 353 ENTR 0 24 50 25 LGA 1 25 52 224 TS 25 53 32 JPFC [ 2] -> 56 26 55 354 RTN 27 56 25 LGA 3 30 60 122 SGW2 30 61 354 RTN #
As you can see, most of these procedures are implemented with the help of a SVC (super visor call) instruction which doesn't belong to the original instruction set of the Lilith architecture. I added this instruction as a hook that allowed me to implement these procedures within the Lilith emulator. You'll find the implementations of this within the l_svc.c source file of the Lilith emulator (in the /tmp/lilith/src subdirectory. SVC 101, for example, is implemented here (note that 101 is octal which is 65 in decimal notation):
case 65 : /* PROCEDURE WriteString(s: ARRAY OF CHAR); */ len = pop() + 1; index = pop(); ptr = (char *) &stack[index]; #ifdef TRACE trace("WriteString(\""); #endif TRACE while ( len && *ptr ) { #ifdef TRACE trace("%c",*ptr); #endif TRACE putchar(*ptr++); --len; } #ifdef TRACE trace("\")\n"); #endif TRACE break;
These objects were not derived from the original sources but from stripped-down variants where the procedure bodies were replaced by CODE-constructs, a language-extension which was supported by the Modula-2 compiler for the Lilith. This allowed the inclusion of native machine code. These stripped-down sources existed on the PDP-11/40 only and were not transferred with the other files.
In general, the surviving library modules are quite minimalistic in their selection, even InOut is missing as I considered strictly those sources only which were required for bootstrapping.
# sync # sync # ^E Simulation stopped, PC: 00D20 (EPSR R1,R0) sim> exit Goodbye clonard$
Next time, you just need to boot the system again, following the instructions above, and you will find everything back again including the installed directories below /tmp.