- Modula-2 70%
- Macaulay2 11.8%
- Shell 7.4%
- DTrace 5.7%
- AMPL 4.8%
- Other 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Records what was verified and what work it implies: - xterm 411 in /usr/local/bin has ReGIS, but graphics decoding is off by default (decGraphicsID = 0); `-xrm 'XTerm*decGraphicsID: 340'` plus `regisScreenSize: 768x592` makes the Lilith geometry fit (screenshot included). - The MEDOS display is a 768x592 1-bit framebuffer with hardware REPL (343B), BBLT (344B), DCH (345B) and GET/PUT (240B/241B); mcode aborts on the first three, so the (compilable) display stack cannot run. A host port of DisplayDriver alone is insufficient: Screen and ScreenDriver2 issue the opcodes directly. - Plan P1..P5: emulate the device in mcode + PPM dumps (headless tests), a ReGIS presenter (dirty rects, run rasterisation), the text shortcut (DisplayDriver.Write/TerminalBase), a vector fast path, then input and windows. Limits: no bitblt/bitmap upload in ReGIS, no font download, terminal-speed animation. - Recommended first step: P1. Uses ../VT100/Regis.def for the presenter primitives. |
||
| examples | ||
| lib | ||
| mplib | ||
| src | ||
| tests | ||
| .gitignore | ||
| BUILD.md | ||
| git.txt | ||
| install.sh | ||
| Makefile | ||
| Mcode-Phase3v2-Summary.md | ||
| Mcode-Phase3v4-Summary.md | ||
| medos | ||
| MEDOS-Display-ReGIS-xterm.png | ||
| MEDOS-Display-ReGIS.md | ||
| MEDOS2-SelfHosting-Summary.md | ||
| Open-Work.md | ||
| Phase3-Overlay-Summary.md | ||
| README.md | ||
| REAL-Support-Summary.md | ||
| ROADMAP.md | ||
| Self-Hosting-Artifacts.md | ||
| SelfHost-Diagnostic-and-Next-Steps.md | ||
| Toolchain-Summary.md | ||
| TUTORIAL-LANGUAGE.md | ||
| TUTORIAL.md | ||
Lilith M-code interpreter (GNU Modula-2)
Runs M-code objects under Linux: .OBJ from the single-pass compiler port
(../SinglePassCompiler/gm2-port, program m3pl) and .o from the
multipass compiler port
(../lilith-multipass-modula2-compiler-master/gm2-port, program mcc) —
no Lilith hardware, no Medos disk image, no linker needed.
New here? Start with TUTORIAL-LANGUAGE.md — it walks through
building both tools, Hello world, HostIO output, multi-module builds,
tracing, and the test suite. For install-from-scratch instructions for
all three binaries (m3pl, mcc, mcode), see BUILD.md.
For the whole chain end to end — both gm2 ports and both self-hosted
compilers — see TUTORIAL.md. Prioritised follow-up work
is in ROADMAP.md.
Build and test
make # builds src/mcode (gm2 -fpim, no extensions needed)
make test # compiles the tests with m3pl, runs them, diffs output
make test-multi # compiles tests/mp/*.m2 with mcc, runs the .o files
make bootstrap # builds all three tools and runs every self-hosting gate
make showcase # the same program through both compilers, outputs diffed
./install.sh # builds all three tools, installs them + `medos` (~/.local/bin)
make test needs the sibling SinglePassCompiler checkout in place
(paths: ../src/mcode, ../../SinglePassCompiler/gm2-port/m3pl).
make showcase compiles examples/showcase/ with both compilers and
checks that the two runs are byte-identical (examples/showcase/README.md
lists everything it exercises).
./medos is a small dispatcher for the whole chain, usable installed or
straight from this checkout: medos m3pl HELLO, medos mcc T.m2 SC.m2,
medos run DK.HELLO.OBJ, medos decode …, medos version.
Use
$ ./src/mcode
Lilith M-code interpreter (gm2 port)
OBJ> DK.T1.OBJ
14
...
end.
OBJ> <ESC>
OBJ> prompt, ESC/empty line exits (piped input works). Filename suffixes:
/T traces each opcode (PC in octal + mnemonic + eval depth/top),
/D dumps main-module globals at end. --version/--help print the
banner/usage and exit.
Machine model
16-bit words, 64K-word data memory, per-module byte-coded code, 4-word
frames (static, dynamic, retPC, pad; locals at L+4), 16-deep eval
stack. Capacity per module: 16KB code, 128 procedures, 1024 relocations,
31 imports (64 modules total) — sized to cover what the compilers emit
(CodeLength 12000, RelTabLength 1000, ProcTabLength/StackDepth). Module data word 0 holds the runtime module index. Imports load
recursively with name+key checks; each module body runs once as its own
init guard (LGA/TS) requires. Normal end (RTN to 0) exits 0; anything
else (TRAP, checks, bounds) prints a message and aborts nonzero.
Opcode semantics follow lilith-emulator-c/l_exec.c (1983, after Wirth
ETH Report 40), cross-checked against M3GL.mod code generation and the
decode.mod mnemonic table (copied for /T).
Container format (OUR OBJs only!)
m3pl's gm2 port writes W fields as 4-byte little-endian CARDINALs and
characters as single bytes (original Lilith files use 2-byte words and are
NOT accepted). Layout per module: magic 200B,1,4, header 201B,14 +
16-byte name + 3-word key + datasize/codesize, optional imports 202B
(7 words/entry), data 204B blocks (words, then at most one raw-byte
string block), entries 203B, code 203B (bytes), relocation 205B
(byte offsets into the image, patched to runtime module numbers at load
like mcl does).
Multipass .o files (imports included)
The multipass compiler (../lilith-multipass-modula2-compiler-master/gm2-port,
program mcc) writes one .o per module: flat big-endian 16-bit words
as { type, size, payload } frames — 0200B VERSION (must be 3), 0201B
MODULE (16 name bytes hi-first + 3 key words + datasize), 0202B IMPORT
(11 words/entry ordered by modnum, self at its own position), 0204B
DATATEXT (word offset + data), 0203B CODETEXT (fs=2: procnum → byte
offset; longer: body chunk at word offset, bytes hi-first), 0205B FIXUP
(byte offsets patched at load by the shared PatchRelocs). The opcode set
is the same M-code, so the existing dispatch runs it unchanged; data[1]
is the init flag, data[2] the string pointer. At the OBJ> prompt, names
ending in .o take this loader (imports resolve as <name>.o /
DK.<name>.o); everything else takes the single-pass loader. .r files
are debugger info, never loaded. The loader also synthesizes the
HostIO/Terminal/Math/Files trap modules for .o programs, so
multipass code can do I/O, math and files: compile the mplib/*.d
definitions with mcc once per work directory for their .sy files.
make test-multi compiles
tests/mp/ (.d first for the .sy, then .m2) with mcc and diffs
the runs: IO (all four synth modules), MS (Strings), MI/MR/MT
(InOut/Random/Time) and the Stats data-file analyzer.
HostIO / Terminal / Math / Files — running programs with output
Medos FileSystem doesn't exist here. Instead the loader synthesizes
four modules (no .OBJ needed, trusted without key check) — definitions
live in lib/; compile them with m3pl once per work directory for
type-checking:
DEFINITION MODULE HostIO;
PROCEDURE WriteCard(c: CARDINAL);
PROCEDURE WriteInt(i: INTEGER);
PROCEDURE WriteLn;
END HostIO.
Compile it with m3pl for type-checking test programs; bodies are
3-byte LIB n / TRAP stubs (traps 20/21/22) handled by the interpreter
with resume. Terminal (Medos interface v4, lib/DK.Terminal.MOD) works
the same way via traps 30..35 (Read, BusyRead, ReadAgain, Write,
WriteString, WriteLn). Math (lib/DK.Math.MOD) provides Sqrt,
Exp, Ln, Sin, Cos, Tan, ArcTan, Entier, Log10, Power
via traps 40..49
(host libm; domain errors abort). Files (lib/DK.Files.MOD) offers
sequential host text files via traps 50..60 (OpenRead/OpenWrite/
OpenAppend → slot handle or INVALID, Close, Eof, ReadChar,
WriteChar, WriteString, WriteLn, ReadLine, Remove). TRAP n otherwise aborts
(TRAP 10, i.e. HALT, too).
Note the dialect facts this exposed: TRUNC returns CARDINAL (so
TRUNC of a negative correctly traps via the compiler's CHKS), and
unary minus / operators on Double constants are rejected at compile
time (original GenNeg, error 112) — write them with variables.
Real format decision: IEEE-754 single
M2Emul's le_mcode.c scales floats ×4/×0.25 claiming non-IEEE Lilith
format; lilith-emulator-c/l_exec.c (contemporary, 1983, used in real
bootstrap work) uses plain C floats, and Wirth's code generator emits
FLOAT/TRUNC/arithmetic with no scaling anywhere. This port implements
IEEE single on both sides (compiler M3GL.PutReal, interpreter
F32ToR/RToF32; runtime arithmetic stays gm2 double — last-ulp
differences only). If real-Lilith evidence ever lands for bias-129, flip
exactly these two spots (×4 in, ×0.25 out).
Porting traps found (useful for the multipass interpreter)
Writing the dispatch in Modula-2 after C references kept biting:
PC += next()-1re-readsPCafter the call in C; Modula-2PC := PC + Cb() - 1uses the stale value. Sequence every PC-relative branch explicitly (t := Cb(); PC := PC + t - 1).- C
switchinsideelse if (IR < 0240)silently skips 0220B–0237B (no matching case, chain done); the same tier split inELSIFneeds mergedCASElabels instead. - gm2 silently ignores unmatched
CASE(observed!) — add explicitDielabels for gaps (0214B/0215B); never rely on the trap. FOR i := 0 TO n-1withCARDINAL n = 0underflows to 4 billion — useWHILEfor possibly-empty ranges (reloc patching died on this).- Mixed-sign expressions (
CARDINALvs negative literals,INTEGERloop counters withDEC) — convert explicitly (VAL, guardedIFs); thee: CARDINALtypo alone broke every float op. - Uninitialized module-record fields (
nrel,synth, keys) — the language doesn't zero them; init on both load paths.
Tests (tests/ sources, lib/ libraries, examples/ demos, all run by run.sh)
| test | covers | expected |
|---|---|---|
HELLO |
load/init/exit, globals dump | end. |
T1 |
precedence, wrap, FOR, signed, WHILE, IF |
14 0 55 -7 0 1 |
BAR+FOO |
imports, cross-module calls, module init | 9 16 |
PV+PVM |
procedure variables: assign, call, pass as param, reassign (same-module + imported) | 107 105 16 27 8 8 |
MSK |
opcode 326B via CODE procedure: mask {0..n-1} |
0 1 15 32767 |
MODT |
MOD/DIV by powers of two (mask codegen; 577 MOD 256 was 512) |
65 255 2 345 15 |
T3 |
reals, sets, recursion, nested procs, LONGINT, CASE |
3 2 1 0 720 42 1 20 |
T4 |
long arithmetic, TRUNC division |
1 1 1 25 0 |
TNEG |
negative test: TRUNC(-2.5) must abort via CHKS |
abort |
MNEG |
negative test: Sqrt(-1.0) must abort via math domain |
abort |
FBAD |
negative test: Close(7) must abort via bad file handle |
abort |
CORN |
NEWPROCESS builds a context but runs nothing until a TRANSFER |
111 222 |
COR |
SYSTEM coroutines: NEWPROCESS + TRANSFER back and forth |
1 2 3 4 5 6 |
CORT |
negative test (multi only): TRANSFER to a coroutine never created must abort |
abort |
TCHK |
negative test (multi only): negative INTEGER→CARDINAL must abort via CHKS |
abort |
TNEG |
negative test (multi only): WriteInt(TRUNC(-2.5)) must abort via CHKS |
abort |
SA |
Storage synth module (alloc order + no-op deallocate) |
1 1 |
SC |
named string constant declare + use (assign, read back) | 104 105 33 |
SE |
sets, multi only (constructors bit order, IN T/F, =/<>, +/-) |
11 14 1 0 0 1 |
TS |
Strings lib + Terminal output (length/compare/copy/concat/numtostr/pos/sub/append/case/trim) |
5 0 -1 1 5 hello!! 12345 -678 0 2 65535 ell hello!! MIXED mixed pad| |
TM |
Math lib (sqrt/exp/ln/sin/cos/tan/arctan/entier/pow/log10, scaled) |
141 271 230 479 877 546 7853 2 -2 1024 27 3 25 3010 1 |
TF |
Files lib (write/read round-trip, missing file, EOF, append, remove) |
hello abc Z 1 0 65535 1 one two 1 0 |
Echo |
Terminal stdin→stdout, ends at host EOF |
echoes input |
Caesar |
Terminal+Strings Caesar+3 over one line |
caesar+3, one line: Khoor, Zruog! len=13 |
Tour |
all five libs over one line (upper/len/pos/stats/file round-trip) | up=HELLO, WORLD! len=13 pos-o=4 root100=360 log1000=3113 pow=1024 file:… removed=1 |
TI |
InOut reads (card/int/bad input + Done) |
42 1 -7 0 |
TR |
Random with fixed seed |
22 87 0 |
TT |
Time monotonicity |
1 1 |
TRI |
RealIO fixed output + parse (Done) |
3.14 1 -2.5 100 0.000 0 |
HFS |
Heap/Clock/FileSystem synth modules (alloc order, clock ranges, word round-trip via SetOpen, delete/missing) |
1 1 1 1 1 0 12 20741 0 65535 0 1 |
lib/ holds the shared libraries (see lib/README.md): HostIO,
Terminal, Math, Files, Time (all synthesized at load, compile the
definitions only for checking) plus Heap, Clock, FileSystem — the
three synthesized host modules needed to run a self-compiled m3pl
(see ../SinglePassCompiler/selfhost/) — plus the pure-M-code Strings library
(definition Strings + implementation StringsImp). examples/ holds
the Echo / Caesar demos and the two-dialect showcase/ tour (see
examples/README.md and examples/showcase/README.md).
run.sh copies tests/ + lib/ + examples/ DK.*.MOD into a temp
dir, so keep reusable modules in lib/ and demo programs in examples/;
showcase/ is run separately by make showcase.
Roadmap
- Multipass
.oloader: imports +FIXUPlinking done (make test-multi). Procedure variables work in both worlds (PVtest each side: the single-pass loader patchesLIWdescriptors likeLEA/CXmodule bytes; indirect calls run through the existingCFdispatch).SYSTEMcoroutines are implemented on the multipass side:NEWPROCESS(CX 0 2) builds the MEDOS-2ProcessDescriptorin the workspace andTRANSFER(TRA) saves the running context and switches (CORtest: two routines interleave 1 2 3 4 5 6;TRANSFERto a nil coroutine aborts —CORT). The single-pass compiler'sSYSTEMprovides them too: standard procedures 21/22 emitCLX 0 2/TRA(PROCESSis aSYSTEMtype; coroutine handles are word-sized), with theDK.CORtest intests/and the two-dialectexamples/showcase(identical output fromm3plandmcc, coroutines and all the libraries). - Self-hosting loop closed: the original compiler sources compile under
m3plitself (../SinglePassCompiler/selfhost/build-self.sh, all sixDK.M*.OBJ), run undermcodevia synthesizedHeap/Clock/FileSystem(traps 70–81,HFStest), and compile test programs to byte-identical OBJs modulo timestamp keys.test-self.shreplays the loop as a regression test (8 positive + 2 negative diffs), andtest-fixpoint.shnow reports 6/6 — every object, including the gen 1→2 step, is bit-identical modulo keys. The two one-time host differences (theLIW-vs-LINencoding from 32- vs 16-bitCARDINALwidth, and theMAX(REAL)low byte from double- vs single-path parsing) are fixed in the port itself. - The multipass twin is closed the same way:
../lilith-multipass-modula2-compiler-master/selfhost/build-phase4.shrebuilds everysrc/module with the self-hosted C18 and gets byte-identical objects to the hostmcc(0 deltas), andbuild-phase5.shhasmcccompile its owngm2-portsources (37/0).