Appendix J.2 - SRM-D Configuration and Initialization

<< Click to Display Table of Contents >>

Navigation:  ASA-EMulatR Reference Guide > Introduction > Appendix > Appendix J - SRM Firmware Topic Hive >

Appendix J.2 - SRM-D Configuration and Initialization

This section covers all SRM-D configuration parameters, the loader config struct and its validation invariants, the snapshot directory and filename conventions, and the full initialization flow from Phase 14 boot bifurcation through copy loop exit.

 


 

J.2.1 INI Settings

 

All SRM-D parameters are sourced from the ASA-EMulatR.ini file at startup. Two sections govern snapshot behavior: [MemoryMap] controls the decompressor load addresses and [ROM] controls snapshot enable, location, and validation policy.

 

[MemoryMap]

SrmBase = 0x900000 ; PA where decompressor is loaded

SrmInitialPC = 0x900001 ; CPU start PC (SrmBase | PAL mode bit)

SrmDonePC = 0x200000 ; PC below this = decompression complete

SrmMirrorPA = 0x000000 ; PA mirrored for copy-loop source

SrmMaxSteps = 200000000 ; Safety step limit (abort on stall)

 

[ROM]

SrmRomFile = ES40_V6_2.EXE ; Path to SRM EXE image

SrmSnapshot = true ; false or absent = disabled

SrmSnapshotDir = ; Optional override (see J.2.3)

 

SrmSnapshot is the master enable gate. It defaults to false when absent -- no snapshot behavior is activated on a fresh installation. Setting it to true enables the full bifurcated boot path described in Section J.2.4.

 

Critical note on SrmBase / SrmInitialPC. These values are path-dependent. For self-decompressing .EXE images (Load Path A), SrmBase=0x900000 and SrmInitialPC=0x900001. For pre-decompressed flat .bin images (Load Path B -- QEMU export), SrmBase=0x0 and SrmInitialPC=0x8001. Using the wrong combination will corrupt the copy loop source or produce an invalid PAL mode entry.

 

J.2.2 SrmLoaderConfig

 

SrmLoaderConfig carries all resolved INI values into decompress(), eliminating all hardcoded load addresses from the loader. It is populated by SrmLoaderConfig::fromSettings(QSettings&) and validated by isValid() before decompression begins.

 

struct SrmLoaderConfig {

 quint64 loadPA = 0x900000; // PA where decompressor is placed

 quint64 startPC = 0x900001; // loadPA | PAL mode bit

 quint64 palBase = 0x900000; // PAL_BASE initial value

 quint64 donePC = 0x200000; // PC below here = done

 quint64 mirrorPA = 0x000000; // Copy-loop source mirror

 int maxSteps = 200000000;

};

 

isValid() enforces two invariants before any decompression occurs:

 

1. startPC == (loadPA | 1ULL) -- the PAL mode bit must be set in the start PC. A mismatch causes the CPU to enter the decompressor in normal mode, producing an immediate illegal instruction fault.

 

2. loadPA != mirrorPA -- the decompressor load address and the copy-loop mirror address must not alias the same physical page. Aliasing causes the copy loop to read from the same memory it is writing to, producing corrupted output.

 

J.2.3 Snapshot Directory and Filename

 

When SrmSnapshot=true and SrmSnapshotDir is absent or empty, the snapshot directory resolves to:

 

<applicationDirPath>/snapshot/

 

where applicationDirPath is QCoreApplication::applicationDirPath(). This places snapshots in a dedicated subdirectory alongside the binary, clearly separated from ROM images and build artifacts. The directory is created automatically on first run.

 

The snapshot filename is constructed from the ROM stem and a truncated ROM hash:

 

<romStem>_<hash8>.axpsnap

 

Loading ES40_V6_2.EXE produces, for example:

 

D:\EmulatR\EmulatRAppUni\bin\debug\snapshot\ES40_V6_2_9689831940DA2165.axpsnap

 

The hash component guarantees that different ROM revisions produce different snapshot filenames with no collision. The SrmSnapshotDir override allows the snapshot directory to be placed on a separate drive or shared across build configurations.

 


 

J.2.4 Phase 14 Boot Bifurcation

 

SRM firmware loading occurs in Phase 14 of the emulator initialization sequence in Emulatr_Init.cpp. Phase 14 selects between the snapshot fast path and the decompressor slow path based on snapshot availability and validity.

 

Phase 14 -- SRM Firmware Load:

 

IF SrmSnapshot=false OR SrmSnapshotDir not resolvable:

 -> SLOW PATH: decompress() only, no snapshot save

 

IF SrmSnapshot=true:

 snapshotPath = resolveSnapshotPath(romStem, romHash)

 IF snapshotPath exists on disk:

 result = m_srmRomLoader.loadSnapshot(snapshotPath, ...)

 IF result.success:

 -> FAST PATH: snapshot loaded, boot handoff at result.finalPC

 ELSE (validation failed):

 -> Log specific failure reason

 -> Delete stale snapshot file

 -> Fall through to SLOW PATH

 IF snapshotPath does not exist (first run):

 -> SLOW PATH (no snapshot yet)

 

SLOW PATH:

 SrmLoaderConfig cfg = SrmLoaderConfig::fromSettings(m_settings)

 result = m_srmRomLoader.decompress(cfg, ...)

 IF result.success AND SrmSnapshot=true:

 saveSnapshot(snapshotPath, result, regions, ...)

 -> Log "Snapshot saved to <path>"

 

