Runtime entry points and simulation pacing
This is a map of original DBSIM 1.0 consumers and their native implementation. The current comparison coverage is summarized in ANALYSIS_STATUS.md and AUTONOMOUS_VALIDATION.md. Addresses refer to the flat application image. Resource readers, native fixtures and packet captures are described in FORMATS.md and MUSIC.md.
Shell and simulation boundary
GO owns the shell, mission-source condition evaluation, compilation and briefing selection. The compiler's nine output tables become SCRIPT.DAT; the handoff also includes PLAYER.MEC, MISSION.VAR, PREFS.CFG, RESTORE.DAT and OBJECT.STR. Recorded TAP bundles retain these starting files. The zero/seeded/all-one/patterned compiler fixtures are explicit research states, not claims about initial campaign flags.
DBSIM's application entry is 0x59350. It sets up memory/resource paths, requires the launcher handoff, calls simulation initialization 0x5a9dc, then enters the frame loop. The initial world loader 0x1d850 reads the handoff, plans resource preloads and loads the world. Actor construction and group assignment follow in 0x1e638; these stages now have complete native captures (ACTORS.md). Its source-to-live maps are resource-reference maps, not activation flags; the player roster is appended separately.
The core loop in 0x59350 has these direct calls, in order:
| Phase | Original consumer | Established boundary |
|---|---|---|
| Frame-start callbacks | 0x00af4(4) |
Runs before platform processing |
| Platform processing | 0x58574 |
Before frame pacing; keyboard state and command dispatch mapped in INPUT.md |
| Live frame pacing | 0x63f40 |
Skipped in recorded playback |
| Frame simulation/input | 0x59700 |
Returns termination request; also observes pause and replay state |
| View/object update | 0x004b8 |
Runs after simulation; actor, visual and cockpit consumers are covered by the component and complete-replay comparisons below |
| Scene generation | 0x59c08 |
Begins phase-5 deferred deletions, then scene lighting/terrain/objects and instruments; conditional during paused playback |
| Cockpit/presentation/flip | 0x59b40 |
Cockpit update, overlays, buffer pointer swap and frame presentation |
| Frame-end callbacks | 0x00af4(6) |
Includes optional VCR state-sidecar checking |
0x59700 first gets input through 0x4cb14. Replay copies the recorded frame ratio into global 0xbdbd0. When simulation is not paused, it visits typed object pools and their update methods, advances/removes primary effects (0x5d74), and processes other effect/object pools. Subsequent consumers include player input/control 0x5a0c0, mission/group processing, HERC updates and the outcome decision 0x0f454. The exact order is retained in the decompiled original; unnamed pool entries should not be assigned speculative gameplay meanings.
This interleaving matters: replacing the whole loop with an approximate generic actor tick would lose the ordering between controls, effects, AI and mission outcomes. Original spatial-trigger and mission-outcome fixtures are now documented in MISSION_RUNTIME.md; normal player locomotion and articulated aiming also have comparisons (LOCOMOTION.md). Component damage, projectile hit primitives and salvage now have comparisons (DAMAGE.md, CAMPAIGN.md); weapon readiness, power allocation and collision-free projectile updates are covered in WEAPONS.md. Moving contacts, complete scenery ray selection and outer hit dispatch now have comparisons (COLLISION.md, WEAPONS.md). AI selection, navigation and engagement boundaries are documented in CONTROLLERS.md; complete combat replay is now compared as described below and in PIXEL_FIDELITY.md.
Time representation
0x63e64 initializes the frame ratio to 256 and registers a timer callback. Live pacing 0x63f40 uses elapsed timer counts and the nominal period. Helper 0x63f04 computes the Q8 ratio and clamps its 16-bit result to 64–450. Its upper return bits are not a reliable 32-bit result; Ghidra's inferred integer signature must not be copied literally.
At unity, 256, 0x63fa4(100) gives 100 nominal milliseconds for articulated animation. At the normal limits, this corresponds to roughly 25–175.8 nominal milliseconds. A slow real frame therefore has a bounded simulation step rather than an unlimited elapsed-time jump. The software timer and live pacing are now traced and compared below; the host uses nominal 120 Hz rather than emulating PIT electrical timing.
0x63fa4(base) scales a signed short by the signed Q8 ratio, using original fixed-point rounding, then sign-extends the low word. The -32768 result is restricted to -32767. This means it must not be replaced with a superficially similar floating-point multiplication and saturation rule. Physics and animation consumers can round at different points; a tape's sum of nominal step lengths is not an assertion that every subsystem accumulates identical milliseconds.
check_clock.py executes 71 original ratio cases and 290 time-scale cases, including signed values and return-width edges. It also characterizes all 9,300 recorded frame ratios. The tape browser shows their nominal simulation durations and retains the exact inputs. That browser is a tape inspector. The native replay runner executes all four recordings and checks their gameplay states and return files, as described below.
Rendering status
Model readers, original articulated math, cell state, LOD node sharing, starting weapon assembly and effect cell stepping have native comparisons. Inspection rendering now uses original palette ramps and native generated texture-lighting maps. The depth-buffer preview is intentionally separate from the game's painter/BSP and span renderer; original ambient/directional/point intensity and default light setup now have native comparisons (LIGHTING.md). Dynamic light selection has separate comparisons. Native C++ clipping, palette bands, textures, outlines and billboards now match 3,720 complete original model renders (RENDERING.md); terrain, cockpit masks, mixed scenes and dynamic effects also have separate complete original-frame comparisons (RENDERING.md). Normal player movement/aiming sequence selection is verified separately in LOCOMOTION.md. Queued animation transitions, reverse stepping and root motion now have independent comparisons (ANIMATION.md).
The cockpit browser reconstructs static panel placement and original palette patches. Its output agrees with original decoded pixels. The instrument data exchange and all eight GAU layouts now have independent original-code comparisons. Native C++ cockpit artwork, all window masks and projection centres, reactor/throttle/shield/weapon instruments, mouse controls and navigation/target/waypoint HUD drawing now have original-code comparisons and live integration (INPUT.md). Clock and decorative TTM animation timing are now integrated and checked against original frames. Multifunction displays, damage flicker and shell transitions are also integrated and compared; see MULTIFUNCTION_DISPLAYS.md, EFFECTS.md and SHELL.md.
Initialized runtime references
REFERENCE_CAPTURE.md documents the DOSX page-table reconstruction, private initialized mission snapshot, 7,200 whole-frame ordering comparisons, and execution of the original scene renderer with cached VGA memory. PERCEPTION.md adds 1,110 independent actor recognition, contact-sharing and visibility-cache cases. Full frame dispatch, controller integration and all four complete TAP replays now have comparisons; see the later replay sections and AUTONOMOUS_VALIDATION.md.
COLLISION.md adds all 140 scenery fields, stepped intersections and HERC ramming dispatch; CONTROLLERS.md identifies all 27 named HERC/flyer states and their independently checked entry, selection and displayed-status rules.
Live timer and pacing comparison (2026-10-30)
The HMI timer descriptor requests 9,939 PIT counts, nominally 120 callbacks per second. Initialization at 0x63e64 stores 120 as the countdown maximum and 12 as the nominal frame period. Callback 0x63e3c decrements the signed 16-bit countdown until zero, increments the wrapping 32-bit fast clock, and increments the slow clock when the old fast-clock value is odd.
Pacer 0x63f40 waits until at least two countdown ticks have elapsed, computes maximum minus countdown, obtains the two ratios through 0x63f04, and resets the countdown. The ratio clamp is still 64 through 450. Thus a two-tick interval (about 16.7 ms) produces a nominal 25 ms simulation step: the original minimum wait and minimum simulation step are different. A stall stops the countdown at zero and the next ratio is bounded by the upper clamp. Playback skips this pacer and supplies the recorded ratio instead.
The capture executes both original routines for 32 traces / 3,840 states, including short intervals, long stalls, alternate nominal periods and wrapping clocks. Results are retained in frame-pacing.json; the portable clock matches every state. Timer progression is independent of mission updates, so mission dialogs do not suspend audio. The native application retains its last rendered frame between simulation updates rather than consuming visual state again for each host presentation. Losing host-window focus suspends advancement as a platform convenience.
This establishes timer arithmetic and live pacing, not full replay parity. The complete original tape-input consumer now also has all 9,300 packet outputs captured by capture_tape_packets.py, including post-read routing; mouse queue insertion is an explicit boundary. Replay's modal ownership and whole-frame ordering have separate completed checks below.
In-flight preferences integration (2026-10-30)
F12 and Alt+P enter 57820, which freezes simulation, saves the camera and shows a close external HERC preview above PRF_ALRT. Initial orbit distance is clamped from zero to 2500; the projection centre is (160,50), with bounds through y=110. Callback 577f8 sends turn -64 through camera control 0a04, updates the camera and renders again. Exit restores the saved camera. Simulator Controls/Calibration use origin (48,45), CPYLW/GREEN6X8 fonts and value background index 63; shell versions use (49,50), GRAY/NM_GREEN and 31. The panels' bitmaps and strings are shared. See capture_simulation_option_details.py for 136 complete simulator rasters and the 512-entry full/low-memory Shapes table comparison.
Shapes 57c08 sets whole-model scale/bias at 903e0 separately from nested-part biases bc198 (ordinary actors), bc2b8 (weapons) and bc460 (HERCs). Part selector 530b0 reads scale at 9615a and bias at 9615c; the decompiler's overlapping global names obscure the latter reference. Wrapper 0a4cc sets the weapon bias and resets it to zero after drawing, rather than restoring the parent HERC's bias. capture_shape_preference_rasters.py retains 640 complete armed-HERC outputs across both tables and threshold boundaries. These all match the native port.
Music gate 4e294 and effects gate 4e2c8 stop active objects when disabled. Speech start 4e518 checks its own 959e4 flag, independently of the effects gate. Changing radio mode does not stop speech already in progress. Caption enable is the separate 930d2 flag. Original music re-enable callback 4ad0c invokes 585dc unless the special b1734 state suppresses it.
A nested persistence bug is reproducible: modify a flight option, open Controls, accept Controls and then close preferences without another edit. Controls calls 4a83c to commit the whole preference manager after writing only slots 7-11. Outer save helper 4839d consequently sees no changed flight values and skips its write, leaving the earlier flight edit only in memory. Another flight edit after Controls causes all seven flight slots to be written. The 28 cases in capture_nested_flight_preferences.py retain all manager arrays and the resulting in-memory file; native integration preserves this original behavior.
General alert input
capture_alert_input.py executes all nineteen original general dialogs' virtual input handlers in 1,600 cases. Combined fire takes priority over next-button and keyboard input. With no focused button, fire/Enter establishes focus zero without activating it. Escape explicitly focuses and selects response zero; Tab moves forward, Alt+Tab backward. Insert is consumed in either WCS mode but changes focus only when WCS is enabled. Raw Space and arrow scan codes do not navigate this handler; the ordinary input layer separately maps Space to fire.
Dialog initializer 45f00 saves the 43-byte application input packet and sets four button suppression latches. Its focus-minus-one call through 4646e/46470 polls 4cb14 once if a mouse object exists, before incrementing modal depth. The modal loop performs subsequent polls; 460b8 restores the saved packet on exit. Thus a recorded command opening a modal is followed by one initialization packet, not two extra initialization polls. Playback packets already contain debounced button values: the live button-debounce loop is skipped in playback.
Cockpit view ownership and complete DEMO4 simulation
capture_cockpit_transition_policy.py runs original 22634, 226f0, 22904 and 59b40 through 114 sequential presentations. Requests are accepted only while pending is -1. Side transitions prepare on a specific VGA page (right: 0, left: 2; returning uses the matching opposite page). A prepared pan completes on the next presentation; its extra page swap cancels the normal swap. Completion sets cooldown 2, immediately decremented to 1 by that presentation. External mode is exceptional: 222c0 saves the prior view and sets mode 4 during preparation; return restores the saved view when the transition completes. The native ownership state matches every capture. The individual intermediate VGA scanouts remain available in the earlier transition gallery.
The complete DEMO4 native comparison now matches 1,573 states: initial state, all 1,571 recorded input updates, and the extra zero-input EOF update using the previous outer ratio. It checks all actor movement, HERC aiming/motion/power/ shields/damage, target ownership, mobile controller states, group orders/routes, and the entire gameplay RNG. No actor/RNG snapshots are injected; source TAP records drive the native owner. The original observed presentation clock is supplied separately. Full-frame pixels, visual RNG, sound timing and the other tapes' modal ownership are not implied by this simulation comparison.
The HUD owns a cached target separately from the shell's selected contact. 2cf28 first draws the HUD, then validates the shell selection without the forward targeting cone. 155a8 exchanges the cached target after weapon/power updates; 59c08 draws the HUD again afterward. Losing a contact therefore has a one-update handoff that immediate target clearing does not reproduce. Wingmate and cockpit commands run inside the player phase after world AI; the TRACK widget request is consumed by the later weapon-instrument exchange.
All four supplied TAP simulations
The native ESRPLY04 comparisons now cover every completed simulation frame from the four retained unmodified DOSBox runs: 1,939 states in DEMO1, 1,830 in DEMO2, 1,686 in DEMO3 and 1,573 in DEMO4, including initial states. Besides the previous movement/combat fields, the comparisons retain contact knowledge, visibility, perception clocks, controller entry/update latches, command flags and torso/elevation rates. No actor or random state is injected.
Original playback restores embedded files beside its TAP path. Therefore -pTAPES/DEMOx restores TAPES/PREFS.CFG and TAPES/KEYJOY.CFG while runtime input still reads DATA/PREFS.CFG and DATA/KEYJOY.CFG. Their values differ in this installation. The fixture records the installed preference manager's 17 bytes; the original source TAP remains the input stream.
DEMO3's two F12 panels consume 54 intervening packets through the native preferences dialog, including one initialization poll per entry. Original playback skips live debouncing, so the button-suppression latches set on entry persist after return. This affects movement axes in control mode zero.
Controls and camera preparation have distinct timing. Mouse callbacks run in 4cb14 before world AI; MFD commands execute immediately, while HDD callbacks store a request for later instrument update. Camera command 59e87 sets the requested mode, and 5894c applies it after player input only while the panel has a prepared transition. This transfers control one frame after the external panel first appears, but before the return pan completes. Immediate switching or delaying both directions uniformly gives different recorded movement.
The terminal Q dialogs, DEMO4 zero-input EOF update and full visual RNG are covered by the later replay fixtures below. PIXEL_FIDELITY.md records the final sampled-frame comparison. Engine-sample start timing retains the limits in MUSIC.md.
Terminal recorded dialogs and exact return data
ESRPLY05 adds each original exit snapshot, including partial final frames, bringing native comparison coverage to 7,032 states and all 9,300 TAP packets. DEMO1 Q shows alert 13, DEMO3 Q shows alert 4, and both choose response 1. Those branches exit 59700 before player controls and post-player updates. DEMO2 automatically shows alert 6 after perception/power and chooses response 1. It is not a Q dialog. DEMO4 completes its extra EOF frame and leaves normally.
The terminal dialogs consume 369, 1,121 and 730 polls respectively, including their initialization polls. Together with DEMO3's 54 preferences polls these account for all recorded modal input. Constructor/entry/exit observations are retained in analysis/tapes/replays/modal-ownership.json. All final RESULTS.DAT and MISSION.VAR bytes independently match, including salvage, conditions, kill counts and mission counters. This supersedes the earlier terminal-dialog boundary above; full replay pictures and audio remain separate comparisons.
Live SDL modal suspension
The live host now opens Pause, mission status, program-exit confirmation and flight options inside the same player/outcome callbacks verified by the full recordings. World simulation stays suspended at that call site while SDL and the audio clock continue. Options retains its original rotating camera preview. Leaving a mission releases its owner only after the suspended frame unwinds; continuing does not clear the sound queue.
Host input stops at a pending modal command until its callback opens, preserving later queued keys for that dialog. In particular, a rapid P/Escape pair cannot send Escape to the underlying inspector before Pause appears. Closing a modal also ends that event poll before another event can destroy its suspended owner.
The private X11 smoke script tools/check_native_modals.py verifies a retained pause image, options entry/return, mission-status cancellation, Ctrl+Escape cancellation, a rapid Pause/Escape pair and confirmed return to the main menu. Its screenshots are host integration evidence, not original pixel comparisons.
Original cockpit launch sequence and native ownership
The nearby-friendly-base branch at DBSIM 5b396/57ffc is now connected to native mission launch. It uses the original INTRO.DBA lift door, lowers the view by 4,000 units, then advances 35 units per rendered frame (115 frames). The player and world simulation remain stationary. A side-zero ground actor within 40,000 units selects this branch; other starts perform the original initial draw.
The 57cf4 dim palette and 287a8 fade preserve the original channel mixing, fixed-point increments and final rounding remainder. Reflection is suspended during startup. The 120-tick fade and 30-tick landing shake use the external 60 Hz clock; the lift itself is frame-driven. Their appearance therefore depends on rendering cadence, as in the original. The live host uses its existing frame pacer; it does not hard-code a recorded shake count.
During the landing shake the original keeps scanning out the last lift page while drawing into the other page. Native presentation retains that behaviour, including page origin displacement and restoration. Sound 33 accompanies the lift, sound 41 starts on landing, and startup sound 19 begins after the sequence. The scene inspector continues to offer immediate entry without the lift.
The unchanged DEMO1 original supplies 115 complete palettes and 34 landing random/displacement states. Both the isolated controller and integrated mission_view match them, including all 114 visual-RNG bytes and frozen gameplay state. Three additional complete original captures have 35, 18 and 34 shake frames respectively. The original screenshots are browsable in analysis/cockpits/startup.html in the analysis tree.
All 242 CTest checks passed after integration. Frame comparison separately found a missing second HUD paint above the lift door; that ordering is corrected. Remaining pixel differences include the separately drawn software cursor, instrument clock initialization and a transient hardware-DAC versus CPU-palette difference. The controller comparison does not imply whole-screen equality.
Radio, complete visual RNG and frozen options previews
The four ESRPLY08 replay fixtures now extend all 7,032 gameplay/exit comparisons to the complete 114-byte visual RNG, including both index bytes, after every captured frame. All 9,300 source inputs and the final RESULTS.DAT/MISSION.VAR files still agree. Sources are the unchanged DOSBox runs work/research/tapes/replays/demo1-radio-playback through demo4-radio-playback. The observer records inputs at their actual consumer instructions; it does not patch guest code or inject native actor, queue, portrait or random state.
Independent external inputs are the startup draw/fade/end clocks, interface clock, speech-animation clock, and sound driver's success/failure return when speech starts. Both clocks are nominally 60 Hz but have different phases. The native live game uses its timer and PCM mixer; the replay supplies the observed device result to isolate radio/visual behavior. The PCM stream has a separate completion/voice-allocation boundary. The speech-rejection investigation below identifies its cause in a captured run.
System alerts and pilot speech use separate queues. Native mission events now reach them at their original call sites: contact, combat, selected-target, shield and objective notices as well as orders. First-contact notification is the HERC virtual override at 0e760; base/flyer observers use the empty 0e75c method and must not decrement its cooldown. Individual orders cancel that pilot's pending status request, rather than creating another status reply.
Original 59c08 updates portraits and the system queue before world rendering, then advances the pilot queue afterwards. Options 57820/577f8 uses the same render/interface path while simulation is frozen, including entering/leaving the external camera and the interface-suppressed return preparation. Both DEMO3 options dialogs now consume every recorded observation in this order. The lift enters with draw/other pages 0/2 and finishes at 2/0; previously the reversed pages changed later held-view arbitration and hid cockpit clicks.
Original speech playback failure is observable in the recordings. At 3e682 the failure sets both radio readiness and cancellation during the opening phase. The queue removes the message but retains its starting latch, so later messages can remain blocked until expiry. Native preserves that behavior. Disabling speech is different: the original reports success to the portrait while making no sound. No guessed hardware success is substituted in either path.
Additional isolated comparisons cover 60 system-banner rasters, 15 targeted status cancellations and 2,400 portrait transitions with an explicitly rejected PCM start, alongside the existing 2,400 ordinary portrait and 1,920 radio controller states. Whole-frame pixels and software cursor ownership are covered by PIXEL_FIDELITY.md. MUSIC.md records exact non-engine starts and all stops, with engine-start timing reported separately.
Hardware palette upload side effect
The CPU palette is not always the displayed DAC palette. VGA upload 88684 reads port 3c7 and, if it is in write mode, writes zero to 3c9 before selecting the new start index. Ordinary uploads finish at an RGB boundary, so this clears the next colour's red component. For example, uploading the six shield colours 80–85 leaves the write index at 86; a later reflection upload clears 86 red without restoring it. The resulting dark teal cockpit colour is original.
27f74 uploads all 256 entries when the palette object changes, otherwise the union of dirty ranges, or entry zero when no edit is pending. Startup 287a8 uploads the complete fade range. Shield exchange 392d0 compares front charge, rear charge and balance; 391ac uploads only on its first dirty repaint. Full cockpit-page preparation calls the shield painter independently of value changes. Updating those colours every frame incorrectly triggers the DAC side effect at startup.
capture_palette_device.py retains 500 original manager/device transitions in
work/research/cockpits/palette-device.bin. The unchanged game's passive port trace is
work/research/tapes/replays/demo1-dac; it additionally records PCM acceptance paths.
The native implementation matches the isolated DAC transitions and sampled
integrated colour changes. Cursor, clock, radio-region retention and complete
frame composition remain separately assessed below the replay state checks.
The additional unchanged demo1-dac run identifies the speech rejection path: 50a90 rejects a start after HMI reports 31 active voices and the sound manager finds no registered playing sound with a reported volume below its replacement threshold of 100. That run has 168 such rejected sound requests and no observed 50c00 allocator failure. The rejected speech request is simvoice/p1_08000.sfx at frame 817, clock 2637 (frame timing differs slightly across independent live captures). This establishes the cause in this run. Matching voice occupancy and PCM output over a complete native recording remains a separate audio integration comparison; supplying the observed rejection to the radio fixture isolates its visual/control consequences without proving the whole audio timeline.