{ "chunk": "game_ovl_6F00_B_7C6E", "unit": "game/ovl_6F00_B", "routines": [ { "addr": "7C5C", "name": "getComcenCentredViewOrigin", "summary": "Returns the map origin that centres the player's comcen in overlay B's 21x17-cell screen-1 map window: X = unitCol[comcenUnit]-10, Y = unitRow[comcenUnit]-8. Called from $7959 and $7BC5 (screen 1 comcen drawing). Starts two bytes before the nominal chunk start ($7C6E) but is on the first line of the line range.", "inputs": "$9236 comcenUnit, unitCol $F640, unitRow $F6A4", "outputs": "X = origin column, Y = origin row, A clobbered", "confidence": "medium" }, { "addr": "7FE6", "name": "enterDroneScreen", "summary": "Entry of game screen 2 (the DRONE screen, F5), jumped to by the screen switcher sub_06BA ($06FC) when currentScreenId becomes 2. Copies dronesLeft[0] ($92AE) to $9233, unpacks the eight aircraft silhouettes at $FE24 into the $0200 shape buffer (unpackRleTo0200) and draws the whole screen with sub_70F5, then falls into resetDroneScreen.", "inputs": "$92AE dronesLeft[own]", "outputs": "$9233, $0200-$03FF shape buffer, screen 2 drawn", "confidence": "high" }, { "addr": "7FF6", "name": "resetDroneScreen", "summary": "(Re)initialises the drone screen's panel state and enters the idle loop. Also the re-entry point used by the knock-out sequence ($076A) and after a drone flight ends ($81FB). Reloads $9233 from dronesLeft[0], positions sprite 0 (X $37,Y $64, colour 1), sprite 5 (X $38,Y $64, colour 2) and sprite 3 (X $38,Y $65) in the left panel, sets droneJoyHeading zp_5E=0 (north), draws the heading indicator (sub_823F), loads aircraft shape 0 into sprite 5, enables sprites 0+3, and sets droneFuel $9220 to 100 when drones are left (else 0) for the fuel read-out.", "inputs": "$92AE, sprite tables", "outputs": "$9233, $9220, zp_5E, $9010/$9013/$9015, $9018/$901B/$901D, $9008/$900D, $9022=$09, $91CC=5; falls into droneScreenIdleLoop", "confidence": "high" }, { "addr": "803D", "name": "droneScreenIdleLoop", "summary": "Idle loop of the drone screen while no drone is in flight: runs updateGameFrame and the screen switcher every frame; every 11 frames (zp_6C) blinks the drones-left box (sub_7061, only when fewer than 2 drones remain) and XORs colour 8 into the 2x2 LAUNCH box at row 12 column 34. A fresh fire press (checkFirePressedNotSolo) launches a drone via launchDrone unless the own comcen is cloaked ($9245 bit 7), no drones are left ($9233 = 0), or the drone system is damaged ($9244 != 0), in which case only one press in four (random & 3 == 0) succeeds.", "inputs": "zp_6C, $9233, $9244, $9245, fire button", "outputs": "colour cells toggled; jumps to launchDrone", "confidence": "high" }, { "addr": "808D", "name": "launchDrone", "summary": "Launch sequence: reads the joystick heading (sub_82A4), droneHeading $920B = joyDirToHeadingTable[zp_5E], queues the 1-byte command $97 (drone launch), paints the LAUNCH box (12,34) colour 5, droneFuel = 100 (printed by sub_6F99), decrements $9233 and reprints it (sub_7036), sets droneStateFlags $920D |= $C0 (active + locally steered), sets the view origin and droneViewOriginCol/Row $92F6/$92F7 to comcen position minus (6,5) so the drone starts over the comcen, draws the comcen ring sprite (sub_3456), loads the targeting reticle at $FF93 into sprite 2 (black, X $AC, Y $88) and enables it, puts the heading silhouette into sprite 5, plays sound $0F, clears the camera window with static (sub_7135), sets colour override $AA, runs the iris-open animation sub_7221 and enters droneFlightLoop.", "inputs": "zp_5D joystick, $9236 comcenUnit, unitCol/unitRow, $FF93 reticle shape", "outputs": "$920B, $920D, $9220, $9233, $92F6/$92F7, zp_A5/zp_A6, sprites 2/4/5, command $97 queued", "confidence": "high" }, { "addr": "8124", "name": "droneFlightLoop", "summary": "Per-frame loop while the drone flies: updateGameFrame; a fire press goes to strikeWithDrone. Every 40 frames (zp_6C) blinks the drones-left box (<2 left) and XORs colour 8 into the STRIKE box at (16,34). When zp_6E has expired re-reads the joystick heading (sub_82A4). When exchangeStepFlag $920E bit 7 is set (a lock-step exchange was applied) advances the camera one step (sub_8279); if droneFuel reaches 0 or the game ended ($0BA8 != 0) the drone is released (sub_8230) and the loss animation sub_7163 + sound $1C run; if $920D bit 7 was cleared by the command handlers (drone arrived $92 / remote detonation) the same ending runs without sending $8F. Then endDroneFlight.", "inputs": "zp_6C, zp_6E, $920E, $920D, $9220, $0BA8, fire button", "outputs": "drone view redrawn per step; falls into endDroneFlight", "confidence": "high" }, { "addr": "8182", "name": "strikeWithDrone", "summary": "Fire pressed during flight: droneStateFlags = ($920D & $BF) | $20 (stop local steering, detonation pending), paints the STRIKE box (16,34) colour 2, computes the drone's map position (sub_81FE) and queues command $8E col,row (detonate). Then waits: while bit 5 stays set it keeps advancing the camera on each exchange with sound $1C; once cmdDroneDetonate has cleared bit 5 (own command executed) and bit 7 is still set it plays sound $11 and runs the strike flash/loss animation sub_7552. Ends in endDroneFlight.", "inputs": "$920D, $920E, $92F6/$92F7", "outputs": "command $8E queued, $920D, $74/$75", "confidence": "high" }, { "addr": "81D0", "name": "endDroneFlight", "summary": "Common tail of a drone flight: sound $1C, droneFuel = 0 (reprinted), droneStateFlags &= $3F, view origin back to comcen minus (6,5), camera window cleared without static (sub_713C), then jumps to resetDroneScreen for the next launch.", "inputs": "$9236, unitCol/unitRow", "outputs": "$9220=0, $920D, zp_A5/zp_A6", "confidence": "high" }, { "addr": "81FE", "name": "getDroneMapPosition", "summary": "cmdParam1/2 ($74/$75) = droneViewOriginCol+6, droneViewOriginRow+5, i.e. the drone's own map cell (the camera window is kept centred on the drone). Used by sendOutgoingPacket ($563C) to build the $8D heading,col,row move command and by strikeWithDrone for the $8E detonate command.", "inputs": "$92F6/$92F7", "outputs": "zp_74 = drone column, zp_75 = drone row", "confidence": "high" }, { "addr": "820F", "name": "showDroneAlert", "summary": "Opponent drone launch notification called by cmdDroneLaunch ($53AF): plays sound 7 and, unless playModeFlags $0B9B bit 7 (solo/film) is set, shows message $20 'DRONE ALERT!' through the $C000 overlay's sub_C566. Assembled as .byte in the listing (misclassified).", "inputs": "$0B9B", "outputs": "sound, message", "confidence": "high" }, { "addr": "821F", "name": "fillDroneViewWindow", "summary": "Fills the drone camera window (14 columns x 12 rows at row 6, column 13) with colour byte A through fillScreenColourRect. Used to clear the window ($7141), to show static ($AA) when the drone system is damaged, and by the main program at $2E07.", "inputs": "A = colour byte", "outputs": "D_2A2D=6, D_2A2E=13, screen RAM cells", "confidence": "high" }, { "addr": "8230", "name": "releaseDroneControl", "summary": "If a drone is active ($920D bit 7) clears bits 6-7 of droneStateFlags and queues the 1-byte command $8F (drone released) so the opponent stops tracking it. Called when fuel runs out or the game ends, and by the comcen knock-out sequence ($0709).", "inputs": "$920D", "outputs": "$920D &= $3F, command $8F queued", "confidence": "high" }, { "addr": "823F", "name": "drawDroneHeadingIndicator", "summary": "Draws the heading pointer of the left panel: clears sprite 3, draws a solid line in it from the centre (col 12,row 10) to headingLineEndCol/RowTable[joyDirToHeadingTable[zp_5E]] with drawLineInPathSprite, sets sprite 3's colour to $0B (or 6 for north/south, zp_5E < 2), copies the 64-byte shape to sprite 0 (which sits one pixel up-left in white, giving a shadowed line) and enables sprites 0 and 3.", "inputs": "zp_5E droneJoyHeading, tables $7FCE/$7FD6/$7FDE", "outputs": "$8AC0 and $8A00 sprite shapes, $900B, $9022 |= $09, zp_92-zp_95 clobbered", "confidence": "high" }, { "addr": "8279", "name": "advanceDroneCamera", "summary": "One drone step on the DRONE screen, run whenever exchangeStepFlag $920E bit 7 is set: clears the flag, updateMarkerSprite, redraws the 14x12 camera window from the map around droneViewOrigin (sub_741C); if the drone system is damaged ($9244) half of the steps (random bit 7 clear) fill the window with static colour $AA; decrements droneFuel, reloads the heading silhouette into sprite 5 and reprints the fuel (sub_6F99). The actual drone movement is done by sendOutgoingPacket via $731E.", "inputs": "$920B, $9220, $9244, $92F6/$92F7", "outputs": "$920E=0, $9220-1, sprite 5, camera window", "confidence": "high" }, { "addr": "82A4", "name": "readDroneHeadingFromJoystick", "summary": "Reads joystickDirectionTable[zp_5D]; if a direction is held (entry >= 0) stores it in droneJoyHeading zp_5E, sets the re-read delay zp_6E = 20 frames and redraws the heading indicator (sub_823F). Called at launch and, when zp_6E has expired, during flight.", "inputs": "zp_5D joystick bits, $1FA6 table", "outputs": "zp_5E, zp_6E, heading sprites", "confidence": "high" }, { "addr": "82BF", "name": "drawShotLineInViewport", "summary": "Draws the shot of resolveAttack ($3A30) in the 7x5-cell battlefield viewport (2x2 characters per cell at row 1, column 25). First forces the upper colour nibble of the viewport's 14x10 screen RAM cells to 1 (white for bit pair 01). Handles a negative view origin (zp_A5/zp_A6 < 0) by shifting all columns/rows, computes the viewport bounds $82B5-$82B8, and records in $82B9 which endpoints lie inside (attacker -> -1, target -> +2); returns if neither. Then rebases the coordinates (sub_840F), scales to fat pixels (x8, +3/+4 for the cell centre, sub_84B2), runs the line stepper from attacker (zp_93,zp_92) to target (zp_95,zp_94) and plots colour-1 fat pixels via plotFatPixel for points inside the window (X = dx+100, Y = dy+4). The first 4 steps from the shooter are skipped, the 5th gets the 4-pixel diamond (sub_84F1), unless only the target is visible ($82B9 = 2) in which case the whole line is solid. Boomers (attackerType $923D = 2) step 3 cells per plot (dotted). Finally viewRedrawTimer $9234 = $8F so the view is redrawn (line erased) 15 frames later.", "inputs": "zp_92/zp_93 attacker row/col, zp_94/zp_95 target row/col, zp_A5/zp_A6 view origin, $923D attackerType, $9382/$939B screen row tables", "outputs": "bitmap pixels, screen RAM colour nibbles, $9234 = $8F, $82B5-$82BC, zp_92-zp_95 and line stepper state clobbered, zp_B6/zp_B7", "confidence": "high" }, { "addr": "840F", "name": "rebaseLineCoordsToMin", "summary": "Subtracts min(viewLeft $82B5, zp_93, zp_95) from all three column values and min(viewTop $82B7, zp_92, zp_94) from the three row values so that the smallest becomes 0. Keeps the following x8 scaling from overflowing a byte.", "inputs": "$82B5, $82B7, zp_92-zp_95", "outputs": "$82B5, $82B7, zp_92-zp_95 rebased", "confidence": "high" }, { "addr": "84B2", "name": "scaleLineCoordsToFatPixels", "summary": "Converts the four line coordinates from map cells to fat-pixel units: zp_94 = row*8+4, zp_95 = col*8+3, zp_92 = row*8+4, zp_93 = col*8+3, saturating to $FF on overflow (the zp_92 case lacks the LDA #$FF and only skips the +4 when the overflowed value is negative - harmless in practice).", "inputs": "zp_92-zp_95 in cells", "outputs": "zp_92-zp_95 in fat pixels", "confidence": "high" }, { "addr": "84F1", "name": "plotDiamondMarker", "summary": "Plots four colour-1 fat pixels at (X-1,Y-1), (X+1,Y-1), (X+1,Y+1), (X-1,Y+1) with plotFatPixel and restores X/Y: the small diamond drawn at the muzzle end of a shot line.", "inputs": "X, Y = fat pixel position", "outputs": "4 pixels; X, Y preserved", "confidence": "high" }, { "addr": "8518", "name": "drawStatsTab", "summary": "Fills in the STATS tab of the in-game menu (called from $1460/$14D3): text window left 10, colour $61, clears exchangeStepFlag $920E (the refresh request). Rows 9-12: number of living GRUNTS/RIDERS/BOOMERS/SPIES per side, counted by scanning the 50 unit slots down from each comcen (own comcen $9236 in column 9 = US, the other comcen $9236 EOR $52 in column 14 = THEM). Row 14 DRONES ($92AE/$92AF), row 15 MISSILES ($92B0/$92B1), row 17 COMSPEED (comSpeedDisplayTable[$92B4/$92B5]), row 18 TER PTS ($92AC[side EOR 1] under US, $92AC[side] under THEM), row 19 RECYCLER words via printOptionWord ($92A6[side], $92A6[side EOR 1]), row 20 the MAP ID string at $0BA9 in column 9 for side 0, column 14 for side 1.", "inputs": "$9236, $F76C unitTypeFlags, $92AE-$92B5, $92AC, $92A6, $0B9F playerSide, $0BA9", "outputs": "text printed; $920E = 0; zp_18, zp_1A, zp_20, zp_21, zp_3B, zp_3D, zp_3F, zp_40 used", "confidence": "high" }, { "addr": "8601", "name": "printNumberRightAligned", "summary": "Prints the 8-bit value A right-aligned in a 3-character field at text column X of the current row: sets zp_3F = X, computes the number of leading fill characters (0 for >= 100, 1 for 10-99, 2 for < 10) into the self-modified operands at $8622/$8627/$8629, positions the cursor (sub_C099), prints the fill characters with the $C000 overlay routine at $C0EF and the number with sub_C17D (Y = 0 high byte). The CPY #0/BNE at $8607 is dead (Y is always 0).", "inputs": "A = value, X = column", "outputs": "text; zp_3F; operands $8622, $8627, $8629", "confidence": "high" }, { "addr": "862D", "name": "setTextColumn", "summary": "Sets the text cursor column zp_3F = A and repositions the cursor through sub_C099 (X and Y preserved).", "inputs": "A = column relative to textWindowLeft", "outputs": "zp_3F, text cursor pointers", "confidence": "high" }, { "addr": "8632", "name": "drawComcenSchematic", "summary": "Unpacks the RLE-compressed comcen picture at $7C6E straight into the bitmap for the REPAIR tab (called from drawRepairTab $176B): marker = first byte ($13) -> rleMarkerByte $9246, $9247 = 0, fetchRleByte's pointer ($681B/$681C) = $7C6F, destination = bitmap cell (row 10, column 13) from calcCellPointersXY. Emits 10 character rows of 160 bytes (20 characters x 8 lines) - 'marker,count,value' runs (count 0 = 256) may cross row boundaries - advancing zp_B4/zp_B5 by 320 per row. The picture is a side view of the tracked comcen vehicle with dish and antenna.", "inputs": "$7C6E compressed data", "outputs": "bitmap $A000 area rows 10-19 columns 13-32; $9246, $9247, $681B/$681C, zp_1A, zp_1B, zp_B4/zp_B5", "confidence": "high" } ], "variables": [ { "addr": "5E", "scope": "zp", "name": "droneJoyHeading", "meaning": "Desired drone direction in joystickDirectionTable order (0 N, 1 S, 2 E, 3 W, 4 NW, 5 NE, 6 SE, 7 SW); converted with joyDirToHeadingTable to the clockwise heading $920B by launchDrone and the steering routine $731E.", "confidence": "high" }, { "addr": "5D", "scope": "zp", "name": "joystickDirection", "meaning": "Joystick port 2 direction bits (active low, $0F centred); index into joystickDirectionTable $1FA6.", "confidence": "high" }, { "addr": "6C", "scope": "zp", "name": "blinkRepeatTimer", "meaning": "IRQ frame countdown; on the drone screen it paces the LAUNCH/STRIKE box blink (11 frames idle, 40 in flight).", "confidence": "high" }, { "addr": "6E", "scope": "zp", "name": "headingRereadDelay", "meaning": "IRQ frame countdown set to 20 after a joystick heading was accepted; the joystick is re-read for the drone only when it is 0.", "confidence": "high" }, { "addr": "74", "scope": "zp", "name": "cmdParam1", "meaning": "Second byte of an outgoing command; getDroneMapPosition stores the drone column here.", "confidence": "high" }, { "addr": "75", "scope": "zp", "name": "cmdParam2", "meaning": "Third byte of an outgoing command; getDroneMapPosition stores the drone row here.", "confidence": "high" }, { "addr": "91", "scope": "zp", "name": "lineStepsLeft", "meaning": "Steps left in the high-memory line stepper; 0 ends the shot line.", "confidence": "high" }, { "addr": "92", "scope": "zp", "name": "lineCurRow", "meaning": "Line start/current row (attacker row in drawShotLineInViewport, 10 = sprite centre in drawDroneHeadingIndicator).", "confidence": "high" }, { "addr": "93", "scope": "zp", "name": "lineCurCol", "meaning": "Line start/current column (attacker column; 12 = sprite centre for the heading indicator).", "confidence": "high" }, { "addr": "94", "scope": "zp", "name": "lineEndRow", "meaning": "Line end row (target row / heading line end).", "confidence": "high" }, { "addr": "95", "scope": "zp", "name": "lineEndCol", "meaning": "Line end column (target column / heading line end).", "confidence": "high" }, { "addr": "A5", "scope": "zp", "name": "viewOriginCol", "meaning": "Map column of the left edge of the battlefield/drone view (may be negative); the drone screen sets it to comcen column - 6.", "confidence": "high" }, { "addr": "A6", "scope": "zp", "name": "viewOriginRow", "meaning": "Map row of the top edge of the view (may be negative); drone screen sets comcen row - 5.", "confidence": "high" }, { "addr": "B4", "scope": "zp", "name": "bitmapPtr", "meaning": "Bitmap destination pointer (lo) of drawComcenSchematic, from calcCellPointersXY.", "confidence": "high" }, { "addr": "B5", "scope": "zp", "name": "bitmapPtrHi", "meaning": "Bitmap destination pointer (hi).", "confidence": "high" }, { "addr": "B6", "scope": "zp", "name": "screenPtr", "meaning": "Screen RAM row pointer (lo) used by drawShotLineInViewport to whiten the viewport colour nibbles.", "confidence": "high" }, { "addr": "B7", "scope": "zp", "name": "screenPtrHi", "meaning": "Screen RAM row pointer (hi).", "confidence": "high" }, { "addr": "18", "scope": "zp", "name": "unitCountScratch", "meaning": "drawStatsTab: number of living units of the current type on the side being counted.", "confidence": "high" }, { "addr": "1A", "scope": "zp", "name": "loopCounter1A", "meaning": "drawStatsTab: 50-slot unit loop counter; drawComcenSchematic: character rows left (10).", "confidence": "high" }, { "addr": "1B", "scope": "zp", "name": "rleRunLength", "meaning": "drawComcenSchematic: run length of the current RLE run (0 = 256).", "confidence": "high" }, { "addr": "20", "scope": "zp", "name": "statsUnitType", "meaning": "drawStatsTab: unit type currently counted (0 GRUNT .. 3 SPY).", "confidence": "high" }, { "addr": "21", "scope": "zp", "name": "statsSidePass", "meaning": "drawStatsTab: 1 = counting own side (column 9), 0 = opponent (column 14).", "confidence": "high" }, { "addr": "3B", "scope": "zp", "name": "textWindowLeft", "meaning": "Text engine left column (10 for the STATS tab).", "confidence": "high" }, { "addr": "3D", "scope": "zp", "name": "textColour", "meaning": "Text colour byte ($61 on the STATS tab).", "confidence": "high" }, { "addr": "3F", "scope": "zp", "name": "textCursorCol", "meaning": "Text cursor column relative to textWindowLeft (9 = US column, 14 = THEM column).", "confidence": "high" }, { "addr": "40", "scope": "zp", "name": "textCursorRow", "meaning": "Text cursor row (STATS rows 9-20).", "confidence": "high" }, { "addr": "9220", "scope": "abs", "name": "droneFuel", "meaning": "Flight time left of the current drone: 100 at launch, -1 per lock-step exchange (advanceDroneCamera); shown by sub_6F99 as a three-digit read-out whose colour changes below 21 and below 11 and alternates on even values (blink); 0 forces releaseDroneControl. Before a launch it shows 100 if drones remain, else 0.", "confidence": "high" }, { "addr": "9233", "scope": "abs", "name": "dronesLeftDisplay", "meaning": "Drone screen's copy of dronesLeft[own] ($92AE): reloaded on every (re)entry, decremented locally at launch, printed by sub_7036; 0 blocks launching, < 2 makes the drone box blink.", "confidence": "high" }, { "addr": "920B", "scope": "abs", "name": "droneHeading", "meaning": "Drone heading 0-7 clockwise from north; index of the aircraft silhouette (sprite 5) and first byte of command $8D. Set from joyDirToHeadingTable at launch, turned one step per exchange by $731E.", "confidence": "high" }, { "addr": "920D", "scope": "abs", "name": "droneStateFlags", "meaning": "Bit 7 = own drone in flight, bit 6 = steered locally (sendOutgoingPacket emits $8D moves), bit 5 = strike ($8E) pending, bit 0 set by cmdDroneMove. Set to $C0 at launch, &= $3F when the flight ends.", "confidence": "high" }, { "addr": "920E", "scope": "abs", "name": "exchangeStepFlag", "meaning": "Bit 7 set (at $41F8) after each merged command exchange is applied; the drone screen consumes it to advance the camera one step, the STATS tab clears it as its refresh request.", "confidence": "high" }, { "addr": "9236", "scope": "abs", "name": "comcenUnit", "meaning": "Index of the player's comcen unit (49 for side 0, 99 for side 1; EOR $52 gives the other comcen).", "confidence": "high" }, { "addr": "9244", "scope": "abs", "name": "droneSystemStatus", "meaning": "comcenSystemStatus[3] ($9241+3): $80 when the comcen's drone system was damaged by a hit (sub_3D57). Then only 1 in 4 launch presses succeeds and half of the camera steps show static.", "confidence": "high" }, { "addr": "9245", "scope": "abs", "name": "unitInspectLockFlags", "meaning": "Bit 7 = own comcen cloaked; blocks drone launches.", "confidence": "medium" }, { "addr": "92F6", "scope": "abs", "name": "droneViewOriginCol", "meaning": "Map column of the left edge of the 14x12 drone camera window; the drone itself is at +6 (sent in $8D/$8E).", "confidence": "high" }, { "addr": "92F7", "scope": "abs", "name": "droneViewOriginRow", "meaning": "Map row of the top edge of the drone camera window; the drone is at +5.", "confidence": "high" }, { "addr": "92AE", "scope": "abs", "name": "dronesLeft", "meaning": "2 bytes: drones remaining, [0] own, [1] opponent (STATS row DRONES).", "confidence": "high" }, { "addr": "92B0", "scope": "abs", "name": "missilesLeft", "meaning": "2 bytes: missiles remaining, [0] own, [1] opponent (STATS row MISSILES).", "confidence": "high" }, { "addr": "92B4", "scope": "abs", "name": "comcenSpeedClass", "meaning": "2 bytes [0 own, 1 opponent]: comcen mobility class 0-5, shown on the STATS row COMSPEED through comSpeedDisplayTable.", "confidence": "medium" }, { "addr": "92AC", "scope": "abs", "name": "territoryPoints", "meaning": "2 bytes indexed by absolute side: STATS row TER PTS shows [playerSide EOR 1] under US and [playerSide] under THEM (the entry written by each player's own $A5 kind-1 setup command).", "confidence": "medium" }, { "addr": "92A6", "scope": "abs", "name": "recyclerMode", "meaning": "2 bytes indexed by absolute side: 0 FULL, 1 HALF, 2 NONE (STATS row RECYCLER via printOptionWord).", "confidence": "high" }, { "addr": "9234", "scope": "abs", "name": "viewRedrawTimer", "meaning": "Set to $8F by drawShotLineInViewport: the battlefield view is redrawn (erasing the line) when it counts down to $80.", "confidence": "high" }, { "addr": "923D", "scope": "abs", "name": "attackerType", "meaning": "Type of the attacking unit; 2 (Boomer) makes the viewport shot line dotted (3 steps per pixel).", "confidence": "high" }, { "addr": "0BA8", "scope": "abs", "name": "gameOverFlag", "meaning": "Nonzero when the game is over / no game running; ends a drone flight immediately.", "confidence": "medium" }, { "addr": "0B9F", "scope": "abs", "name": "playerSide", "meaning": "Local player's side 0/1; EOR 1 = opponent; selects STATS columns.", "confidence": "high" }, { "addr": "0B9B", "scope": "abs", "name": "playModeFlags", "meaning": "Bit 7 (solo / film playback) suppresses the DRONE ALERT message.", "confidence": "medium" }, { "addr": "0BA9", "scope": "abs", "name": "mapIdText", "meaning": "Map id string (bit 7 terminated) printed on the MAP ID row of the STATS tab.", "confidence": "medium" }, { "addr": "2A2D", "scope": "abs", "name": "windowRow", "meaning": "Row of the fill/XOR rectangle origin (12 LAUNCH box, 16 STRIKE box, 6 camera window).", "confidence": "high" }, { "addr": "2A2E", "scope": "abs", "name": "windowCol", "meaning": "Column of the fill/XOR rectangle origin (34 for the buttons, 13 for the camera window).", "confidence": "high" }, { "addr": "2A29", "scope": "abs", "name": "colourOverride", "meaning": "Nonzero = fixed colour for drawn characters; set to $AA during the drone camera iris animation.", "confidence": "medium" }, { "addr": "2A2A", "scope": "abs", "name": "drawColourByte", "meaning": "Current colour byte for fills/characters ($AA static colour).", "confidence": "medium" }, { "addr": "9010", "scope": "abs", "name": "spriteXShadow", "meaning": "Sprite X shadows; $9010 sprite 0 (heading line, $37), $9012 sprite 2 (reticle, $AC), $9013 sprite 3 (line shadow, $38), $9015 sprite 5 (drone silhouette, $38).", "confidence": "high" }, { "addr": "9018", "scope": "abs", "name": "spriteYShadow", "meaning": "Sprite Y shadows; $9018 sprite 0 ($64), $901A sprite 2 ($88), $901B sprite 3 ($65), $901D sprite 5 ($64).", "confidence": "high" }, { "addr": "9008", "scope": "abs", "name": "spriteColorShadow", "meaning": "Sprite colour shadows; $9008 sprite 0 = 1 white, $900A sprite 2 = 0 black reticle, $900B sprite 3 = $0B/$06 line shadow, $900D sprite 5 = 2 red silhouette.", "confidence": "high" }, { "addr": "9022", "scope": "abs", "name": "spriteEnableShadow", "meaning": "Shadow of $D015; the drone screen enables sprites 0+3 ($09), sprite 2 (4) and sprite 5 via loadDirectionShapeSprite.", "confidence": "high" }, { "addr": "91CC", "scope": "abs", "name": "activeSpriteIndex", "meaning": "Target sprite of loadDirectionShapeSprite; 5 on the drone screen.", "confidence": "high" }, { "addr": "8A00", "scope": "abs", "name": "sprite0Data", "meaning": "Sprite 0 shape; receives a copy of the heading line from sprite 3.", "confidence": "high" }, { "addr": "8A80", "scope": "abs", "name": "sprite2Data", "meaning": "Sprite 2 shape; receives the 64-byte targeting reticle from $FF93 at launch.", "confidence": "high" }, { "addr": "8AC0", "scope": "abs", "name": "sprite3Data", "meaning": "Sprite 3 shape; the heading line is drawn here by drawLineInPathSprite.", "confidence": "high" }, { "addr": "FF93", "scope": "abs", "name": "droneReticleSprite", "meaning": "64-byte sprite shape of the drone camera reticle (four corner brackets, centre ticks), copied into sprite 2 by launchDrone.", "confidence": "high" }, { "addr": "F640", "scope": "abs", "name": "unitCol", "meaning": "Per-unit map column (100 entries).", "confidence": "high" }, { "addr": "F6A4", "scope": "abs", "name": "unitRow", "meaning": "Per-unit map row (100 entries).", "confidence": "high" }, { "addr": "F76C", "scope": "abs", "name": "unitTypeFlags", "meaning": "Per-unit byte: bits 0-2 type (0 GRUNT 1 RIDER 2 BOOMER 3 SPY 4 COMCEN), bit 7 = dead/unused; counted by drawStatsTab.", "confidence": "high" }, { "addr": "9246", "scope": "abs", "name": "rleMarkerByte", "meaning": "Escape byte of the RLE stream being unpacked ($13 for the comcen picture).", "confidence": "high" }, { "addr": "9247", "scope": "abs", "name": "rleFlag9247", "meaning": "Cleared by both RLE unpackers; no reader found.", "confidence": "low" }, { "addr": "681B", "scope": "abs", "name": "rleSrcPtr", "meaning": "Self-modified operand (lo/hi at $681B/$681C) of fetchRleByte; drawComcenSchematic sets it to $7C6F.", "confidence": "high" }, { "addr": "82B5", "scope": "abs", "name": "shotViewLeft", "meaning": "drawShotLineInViewport: viewport left column (rebased, later x8 = fat pixels).", "confidence": "high" }, { "addr": "82B6", "scope": "abs", "name": "shotViewRight", "meaning": "viewport left column + 7 (exclusive right bound, cells).", "confidence": "high" }, { "addr": "82B7", "scope": "abs", "name": "shotViewTop", "meaning": "viewport top row (rebased, later x8).", "confidence": "high" }, { "addr": "82B8", "scope": "abs", "name": "shotViewBottom", "meaning": "viewport top row + 5 (exclusive bottom bound, cells).", "confidence": "high" }, { "addr": "82B9", "scope": "abs", "name": "shotEndpointVisibility", "meaning": "-1 attacker inside the viewport, +2 target inside (1 = both, 0 = neither -> nothing drawn).", "confidence": "high" }, { "addr": "82BA", "scope": "abs", "name": "shotColShift", "meaning": "Offset added to all columns when the view origin column is negative.", "confidence": "high" }, { "addr": "82BB", "scope": "abs", "name": "shotRowShift", "meaning": "Offset added to all rows when the view origin row is negative.", "confidence": "high" }, { "addr": "82BC", "scope": "abs", "name": "shotDashCountdown", "meaning": "Starts at 4: steps with a positive value are not plotted (gap at the shooter), 0 plots the diamond marker, negative plots normally.", "confidence": "high" } ], "dataBlocks": [ { "addr": "7C6E", "length": 864, "type": "unknown", "name": "comcenSchematicRle", "description": "RLE-compressed 160x80 pixel hires picture (20x10 characters, 1600 bytes unpacked) of the comcen vehicle shown on the REPAIR tab. Byte 0 = escape marker $13; stream of literals and 'marker,count,value' runs (count 0 = 256); ends at $7FCD with the run $13,$00,$00 (256 zero bytes). The labels sub_7CA2/7CAE/7CBF/7D70/7D7D/7D8B/7D98/7DA7/7E4F/7E74/7EA0/7F3F/7FA6/7FCB and the L_/D_ labels inside come from overlay A references and have no meaning in variant B." }, { "addr": "7FCE", "length": 8, "type": "byteTable", "name": "joyDirToHeadingTable", "description": "00 04 02 06 07 01 03 05: maps the joystickDirectionTable index (N,S,E,W,NW,NE,SE,SW) to the clockwise heading 0-7 (N,NE,E,SE,S,SW,W,NW) used for sprite shapes and command $8D. Also read by $731E." }, { "addr": "7FD6", "length": 8, "type": "byteTable", "name": "headingLineEndColTable", "description": "0C 13 16 13 0C 05 03 05: end column inside the 24x21 sprite of the heading pointer line for headings 0-7 (centre column 12)." }, { "addr": "7FDE", "length": 8, "type": "byteTable", "name": "headingLineEndRowTable", "description": "00 03 0A 11 14 11 0A 03: end row of the heading pointer line for headings 0-7 (centre row 10)." }, { "addr": "82B5", "length": 8, "type": "byteTable", "name": "shotLineWorkVars", "description": "Eight zero-initialised work bytes of drawShotLineInViewport (see variables $82B5-$82BC)." }, { "addr": "82BD", "length": 2, "type": "unknown", "name": "unusedBytes82BD", "description": "$00,$60: no reference in either variant; filler between the shot-line work bytes and drawShotLineInViewport." }, { "addr": "8512", "length": 6, "type": "byteTable", "name": "comSpeedDisplayTable", "description": "01 02 02 02 00 03: COMSPEED value printed on the STATS tab for comcen speed class 0-5 (class 4 = immobile shows 0)." } ], "misclassified": [ { "addr": "820F", "length": 16, "actual": "code", "evidence": "Bytes A9 07 20 9A C8 AD 9B 0B 30 05 A9 20 20 66 C5 60 = LDA #7 / JSR $C89A / LDA $0B9B / BMI +5 / LDA #$20 / JSR $C566 / RTS; reached by JSR $820F from the (also data-hidden) cmdDroneLaunch handler at $53AF (bytes 20 0F 82). It is the 'DRONE ALERT!' notification." } ], "insights": [ "Overlay B screen 2 (currentScreenId 2, F5 key) is the DRONE screen: entry L_7FE6 from the screen switcher sub_06BA ($06FC), re-entry L_7FF6 from the knock-out sequence ($076A). Layout: left panel with a red aircraft silhouette (sprite 5, shapes from the RLE set at $FE24 = 8 headings clockwise from north) overlaid by a white heading line with a dark shadow (sprites 0 and 3, line drawn into $8AC0 and copied to $8A00), a three-digit fuel read-out (sub_6F99), the drones-left digit (sub_7036, box at row 5 column 33), a 14x12-character camera window at row 6 column 13 (one character per map cell, redrawn by sub_741C, reticle sprite 2 from $FF93 at X $AC Y $88), and two 2x2 buttons at column 34: LAUNCH (row 12) and STRIKE (row 16). Strings 'DRONE','LAUNCH','STRIKE','FIRE','DRONE ALERT' follow the chunk at $868C.", "Drone flight protocol as seen from this screen: fire -> $97 (launch), then sendOutgoingPacket emits $8D heading,col,row every exchange while $920D bit 6 is set (steering toward joyDirToHeadingTable[zp_5E] one 45-degree step per exchange in $731E), fuel $9220 counts 100 exchanges; fire again -> $8E col,row (detonate, bit 5 pending until the own command executes); fuel out / game over -> $8F (released). The drone arriving ($92, cleared bit 7) or a remote detonation ends the flight without $8F. The camera advances only when exchangeStepFlag $920E bit 7 is set, i.e. one step per lock-step exchange, keeping both machines in sync.", "The comcen systems table comcenSystemStatus $9241-$9244 maps to: [1] $9242 overview drawing, [2] $9243 screen 1 (scanner) events, [3] $9244 the drone system. With $9244 = $80 only one launch press in four is accepted (random & 3) and half of the camera steps are replaced by static (fillDroneViewWindow $AA).", "drawShotLineInViewport ($82BF) is the routine that draws shots inside the 7x5-cell zoomed battlefield viewport (2x2 characters per cell, characters row 1-10, columns 25-38, fat-pixel origin X=100,Y=4, 8 fat pixels per cell). It is the only user of plotFatPixel ($FC58). Line: gap of 4 steps at the shooter, a 4-pixel diamond, then a solid (or for Boomers dotted) white line; the viewport colour nibbles are forced to white first and viewRedrawTimer $9234 = $8F schedules the erase 15 frames later. Clipping excludes relative coordinate 0 (left column / top row of the window are never plotted).", "The comcen schematic on the REPAIR tab is an RLE-compressed 20x10 character hires bitmap at $7C6E-$7FCD (same stream format as unpackRleTo0200: first byte = marker, marker,count,value runs, count 0 = 256) unpacked by drawComcenSchematic ($8632) directly into the bitmap at row 10 column 13, 160 bytes per character row with a 320-byte row stride; it shares fetchRleByte ($681A) and rleMarkerByte ($9246) with the main program's unpacker.", "The high-memory block labelled unknownShapeTail at $FF90/$FF93 in the FBB8 survey is actually the 64-byte drone camera reticle sprite copied to sprite 2 ($8A80) by launchDrone ($80EB).", "Many sub_/L_/D_ labels inside $7C6E-$868B (sub_7CA2, sub_7CAE, sub_7CBF, sub_7D70, sub_7D7D, sub_7D8B, sub_7D98, sub_7DA7, sub_7E4F, sub_7E74, sub_7EA0, sub_7F3F, sub_7FA6, sub_7FCB, sub_7FDD, sub_8143, sub_81B6, sub_81F4, sub_8364, sub_84EC, sub_8511, sub_85DB, sub_860B, D_81C8, D_829E, D_8319, D_844F, D_846A, D_8493, D_85EC, D_8622...) are artefacts of XREF merging both overlay variants: they are referenced only by variant A code and are plain data bytes or mid-instruction addresses in variant B. Likewise the main program's JMP L_7DB8 ($0C9D), JMP L_7DE3 ($1C78) and JSR/JMP sub_8658 ($25C4/$6002) target overlay A code; in variant B those addresses are inside the compressed picture / the bitmap unpacker.", "STATS tab layout (drawStatsTab $8518, text window left 10): rows 9-12 GRUNTS/RIDERS/BOOMERS/SPIES counts, 14 DRONES, 15 MISSILES, 17 COMSPEED, 18 TER PTS, 19 RECYCLER, 20 MAP ID; column 9 = US (own side), column 14 = THEM. Unit counting scans the 50 slots downward from each comcen (49 / 99), so the comcen is the highest-numbered unit of each side. Numbers are printed right-aligned in 3 columns by printNumberRightAligned ($8601), which uses the $C000 overlay routines $C0EF (print A fill characters zp_3E) and $C17D (print decimal number)." ] }