# Step 4 — Composites (tag: `step4` to create) 74/74 tests green (49 regressions + 15 run + 10 rejection), mc64 24/24 + boot + readtest green. ## Goal Composite types (Phase 3): fixed ARRAYs (bounds, 1D/multi-D, unchecked scaled indexing, byte-packed CHAR/BOOLEAN), RECORDs (offsets, .field, WITH incl. nested), SETs (+ union, - difference, * intersection, inclusive `..` ranges), POINTERs (NEW/DISPOSE via VM ALLOCATE, `^`, NIL), strings (ARRAY OF CHAR literal assign). Open arrays deferred; whole array/record copy deferred except string literals. ## What was built - `SymTab`: `TypeSlots` (slots, 0=unsized), `TypeLo/Hi/Len`, `ArrayLo/Hi/Len`, `FieldOffset`, `NewSubB/NewArrayB/EnumAdd`, record `tNext` offsets, sized `FixPending` (locals re-based, base=lowest), pointer structural compatibility in `Assignable` (same-base Ptrs assignable). - `MGen`: multi-slot globals (`DeclVarSized`, `vBase/vSize`, `DVarCount=nVars`), `IdxScale` (unchecked base+(i-lo)*bytes), `FieldAdd`, `CopyBlock`, `AllocOp/DeallocOp` (40H 05H/06H), `BitXor`, `EmitString` (8CH inline), `StrLenOf/StrComp`, `Swap/LoadByte/StoreByte` (0DH/1DH for CHAR), `WithEnter/Exit/WithAddr` (temp globals, top-down field search). - `M2c.atg`: `VarDecl` sized, subrange literal bounds (`NewSubB`, else 230), `EnumAdd`, nested `NewArrayB`, value-composite params 230, `DesignHead` (Ptr→PushVar, Array/Record→PushAddr, Field→WithAddr), `DesignTail` (`[]` IdxScale + byte-sized CHAR, `.` FieldAdd, `^` with first-tail keep else Deref), `Fact` deref (LoadIndir/LoadByte), `AssignOrCall` (sfx→StoreIndir0/StoreByte, whole composite 210/230, string `Str→Array` copy with length check 210), `New/DispStat` (PushAddr/WithAddr + PushBytes + Alloc/Dealloc), set `+-*` (Or/And+Xor), string literals via `EmitString` (no 230), composite `=/#` rejected 213, `WithStat` (PushAddr/WithAddr + WithEnter, no 230). - `mc64/Extended,Loader2`: heap bump-up fix (was top-down colliding with stack at 16777208, giving heapAddr instead of 42). `SetHeapBase(next)` after loads, `AllocateHeap` grows up with `SP` check, `FREEMEM=SP-hp`. mkdtest 24/24 still 252 (exclusive VM kept; M2c ranges made inclusive via hi+1). ## Tests — 74/74 (49 + 15 + 10) 15 run: `t_arr` 39 (1D), `a_mat` 134 (2x2 multiply), `a_sieve` 129 (BOOLEAN sieve), `a_varpar` 150 (VAR array params), `a_enum` 21 (enum index), `t_rec` 30, `r_nest` 135 (nested records + nested WITH), `t_with` 37, `r_varpar` 303 (VAR record), `t_ptr` 42 (NEW/^/DISPOSE), `t_ptr0` 5 (NIL/=), `p_list` 21 (linked list + recursion), `t_setop` 22 (+*-), `s_range` 21 (ranges), `t_str` 11 (strings). 10 rejections, each exactly one error: 218, 217, 210 (array:=int), 216, 215, 219 (NEW non-ptr), 222, 230 (value array param), 210 (string too long), 210 (record:=int). ## Bugs found and fixed 1. `51H` store order wrong (addr on top) for addr,value order — use `70H` (`StoreIndir0`). 2. VM heap top-down collided with stack (16777208) — bump-up from `next` with `SP` check. 3. `FieldMask` exclusive hi (`(1<