# FS2 C Port - Status vs Original Comparison of the original Apple II FS2 (disassembled in `src/chunk*.s`) against the C port in `port/`. Updated 2026-05-14. Legend: ok = done; partial = approximation or limited; missing = not implemented --- ## Flight model | Feature | Original | Port | |---|---|---| | Position integrator (24-bit XYZ) | `IntegratePhysicsStep` | ok `aircraftStep` (Q16.16) | | Pitch / bank / yaw rates | `UpdateAutoTrimAndYaw` etc | ok `stepFlight` | | Auto-coordination (bank to yaw) | `ApplyAutoCoordination` | ok `stepFlight` | | Wind / turbulence | chunk2 `ApplyWind` (64K) | ok `windCompute` / `windApply` | | Stall detection and break | per-instrument check | ok `stalled` flag | | Spin entry from stall | implicit in stall handling | ok yawRate-driven `spinning` state | | Spin recovery | opposite rudder + nose-down | ok 30-frame recovery condition | | High-G / VNE damage | `CheckFlightEnvelope` | ok `loadFactor_q88` + `airframeDamage` accumulator | | Flap speed drag | `RefreshElevatorIndicator` | ok `forwardSpeed *= (256 - flaps>>3) / 256` | | Mixture too-lean = engine cut | implicit | partial audio penalty, no full model | | Carb heat icing | chunk5 `CarbHeat` | partial audio penalty, no icing model | | Magneto on/off effect on engine | `UpdateEngineWithMagneto` | partial audio penalty | | Engine fault dispatch | `FailureProcTable` | ok reality-mode dispatch | | Engine knock audio | per-fault sound | ok `audioUpdate` wobble | | Crash detection (ground) | `HandleCrashOrSplash` | ok | | Splash detection (water) | `CheckSplash` | ok via `worldZ` water range | | Building / mountain crash | `crash_msg_table` | partial type set, no scenery-aware test | ## Modes | Feature | Original | Port | |---|---|---| | Free flight | default | ok | | Slew mode | `SlewMode` (chunk5) | ok `aircraftToggleSlew` | | Slew digit overlay | `DrawSlewOverlays` | ok | | Demo mode | `DemoMode64K` | ok `autopilotDemo` with 4-waypoint sequence | | Edit mode | `EditModeFlag` | ok F7 toggle, full field editor (`editor.c`) | | Reality mode | `RealityMode` | ok instrument and engine failures | | Radar view | `RadarView` | ok `worldRenderRadar` | | WW1 ace combat | `WW1AceMode` | ok bullets, bombs, AI fire, damage | | Course Plotter | chunk2 `CoursePlottingMenu` | ok `coursePlotter.c` (record/display) | | Pause | `TogglePause` | ok P key | | Title / config screen | boot menu sequence | ok `title.c` (MODE/REGION/DISPLAY/TIME/REALITY/START/QUIT) | | Boot DOS | `BootDOS` | missing (no DOS to boot) | ## Instruments | Feature | Original | Port | |---|---|---| | Airspeed needle | `UpdateAirspeedIndicator` | ok | | Altimeter main hand | `UpdateAltimeterIndicator` | ok | | Altimeter 10K hand | `UpdateAltimeter10K` (64K) | ok | | Attitude indicator | tilted disc | ok `drawHorizonDisc` | | Heading bug | `DrawHeading` | ok digit readout | | Magnetic compass | `DrawMagCompass` | partial digit only | | Vertical speed | `UpdateVerticalSpeedIndicator` | ok | | Turn coordinator | `UpdateTurnCoordinator` | ok | | Slip/skid ball | `PLSlipSkidIndicator` | ok reads `sideslip_q88` | | Throttle position | `UpdateThrottleIndicator` | ok bar indicator | | Mixture position | `UpdateMixtureControlIndicator` | ok bar indicator | | Flap position | `UpdateFlapsIndicator` | ok bar indicator | | Trim position | implicit in auto-trim | ok HOME/END keys + indicator | | Fuel tank L/R | `UpdateFuelTankGauges` | ok per-frame burn, alternating tanks | | Oil temp/pressure | `UpdateOilTempAndPressureGauges` | missing | | RPM display | `DrawRPM` | ok digit | | Magneto state visual | `DrawMagnetoState` | ok MAG OFF/L/R/START indicator | | Carb heat state | switch position | ok "C.H." / "HEAT" indicator | | Lights state | switch position | ok "1" / "O" indicator | | Failure indicator (X over gauge) | `DrawX` per gauge | ok `drawFailX` | | Stall / VNE warning | `STALL` / `VNE` text | ok | | VOR2 / ADF gauge mode-gating | chunk4 ADFMode flag | ok `ac->adfMode` switches needle/digits | ## Radios / Navigation | Feature | Original | Port | |---|---|---| | NAV1 frequency | `NAV1` ($08F7) | ok `radios.c` | | NAV2 frequency | `NAV2` ($08F5) | ok | | ADF frequency | `ADFFreq*` | ok | | COM1 frequency | `str_com1` | ok | | Station database (deduped) | per-region | ok 695 entries from extractstations | | BCD frequency increment | step keys | ok shift+digit / digit | | BCD per-digit entry | `KeyDecreasePatch` | ok Ctrl+1..4 arms input, digit keys append | | OBS course knob | OBS-related | ok `,` `.` keys | | VOR CDI needle | `DrawVOR1IndicatorChanges` | ok | | VOR TO/FROM flag | `msg_vor_flags` | ok "TO" / "FR" / "OFF" | | ILS glide slope | not in original | missing | | DME readout | `DrawATISMessage` ATIS bound | ok | | ADF needle | `DrawADFPanel` | ok gated on `ac->adfMode` | | ADF heading digits | `DrawADFHeadingDigits` | ok | | ATIS chunked text | `UpdateCOMMessageChunks` | ok 8-char scrolling window | | Tune-to-nearest button | not in original | ok T key (port-only) | ## Scenery system | Feature | Original | Port | |---|---|---| | Disk loader (`SceneryReadUntilC0`) | chunk3 `SceneryLoaderEntry1` | ok RAM dump + .SD demand-load (default-on) | | Block-list indirection | chunk4 `ComputeBlockFromSector` | ok `doHeader` correct mapping | | Nibble decode | `SceneryNibbleDecode` | missing (only used by Entry4 path) | | HEADER opcode ($0D) | `SceneryOpHeader` + `LA63A` | ok `doHeader` with cache, default-on demand-load | | L631D section base | `L631D` | ok `sceneryComputeBaseL631D` | | EnterLocalFrame ($07) | `SceneryOpEnterLocalFrame` | partial simplified passthrough | | Vertex emit + transform ($00-$02, $40-$42) | `SceneryOpEmitV*` | ok | | Cull ($20/$21/$22) | `SceneryOpCullIfOutside*` | ok `doCullN` | | Cull by outcode list ($04) | `SceneryOpCullByOutcodeList` | partial walks list, no actual cull | | Jump-if-beyond-XY/XYZ ($13/$14) | `SceneryOpJumpIfBeyondXY*` | ok | | REL_JUMP ($0B) | `SceneryOpJumpRelative` | ok | | SUB_INVOKE ($18) / RETURN ($19) | `SceneryOpSubInvoke` | ok | | RESET_STATE ($2F) | `SceneryOpResetState` | ok | | MODE_WHITE ($1B) | line-kernel patch | ok semantic equivalent | | DAY_ONLY ($1C) | line-kernel patch | ok skip-on-night flag | | WriteWord ($1A) / StoreImmWord ($25) | self-mod patches | ok | | Curve emit ($2B) | `SceneryOpEmitCurve` | ok via 6502 interpreter on MAME-patched RAM | | Vertex-cache ops ($31/$32/$33/$35/$42) | cached vertex pool at $0140 | ok pool reads | | ADF/NAV/COM record ($05/$1D/$1E) | station records | ok | | SET_COLOR ($12) | `SceneryOpSetColor` | ok | | Polygon edge emit | `EmitClippedLine` | partial line draw only, no polygon close | | Polygon scanline fill | `DrawColorSpan` etc | partial 2D scanline edge-intercept; not chunk5's 3D-clipped emitter (FS2 boot Meigs is line-only) | | Polygon 4-pass 3D clipper | `PolygonScanFillSetup` | ok `sceneryClipPolygon3D` (Sutherland-Hodgman against Z-X / Z-Y / X+Z / Y+Z) | | Sky/ground tilted fill | `FlipPagesFillViewport` | ok `rendererFillTiltedSkyGround` | | Frustum line clip (5-plane) | `ClipBothVerticesToFrustum` | ok full-frustum clipper, plane-snap via `chunk5ScaleC2ByC4`; 36/36 pixel-exact vs MAME | | Vertex pool / EmitPrimaryVertex | $0AB8 column array | partial small pool, no polygon closure | ## Display | Feature | Original | Port | |---|---|---| | 280x192 framebuffer | hires page 1/2 | ok | | Page flip | `FlipPagesFillViewport` | partial single buffer | | Color/B&W mode | `ColorModePatch` / `BWModePatch` | partial always color | | Dotted-pattern night | `SceneryOpDayOnly` etc | ok DAY_ONLY skip | | Panel bitmap | hires loaded from disk | ok `res/loading_panel.bin` | | Panel lights overlay (64K) | `UpdateInstrumentLights` | partial state shown as text | | Message text (`DrawMultiMessage`) | string blit | ok `font.c` + `fontDrawStringCentered` | | Crash message overlay | `crash_msg_table` | ok MOUNTAIN / BUILDING / SPLASH / CRASH | | Wing/tail overlays in side views | `DrawWingsOrTailOverlays` | ok `rendererDrawWingOverlay` (right/left strut, back fin, down well) | | Bomb sight | WW1 bombsight pixels | ok `ww1aceHudDraw` | | Gunsight | WW1 only | ok `ww1aceHudDraw` | | NTSC fringing (HIRES decode) | true HIRES pair-merge | partial palette buffer by default; `SCENERY_NTSC=1` reverts to HIRES decode | ## Audio | Feature | Original | Port | |---|---|---| | Engine sound | speaker click | ok sawtooth + throttle modulation | | Engine fault wobble | not present in original | ok phase-modulated wobble | | Magneto-off engine cut | engine flag | ok amp = 0 when MAG OFF | | Stall horn | beeper trill | ok 800 Hz square wave | | Crash impact | speaker noise | ok noise burst | | Gun fire (WW1) | not in original | ok rapid sawtooth burst | | Bomb drop (WW1) | not in original | ok pitch sweep | | Wind hiss | not in original | ok speed-scaled noise (LCG-driven) | ## Input | Feature | Original | Port | |---|---|---| | Yoke (arrows / WASD) | arrow + paddle | ok | | Rudder | `/` and Ctrl | ok Q/E | | Throttle | `[` `]` Ctrl+H | ok Up/PgUp/Dn/PgDn | | Brake | space | ok space cuts throttle | | Slew controls | 8/9, 0/-, ,/. , +/= | partial W/A/S/D in slew mode | | View directions (F1-F5) | 1-5 keys | ok F1-F5 | | Magneto select | 1-3 keys | ok Shift+M cycles | | Lights toggle | L key | ok L | | Carb heat | H key | ok H | | Pause | Ctrl-P | ok P | | Edit mode | Ctrl+[ | ok F7 (full field editor) | | Demo mode | Ctrl+D | ok F10 | | Slew toggle | Ctrl+S | ok F12 | | Reality mode | Ctrl+R | ok Tab | | Radar view | F | ok ` (backquote) | | Course plotter menu | Ctrl+C | ok C/V/B/N (record/precision/display/off) | | BCD digit entry arm | KeyDecreasePatch | ok Ctrl+1..4 (NAV1/NAV2/COM1/ADF) | | Joystick | game port | ok SDL_Joystick (button 0 = gun, 1 = bomb, 2 = view, 3 = radar, 4 = throttle cut) | ## Persisted state | Feature | Original | Port | |---|---|---| | Edit-mode instrument save buffer | $FC00+ | ok `editSavedState` snapshot on toggle | | Crash recovery snapshot | yes | ok `aircraftArmRecovery` / `aircraftRestoreRecovery` (Space when crashed) | --- ## Multi-region scenery The FS2.1 base disk's four region variants (Chicago / LA / Seattle / NY) all share the same `FS2.1` .SD file as their demand-load source. `port/include/sceneryData.h` exposes the per-region enums; the `sceneryDataRegionFromName()` table lookup maps `SCENERY_REGION` env var strings (e.g. "FS2.1_chicago", "SD3", "SDS1") back to enum values for the `--screenshot` path. | Region | Source | Default start | |---|---|---| | `SCENERY_FS2_1` | `FS2.1` | WW1 ace training field | | `SCENERY_FS2_1_CHICAGO` | `FS2.1` | KCGX / Meigs Field `(96, 268)` | | `SCENERY_FS2_1_LA` | `FS2.1` | KLAX `(200, 0)` | | `SCENERY_FS2_1_SEATTLE` | `FS2.1` | KSEA `(970, 0)` | | `SCENERY_FS2_1_NY` | `FS2.1` | KJFK `(400, 0)` | | `SCENERY_SD1..SDS1` | `A2.SD` | per-disk default | `SCENERY_DEMAND_TRACE=1` logs every fired demand-load. Boot Meigs fires sid $44 (6 sub-blocks @ $A887) and sid $4E (1 sub-block @ $BA3D). ## Still missing - **Polygon scanline fill** for non-boot scenes (FS2 boot Meigs is line-only so this is mostly cosmetic until other regions surface real polygons). - **Stuck-key magneto auto-alternation** - chunk5 `MagnetosLeft/Right` handle key-held edge cases. - **Day-side detailed runway striping** - chunk5 has runway-specific colour-only details we don't replicate. - **Oil temp/pressure gauges** - chunk5 `UpdateOilTempAndPressureGauges`. - **Section anchor / $07 EnterLocalFrame** for real bytecode in unsurfaced sections. The simplified passthrough is correct for boot Meigs but full multi-section navigation may need anchor-coord reads from each section's preamble. - **ILS glide slope** - FS2 doesn't have it either, but port could add it. ## Recent code-cleanup pass (2026-05-14) - Dead code removed: `ww1aceDropBomb()` (legacy stub), `Coord16T` / `VertexT` (unused), `DEG2RAD` (unused macro), `rendererSwapFillColors()` (uncalled). - Shared helpers consolidated: - `camera.h`: `metresFromQ1616`, `q1616FromMetres`, `byteAngleToDegrees` - `font.h`: `fontDrawStringCentered` - `fs2math.h`: `fs2ClampInt`, `fs2StepClamp` - `sceneryDataRegionFromName()` replaces the 19-branch SCENERY_REGION strcmp chain in main.c. - `AC_MAX_FORWARD_SPEED_Q88` promoted to `aircraft.h` (audio.c was redefining the same literal). - Recovery flag tracked via `ac->hasRecoverySnapshot` only (removed the `recoveryValid` file-static second-source-of-truth). - Net change: 16104 -> 16018 lines.