Fork me on GitHub
Several sprites contain incorrect pixel data
The player plane, fighter, advanced helicopter, and tank sprites all contain bytes with incorrect pixel values, corrected via ofix directives.
Pressing left and right simultaneously shows banked plane sprite
When both left and right are held at the same time, the plane appears banked despite no net movement. Both handle_right and handle_left are called in sequence: handle_right moves the plane +2 pixels and renders the banked sprite, then handle_left moves it −2 pixels and renders the banked sprite again, leaving the plane at its original position. However, both handlers always set state_plane_sprite_bank to 4, which causes the main render pass in render_plane to select the banked sprite rather than the centered one. Since neither handler checks whether the opposing direction is also active, there is no way for the net-zero input to produce the level sprite. Contrast with up+down, where the last-evaluated key wins outright — see Pressing up and down simultaneously always results in slow speed.
Pressing up and down simultaneously always results in slow speed
When both up and down are held at the same time, the plane always slows down. scan_keyboard evaluates up before down: handle_up writes SPEED_FAST and clears SOUND_BIT_SPEED_NOT_FAST, then handle_down immediately overwrites with SPEED_SLOW and sets both speed control bits. Last writer wins unconditionally.
This is inconsistent with the left/right case (see Pressing left and right simultaneously shows banked plane sprite), where the two moves cancel each other out and the net position is unchanged. Here there is no cancellation — one input simply silently overwrites the other. The outcome in both cases is an accident of evaluation order, not a deliberate design choice.
Missile spawns 1 pixel to the right of the plane tip
The plane tip is a single pixel at offset +3 from the sprite's left edge (sprite_plane frame 1, first row: $10 = bit 4). The missile spawns at plane_X + 4 (handle_fire), and since the plane always moves in 2-pixel steps, plane_X is always even — so the missile's 2 visible pixels always land at plane_X + 4 and plane_X + 5, one pixel to the right of the tip with no overlap. Using offset +3 instead would align the right missile pixel with the tip, which is the closest centering possible given the even/odd constraint.
Fuel tank and gauge off-by-one errors
Refueling stops at FUEL_LEVEL_ALMOST_FULL (252) rather than FUEL_LEVEL_FULL (255) — see add_fuel. The difference is not visible: both values map to the same gauge column via fuel >> 2.
The refueling gauge routine (update_fuel_gauge_refuel) uses column offset 63, while the consumption routine (update_fuel_gauge) uses 64. At the same fuel level, the refueling needle sits 1 pixel to the left of where the consumption needle would appear. The needle jumps 1 pixel right the moment the player leaves the fuel depot.
Missile trail is implemented but never rendered
The missile trail sprite (sprite_missile_trail, 4 frames) and its frame-selection logic in deactivate_missile are fully implemented, but the trail is never drawn. After selecting the correct frame based on the missile's X position, deactivate_missile immediately discards the pointer and substitutes sprite_erasure, so only an erasure is rendered at the trail's location. The feature appears unfinished.
Speed-dependent look-ahead distance is implemented but never used
The terrain rendering routine (render_plane_and_terrain) computes the plane's Y position as 136 minus the current speed setting, placing the plane at Y=135 (slow), 134 (normal), or 132 (fast) — higher on screen at faster speeds, showing more upcoming terrain as a look-ahead effect. However, all three movement handlers — handle_right (handle_right), handle_left (handle_left), and render_plane (render_plane) — immediately overwrite this with the fixed PLANE_COORDINATE_Y (128), making the look-ahead calculation dead code with no visible effect.
Bridge counter not updated during scroll-in
The scroll-in preview (scroll_in_loop) advances the internal bridge index (state_bridge_index) as bridge boundaries scroll past. The player's bridge counter (state_bridge_player_1/state_bridge_player_2), however, only advances when the player shoots a bridge — which requires collision detection, disabled during scroll-in. After the preview ends, the displayed count and level-dependent logic are behind by the number of bridges scrolled through.