Embedded mission setup: translated effects and checks
The seven FE-prefixed x86 blocks in four archive-04 records now have explicit effect descriptions in scenarios.json, readable reference operations in mission_native.py, and byte-addressed disassembly plus differential results in native-verification.json. The scenario browser displays the descriptions beside the stored setup.
All offsets below are offsets in the decompressed resource. Object offsets refer to the original 112-byte runtime object, accessed through SS:BP. Object 0 is the player; other indices follow setup allocation order across all three groups. BP is the most recently allocated object, not invariably the player.
Native calling convention
BE12–BE19 saves AX, sets BX to the helper-pointer table at BEDD, and calls SI. Every observed block advances SI by its own length. Some return directly; others tail-call a helper through BX. The parser restores AX, preserving its counted-object total.
| Pointer | Handler | Effect |
|---|---|---|
| BEDD | C79D | Initialise boost/weapon energy, active boost, pitch, and placement |
| BEDF | 8E24 | Set motion parameters, two clock-relative fields and callback 8DDD |
| BEE1 | 5E59 | Write AL to weapon-1 counters 5E14 and 5E11 |
C79D stores DX to boost reserve 8540 and weapon energy 7FA6. Its incoming carry flag selects active boost high byte 7F0E: clear gives 0; set gives 28h. AX sets pitch. It then enters C7C8, which subtracts the current model's header word at +7 from CX to produce the object's height, stores DI as heading, and refreshes cell and position caches. This model-dependent adjustment must survive a faithful reconstruction.
The seven blocks
| Resource / record / block | Current object | Effects |
|---|---|---|
| 04_012 / 2 / 1AAC | Player | Speed magnitude 200, forward velocity 400; clear flag bit 4; pitch FF00, heading EC00, nominal height 2560; empty boost and weapon energy |
| 04_012 / 4 / 1D51 | Player | Sub-cell X=C8, Y=BB; rewrite following objects 1 and 2 to the player's cell; pitch 0, heading 0F20, nominal height 264; empty boost and weapon energy |
| 04_012 / 4 / 1D80 | Object 1 | Increment height high byte, equivalent to adding 256 with 16-bit wrap |
| 04_012 / 4 / 1D95 | Object 2 | Sub-cell X=4B, Y=46; subtract 2400 from height; speed magnitude 200, pitch 0C00; install callback 8DDD |
| 04_014 / 2 / 0F91 | Player | Speed magnitude 300, forward velocity 600; clear flag bit 4 and weapon-1 counters; pitch FE00, preserve heading, nominal height 4096; empty boost and weapon energy |
| 04_015 / 0 / 0028 | Player | Patch later native instruction with player's model pointer; clear flag bit 4; pitch F500, preserve heading, nominal height 2432; boost/weapon energy 9FFF and active boost high byte 28 |
| 04_015 / 0 / 0060 | Object 2 | Assign the model pointer embedded by the earlier block |
All pitch/heading values in the table are raw hexadecimal words; other quantities are decimal unless indicated. The descriptions intentionally avoid assigning scene identities from motion alone.
Two dependencies are particularly important:
- 1D51 modifies future object records. After advancing SI to 1D79, it writes the player's X cell byte to SI+3 and SI+11h, and Y to SI+5 and SI+12h. These are the high bytes of object 1's absolute coordinates and the cell bytes of object 2's moving-object template. Their original sub-cell values are retained. The map still displays stored source coordinates and labels that limitation.
- 0028 modifies future executable code. After advancing SI to 0044, it copies the player's model pointer to SI+22h = resource offset 0066, the immediate operand of
MOV word ptr [BP+22h], imm16in block 0060. On disk that immediate is zero. The later block gives object 2 the player's model pointer; it does not copy mesh data or the rest of the player's object definition.
For 1D95, helper 8E24 stores clock+256 at object +68, clock+1024 at +5E, and 8DDD at +66, all with 16-bit wrap. Callback 8DDD calls movement helpers and compares the live clock against +5E using a signed difference; on expiry it changes the callback to 8823 and clears flag bit 4. Its complete subsequent movement is outside this setup check.
Script boundary findings
Opcode 38: conditional altitude operand. Handler C194 toggles bit 7 of 7ED2. When the bit becomes set, it consumes a little-endian height word into 80AC; when clear, it consumes no operand. Initial reset 3C43 clears the flag. The ordinary A-key handler changes bit 0, not bit 7.
In 04_015 / 0, resource offset 039B enables height 0200h and continues at 039E. Offset 03CC disables the mode; 03CD is already the next opcode (12, beacon-row change). Propagating this state recovers 65 additional instructions across the record's reached paths. The decoder tracks state through relative jumps and reports conflicting boundaries if encountered. Entry roots assume the reset high bit; this is not proof of all interleavings of concurrently running scripts or arbitrary entry into a supplementary record.
Terminal counter wait. 04_015 / 2 ends its shared block with 1F FF at 0617, immediately before the English section at 0619. Handler C219 compares the operand with the counter held in the immediate byte at C21B. For counter 0–254 it branches to C1E1, which reloads the checkpoint and yields. For counter 255 it returns and falls into the following language bytes. The exporter records this as a guarded boundary exit, not an unconditional stop or a text continuation. Whether this scene can actually reach counter 255 has not been established. The exceptional path remains unanalysed and explicitly visible in both scenario and presentation exports.
Verification scope
- Original
BE07/BF1Asetup parsing now covers all 107 world-setup records and 1,857 placements, including all seven inline blocks, their current-object indices, group boundaries and counted-object totals. The former four exclusions are gone. - The seven blocks were additionally compared against explicit reference effects in 224 cases, varying object contents, clock wraparound and model heights. Comparisons cover the full current object and code segment (including globals and modified stream), SI advancement and DS restoration. Original helpers execute without substitution in this differential check.
- A linked check passes the first block's actual patched immediate to the later block on a different object.
- All 256 altitude flag values and 256 terminal counter values were executed against the original handlers. The wait check observes entry to the retry helper; it does not run the scheduler.
The full parser check uses synthetic zero-height geometry and a known synthetic player position. Underground route initialisation remains substituted. These checks establish setup effects and byte consumption, not complete mission behaviour, real-world placement height, rendering, or mission completion.
Reproduce with:
python3 tools/inspect_missions.py
PYTHONPATH=/tmp/darker-python python3 tools/verify_missions.py
PYTHONPATH=/tmp/darker-python python3 tools/verify_mission_native.py
python3 tools/mission_browser.py
python3 tools/extract_presentations.py