64 bits m-code virtual machine with interpreter and conrsion program

Eric Streit ae221e2d1a docs: add project README and usage tutorial 15 tuntia sitten
MC4 75954dc51f docs/MC4 reorganization: per-project docs/ and MC4/ folders, runners use MC4/ 3 viikkoa sitten
docs 75954dc51f docs/MC4 reorganization: per-project docs/ and MC4/ folders, runners use MC4/ 3 viikkoa sitten
Console.def 83b9f7c6b2 second commit 4 viikkoa sitten
Console.mod 83b9f7c6b2 second commit 4 viikkoa sitten
Console.o 83b9f7c6b2 second commit 4 viikkoa sitten
Extended.def d544a9f943 step4: composites — arrays, records, sets, pointers, strings, WITH; 74/74 tests green 3 viikkoa sitten
Extended.mod d544a9f943 step4: composites — arrays, records, sets, pointers, strings, WITH; 74/74 tests green 3 viikkoa sitten
Extended.o d544a9f943 step4: composites — arrays, records, sets, pointers, strings, WITH; 74/74 tests green 3 viikkoa sitten
FileIO.def 83b9f7c6b2 second commit 4 viikkoa sitten
FileIO.mod 83b9f7c6b2 second commit 4 viikkoa sitten
FileIO.o 83b9f7c6b2 second commit 4 viikkoa sitten
Global.def 83b9f7c6b2 second commit 4 viikkoa sitten
Global.mod 83b9f7c6b2 second commit 4 viikkoa sitten
Global.o 83b9f7c6b2 second commit 4 viikkoa sitten
Instruction.def 83b9f7c6b2 second commit 4 viikkoa sitten
Instruction.mod 83b9f7c6b2 second commit 4 viikkoa sitten
Instruction.o 83b9f7c6b2 second commit 4 viikkoa sitten
Interpreter.def 83b9f7c6b2 second commit 4 viikkoa sitten
Interpreter.mod 26e9458c5d step5: open arrays, whole copy, strings, I/O, NIL trap; 99/99 tests green 3 viikkoa sitten
Interpreter.o 26e9458c5d step5: open arrays, whole copy, strings, I/O, NIL trap; 99/99 tests green 3 viikkoa sitten
Loader2.def 83b9f7c6b2 second commit 4 viikkoa sitten
Loader2.mod d544a9f943 step4: composites — arrays, records, sets, pointers, strings, WITH; 74/74 tests green 3 viikkoa sitten
Loader2.o d544a9f943 step4: composites — arrays, records, sets, pointers, strings, WITH; 74/74 tests green 3 viikkoa sitten
Local.def 83b9f7c6b2 second commit 4 viikkoa sitten
Local.mod 83b9f7c6b2 second commit 4 viikkoa sitten
Local.o 83b9f7c6b2 second commit 4 viikkoa sitten
M2c-git instructions.txt 732aaeb2b3 chore: add shared reference files (skills, grammar, instructions) 3 viikkoa sitten
MC64.mod b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
MC64.o b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
Memory.def 83b9f7c6b2 second commit 4 viikkoa sitten
Memory.mod 83b9f7c6b2 second commit 4 viikkoa sitten
Memory.o 83b9f7c6b2 second commit 4 viikkoa sitten
README.md ae221e2d1a docs: add project README and usage tutorial 15 tuntia sitten
SESSION.MD5.trans8to64 8f2c99e623 feat: run 16-bit module dependencies under mcint 3 viikkoa sitten
Stack.def 83b9f7c6b2 second commit 4 viikkoa sitten
Stack.mod 83b9f7c6b2 second commit 4 viikkoa sitten
Stack.o 83b9f7c6b2 second commit 4 viikkoa sitten
gnu-m2-grammar.txt 732aaeb2b3 chore: add shared reference files (skills, grammar, instructions) 3 viikkoa sitten
img1.png 732aaeb2b3 chore: add shared reference files (skills, grammar, instructions) 3 viikkoa sitten
mc64 d544a9f943 step4: composites — arrays, records, sets, pointers, strings, WITH; 74/74 tests green 3 viikkoa sitten
mcd.o 83b9f7c6b2 second commit 4 viikkoa sitten
mcint 26e9458c5d step5: open arrays, whole copy, strings, I/O, NIL trap; 99/99 tests green 3 viikkoa sitten
mcint.mod b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mcint.o b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkdemo b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkdemo.mod b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkdemo.o b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkdep b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkdep.mod b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkdep.o b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkdtest b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkdtest.mod b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkdtest.o b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkread b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkread.mod b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
mkread.o b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
skills.html 732aaeb2b3 chore: add shared reference files (skills, grammar, instructions) 3 viikkoa sitten
skills.jpeg 732aaeb2b3 chore: add shared reference files (skills, grammar, instructions) 3 viikkoa sitten
skills.md 732aaeb2b3 chore: add shared reference files (skills, grammar, instructions) 3 viikkoa sitten
skills.pdf 732aaeb2b3 chore: add shared reference files (skills, grammar, instructions) 3 viikkoa sitten
skills.png 732aaeb2b3 chore: add shared reference files (skills, grammar, instructions) 3 viikkoa sitten
trans8to64 b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
trans8to64.mod b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
trans8to64.o b0c26bb9f4 step2.1: rename 64-bit images .MCD -> .MC4 per spec sections 8 and 14 3 viikkoa sitten
trans8to64dbg 83b9f7c6b2 second commit 4 viikkoa sitten
tutorial.md ae221e2d1a docs: add project README and usage tutorial 15 tuntia sitten

README.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 pointers | | 256 | link — next module in chain | | 264 | name — 16 chars, NUL-padded | | 280 | loadAddr | | 288 | checksum — u32 | | 292 | flags (bit 2 = TOINIT) | | 293 | varCount | | 294 | depCount | | 295 | pad | | 296 | procsAddr — location of the procedure table | | 304 | varSizes — varCount × u64 byte sizes |

  • Procedure table — N contiguous 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 — depCount names appended at the image tail (8 NUL-padded bytes each); the loader loads dependency modules into low MTBL slots 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 extended 0x40 sub-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.MC4 echoes both lines through SYSTEM service 2 (0).
  • ./mkdep → STAK.MC4 + DEP.MC4; ./mcint DEP.MC4 executes an extern call into STAK through MTBL[0] and prints cross-module ok (0).

Related projects

Siblings in MyWork/:

  • m2compiler-V1 — M2c, the single-pass compiler (Coco/R M2c.atg + SymTab/MGen), step 11, 158/158 tests; the original .MC4 consumer.
  • m2compiler-V2 — M2comp, the Blaise front/back (AST) compiler, step 7, 65/65 tests; its run_tests.sh defaults to MCINT=../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.md only — no disassembly or analysis of classic .MCD binaries.
  • Deterministic build order (§16.1), pure ISO dialect (gm2 -fiso).
  • Every instruction in the demos is hand-encoded: mkd*.mod generators are the known-good .MC4 encoders used as ground truth for the compilers.
  • SESSION.MD5.trans8to64 stores a baseline MD5 of trans8to64.mod: any step that rewrites that file without updating the lock note is a session bug.