DEFINITION MODULE MGen; (* MC64 image emitter for the M2c scalar subset (see M2c.atg). Buffers a single-module MC64 image and writes ".MC4" (current directory) at EndModule. The image satisfies the reference loader (Loader2): magic "MC64", descriptor with name, TOINIT flag, global sizes (one 8-byte slot per variable), two procedures (0 = user code, 1 = decimal print helper), relative procedure-table cells, and the checksum rule. Lowered: scalar globals (INTEGER/CARDINAL/CHAR/BOOLEAN/enums as 32-bit ops on slots, REAL as binary64), CONST initializers that are plain literals (emitted as startup stores), full scalar expressions on the stack machine, IF/CASE/WHILE/REPEAT/LOOP+EXIT/ FOR via E0/E1 jumps with fixups. Everything else is reported by the parser (error 230); this module only needs structurally valid placeholders for those cases. Test convention (no I/O in the language): if the source declares VAR ExitCode : INTEGER; (int family, VAR kind) the epilogue prints its value as decimal + CRLF through the embedded print procedure; otherwise the program just ends. Step 3 adds procedures: user procedures take table entries 1..N (0 = module body), run with ENTER frames, and are called via ED (global), EC (directly nested) or EE (display walk). *) TYPE LitStr = ARRAY [0 .. 63] OF CHAR; (* Literal text: "42", "-5", "0FFH", "3.14", "'a'", "TRUE". "" means "not a plain literal". *) PROCEDURE OpenModule (name: ARRAY OF CHAR); (* Resets all state; reserves slots 0..3 for the print helper. *) PROCEDURE SetModName (name: ARRAY OF CHAR); (* Sets the output image name (multi-file sessions: the program unit calls this; EndModule writes .MC4). *) PROCEDURE DeclVar (name: ARRAY OF CHAR); (* Assigns the next global slot to name (any type; one slot). *) PROCEDURE DeclVarSized (name: ARRAY OF CHAR; slots: CARDINAL); (* Assigns slots global slots to name (composites); 0 = empty record. *) PROCEDURE DeclConst (name: ARRAY OF CHAR; lit: LitStr; t: INTEGER); (* Assigns a slot and buffers a startup initializer from the literal text (t is a SymTab.TypeIndex). Empty lit buffers a 0. *) PROCEDURE DeclConstInt (name: ARRAY OF CHAR; v: INTEGER); (* Assigns a slot with an INTEGER startup value (enum literals). *) PROCEDURE BeginBody; (* Flushes buffered CONST initializers as startup stores. *) PROCEDURE EndModule; (* Emits the epilogue, the print procedure, the procedure table, the descriptor, the checksum, and writes the .MC4 file. *) PROCEDURE LoadVar (name: ARRAY OF CHAR); (* Pushes the global (placeholder push-0 if unknown). *) PROCEDURE StoreVar (name: ARRAY OF CHAR); (* Pops into the global (dropped if unknown). *) PROCEDURE PushVar (name: ARRAY OF CHAR); (* Pushes a variable's value, frame-aware: globals via the global table, same-frame locals/params direct, outer frames via the display, VAR params indirect. Placeholder push-0 if unknown. *) PROCEDURE LoadTemp (t: INTEGER); PROCEDURE StoreTemp (t: INTEGER); (* Direct slot access for compiler temporaries. *) PROCEDURE TempGlobal (): INTEGER; (* Allocates a fresh hidden global slot, returns its index. *) PROCEDURE PushInt (v: INTEGER); (* Small encodings for 0..15; negatives via 0,mag,sub. *) PROCEDURE PushBits (b: LONGCARD); (* 8E + 8 bytes: CARDINAL values and REAL bit patterns. *) PROCEDURE ParseInt (s: ARRAY OF CHAR; VAR v: INTEGER): BOOLEAN; (* Decimal, H-suffixed hex, optional leading '-'. *) PROCEDURE ParseCard (s: ARRAY OF CHAR; VAR v: CARDINAL): BOOLEAN; (* Decimal or H-suffixed hex (for literals past MAXINT). *) PROCEDURE ParseReal (s: ARRAY OF CHAR; VAR b: LONGCARD): BOOLEAN; (* Decimal with optional fraction/exponent/sign to IEEE bits. *) PROCEDURE CharOrd (s: ARRAY OF CHAR): INTEGER; (* ORD of a 1-character literal such as 'a'. *) PROCEDURE NegFold (a: ARRAY OF CHAR; VAR q: LitStr); (* Folds a unary minus into literal text ("5" -> "-5"). *) PROCEDURE IsLit (s: ARRAY OF CHAR): BOOLEAN; (* TRUE for non-empty literal text. *) (* ---- stack operators (one opcode each, see mc64-spec.md §11) ---- *) PROCEDURE Add; PROCEDURE Sub; PROCEDURE MulU; PROCEDURE DivU; PROCEDURE ModU; PROCEDURE MulI; PROCEDURE DivI; PROCEDURE ModI (t: INTEGER); (* Signed a MOD b with truncation semantics via temp slot t: a - (a DIV b) * b. Caller passes TempGlobal(). *) PROCEDURE RealAdd; PROCEDURE RealSub; PROCEDURE RealMul; PROCEDURE RealDiv; PROCEDURE And; PROCEDURE Or; PROCEDURE Not; PROCEDURE Power2; (* Push (1 << pop) for set element masks. *) PROCEDURE FieldMask; (* (1<= 3). Same-frame VAR-param slots hold addresses: read them with LoadLocal, then LoadIndir0. *) PROCEDURE LoadIndir0; PROCEDURE StoreIndir0; (* 60H/70H slot-0 indirect (through VAR-param addresses). *) PROCEDURE FrameAddr (sl: INTEGER; np: CARDINAL); (* Pushes (display-frame(np) + slot*8) as a byte address. *) PROCEDURE LoadIndir; PROCEDURE StoreIndir; (* 41H/51H through a computed address. *) PROCEDURE LocalAddr (sl: INTEGER); (* 80H address of a same-frame slot. *) PROCEDURE GlobalAddr (name: ARRAY OF CHAR); (* 81H address of a global (push-0 if unknown). *) PROCEDURE PushAddr (name: ARRAY OF CHAR); (* Pushes a VAR-actual address: slot addresses for plain variables, slot contents for VAR params; display-aware. Push-0 if unknown. *) PROCEDURE StoreSetup (name: ARRAY OF CHAR); (* Emits an early store address (VAR-param or outer-frame targets) before the value expression; nothing for direct targets. *) PROCEDURE StoreFinish (name: ARRAY OF CHAR); (* Emits the store for a storable variable (global, param, local, VAR-param, outer frame). *) (* ---- composites: arrays, records, pointers, sets, strings ---- *) PROCEDURE IdxScale (lo: INTEGER; elemBytes: CARDINAL); (* [base idx] -> base+(idx-lo)*elemBytes (unchecked, 32-bit). *) PROCEDURE FieldAdd (offSlots: CARDINAL); (* [base] -> base+offSlots*8 (no-op when 0). *) PROCEDURE CopyBlock; (* 30H copy_block: pops size, src, dst (push dst, src, size). *) PROCEDURE LoadByte; (* [addr] -> [byte at addr] (0DH with index 0). *) PROCEDURE StoreByte; (* [addr value] -> [] (1DH with index 0). *) PROCEDURE PushBytes (n: CARDINAL); (* Pushes n as byte count (for ALLOCATE/copy sizes). *) PROCEDURE AllocOp; (* 40H 05H ALLOCATE: pops size, p-addr; *p := heap block. *) PROCEDURE DeallocOp; (* 40H 06H DEALLOCATE: pops size, p-addr; free + *p := NIL. *) PROCEDURE BitXor; (* E9H bitset symmetric difference (for SET - via AND/NOT). *) PROCEDURE EmitString (s: ARRAY OF CHAR); (* 8CH inline NUL-terminated string: pushes its code address. s includes quotes (e.g. "'abc'"); longer than 255 rejected. *) PROCEDURE StrLenOf (s: ARRAY OF CHAR): CARDINAL; (* Length without quotes (bytes excluding NUL). *) PROCEDURE StrComp; (* C4H string compare (see spec 12.1). *) (* ---- WITH statement (record scope) ---- *) PROCEDURE WithEnter (typ: INTEGER); (* Pops record address, stores to fresh temp, pushes WITH level. typ is a SymTab.TypeIndex (record type). *) PROCEDURE WithExit; (* Pops one WITH level. *) PROCEDURE WithDepth (): CARDINAL; (* Current WITH nesting (0 outside). *) PROCEDURE WithAddr (name: ARRAY OF CHAR); (* Pushes field address for visible WITH field (or push-0 if absent). *) PROCEDURE PrintNum (): CARDINAL; (* Number of the embedded decimal-print helper (maxNum+1). *) PROCEDURE CallPrint; (* Calls the print helper (expects value on stack), drops result. *) PROCEDURE SysCall; (* C3H SYSTEM host call (pops id+param; see spec 9.3). *) (* ---- procedures (see §5.2) ---- *) PROCEDURE ProcEntry (num: INTEGER; nLoc: CARDINAL); (* Records the procedure address and emits ENTER for nLoc locals. *) PROCEDURE CallProc (num: INTEGER); (* ED: call a global procedure (OFP = NIL). *) PROCEDURE CallNested (num: INTEGER); (* EC: call a directly nested procedure (OFP = FP). *) PROCEDURE CallDisplay (num: INTEGER; np: CARDINAL); (* 11H np display walk, then EE call with that frame. *) PROCEDURE Leave (nPar: CARDINAL; func: BOOLEAN); (* 84H (proper) or 85H (function) procedure leave. *) PROCEDURE ModInitBegin (): INTEGER; (* Opens a module init body as a parameterless proper procedure, recorded for the startup calls in BeginBody. The number comes from SymTab's shared proc pool. -1 when full. *) PROCEDURE ModInitEnd (num: INTEGER); (* Closes a module init body (no-op for num < 0). *) (* ---- actual parameters (nested-call-safe frame stack) ---- *) PROCEDURE ActBegin (pn: ARRAY OF CHAR); (* Opens an actual-parameter frame for a call to pn. *) PROCEDURE ActValue (t: INTEGER; v: BOOLEAN; vn: ARRAY OF CHAR): INTEGER; (* Checks one actual against its formal (0 = ok): VAR formals need a plain variable of identical type (address pushed), value formals need assignment compatibility (INTEGER widens to REAL). Always consumes the value (temp store). 1 means error 233. *) PROCEDURE ActIsVarNext (): BOOLEAN; (* TRUE if the current formal (actSt.n) is a VAR parameter (known callee, in range). Used to keep addresses (not values) for VAR a[i] actuals. *) PROCEDURE StashAddr; (* Duplicates the address on top of stack into a per-actual temp of the current call frame (VAR actuals with tails). *) PROCEDURE ClrStash; (* Invalidates the stashed address of the current actual (a value-combining operator followed). *) PROCEDURE NoteSfx (b: BOOLEAN); (* Records tails-present for the current actual (top frame). *) PROCEDURE NoteIdx (b: BOOLEAN); (* Records index-only tails for the current actual (top frame). *) PROCEDURE ActEnd (pn: ARRAY OF CHAR; sfx, inExpr: BOOLEAN): INTEGER; (* Verifies arity and callee kind, reloads actuals reversed and emits the depth-correct call (0 = ok, 1 = error 233). *) PROCEDURE ActBeginNum (num: INTEGER); (* Opens an actual-parameter frame for the procedure numbered num (exported module procs, invisible by name after ExitModule). *) PROCEDURE ActEndNum (num: INTEGER; inExpr: BOOLEAN): INTEGER; (* By-number ActEnd: verifies arity, reloads actuals reversed and emits the global call (module procs are always global-level). 0 = ok, 1 = error 233. *) (* ---- control flow (E0/E1 with fixups) ---- *) PROCEDURE NewLabel (): INTEGER; PROCEDURE DefLabel (id: INTEGER); PROCEDURE Jmp (id: INTEGER); PROCEDURE Jz (id: INTEGER); (* Jump if false (pops the condition). *) PROCEDURE PushLoop (exit: INTEGER); PROCEDURE PopLoop; PROCEDURE TopLoop (VAR exit: INTEGER): BOOLEAN; (* Innermost LOOP exit label; FALSE outside any LOOP. *) PROCEDURE NoSupEnter; PROCEDURE NoSupExit; PROCEDURE CopyName (s: ARRAY OF CHAR; VAR d: ARRAY OF CHAR); (* Copies zero-terminated strings between any char arrays. *) PROCEDURE NoEmitEnter; PROCEDURE NoEmitExit; (* Suppresses code emission (not literal capture) in declarative contexts such as CONST initializers and subrange bounds. *) PROCEDURE NoSup (): BOOLEAN; (* Guard depth for unsupported constructs: while > 0 the grammar suppresses follow-on 230 errors to avoid cascades. *) END MGen.