Memory management (RAM heap and Flash archive)
The memory manager divides about 24 KiB of user RAM among variables, temporaries, the floating-point stack, and the active program. The archive path moves variables between that RAM heap and Flash.
The RAM heap [standard]
The dynamic region runs from userMem (0x9D95) up to symTable (0xFE66). Two structures grow toward each other with free RAM in the middle:
flowchart TB
A["0xFE66 · symTable — top of user RAM"]
B["VAT — variable names + metadata<br/>type, data ptr/page, name · grows DOWNWARD ↓"]
C["( free RAM )"]
D["user data — variable contents<br/>grows UPWARD ↑"]
E["0x9D95 · userMem — bottom of user RAM"]
A --- B --- C --- D --- E
style C fill:#1b1b1b,stroke-dasharray:5 5
VAT entry layout: type, data ptr/page, name — see variables-vat.md.
Boundary/work pointers (clustered at 0x9820-0x983A) [confirmed]:
| Ptr | Addr | Role |
|---|---|---|
tempMem | 0x9820 | base of the temporary area |
fpBase | 0x9822 | floating-point stack base |
FPS | 0x9824 | FP stack pointer (grows; _PushReal/_PopReal) |
OPBase | 0x9826 | base of OP/symbol scratch |
OPS | 0x9828 | OP/symbol scratch stack pointer (top) |
pTemp | 0x982E | temp-variable pointer |
progPtr | 0x9830 | currently-executing program pointer |
pagedBuf | 0x983A | paged scratch buffer |
_MemChk reports free RAM as OPS - FPS + 1: the inclusive span between the
floating-point stack and the operand/symbol stack in the middle of the region.
User data grows upward, the VAT grows downward, and a variable resize shifts
everything above the resized object. [confirmed]
Core allocation primitives [confirmed]
_InsertMem(ram:0F81) — open a gap ofHLbytes at addressDEby shifting all memory above it up. It callsinsertmem_setup(ram:0F8B), which does theLDDRblock move (atram:0FA1), thendelmem_fixup_tail(ram:1398) to fix up pointers._InsertMemdoes not check free space itself — callers must ensure room first via_EnoughMem(the wrapper_ErrNotEnoughMematram:1735calls_EnoughMemthen jumps to_ErrMemoryatram:2721on shortfall)._DelMem(ram:1368) — the inverse: close a gap, shifting memory down._EnoughMem(ram:0FA6) — ensure N free bytes; if short, it walks the temp/scratch entries (9-byte stride frompTempdown toOPBase) and_DelVars reclaimable temporaries to make room. [confirmed]_MemChk(ram:0E20) — compute current free RAM.
Variable-creation bcalls — _CreateReal, _CreateStrng, _CreateAppVar,
_CreateRList, and others — share var_create_core at ram:1011.
_CreateReal at ram:10B8 jumps into that core. The core calls
var_create_gap at ram:0F0C, which moves the block and updates the temporary
and FP-stack pointers before registering the variable in the VAT. This path is
distinct from the public _InsertMem. See Variables & the VAT.
[confirmed]
Resident assembly programs need an additional rule: OS pointer repair updates
OS-owned pointer slots, not program counters or runtime-owned pointers. The
compiled Asm( path also caps its internal program-data size at 0x2000, below
the full ram:9D95–ram:BFFF span. See
Resident assembly programs for the launch copy,
cleanup, AppVar handle protocol, and archived streaming contract. [confirmed]
The familiar OP, display, graph, statistics, and context buffers are not one pool of universally safe scratch space. See Resident scratch RAM for direct clobber measurements, conditional ownership, and the bank-A page-mapping protocol. [confirmed]
Flash archive [confirmed]
To save scarce RAM, variables can be archived to Flash. The archive entry point is on flash page 0x07, while the low-level flash read/write/erase workers are on page 0x3D:
_Arc_Unarc(07:6248) — move OP1’s variable between RAM and the Flash archive (toggles the archive bit, then relocates the data and rewrites the VAT entry’s page to the Flash page)._FlashToRam(id5017→ body3D:6745) — copy archived data back into RAM. Archived vars are appended to Flash, which cannot be overwritten in place, so deleting one only marks it dead.archive_gc_collectat3C:7733rewrites live records in 64 KiB sector units and erases the old sectors.gc_show_screenat3C:7E0Ddisplays"Garbage"and"Collecting..."from page01. The collector also journals its phase in the inactive 8 KiB half of page3E. [confirmed]
_CleanAll is RAM cleanup (not Flash GC) [confirmed]: _CleanAll (07:52CF) compacts the floating-point stack down to tempMem (fpBase/FPS) and the OP/scratch stack down to pTemp (it sets OPBase = pTemp, LDDRs the live span down, and sets OPS to its new top), reclaiming temporary RAM after a command/expression finishes. It does not touch Flash.
Flash is erased a physical sector at a time but programmed byte by byte. archive_write_record at 3D:64AA calls _WriteAByte (8021) and _WriteFlashUnsafe (8087) through the Flash-control port 0x14. See Flash memory for the hardware and boot-bcall path, and Variables, archive & unarchive for record format and allocation. [confirmed]
_FlashToRam(3D:6745) copies archived bytes through a worker at0x8100.ram_worker_launcherat3D:678Cinstalls that worker. The same launcher also runs the internal certificate-page program worker. [confirmed]archive_find_free_span(3D:62C2) scans upward from page08to the dynamic App boundary fromarchive_app_boundary(3D:6413). The OS-only trace returns boundary0x29and selects08:4000. [confirmed]archive_write_record(3D:64AA) writes record states0xFEthen0xFC; the helpers at3D:7C8F,3D:7C93, and3D:7C97implement additional monotonic bit-clears. [confirmed]- Archive workers:
_Arc_Unarc(07:6248) →arc_ram_to_flash(07:6107, RAM→Flash) /arc_flash_to_ram(07:61F4, Flash→RAM). (_Arc_Unarcdispatches on the FindSym page byteB:B==0/in-RAM →6107archive,B≠0/in-Flash →61F4unarchive.)
The _FindSym VAT walk, public Flash workers, and normal garbage-collection path are byte-verified in Variables, archive & unarchive and Flash memory. TilEm and Wabbitemu restart successfully from each persistent phase marker. Physical power loss at those markers and cuts during busy commands remain untested. [confirmed] for the emulator command-boundary runs; [hypothesis] for physical interruption behavior.