{ "chunk": "game_comcenScreens6F00_7551", "unit": "game/comcenScreens6F00", "unitLabels": { "game/comcenScreens6F00": { "7688": "refreshMissileScreen", "761C": "radarCursorRepeatStep", "762C": "radarCursorRepeatFast", "7642": "checkMissilesRemain", "764A": "launchMissileEffect", "76B2": "moveRadarCursorByDirection", "76EC": "offsetRadarCursor", "7701": "setCursorColImm", "7705": "setCursorRowImm", "7713": "animateMissileFlight", "7717": "missileLineFromComcen", "77DF": "missileSpriteXOrigin", "77E7": "missileSpriteYOrigin", "7807": "missileTickThresholdCmp", "782A": "missileStepIndexLoad", "7841": "hideMissileSprite", "7847": "plotMissileTrailSegment", "785B": "stepAndPlotTrailPixel", "7861": "saveLineStepperState", "786D": "restoreLineStepperState", "7872": "restoreLineStateStore", "7879": "rebasePairToMin", "7890": "rebasePairToMinRts", "7891": "radarSweepStep", "7892": "radarSweepRow", "7893": "radarSweepDarkFlag", "7897": "radarSweepThresholdCmp", "796F": "subNewOriginCol", "7978": "subNewOriginRow" } }, "notes": { "7551": { "unit": "game/comcenScreens6F00", "block": [ "Work byte of playStrikeZoomAnimation (see below). In overlay A the same address holds unrelated", "map-generator data, so the label is unit-scoped." ], "line": "zoom step counter: loaded with 3 and counted down to $FF (four magnification passes)" }, "7552": { "unit": "game/comcenScreens6F00", "routine": [ "playStrikeZoomAnimation - Drone strike effect on the DRONE screen: magnifies the middle of the", "14x12 drone camera window four times in a row, each pass blowing the centre 7x6 cells up to 2x2", "characters per cell so the picture appears to dive into the target, then hands over to", "playDroneLostAnimation which flashes and wipes the window and shrinks the drone icon away.", "In: nothing; the camera window must already show the drone's surroundings. Uses zoomStepCounter", " ($7551) as the pass counter.", "Out: the camera window (rows 6-17, columns 13-26) is left magnified; zoomStepCounter = $FF; never", " returns - it jumps into playDroneLostAnimation ($7163).", "Called from: strikeWithDrone's wait loop ($81CD), right after sound $11 (the detonation)." ], "block": [ "Four magnification passes: 12, 10, 8 and 6 game frames are run between them." ], "line": "four zoom passes: the counter runs 3,2,1,0" }, "7554": { "unit": "game/comcenScreens6F00", "line": "pass counter for the loop below" }, "7557": { "unit": "game/comcenScreens6F00", "block": [ "One zoom pass. magnifyWindow2x eats windowWidth/windowHeight, so both are reloaded every time.", "Source = the 7x6 cell block at row 9, column 16; the routine centres it, so the result covers", "rows 6-17 and columns 13-26 - exactly the drone camera window." ], "line": "fillRow = character row 9" }, "7559": { "unit": "game/comcenScreens6F00", "line": "row of the source block magnifyWindow2x reads" }, "755C": { "unit": "game/comcenScreens6F00", "line": "fillColumn = character column 16" }, "755E": { "unit": "game/comcenScreens6F00", "line": "column of the source block" }, "7561": { "unit": "game/comcenScreens6F00", "line": "source block is 7 cells wide" }, "7563": { "unit": "game/comcenScreens6F00", "line": "windowWidth for magnifyWindow2x" }, "7566": { "unit": "game/comcenScreens6F00", "line": "source block is 6 cells high" }, "7568": { "unit": "game/comcenScreens6F00", "line": "windowHeight for magnifyWindow2x" }, "756B": { "unit": "game/comcenScreens6F00", "line": "blow those 7x6 cells up 2x2 in place, filling the whole 14x12 camera window" }, "756E": { "unit": "game/comcenScreens6F00", "line": "pass number 3..0" }, "7571": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "7572": { "unit": "game/comcenScreens6F00", "line": "pass+3 = 6,5,4,3" }, "7574": { "unit": "game/comcenScreens6F00", "line": "doubled: 12,10,8,6 game frames of pause after this pass" }, "7575": { "unit": "game/comcenScreens6F00", "line": "keep the battle running while the picture is held" }, "7578": { "unit": "game/comcenScreens6F00", "line": "one pass done" }, "757B": { "unit": "game/comcenScreens6F00", "line": "branch while zoom passes remain (counter 3..0; it leaves the loop at $FF)" }, "757D": { "unit": "game/comcenScreens6F00", "line": "hold the final, fully magnified picture" }, "757F": { "unit": "game/comcenScreens6F00", "line": "for 6 more game frames" }, "7582": { "unit": "game/comcenScreens6F00", "line": "continue with the drone-destroyed animation (white flash, iris wipe, shrinking icon)" }, "7585": { "unit": "game/comcenScreens6F00", "block": [ "Two state bytes of the missile screen." ], "line": "bit 7 set while the missile screen is inoperative (SCRIMAGE game type, or our comcen is cloaked)" }, "7586": { "unit": "game/comcenScreens6F00", "line": "0 until the target cursor has been moved once, then $7F; firing is refused while it is 0" }, "7587": { "unit": "game/comcenScreens6F00", "routine": [ "runMissileScreen - Entry of game screen 1, the comcen RADAR / MISSILE screen (function key F3).", "Resets the 'cursor moved' interlock, takes a display copy of the local missile count, unpacks the", "eight direction sprite shapes used for the enemy drone marker and builds the whole screen; then", "falls into missileScreenResume, which never returns.", "In: missilesLeft ($92B0, own side), comcenUnit ($9236), the $F000 map and the unit arrays.", "Out: cursorMovedFlag = 0, radarMissilesLeft ($9232) = missilesLeft, $0200 = unpacked sprite shapes,", " screen 1 drawn. Never returns.", "Called from: the screen dispatcher at $0703 (game/scratchAndMessages0400) when currentScreen = 1." ], "line": "clear the interlock" }, "7589": { "unit": "game/comcenScreens6F00", "line": "a fire press does nothing until the cursor has been moved" }, "758C": { "unit": "game/comcenScreens6F00", "line": "missiles the local player still has" }, "758F": { "unit": "game/comcenScreens6F00", "line": "display copy shown in the MISSILES box and decremented as they are launched" }, "7592": { "unit": "game/comcenScreens6F00", "line": "low byte of the packed sprite shapes at $FD70" }, "7594": { "unit": "game/comcenScreens6F00", "line": "high byte: $FD70 in the resident high-memory block" }, "7596": { "unit": "game/comcenScreens6F00", "line": "unpack the eight direction/aircraft silhouettes to $0200 for loadDirectionShapeSprite" }, "7599": { "unit": "game/comcenScreens6F00", "line": "draw the frames, the MISSILES/FIRE/DRONE ALERT panels and the 21x17 radar map" }, "759C": { "unit": "game/comcenScreens6F00", "routine": [ "missileScreenResume - Main loop of the missile screen, and the re-entry point used after the", "'COMCEN STUNNED!' blackout cut-scene. Decides first whether the screen works at all (it is dead in", "a SCRIMAGE game, which has no comcens, and while our own comcen is cloaked); if it works the radar", "picture, the sweep and the target cross are (re)built. The loop then runs one game frame at a", "time, blinks the FIRE button and - on the last missile - the count box, redraws after every command", "exchange, advances the radar sweep, and turns a fire press into a $90 (missile strike) command or a", "joystick deflection into a cursor move with auto-repeat.", "In: gameTypeOptions ($0BA3 bits 0-2), comcenCloakedFlag ($9245), exchangeAppliedFlag ($920E),", " comcenRadarStatus ($9243), radarMissilesLeft ($9232), droneArrivalFlags ($92F9),", " joyDirection (zp_5D), joyRepeatTimer (zp_6C), markerBlinkTimer (zp_6F), joyRepeatMask/", " joyHoldCounter ($91C9/$91CA), joystickDirectionTable ($1FA6).", "Out: missileScreenLocked, cursorMovedFlag, radarMissilesLeft, joyRepeatMask = $7F, sprites 1-3,", " $90 commands queued. Never returns.", "Called from: fallen into from runMissileScreen; JMP from $0764 (end of runComcenStunnedBlackout)." ], "block": [ "Is the screen usable? Game type 0 (SCRIMAGE) is played without command centres, and a cloaked", "comcen runs its radar silent. Either sets bit 7 of missileScreenLocked." ], "line": "game option byte" }, "759F": { "unit": "game/comcenScreens6F00", "line": "bits 0-2 = game type (0 SCRIMAGE .. 6 DEFENDER)" }, "75A1": { "unit": "game/comcenScreens6F00", "line": "branch unless the type is 0 (SCRIMAGE), which is played without comcens" }, "75A3": { "unit": "game/comcenScreens6F00", "line": "no comcen: mark the missile screen inoperative" }, "75A5": { "unit": "game/comcenScreens6F00", "line": "bit 7 of $9245 is set while our own comcen is cloaked (radar off the air)" }, "75A8": { "unit": "game/comcenScreens6F00", "line": "bit 7 = screen locked" }, "75AB": { "unit": "game/comcenScreens6F00", "line": "branch if locked: keep ticking the game but draw nothing" }, "75AD": { "unit": "game/comcenScreens6F00", "line": "enemy blips, missile count and the DRONE ALERT panel" }, "75B0": { "unit": "game/comcenScreens6F00", "line": "the 21x17 cell radar map centred on our comcen" }, "75B3": { "unit": "game/comcenScreens6F00", "line": "start a fresh sweep from the top row" }, "75B6": { "unit": "game/comcenScreens6F00", "line": "sprite 2 = the target cross, parked on the comcen at grid cell (10,8)" }, "75B9": { "unit": "game/comcenScreens6F00", "block": [ "Main loop. One pass per game frame. Everything below the 'locked' test is skipped while the", "screen is inoperative." ], "line": "one game tick: IRQ counters, unit updates, link traffic" }, "75BC": { "unit": "game/comcenScreens6F00", "line": "drone status flags" }, "75BF": { "unit": "game/comcenScreens6F00", "line": "bit 0 = our own $92 'drone destroyed' command is queued but not yet acknowledged" }, "75C1": { "unit": "game/comcenScreens6F00", "line": "branch if it is still pending - do not leave the screen before it is confirmed" }, "75C3": { "unit": "game/comcenScreens6F00", "line": "F1/F3/F5/F7 may switch to another screen (never returns if it does)" }, "75C6": { "unit": "game/comcenScreens6F00", "line": "IRQ frame countdown for the button blink" }, "75C8": { "unit": "game/comcenScreens6F00", "line": "branch until the 9-frame blink interval has expired" }, "75CA": { "unit": "game/comcenScreens6F00", "line": "next blink in 9 frames" }, "75CC": { "unit": "game/comcenScreens6F00", "line": "restart the countdown" }, "75CE": { "unit": "game/comcenScreens6F00", "line": "character row 10" }, "75D0": { "unit": "game/comcenScreens6F00", "line": "row of the 2x2 FIRE button icon" }, "75D3": { "unit": "game/comcenScreens6F00", "line": "character column 32" }, "75D5": { "unit": "game/comcenScreens6F00", "line": "column of the FIRE button icon" }, "75D8": { "unit": "game/comcenScreens6F00", "line": "EOR mask 8: flips bit 3 of the %10 colour nibble, so green 5 <-> light green $D" }, "75DA": { "unit": "game/comcenScreens6F00", "line": "toggle the 4 video-matrix cells of the button, making it pulse" }, "75DD": { "unit": "game/comcenScreens6F00", "line": "missiles left on the display" }, "75E0": { "unit": "game/comcenScreens6F00", "line": "compare with 2" }, "75E2": { "unit": "game/comcenScreens6F00", "line": "branch while at least 2 missiles remain" }, "75E4": { "unit": "game/comcenScreens6F00", "line": "down to the last missile: flash the count box as well" }, "75E7": { "unit": "game/comcenScreens6F00", "line": "screen lock flag" }, "75EA": { "unit": "game/comcenScreens6F00", "line": "branch if locked - nothing but the frame tick and the blink runs" }, "75EC": { "unit": "game/comcenScreens6F00", "block": [ "A command exchange has been applied ($920E = $80 from $41F8): the opponent's units have moved, so", "the enemy blips and the drone panel are redrawn and a pending drone kill is shown." ], "line": "$80 after processCommandExchange has applied a turn's commands" }, "75EF": { "unit": "game/comcenScreens6F00", "line": "branch unless a new exchange has arrived" }, "75F1": { "unit": "game/comcenScreens6F00", "line": "redraw the enemy blips, the count and the DRONE ALERT panel" }, "75F4": { "unit": "game/comcenScreens6F00", "line": "if our missile killed the drone, play the burst now" }, "75F7": { "unit": "game/comcenScreens6F00", "line": "advance the sweep band by one step if its 2-frame slot is due" }, "75FA": { "unit": "game/comcenScreens6F00", "block": [ "Fire button. A press only counts once the cursor has been moved, and a comcen whose fire control", "is damaged loses roughly half of the presses." ], "line": "returns A = 0 (Z set) only for a fresh, accepted press" }, "75FD": { "unit": "game/comcenScreens6F00", "line": "no press this frame - go and read the joystick instead" }, "75FF": { "unit": "game/comcenScreens6F00", "line": "has the cursor ever been moved?" }, "7602": { "unit": "game/comcenScreens6F00", "line": "no - ignore the press (guards against firing at the comcen's own cell)" }, "7604": { "unit": "game/comcenScreens6F00", "line": "non-zero once the comcen radar/fire control has been knocked out" }, "7607": { "unit": "game/comcenScreens6F00", "line": "undamaged: the shot always goes off" }, "7609": { "unit": "game/comcenScreens6F00", "line": "damaged: draw a random bit" }, "760C": { "unit": "game/comcenScreens6F00", "line": "about half the presses still get through" }, "760E": { "unit": "game/comcenScreens6F00", "block": [ "Joystick: move the target cursor over the radar grid, with a 10-frame delay before the first", "repeat and 4 frames between the following ones." ], "line": "IRQ auto-repeat countdown" }, "7610": { "unit": "game/comcenScreens6F00", "line": "branch back while the repeat delay is still running" }, "7612": { "unit": "game/comcenScreens6F00", "line": "active-low joystick nibble latched by the IRQ ($0F = centred)" }, "7614": { "unit": "game/comcenScreens6F00", "line": "-> direction code 0-7 (N,S,E,W,NW,NE,SE,SW), $FF when centred" }, "7617": { "unit": "game/comcenScreens6F00", "line": "stick centred - nothing to do this frame" }, "7619": { "unit": "game/comcenScreens6F00", "line": "10 frames before the first auto-repeat" }, "761B": { "unit": "game/comcenScreens6F00", "line": "stash the direction code across the timing code" }, "761C": { "unit": "game/comcenScreens6F00", "block": [ "Auto-repeat pacing. joyRepeatMask is cleared by the IRQ ($1220) whenever the stick returns to", "centre, so the first deflection sees mask 0 (counter stays 0 -> long delay) and every later frame", "sees mask $7F (counter free-runs -> short delay).", "(The label itself is only a branch target in the overlay A variant of this address.)" ], "line": "count the frames the stick has been held" }, "761F": { "unit": "game/comcenScreens6F00", "line": "reload it" }, "7622": { "unit": "game/comcenScreens6F00", "line": "mask = 0 on the very first deflection, $7F while the stick stays deflected" }, "7625": { "unit": "game/comcenScreens6F00", "line": "store the masked counter back" }, "7628": { "unit": "game/comcenScreens6F00", "line": "still zero?" }, "762A": { "unit": "game/comcenScreens6F00", "line": "yes - first step, keep the long 10-frame delay" }, "762C": { "unit": "game/comcenScreens6F00", "line": "held down: repeat every 4 frames" }, "762E": { "unit": "game/comcenScreens6F00", "line": "arm the IRQ countdown" }, "7630": { "unit": "game/comcenScreens6F00", "line": "mask $7F" }, "7632": { "unit": "game/comcenScreens6F00", "line": "from now on the hold counter free-runs, so the fast repeat sticks" }, "7635": { "unit": "game/comcenScreens6F00", "line": "$7F also marks 'the cursor has been moved', which unlocks firing" }, "7638": { "unit": "game/comcenScreens6F00", "line": "recover the direction code" }, "7639": { "unit": "game/comcenScreens6F00", "line": "step the cursor one grid cell that way" }, "763C": { "unit": "game/comcenScreens6F00", "line": "back to the top of the loop" }, "763F": { "unit": "game/comcenScreens6F00", "block": [ "Fire accepted: spend a missile, light the button, queue command $90 (missile strike at col,row)", "and play the flight and impact animation." ], "line": "missiles left" }, "7642": { "unit": "game/comcenScreens6F00", "line": "none left - the press does nothing" }, "7644": { "unit": "game/comcenScreens6F00", "line": "spend one" }, "7647": { "unit": "game/comcenScreens6F00", "line": "redraw the two-digit count" }, "764A": { "unit": "game/comcenScreens6F00", "block": [ "Light the FIRE button while the missile is away." ], "line": "character row 10" }, "764C": { "unit": "game/comcenScreens6F00", "line": "row of the button icon" }, "764F": { "unit": "game/comcenScreens6F00", "line": "character column 32" }, "7651": { "unit": "game/comcenScreens6F00", "line": "column of the button icon" }, "7654": { "unit": "game/comcenScreens6F00", "line": "$05 = %01 pixels black, %10 pixels green: button lit" }, "7656": { "unit": "game/comcenScreens6F00", "line": "2 cells wide" }, "7658": { "unit": "game/comcenScreens6F00", "line": "2 cells high" }, "765A": { "unit": "game/comcenScreens6F00", "line": "write the 4 video-matrix cells" }, "765D": { "unit": "game/comcenScreens6F00", "line": "sound $0F = missile launch" }, "765F": { "unit": "game/comcenScreens6F00", "line": "play it" }, "7662": { "unit": "game/comcenScreens6F00", "line": "cmdParam1/cmdParam2 = the map cell under the target cursor" }, "7665": { "unit": "game/comcenScreens6F00", "line": "command $90 = missile strike at col,row" }, "7667": { "unit": "game/comcenScreens6F00", "line": "queue it for this turn's packet (and for the game film)" }, "766A": { "unit": "game/comcenScreens6F00", "line": "fly sprite 3 from the comcen to the target cell" }, "766D": { "unit": "game/comcenScreens6F00", "block": [ "Impact: put the ring-burst sprite 1 exactly where the target cross (sprite 2) is." ], "line": "target cross X" }, "7670": { "unit": "game/comcenScreens6F00", "line": "-> ring burst X" }, "7673": { "unit": "game/comcenScreens6F00", "line": "target cross Y" }, "7676": { "unit": "game/comcenScreens6F00", "line": "-> ring burst Y" }, "7679": { "unit": "game/comcenScreens6F00", "line": "3 radius steps" }, "767B": { "unit": "game/comcenScreens6F00", "line": "small expanding ring at the impact point" }, "767E": { "unit": "game/comcenScreens6F00", "line": "did the shot hit an airborne drone?" }, "7681": { "unit": "game/comcenScreens6F00", "line": "10 frames" }, "7683": { "unit": "game/comcenScreens6F00", "line": "swallow further fire presses for that long" }, "7685": { "unit": "game/comcenScreens6F00", "line": "back to the top of the loop" }, "7688": { "unit": "game/comcenScreens6F00", "routine": [ "refreshMissileScreen - Redraw hook of screen 1, run once when the screen is built and again after", "every applied command exchange: acknowledges the refresh request, repaints every enemy unit blip", "with a fresh noise pattern and puts the DRONE ALERT panel into its flashing or its dark state", "depending on whether any drone is airborne. With no drone up it also blanks and disables sprite 0", "(the enemy drone marker) and sprite 1 (the ring burst).", "In: droneControlFlags ($920D bit 0, set by cmdDroneMove while a drone is flying), gameEndReason", " ($0BA8), the unit arrays.", "Out: exchangeAppliedFlag = 0, blips redrawn, radarNoiseMasks re-randomised, sprites 0/1 off or", " left alone, DRONE ALERT panel repainted. Tail-jumps into the panel routines.", "Called from: missileScreenResume ($75AD screen build, $75F1 after an exchange).", "(survey name sub_7688)" ], "line": "clear the request" }, "768A": { "unit": "game/comcenScreens6F00", "line": "the refresh has been served" }, "768D": { "unit": "game/comcenScreens6F00", "line": "repaint all 50 enemy units with a new random dropout pattern" }, "7690": { "unit": "game/comcenScreens6F00", "line": "0 while a game is running; 1 = aborted, $08 = time up, $A0/$C0 = a comcen was knocked out" }, "7693": { "unit": "game/comcenScreens6F00", "line": "branch while a game is running - so the count is only refreshed once the game is already over," }, "7695": { "unit": "game/comcenScreens6F00", "block": [ "which looks like an inverted test (the count is redrawn from the fire handler at $7647 anyway,", "so during play this branch simply never fires)." ], "line": "redraw the two-digit missile count" }, "7698": { "unit": "game/comcenScreens6F00", "block": [ "DRONE ALERT panel: flashing while any drone is up, dark otherwise." ], "line": "drone control flags" }, "769B": { "unit": "game/comcenScreens6F00", "line": "bit 0 is set by cmdDroneMove ($52E9) while a drone - ours or theirs - is in the air" }, "769D": { "unit": "game/comcenScreens6F00", "line": "a drone is up: flash the panel" }, "769F": { "unit": "game/comcenScreens6F00", "line": "sprite 1 = the ring burst" }, "76A1": { "unit": "game/comcenScreens6F00", "line": "blank its shape" }, "76A4": { "unit": "game/comcenScreens6F00", "line": "sprite enable shadow" }, "76A7": { "unit": "game/comcenScreens6F00", "line": "clear bits 0 and 1: drone marker (sprite 0) and ring burst (sprite 1) off" }, "76A9": { "unit": "game/comcenScreens6F00", "line": "store it back" }, "76AC": { "unit": "game/comcenScreens6F00", "line": "print DRONE / ALERT dark and return through it" }, "76AF": { "unit": "game/comcenScreens6F00", "line": "toggle the panel colour and return through it" }, "76B2": { "unit": "game/comcenScreens6F00", "routine": [ "moveRadarCursorByDirection - Turns a joystick direction code 0-7 into a (dCol,dRow) pair and hands", "it to offsetRadarCursor. Codes 0/1 are N/S, 2/3 are E/W and 4-7 are the diagonals, whose deltas", "are packed one per nibble in diagonalDirectionTable; a diagonal step also stretches the auto-repeat", "delay by 1.5 so diagonal travel is not faster than straight travel.", "In: A = direction code 0-7 (from joystickDirectionTable), joyRepeatTimer (zp_6C).", "Out: X = column delta, Y = row delta, then tail-calls offsetRadarCursor; zp_6C stretched for", " diagonals.", "Called from: missileScreenResume ($7639).", "(survey name sub_76B2)" ], "line": "row delta defaults to 0" }, "76B4": { "unit": "game/comcenScreens6F00", "line": "column delta defaults to 0" }, "76B6": { "unit": "game/comcenScreens6F00", "line": "codes 0-3 are orthogonal, 4-7 diagonal" }, "76B8": { "unit": "game/comcenScreens6F00", "line": "branch for the four diagonals" }, "76BA": { "unit": "game/comcenScreens6F00", "line": "codes 0/1 = N/S, 2/3 = E/W" }, "76BC": { "unit": "game/comcenScreens6F00", "line": "branch for the two horizontal directions" }, "76BE": { "unit": "game/comcenScreens6F00", "line": "N/S: use the code as the table index" }, "76BF": { "unit": "game/comcenScreens6F00", "line": "$20F0: index 0 -> $FF (up), 1 -> $01 (down)" }, "76C2": { "unit": "game/comcenScreens6F00", "line": "that is the row delta" }, "76C3": { "unit": "game/comcenScreens6F00", "line": "apply it (column delta stays 0)" }, "76C6": { "unit": "game/comcenScreens6F00", "line": "E/W: use the code as the table index" }, "76C7": { "unit": "game/comcenScreens6F00", "line": "$20F0: index 2 -> $01 (right), 3 -> $FF (left)" }, "76CA": { "unit": "game/comcenScreens6F00", "line": "that is the column delta" }, "76CB": { "unit": "game/comcenScreens6F00", "line": "apply it (row delta stays 0)" }, "76CE": { "unit": "game/comcenScreens6F00", "line": "diagonal: index the packed table with the code" }, "76CF": { "unit": "game/comcenScreens6F00", "line": "$1FB2+4..7 = $30,$20,$21,$31 = (column index << 4) | row index" }, "76D2": { "unit": "game/comcenScreens6F00", "line": "keep the packed byte" }, "76D3": { "unit": "game/comcenScreens6F00", "line": "low nibble = row index into directionDeltaTable" }, "76D5": { "unit": "game/comcenScreens6F00", "line": "as index" }, "76D6": { "unit": "game/comcenScreens6F00", "line": "-1 or +1" }, "76D9": { "unit": "game/comcenScreens6F00", "line": "row delta" }, "76DA": { "unit": "game/comcenScreens6F00", "line": "packed byte back" }, "76DB": { "unit": "game/comcenScreens6F00", "line": "shift the" }, "76DC": { "unit": "game/comcenScreens6F00", "line": "high nibble" }, "76DD": { "unit": "game/comcenScreens6F00", "line": "down into" }, "76DE": { "unit": "game/comcenScreens6F00", "line": "bits 0-3" }, "76DF": { "unit": "game/comcenScreens6F00", "line": "column index into directionDeltaTable" }, "76E0": { "unit": "game/comcenScreens6F00", "line": "-1 or +1" }, "76E3": { "unit": "game/comcenScreens6F00", "line": "column delta" }, "76E4": { "unit": "game/comcenScreens6F00", "line": "current auto-repeat delay" }, "76E6": { "unit": "game/comcenScreens6F00", "line": "half of it" }, "76E7": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "76E8": { "unit": "game/comcenScreens6F00", "line": "delay + delay/2" }, "76EA": { "unit": "game/comcenScreens6F00", "line": "a diagonal step costs 1.5 repeat delays, so diagonal travel is not faster" }, "76EC": { "unit": "game/comcenScreens6F00", "routine": [ "offsetRadarCursor - Moves the missile target cursor by (X,Y) grid cells, refusing the move if it", "would leave the 21x17 radar grid, and repositions the cross-hair sprite 2 on the new cell.", "In: X = column delta, Y = row delta, gridCursorCol/gridCursorRow (zp_67/zp_68).", "Out: zp_67/zp_68 and sprite2XShadow/sprite2YShadow updated, or nothing at all when the target cell", " is off the grid.", "Called from: moveRadarCursorByDirection ($76C3, $76CB) and scrollRadarOnComcenMove ($797B).", "Self-modifying: the candidate column and row are parked in the LDA # operands at setCursorColImm+1", "($7702) and setCursorRowImm+1 ($7706) while the range test runs.", "(survey name sub_76EC)" ], "line": "column delta" }, "76ED": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "76EE": { "unit": "game/comcenScreens6F00", "line": "current cursor column 0-20" }, "76F0": { "unit": "game/comcenScreens6F00", "line": "candidate column in X for the range test" }, "76F1": { "unit": "game/comcenScreens6F00", "line": "park it in the LDA # operand at $7701 (self-modifying code)" }, "76F4": { "unit": "game/comcenScreens6F00", "line": "row delta" }, "76F5": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "76F6": { "unit": "game/comcenScreens6F00", "line": "current cursor row 0-16" }, "76F8": { "unit": "game/comcenScreens6F00", "line": "candidate row in Y for the range test" }, "76F9": { "unit": "game/comcenScreens6F00", "line": "park it in the LDA # operand at $7705 (self-modifying code)" }, "76FC": { "unit": "game/comcenScreens6F00", "line": "C = 0 only when the cell lies inside the 21x17 grid" }, "76FF": { "unit": "game/comcenScreens6F00", "line": "off the grid - leave the cursor where it was" }, "7701": { "unit": "game/comcenScreens6F00", "line": "operand patched at $76F1: the accepted column" }, "7703": { "unit": "game/comcenScreens6F00", "line": "new cursor column" }, "7705": { "unit": "game/comcenScreens6F00", "line": "operand patched at $76F9: the accepted row" }, "7707": { "unit": "game/comcenScreens6F00", "line": "new cursor row" }, "7709": { "unit": "game/comcenScreens6F00", "line": "grid cell -> sprite coordinates (column*8+$27, row*8+$43)" }, "770C": { "unit": "game/comcenScreens6F00", "line": "move the target cross horizontally" }, "770F": { "unit": "game/comcenScreens6F00", "line": "and vertically" }, "7712": { "unit": "game/comcenScreens6F00", "line": "done" }, "7713": { "unit": "game/comcenScreens6F00", "routine": [ "animateMissileFlight - Flies the launched missile across the radar window: builds a short slanted", "dash in sprite 3 pointing along the line from the comcen cell (grid 10,8) to the target cursor,", "then walks that line pixel by pixel, moving the sprite every 6 frames and taking more pixel steps", "per move as the flight goes on (X = squaresTable[move/4]) so the missile accelerates. The battle", "keeps running underneath, so the 12-byte line stepper state is saved and restored around every", "game frame. The animation stops when the line is exhausted or the game ends.", "In: gridCursorCol/gridCursorRow (zp_67/zp_68) = the target cell, missileFlightTick ($92F8) which", " the IRQ increments once per frame, gameEndReason ($0BA8).", "Out: sprite 3 shape/position/colour, sprite 3 enabled then disabled again, line stepper state", " ($8C-$97) consumed, savedLineState ($92CA) used as scratch.", "Called from: missileScreenResume ($766A), right after the $90 command has been queued.", "Self-modifying: the sprite origin bytes at missileSpriteXOrigin+1/$77E0 and missileSpriteYOrigin+1/", " $77E8, the frame threshold at missileTickThresholdCmp+1/$7808 and the move counter at", " missileStepIndexLoad+1/$782B are all written by this routine.", "(survey name sub_7713)" ], "block": [ "Rebase both endpoints of the flight to the top-left corner of their bounding box, so the line", "stepper works with small positive numbers; scratch20/scratch21 keep that corner in grid cells." ], "line": "the comcen is always drawn at grid column 10" }, "7715": { "unit": "game/comcenScreens6F00", "line": "target column 0-20" }, "7717": { "unit": "game/comcenScreens6F00", "block": [ "$7717 is only a fall-through point here (in the overlay A variant the same address is the map", "generator's enterMapName, which is why the survey gave it a label)." ], "line": "X = min of the two, A and Y = both values minus that min" }, "771A": { "unit": "game/comcenScreens6F00", "line": "left column of the bounding box, in grid cells" }, "771C": { "unit": "game/comcenScreens6F00", "line": "line start column, relative to the box" }, "771E": { "unit": "game/comcenScreens6F00", "line": "keep a copy: the line is walked twice" }, "7720": { "unit": "game/comcenScreens6F00", "line": "line end column, relative to the box" }, "7722": { "unit": "game/comcenScreens6F00", "line": "copy" }, "7724": { "unit": "game/comcenScreens6F00", "line": "the comcen is always drawn at grid row 8" }, "7726": { "unit": "game/comcenScreens6F00", "line": "target row 0-16" }, "7728": { "unit": "game/comcenScreens6F00", "line": "same rebasing for the rows" }, "772B": { "unit": "game/comcenScreens6F00", "line": "top row of the bounding box" }, "772D": { "unit": "game/comcenScreens6F00", "line": "line start row" }, "772F": { "unit": "game/comcenScreens6F00", "line": "copy" }, "7731": { "unit": "game/comcenScreens6F00", "line": "line end row" }, "7733": { "unit": "game/comcenScreens6F00", "line": "copy" }, "7735": { "unit": "game/comcenScreens6F00", "line": "end row" }, "7736": { "unit": "game/comcenScreens6F00", "line": "OR in the end column" }, "7738": { "unit": "game/comcenScreens6F00", "line": "OR in the start row" }, "773A": { "unit": "game/comcenScreens6F00", "line": "OR in the start column" }, "773C": { "unit": "game/comcenScreens6F00", "line": "branch if any of the four is non-zero" }, "773E": { "unit": "game/comcenScreens6F00", "line": "all four zero: the cursor sits on the comcen itself, nothing to fly" }, "7741": { "unit": "game/comcenScreens6F00", "line": "is the longest side at least 4 cells?" }, "7743": { "unit": "game/comcenScreens6F00", "line": "yes - the coordinates already have enough resolution" }, "7745": { "unit": "game/comcenScreens6F00", "block": [ "Very short shot: scale the four coordinates by 4 so that the three-step probe below still finds", "the true direction of the line." ], "line": "start column x2" }, "7747": { "unit": "game/comcenScreens6F00", "line": "x4" }, "7749": { "unit": "game/comcenScreens6F00", "line": "start row x2" }, "774B": { "unit": "game/comcenScreens6F00", "line": "x4" }, "774D": { "unit": "game/comcenScreens6F00", "line": "end column x2" }, "774F": { "unit": "game/comcenScreens6F00", "line": "x4" }, "7751": { "unit": "game/comcenScreens6F00", "line": "end row x2" }, "7753": { "unit": "game/comcenScreens6F00", "line": "x4" }, "7755": { "unit": "game/comcenScreens6F00", "block": [ "Build the missile shape: walk three units along the line, then draw that little segment twice", "(one pixel apart) into sprite 3, giving a 4-pixel long, 2-pixel thick dash aimed at the target." ], "line": "line start column" }, "7757": { "unit": "game/comcenScreens6F00", "line": "remember it" }, "7759": { "unit": "game/comcenScreens6F00", "line": "line start row" }, "775B": { "unit": "game/comcenScreens6F00", "line": "remember it" }, "775D": { "unit": "game/comcenScreens6F00", "line": "set up the Bresenham walk from (start) to (end)" }, "7760": { "unit": "game/comcenScreens6F00", "line": "step 1" }, "7763": { "unit": "game/comcenScreens6F00", "line": "step 2" }, "7766": { "unit": "game/comcenScreens6F00", "line": "step 3 - far enough to know the direction" }, "7769": { "unit": "game/comcenScreens6F00", "line": "segment start column" }, "776B": { "unit": "game/comcenScreens6F00", "line": "segment end column after 3 steps" }, "776D": { "unit": "game/comcenScreens6F00", "line": "rebase the short segment to its own origin" }, "7770": { "unit": "game/comcenScreens6F00", "line": "segment start column, 0-based" }, "7772": { "unit": "game/comcenScreens6F00", "line": "segment end column" }, "7774": { "unit": "game/comcenScreens6F00", "line": "segment start row" }, "7776": { "unit": "game/comcenScreens6F00", "line": "segment end row after 3 steps" }, "7778": { "unit": "game/comcenScreens6F00", "line": "rebase the rows the same way" }, "777B": { "unit": "game/comcenScreens6F00", "line": "segment start row, 0-based" }, "777D": { "unit": "game/comcenScreens6F00", "line": "segment end row" }, "777F": { "unit": "game/comcenScreens6F00", "line": "sprite 3 is the missile" }, "7781": { "unit": "game/comcenScreens6F00", "line": "blank its 64-byte shape" }, "7784": { "unit": "game/comcenScreens6F00", "line": "plot the 4-pixel dash" }, "7787": { "unit": "game/comcenScreens6F00", "line": "shift the start one pixel right" }, "7789": { "unit": "game/comcenScreens6F00", "line": "and the end with it" }, "778B": { "unit": "game/comcenScreens6F00", "line": "plot it again: the dash is now 2 pixels thick" }, "778E": { "unit": "game/comcenScreens6F00", "block": [ "Now walk the real flight path. The four saved cell coordinates are turned into pixels (8 pixels", "per grid cell) and stepped one pixel at a time." ], "line": "index of the last of the four saved coordinates ($1C..$1F)" }, "7790": { "unit": "game/comcenScreens6F00", "line": "one saved coordinate (absolute addressing forced so it is a 3-byte instruction)" }, "7793": { "unit": "game/comcenScreens6F00", "line": "x2" }, "7794": { "unit": "game/comcenScreens6F00", "line": "x4" }, "7795": { "unit": "game/comcenScreens6F00", "line": "x8 - one grid cell is 8 pixels" }, "7796": { "unit": "game/comcenScreens6F00", "line": "write it back" }, "7799": { "unit": "game/comcenScreens6F00", "line": "next coordinate" }, "779A": { "unit": "game/comcenScreens6F00", "line": "loop over start column, start row, end column, end row" }, "779C": { "unit": "game/comcenScreens6F00", "line": "start column in pixels" }, "779E": { "unit": "game/comcenScreens6F00", "line": "into the line stepper" }, "77A0": { "unit": "game/comcenScreens6F00", "line": "start row in pixels" }, "77A2": { "unit": "game/comcenScreens6F00", "line": "into the line stepper" }, "77A4": { "unit": "game/comcenScreens6F00", "line": "end column in pixels" }, "77A6": { "unit": "game/comcenScreens6F00", "line": "into the line stepper" }, "77A8": { "unit": "game/comcenScreens6F00", "line": "end row in pixels" }, "77AA": { "unit": "game/comcenScreens6F00", "line": "into the line stepper" }, "77AC": { "unit": "game/comcenScreens6F00", "line": "set the walk up again, now measured in pixels" }, "77AF": { "unit": "game/comcenScreens6F00", "line": "skip the first pixel" }, "77B2": { "unit": "game/comcenScreens6F00", "line": "and the second, so the missile starts clear of the comcen" }, "77B5": { "unit": "game/comcenScreens6F00", "line": "bounding box origin column (grid cells)" }, "77B7": { "unit": "game/comcenScreens6F00", "line": "bounding box origin row" }, "77B9": { "unit": "game/comcenScreens6F00", "line": "the returned carry is never tested - a leftover range check" }, "77BC": { "unit": "game/comcenScreens6F00", "line": "-> sprite coordinates of that corner cell" }, "77BF": { "unit": "game/comcenScreens6F00", "line": "sprite X of the corner" }, "77C0": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "77C1": { "unit": "game/comcenScreens6F00", "line": "+11 pixels centres the dash inside the cell" }, "77C3": { "unit": "game/comcenScreens6F00", "line": "patch the ADC # operand at $77DF (self-modifying code)" }, "77C6": { "unit": "game/comcenScreens6F00", "line": "sprite Y of the corner" }, "77C7": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "77C8": { "unit": "game/comcenScreens6F00", "line": "+9 pixels centres the dash vertically" }, "77CA": { "unit": "game/comcenScreens6F00", "line": "patch the ADC # operand at $77E7 (self-modifying code)" }, "77CD": { "unit": "game/comcenScreens6F00", "line": "restart the frame counter" }, "77CF": { "unit": "game/comcenScreens6F00", "line": "$92F8 is incremented once per frame by the IRQ ($129C)" }, "77D2": { "unit": "game/comcenScreens6F00", "line": "first move after 6 frames" }, "77D4": { "unit": "game/comcenScreens6F00", "line": "patch the CMP # operand at $7807 (self-modifying code)" }, "77D7": { "unit": "game/comcenScreens6F00", "line": "no moves made yet" }, "77D9": { "unit": "game/comcenScreens6F00", "line": "patch the LDA # operand at $782A (self-modifying code)" }, "77DC": { "unit": "game/comcenScreens6F00", "block": [ "Per-move: place sprite 3 at the current pixel, then wait 6 frames while the game keeps running." ], "line": "current pixel column along the line" }, "77DE": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "77DF": { "unit": "game/comcenScreens6F00", "line": "operand patched at $77C3: sprite X of the bounding box corner + 11" }, "77E1": { "unit": "game/comcenScreens6F00", "line": "sprite 3 X position" }, "77E4": { "unit": "game/comcenScreens6F00", "line": "current pixel row along the line" }, "77E6": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "77E7": { "unit": "game/comcenScreens6F00", "line": "operand patched at $77CA: sprite Y of the bounding box corner + 9" }, "77E9": { "unit": "game/comcenScreens6F00", "line": "sprite 3 Y position" }, "77EC": { "unit": "game/comcenScreens6F00", "line": "colour 1 = white" }, "77EE": { "unit": "game/comcenScreens6F00", "line": "sprite 3 colour" }, "77F1": { "unit": "game/comcenScreens6F00", "line": "sprite 3 enable bit" }, "77F3": { "unit": "game/comcenScreens6F00", "line": "switch it on" }, "77F6": { "unit": "game/comcenScreens6F00", "line": "the game frame below reuses the line stepper ($8C-$97), so save it" }, "77F9": { "unit": "game/comcenScreens6F00", "line": "keep the radar sweep running during the flight" }, "77FC": { "unit": "game/comcenScreens6F00", "line": "one game tick: units keep moving, the link keeps talking" }, "77FF": { "unit": "game/comcenScreens6F00", "line": "0 while a game is running" }, "7802": { "unit": "game/comcenScreens6F00", "line": "the game ended under us - drop the animation" }, "7804": { "unit": "game/comcenScreens6F00", "line": "free-running frame counter" }, "7807": { "unit": "game/comcenScreens6F00", "line": "operand patched at $77D4/$781F: the frame at which the next move is due" }, "7809": { "unit": "game/comcenScreens6F00", "line": "not yet - run another frame" }, "780B": { "unit": "game/comcenScreens6F00", "line": "current threshold" }, "780E": { "unit": "game/comcenScreens6F00", "block": [ "Both counters are folded back into 0-$7F before the threshold could pass $FF, so the unsigned", "compare above stays meaningful for arbitrarily long flights." ], "line": "still below $80 - no wrap needed" }, "7810": { "unit": "game/comcenScreens6F00", "line": "fold the threshold back into 0-$7F" }, "7812": { "unit": "game/comcenScreens6F00", "line": "keep it" }, "7813": { "unit": "game/comcenScreens6F00", "line": "frame counter" }, "7816": { "unit": "game/comcenScreens6F00", "line": "fold it back the same way" }, "7818": { "unit": "game/comcenScreens6F00", "line": "store it" }, "781B": { "unit": "game/comcenScreens6F00", "line": "threshold back into A" }, "781C": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "781D": { "unit": "game/comcenScreens6F00", "line": "the next move is due 6 frames later" }, "781F": { "unit": "game/comcenScreens6F00", "line": "patch the CMP # operand at $7807" }, "7822": { "unit": "game/comcenScreens6F00", "line": "put the line stepper state back the way the game frame found it" }, "7825": { "unit": "game/comcenScreens6F00", "line": "count this move (patches the LDA # operand at $782A)" }, "7828": { "unit": "game/comcenScreens6F00", "line": "at least one pixel step per move" }, "782A": { "unit": "game/comcenScreens6F00", "line": "operand patched at $77D9/$7825: the move number" }, "782C": { "unit": "game/comcenScreens6F00", "line": "never taken in practice - the flight ends long before 128 moves" }, "782E": { "unit": "game/comcenScreens6F00", "line": "move/2" }, "782F": { "unit": "game/comcenScreens6F00", "line": "move/4" }, "7830": { "unit": "game/comcenScreens6F00", "line": "as table index" }, "7831": { "unit": "game/comcenScreens6F00", "line": "n*n extra steps: the missile accelerates as it travels" }, "7834": { "unit": "game/comcenScreens6F00", "line": "advance one pixel along the line" }, "7837": { "unit": "game/comcenScreens6F00", "line": "returns 0 when the line is exhausted - the missile has arrived" }, "7839": { "unit": "game/comcenScreens6F00", "line": "one of the extra steps used" }, "783A": { "unit": "game/comcenScreens6F00", "line": "keep stepping while X >= 0" }, "783C": { "unit": "game/comcenScreens6F00", "line": "always taken: redraw the sprite at its new position" }, "783E": { "unit": "game/comcenScreens6F00", "block": [ "Common exit: switch the missile sprite off again." ], "line": "sprite enable shadow" }, "7841": { "unit": "game/comcenScreens6F00", "block": [ "$7841 is only a fall-through point in this overlay (the same address is printMapName in the", "overlay A variant)." ], "line": "clear bit 3 = sprite 3 off" }, "7843": { "unit": "game/comcenScreens6F00", "line": "store it back" }, "7846": { "unit": "game/comcenScreens6F00", "line": "done (also the early exit for a zero-length shot)" }, "7847": { "unit": "game/comcenScreens6F00", "routine": [ "plotMissileTrailSegment - Draws one 4-pixel line segment into the shape of sprite 3, from", "(scratch1B,scratch1A) to (lineEndRow,lineEndCol): the start pixel plus three stepped pixels.", "Called twice by animateMissileFlight, one pixel apart, to make the missile dash 2 pixels thick.", "In: scratch1A/scratch1B = segment start column/row, lineEndCol/lineEndRow = segment end.", "Out: 4 bits set in sprite 3's shape at $8AC0; the line stepper state is consumed.", "Called from: animateMissileFlight ($7784, $778B).", "(survey name sub_7847)" ], "line": "segment start column" }, "7849": { "unit": "game/comcenScreens6F00", "line": "into the line stepper" }, "784B": { "unit": "game/comcenScreens6F00", "line": "segment start row" }, "784D": { "unit": "game/comcenScreens6F00", "line": "into the line stepper" }, "784F": { "unit": "game/comcenScreens6F00", "line": "set the Bresenham walk up" }, "7852": { "unit": "game/comcenScreens6F00", "line": "plot pixel 1 (the start)" }, "7855": { "unit": "game/comcenScreens6F00", "line": "step and plot pixel 2" }, "7858": { "unit": "game/comcenScreens6F00", "line": "step and plot pixel 3" }, "785B": { "unit": "game/comcenScreens6F00", "routine": [ "stepAndPlotTrailPixel - Advances the line stepper one cell and sets the resulting pixel in sprite", "3's shape. plotMissileTrailSegment both calls it and falls through into it, so the segment ends", "up 4 pixels long.", "In: the active line stepper state ($8C-$97).", "Out: one bit set in $8AC0-$8AFF; returns through plotPathSpritePixel.", "Called from: plotMissileTrailSegment ($7855, $7858) and by falling through from it.", "(survey name sub_785B)" ], "line": "advance one pixel" }, "785E": { "unit": "game/comcenScreens6F00", "line": "set that pixel and return to the caller" }, "7861": { "unit": "game/comcenScreens6F00", "routine": [ "saveLineStepperState - Copies the 12-byte line stepper working set $8C-$97 (both error", "accumulators, the deltas, the step signs, the current and the end point) into savedLineState", "($92CA-$92D5) so that a game frame, which uses the same variables for unit movement, can run in", "the middle of the missile flight.", "In: $8C-$97.", "Out: $92CA-$92D5.", "Called from: animateMissileFlight ($77F6).", "(survey name sub_7861)" ], "line": "12 bytes: lineErrRow $8C .. lineStepCol $97" }, "7863": { "unit": "game/comcenScreens6F00", "line": "read one byte of the live stepper state (absolute addressing so Y indexes past $FF safely)" }, "7866": { "unit": "game/comcenScreens6F00", "line": "park it in the $92CA save area" }, "7869": { "unit": "game/comcenScreens6F00", "line": "next byte" }, "786A": { "unit": "game/comcenScreens6F00", "line": "loop over all 12" }, "786C": { "unit": "game/comcenScreens6F00", "line": "done" }, "786D": { "unit": "game/comcenScreens6F00", "routine": [ "restoreLineStepperState - Copies savedLineState ($92CA-$92D5) back over the 12-byte line stepper", "working set $8C-$97, undoing whatever the game frame did to it.", "In: $92CA-$92D5.", "Out: $8C-$97.", "Called from: animateMissileFlight ($7822), before every stepLine burst.", "(survey name sub_786D)" ], "line": "12 bytes" }, "786F": { "unit": "game/comcenScreens6F00", "line": "read the saved byte" }, "7872": { "unit": "game/comcenScreens6F00", "block": [ "$7872 is only a fall-through point here (the same address is randomTileVariant in the overlay A", "variant)." ], "line": "write it back into the live stepper state" }, "7875": { "unit": "game/comcenScreens6F00", "line": "next byte" }, "7876": { "unit": "game/comcenScreens6F00", "line": "loop over all 12" }, "7878": { "unit": "game/comcenScreens6F00", "line": "done" }, "7879": { "unit": "game/comcenScreens6F00", "routine": [ "rebasePairToMin - Subtracts the smaller of A and Y from both, i.e. shifts a pair of coordinates so", "that the smaller one becomes 0, and returns that offset. Used by animateMissileFlight to move the", "flight path into a small positive coordinate box before the x4 / x8 scaling can overflow a byte.", "In: A = first value, Y = second value.", "Out: X = min(A,Y), A = A - X, Y = Y - X (one of A and Y is always 0); scratch18 clobbered.", "Called from: animateMissileFlight ($7717, $7728, $776D, $7778).", "(survey name sub_7879)" ], "line": "keep the second value" }, "787B": { "unit": "game/comcenScreens6F00", "line": "which of the two is smaller?" }, "787D": { "unit": "game/comcenScreens6F00", "line": "branch if A >= Y" }, "787F": { "unit": "game/comcenScreens6F00", "line": "A is the smaller one - it becomes the offset" }, "7881": { "unit": "game/comcenScreens6F00", "line": "the larger value" }, "7882": { "unit": "game/comcenScreens6F00", "line": "set carry for the subtract" }, "7883": { "unit": "game/comcenScreens6F00", "line": "Y - A" }, "7885": { "unit": "game/comcenScreens6F00", "line": "back into Y" }, "7886": { "unit": "game/comcenScreens6F00", "line": "and A itself becomes 0" }, "7888": { "unit": "game/comcenScreens6F00", "line": "always taken" }, "788A": { "unit": "game/comcenScreens6F00", "line": "A - Y (carry is already set by the CMP at $787B)" }, "788C": { "unit": "game/comcenScreens6F00", "line": "and Y becomes 0" }, "788E": { "unit": "game/comcenScreens6F00", "line": "return the offset that was subtracted" }, "7890": { "unit": "game/comcenScreens6F00", "block": [ "$7890 carries a label only because the overlay A variant writes to this byte; here it is just the", "RTS of rebasePairToMin." ], "line": "done" }, "7891": { "unit": "game/comcenScreens6F00", "block": [ "Three state bytes of the radar sweep animation, all zero on disk. (The survey called the block", "radarSweepState; the three bytes are used independently, so they are named separately here.)" ], "line": "sweep step 0-30; steps 0-16 light grid row = step, steps 14-30 re-hide row = step-14" }, "7892": { "unit": "game/comcenScreens6F00", "line": "grid row paintRadarSweepRow is currently working on" }, "7893": { "unit": "game/comcenScreens6F00", "line": "$FF = this whole sweep stays dark (a damaged radar drops roughly every other sweep)" }, "7894": { "unit": "game/comcenScreens6F00", "routine": [ "updateRadarSweep - Per-frame driver of the radar sweep on screen 1. Every two frames it performs", "one sweep step: step s lights grid row s and re-hides grid row s-14, so a 14-row bright band wipes", "down the 21x17 radar window in 31 steps (about 62 frames); after the last step the sweep is", "restarted. The pacing uses the free-running frame counter $92FB against a threshold kept in the", "CMP operand at radarSweepThresholdCmp+1 ($7898).", "In: radarSweepTick ($92FB, bumped by the IRQ at $129F), radarSweepStep ($7891), the threshold byte", " at $7898.", "Out: radarSweepStep incremented or reset, radarSweepRow = the row painted, $7898 += 2, video matrix", " cells of one or two radar rows rewritten; A/X/Y clobbered.", "Called from: missileScreenResume ($75F7) and animateMissileFlight ($77F9).", "Self-modifying: the immediate operand of the CMP at $7897 is the next-step frame number; it is", " advanced here and cleared by restartRadarSweep." ], "line": "free-running frame counter, incremented once per frame by the IRQ" }, "7897": { "unit": "game/comcenScreens6F00", "line": "operand patched at $78A1/$78D6: the frame number of the next sweep step" }, "7899": { "unit": "game/comcenScreens6F00", "line": "not yet time for the next step" }, "789B": { "unit": "game/comcenScreens6F00", "line": "current threshold" }, "789E": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "789F": { "unit": "game/comcenScreens6F00", "line": "one sweep step every 2 frames" }, "78A1": { "unit": "game/comcenScreens6F00", "line": "patch the CMP # operand at $7897" }, "78A4": { "unit": "game/comcenScreens6F00", "line": "current step number" }, "78A7": { "unit": "game/comcenScreens6F00", "line": "31 steps make one full sweep" }, "78A9": { "unit": "game/comcenScreens6F00", "line": "sweep finished - start the next one (and return through it)" }, "78AB": { "unit": "game/comcenScreens6F00", "line": "the row to light is the step number itself" }, "78AE": { "unit": "game/comcenScreens6F00", "line": "the grid has 17 rows" }, "78B0": { "unit": "game/comcenScreens6F00", "line": "steps 17-30 are past the bottom, so there is no leading row left to light" }, "78B2": { "unit": "game/comcenScreens6F00", "line": "light grid row = step" }, "78B5": { "unit": "game/comcenScreens6F00", "line": "step number again" }, "78B8": { "unit": "game/comcenScreens6F00", "block": [ "Reached with carry already set from the BCS at $78B0; the SEC only matters on the fall-through." ], "line": "set carry for the subtract" }, "78B9": { "unit": "game/comcenScreens6F00", "line": "trailing edge of the band = step - 14 (a 14-row bright band)" }, "78BB": { "unit": "game/comcenScreens6F00", "line": "row to re-hide" }, "78BE": { "unit": "game/comcenScreens6F00", "line": "still negative - the band has not fully entered the window yet" }, "78C0": { "unit": "game/comcenScreens6F00", "line": "17 grid rows" }, "78C2": { "unit": "game/comcenScreens6F00", "line": "past the bottom row - nothing to re-hide" }, "78C4": { "unit": "game/comcenScreens6F00", "line": "darken that row again" }, "78C7": { "unit": "game/comcenScreens6F00", "line": "next step" }, "78CA": { "unit": "game/comcenScreens6F00", "line": "done" }, "78CB": { "unit": "game/comcenScreens6F00", "routine": [ "restartRadarSweep - Starts a new sweep at grid row 0 and immediately: clears the step counter, the", "dark flag, the frame counter and the step threshold. If the comcen's radar has been knocked out,", "a coin toss makes the entire coming sweep dark, so a damaged radar shows nothing on roughly half", "of its sweeps.", "In: comcenRadarStatus ($9243, non-zero once the radar system is destroyed), the game RNG $57-$59.", "Out: radarSweepStep = 0, radarSweepDarkFlag = 0 or $FF, radarSweepTick = 0, threshold at $7898 = 0;", " X clobbered.", "Called from: missileScreenResume ($75B3) and updateRadarSweep ($78A9, end of every sweep)." ], "line": "zero" }, "78CD": { "unit": "game/comcenScreens6F00", "line": "sweep starts at grid row 0" }, "78D0": { "unit": "game/comcenScreens6F00", "line": "assume a normal, visible sweep" }, "78D3": { "unit": "game/comcenScreens6F00", "line": "restart the frame counter" }, "78D6": { "unit": "game/comcenScreens6F00", "line": "threshold 0 = the first step happens on the next call" }, "78D9": { "unit": "game/comcenScreens6F00", "line": "non-zero once the comcen radar has been knocked out" }, "78DC": { "unit": "game/comcenScreens6F00", "line": "radar intact - always sweep normally" }, "78DE": { "unit": "game/comcenScreens6F00", "line": "damaged: draw a random byte" }, "78E1": { "unit": "game/comcenScreens6F00", "line": "about half the draws still give a normal sweep" }, "78E3": { "unit": "game/comcenScreens6F00", "line": "0 -> $FF: this whole sweep will show nothing" }, "78E6": { "unit": "game/comcenScreens6F00", "line": "done" }, "78E7": { "unit": "game/comcenScreens6F00", "routine": [ "paintRadarSweepRow - Rewrites the 21 video-matrix bytes of one radar grid row (screen row A+3,", "columns 3-23). In the multicolour bitmap the video matrix byte gives the colours of the %01 and", "%10 pixel pairs, so writing $F0 (light grey / black) makes the cell's map glyph visible and $00", "makes it black. A cell is lit when its colour RAM nibble is 1 - the colour drawRadarMap gives a", "spotted enemy unit - and the radar is intact, or while the bright sweep band is passing over it.", "In: A = grid row 0-16; radarSweepStep/radarSweepRow/radarSweepDarkFlag ($7891-$7893),", " comcenRadarStatus ($9243), colour RAM ($D800) of that row.", "Out: 21 bytes at $8C00 + (row+3)*40 + 3 set to $F0 or $00; blockPtr (zp_B8) = video matrix pointer,", " screenPtr (zp_B6) = colour RAM pointer; A/X/Y clobbered.", "Called from: updateRadarSweep ($78B2 leading edge, $78C4 trailing edge)." ], "line": "grid row 0-16 as table index" }, "78E8": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "78E9": { "unit": "game/comcenScreens6F00", "line": "$9382+3: low byte of the screen address of row+3" }, "78EC": { "unit": "game/comcenScreens6F00", "line": "+3 characters: the radar window starts at column 3" }, "78EE": { "unit": "game/comcenScreens6F00", "line": "low byte of the video matrix pointer" }, "78F0": { "unit": "game/comcenScreens6F00", "line": "and of the colour RAM pointer" }, "78F2": { "unit": "game/comcenScreens6F00", "line": "$939B+3: high byte of the screen address of row+3" }, "78F5": { "unit": "game/comcenScreens6F00", "line": "propagate the carry from the +3" }, "78F7": { "unit": "game/comcenScreens6F00", "line": "blockPtr now points into the video matrix at $8C00" }, "78F9": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "78FA": { "unit": "game/comcenScreens6F00", "line": "+$4C pages: $8C00 + $4C00 = colour RAM $D800" }, "78FC": { "unit": "game/comcenScreens6F00", "line": "screenPtr now points at the matching colour RAM cell" }, "78FE": { "unit": "game/comcenScreens6F00", "line": "start at the left edge of the row" }, "7900": { "unit": "game/comcenScreens6F00", "block": [ "One cell per pass; 21 cells make up the width of the radar window." ], "line": "colour RAM byte = the colour of this cell's %11 pixels" }, "7902": { "unit": "game/comcenScreens6F00", "line": "only the low nibble is real" }, "7904": { "unit": "game/comcenScreens6F00", "line": "colour 1 (white) is what drawRadarMap gives a spotted enemy unit" }, "7906": { "unit": "game/comcenScreens6F00", "line": "not an enemy blip - it only shows while the band passes" }, "7908": { "unit": "game/comcenScreens6F00", "line": "is the comcen radar still working?" }, "790B": { "unit": "game/comcenScreens6F00", "line": "yes - a spotted enemy stays lit permanently" }, "790D": { "unit": "game/comcenScreens6F00", "line": "default: both nibbles black, the cell is hidden" }, "790F": { "unit": "game/comcenScreens6F00", "line": "is this sweep a dead one?" }, "7912": { "unit": "game/comcenScreens6F00", "line": "yes - leave everything dark" }, "7914": { "unit": "game/comcenScreens6F00", "line": "the row being painted" }, "7917": { "unit": "game/comcenScreens6F00", "line": "is it the leading (bright) edge of the band?" }, "791A": { "unit": "game/comcenScreens6F00", "line": "no - this call is the trailing edge, so hide the row" }, "791C": { "unit": "game/comcenScreens6F00", "line": "$F0 = %01 pixels light grey, %10 pixels black: the cell shows" }, "791E": { "unit": "game/comcenScreens6F00", "line": "write the video matrix byte" }, "7920": { "unit": "game/comcenScreens6F00", "line": "next cell" }, "7921": { "unit": "game/comcenScreens6F00", "line": "21 cells across" }, "7923": { "unit": "game/comcenScreens6F00", "line": "loop" }, "7925": { "unit": "game/comcenScreens6F00", "line": "done" }, "7926": { "unit": "game/comcenScreens6F00", "routine": [ "redrawEnemyUnitsOnRadar - Repaints all 50 units of the opposing side on the radar window and gives", "them a fresh random dropout pattern, so the blips move and shimmer once per turn. Walks downwards", "from the enemy comcen index (comcenUnit EOR $52 turns 49 into 99 and 99 into 49) through 50 units,", "handing each one's map cell value and position to the main program's drawScreen1Cell.", "In: comcenUnit ($9236); unit arrays unitColTable ($F640), unitRowTable ($F6A4), unitTypeTable", " ($F76C) and unitTerrainUnderTable ($FAF0), all 100 bytes, $64 apart.", "Out: radarNoiseMasks ($9210-$921F) re-randomised, cells redrawn; scratch18 = unit index,", " scratch19 = countdown, savedRegA/X/Y ($54-$56) = cell value/column/row.", "Called from: refreshMissileScreen ($768D)." ], "line": "new random dropout mask set for the unit glyphs" }, "7929": { "unit": "game/comcenScreens6F00", "line": "49 for player 0, 99 for player 1" }, "792C": { "unit": "game/comcenScreens6F00", "line": "$31 EOR $52 = $63 and $63 EOR $52 = $31: the opposing side's comcen index" }, "792E": { "unit": "game/comcenScreens6F00", "line": "unit index, walked downwards from there" }, "7930": { "unit": "game/comcenScreens6F00", "line": "50 units per side" }, "7932": { "unit": "game/comcenScreens6F00", "line": "loop counter" }, "7934": { "unit": "game/comcenScreens6F00", "block": [ "One enemy unit per pass, from the enemy comcen down to that side's first unit." ], "line": "current unit index" }, "7936": { "unit": "game/comcenScreens6F00", "line": "unitColTable[unit] = map column" }, "7939": { "unit": "game/comcenScreens6F00", "line": "drawScreen1Cell takes the column in $55" }, "793B": { "unit": "game/comcenScreens6F00", "line": "unitRowTable[unit] = map row (the next 100-byte array, $64 further on)" }, "793E": { "unit": "game/comcenScreens6F00", "line": "drawScreen1Cell takes the row in $56" }, "7940": { "unit": "game/comcenScreens6F00", "line": "unit index" }, "7941": { "unit": "game/comcenScreens6F00", "line": "map cell code $80|index = 'this unit stands on this cell'" }, "7943": { "unit": "game/comcenScreens6F00", "line": "unitTypeTable[unit]; bit 7 set = the unit is dead" }, "7946": { "unit": "game/comcenScreens6F00", "line": "alive - draw the unit itself" }, "7948": { "unit": "game/comcenScreens6F00", "line": "dead: draw the terrain the corpse was standing on instead" }, "794B": { "unit": "game/comcenScreens6F00", "line": "$FF = the unit is not on the map at all, skip it" }, "794D": { "unit": "game/comcenScreens6F00", "line": "drawScreen1Cell takes the cell value in $54" }, "794F": { "unit": "game/comcenScreens6F00", "line": "plot that one cell of the radar window" }, "7952": { "unit": "game/comcenScreens6F00", "line": "previous unit index" }, "7954": { "unit": "game/comcenScreens6F00", "line": "one unit done" }, "7956": { "unit": "game/comcenScreens6F00", "line": "loop over all 50 units of the enemy side" }, "7958": { "unit": "game/comcenScreens6F00", "line": "done" }, "7959": { "unit": "game/comcenScreens6F00", "routine": [ "scrollRadarOnComcenMove - Entered by JMP from drawScreen1Cell ($5D8A) whenever the cell being", "redrawn is the local player's comcen. The radar window is always centred on the comcen, so if the", "comcen has moved the whole window has to scroll: the target cursor is shifted by the same amount", "(unless it is parked on the comcen cell itself, where it simply rides along), the map is redrawn", "and the sweep restarts at the top row.", "In: comcenUnit ($9236) and the unit position arrays, viewOriginCol/Row (zp_A5/zp_A6),", " gridCursorCol/Row (zp_67/zp_68).", "Out: nothing at all when the comcen has not moved; otherwise zp_A5/zp_A6 = the new origin (set by", " drawRadarMap), cursor and sprite 2 shifted, window redrawn, radarSweepStep = 0.", "Called from: drawScreen1Cell ($5D8A, JMP).", "Self-modifying: the new origin is written into the SBC # operands at subNewOriginCol+1 ($7970) and", " subNewOriginRow+1 ($7979) to compute the shift." ], "line": "X = comcenCol - 10, Y = comcenRow - 8: the map cell shown at the top-left of the window" }, "795C": { "unit": "game/comcenScreens6F00", "line": "same column as the window currently shows?" }, "795E": { "unit": "game/comcenScreens6F00", "line": "no - the comcen has moved sideways" }, "7960": { "unit": "game/comcenScreens6F00", "line": "same row?" }, "7962": { "unit": "game/comcenScreens6F00", "line": "origin unchanged - the comcen just repainted itself, nothing to do" }, "7964": { "unit": "game/comcenScreens6F00", "line": "is the target cursor sitting on the comcen cell (10,8)?" }, "7967": { "unit": "game/comcenScreens6F00", "line": "yes - leave it there so it rides along with the comcen" }, "7969": { "unit": "game/comcenScreens6F00", "line": "park the new origin column in the SBC # operand at $796F" }, "796C": { "unit": "game/comcenScreens6F00", "line": "old origin column" }, "796E": { "unit": "game/comcenScreens6F00", "line": "set carry for the subtract" }, "796F": { "unit": "game/comcenScreens6F00", "line": "operand patched at $7969: the new origin column" }, "7971": { "unit": "game/comcenScreens6F00", "line": "column shift = old - new" }, "7972": { "unit": "game/comcenScreens6F00", "line": "park the new origin row in the SBC # operand at $7978" }, "7975": { "unit": "game/comcenScreens6F00", "line": "old origin row" }, "7977": { "unit": "game/comcenScreens6F00", "line": "set carry for the subtract" }, "7978": { "unit": "game/comcenScreens6F00", "line": "operand patched at $7972: the new origin row" }, "797A": { "unit": "game/comcenScreens6F00", "line": "row shift = old - new" }, "797B": { "unit": "game/comcenScreens6F00", "line": "shift the cursor so it keeps pointing at the same map cell (refused if it would leave the grid)" }, "797E": { "unit": "game/comcenScreens6F00", "line": "redraw the whole 21x17 window and set zp_A5/zp_A6 to the new origin" }, "7981": { "unit": "game/comcenScreens6F00", "line": "zero" }, "7983": { "unit": "game/comcenScreens6F00", "line": "restart the sweep at the top row of the new picture" }, "7986": { "unit": "game/comcenScreens6F00", "line": "done" }, "7987": { "unit": "game/comcenScreens6F00", "routine": [ "isRadarCursorOnComcen - Tests whether the missile target cursor is on the comcen's own cell, which", "is always the centre cell (10,8) of the 21x17 radar grid.", "In: gridCursorCol (zp_67), gridCursorRow (zp_68).", "Out: Z = 1 when the cursor is on the comcen cell; A clobbered.", "Called from: scrollRadarOnComcenMove ($7964)." ], "line": "cursor column 0-20" }, "7989": { "unit": "game/comcenScreens6F00", "line": "the comcen is always drawn at grid column 10" }, "798B": { "unit": "game/comcenScreens6F00", "line": "different column - return with Z clear" }, "798D": { "unit": "game/comcenScreens6F00", "line": "cursor row 0-16" }, "798F": { "unit": "game/comcenScreens6F00", "line": "the comcen is always drawn at grid row 8; Z set = cursor is on it" }, "7991": { "unit": "game/comcenScreens6F00", "line": "done" }, "7992": { "unit": "game/comcenScreens6F00", "routine": [ "radarCursorToMapCoords - Converts the radar grid position of the target cursor into absolute map", "coordinates for an outgoing command, by adding the map coordinates of the window's top-left cell.", "In: gridCursorCol/Row (zp_67/zp_68), viewOriginCol/Row (zp_A5/zp_A6).", "Out: cmdParam1 (zp_74) = map column, cmdParam2 (zp_75) = map row; A clobbered.", "Called from: missileScreenResume ($7662, before queueing the $90 strike) and checkStrikeHitsDrone", " ($79AB)." ], "line": "cursor column inside the grid" }, "7994": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "7995": { "unit": "game/comcenScreens6F00", "line": "map column of the window's left edge" }, "7997": { "unit": "game/comcenScreens6F00", "line": "command argument 1 = map column of the target cell" }, "7999": { "unit": "game/comcenScreens6F00", "line": "cursor row inside the grid" }, "799B": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "799C": { "unit": "game/comcenScreens6F00", "line": "map row of the window's top edge" }, "799E": { "unit": "game/comcenScreens6F00", "line": "command argument 2 = map row of the target cell" }, "79A0": { "unit": "game/comcenScreens6F00", "line": "done" }, "79A1": { "unit": "game/comcenScreens6F00", "routine": [ "checkStrikeHitsDrone - Run straight after a missile has been launched: if a drone is airborne and", "has not been dealt with yet, works out whether the targeted map cell is one of the four cells the", "drone occupies (the drone covers a 2x2 block whose top-left corner is its own cell), and if so", "queues the 1-byte command $92 that tells both machines the drone is finished.", "In: droneControlFlags ($920D), droneArrivalFlags ($92F9), droneViewOriginCol/Row ($92F6/$92F7 =", " the drone's map cell), the cursor and view origin used by radarCursorToMapCoords.", "Out: cmdParam1/2 = the target cell; on a hit, command $92 queued and droneArrivalFlags bit 0 set", " (which also blocks screen switching until the command has gone out).", "Called from: missileScreenResume ($767E)." ], "line": "drone flags: 0 = no drone in the air" }, "79A4": { "unit": "game/comcenScreens6F00", "line": "no drone - nothing to hit" }, "79A6": { "unit": "game/comcenScreens6F00", "line": "non-zero once the drone has been hit, detonated or has landed" }, "79A9": { "unit": "game/comcenScreens6F00", "line": "the drone is already out of the game" }, "79AB": { "unit": "game/comcenScreens6F00", "line": "cmdParam1/2 = map cell the missile was aimed at" }, "79AE": { "unit": "game/comcenScreens6F00", "line": "the drone's own map column" }, "79B1": { "unit": "game/comcenScreens6F00", "line": "set carry for the subtract" }, "79B2": { "unit": "game/comcenScreens6F00", "line": "difference to the targeted column" }, "79B4": { "unit": "game/comcenScreens6F00", "line": "compare with -1" }, "79B6": { "unit": "game/comcenScreens6F00", "line": "difference is -1: the target is the drone's right-hand cell, still a hit" }, "79B8": { "unit": "game/comcenScreens6F00", "line": "compare with +1" }, "79BA": { "unit": "game/comcenScreens6F00", "line": "difference is not 0 or -1 - the shot missed" }, "79BC": { "unit": "game/comcenScreens6F00", "line": "the drone's own map row" }, "79BF": { "unit": "game/comcenScreens6F00", "line": "set carry for the subtract" }, "79C0": { "unit": "game/comcenScreens6F00", "line": "difference to the targeted row" }, "79C2": { "unit": "game/comcenScreens6F00", "line": "compare with -1" }, "79C4": { "unit": "game/comcenScreens6F00", "line": "difference is -1: the drone's lower cell, still a hit" }, "79C6": { "unit": "game/comcenScreens6F00", "line": "compare with +1" }, "79C8": { "unit": "game/comcenScreens6F00", "line": "difference is not 0 or -1 - the shot missed" }, "79CA": { "unit": "game/comcenScreens6F00", "line": "command $92 = the drone is destroyed / its flight is over" }, "79CC": { "unit": "game/comcenScreens6F00", "line": "queue the single command byte for this turn's packet" }, "79CF": { "unit": "game/comcenScreens6F00", "line": "drone flags" }, "79D2": { "unit": "game/comcenScreens6F00", "line": "bit 0 = our own $92 is queued and not yet acknowledged" }, "79D4": { "unit": "game/comcenScreens6F00", "line": "store it back" }, "79D7": { "unit": "game/comcenScreens6F00", "line": "done" }, "79D8": { "unit": "game/comcenScreens6F00", "routine": [ "showDroneKillBurst - Plays the 'drone shot down' effect on the radar screen once our $92 command", "has actually been executed by the engine (cmdDroneArrived sets droneArrivalFlags bit 4 for an own", "command). Bit 4 is moved to bit 5 so the burst is played only once. Suppressed during game film", "playback.", "In: filmPlaybackMode ($0B9B bit 7 = a film is being replayed), droneArrivalFlags ($92F9),", " droneViewOriginCol/Row ($92F6/$92F7) = the drone's map cell.", "Out: droneArrivalFlags bit 4 -> bit 5, droneControlFlags = 0, sounds $17 and $09, sprite 1", " positioned and animated as an 11-step expanding ring.", "Called from: missileScreenResume ($75F4), whenever an exchange has just been applied." ], "line": "bit 7 = a recorded game film is being played back" }, "79DB": { "unit": "game/comcenScreens6F00", "line": "during playback the effect is skipped" }, "79DD": { "unit": "game/comcenScreens6F00", "line": "drone flags" }, "79E0": { "unit": "game/comcenScreens6F00", "line": "bit 4 = cmdDroneArrived executed our own $92" }, "79E2": { "unit": "game/comcenScreens6F00", "line": "not our kill - nothing to show" }, "79E4": { "unit": "game/comcenScreens6F00", "line": "drone flags again" }, "79E7": { "unit": "game/comcenScreens6F00", "line": "clear bit 4" }, "79E9": { "unit": "game/comcenScreens6F00", "line": "set bit 5 instead, so the burst plays exactly once" }, "79EB": { "unit": "game/comcenScreens6F00", "line": "store it back" }, "79EE": { "unit": "game/comcenScreens6F00", "line": "zero" }, "79F0": { "unit": "game/comcenScreens6F00", "line": "there is no drone under control any more" }, "79F3": { "unit": "game/comcenScreens6F00", "line": "sound $17 = explosion" }, "79F5": { "unit": "game/comcenScreens6F00", "line": "play it" }, "79F8": { "unit": "game/comcenScreens6F00", "line": "sound $09 = debris" }, "79FA": { "unit": "game/comcenScreens6F00", "line": "play it" }, "79FD": { "unit": "game/comcenScreens6F00", "line": "the drone's map column" }, "7A00": { "unit": "game/comcenScreens6F00", "line": "and map row" }, "7A03": { "unit": "game/comcenScreens6F00", "line": "-> sprite coordinates of that cell's centre (C = 1 if off the window; not tested here)" }, "7A06": { "unit": "game/comcenScreens6F00", "line": "ring burst sprite 1 X" }, "7A09": { "unit": "game/comcenScreens6F00", "line": "ring burst sprite 1 Y" }, "7A0C": { "unit": "game/comcenScreens6F00", "line": "11 radius steps = a big burst" }, "7A0E": { "unit": "game/comcenScreens6F00", "line": "play the expanding/contracting ring" }, "7A11": { "unit": "game/comcenScreens6F00", "line": "done" }, "7A12": { "unit": "game/comcenScreens6F00", "block": [ "Single unreferenced $00 byte between the two routines (alignment filler)." ], "line": "filler" }, "7A13": { "unit": "game/comcenScreens6F00", "routine": [ "mapCellToRadarGrid - Converts absolute map coordinates into radar grid coordinates relative to the", "local comcen, which is always drawn at the centre cell (10,8) of the 21x17 window, and falls into", "the range test.", "In: X = map column, Y = map row, comcenUnit ($9236), unitColTable/unitRowTable.", "Out: X = grid column, Y = grid row, C = 0 when the cell is inside the window, C = 1 outside.", "Called from: the main program's getUnitSpriteCentreOnGrid ($340B) and the opponent modules", " (trainer $EE7C, drone AI $EEE1) when they place sprites on screen 1." ], "line": "map row" }, "7A14": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "7A15": { "unit": "game/comcenScreens6F00", "line": "the comcen is drawn at grid row 8" }, "7A17": { "unit": "game/comcenScreens6F00", "line": "set carry for the subtract" }, "7A18": { "unit": "game/comcenScreens6F00", "line": "the local comcen's unit index (49 or 99)" }, "7A1B": { "unit": "game/comcenScreens6F00", "line": "minus the comcen's map row" }, "7A1E": { "unit": "game/comcenScreens6F00", "line": "Y = grid row" }, "7A1F": { "unit": "game/comcenScreens6F00", "line": "map column" }, "7A20": { "unit": "game/comcenScreens6F00", "line": "clear carry before the add" }, "7A21": { "unit": "game/comcenScreens6F00", "line": "the comcen is drawn at grid column 10" }, "7A23": { "unit": "game/comcenScreens6F00", "line": "set carry for the subtract" }, "7A24": { "unit": "game/comcenScreens6F00", "line": "the local comcen's unit index again" }, "7A27": { "unit": "game/comcenScreens6F00", "line": "minus the comcen's map column" }, "7A2A": { "unit": "game/comcenScreens6F00", "line": "X = grid column; fall into the range test" }, "7A2B": { "unit": "game/comcenScreens6F00", "routine": [ "isRadarGridCellInRange - Range check for a radar grid cell: the window is 21 columns by 17 rows,", "and the comparisons are unsigned so a negative coordinate (which wraps to >= $80) counts as", "outside.", "In: X = grid column, Y = grid row.", "Out: C = 0 when Y < 17 and X < 21, C = 1 otherwise; registers untouched.", "Called from: mapCellToRadarGrid (fall-through), offsetRadarCursor ($76FC), animateMissileFlight", " ($77B9, result unused) and the main program at $33A0." ], "line": "17 grid rows" }, "7A2D": { "unit": "game/comcenScreens6F00", "line": "off the bottom, or negative (which wraps above $80)" }, "7A2F": { "unit": "game/comcenScreens6F00", "line": "21 grid columns; C = 0 only if the column is inside too" }, "7A31": { "unit": "game/comcenScreens6F00", "line": "done" }, "7A32": { "unit": "game/comcenScreens6F00", "routine": [ "initRadarScreen - Builds the whole RADAR / MISSILE screen (game screen 1): resets the glyph", "plotter's parameter block, sets border and background to grey, then draws the MISSILES panel and", "its count, the FIRE button, the DRONE ALERT panel, the framed 21x17 radar map and the bottom", "message frame, and finally reprints whatever message the status line was showing.", "In: radarMissilesLeft ($9232), comcen position, the $F000 map.", "Out: whole screen drawn; vicBorderShadow/vicBg0Shadow ($9003/$9004) = $0C, cellBackgroundColour", " ($9226) = 0, screenBuiltFlag (zp_41) = 1, textDrawParams block ($2A29-$2A2C) cleared.", "Called from: runMissileScreen ($7599)." ], "line": "zero" }, "7A34": { "unit": "game/comcenScreens6F00", "line": "useFixedColourFlag = 0: glyphs take their colour from fontColorTable" }, "7A37": { "unit": "game/comcenScreens6F00", "line": "fixed draw colour byte = 0" }, "7A3A": { "unit": "game/comcenScreens6F00", "line": "block fill flag = 0" }, "7A3D": { "unit": "game/comcenScreens6F00", "line": "block fill character = 0" }, "7A40": { "unit": "game/comcenScreens6F00", "line": "colour $0C = medium grey" }, "7A42": { "unit": "game/comcenScreens6F00", "line": "border colour shadow (the IRQ copies it to $D020)" }, "7A45": { "unit": "game/comcenScreens6F00", "line": "background colour shadow ($D021)" }, "7A48": { "unit": "game/comcenScreens6F00", "line": "zero" }, "7A4A": { "unit": "game/comcenScreens6F00", "line": "screen 1 writes glyph colours straight to colour RAM, so no background nibble is ORed in" }, "7A4D": { "unit": "game/comcenScreens6F00", "line": "1" }, "7A4F": { "unit": "game/comcenScreens6F00", "line": "every screen builder sets this flag; nothing reads it" }, "7A51": { "unit": "game/comcenScreens6F00", "line": "zero" }, "7A53": { "unit": "game/comcenScreens6F00", "line": "redundant - D_2A2B was already cleared at $7A3A" }, "7A56": { "unit": "game/comcenScreens6F00", "line": "MISSILES panel, bottom right" }, "7A59": { "unit": "game/comcenScreens6F00", "line": "the two-digit count inside it" }, "7A5C": { "unit": "game/comcenScreens6F00", "line": "FIRE button panel" }, "7A5F": { "unit": "game/comcenScreens6F00", "line": "DRONE ALERT panel, top right" }, "7A62": { "unit": "game/comcenScreens6F00", "line": "the two frames plus the 21x17 radar map itself" }, "7A65": { "unit": "game/comcenScreens6F00", "line": "the bottom status/message frame shared by all screens" }, "7A68": { "unit": "game/comcenScreens6F00", "line": "reprint the current status-line message and return through it" }, "7A6B": { "unit": "game/comcenScreens6F00", "routine": [ "redrawRadarMapWindow - Draws the double frame around the radar window and then the map inside it.", "In: the comcen position and the $F000 map (used by drawRadarMap).", "Out: window 5 (25x21 at column 1, row 1) and window 6 (23x19 at column 2, row 2) drawn, then the", " 21x17 cell map.", "Called from: initRadarScreen ($7A62) and clearCurrentScreenWindows ($2DFA, JMP) while screen 1 is", " the active screen." ], "line": "window 5 = the outer frame, 25x21 cells at (column 1, row 1)" }, "7A6D": { "unit": "game/comcenScreens6F00", "line": "draw it" }, "7A70": { "unit": "game/comcenScreens6F00", "line": "window 6 = the inner frame, 23x19 cells at (column 2, row 2)" }, "7A72": { "unit": "game/comcenScreens6F00", "line": "draw it" }, "7A75": { "unit": "game/comcenScreens6F00", "line": "fill it with the 21x17 cell radar map and return through it" }, "7A78": { "unit": "game/comcenScreens6F00", "routine": [ "drawMissilePanel - Draws the MISSILES panel in the bottom right corner: an outer frame with inner", "corner marks, a small inner frame around the two-digit counter, the coloured background of the", "counter box and the caption 'MISSILES'.", "In: nothing.", "Out: window 7 (12x7 at (27,15)) and window 8 (4x4 at (31,16)) drawn, the 2x2 counter box at row 17", " column 32 filled with $30, the caption printed at column 29 row 19; textColour (zp_3D), the", " text cursor and the draw modes are left reset.", "Called from: initRadarScreen ($7A56)." ], "line": "text window = the whole screen, so the coordinates below are absolute" }, "7A7B": { "unit": "game/comcenScreens6F00", "line": "window 7 = 12x7 cells at column 27, row 15" }, "7A7D": { "unit": "game/comcenScreens6F00", "line": "draw the frame" }, "7A80": { "unit": "game/comcenScreens6F00", "line": "put the four inner corner marks (character $38) inside it" }, "7A83": { "unit": "game/comcenScreens6F00", "line": "window 8 = 4x4 cells at column 31, row 16" }, "7A85": { "unit": "game/comcenScreens6F00", "line": "draw the inner frame around the counter" }, "7A88": { "unit": "game/comcenScreens6F00", "line": "character row 17" }, "7A8A": { "unit": "game/comcenScreens6F00", "line": "row of the counter box" }, "7A8D": { "unit": "game/comcenScreens6F00", "line": "character column 32" }, "7A8F": { "unit": "game/comcenScreens6F00", "line": "column of the counter box" }, "7A92": { "unit": "game/comcenScreens6F00", "line": "$30 = %01 pixels cyan, %10 pixels black" }, "7A94": { "unit": "game/comcenScreens6F00", "line": "2 cells wide" }, "7A96": { "unit": "game/comcenScreens6F00", "line": "2 cells high" }, "7A98": { "unit": "game/comcenScreens6F00", "line": "paint the counter box background" }, "7A9B": { "unit": "game/comcenScreens6F00", "line": "colour byte for the caption glyphs" }, "7A9D": { "unit": "game/comcenScreens6F00", "line": "text colour" }, "7A9F": { "unit": "game/comcenScreens6F00", "line": "bitmap draw mode 1" }, "7AA1": { "unit": "game/comcenScreens6F00", "line": "OR the glyphs into the bitmap so the frame keeps showing through" }, "7AA4": { "unit": "game/comcenScreens6F00", "line": "column 29" }, "7AA6": { "unit": "game/comcenScreens6F00", "line": "text cursor column" }, "7AA8": { "unit": "game/comcenScreens6F00", "line": "row 19" }, "7AAA": { "unit": "game/comcenScreens6F00", "line": "text cursor row" }, "7AAC": { "unit": "game/comcenScreens6F00", "line": "recompute the bitmap/screen pointers from the cursor" }, "7AAF": { "unit": "game/comcenScreens6F00", "line": "low byte of the string address" }, "7AB1": { "unit": "game/comcenScreens6F00", "line": "$CC9E = 'MISSILES' in the $C000 text overlay" }, "7AB3": { "unit": "game/comcenScreens6F00", "line": "print it" }, "7AB6": { "unit": "game/comcenScreens6F00", "line": "back to replace mode and return through it" }, "7AB9": { "unit": "game/comcenScreens6F00", "block": [ "Colour byte the missile counter box is currently painted with." ], "line": "$30 (cyan digits on black) or $03 (black digits on cyan) while it flashes" }, "7ABA": { "unit": "game/comcenScreens6F00", "routine": [ "drawMissileCount - Shows the number of missiles left as two tall (two-character-high) digits in", "the counter box at row 17, columns 32 and 33, in the fixed colour $30.", "In: radarMissilesLeft ($9232).", "Out: the two digits drawn, missileCountColour ($7AB9) = $30, the glyph plotter's fixed-colour mode", " switched on and off again, scratch18/scratch19 = the row/column drawTallGlyph used.", "Called from: initRadarScreen ($7A59), the fire handler after a launch ($7647) and", " refreshMissileScreen ($7695)." ], "line": "missiles left" }, "7ABD": { "unit": "game/comcenScreens6F00", "line": "split into $C44E hundreds, $C44F tens, $C450 ones" }, "7AC0": { "unit": "game/comcenScreens6F00", "line": "character row 17" }, "7AC2": { "unit": "game/comcenScreens6F00", "line": "drawTallGlyph's row" }, "7AC4": { "unit": "game/comcenScreens6F00", "line": "character column 32" }, "7AC6": { "unit": "game/comcenScreens6F00", "line": "drawTallGlyph's column" }, "7AC8": { "unit": "game/comcenScreens6F00", "line": "non-zero" }, "7ACA": { "unit": "game/comcenScreens6F00", "line": "useFixedColourFlag: take the colour from $2A2A, not from the font colour table" }, "7ACD": { "unit": "game/comcenScreens6F00", "line": "$30 = %01 pixels cyan, %10 pixels black" }, "7ACF": { "unit": "game/comcenScreens6F00", "line": "the fixed draw colour" }, "7AD2": { "unit": "game/comcenScreens6F00", "line": "remember it so blinkMissileCount can toggle it" }, "7AD5": { "unit": "game/comcenScreens6F00", "line": "tens digit" }, "7AD8": { "unit": "game/comcenScreens6F00", "line": "draw it as a two-character-high glyph pair" }, "7ADB": { "unit": "game/comcenScreens6F00", "line": "next column (33)" }, "7ADD": { "unit": "game/comcenScreens6F00", "line": "ones digit" }, "7AE0": { "unit": "game/comcenScreens6F00", "line": "draw it" }, "7AE3": { "unit": "game/comcenScreens6F00", "line": "zero" }, "7AE5": { "unit": "game/comcenScreens6F00", "line": "back to per-character colours" }, "7AE8": { "unit": "game/comcenScreens6F00", "line": "and clear the fixed colour byte" }, "7AEB": { "unit": "game/comcenScreens6F00", "line": "done" }, "7AEC": { "unit": "game/comcenScreens6F00", "routine": [ "blinkMissileCount - Swaps the two colour nibbles of the missile counter box, which exchanges the", "digit and background colours and makes the counter flash.", "In: missileCountColour ($7AB9).", "Out: $7AB9 toggled between $30 and $03, the 2x2 video matrix cells at row 17 column 32 rewritten.", "Called from: missileScreenResume ($75E4), every 9 frames while fewer than two missiles are left." ], "line": "current box colour" }, "7AEF": { "unit": "game/comcenScreens6F00", "line": "EOR $33 swaps the two nibbles' colours: $30 <-> $03" }, "7AF1": { "unit": "game/comcenScreens6F00", "line": "keep the new one" }, "7AF4": { "unit": "game/comcenScreens6F00", "line": "character row 17" }, "7AF6": { "unit": "game/comcenScreens6F00", "line": "row of the counter box" }, "7AF9": { "unit": "game/comcenScreens6F00", "line": "character column 32" }, "7AFB": { "unit": "game/comcenScreens6F00", "line": "column of the counter box" }, "7AFE": { "unit": "game/comcenScreens6F00", "line": "2 cells wide" }, "7B00": { "unit": "game/comcenScreens6F00", "line": "2 cells high" }, "7B02": { "unit": "game/comcenScreens6F00", "line": "repaint them (the draw mode is 'replace' here) and return through it" }, "7B05": { "unit": "game/comcenScreens6F00", "routine": [ "drawFireButton - Draws the FIRE button panel on the right: a framed box with inner corner marks,", "the 2x2 character button icon from $9804 in green, and the caption 'FIRE' underneath.", "In: nothing.", "Out: window 9 (8x7 at (29,8)) drawn, the icon at row 10 column 32 painted with colour byte $05,", " the caption at column 31 row 12; the text window and the draw modes are left reset.", "Called from: initRadarScreen ($7A5C). The main loop makes the button pulse by EORing 8 into the", " same four cells every 9 frames ($75DA)." ], "line": "window 9 = 8x7 cells at column 29, row 8" }, "7B07": { "unit": "game/comcenScreens6F00", "line": "draw the frame" }, "7B0A": { "unit": "game/comcenScreens6F00", "line": "add the four inner corner marks" }, "7B0D": { "unit": "game/comcenScreens6F00", "line": "character row 10" }, "7B0F": { "unit": "game/comcenScreens6F00", "line": "row of the button icon" }, "7B12": { "unit": "game/comcenScreens6F00", "line": "character column 32" }, "7B14": { "unit": "game/comcenScreens6F00", "line": "column of the button icon" }, "7B17": { "unit": "game/comcenScreens6F00", "line": "low byte of the block address" }, "7B19": { "unit": "game/comcenScreens6F00", "line": "$9804 = a 2x2 character block (width, height, then glyphs $45-$48)" }, "7B1B": { "unit": "game/comcenScreens6F00", "line": "stamp the icon into the bitmap" }, "7B1E": { "unit": "game/comcenScreens6F00", "line": "$05 = %01 pixels black, %10 pixels green" }, "7B20": { "unit": "game/comcenScreens6F00", "line": "2 cells wide" }, "7B22": { "unit": "game/comcenScreens6F00", "line": "2 cells high" }, "7B24": { "unit": "game/comcenScreens6F00", "line": "paint the icon's colour cells" }, "7B27": { "unit": "game/comcenScreens6F00", "line": "colour byte for the caption glyphs" }, "7B29": { "unit": "game/comcenScreens6F00", "line": "text colour" }, "7B2B": { "unit": "game/comcenScreens6F00", "line": "text window = the whole screen again" }, "7B2E": { "unit": "game/comcenScreens6F00", "line": "column 31" }, "7B30": { "unit": "game/comcenScreens6F00", "line": "text cursor column" }, "7B32": { "unit": "game/comcenScreens6F00", "line": "row 12" }, "7B34": { "unit": "game/comcenScreens6F00", "line": "text cursor row" }, "7B36": { "unit": "game/comcenScreens6F00", "line": "recompute the bitmap/screen pointers" }, "7B39": { "unit": "game/comcenScreens6F00", "line": "low byte of the string address" }, "7B3B": { "unit": "game/comcenScreens6F00", "line": "$869D = 'FIRE'" }, "7B3D": { "unit": "game/comcenScreens6F00", "line": "print it" }, "7B40": { "unit": "game/comcenScreens6F00", "line": "back to replace mode and return through it" }, "7B43": { "unit": "game/comcenScreens6F00", "block": [ "Flash colour of the DRONE ALERT panel." ], "line": "$02 (red text area) or $20 (inverted), toggled by flashDroneAlertPanel" }, "7B44": { "unit": "game/comcenScreens6F00", "routine": [ "drawDroneAlertPanel - Draws the three nested frames of the DRONE ALERT panel in the top right", "corner and falls into resetDroneAlertPanel, which prints the text and leaves the panel dark.", "In: nothing.", "Out: windows $0A (11x8 at (28,0)), $0B (9x6 at (29,1)) and $0C (7x4 at (30,2)) drawn, then the", " text and the inactive colour.", "Called from: initRadarScreen ($7A5F)." ], "line": "text window = the whole screen" }, "7B47": { "unit": "game/comcenScreens6F00", "line": "window $0A = the outer frame, 11x8 cells at column 28, row 0" }, "7B49": { "unit": "game/comcenScreens6F00", "line": "draw it" }, "7B4C": { "unit": "game/comcenScreens6F00", "line": "add the four inner corner marks" }, "7B4F": { "unit": "game/comcenScreens6F00", "line": "window $0B = the middle frame, 9x6 cells at column 29, row 1" }, "7B51": { "unit": "game/comcenScreens6F00", "line": "draw it" }, "7B54": { "unit": "game/comcenScreens6F00", "line": "window $0C = the inner frame, 7x4 cells at column 30, row 2" }, "7B56": { "unit": "game/comcenScreens6F00", "line": "draw it and fall into the text/colour part" }, "7B59": { "unit": "game/comcenScreens6F00", "routine": [ "resetDroneAlertPanel - Prints the two-line 'DRONE' / 'ALERT' label into the panel and paints its", "interior in the dark, inactive colour, also resetting the flash state so the next alert starts on", "the red phase.", "In: nothing.", "Out: the label printed at column 31, row 3; droneAlertColour ($7B43) = $02; textWindowLeft (zp_3B)", " = 31, textColour (zp_3D) = $0B, the 7x4 interior filled with $0B via fillDroneAlertPanel.", "Called from: drawDroneAlertPanel (fall-through) and refreshMissileScreen ($76AC, JMP) whenever no", " drone is airborne." ], "line": "colour draw mode 0" }, "7B5B": { "unit": "game/comcenScreens6F00", "line": "replace the colour cells instead of ORing/EORing into them" }, "7B5E": { "unit": "game/comcenScreens6F00", "line": "colour byte $0B for the label glyphs" }, "7B60": { "unit": "game/comcenScreens6F00", "line": "text colour" }, "7B62": { "unit": "game/comcenScreens6F00", "line": "$02 = the first flash phase" }, "7B64": { "unit": "game/comcenScreens6F00", "line": "reset the flash state so the next alert starts red" }, "7B67": { "unit": "game/comcenScreens6F00", "line": "column 31" }, "7B69": { "unit": "game/comcenScreens6F00", "line": "make it the left edge of the text window, so the label wraps inside the panel" }, "7B6B": { "unit": "game/comcenScreens6F00", "line": "column 0 relative to that" }, "7B6D": { "unit": "game/comcenScreens6F00", "line": "text cursor column" }, "7B6F": { "unit": "game/comcenScreens6F00", "line": "row 3" }, "7B71": { "unit": "game/comcenScreens6F00", "line": "text cursor row" }, "7B73": { "unit": "game/comcenScreens6F00", "line": "recompute the bitmap/screen pointers" }, "7B76": { "unit": "game/comcenScreens6F00", "line": "low byte of the string address" }, "7B78": { "unit": "game/comcenScreens6F00", "line": "$86A1 = 'DRONE' + CR + 'ALERT'" }, "7B7A": { "unit": "game/comcenScreens6F00", "line": "print both lines" }, "7B7D": { "unit": "game/comcenScreens6F00", "line": "11 frames" }, "7B7F": { "unit": "game/comcenScreens6F00", "line": "delays the keyboard auto-repeat countdown; nothing here depends on it (leftover)" }, "7B81": { "unit": "game/comcenScreens6F00", "line": "$0B = %01 pixels black, %10 pixels dark grey: the panel is dark/inactive" }, "7B83": { "unit": "game/comcenScreens6F00", "line": "fill the 7x4 interior with it and return through it" } }, "dataTypes": { "7551": [ "byte", 1 ], "7585": [ "byte", 1 ], "7586": [ "byte", 1 ], "7891": [ "byte", 1 ], "7892": [ "byte", 1 ], "7893": [ "byte", 1 ], "7A12": [ "byte", 1 ], "7AB9": [ "byte", 1 ], "7B43": [ "byte", 1 ] }, "zpComments": { "6C": "first of the six IRQ frame countdowns $6C-$71: joystick auto-repeat delay; the radar cursor code stretches it by 1.5x for diagonal steps ($76E4)", "B8": "pointer (lo) to the character-block/frame-tile data being drawn; paintRadarSweepRow reuses it as the video-matrix ($8C00) row pointer while screenPtr addresses the matching colour RAM" } }