- AMPL 52.4%
- HTML 47.6%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| docs | ||
| MC4 | ||
| Console.def | ||
| Console.mod | ||
| Console.o | ||
| Extended.def | ||
| Extended.mod | ||
| Extended.o | ||
| FileIO.def | ||
| FileIO.mod | ||
| FileIO.o | ||
| Global.def | ||
| Global.mod | ||
| Global.o | ||
| gnu-m2-grammar.txt | ||
| img1.png | ||
| Instruction.def | ||
| Instruction.mod | ||
| Instruction.o | ||
| Interpreter.def | ||
| Interpreter.mod | ||
| Interpreter.o | ||
| Loader2.def | ||
| Loader2.mod | ||
| Loader2.o | ||
| Local.def | ||
| Local.mod | ||
| Local.o | ||
| M2c-git instructions.txt | ||
| mc64 | ||
| MC64.mod | ||
| MC64.o | ||
| mcd.o | ||
| mcint | ||
| mcint.mod | ||
| mcint.o | ||
| Memory.def | ||
| Memory.mod | ||
| Memory.o | ||
| mkdemo | ||
| mkdemo.mod | ||
| mkdemo.o | ||
| mkdep | ||
| mkdep.mod | ||
| mkdep.o | ||
| mkdtest | ||
| mkdtest.mod | ||
| mkdtest.o | ||
| mkread | ||
| mkread.mod | ||
| mkread.o | ||
| README.md | ||
| SESSION.MD5.trans8to64 | ||
| skills.html | ||
| skills.jpeg | ||
| skills.md | ||
| skills.pdf | ||
| skills.png | ||
| Stack.def | ||
| Stack.mod | ||
| Stack.o | ||
| trans8to64 | ||
| trans8to64.mod | ||
| trans8to64.o | ||
| trans8to64dbg | ||
| tutorial.md | ||
MC64 — a 64-bit Modula-2 virtual machine
MC64 is a 64-bit generalization of the MCode virtual machine that Borland
Turbo Modula-2 for CP/M executed on the Z80. The machine word — the unit of
the stack, frame cells and global cells — is widened from 16 to 64 bits,
the address space becomes flat and byte-addressable, and the 256-entry opcode
map is preserved 1:1: only operand widths and the scalar type mapping differ.
The normative reference is docs/mc64-spec.md, §14 of
which contains the row-by-row MCode/16 → MC64 migration table.
The machine loads .MC4 image files (a 64-byte "MC64" header + a module
descriptor + code + a procedure table) and boots them with the mcint
launcher. It is the execution target of the experimental Modula-2 compilers in
MyWork/ — V1 (M2c), V2 (M2comp) and V3 — whose code generators emit
.MC4 images that this interpreter runs. As of this writing the loader is at
the dependency-chain milestone: a guest module can name up to 32
dependencies and call across module boundaries through the 256-slot module
table (MTBL).
Quick start
The repository ships prebuilt executables and ready-made sample images, so the first run takes one command:
./mkdemo # writes boot.MC4 in the current directory
./mcint boot.MC4 # prints "Hello MC64!" and exits 0
The machine model
A stack machine with a single stack shared by values and activation frames,
growing down (Push = SP -= 8).
| Register | Meaning |
|---|---|
IP |
instruction pointer — byte address of the next opcode |
SP |
stack pointer — byte address of the top word |
FP |
frame pointer — base of the current activation frame |
GP |
global pointer — base of the current module's data window |
OFP |
outer frame pointer — static chain / display link |
MTBL |
module table — MTBL[i] = data base of loaded module i |
Data types
| Modula-2 type | Width | Stack slots | Notes |
|---|---|---|---|
| BYTE, CHAR, BOOLEAN | 8 bits | 1 | held in the low bits of a slot |
| CARDINAL | 32 bits | 1 | unsigned, zero-extended |
| INTEGER | 32 bits | 1 | two's complement, sign-extended |
| SET | 64 bits | 1 | elements 0..63 |
| LONGINT / LONGCARD | 64 bits | 1 | signed / unsigned 64-bit arithmetic |
| REAL | 64 bits | 1 | IEEE 754 binary64 (double) |
| LONGREAL | 128 bits | 2 | IEEE 754 binary128 (quad) |
| pointer / address | 64 bits | 1 | byte address |
Stack slots are always 64 bits; 32-bit values live in the low 32 bits (CARDINAL zero-extended, INTEGER sign-extended), so 32↔64-bit widening is a value no-op. Memory is little-endian everywhere.
Locals live at FP[-1], FP[-2], …; parameter k at FP[k+2]; arguments are
pushed in reverse declaration order. ENTER k reserves (255−k)×8 bytes of
locals (k=0FFH → none). EXTERN_CALL mod,proc switches GP to
MTBL[mod] and jumps through the callee module's procedure table.
Images (.MC4)
A module file is a 64-byte header followed by the image: the module descriptor, its code, a procedure table and (optionally) a dependency-name table.
-
File header — magic
"MC64"at offset 0; the descriptor itself sits at image offset 0 (i.e. immediately after the 64-byte header) in the generator images. -
Descriptor (image offsets, little-endian):
offset field 0 dependencies— 256 bytes of module-base pointers256 link— next module in chain264 name— 16 chars, NUL-padded280 loadAddr288 checksum— u32292 flags(bit 2 = TOINIT)293 varCount294 depCount295 pad296 procsAddr— location of the procedure table304 varSizes—varCount× u64 byte sizes -
Procedure table —
Ncontiguous 64-bit relative byte offsets:procedureAddress(k) = procsAddr + k*8 + table[k]. -
Checksum — plain 32-bit sum of the image bytes (file offsets 64 up to end of file), excluding the four checksum bytes themselves (file 352..355 = image 288..291).
-
Dependencies —
depCountnames appended at the image tail (8 NUL-padded bytes each); the loader loads dependency modules into lowMTBLslots first (extern module 0 = the first import), then the main module.
The loader places the image at ArenaBase (1 MiB) and allocates the module's
globals (varSizes bytes per entry, zero-filled) in an 8-byte-aligned data
window right after the image. Historically these files were called .MCD; the
64-bit format was renamed to .MC4 per spec sections 8 and 14.
The toolchain
| Tool | Purpose | Sample |
|---|---|---|
mcint |
interpreter launcher — boots a single-module image or the main module of a dependency chain | ./mcint boot.MC4 |
mc64 |
demo wrapper — boots boot.MC4 then prints [vm end] |
./mc64 |
mkdemo |
writes boot.MC4 ("Hello MC64!" via the SYSTEM write-string service) |
./mkdemo |
mkdtest |
writes example.MC4, a 24-instruction opcode/composite exercise |
./mkdtest && ./mcint example.MC4 |
mkread |
writes readtest.MC4, which reads two stdin lines and echoes them back |
printf 'a\nb\n' | ./mcint readtest.MC4 |
mkdep |
writes STAK.MC4 + DEP.MC4, a two-module cross-module dependency-call demo |
./mkdep && ./mcint DEP.MC4 |
trans8to64 |
translates a classic 16-bit Turbo Modula-2 .MCD binary into a 64-bit .MC4 image |
./trans8to64 in.MCD out.MC4 |
mcint takes the image path as its first program argument; exit status is 0
when the guest reaches end_program (SYSTEM service 0), 1 when the loader
or a guest trap aborts. With no argument it prints its usage line.
Building from source
Everything is ISO Modula-2, compiled with GNU Modula-2:
# in a build directory containing the sources (cp src/*.mod src/*.def .)
for m in Memory Local Global Stack Instruction Extended Interpreter Loader2 \
Console FileIO; do
gm2 -fiso -c "$m.mod"
done
gm2 -fiso -o mcint mcint.mod \
Loader2.o Interpreter.o Extended.o Instruction.o Stack.o Global.o Local.o \
Memory.o Console.o FileIO.o
gm2 -fiso -o mc64 MC64.mod \
Stack.o Memory.o Console.o Global.o Loader2.o Interpreter.o Extended.o \
Instruction.o Local.o FileIO.o
gm2 -fiso -o mkdemo mkdemo.mod FileIO.o Console.o
gm2 -fiso -o mkdtest mkdtest.mod FileIO.o Console.o
gm2 -fiso -o mkread mkread.mod FileIO.o Console.o
gm2 -fiso -o mkdep mkdep.mod FileIO.o Console.o
gm2 -fiso -o trans8to64 trans8to64.mod FileIO.o Console.o
One build rule matters (see spec §16.1): only the program module is passed
to the link step; library modules are given as their precompiled .o. Passing
several .mod files at once makes gm2 emit one main per module and the link
fails with duplicate-main errors — and linking all *.o indiscriminately
runs every module's init body at startup.
Project layout
src/ VM library modules (Memory, Local, Global, Stack, Instruction,
Extended, Interpreter, Loader2, Console, FileIO) and the seven
tool programs (mcint, MC64, mkdemo, mkdtest, mkread, mkdep,
trans8to64) — .mod/.def sources
MC4/ generated sample images (boot.MC4, example.MC4, readtest.MC4,
STAK.MC4, DEP.MC4)
docs/ mc64-spec.md (normative spec), session-summary.md, SESSION.md,
m-code-summary-64.md, m-code-summary.md, porting-plan-m2sp.md
root built executables, *.o, reference files (skills.*,
gnu-m2-grammar.txt, M2c-git instructions.txt, SESSION.MD5.trans8to64)
Documentation
docs/mc64-spec.md— the normative specification: memory model, registers, types, frames, addressing, module linkage, the on-disk format (§8), the host interface (§9.3), the extended and quad sub-dispatches (§10), the 256-opcode instruction-set reference (§11), the MCode/16 → MC64 migration table (§14) and the GNU Modula-2 build notes (§16).docs/m-code-summary-64.md— condensed summary of the 64-bit machine.docs/session-summary.md— day-by-day log of loader/interpreter milestones: build recipes, verified behaviors, hard-won tools-and-gotchas.docs/porting-plan-m2sp.md— plan for porting Niklaus Wirth's M2SP single-pass compiler to ISO gm2 with an MC4 code generator.docs/SESSION.md— working notes.
Verification status
All of the following are green with the freshly built binaries:
./mkdemo→boot.MC4;./mcint boot.MC4→Hello MC64!(0);./mc64→Hello MC64!+[vm end](0);./mcint→ usage (0);./mcint nope.MC4→ loader fatal (1)../mkdtest→example.MC4;./mcint example.MC4— 24/24 checks:g after 10*2+1 = 2047, add/sub/mul/div/mod, bit_and/or/xor, power2, the extended0x40sub-dispatch (uc_add/uc_mul/uc_div/uc_mod, long_negate, build_field_mask),real_add 3.5+2.25 = 5, dup/swap, global/local dword loads,copy_block→abc(0)../mkread→readtest.MC4;printf 'a\nb\n' | ./mcint readtest.MC4echoes both lines throughSYSTEMservice 2 (0)../mkdep→STAK.MC4+DEP.MC4;./mcint DEP.MC4executes an extern call intoSTAKthroughMTBL[0]and printscross-module ok(0).
Related projects
Siblings in MyWork/:
m2compiler-V1—M2c, the single-pass compiler (Coco/RM2c.atg+SymTab/MGen), step 11, 158/158 tests; the original.MC4consumer.m2compiler-V2—M2comp, the Blaise front/back (AST) compiler, step 7, 65/65 tests; itsrun_tests.shdefaults toMCINT=../m-code-64/mcint.m2compiler-V3— the larger successor generation.m-code— the original 16-bit Turbo Modula-2 reference material and MCode sources (used only as Modula-2 sources, never binary analysis).CocoGm2— the GNU-Modula-2 port of Coco/R (CR) used to build the compiler front ends.
Working conventions
- The interpreter is written against
docs/mc64-spec.mdonly — no disassembly or analysis of classic.MCDbinaries. - Deterministic build order (§16.1), pure ISO dialect (
gm2 -fiso). - Every instruction in the demos is hand-encoded:
mkd*.modgenerators are the known-good.MC4encoders used as ground truth for the compilers. SESSION.MD5.trans8to64stores a baseline MD5 oftrans8to64.mod: any step that rewrites that file without updating the lock note is a session bug.