{
 "chunk": "game_ovl_6F00_B_6F00",
 "unit": "game/ovl_6F00_B",
 "routines": [
  {"addr":"6F00","name":"drawDroneHeadingPanel","summary":"Draws the top-left panel of the drone screen: window $0D (left 1, top 3, 9x9), the 7x7 character picture at $97D1 at row 4 column 2, the 5x5 picture at $97B6 at row 5 column 3 and a 3x3 colour fill of $6E at row 6 column 4. Sprites 3 and 5 (heading line and direction arrow, positioned at X=$37/$38 Y=$64 by the screen-2 entry) sit on top of this panel, so it is the drone heading / compass indicator.","inputs":"none (colour state D_2A29-D_2A2C as left by buildDroneScreen)","outputs":"frame, characters and colour cells drawn; D_2A2D=6, D_2A2E=4, D_9221/D_9222 set by the block drawer","confidence":"medium"},
  {"addr":"6F36","name":"drawDroneFuelPanel","summary":"Draws the bottom-left panel of the drone screen: window $0E (left 1, top 12, 9x9), the 7x7 picture at $97D1 at row 13 column 2 in the fixed colour $BC, then the three label characters $57,$58,$59 at row 17 columns 4-6 in colour $0C (the caption of the fuel read-out that drawDroneFuelDigits later prints at row 15).","inputs":"none","outputs":"frame/characters drawn; D_2A29 and D_2A2A cleared on exit; D_2A2B = 0; zp_18=$11, zp_19=6","confidence":"medium"},
  {"addr":"6F89","name":"drawDroneRadarFrame","summary":"Builds the radar-noise masks (buildRadarNoiseMasks), draws the two nested frames around the radar area (windows $0F: left 11, top 4, 18x16 and $10: left 12, top 5, 16x14) and falls into clearRadarWindowBlack, which blanks the 14x12 radar area and centres the view on the own comcen. Called once by buildDroneScreen. Note: sub_6F91 ($6F91) is only a fall-through point in variant B; the JSRs to it in XREF come from overlay A.","inputs":"D_9236 comcenUnit","outputs":"frames drawn, D_9210 masks, radar area cleared, zp_A5/zp_A6 = comcen - (6,5), D_920F = 0, D_7134 = 0","confidence":"high"},
  {"addr":"6F99","name":"drawDroneFuelDigits","summary":"Shows droneFuel (D_9220) as three tall two-character digits at row 15 columns 4-6: converts it to decimal with sub_C428 ($C44E hundreds, $C44F tens, $C450 ones) and draws them with drawTallGlyph in a fixed colour chosen by the level: $30 for >= 21, $B7 for 11-20, $21 below 11; for even values the two colour nibbles are swapped so a low read-out pulses as it counts down. Called at screen build, at launch (fuel 100), every radar update and when the drone is lost (fuel 0).","inputs":"D_9220 droneFuel","outputs":"three glyph pairs at rows 15-16; D_C44E-D_C450; D_2A29/D_2A2A cleared; zp_18=$0F, zp_19=6, zp_1A scratch","confidence":"high"},
  {"addr":"6FF5","name":"drawDroneCountPanel","summary":"Draws the top-right panel of the drone screen: full-screen text window (sub_C0D7), window $11 (left 29, top 3, 11x7) with inner corner marks, window $12 (left 32, top 4, 5x4), a 3x2 colour fill of $30 at row 5 column 33 (the box for the drones-left digit) and the caption 'DRONE' ($868C) at row 7 column 32 in text colour $CF with OR draw mode, reset afterwards.","inputs":"none","outputs":"frames/text drawn; zp_3D=$CF, zp_3F=$20, zp_40=7; draw modes reset","confidence":"high"},
  {"addr":"7036","name":"drawDroneCountDigit","summary":"Draws dronesLeftDisplay (D_9233) as one tall digit at row 5 column 34 in fixed colour $30, or in colour 0 (invisible) when no drones are left. The 'bne L_7055' at $7053 is a no-op branch (both paths continue at $7055). Called by buildDroneScreen and after each launch.","inputs":"D_9233 dronesLeftDisplay (0-9)","outputs":"glyph pair at rows 5-6 column 34; D_2A29/D_2A2A cleared; zp_18=5, zp_19=$22","confidence":"high"},
  {"addr":"7061","name":"blinkDroneCountBox","summary":"Blinks the background of the drones-left box: fills the 3x2 colour cells at row 5 column 33 with $30 on odd frames (zp_45 bit 0) or with the nibble-swapped $03 on even frames. The screen-2 loops call it every 11 or 40 frames while fewer than two drones remain.","inputs":"zp_45 frameCounter bit 0","outputs":"6 colour cells; D_2A2D=5, D_2A2E=$21, D_2A2A; zp_1A scratch","confidence":"high"},
  {"addr":"708D","name":"drawLaunchStrikeButtons","summary":"Draws the right-hand button panel of the drone screen: window $13 (left 29, top 10, 14x11) with inner corner marks, the 2x2 button icon block at $9804 at row 12 column 34 coloured $05 with the caption 'LAUNCH' ($8691) at row 14 column 32 (colour $CD), and the same icon at row 16 column 34 with 'STRIKE' ($8697) at row 18 column 32 (colour $C7). The screen-2 loops later recolour these 2x2 icons ($05 when pressed, $02 for strike, XOR $08 blink).","inputs":"none","outputs":"frame, icons and text drawn; zp_3D, zp_3F, zp_40 set; draw modes reset","confidence":"high"},
  {"addr":"70F5","name":"buildDroneScreen","summary":"Builds the complete drone screen (game screen 2, entered from $7FE6): clears the colour state D_2A29-D_2A2C, sets border and background to $0C, radarBackgroundColour D_9226 = $0A and zp_41 = 1, then draws the drone count panel, count digit, LAUNCH/STRIKE buttons, heading panel, fuel panel, fuel digits and the radar frames, finally the status-line window (sub_28E1) and jumps to L_C5D7 to re-show the current status message.","inputs":"D_9220, D_9233, D_9236","outputs":"whole screen redrawn; D_9003/D_9004 = $0C, D_9226 = $0A, zp_41 = 1, D_2A29-D_2A2C = 0","confidence":"high"},
  {"addr":"7135","name":"clearRadarWindowGrey","summary":"Sets radarWipeColour D_7134 = $BB and falls into the shared tail at $7141: fills the 14x12 radar area (row 6 column 13) with that colour, clears the ghost countdown D_920F and sets the view origin zp_A5/zp_A6 to the own comcen minus (6,5) so the comcen sits at the radar centre. Called right after a launch; the nonzero D_7134 also makes the following drawRadarIrisWipe animate with 3-frame pauses.","inputs":"D_9236 comcenUnit, D_F640/D_F6A4","outputs":"D_7134 = $BB, radar area colour $BB, D_920F = 0, zp_A5/zp_A6","confidence":"high"},
  {"addr":"713C","name":"clearRadarWindowBlack","summary":"Same as clearRadarWindowGrey but with D_7134 = 0: blanks the radar area to colour 0, clears D_920F and centres the view on the own comcen. Used at screen build and when a drone flight ends; with D_7134 = 0 a later iris wipe runs without delays.","inputs":"D_9236, D_F640/D_F6A4","outputs":"D_7134 = 0, radar area black, D_920F = 0, zp_A5/zp_A6 = comcen - (6,5)","confidence":"high"},
  {"addr":"7163","name":"playDroneLostAnimation","summary":"Visual for a drone that is destroyed, detonated or out of fuel: forces fixed colour, switches sprite 4 (comcen rings) off, flashes the radar area white and wipes it (flashRadarAndWipe), then turns sprite 2 (the drone icon) into a filled disc (circles of radius 3,2,1 with spritePlotMode $40) in white and lets it shrink and fade (erase radius 3 -> colour $0F, radius 2 -> $0C, radius 1 -> $0B with 3/3/5/6 game frames between), disables sprite 2, zeroes droneFuel, redraws the fuel digits and waits 30 frames. The 'sta zp_30' at $716A and the store to D_7271 are dead (zp_30 is only used inside magnifyWindow2x, D_7271 is never read).","inputs":"D_920B (saved but unused)","outputs":"D_9022 bits 4 and 2 cleared, sprite 2 shape/colour changed, D_91F3 = 0, D_9220 = 0, D_2A29/D_2A2A = 0, ~50 game frames elapse","confidence":"high"},
  {"addr":"7204","name":"flashRadarAndWipe","summary":"Fills the 14x12 radar area (row 6, column 13) with colour 1 (white), runs 2 game frames, clears the animation flag D_7134 and falls into drawRadarIrisWipe so the wipe closes instantly. Only used by playDroneLostAnimation.","inputs":"none","outputs":"radar area white, D_7134 = 0, then see drawRadarIrisWipe","confidence":"high"},
  {"addr":"7221","name":"drawRadarIrisWipe","summary":"Iris effect over the radar area: draws window frame $16 (the radar area itself, 14x12 at row 6 column 13), then repeatedly draws a frame box from the 9-byte all-$03 tile set at $9848 starting at row 6 column 14 with 12x12 cells, shrinking by two and moving in by one each pass (drawFrameBox decrements the size once, this loop once more) until the height drops below 3, waiting for raster line $B0 before each frame and, when D_7134 is nonzero, running 3 game frames per pass; finally draws the 2x2 block of character $05 at $980A at the centre (row 11, column 19). Colours come from the current colour override ($AA after a launch, 1 during the loss animation).","inputs":"D_7134 animate flag, D_2A29/D_2A2A colour","outputs":"radar area covered by concentric frames; D_2A2D = 11, D_2A2E = $13, D_9221/D_9222 clobbered","confidence":"high"},
  {"addr":"727A","name":"buildRadarNoiseMasks","summary":"Fills the 16-byte table radarNoiseMasks (D_9210) with $FF and clears four random bits in each byte (bit chosen by nextGameRandom & 7 through clearBitMaskTable). drawRadarGlyph ANDs glyph rows with eight consecutive masks to give unspotted enemy blips a static/noise look. Called by drawDroneRadarFrame, drawDroneRadarWindow and the screen-1 map redraw ($7926) and unit marker code ($7BA2).","inputs":"RNG $57-$59","outputs":"D_9210-D_921F, zp_1A = 0, Y = $FF","confidence":"high"},
  {"addr":"72A1","name":"applyDroneBlast","summary":"Hidden as .byte in the listing but real code, called by cmdDroneDetonate ($536B) with A = $19: damages the 2x2 cells whose top-left corner is (zp_74, zp_75) - (x,y), (x+1,y), (x+1,y+1), (x,y+1) - through the inner routine at $72B3. The blast strength is stored in the operand at $7311 so that each hit ends with 'LDA #strength / JSR applyHitToUnit ($3B22)'.","inputs":"A = blast strength, zp_74/zp_75 = top-left map cell","outputs":"units in the four cells damaged; $7311 patched; see applyDroneBlastToCell","confidence":"high"},
  {"addr":"72B3","name":"applyDroneBlastToCell","summary":"Inner part of applyDroneBlast for one cell (zp_74, zp_75): returns if the cell is off the map or holds no unit (readMapCell $25FA, bit 7 clear). With strength >= 15 and game type != 2 (THE BOMB) the unit is stunned (unitStunTable $F960 |= $0F) and halted (haltStunnedUnit $3FAC); if it is the own comcen: updateComcenCloakLock ($5151), then with the DAMAGE option on and not in solo/film mode the comcen is knocked out (D_9242/D_9243/D_9244 = $80, comcenStunStatus $922A = $FF), otherwise message $0C is queued (sub_C566). The 'LDY $9236 / BNE' after the message is always taken because comcenUnit is 49 or 99. Finally applyHitToUnit with A = strength, Y = unit.","inputs":"zp_74/zp_75 cell, $7311 strength, D_0BA3 gameTypeOptions, D_0B9B, D_9236","outputs":"unit damage/stun, possibly knock-out flags; A = strength, Y = unit","confidence":"high"},
  {"addr":"731E","name":"steerDrone","summary":"Moves the locally controlled drone one cell per command exchange (called from sendOutgoingPacket $5639 while droneStateFlags bit 6 is set, before the $8D report is queued). Converts the last joystick direction zp_5E to a clockwise heading through D_7FCE, turns droneHeading (D_920B) one step toward it the short way round (playing sound $10 on a turn), then translates the heading through headingToDirectionTable and the direction delta tables D_20F0/D_1FB2 into dx/dy and adds them to droneCol/droneRow (D_92F6/D_92F7).","inputs":"zp_5E joystick direction code, D_920B, D_92F6/D_92F7","outputs":"D_920B updated (0-7), D_92F6 += dx, D_92F7 += dy, sound $10 on turn; X/Y clobbered","confidence":"high"},
  {"addr":"7396","name":"drawRadarGlyph","summary":"Plots one 8x8 map glyph at the bitmap cell zp_B4/zp_B5 and writes its colour to (zp_B6),Y: glyph source is $9C6E + (A & $7F)*8 via setTerrainTilePtr. If radarHalfToneFlag D_922D is set (cloaked unit) the even rows are blanked and only odd rows copied; if A bit 7 is set the rows are ANDed with eight consecutive radarNoiseMasks starting at a random index (ghost blip). Then D_922D is cleared, zp_B4 advances by 8 to the next cell, and the colour is stored: on screen 1 the raw zp_32 byte, otherwise (zp_32 << 4) | radarBackgroundColour. The D_2A29 test at $73F9 can never succeed because setTerrainTilePtr has just cleared it. Also called from drawScreen1Cell ($5DF5) in the main program. A/X/Y are preserved via self-modified operands.","inputs":"A = glyph (bit 7 = noise), X/Y preserved (Y = colour offset), zp_32 radarGlyphColour, zp_B4-zp_B7, D_922D, D_90FB, D_9226","outputs":"bitmap cell and colour cell written; zp_B4/zp_B5 += 8; D_922D = 0; A = glyph & $7F, X, Y restored; D_7395 = Y","confidence":"high"},
  {"addr":"741C","name":"drawDroneRadarWindow","summary":"Redraws the 14x12 radar view of the drone screen (character rows 6-17, columns 13-26, one character per map cell) from the view origin zp_A5/zp_A6: sets radarTerrainColour = 8, own/enemy unit colours from D_9224/D_9225, rebuilds the noise masks, fetches the map pointer with getViewportOriginPointer, computes the bitmap pointer from the $9300 row/column tables (row 6, column 13) and the screen RAM pointer (row 6 + 13), waits for raster line $90 and then for each row (X) and column (Y) runs getRadarCellGlyph and drawRadarGlyph. zp_A5/zp_A6 are walked cell by cell for the off-map test and restored from D_90F6/D_90F7 at the end. Called from the per-turn radar update sub_8279.","inputs":"zp_A5/zp_A6 view origin, map at $F000, unit arrays, D_9224/D_9225","outputs":"radar area redrawn; D_9237-D_9239, D_9210, D_90F6/D_90F7, zp_48/zp_49, zp_B4-zp_B7 clobbered; D_2A29 = 0","confidence":"high"},
  {"addr":"74C3","name":"getRadarCellGlyph","summary":"Converts map cell byte A at (zp_A5, zp_A6) into a radar glyph and colour (X/Y preserved). Off-map coordinates (>= 40) give one of four hatch glyphs $28-$2B picked by (row+col)&3. Terrain cells >= $40 give glyph cell-$40 in radarTerrainColour (cells below $40, not expected, give cell-$20). A unit cell ($80|index) gives unitGlyphBaseTable[type]+facing in the own colour D_9237; for the enemy half of the unit array (the BCS at $750A is a side-dependent opcode copied from $206B by loadOverlayB) it uses D_9238 if the unit is spotted (unitEnergy bit 7), otherwise the terrain under the unit is shown except that every 1-8th unspotted enemy (countdown D_920F reseeded from the RNG) is drawn as a noise 'ghost' (glyph | $80) in the terrain colour. Dead units show their underlying terrain. A cloaked unit (unitCloakFlags bit 7) sets radarHalfToneFlag so drawRadarGlyph draws it on alternate scanlines.","inputs":"A = map cell, zp_A5/zp_A6 cell coordinates, D_9237-D_9239, D_920F, unit arrays F76C/F708/F898/F9C4/FAF0, D_0799","outputs":"A = glyph index (bit 7 = draw with noise), zp_32 = colour, D_922D set for cloaked units, D_920F updated; X/Y restored","confidence":"high"},
  {"addr":"7552","name":"playStrikeZoomAnimation","summary":"Effect for a drone strike: zooms into the centre of the radar four times with magnifyWindow2x on the 7x6 cell block at row 9 column 16, running 12, 10, 8 and 6 game frames between steps (D_7551 counts 3..0), waits 6 more frames and then jumps to playDroneLostAnimation. Called from the strike wait loop ($81CD) after sound $11.","inputs":"none","outputs":"radar area magnified and then cleared; D_7551 = $FF; see playDroneLostAnimation","confidence":"high"},
  {"addr":"7587","name":"runMissileScreen","summary":"Entry of game screen 1 (jumped to by the screen dispatcher sub_06BA): clears cursorMovedFlag, copies the own missile count D_92B0 into missilesLeftDisplay, unpacks the direction sprite shapes at $FD70 into $0200 and builds the screen with sub_7A32 (21x17 cell map window at character (3,3) centred on the own comcen at grid cell (10,8)). Continues at missileScreenResume.","inputs":"D_92B0, D_9236","outputs":"D_7586 = 0, D_9232, screen built; never returns (loop)","confidence":"high"},
  {"addr":"759C","name":"missileScreenResume","summary":"Re-entry after the knock-out sequence ($0764) and main loop of the missile screen. Sets screenLockedFlag D_7585 = $80 when the game type is Scrimmage (no comcen) or the own comcen is cloaked (D_9245 bit 7); if not locked redraws the map window (sub_7688), unit markers (sub_7BA2), resets the sweep (sub_78CB) and shows the comcen cross (sprite 2, sub_3383). Loop at $75B9: updateGameFrame; screen switch via sub_06BA unless D_92F9 bit 0; every 9 frames (zp_6F) XOR-blinks the 2x2 button at row 10 column 32 and, with fewer than two missiles, the count box (sub_7AEC); when locked nothing else runs. Otherwise: D_920E refresh -> redraw map and drone detonation effect (sub_79D8); radar sweep (sub_7894); a fire press (sub_0ED5) after the cursor has moved launches a missile if missiles remain - always when missileJamFlag D_9243 is 0, only on a random bit when set: decrement the count, redraw it, fill the button $05, sound $0F, queue command $90 x y at the cursor cell (sub_7992), draw the trajectory (sub_7713), ring burst at the cursor and sub_79A1; else joystick input moves the grid cursor zp_67/zp_68 through sub_76B2 with auto-repeat (10 frames first, then 4, via D_91CA/D_91C9) and sets cursorMovedFlag = $7F.","inputs":"D_0BA3, D_9245, D_920E, D_9243, D_9232, zp_5D, zp_6C, zp_6F, D_91C9/D_91CA, D_1FA6","outputs":"D_7585, D_7586, zp_6C, zp_6F, D_91C9 = $7F, commands queued, sprites/screen updated; never returns","confidence":"high"}
 ],
 "variables": [
  {"addr":"5E","scope":"zp","name":"joystickSteerDirection","meaning":"Direction code 0-7 (N,S,E,W,NW,NE,SE,SW) of the last joystick steer input on the drone screen (set by sub_82A4, cleared at launch); steerDrone maps it to a heading through D_7FCE","confidence":"high"},
  {"addr":"67","scope":"zp","name":"gridCursorCol","meaning":"Missile-screen cursor column in the 21x17 grid (0-20, comcen at 10); sprite 2 follows it","confidence":"high"},
  {"addr":"68","scope":"zp","name":"gridCursorRow","meaning":"Missile-screen cursor row in the 21x17 grid (0-16, comcen at 8)","confidence":"high"},
  {"addr":"6C","scope":"zp","name":"joyRepeatTimer","meaning":"Frame countdown gating joystick auto-repeat (10 then 4 frames on the missile screen, 11/40 on the drone screen)","confidence":"high"},
  {"addr":"6F","scope":"zp","name":"buttonBlinkTimer","meaning":"9-frame countdown for the XOR blink of the missile button (row 10 column 32) and count box","confidence":"medium"},
  {"addr":"1A","scope":"zp","name":"nibbleSwapTemp","meaning":"Scratch: high nibble collector for the colour nibble swap in drawDroneFuelDigits/blinkDroneCountBox, bit counter in buildRadarNoiseMasks","confidence":"high"},
  {"addr":"30","scope":"zp","name":"magnifyRow","meaning":"Written with 5 by playDroneLostAnimation but only read inside magnifyWindow2x, which sets it itself: dead store here","confidence":"medium"},
  {"addr":"32","scope":"zp","name":"radarGlyphColour","meaning":"Colour nibble/byte for the glyph being drawn by drawRadarGlyph (terrain, own or enemy unit colour chosen by getRadarCellGlyph)","confidence":"high"},
  {"addr":"41","scope":"zp","name":"screenBuiltFlag","meaning":"Set to 1 by the screen builders (buildDroneScreen, sub_7A32); never read","confidence":"low"},
  {"addr":"45","scope":"zp","name":"frameCounter","meaning":"IRQ frame counter; bit 0 drives the blink in blinkDroneCountBox","confidence":"high"},
  {"addr":"A5","scope":"zp","name":"viewOriginCol","meaning":"Map column of the view origin; on the drone screen = drone column - 6 (radar top-left), on the missile screen = comcen column - 10. drawDroneRadarWindow walks it per cell","confidence":"high"},
  {"addr":"A6","scope":"zp","name":"viewOriginRow","meaning":"Map row of the view origin; drone row - 5 on the drone screen, comcen row - 8 on the missile screen","confidence":"high"},
  {"addr":"B4","scope":"zp","name":"bitmapCellPtr","meaning":"Bitmap address of the current radar cell (lo); advanced by 8 per glyph in drawRadarGlyph","confidence":"high"},
  {"addr":"B5","scope":"zp","name":"bitmapCellPtrHi","meaning":"High byte of bitmapCellPtr","confidence":"high"},
  {"addr":"B6","scope":"zp","name":"colourCellPtr","meaning":"Screen RAM (drone screen) or colour RAM (screen 1 caller) address of the current radar row (lo); indexed by the column in Y","confidence":"high"},
  {"addr":"B7","scope":"zp","name":"colourCellPtrHi","meaning":"High byte of colourCellPtr","confidence":"high"},
  {"addr":"B2","scope":"zp","name":"glyphPtr","meaning":"Pointer (lo) to the 8-byte glyph at $9C6E + glyph*8 set by setTerrainTilePtr","confidence":"high"},
  {"addr":"B3","scope":"zp","name":"glyphPtrHi","meaning":"High byte of glyphPtr","confidence":"high"},
  {"addr":"74","scope":"zp","name":"cmdParam1","meaning":"Command x parameter; for applyDroneBlast the top-left column of the 2x2 blast","confidence":"high"},
  {"addr":"75","scope":"zp","name":"cmdParam2","meaning":"Command y parameter; for applyDroneBlast the top-left row of the 2x2 blast","confidence":"high"},
  {"addr":"7134","scope":"abs","name":"radarWipeColour","meaning":"Colour used to blank the radar area by clearRadarWindowGrey/Black ($BB or 0); nonzero also makes drawRadarIrisWipe pause 3 game frames per frame","confidence":"high"},
  {"addr":"7271","scope":"abs","name":"savedDroneHeading","meaning":"Copy of droneHeading taken by playDroneLostAnimation; never read (dead)","confidence":"high"},
  {"addr":"7395","scope":"abs","name":"radarGlyphSavedY","meaning":"Y register (column offset) saved and restored by drawRadarGlyph","confidence":"high"},
  {"addr":"7551","scope":"abs","name":"zoomStepCounter","meaning":"Loop counter 3..0 of playStrikeZoomAnimation (also sets the delay (n+3)*2 frames)","confidence":"high"},
  {"addr":"7585","scope":"abs","name":"missileScreenLocked","meaning":"Bit 7 set = missile screen inactive (Scrimmage game type has no comcen, or the own comcen is cloaked per D_9245); only frame updates and screen switching run","confidence":"high"},
  {"addr":"7586","scope":"abs","name":"cursorMovedFlag","meaning":"0 until the joystick has moved the missile cursor, then $7F; fire is ignored while 0","confidence":"high"},
  {"addr":"9220","scope":"abs","name":"droneFuel","meaning":"Remaining flight time of the own drone: 100 at launch, decremented once per command exchange by sub_8279, 0 = drone expires ($8F sent); displayed by drawDroneFuelDigits; preset to 100 or 0 at screen entry depending on drones left","confidence":"high"},
  {"addr":"920B","scope":"abs","name":"droneHeading","meaning":"Heading 0-7 clockwise from north of the own drone (set from the joystick at launch via D_7FCE, turned one step per exchange by steerDrone); also selects the direction sprite shape","confidence":"high"},
  {"addr":"920F","scope":"abs","name":"radarGhostCountdown","meaning":"Counts unspotted enemy units drawn on the drone radar; when it underflows the next one is drawn as a noise ghost and it is reseeded with RNG & 7","confidence":"high"},
  {"addr":"9210","scope":"abs","name":"radarNoiseMasks","meaning":"16 bytes of $FF with 4 random bits cleared each (buildRadarNoiseMasks); ANDed over glyph rows for the ghost/static effect","confidence":"high"},
  {"addr":"9224","scope":"abs","name":"ownUnitRadarColour","meaning":"Colour nibble for own units on the drone radar ($0B, written by overlay A at $7904)","confidence":"medium"},
  {"addr":"9225","scope":"abs","name":"enemyUnitRadarColour","meaning":"Colour nibble for spotted enemy units on the drone radar ($09, written by overlay A at $78FB)","confidence":"medium"},
  {"addr":"9226","scope":"abs","name":"radarBackgroundColour","meaning":"Low colour nibble ORed into the screen RAM byte of every radar cell by drawRadarGlyph ($0A on the drone screen, 0 on screen 1)","confidence":"high"},
  {"addr":"922D","scope":"abs","name":"radarHalfToneFlag","meaning":"$FF when the unit being drawn is cloaked (unitCloakFlags bit 7): drawRadarGlyph blanks even rows and copies only odd rows; cleared after each glyph","confidence":"high"},
  {"addr":"9232","scope":"abs","name":"missilesLeftDisplay","meaning":"Screen-1 working copy of the own missile count (from D_92B0), decremented on each launch and shown by sub_7ABA","confidence":"high"},
  {"addr":"9233","scope":"abs","name":"dronesLeftDisplay","meaning":"Screen-2 working copy of the own drone count (from D_92AE), decremented on launch and shown by drawDroneCountDigit","confidence":"high"},
  {"addr":"9237","scope":"abs","name":"radarOwnUnitColour","meaning":"Colour used by getRadarCellGlyph for own units (D_9224 on the drone screen, $0B from drawScreen1Cell)","confidence":"high"},
  {"addr":"9238","scope":"abs","name":"radarEnemyUnitColour","meaning":"Colour for spotted enemy units (D_9225 on the drone screen, 1 from drawScreen1Cell)","confidence":"high"},
  {"addr":"9239","scope":"abs","name":"radarTerrainColour","meaning":"Colour for terrain and ghost blips (8 on the drone screen, $0B from drawScreen1Cell)","confidence":"high"},
  {"addr":"9243","scope":"abs","name":"missileJamFlag","meaning":"Nonzero ($80, set with the DAMAGE option when a drone blast hits the own comcen): missile launches succeed only on a random coin flip and the radar sweep is disturbed","confidence":"medium"},
  {"addr":"9244","scope":"abs","name":"droneJamFlag","meaning":"Nonzero ($80, same cause): drone launches succeed only 1 in 4 and the radar is randomly blanked to $AA each turn","confidence":"medium"},
  {"addr":"9245","scope":"abs","name":"comcenCloakedFlag","meaning":"Bit 7 = own comcen cloaked: disables the missile screen and ignores the drone launch button","confidence":"high"},
  {"addr":"92F6","scope":"abs","name":"droneCol","meaning":"Drone position (column). On the drone screen it is kept as the radar origin (drone column - 6; sub_81FE adds 6 before it is sent in $8D/$8E and the own $8D copies it to zp_A5); cmdDroneMove on screen 1 stores the plain cell","confidence":"medium"},
  {"addr":"92F7","scope":"abs","name":"droneRow","meaning":"Drone position (row), stored as radar origin (drone row - 5) on the drone screen","confidence":"medium"},
  {"addr":"C44E","scope":"abs","name":"decimalHundreds","meaning":"Hundreds digit produced by sub_C428 (binary to 3 decimal digits)","confidence":"high"},
  {"addr":"C44F","scope":"abs","name":"decimalTens","meaning":"Tens digit produced by sub_C428","confidence":"high"},
  {"addr":"C450","scope":"abs","name":"decimalOnes","meaning":"Ones digit produced by sub_C428","confidence":"high"},
  {"addr":"0799","scope":"abs","name":"unitGlyphBaseTable","meaning":"First 5 entries: base glyph index per unit type for the 1-character map glyph set at $9C6E ($2C grunt, $30 rider, $34 boomer, $38 spy, $3C comcen), plus the facing 0-3 from unitFlags bits 0-1","confidence":"high"},
  {"addr":"7FCE","scope":"abs","name":"joyDirToHeadingTable","meaning":"8 bytes: joystick direction code (N,S,E,W,NW,NE,SE,SW) -> clockwise heading 0-7 (0,4,2,6,7,1,3,5)","confidence":"high"},
  {"addr":"9236","scope":"abs","name":"comcenUnit","meaning":"Index of the own comcen unit: 49 for side 0, 99 for side 1 (from D_0C8A); its position is the centre of both overlay-B screens","confidence":"high"},
  {"addr":"F9C4","scope":"abs","name":"unitEnergy","meaning":"Per-unit energy; bit 7 = spotted by the other side (enemy units are drawn on the radar only when set)","confidence":"high"},
  {"addr":"F898","scope":"abs","name":"unitCloakFlags","meaning":"Per-unit; bit 7 = cloaked (drawn half-tone on the radar)","confidence":"high"},
  {"addr":"FAF0","scope":"abs","name":"unitTerrainUnder","meaning":"Terrain cell byte under each unit; shown instead of hidden enemy or dead units","confidence":"high"},
  {"addr":"90F6","scope":"abs","name":"savedViewOriginCol","meaning":"View origin column saved while drawDroneRadarWindow walks zp_A5","confidence":"high"},
  {"addr":"90F7","scope":"abs","name":"savedViewOriginRow","meaning":"View origin row saved while drawDroneRadarWindow walks zp_A6","confidence":"high"}
 ],
 "dataBlocks": [
  {"addr":"7134","length":1,"type":"byteTable","name":"radarWipeColour","description":"Fill colour / slow-animation flag for the radar wipe ($BB or 0)"},
  {"addr":"715D","length":6,"type":"unknown","name":"unusedBytes715D","description":"$21,$A1,$21,$81,$A1,$11 between the RTS at $715C and playDroneLostAnimation; not referenced by variant B (L_7161 is a branch target only in variant A). Probably leftover data or code fragment."},
  {"addr":"7271","length":1,"type":"byteTable","name":"savedDroneHeading","description":"Dead copy of droneHeading written by playDroneLostAnimation"},
  {"addr":"7272","length":8,"type":"byteTable","name":"clearBitMaskTable","description":"$FE,$FD,$FB,$F7,$EF,$DF,$BF,$7F: AND masks clearing bit n, used by buildRadarNoiseMasks"},
  {"addr":"7316","length":8,"type":"byteTable","name":"headingToDirectionTable","description":"Clockwise heading 0-7 (N,NE,E,SE,S,SW,W,NW) -> direction code 0-7 (0 N,1 S,2 E,3 W,4 NW,5 NE,6 SE,7 SW): 0,5,2,6,1,7,3,4. Used by steerDrone and by the comm sub-overlay at $EF38."},
  {"addr":"7395","length":1,"type":"byteTable","name":"radarGlyphSavedY","description":"Saved Y register of drawRadarGlyph"},
  {"addr":"7551","length":1,"type":"byteTable","name":"zoomStepCounter","description":"Counter of playStrikeZoomAnimation"},
  {"addr":"7585","length":1,"type":"byteTable","name":"missileScreenLocked","description":"Bit 7 = missile screen inactive"},
  {"addr":"7586","length":1,"type":"byteTable","name":"cursorMovedFlag","description":"0 until the missile cursor was moved, then $7F"}
 ],
 "misclassified": [
  {"addr":"72A1","length":117,"actual":"code","evidence":"Disassembles cleanly as 'STA $7311 / JSR $72B3 / INC $74 / JSR $72B3 / INC $75 / JSR $72B3 / DEC $74 / LDX $74 / CPX #$28 / BCS $7315 ... JSR $25FA / ... JSR $3FAC / CPY $9236 / ... JSR $C566 / ... LDA #$FF / STA $922A / LDA #$FF / JSR $3B22 / RTS' with every branch target landing on an instruction boundary; the main program's cmdDroneDetonate handler (itself byte data at $5336) contains 'A9 19 20 A1 72' = LDA #$19 / JSR $72A1 at $536B. The sub_72F6/L_730D/L_731A labels inside the block are artefacts of variant A."}
 ],
 "insights": [
  "Overlay B implements the two comcen screens reached with the function keys through sub_06BA: screen 1 ($7587, runMissileScreen) and screen 2 ($7FE6, the DRONE screen built by buildDroneScreen $70F5). Both are centred on the own comcen (comcenUnit $9236 = 49 or 99). Screen 1 shows a 21x17 cell map window at character (3,3) (one character per map cell, comcen at grid cell (10,8), cursor sprite 2 at zp_67/zp_68) and fires command $90 x y (missile strike) at the cursor cell when fire is pressed after the cursor has moved. Screen 2 launches a drone ($97) with the heading taken from the joystick, shows a 14x12 radar (rows 6-17, columns 13-26) that follows the drone, steers it one cell per command exchange through steerDrone ($731E, called from sendOutgoingPacket before the $8D report), detonates it with fire ($8E x y, resolved by applyDroneBlast $72A1 with strength 25 over a 2x2 area) and releases it ($8F) when droneFuel $9220 (100 exchanges) runs out.",
  "Drone screen layout (window table indices of drawWindowFrame): $0D (left 1, top 3, 9x9) heading/compass panel with sprites 3 and 5 as heading line/arrow; $0E (left 1, top 12, 9x9) fuel panel with 3 tall digits at row 15 columns 4-6 and a 3-character caption at row 17; $0F (left 11, top 4, 18x16) and $10 (left 12, top 5, 16x14) radar frames; $16 (left 13, top 6, 14x12) the radar area itself (used by the iris wipe); $11 (left 29, top 3, 11x7) and $12 (left 32, top 4, 5x4) drones-left box with one tall digit at row 5 column 34 and the caption 'DRONE' ($868C) at row 7 column 32; $13 (left 29, top 10, 14x11) buttons: 2x2 icon block $9804 at row 12 column 34 with 'LAUNCH' ($8691) at row 14, icon at row 16 with 'STRIKE' ($8697) at row 18. Character block data: $97D1 = 7x7 picture, $97B6 = 5x5 picture, $9804 = 2x2 icon (chars $45-$48), $980A = 2x2 of char $05, $9848 = 9 x char $03 frame tile set. Sprite 2 is the drone icon (shape copied from $FF93, at X=$AC Y=$88, the radar centre) and sprite 4 the comcen rings.",
  "Radar rendering pipeline: getRadarCellGlyph ($74C3) maps a map cell to a glyph index in the 8-byte-per-glyph set at $9C6E (terrain = cell - $40; off-map hatch $28-$2B; units = unitGlyphBaseTable $0799[type] + facing: $2C grunt, $30 rider, $34 boomer, $38 spy, $3C comcen) and a colour in zp_32; drawRadarGlyph ($7396) plots it with optional effects: cloaked units on alternate scanlines (D_922D), unspotted enemies hidden except for random noise 'ghosts' (D_920F countdown, masks D_9210 built by buildRadarNoiseMasks $727A). Both routines are also used by the main program's drawScreen1Cell ($5D7F) for screen 1, where the colour goes to colour RAM as a raw byte (D_90FB = 1) instead of (colour << 4) | D_9226 in screen RAM.",
  "Side-dependent opcode inside this overlay: the BCS at $750A ('cpy #$32 / bcs enemyPath' in getRadarCellGlyph) is not constant - loadOverlayB ($10A5) copies the patched opcode from $206B ($B0 for side 0, $90 for side 1, written by overlay A's sub_7BC6) into it right after loading the overlay. Annotators must treat $750A as a variable, and overlay B must be (re)loaded after the side is chosen for the radar to colour units correctly.",
  "Phantom routine labels: sub_6F91, sub_701C, sub_7097, sub_70D9, sub_73EE, sub_7444, sub_74FE and sub_761C exist only because overlay variant A has JSRs to those addresses (XREF merges both variants). In variant B they are plain instruction boundaries in the middle of the routines listed above, not entry points. Likewise the L_/D_ labels inside $72A1-$7315 (L_72B1, L_72D3, L_72D5, L_72E1, L_72E5, L_72F2, L_72F5, sub_72F6, L_730D, L_731A) come from variant A.",
  "Dead or ineffective code found: 'sta zp_30' at $716A (zp_30 is only used by magnifyWindow2x, which initialises it), the store to D_7271 at $717C (never read), 'bne L_7055' at $7053 (both paths fall into $7055), the 'lda D_2A29 / beq' test at $73F9 (setTerrainTilePtr at $73A4 always clears D_2A29 first, so the forced colour 8 is never applied), 'lda #0 / sta D_2A29' at $74B3 (already 0), and 'LDY $9236 / BNE' at $72FB (comcenUnit is never 0).",
  "Fuel read-out colour code (drawDroneFuelDigits): >= 21 colour byte $30, 11-20 $B7, <= 10 $21, with the two nibbles swapped on even values so the warning colours alternate every exchange. The drones-left digit is drawn in colour 0 (invisible) when none remain, and blinkDroneCountBox toggles its box between $30/$03 by frame parity.",
  "applyDroneBlast consequences: each unit in the 2x2 blast gets applyHitToUnit with 25 points and, unless the game type is THE BOMB, a stun counter of 15 plus haltStunnedUnit; if the own comcen is hit and the DAMAGE option ($0BA3 bit 4) is on (and not solo/film), D_9242/D_9243/D_9244 are set to $80 (overview shows terrain only, missile launches fail 50%, drone launches fail 75% and the radar is randomly jammed) and comcenStunStatus $922A = $FF which triggers the knock-out round at $0709; without DAMAGE only message $0C is queued.",
  "The radar wipe (drawRadarIrisWipe $7221) is a concentric-frame 'iris' drawn from the all-$03 tile set at $9848, shrinking by two cells per pass about the radar centre (row 12, column 20) and finishing with a 2x2 block of char $05; D_7134 doubles as the blank colour and the 'animate with 3-frame pauses' flag ($BB after a launch with colour override $AA, 0 for the instant white wipe of playDroneLostAnimation)."
 ]
}
