فهرست منبع

docs: add project README and usage tutorial

Eric Streit 14 ساعت پیش
والد
کامیت
ae221e2d1a
2فایلهای تغییر یافته به همراه543 افزوده شده و 0 حذف شده
  1. 219 0
      README.md
  2. 324 0
      tutorial.md

+ 219 - 0
README.md

@@ -0,0 +1,219 @@
+# 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`](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:
+
+```sh
+./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:
+
+```sh
+# 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
+
+```text
+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.

+ 324 - 0
tutorial.md

@@ -0,0 +1,324 @@
+# MC64 — tutorial
+
+This tutorial walks you through the MC64 64-bit Modula-2 virtual machine: the
+toolchain, the assembled sample images, the image format and the two most
+satisfying facts about the machine — that a 5 KB text source hands you a
+running program, and that every demo image in this repository is *hand-encoded*
+byte by byte.
+
+All commands below were verified against a freshly built toolchain. The
+repository already ships built executables (`mcint`, `mkdemo`, …) and sample
+images in `MC4/`, so you can either use those directly or rebuild from source
+as shown in step 2.
+
+## 1. What's here
+
+| File/dir | Role |
+|---|---|
+| `mcint` | the interpreter launcher: `mcint module.MC4` |
+| `mkdemo` | writes `boot.MC4`, a one-procedure image that prints a string |
+| `mkdtest` | writes `example.MC4`, a 24-instruction opcode exercise |
+| `mkread` | writes `readtest.MC4`, an interactive stdin demo |
+| `mkdep` | writes `STAK.MC4` + `DEP.MC4`, a cross-module dependency demo |
+| `trans8to64` | translates classic 16-bit `.MCD` binaries to 64-bit `.MC4` |
+| `docs/mc64-spec.md` | the normative specification (`§` references below) |
+
+Prerequisites: GNU Modula-2 (`gm2 -fiso`), a POSIX shell, and the usual
+`cp`/`printf`/`xxd` utilities.
+
+## 2. Building the VM and the tools
+
+Use a scratch build directory so the repository stays untouched:
+
+```sh
+mkdir -p /tmp/mc64-tut && cd /tmp/mc64-tut
+cp -r /path/to/m-code-64/src/. .
+```
+
+Compile the ten library modules (deterministic order, spec §16.1):
+
+```sh
+for m in Memory Local Global Stack Instruction Extended Interpreter Loader2 \
+         Console FileIO; do
+  gm2 -fiso -c "$m.mod"
+done
+```
+
+Link each tool. **Only the program module goes to the link step** — the
+libraries are the `.o` files. Passing several `.mod` files at once makes gm2
+emit one `main` per module and the link fails on duplicate `main` (§16.1).
+
+```sh
+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
+```
+
+All eight commands should link without diagnostics. (If you'd rather not
+build, skip to the next step and run the *shipped* binaries from the
+repository root — they are equivalent.)
+
+## 3. Your first image
+
+`mkdemo` writes `boot.MC4`, a single-module image whose one procedure prints a
+string through the *write string* host service:
+
+```sh
+./mkdemo && ls -l boot.MC4
+./mcint boot.MC4
+```
+
+```text
+-rw-rw-r-- 1 ... 396 ... boot.MC4
+Hello MC64!
+```
+
+Exit status is 0: the guest reached `end_program` (`SYSTEM` service 0). The
+string is written by `SYSTEM` service 1 (`write NUL-terminated string at
+param`, §9.3); the image pushes the buffer address, pushes service `1`, calls
+`system` (opcode `0C3H`), then executes `end_program` (`50H`). In Modula-2,
+`13C` and `12C` in `mkdemo.mod` are *octal* literals — `0B` (vertical tab) and
+`0A` (line feed) — so the demo string really is `"Hello MC64!"` + VT + LF.
+
+`mc64` is a tiny wrapper that boots `boot.MC4` and then prints `[vm end]`:
+
+```sh
+./mc64
+```
+
+```text
+Hello MC64!
+[vm end]
+```
+
+## 4. `mcint` argument behaviour
+
+```sh
+./mcint                    # no argument -> usage, exit 0
+./mcint nope.MC4           # missing/bad file -> loader fatal, exit 1
+```
+
+```text
+usage: mcint module.MC4
+loader: cannot open module file
+```
+
+`mcint` takes the image path as its first program argument (`NextArg` +
+`ReadString (ArgChan (), fname)`, then `Loader2.Call`). A missing or malformed
+image aborts through the loader, exit 1.
+
+## 5. The opcode exercise: `example.MC4`
+
+`mkdtest` writes a 1350-byte image that performs 24 instruction checks —
+arithmetic, bit flips, the extended `0x40` sub-dispatch, quads and a few
+addressing forms — and prints each one:
+
+```sh
+./mkdtest && ./mcint example.MC4
+```
+
+```text
+mc64 test vm :: opcode exercise
+g after 10*2+1 = 2047
+add 10+6 = 16
+sub 20-7 = 13
+mul 6*7 = 42
+div 100/8 = 12
+mod 100%8 = 4
+bitand FF&0F = 15
+bitor F0|0F = 255
+bitxor FF^0F = 240
+power2 2^6 = 64
+uc_add (2^32-1)+2 = 1
+uc_mul 1234567890*2 = 2469135780
+uc_div 2^36/2^16 = 1048576
+uc_mod (2^32+6) rem 16 = 6
+long_negate F0F0F0F0 low = 252645136
+field_mask (1<<8)-(1<<2) = 252
+real_add 3.5+2.25 = 5
+dup+add check = 5
+swap check = 3
+load_global_dw g = 2047
+local_dw FP[-2] = 99
+drop then 12 = 12
+jp_fwd skip -> 7 = 7
+copy_block -> abc
+```
+
+Highlights:
+
+- `uc_*` lines exercise the **extended sub-dispatch `0x40`** (§10.2) that
+  provides unsigned 64-bit (LONGCARD) arithmetic — `uc_add`, `uc_mul`,
+  `uc_div`, `uc_mod` — plus `long_negate` and `build_field_mask`.
+- `real_add` pushes two REALs (`0x8E/0x8F`-family constants) and a `real_add`
+  opcode: 64-bit floats are first-class scalars.
+- `jp_fwd`/`jpfalse_back` prove the 64-bit relative jumps; `copy_block`
+  (`0x30`) pops `(size, src, dst)` and memcpy's a whole string; the
+  `store_indexed_byte` line shows the byte-addressed CHAR path.
+
+## 6. Interactive stdin: `readtest.MC4`
+
+`mkread` writes an image that reads **two lines** from the host standard input
+via `SYSTEM` service 2 and echoes each with a prefix (plus CRLF):
+
+```sh
+./mkread
+printf 'First line\nSecond line\n' | ./mcint readtest.MC4
+```
+
+```text
+you said: First line
+again: Second line
+```
+
+Service 2 (`read line`, §9.3) pulls characters up to (but not including) the
+line mark, NUL-terminates the buffer at `param`, and pushes the byte count
+(excluding the terminator) — empty line and immediate EOF push 0. Because the
+buffer is NUL-terminated, the guest can bounce it straight back through
+service 1. The two consecutive reads also prove the line mark gets consumed:
+the second read starts fresh.
+
+## 7. Cross-module calls: `STAK.MC4` + `DEP.MC4`
+
+`mkdep` synthesises the two-module dependency-chain test:
+
+```sh
+./mkdep && ./mcint DEP.MC4
+```
+
+```text
+cross-module ok
+```
+
+(notice: no trailing newline this time — the toast comes straight from the
+dependency's own `write_string` code). Exit status is 0.
+
+What happens:
+
+- `STAK.MC4` is a dependency-free sidecar with two procedures: an empty TOINIT
+  and a "write a host string" procedure.
+- `DEP.MC4` is the main image with `depCount = 1` and the dependency name
+  `STAK` appended (8 NUL-padded bytes) at the image tail. Its TOINIT flag
+  (descriptor `flags` bit 2) is set, so the loader boots it through
+  `procedureAddress(0,0)`.
+- The loader loads the dependencies **first**, into low `MTBL` slots (extern
+  module `0` = first import), then the main module, then runs TOINIT of the
+  dependencies and of the main image. `DEP` calls `EXTERN_CALL 0, proc1`, which
+  switches `GP := MTBL[0]` (the callee's data window) and jumps through
+  `STAK`'s procedure table.
+
+Run `./mcint DEP.MC4` from a directory that contains `STAK.MC4` — the loader
+searches for dependency images next to the requested one.
+
+## 8. A compiler-produced image, end to end
+
+The whole point of MC64 is to run code emitted by a Modula-2 compiler. The
+sibling `m2compiler-V2` project's `M2comp` accepts a source file and writes a
+`<Module>.MC4` image, which this interpreter then boots:
+
+```sh
+cd /path/to/m2compiler-V2
+./build.sh                        # the first time; reports "No errors detected."
+cp tests/r_flow.mod /tmp/mc64-tut/ && cd /tmp/mc64-tut
+/path/to/m2compiler-V2/M2comp r_flow.mod
+/path/to/m-code-64/mcint RFlow.MC4
+```
+
+```text
+Parsing r_flow.mod
+--- Symbol table ---
+...
+Parsed correctly          (M2comp's driver, abridged: it also dumps the AST
+                           and the -- Code RFlow -- backend listing)
+69                        (mcint: the program's ExitCode global)
+```
+
+`M2comp` compiles `MODULE RFlow` (a control-flow tour: IF/WHILE/REPEAT/LOOP/
+FOR) into a 918-byte `RFlow.MC4` image; `mcint` boots it and the epilogue that
+the compiler embeds for its `ExitCode : INTEGER` global prints `69`. Run the
+compiler's own suite (`./run_tests.sh` inside V2) to see the same pipeline at
+scale; its default interpreter path is `../m-code-64/mcint`.
+
+## 9. `trans8to64`: 16-bit images forward
+
+Classic Turbo Modula-2 images were 16-bit `.MCD` files. `trans8to64` converts
+one into a 64-bit `.MC4` image (usage below); it widens word operands 2→8
+bytes, rebuilds the procedure table, and re-emits the `"MC64"` header with a
+correct checksum:
+
+```sh
+./trans8to64                     # usage, exit 1
+./trans8to64 missing.MCD out.MC4 # bad input, exit 1
+```
+
+```text
+usage: trans8to64 in.MCD out.MC4
+trans8to64: cannot read input
+```
+
+For a valid single-module input it prints `trans8to64: in -> out : ok
+(<N> bytes)`. The repository deliberately ships no classic `.MCD` sample, and
+the tool documents its own limits in the `trans8to64.mod` header: it is
+runnable only for **dependency-free** modules (`extern` calls and module-table
+address tricks are not mapped), `0x8F` REAL constants decode as LONGINT, and
+16-bit `limit_check` is emitted as `DC 00` (dead code). `SESSION.MD5.
+trans8to64` records a baseline hash of the translator so accidental rewrites
+are caught.
+
+## 10. Anatomy of an image
+
+Every `.MC4` is a 64-byte header plus an *image* (descriptor + code + proc
+table). Read the tail of `boot.MC4`:
+
+```sh
+xxd -s 360 /path/to/m-code-64/MC4/boot.MC4
+```
+
+```text
+00000168: 3001 0000 0000 0000 0800 0000 0000 0000  ................
+00000178: 8c0e 4865 6c6c 6f20 4d43 3634 210b 0a00  ..Hello MC64!...
+00000188: 8d01 c350                                ...P
+```
+
+| bytes | file | image off | meaning |
+|---|---|---|---|
+| `30 01 …` | 360 | 296 | `procsAddr = 0x130 = 304` — procedure table at image offset 304 |
+| `08 00 …` | 368 | 304 | proc cell 0 = `8`; `procedureAddress(0) = 304 + 8 = 312` |
+| `8c 0e` | 376 | 312 | `call_rel 14`: jump 14 bytes ahead, into the string |
+| `… "Hello MC64!" 0b 0a 00` | 378 | 314 | the string, VT (octal `13C`), LF (octal `12C`), NUL |
+| `8d 01 c3 50` | 392 | 328 | `load_imm_byte 1`; `system`; `end_program` |
+
+The first 64 bytes are the file header; bytes 0–3 are the magic `"MC64"`. The
+descriptor then occupies image offset 0 (file 64): name at +264, flags
+(TOINIT = 4) at +292, varCount/depCount/pad at +293..295, procsAddr at +296.
+Finally, the checksum is the plain 32-bit sum of the image bytes (file
+64..end) **excluding** the four checksum bytes themselves at file 352..355
+(image 288..291) — written by the generators after laying out the image (§8.3).
+
+Boot an image and re-decode it in your head: it's 20 bytes of code driving the
+whole demo.
+
+## 11. Going further
+
+- Read `docs/mc64-spec.md` — §8 (loader/image), §9.3 (the host interface),
+  §10 (extended/quad sub-dispatches), §11 (the full opcode table), §12
+  (switch/string instructions), §14 (MCode/16 migration) and §15 (a minimal
+  encoded procedure, step by step).
+- The demo generators in `src/mkd*.mod` are small enough to read whole and are
+  the known-good `.MC4` encoders — treat them as ground truth when emitting
+  images from a compiler back end.
+- `docs/session-summary.md` documents every loader/interpreter milestone and
+  the tools-and-gotchas behind them (stack polarity, link rules, checksum,
+  dependency loading).
+- The compiler tutorials in `m2compiler-V1/tutorial.md` and
+  `m2compiler-V2/tutorial.md` cover producing images from Modula-2 source;
+  `docs/porting-plan-m2sp.md` sketches the largest target yet — Wirth's M2SP
+  compiler retargeted to emit MC4.