The bifurcation point is clean and unconditional. Either path produces an identical SrmRomLoadResult with success=true, finalPC, and finalPalBase populated. Phase 15 and later do not need to know which path was taken.

 

J.2.5 SrmRomLoadResult

 

SrmRomLoadResult is the output contract of both the decompressor and the snapshot loader. All downstream consumers use only this struct and are fully decoupled from the load mechanism.

 

struct SrmRomLoadResult {

 bool success = false;

 quint64 finalPC = 0; // Boot handoff PC (with PAL bit)

 quint64 finalPalBase = 0; // PAL_BASE at handoff

 quint64 cyclesExecuted = 0; // Decompressor cycle count

 double elapsedMs = 0.0; // Wall-clock time (ms)

 size_t headerSkip = 0; // ROM header bytes skipped

 // Phase timing (decompressor path only -- zero on snapshot load)

 quint64 initSteps = 0;

 quint64 copyLoopSteps = 0;

 quint64 postCopySteps = 0;

 double copyLoopMs = 0.0;

 // Snapshot metadata

 bool fromSnapshot = false; // true = loaded from .axpsnap

 QString snapshotPath;

 QString errorMessage;

 quint64 bootPC() const { return finalPC; }

 quint64 cleanPC() const { return finalPC & ~1ULL; }

 bool isPalMode() const { return (finalPC & 1ULL) != 0; }

};

 

Verified values from first clean ES40 decompressor run (2026-03-06):

 

Field

Value

Notes

finalPC

0x00000000000005C0

Boot entry in decompressed firmware at PA 0x0

finalPalBase

0x0000000000900000

PALcode base = decompressor load address

cyclesExecuted

5,767,331

Total instructions executed by decompressor

elapsedMs

999,224 ms (16 min 39 sec)

Debug build with full trace I/O

initSteps

158

Setup before copy loop entry at 0x9002AC

postCopySteps

5,767,165

Copy loop + actual decompressor (see J.2.7)

 

J.2.6 Decompressor Phase Tracking

 

decompress() divides execution into three internal phases tracked against milestone PCs.

 

Phase 1 -- Init (158 steps). Decompressor startup from startPC=0x900001. CPU probe sequence: BSR to 0x900060, layer detection via LDA/ZAPNOT/LDAH/BEQ cascade, outer loop counter initialization via SRL/SUBQ/SLL at 0x9002E0. Enters copy loop at kCopyLoopPC=0x9002AC.

 

Phase 2 -- Copy Loop. The inner copy loop at 0x9002AC--0x9002C8 copies the ROM payload from PA 0x0 (mirror) into PA 0x900000 (decompressor home), 8 bytes per iteration, 524,288 total quadword iterations. The BNE at 0x9002C8 falls through when R1 reaches zero. Exit detected at kCopyExitPC=0x9002CC.

 

Phase 3 -- Post-Copy / Decompressor (5,767,165 steps). After copy loop exit the BLT at 0x9002D0 falls through (R4 positive), R30 is saved, R0 is loaded with R26+0x5C0 = 0x5C0, and the JSR at 0x9002DC jumps to the actual decompressor engine at PA 0x5C0. Completes when PC drops below donePC=0x200000.

 

Note on copyLoopSteps reporting. The current implementation detects kCopyExitPC against the fetch-stage PC rather than the commit-stage PC. The BNE at 0x9002C8 causes a speculative fetch advance to 0x9002CC before branch resolution redirects to 0x9002AC, causing the copy loop phase to report 8 steps (one iteration) rather than the actual ~5.7M steps. This is a cosmetic reporting inaccuracy only -- execution is correct. It will be corrected when phase tracking is refactored to compare against the writeback-stage committed PC.

 

J.2.7 Copy Loop Verified Exit Sequence

 

Committed instructions at copy loop exit, verified from the 4.19 million line trace captured 2026-03-06:

 

Instr 05767318 0x9002B8 SUBQ R1,#0x08,R1 R1 = 0x0000000000000000

Instr 05767322 0x9002C8 BNE R1,0x9002AC not taken (R1==0) -- fall through

Instr 05767323 0x9002CC HW_ST R0,0x0(R26) last quadword stored

Instr 05767324 0x9002D0 BLT R4,0x9002E0 not taken (R4=0x4000... positive)

Instr 05767325 0x9002D4 BIS R31,R0,R30 R30 = 0x0000000000400000

Instr 05767326 0x9002D8 LDA R0,0x5C0(R26) R0 = 0x00000000000005C0

Instr 05767327 0x9002DC JSR R31,(R0) -> 0x00000000000005C0 BOOT HANDOFF

 

R26=0x0 confirms the decompressed firmware landed at PA 0x0. R0=0x5C0 is the boot entry point. R30=0x400000 is the end-of-firmware marker preserved for the decompressor engine. The JSR at instruction 05767327 is the precise capture point for the snapshot save.

 

See Also: J.3 - SRM-D Snapshot Mechanics; J.3 - SRM-D Snapshot Mechanics (file format, integrity fields, validation failure handling); J.5 - SRM-D Full Cycle Execution ;memoryLib/SrmRomLoader.h; memoryLib/SrmRomLoader.cpp;ASA-EMulatR.ini [MemoryMap] and [ROM] sections.