DARKER Research archive

Reserve activation and script contexts

The scenario viewer now lists each record's initial air, ground and static reserve queues, with selectable object entries, and its decoded activation/context operations. activation.json exports the same information: 150 activation instructions, five owner registrations and two explicit context swaps across 124 records. These are static instruction counts, not counts of events during a playthrough.

Queues and activation

Setup groups and runtime categories are distinct. Group 0 becomes initially active, group 1 becomes reserve, and group 2 remains on free lists. Within each group, header bit 6 selects static objects; otherwise slots above 28 select ground objects and the remaining slots select air objects (BE44–BE7F).

BF1A prepends objects to a category's free list as they are parsed. BDE3 transfers that list head to the reserve head after group 1. Thus each initial reserve queue is in reverse source order within its category. The viewer exposes this order, rather than guessing which instruction activates each object. Native parser checks now verify category/list membership and order alongside all 1,857 placements.

Opcode Handler Category/list header Initial script deadline
09 C339 air / 6FD8 current BF8 clock
0A C329 ground / 6FC8 BF8 + F000h, wrapping 16 bits
0B C331 static / 6FB0 current BF8 clock

The common loop C33E–C39C:

  1. Reads the requested count byte.
  2. Removes the object at header +6 (reserve head).
  3. Sets its +5E deadline and adds bit 0 of object +46 to the byte objective counter C16F.
  4. Runs the selected placement callback.
  5. Prepends the object to the active doubly linked list (header +0 head, +2 tail).
  6. Repeats until the byte count reaches zero, then restores the calling object's BP and script SI.

A multi-object activation reverses those objects again as it prepends them. The ground deadline is deliberately 4,096 ticks behind the current clock; this is a native write, not a newly inferred delay. How it affects ground-script catch-up depends on the subsequent ground update path.

There is no reserve-empty guard here. Exhaustion yields BP=0 and the next instruction writes SS:005E. Controlled tests stop before that write. This does not prove any real mission can exhaust a reserve queue. Also, count byte zero requests 256 iterations rather than no objects. None of the 150 decoded activation instructions uses zero.

Air callback selection depends on mode 6202 and byte 084D: mode 2 uses the no-op C002; otherwise 084D=4 selects C3E2, and other values select C39E. Ground/static use C002. The tests verify callback selection but substitute C39E/C3E2; their proximity/geometry adjustments are outside this fixture.

Owner registration (24)

C2E4 exchanges current BP with the shared word at 3E8C, stores the previous value in current object +5A and enters the normal stop-script path C2D1. Thus the earlier name transfer_script_owner must not be read as “immediately execute the next instruction on another object.” It registers the current object and stops its script. The +5A field is reused here rather than remaining a checkpoint pointer.

At 3E83–3E93, the game checks whether context flag 7E61 is zero and the registered word is nonzero. If so, it selects the player object and calls the context-swap handler. The complete lifecycle and downstream use of the registered object still need tracing.

Context exchange (26)

C77E–C79C exchanges two shared pairs:

The incoming script pointer is also written to object +58; the deadline becomes current BF8 and byte 7E61 is complemented. RET returns to the interpreter, so execution continues immediately in the incoming script. There is no automatic scheduler yield.

The supplementary-record loader BB3D–BB4D seeds the saved script/message pointers. This directly connects supplementary scripts to the message-order ambiguity already marked in the viewer. A native test executes two consecutive swaps and verifies that the original text cursor is restored and execution resumes after the original swap opcode.

Verification and scope

PYTHONPATH=/tmp/darker-python python3 tools/verify_mission_activation.py
PYTHONPATH=/tmp/darker-python python3 tools/verify_missions.py
python3 tools/mission_activation.py
python3 tools/mission_message_sections.py
python3 tools/mission_browser.py

Native results: 42 reserve cases including existing/empty active lists, exhaustion, wrapping counters and a full 256-object request; four air-callback selections; three owner registrations; one consecutive context-swap trace. List links, deadlines, counters and caller restoration are checked against original instructions. Air geometry callbacks are substituted and invalid reserve accesses intercepted before writing.

These fixtures do not simulate all objects together or establish runtime reachability of reserve exhaustion. Native update-order analysis now covers three controlled multi-object activation traces and the late-frame owner/context gate. The full context lifecycle and a real multi-object mission trace remain outstanding.