4972 lines
328 KiB
ArmAsm
4972 lines
328 KiB
ArmAsm
; ============================================================================
|
|
; $6F00-$87FF - comcen missile and drone screens (track 33 sectors 0-16 + track 32 sectors 8-15)
|
|
; ============================================================================
|
|
; The variant the boot loader installs; $1086 reloads it. Implements the two command centre screens
|
|
; reached with the function keys: the missile screen ($7587) and the drone screen ($7FE6), including the
|
|
; radar rendering, plus the end-of-game result messages.
|
|
|
|
.setcpu "6502"
|
|
.include "c64.inc"
|
|
.include "zeropage.inc"
|
|
|
|
; ---- references to code/data outside this file ----
|
|
switchToRequestedScreen := $06BA
|
|
unitGlyphBaseTable := $0799
|
|
filmPlaybackMode := $0B9B
|
|
playerSide := $0B9F
|
|
gameTypeOptions := $0BA3
|
|
gameEndReason := $0BA8
|
|
checkFirePressed := $0EB1
|
|
checkFirePressedInteractive := $0ED5
|
|
joystickDirectionTable := $1FA6
|
|
diagonalDirectionTable := $1FB2
|
|
directionDeltaTable := $20F0
|
|
getViewportOriginPointer := $25C7
|
|
readMapCell := $25FA
|
|
drawMainWindowFrame := $28E1
|
|
printOptionWord := $2A1A
|
|
useDrawColourFlag := $2A29
|
|
drawColourByte := $2A2A
|
|
blockFillFlag := $2A2B
|
|
blockFillChar := $2A2C
|
|
windowOriginRow := $2A2D
|
|
windowOriginCol := $2A2E
|
|
setBitmapDrawMode := $2A38
|
|
setColourDrawMode := $2A47
|
|
resetDrawModes := $2A56
|
|
fillScreenColourRect := $2A64
|
|
calcCellPointersXY := $2AA3
|
|
drawCharAt := $2AC3
|
|
drawCharBlock := $2B18
|
|
drawWindowInnerCorners := $2B75
|
|
drawTallGlyph := $2BB7
|
|
xorColourBlock2x2 := $2BCE
|
|
drawWindowFrame := $2C7F
|
|
drawFrameBox := $2CA1
|
|
setTerrainTilePtr := $2DBD
|
|
magnifyWindow2x := $2E0F
|
|
clearSpriteShape := $2FB2
|
|
drawLineInPathSprite := $306C
|
|
plotPathSpritePixel := $30A1
|
|
enableSprites := $31A3
|
|
drawCircleInSprite := $3239
|
|
loadDirectionShapeSprite := $3340
|
|
showComcenCrossMarker := $3383
|
|
gridCellToSpriteCoords := $33B1
|
|
getUnitSpriteCentreOnGrid := $340B
|
|
updateExplosionMarker := $341F
|
|
drawComcenRingSprite := $3456
|
|
squaresTable := $3860
|
|
applyHitToUnit := $3B22
|
|
animateRingBurst := $3CF2
|
|
haltStunnedUnit := $3FAC
|
|
runGameFrames := $4094
|
|
updateGameFrame := $40B2
|
|
updateComcenCloakLock := $5151
|
|
queueCmdBytePending := $55EC
|
|
queueCmd3 := $560F
|
|
drawScreen1Cell := $5D7F
|
|
unpackRleTo0200 := $67CB
|
|
fetchRleByte := $681A
|
|
D_681B := $681B
|
|
D_681C := $681C
|
|
sprite0Shape := $8A00
|
|
sprite2Shape := $8A80
|
|
sprite3Shape := $8AC0
|
|
vicBorderShadow := $9003
|
|
vicBg0Shadow := $9004
|
|
spriteColourShadow := $9008
|
|
sprite2ColourShadow := $900A
|
|
sprite3ColourShadow := $900B
|
|
sprite5ColourShadow := $900D
|
|
spriteXShadow := $9010
|
|
sprite1XShadow := $9011
|
|
sprite2XShadow := $9012
|
|
sprite3XShadow := $9013
|
|
sprite5XShadow := $9015
|
|
spriteYShadow := $9018
|
|
sprite1YShadow := $9019
|
|
sprite2YShadow := $901A
|
|
sprite3YShadow := $901B
|
|
sprite5YShadow := $901D
|
|
spriteEnableShadow := $9022
|
|
drawCellSaveCol := $90F6
|
|
drawCellSaveRow := $90F7
|
|
currentScreen := $90FB
|
|
joyRepeatMask := $91C9
|
|
joyHoldCounter := $91CA
|
|
activeSpriteIndex := $91CC
|
|
spritePlotMode := $91F3
|
|
droneHeading := $920B
|
|
droneControlFlags := $920D
|
|
exchangeAppliedFlag := $920E
|
|
radarGhostCountdown := $920F
|
|
radarNoiseMasks := $9210
|
|
droneFuel := $9220
|
|
frameWidth := $9221
|
|
frameHeight := $9222
|
|
droneRadarOwnColour := $9224
|
|
droneRadarEnemyColour := $9225
|
|
cellBackgroundColour := $9226
|
|
comcenStunStatus := $922A
|
|
radarHalfToneFlag := $922D
|
|
radarMissilesLeft := $9232
|
|
radarDronesLeft := $9233
|
|
viewRedrawTimer := $9234
|
|
comcenUnit := $9236
|
|
radarOwnUnitColour := $9237
|
|
radarEnemyUnitColour := $9238
|
|
radarTerrainColour := $9239
|
|
attackerType := $923D
|
|
comcenBattleStatus := $9242
|
|
comcenRadarStatus := $9243
|
|
comcenDroneStatus := $9244
|
|
comcenCloakedFlag := $9245
|
|
rleMarkerByte := $9246
|
|
rleUnpackFlag := $9247
|
|
recyclerMode := $92A6
|
|
terrainPointsBySide := $92AC
|
|
dronesLeft := $92AE
|
|
opponentDronesLeft := $92AF
|
|
missilesLeft := $92B0
|
|
opponentMissilesLeft := $92B1
|
|
comcenSpeedClass := $92B4
|
|
opponentComcenSpeedClass := $92B5
|
|
savedLineState := $92CA
|
|
droneViewOriginCol := $92F6
|
|
droneViewOriginRow := $92F7
|
|
missileFlightTick := $92F8
|
|
droneArrivalFlags := $92F9
|
|
radarSweepTick := $92FB
|
|
bitmapRowLoTableFrom3 := $9303
|
|
bitmapRowLoTableFrom6 := $9306
|
|
bitmapRowHiTableFrom3 := $931C
|
|
bitmapRowHiTableFrom6 := $931F
|
|
bitmapColLoTableFrom3 := $9335
|
|
bitmapColLoTableFrom13 := $933F
|
|
bitmapColHiTableFrom3 := $935D
|
|
bitmapColHiTableFrom13 := $9367
|
|
screenRowLoTable := $9382
|
|
screenRowLoTableFrom3 := $9385
|
|
screenRowLoTableFrom6 := $9388
|
|
screenRowHiTable := $939B
|
|
screenRowHiTableFrom3 := $939E
|
|
screenRowHiTableFrom6 := $93A1
|
|
updateCursorPointers := $C099
|
|
setFullScreenWindow := $C0D7
|
|
bootReceiveSectorData := $C0EF
|
|
printString := $C133
|
|
printDecimal := $C17D
|
|
splitDecimalDigits := $C428
|
|
decimalHundreds := $C44E
|
|
decimalTens := $C44F
|
|
decimalOnes := $C450
|
|
queueMessage := $C566
|
|
redrawStatusMessage := $C5D7
|
|
playSound := $C89A
|
|
unitColTable := $F640
|
|
unitRowTable := $F6A4
|
|
unitFlagsTable := $F708
|
|
unitTypeTable := $F76C
|
|
unitPathColTable := $F898
|
|
unitStunTable := $F960
|
|
unitEnergyTable := $F9C4
|
|
unitTerrainUnderTable := $FAF0
|
|
initLineStepper := $FBB8
|
|
stepLine := $FBFA
|
|
plotFatPixel := $FC58
|
|
nextGameRandom := $FD5D
|
|
droneViewfinderSprite := $FF93
|
|
|
|
; Contents
|
|
; --------
|
|
; $6F00 drawDroneHeadingPanel Draws the compass / heading dial in the top-left corner of the DRONE screen
|
|
; (game screen 2): frame window $0D (columns 1-9, rows 3-11), the 7x7 character picture at $97D1 at row 4 column 2, the
|
|
; 5x5 picture at $97B6 at row 5 column 3, and a 3x3 screen-RAM colour fill of $6E (blue %01 pixels over light-blue %10
|
|
; pixels) at row 6 column 4.
|
|
; $6F36 drawDroneFuelPanel Draws the fuel gauge panel in the bottom-left corner of the DRONE screen: frame
|
|
; window $0E (columns 1-9, rows 12-20), the same 7x7 picture as the compass ($97D1) at row 13 column 2 but forced to
|
|
; colour $BC (dark grey over medium grey), then the three caption characters $57,$58,$59 at row 17 columns 4-6 in colour
|
|
; $0C (black over medium grey).
|
|
; $6F89 drawDroneRadarFrame Draws the double frame around the drone camera window and clears the window:
|
|
; builds a fresh set of radar noise masks, draws window $0F (left 11, top 4, 18x16) and window $10 (left 12, top 5,
|
|
; 16x14), then falls through into clearRadarWindowBlack which blanks the 14x12 camera area and re-centres the view on
|
|
; the player's own comcen.
|
|
; $6F99 drawDroneFuelDigits Prints droneFuel ($9220) as three tall (2-row) digits at rows 15-16, columns
|
|
; 4-6 of the fuel panel.
|
|
; $6FF5 drawDroneCountPanel Draws the 'DRONE' counter panel at the top right of the DRONE screen: resets
|
|
; the text window to the full screen, draws window $11 (left 29, top 3, 11x7) with the four inner corner marks, window
|
|
; $12 (left 32, top 4, 5x4), fills the 3x2 colour box for the digit at row 5 column 33 with $30, and prints the caption
|
|
; 'DRONE' ($868C) at row 7 column 32 in colour $CF with the bitmap draw mode set to OR.
|
|
; $7036 drawDroneCountDigit Draws the number of drones still available (radarDronesLeft $9233) as a single
|
|
; tall digit at rows 5-6, column 34, in the fixed colour $30.
|
|
; $7061 blinkDroneCountBox Alternates the background of the drones-left box (3x2 colour cells at row 5,
|
|
; column 33) between $30 and its nibble-swapped twin $03 according to bit 0 of the frame counter, which swaps the
|
|
; foreground and background of the tall digit sitting in it and makes the counter flash.
|
|
; $708D drawLaunchStrikeButtons Draws the two-button panel down the right edge of the DRONE screen: frame
|
|
; window $13 (left 30, top 10, 10 wide x 11 rows) with inner corner marks, the 2x2 button icon from $9804 (characters
|
|
; $45-$48) at row 12 column 34 painted colour $05, the caption 'LAUNCH' ($8691) at row 14 column 32, the same icon again
|
|
; at row 16 column 34, and the caption 'STRIKE' ($8697) at row 18 column 32.
|
|
; $70F5 buildDroneScreen Paints the whole DRONE screen (game screen 2) from scratch: clears the glyph
|
|
; plotter's parameter block, sets border and background shadows to $0C (medium grey), sets the radar cell background
|
|
; nibble to $0A (light red) and marks the screen as built, then draws every panel in turn and finishes by redrawing the
|
|
; status line through the $C000 overlay.
|
|
; $7135 clearRadarWindowGrey Blanks the 14x12 drone camera window (row 6, column 13) to colour $BB (dark
|
|
; grey on dark grey, i.e.
|
|
; $713C clearRadarWindowBlack Same as clearRadarWindowGrey but with colour 0: blanks the drone camera window
|
|
; to black and re-centres the view on the own comcen.
|
|
; $7163 playDroneLostAnimation The 'drone is gone' effect, used when the drone is shot down, detonated by its
|
|
; own strike or runs out of fuel.
|
|
; $7204 flashRadarAndWipe Paints the whole 14x12 drone camera window white for two game frames, then
|
|
; clears radarWipeColour and falls into drawRadarIrisWipe, so the iris closes over the white flash with no pauses.
|
|
; $7221 drawRadarIrisWipe Closes (or opens) an iris over the drone camera window: it first draws window
|
|
; $16 (the camera area itself, left 13, top 6, 14x12) and then repeatedly draws a rectangular frame out of the all-$03
|
|
; solid tile set at $9848, starting 12x12 cells at row 6 column 14 and shrinking two cells and moving in one cell each
|
|
; pass until the height drops below 3.
|
|
; $727A buildRadarNoiseMasks Fills the 16-byte table radarNoiseMasks ($9210-$921F) with $FF and then knocks
|
|
; four randomly chosen bits out of each byte.
|
|
; $72A1 applyDroneBlast Resolves a drone detonation: damages every unit standing in the 2x2 block of
|
|
; map cells whose top-left corner is (cmdParam1, cmdParam2) - (x,y), (x+1,y), (x+1,y+1) and (x,y+1) - by calling
|
|
; applyDroneBlastToCell four times.
|
|
; $72B3 applyDroneBlastToCell Applies the drone blast to the single map cell (cmdParam1, cmdParam2).
|
|
; $731E steerDrone Advances the locally flown drone by one map cell.
|
|
; $7396 drawRadarGlyph Plots one 8x8 map glyph into the bitmap cell pointed at by bitmapPtr
|
|
; (zp_B4/zp_B5) and writes its colour through screenPtr (zp_B6/zp_B7) at offset Y.
|
|
; $741C drawDroneRadarWindow Redraws the whole 14x12 drone camera window (character rows 6-17, columns
|
|
; 13-26, one character per map cell) from the map around the view origin zp_A5/zp_A6.
|
|
; $74C3 getRadarCellGlyph Turns one map cell byte into the glyph index and colour that drawRadarGlyph
|
|
; needs.
|
|
; $7552 playStrikeZoomAnimation Drone strike effect on the DRONE screen: magnifies the middle of the
|
|
; $7587 runMissileScreen Entry of game screen 1, the comcen RADAR / MISSILE screen (function key F3).
|
|
; $759C missileScreenResume Main loop of the missile screen, and the re-entry point used after the
|
|
; $7688 refreshMissileScreen Redraw hook of screen 1, run once when the screen is built and again after
|
|
; $76B2 moveRadarCursorByDirection Turns a joystick direction code 0-7 into a (dCol,dRow) pair and hands
|
|
; $76EC offsetRadarCursor Moves the missile target cursor by (X,Y) grid cells, refusing the move if it
|
|
; $7713 animateMissileFlight Flies the launched missile across the radar window: builds a short slanted
|
|
; $7847 plotMissileTrailSegment Draws one 4-pixel line segment into the shape of sprite 3, from
|
|
; $785B stepAndPlotTrailPixel Advances the line stepper one cell and sets the resulting pixel in sprite
|
|
; $7861 saveLineStepperState Copies the 12-byte line stepper working set $8C-$97 (both error
|
|
; $786D restoreLineStepperState Copies savedLineState ($92CA-$92D5) back over the 12-byte line stepper
|
|
; $7879 rebasePairToMin Subtracts the smaller of A and Y from both, i.e.
|
|
; $7894 updateRadarSweep Per-frame driver of the radar sweep on screen 1.
|
|
; $78CB restartRadarSweep Starts a new sweep at grid row 0 and immediately: clears the step counter, the
|
|
; $78E7 paintRadarSweepRow Rewrites the 21 video-matrix bytes of one radar grid row (screen row A+3,
|
|
; $7926 redrawEnemyUnitsOnRadar Repaints all 50 units of the opposing side on the radar window and gives
|
|
; $7959 scrollRadarOnComcenMove Entered by JMP from drawScreen1Cell ($5D8A) whenever the cell being
|
|
; $7987 isRadarCursorOnComcen Tests whether the missile target cursor is on the comcen's own cell, which
|
|
; $7992 radarCursorToMapCoords Converts the radar grid position of the target cursor into absolute map
|
|
; $79A1 checkStrikeHitsDrone Run straight after a missile has been launched: if a drone is airborne and
|
|
; $79D8 showDroneKillBurst Plays the 'drone shot down' effect on the radar screen once our $92 command
|
|
; $7A13 mapCellToRadarGrid Converts absolute map coordinates into radar grid coordinates relative to the
|
|
; $7A2B isRadarGridCellInRange Range check for a radar grid cell: the window is 21 columns by 17 rows,
|
|
; $7A32 initRadarScreen Builds the whole RADAR / MISSILE screen (game screen 1): resets the glyph
|
|
; $7A6B redrawRadarMapWindow Draws the double frame around the radar window and then the map inside it.
|
|
; $7A78 drawMissilePanel Draws the MISSILES panel in the bottom right corner: an outer frame with inner
|
|
; $7ABA drawMissileCount Shows the number of missiles left as two tall (two-character-high) digits in
|
|
; $7AEC blinkMissileCount Swaps the two colour nibbles of the missile counter box, which exchanges the
|
|
; $7B05 drawFireButton Draws the FIRE button panel on the right: a framed box with inner corner marks,
|
|
; $7B44 drawDroneAlertPanel Draws the three nested frames of the DRONE ALERT panel in the top right
|
|
; $7B59 resetDroneAlertPanel Prints the two-line 'DRONE' / 'ALERT' label into the panel and paints its
|
|
; $7B86 flashDroneAlertPanel Toggles the DRONE ALERT panel between its two colour states and repaints the
|
|
; $7B91 fillDroneAlertPanel Shared tail of resetDroneAlertPanel and flashDroneAlertPanel: fills the 7 x 4
|
|
; $7BA2 drawRadarMap Redraws the whole 21 x 17 cell strategic map of screen 1 (the RADAR/MISSILE
|
|
; screen)
|
|
; $7C5C getComcenCentredViewOrigin Returns the map origin that puts the player's own comcen in the middle
|
|
; $7FE6 enterDroneScreen Entry point of game screen 2, the DRONE screen (F5).
|
|
; $7FF6 resetDroneScreen (Re)initialises the drone screen's sprites and read-outs and drops into the
|
|
; idle
|
|
; $803D droneScreenIdleLoop Main loop of the DRONE screen while nothing is flying.
|
|
; $808D launchDrone Sends a drone on its way: latches the joystick heading, queues command $97
|
|
; (drone
|
|
; $8124 droneFlightLoop Per-frame loop while our drone is in the air.
|
|
; $8182 strikeWithDrone Fire pressed while the drone is flying: stop steering it, mark a detonation as
|
|
; $81D0 endDroneFlight Common tail of every drone flight: explosion sound, empty the tank, clear the
|
|
; $81FE getDroneMapPosition Converts the camera origin into the drone's own map cell: the 14 x 12 window
|
|
; $820F showDroneAlert Notification that the opponent has launched a drone: plays the alert sound and,
|
|
; in
|
|
; $821F fillDroneViewWindow Fills the drone camera window (14 columns x 12 rows starting at character row
|
|
; $8230 releaseDroneControl Gives an airborne drone back to the engine: clears the 'airborne' and 'locally
|
|
; $823F drawDroneHeadingIndicator Redraws the compass pointer of the drone screen's left panel.
|
|
; $8279 advanceDroneCamera One drone step on the DRONE screen, run once per lock-step command exchange.
|
|
; $82A4 readDroneHeadingFromJoystick Latches the joystick direction as the drone's new steer direction and
|
|
; $82BF drawShotLineInViewport Draws the tracer of one shot inside the 7 x 5 cell zoomed battlefield
|
|
; $840F rebaseLineCoordsToMin Subtracts the smallest of the three column values (window left $82B5,
|
|
; $84B2 scaleLineCoordsToFatPixels Converts the two line endpoints from map cells to fat pixels: row*8+4
|
|
; $84F1 plotDiamondMarker Plots the four colour-1 fat pixels at (X-1,Y-1), (X+1,Y-1), (X-1,Y+1) and
|
|
; $8518 drawStatsTab Fills in the numbers of the console's STATS panel.
|
|
; $8601 printNumberRightAligned prints the unsigned byte in A as decimal, right-aligned in a three-character
|
|
; field that starts at text column X of the current text row.
|
|
; $862D setTextColumn moves the text cursor to column A of the current row and recomputes the bitmap
|
|
; and screen-RAM pointers for that character cell by tail-calling updateCursorPointers ($C099), which leaves X and Y
|
|
; untouched.
|
|
; $8632 drawComcenSchematic unpacks the RLE-compressed comcen picture at comcenSchematicRle ($7C6E)
|
|
; straight into the hires bitmap for the console REPAIR tab: a 20x10 character (160x80 pixel) side view of the tracked
|
|
; comcen vehicle with its dish and antenna, drawn at character row 10, column 13.
|
|
|
|
.org $6F00
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawDroneHeadingPanel - Draws the compass / heading dial in the top-left corner of the DRONE screen
|
|
; (game screen 2): frame window $0D (columns 1-9, rows 3-11), the 7x7 character picture at $97D1 at
|
|
; row 4 column 2, the 5x5 picture at $97B6 at row 5 column 3, and a 3x3 screen-RAM colour fill of $6E
|
|
; (blue %01 pixels over light-blue %10 pixels) at row 6 column 4. Sprites 3 and 0 (the heading line
|
|
; plus its white shadow) and sprite 5 (the red aircraft silhouette) are parked over this panel by the
|
|
; screen-2 entry code, so the panel is only the dial they sit in.
|
|
; In: nothing; expects the glyph-plotter parameter block $2A29-$2A2C to be all zero (buildDroneScreen
|
|
; clears it just before)
|
|
; Out: frame, two character pictures and 6 colour cells drawn; D_2A2D = 6 and D_2A2E = 4 left pointing
|
|
; at the colour patch; frameWidth/frameHeight ($9221/$9222) left at 5x5 by the last drawCharBlock
|
|
; Called from: buildDroneScreen ($7122) - the only caller in this overlay variant
|
|
; ----------------------------------------------------------------------
|
|
drawDroneHeadingPanel:
|
|
ldx #$0D ; 6F00 window $0D = left 1, top 3, 9x9 - the compass panel outline
|
|
jsr drawWindowFrame ; 6F02 draw the panel frame from that window's 9-tile set
|
|
lda #$04 ; 6F05 character-block origin row 4 (one row inside the frame top)
|
|
sta windowOriginRow ; 6F07 D_2A2D is the glyph plotters' window row
|
|
lda #$02 ; 6F0A character-block origin column 2 (one column inside the frame left)
|
|
sta windowOriginCol ; 6F0C D_2A2E is the glyph plotters' window column
|
|
lda #$D1 ; 6F0F low byte of the picture block at $97D1 (header 7,7 then 49 character codes)
|
|
ldy #$97 ; 6F11 high byte -> $97D1: the 7x7 outer dial face
|
|
jsr drawCharBlock ; 6F13 stamp it over rows 4-10, columns 2-8
|
|
lda #$05 ; 6F16 next block starts one row lower, row 5
|
|
sta windowOriginRow ; 6F18 set the window row for the inner picture
|
|
lda #$03 ; 6F1B and one column further right, column 3
|
|
sta windowOriginCol ; 6F1D set the window column
|
|
lda #$B6 ; 6F20 low byte of the picture block at $97B6 (header 5,5 then 25 character codes)
|
|
ldy #$97 ; 6F22 high byte -> $97B6: the 5x5 inner dial face
|
|
jsr drawCharBlock ; 6F24 stamp it over rows 5-9, columns 3-7
|
|
inc windowOriginRow ; 6F27 step the fill origin down to row 6
|
|
L_6F2A:
|
|
inc windowOriginCol ; 6F2A and right to column 4 - the 3x3 centre of the dial (label L_6F2A is a variant-A branch target only)
|
|
lda #$6E ; 6F2D screen-RAM colour byte $6E: %01 pixels blue, %10 pixels light blue
|
|
ldx #$03 ; 6F2F 3 columns wide
|
|
ldy #$03 ; 6F31 3 rows high
|
|
jmp fillScreenColourRect ; 6F33 paint rows 6-8, columns 4-6 and return through fillScreenColourRect
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawDroneFuelPanel - Draws the fuel gauge panel in the bottom-left corner of the DRONE screen: frame
|
|
; window $0E (columns 1-9, rows 12-20), the same 7x7 picture as the compass ($97D1) at row 13 column 2
|
|
; but forced to colour $BC (dark grey over medium grey), then the three caption characters $57,$58,$59
|
|
; at row 17 columns 4-6 in colour $0C (black over medium grey). drawDroneFuelDigits writes the three
|
|
; tall digits into rows 15-16 of the same panel.
|
|
; In: nothing
|
|
; Out: frame, picture and caption drawn; the fixed-colour override textDrawParams ($2A29) and D_2A2A
|
|
; are cleared again on exit; D_2A2B = 0 (block-fill off); zp_18 = 17, zp_19 = 6
|
|
; Called from: buildDroneScreen ($7125) only
|
|
; ----------------------------------------------------------------------
|
|
drawDroneFuelPanel:
|
|
ldx #$0E ; 6F36 window $0E = left 1, top 12, 9x9 - the fuel panel outline
|
|
jsr drawWindowFrame ; 6F38 draw its frame
|
|
lda #$FF ; 6F3B non-zero = 'use the fixed colour in D_2A2A' instead of the per-character colour table $93B4
|
|
sta useDrawColourFlag ; 6F3D textDrawParams ($2A29) is the plotter's useFixedColour flag
|
|
lda #$BC ; 6F40 screen-RAM colour $BC: %01 pixels dark grey, %10 pixels medium grey
|
|
sta drawColourByte ; 6F42 D_2A2A is the fixed colour byte used by every glyph plotted from now on
|
|
lda #$0D ; 6F45 character-block origin row 13
|
|
sta windowOriginRow ; 6F47 set the window row
|
|
lda #$02 ; 6F4A character-block origin column 2
|
|
sta windowOriginCol ; 6F4C set the window column
|
|
lda #$D1 ; 6F4F low byte of the 7x7 picture at $97D1
|
|
ldy #$97 ; 6F51 high byte -> $97D1, the same dial art reused as the gauge body
|
|
jsr drawCharBlock ; 6F53 stamp it over rows 13-19, columns 2-8
|
|
lda #$00 ; 6F56 0 = do not replace the block characters with a fill character
|
|
sta blockFillFlag ; 6F58 D_2A2B is drawCharBlock's block-fill flag
|
|
lda #$FF ; 6F5B keep the fixed-colour override on for the caption too
|
|
sta useDrawColourFlag ; 6F5D textDrawParams = $FF
|
|
lda #$0C ; 6F60 screen-RAM colour $0C: %01 pixels black, %10 pixels medium grey
|
|
sta drawColourByte ; 6F62 fixed colour for the caption characters
|
|
lda #$11 ; 6F65 caption row 17 (two rows below the digits at rows 15-16)
|
|
sta scratch18 ; 6F67 zp_18 is drawCharAt's cell row
|
|
lda #$04 ; 6F69 caption starts at column 4, under the leftmost digit
|
|
sta scratch19 ; 6F6B zp_19 is drawCharAt's cell column
|
|
lda #$57 ; 6F6D caption glyph $57 (first third of the 3-character 'fuel' legend)
|
|
jsr drawCharAt ; 6F6F plot it at row 17, column 4
|
|
inc scratch19 ; 6F72 advance to column 5
|
|
lda #$58 ; 6F74 caption glyph $58
|
|
jsr drawCharAt ; 6F76 plot it at row 17, column 5
|
|
inc scratch19 ; 6F79 advance to column 6
|
|
lda #$59 ; 6F7B caption glyph $59
|
|
jsr drawCharAt ; 6F7D plot it at row 17, column 6
|
|
lda #$00 ; 6F80 release the colour override again...
|
|
sta useDrawColourFlag ; 6F82 ...so later text uses the per-character colour table
|
|
sta drawColourByte ; 6F85 and clear the fixed colour byte
|
|
rts ; 6F88 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawDroneRadarFrame - Draws the double frame around the drone camera window and clears the window:
|
|
; builds a fresh set of radar noise masks, draws window $0F (left 11, top 4, 18x16) and window $10
|
|
; (left 12, top 5, 16x14), then falls through into clearRadarWindowBlack which blanks the 14x12 camera
|
|
; area and re-centres the view on the player's own comcen.
|
|
; In: comcenUnit ($9236), unit column/row arrays
|
|
; Out: two frames drawn; radarNoiseMasks ($9210-$921F) re-randomised; camera area filled with colour
|
|
; 0; radarGhostCountdown ($920F) = 0; viewOriginCol/Row (zp_A5/zp_A6) = comcen - (6,5);
|
|
; radarWipeColour ($7134) = 0
|
|
; Called from: buildDroneScreen ($712B) only
|
|
; ----------------------------------------------------------------------
|
|
drawDroneRadarFrame:
|
|
jsr buildRadarNoiseMasks ; 6F89 re-roll the 16 noise masks used for unspotted enemy blips
|
|
ldx #$0F ; 6F8C window $0F = left 11, top 4, 18 wide x 16 rows - outer camera bezel
|
|
L_6F8E:
|
|
jsr drawWindowFrame ; 6F8E draw it (label L_6F8E is a variant-A branch target only)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; The label at $6F91 is a variant-A entry point (serviceLinkWhileGenerating) that XREF merges into
|
|
; this file; in overlay variant B it is just the next instruction of drawDroneRadarFrame.
|
|
; ----------------------------------------------------------------------
|
|
drawRadarInnerBezel:
|
|
ldx #$10 ; 6F91 window $10 = left 12, top 5, 16 wide x 14 rows - inner camera bezel
|
|
jsr drawWindowFrame ; 6F93 draw it
|
|
jmp clearRadarWindowBlack ; 6F96 blank the camera area and centre the view on the own comcen; returns from there
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawDroneFuelDigits - Prints droneFuel ($9220) as three tall (2-row) digits at rows 15-16, columns
|
|
; 4-6 of the fuel panel. splitDecimalDigits splits the value into hundreds ($C44E), tens ($C44F) and
|
|
; ones ($C450) and drawTallGlyph draws each digit as a pair of characters from the glyph-pair table at
|
|
; $9810. The colour is a fuel warning: $30 (cyan over black) above 20, $B7 (dark grey over yellow) for
|
|
; 11-20, $21 (red over white) for 10 or less; below 21 the two nibbles are swapped on even fuel
|
|
; values, so the read-out flashes as it counts down towards empty.
|
|
; In: droneFuel ($9220), 0-100
|
|
; Out: six characters drawn; $C44E-$C450 = the decimal digits; textDrawParams/D_2A2A cleared on exit;
|
|
; zp_18 = 15, zp_19 = 6, zp_1A used as the nibble-rotate carry buffer
|
|
; Called from: buildDroneScreen ($7128), playDroneLostAnimation ($71FC, fuel forced to 0), launchDrone
|
|
; ($80B5, fuel set to 100), endDroneFlight ($81DA) and advanceDroneCamera ($82A1, once per
|
|
; exchange)
|
|
; ----------------------------------------------------------------------
|
|
drawDroneFuelDigits:
|
|
lda droneFuel ; 6F99 current fuel, counted down one per command exchange
|
|
jsr splitDecimalDigits ; 6F9C split it into hundreds/tens/ones at $C44E/$C44F/$C450
|
|
lda #$0F ; 6F9F digits go on row 15 (drawTallGlyph also uses row 16)
|
|
sta scratch18 ; 6FA1 zp_18 = cell row
|
|
lda #$04 ; 6FA3 leftmost digit column 4
|
|
sta scratch19 ; 6FA5 zp_19 = cell column
|
|
lda #$FF ; 6FA7 force the fixed draw colour instead of the per-character table
|
|
sta useDrawColourFlag ; 6FA9 textDrawParams = $FF
|
|
ldx #$30 ; 6FAC default colour $30: %01 cyan, %10 black - plenty of fuel
|
|
lda droneFuel ; 6FAE reload the fuel level for the range tests
|
|
cmp #$15 ; 6FB1 21 or more?
|
|
bcs L_6FD3 ; 6FB3 yes - keep $30 and skip the flashing logic entirely
|
|
ldx #$B7 ; 6FB5 warning colour $B7: %01 dark grey, %10 yellow
|
|
cmp #$0B ; 6FB7 11 or more?
|
|
bcs L_6FBD ; 6FB9 yes - keep $B7
|
|
ldx #$21 ; 6FBB critical colour $21: %01 red, %10 white
|
|
L_6FBD:
|
|
and #$01 ; 6FBD test the fuel level's parity (A still holds the fuel)
|
|
bne L_6FD3 ; 6FBF odd exchange - draw in the plain colour
|
|
sta scratch1A ; 6FC1 even exchange: A is 0 here, so this clears the rotate buffer
|
|
txa ; 6FC3 take the colour byte into A for the nibble swap
|
|
L_6FC4:
|
|
asl a ; 6FC4 shift the colour left one bit...
|
|
rol scratch1A ; 6FC5 ...catching the bit that falls out in zp_1A (rotate left through 8 bits)
|
|
asl a ; 6FC7 bit 2 of 4
|
|
rol scratch1A ; 6FC8 collect it
|
|
asl a ; 6FCA bit 3 of 4
|
|
L_6FCB:
|
|
rol scratch1A ; 6FCB collect it
|
|
asl a ; 6FCD bit 4 of 4 - the byte has now rotated left by four
|
|
rol scratch1A ; 6FCE collect the last carried bit
|
|
ora scratch1A ; 6FD0 merge the two halves: the nibbles are now swapped
|
|
L_6FD2:
|
|
tax ; 6FD2 back into X as the colour byte
|
|
L_6FD3:
|
|
stx drawColourByte ; 6FD3 install the chosen colour as the plotter's fixed colour
|
|
lda decimalHundreds ; 6FD6 hundreds digit (1 only when fuel is 100)
|
|
jsr drawTallGlyph ; 6FD9 draw it as a 2-row glyph pair at row 15/16, column 4
|
|
inc scratch19 ; 6FDC move to column 5
|
|
L_6FDE:
|
|
lda decimalTens ; 6FDE tens digit
|
|
jsr drawTallGlyph ; 6FE1 draw it at column 5
|
|
inc scratch19 ; 6FE4 move to column 6
|
|
lda decimalOnes ; 6FE6 ones digit
|
|
jsr drawTallGlyph ; 6FE9 draw it at column 6
|
|
lda #$00 ; 6FEC restore the normal colour handling...
|
|
sta useDrawColourFlag ; 6FEE ...clear the fixed-colour flag...
|
|
sta drawColourByte ; 6FF1 ...and the fixed colour byte
|
|
rts ; 6FF4 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawDroneCountPanel - Draws the 'DRONE' counter panel at the top right of the DRONE screen: resets
|
|
; the text window to the full screen, draws window $11 (left 29, top 3, 11x7) with the four inner
|
|
; corner marks, window $12 (left 32, top 4, 5x4), fills the 3x2 colour box for the digit at row 5
|
|
; column 33 with $30, and prints the caption 'DRONE' ($868C) at row 7 column 32 in colour $CF with the
|
|
; bitmap draw mode set to OR.
|
|
; In: nothing
|
|
; Out: frames, colour box and caption drawn; textColour zp_3D = $CF, textCursorCol zp_3F = 32,
|
|
; textCursorRow zp_40 = 7; both draw modes reset to replace on exit
|
|
; Called from: buildDroneScreen ($7119) only
|
|
; ----------------------------------------------------------------------
|
|
drawDroneCountPanel:
|
|
jsr setFullScreenWindow ; 6FF5 text window = whole 40x25 screen so the absolute column below is not shifted
|
|
ldx #$11 ; 6FF8 window $11 = left 29, top 3, 11 wide x 7 rows - outer counter panel
|
|
jsr drawWindowFrame ; 6FFA draw its frame
|
|
jsr drawWindowInnerCorners ; 6FFD add character $38 just inside the four corners of that frame
|
|
ldx #$12 ; 7000 window $12 = left 32, top 4, 5 wide x 4 rows - the box around the digit
|
|
jsr drawWindowFrame ; 7002 draw its frame
|
|
lda #$05 ; 7005 colour fill origin row 5
|
|
sta windowOriginRow ; 7007 D_2A2D = window row
|
|
lda #$21 ; 700A colour fill origin column 33
|
|
sta windowOriginCol ; 700C D_2A2E = window column
|
|
lda #$30 ; 700F screen-RAM colour $30: %01 cyan, %10 black - the digit's background
|
|
ldx #$03 ; 7011 3 columns
|
|
ldy #$02 ; 7013 2 rows (the tall digit is 2 cells high)
|
|
jsr fillScreenColourRect ; 7015 paint rows 5-6, columns 33-35
|
|
lda #$CF ; 7018 colour RAM value for the caption ($F = light grey for the %11 pixels)
|
|
sta textColour ; 701A zp_3D is the colour the $C000 text plotter writes per cell
|
|
printDroneCaption:
|
|
ldy #$01 ; 701C bitmap draw mode 1 = OR the glyph into the existing bitmap (label is a variant-A entry point only)
|
|
jsr setBitmapDrawMode ; 701E patch the plotter's read instruction accordingly
|
|
lda #$20 ; 7021 caption column 32
|
|
sta textCursorCol ; 7023 zp_3F = cursor column, relative to the window left edge (0 here)
|
|
lda #$07 ; 7025 caption row 7 (the bottom frame row of window $12)
|
|
L_7027:
|
|
sta textCursorRow ; 7027 zp_40 = cursor row
|
|
jsr updateCursorPointers ; 7029 recompute the bitmap and colour-RAM pointers for that cell
|
|
lda #$8C ; 702C low byte of the string 'DRONE' at $868C
|
|
ldy #$86 ; 702E high byte -> $868C
|
|
jsr printString ; 7030 print it (strings end on a byte with bit 7 set)
|
|
jmp resetDrawModes ; 7033 put both draw modes back to plain replace and return
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawDroneCountDigit - Draws the number of drones still available (radarDronesLeft $9233) as a single
|
|
; tall digit at rows 5-6, column 34, in the fixed colour $30. When no drones are left the colour byte
|
|
; is forced to 0, which makes the digit the same colour as its background, i.e. invisible.
|
|
; In: radarDronesLeft ($9233), 0-9
|
|
; Out: one glyph pair drawn; textDrawParams/D_2A2A cleared on exit; zp_18 = 5, zp_19 = 34
|
|
; Called from: buildDroneScreen ($711C) and launchDrone ($80BB) after the counter is decremented
|
|
; ----------------------------------------------------------------------
|
|
drawDroneCountDigit:
|
|
lda #$05 ; 7036 digit row 5
|
|
sta scratch18 ; 7038 zp_18 = cell row
|
|
lda #$22 ; 703A digit column 34 (centre of the 33-35 colour box)
|
|
sta scratch19 ; 703C zp_19 = cell column
|
|
lda #$FF ; 703E use the fixed colour in D_2A2A...
|
|
sta useDrawColourFlag ; 7040 ...rather than the per-character colour table
|
|
ldx #$30 ; 7043 normal digit colour $30 (%01 cyan, %10 black)
|
|
lda radarDronesLeft ; 7045 drones left
|
|
cmp #$01 ; 7048 at least one left?
|
|
bcs L_704D ; 704A yes - keep colour $30
|
|
tax ; 704C none left: colour byte 0, so the glyph vanishes into the background
|
|
L_704D:
|
|
stx drawColourByte ; 704D install the colour
|
|
lda radarDronesLeft ; 7050 reload the count - it is also the glyph-pair index for drawTallGlyph
|
|
bne L_7055 ; 7053 no-op branch: both paths continue at $7055 (probably a leftover leading-zero test)
|
|
L_7055:
|
|
jsr drawTallGlyph ; 7055 draw the digit as a 2-row glyph pair at rows 5-6, column 34
|
|
lda #$00 ; 7058 restore normal colour handling...
|
|
sta useDrawColourFlag ; 705A ...clear the fixed-colour flag...
|
|
sta drawColourByte ; 705D ...and the fixed colour
|
|
rts ; 7060 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; blinkDroneCountBox - Alternates the background of the drones-left box (3x2 colour cells at row 5,
|
|
; column 33) between $30 and its nibble-swapped twin $03 according to bit 0 of the frame counter,
|
|
; which swaps the foreground and background of the tall digit sitting in it and makes the counter
|
|
; flash.
|
|
; In: frameCounter (zp_45) bit 0
|
|
; Out: 6 screen-RAM colour cells rewritten; D_2A2D = 5, D_2A2E = 33, D_2A2A = the colour used; zp_1A
|
|
; used as the rotate buffer
|
|
; Called from: droneScreenIdleLoop ($8067, every 11 frames) and droneFlightLoop ($813E, every 40
|
|
; frames), in both cases only while fewer than two drones remain
|
|
; ----------------------------------------------------------------------
|
|
blinkDroneCountBox:
|
|
lda #$05 ; 7061 colour fill origin row 5
|
|
sta windowOriginRow ; 7063 D_2A2D = window row
|
|
lda #$21 ; 7066 colour fill origin column 33
|
|
sta windowOriginCol ; 7068 D_2A2E = window column
|
|
L_706B:
|
|
ldx #$30 ; 706B base colour $30 (%01 cyan, %10 black)
|
|
lda frameCounter ; 706D free-running frame counter maintained by the raster IRQ
|
|
and #$01 ; 706F look at its parity
|
|
bne L_7085 ; 7071 odd frame - use $30 unchanged
|
|
sta scratch1A ; 7073 even frame: A is 0, so this clears the rotate buffer
|
|
txa ; 7075 colour byte into A for the nibble swap
|
|
asl a ; 7076 rotate the byte left by four bits, one shift...
|
|
rol scratch1A ; 7077 ...catching the bit that leaves the byte
|
|
asl a ; 7079 shift 2
|
|
rol scratch1A ; 707A catch
|
|
asl a ; 707C shift 3
|
|
rol scratch1A ; 707D catch
|
|
asl a ; 707F shift 4
|
|
rol scratch1A ; 7080 catch
|
|
ora scratch1A ; 7082 merge - $30 has become $03 (%01 black, %10 cyan)
|
|
tax ; 7084 keep it in X
|
|
L_7085:
|
|
txa ; 7085 colour byte back into A for the fill call
|
|
ldx #$03 ; 7086 3 columns (33-35)
|
|
L_7088:
|
|
ldy #$02 ; 7088 2 rows (5-6)
|
|
jmp fillScreenColourRect ; 708A repaint the box and return through fillScreenColourRect
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawLaunchStrikeButtons - Draws the two-button panel down the right edge of the DRONE screen: frame
|
|
; window $13 (left 30, top 10, 10 wide x 11 rows) with inner corner marks, the 2x2 button icon from
|
|
; $9804 (characters $45-$48) at row 12 column 34 painted colour $05, the caption 'LAUNCH' ($8691) at
|
|
; row 14 column 32, the same icon again at row 16 column 34, and the caption 'STRIKE' ($8697) at row
|
|
; 18 column 32. The screen-2 loops afterwards recolour these two 2x2 icon blocks: colour $05 when
|
|
; LAUNCH is pressed, colour $02 when STRIKE is pressed and an XOR of $08 for the idle blink.
|
|
; In: nothing
|
|
; Out: frame, two icons and two captions drawn; zp_3D/zp_3F/zp_40 left at $C7/32/18; both draw modes
|
|
; reset to replace
|
|
; Called from: buildDroneScreen ($711F) only
|
|
; Note: the survey's window geometry was wrong - windowLeftTable[$13] = 30 and windowWidthTable[$13] =
|
|
; 10, not left 29 / width 14.
|
|
; ----------------------------------------------------------------------
|
|
drawLaunchStrikeButtons:
|
|
ldx #$13 ; 708D window $13 = left 30, top 10, 10 wide x 11 rows - the button panel
|
|
jsr drawWindowFrame ; 708F draw its frame
|
|
jsr drawWindowInnerCorners ; 7092 add the four inner corner marks (character $38)
|
|
lda #$0C ; 7095 LAUNCH icon row 12
|
|
setLaunchIconOrigin:
|
|
sta windowOriginRow ; 7097 D_2A2D = window row (label is a variant-A entry point only)
|
|
lda #$22 ; 709A LAUNCH icon column 34
|
|
sta windowOriginCol ; 709C D_2A2E = window column
|
|
L_709F:
|
|
lda #$04 ; 709F low byte of the 2x2 icon block at $9804 (header 2,2 then characters $45,$46,$47,$48)
|
|
ldy #$98 ; 70A1 high byte -> $9804
|
|
jsr drawCharBlock ; 70A3 stamp the icon at rows 12-13, columns 34-35
|
|
lda #$05 ; 70A6 screen-RAM colour $05: %01 black, %10 green - the unpressed button
|
|
ldx #$02 ; 70A8 2 columns
|
|
ldy #$02 ; 70AA 2 rows
|
|
L_70AC:
|
|
jsr fillScreenColourRect ; 70AC paint the icon's 4 colour cells
|
|
lda #$CD ; 70AF colour RAM value for the caption ($D = light green for the %11 pixels)
|
|
sta textColour ; 70B1 zp_3D = text colour
|
|
jsr setFullScreenWindow ; 70B3 text window = whole screen so column 32 is absolute
|
|
lda #$20 ; 70B6 caption column 32
|
|
sta textCursorCol ; 70B8 zp_3F = cursor column
|
|
lda #$0E ; 70BA caption row 14, just under the icon
|
|
sta textCursorRow ; 70BC zp_40 = cursor row
|
|
jsr updateCursorPointers ; 70BE recompute the cell pointers
|
|
lda #$91 ; 70C1 low byte of 'LAUNCH' at $8691
|
|
ldy #$86 ; 70C3 high byte -> $8691
|
|
jsr printString ; 70C5 print it
|
|
lda #$10 ; 70C8 STRIKE icon row 16
|
|
sta windowOriginRow ; 70CA D_2A2D = window row
|
|
lda #$22 ; 70CD STRIKE icon column 34
|
|
sta windowOriginCol ; 70CF D_2A2E = window column
|
|
lda #$04 ; 70D2 the same 2x2 icon block at $9804...
|
|
ldy #$98 ; 70D4 ...high byte
|
|
jsr drawCharBlock ; 70D6 stamp it at rows 16-17, columns 34-35 (its colour cells are left as the frame drew them until a strike is armed)
|
|
printStrikeCaption:
|
|
lda #$C7 ; 70D9 colour RAM value for the caption (7 = yellow for the %11 pixels) (label is a variant-A entry point only)
|
|
sta textColour ; 70DB zp_3D = text colour
|
|
jsr setFullScreenWindow ; 70DD text window = whole screen
|
|
lda #$20 ; 70E0 caption column 32
|
|
sta textCursorCol ; 70E2 zp_3F = cursor column
|
|
lda #$12 ; 70E4 caption row 18
|
|
sta textCursorRow ; 70E6 zp_40 = cursor row
|
|
jsr updateCursorPointers ; 70E8 recompute the cell pointers
|
|
lda #$97 ; 70EB low byte of 'STRIKE' at $8697
|
|
ldy #$86 ; 70ED high byte -> $8697
|
|
jsr printString ; 70EF print it
|
|
jmp resetDrawModes ; 70F2 restore both draw modes and return
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; buildDroneScreen - Paints the whole DRONE screen (game screen 2) from scratch: clears the glyph
|
|
; plotter's parameter block, sets border and background shadows to $0C (medium grey), sets the radar
|
|
; cell background nibble to $0A (light red) and marks the screen as built, then draws every panel in
|
|
; turn and finishes by redrawing the status line through the $C000 overlay.
|
|
; In: droneFuel ($9220), radarDronesLeft ($9233), comcenUnit ($9236)
|
|
; Out: complete screen 2; vicBorderShadow/vicBg0Shadow ($9003/$9004) = $0C, cellBackgroundColour
|
|
; ($9226) = $0A, screenBuiltFlag (zp_41) = 1, $2A29-$2A2C = 0; jumps away into redrawStatusMessage
|
|
; Called from: resetDroneScreen ($7FF3), which is entered from the screen-2 entry $7FE6 and from the
|
|
; comcen knock-out sequence at $076A
|
|
; ----------------------------------------------------------------------
|
|
buildDroneScreen:
|
|
lda #$00 ; 70F5 clear the glyph plotter parameter block...
|
|
sta useDrawColourFlag ; 70F7 ...useFixedColour flag off
|
|
sta drawColourByte ; 70FA ...fixed colour byte 0
|
|
sta blockFillFlag ; 70FD ...block-fill flag off
|
|
sta blockFillChar ; 7100 ...block-fill character 0
|
|
lda #$0C ; 7103 $0C = medium grey
|
|
sta vicBg0Shadow ; 7105 background colour 0 shadow (written to $D021 by the raster IRQ)
|
|
sta vicBorderShadow ; 7108 border colour shadow ($D020)
|
|
lda #$0A ; 710B $0A = light red
|
|
sta cellBackgroundColour ; 710D cellBackgroundColour supplies the low nibble (%10 pixels) of every radar cell on this screen
|
|
lda #$01 ; 7110 every screen builder stores 1 here
|
|
L_7112:
|
|
sta screenBuiltFlag ; 7112 zp_41 screenBuiltFlag; nothing ever reads it
|
|
lda #$00 ; 7114 redundant - D_2A2B was already cleared at $70FD
|
|
sta blockFillFlag ; 7116 block-fill flag off (again)
|
|
jsr drawDroneCountPanel ; 7119 top-right 'DRONE' counter panel
|
|
jsr drawDroneCountDigit ; 711C the drones-left digit inside it
|
|
jsr drawLaunchStrikeButtons ; 711F the LAUNCH / STRIKE button panel
|
|
L_7122:
|
|
jsr drawDroneHeadingPanel ; 7122 top-left compass dial
|
|
jsr drawDroneFuelPanel ; 7125 bottom-left fuel gauge panel
|
|
jsr drawDroneFuelDigits ; 7128 the three fuel digits
|
|
jsr drawDroneRadarFrame ; 712B camera bezels; also blanks the camera and centres the view on the comcen
|
|
jsr drawMainWindowFrame ; 712E the outer main window / status bar frame (window 4 while a screen id is active)
|
|
jmp redrawStatusMessage ; 7131 re-show whatever the status line was displaying, and return from there
|
|
|
|
|
|
; Radar/camera wipe state byte, shared by clearRadarWindowGrey, clearRadarWindowBlack,
|
|
; flashRadarAndWipe and drawRadarIrisWipe. It doubles as (a) the colour the camera window is blanked
|
|
; with and (b) a flag: non-zero makes the iris wipe pause 3 game frames per ring, zero makes it close
|
|
; instantly.
|
|
radarWipeColour:
|
|
.byte $00 ; 7134 . $BB = animated grey wipe (after a launch), $00 = instant black wipe
|
|
|
|
; ----------------------------------------------------------------------
|
|
; clearRadarWindowGrey - Blanks the 14x12 drone camera window (row 6, column 13) to colour $BB (dark
|
|
; grey on dark grey, i.e. static) and re-centres the view origin on the player's own comcen, leaving
|
|
; radarWipeColour ($7134) non-zero so the iris wipe that follows animates with 3-frame pauses.
|
|
; In: comcenUnit ($9236), unitColTable/unitRowTable
|
|
; Out: radarWipeColour = $BB; camera window filled with $BB; radarGhostCountdown ($920F) = 0;
|
|
; viewOriginCol/Row (zp_A5/zp_A6) = comcen column - 6, comcen row - 5
|
|
; Called from: launchDrone ($8116) only
|
|
; ----------------------------------------------------------------------
|
|
clearRadarWindowGrey:
|
|
lda #$BB ; 7135 $BB = dark grey in both multicolour nibbles - looks like blank static
|
|
sta radarWipeColour ; 7137 remember it as both the fill colour and the 'animate the wipe' flag
|
|
bne L_7141 ; 713A always taken ($BB is non-zero); joins the shared tail
|
|
|
|
; ----------------------------------------------------------------------
|
|
; clearRadarWindowBlack - Same as clearRadarWindowGrey but with colour 0: blanks the drone camera
|
|
; window to black and re-centres the view on the own comcen. radarWipeColour is left 0, so a following
|
|
; iris wipe runs with no pauses.
|
|
; In: comcenUnit ($9236), unitColTable/unitRowTable
|
|
; Out: radarWipeColour = 0; camera window black; radarGhostCountdown ($920F) = 0; viewOriginCol/Row =
|
|
; comcen - (6,5)
|
|
; Called from: drawDroneRadarFrame ($6F96, screen build) and endDroneFlight ($81F8)
|
|
; ----------------------------------------------------------------------
|
|
clearRadarWindowBlack:
|
|
lda #$00 ; 713C colour 0 = all pixels black
|
|
sta radarWipeColour ; 713E 0 also means 'no pauses in the iris wipe'
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Shared tail of the two window clears: paint the camera area, forget any pending noise ghost and park
|
|
; the map view so that the own comcen sits in the middle of the 14x12 window (the drone starts its
|
|
; flight over the comcen).
|
|
; ----------------------------------------------------------------------
|
|
L_7141:
|
|
jsr fillDroneViewWindow ; 7141 fill the 14 column x 12 row camera window at row 6, column 13 with the colour in A
|
|
lda #$00 ; 7144 clear the countdown that decides when the next unspotted enemy is drawn as a ghost blip
|
|
sta radarGhostCountdown ; 7146 radarGhostCountdown ($920F)
|
|
ldy comcenUnit ; 7149 the player's own comcen (unit 49 for side 0, 99 for side 1)
|
|
L_714C:
|
|
lda unitColTable,y ; 714C its map column (unit arrays are 100-byte arrays $64 apart from $F640)
|
|
sec ; 714F prepare the subtraction
|
|
sbc #$06 ; 7150 the window is 14 cells wide, so back off 6 to put the comcen at cell 6
|
|
sta viewOriginCol ; 7152 zp_A5 = map column of the window's left edge
|
|
lda unitRowTable,y ; 7154 the comcen's map row ($F6A4 = $F640 + $64)
|
|
sec ; 7157 prepare the subtraction
|
|
sbc #$05 ; 7158 the window is 12 cells high, so back off 5 to put the comcen at cell 5
|
|
sta viewOriginRow ; 715A zp_A6 = map row of the window's top edge
|
|
rts ; 715C done
|
|
|
|
|
|
; Six bytes with no reference anywhere in overlay variant B ($21,$A1,$21,$81,$A1,$11). They are code
|
|
; or data of overlay variant A, which shares this address range; the label L_7161 is a branch target
|
|
; only in variant A. Dead in this variant.
|
|
unusedBytes715D:
|
|
.byte $21,$A1,$21,$81 ; 715D !.!. unreferenced filler in variant B (variant A code/data)
|
|
L_7161:
|
|
.byte $A1 ; 7161 continuation of the same unreferenced filler
|
|
.byte $11 ; 7162 .
|
|
|
|
; ----------------------------------------------------------------------
|
|
; playDroneLostAnimation - The 'drone is gone' effect, used when the drone is shot down, detonated by
|
|
; its own strike or runs out of fuel. It switches sprite 4 (the comcen range rings) off, flashes the
|
|
; camera window white and irises it shut, then rebuilds sprite 2 (the drone reticle) as a solid disc
|
|
; of radius 3 and lets it shrink and fade (white -> light grey -> medium grey -> dark grey) by erasing
|
|
; one ring at a time, disables sprite 2, zeroes the fuel read-out and waits 30 game frames.
|
|
; In: nothing meaningful (droneHeading is copied to a byte nobody reads)
|
|
; Out: spriteEnableShadow ($9022) bits 4 and 2 cleared; sprite 2 shape and colour changed;
|
|
; spritePlotMode ($91F3) = 0; droneFuel ($9220) = 0 and redrawn; textDrawParams/D_2A2A = 0;
|
|
; roughly 50 game frames elapse (the battle keeps running inside runGameFrames)
|
|
; Called from: playStrikeZoomAnimation ($7582, after a strike) and droneFlightLoop ($8177, drone lost)
|
|
; Dead code: 'sta zp_30' at $716A (only magnifyWindow2x uses zp_30, and it initialises it itself) and
|
|
; the store to savedDroneHeading at $717C (never read back).
|
|
; ----------------------------------------------------------------------
|
|
playDroneLostAnimation:
|
|
lda #$FF ; 7163 force the fixed draw colour for everything drawn from here
|
|
sta useDrawColourFlag ; 7165 textDrawParams = $FF
|
|
lda #$05 ; 7168 dead store: 5 into magnifySrcRow...
|
|
sta magnifySrcRow ; 716A ...zp_30, which magnifyWindow2x always initialises itself
|
|
lda spriteEnableShadow ; 716C current sprite enable mask shadow
|
|
and #$EF ; 716F clear bit 4 = sprite 4, the comcen range rings
|
|
sta spriteEnableShadow ; 7171 the raster IRQ copies this to $D015
|
|
lda #$00 ; 7174 fixed colour 0 (black) for the wipe frames
|
|
L_7176:
|
|
sta drawColourByte ; 7176 D_2A2A = fixed colour byte
|
|
lda droneHeading ; 7179 current drone heading 0-7
|
|
sta savedDroneHeading ; 717C dead store - savedDroneHeading ($7271) is never read
|
|
jsr flashRadarAndWipe ; 717F flash the camera window white and iris it shut
|
|
lda #$02 ; 7182 sprite 2 = the drone reticle
|
|
jsr clearSpriteShape ; 7184 zero its 64 shape bytes at $8A80
|
|
lda #$40 ; 7187 sprite plot mode $40 = OR pixels in (draw)
|
|
sta spritePlotMode ; 7189 spritePlotMode ($91F3)
|
|
lda #$02 ; 718C sprite 2
|
|
ldx #$03 ; 718E radius 3
|
|
jsr drawCircleInSprite ; 7190 draw a radius-3 circle centred at (12,10) in the sprite
|
|
lda #$02 ; 7193 sprite 2 again
|
|
tax ; 7195 X = 2 (A was 2) - radius 2
|
|
jsr drawCircleInSprite ; 7196 fill in the radius-2 ring, making the shape a solid disc
|
|
lda #$02 ; 7199 sprite 2 again
|
|
ldx #$01 ; 719B radius 1 = the 3x3 centre dot
|
|
jsr drawCircleInSprite ; 719D complete the disc
|
|
lda #$04 ; 71A0 bit 2 = sprite 2
|
|
jsr enableSprites ; 71A2 switch it on (unless sprites are suppressed by $922A bit 6)
|
|
lda #$01 ; 71A5 colour 1 = white
|
|
sta sprite2ColourShadow ; 71A7 sprite 2 colour shadow ($900A -> $D029)
|
|
L_71AA:
|
|
lda #$03 ; 71AA hold the white disc for 3 frames...
|
|
jsr runGameFrames ; 71AC ...while the battle keeps updating
|
|
lda #$00 ; 71AF sprite plot mode 0 = clear pixels (erase)
|
|
sta spritePlotMode ; 71B1 spritePlotMode
|
|
lda #$02 ; 71B4 sprite 2
|
|
ldx #$03 ; 71B6 radius 3
|
|
jsr drawCircleInSprite ; 71B8 erase the outer ring - the disc shrinks
|
|
lda #$0F ; 71BB colour $0F = light grey
|
|
sta sprite2ColourShadow ; 71BD fade step 1
|
|
lda #$03 ; 71C0 3 more frames
|
|
jsr runGameFrames ; 71C2 run them
|
|
lda #$02 ; 71C5 sprite 2
|
|
ldx #$02 ; 71C7 radius 2
|
|
jsr drawCircleInSprite ; 71C9 erase the next ring
|
|
lda #$0C ; 71CC colour $0C = medium grey
|
|
sta sprite2ColourShadow ; 71CE fade step 2
|
|
lda #$05 ; 71D1 5 frames
|
|
jsr runGameFrames ; 71D3 run them
|
|
lda #$02 ; 71D6 sprite 2
|
|
ldx #$01 ; 71D8 radius 1
|
|
jsr drawCircleInSprite ; 71DA erase the last dot - the sprite is now empty
|
|
lda #$0B ; 71DD colour $0B = dark grey
|
|
sta sprite2ColourShadow ; 71DF fade step 3
|
|
lda #$06 ; 71E2 6 frames
|
|
jsr runGameFrames ; 71E4 run them
|
|
lda spriteEnableShadow ; 71E7 sprite enable shadow
|
|
and #$FB ; 71EA clear bit 2 = sprite 2 off
|
|
sta spriteEnableShadow ; 71EC store it back
|
|
lda #$00 ; 71EF restore normal colour handling...
|
|
sta useDrawColourFlag ; 71F1 ...fixed-colour flag off...
|
|
sta drawColourByte ; 71F4 ...and fixed colour 0
|
|
lda #$00 ; 71F7 the drone is gone, so the tank reads empty
|
|
sta droneFuel ; 71F9 droneFuel ($9220) = 0
|
|
jsr drawDroneFuelDigits ; 71FC redraw '000' in the critical colour
|
|
lda #$1E ; 71FF 30 frames = about half a second
|
|
jmp runGameFrames ; 7201 wait them out and return from runGameFrames
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; flashRadarAndWipe - Paints the whole 14x12 drone camera window white for two game frames, then
|
|
; clears radarWipeColour and falls into drawRadarIrisWipe, so the iris closes over the white flash
|
|
; with no pauses.
|
|
; In: nothing
|
|
; Out: camera window colour 1; radarWipeColour ($7134) = 0; then see drawRadarIrisWipe
|
|
; Called from: playDroneLostAnimation ($717F) only
|
|
; ----------------------------------------------------------------------
|
|
flashRadarAndWipe:
|
|
lda #$06 ; 7204 fill origin row 6 = top of the camera window
|
|
sta windowOriginRow ; 7206 D_2A2D = window row
|
|
lda #$0D ; 7209 fill origin column 13 = left edge of the camera window
|
|
sta windowOriginCol ; 720B D_2A2E = window column
|
|
ldx #$0E ; 720E 14 columns
|
|
ldy #$0C ; 7210 12 rows
|
|
lda #$01 ; 7212 colour byte $01: %01 pixels black, %10 pixels white - a bright flash
|
|
jsr fillScreenColourRect ; 7214 paint it
|
|
lda #$02 ; 7217 hold for 2 frames
|
|
jsr runGameFrames ; 7219 while the battle keeps running
|
|
L_721C:
|
|
lda #$00 ; 721C 0 = close the iris with no pauses
|
|
sta radarWipeColour ; 721E radarWipeColour; falls straight into drawRadarIrisWipe
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawRadarIrisWipe - Closes (or opens) an iris over the drone camera window: it first draws window
|
|
; $16 (the camera area itself, left 13, top 6, 14x12) and then repeatedly draws a rectangular frame
|
|
; out of the all-$03 solid tile set at $9848, starting 12x12 cells at row 6 column 14 and shrinking
|
|
; two cells and moving in one cell each pass until the height drops below 3. The centre is finished
|
|
; off with a 2x2 block of character $05 at row 11, column 19. Each pass waits for raster line $B0 so
|
|
; the frame appears in one go, and when radarWipeColour is non-zero it also runs 3 game frames per
|
|
; pass, turning the wipe into a visible animation.
|
|
; In: radarWipeColour ($7134); the current fixed colour in $2A29/$2A2A ($AA light red after a launch,
|
|
; 0 during the loss animation)
|
|
; Out: the camera area is covered by concentric frames; D_2A2D = 11, D_2A2E = 19;
|
|
; frameWidth/frameHeight ($9221/$9222) clobbered
|
|
; Called from: flashRadarAndWipe ($721E, fall-through) and launchDrone ($8121, as the iris-open
|
|
; effect)
|
|
; ----------------------------------------------------------------------
|
|
drawRadarIrisWipe:
|
|
ldx #$16 ; 7221 window $16 = left 13, top 6, 14x12 - the camera area drawn as a plain frame
|
|
jsr drawWindowFrame ; 7223 draw it
|
|
lda #$0C ; 7226 the first iris ring is 12x12 cells
|
|
sta frameWidth ; 7228 frameWidth ($9221)
|
|
sta frameHeight ; 722B frameHeight ($9222)
|
|
lda #$06 ; 722E first ring's top row 6
|
|
sta windowOriginRow ; 7230 D_2A2D = frame row
|
|
lda #$0E ; 7233 first ring's left column 14
|
|
sta windowOriginCol ; 7235 D_2A2E = frame column
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Iris loop: draw one concentric frame, then shrink it by two cells and move its origin in by one
|
|
; cell, so the ring closes in on the centre of the camera window. drawFrameBox itself decrements width
|
|
; and height once and this loop decrements them again, hence two cells per pass.
|
|
; ----------------------------------------------------------------------
|
|
L_7238:
|
|
lda frameHeight ; 7238 current ring height
|
|
cmp #$03 ; 723B smaller than 3 cells?
|
|
bcc L_7265 ; 723D yes - the iris is shut, go and plug the centre
|
|
L_723F:
|
|
lda VIC_RASTER ; 723F poll the raster...
|
|
cmp #$B0 ; 7242 ...for line $B0, below the picture, so the ring is drawn between frames
|
|
bne L_723F ; 7244 keep polling until it matches
|
|
lda #$48 ; 7246 low byte of the 9-byte frame tile set at $9848 (all nine tiles are character $03 = solid)
|
|
ldy #$98 ; 7248 high byte -> $9848
|
|
jsr drawFrameBox ; 724A draw the ring; this also decrements frameWidth/frameHeight by one
|
|
dec frameWidth ; 724D second decrement: the ring loses one cell on each side
|
|
dec frameHeight ; 7250 same for the height
|
|
lda radarWipeColour ; 7253 animated wipe?
|
|
beq L_725D ; 7256 no - draw the next ring immediately
|
|
lda #$03 ; 7258 3 game frames of pause...
|
|
jsr runGameFrames ; 725A ...so the iris is visible as an animation
|
|
L_725D:
|
|
inc windowOriginRow ; 725D move the origin one row down...
|
|
inc windowOriginCol ; 7260 ...and one column right, keeping the ring centred
|
|
bne L_7238 ; 7263 always taken - next ring
|
|
L_7265:
|
|
lda #$13 ; 7265 centre column 19
|
|
sta windowOriginCol ; 7267 D_2A2E; D_2A2D is already 11 after the last pass
|
|
lda #$0A ; 726A low byte of the 2x2 block at $980A (header 2,2 then four copies of character $05)
|
|
ldy #$98 ; 726C high byte -> $980A
|
|
jmp drawCharBlock ; 726E plug the 2x2 hole at rows 11-12, columns 19-20 and return from drawCharBlock
|
|
|
|
|
|
; savedDroneHeading: byteTable, 1 bytes. Dead copy of droneHeading written by playDroneLostAnimation
|
|
savedDroneHeading:
|
|
.byte $00 ; 7271 . written by playDroneLostAnimation ($717C) and never read - a dead copy of droneHeading
|
|
|
|
; Eight AND masks that clear a single bit: index n clears bit n. buildRadarNoiseMasks picks the index
|
|
; with a random number 0-7.
|
|
clearBitMaskTable:
|
|
.byte $FE,$FD,$FB,$F7,$EF,$DF,$BF,$7F; 7272 ........ $FE,$FD,$FB,$F7,$EF,$DF,$BF,$7F - clear bit 0..7 respectively
|
|
|
|
; ----------------------------------------------------------------------
|
|
; buildRadarNoiseMasks - Fills the 16-byte table radarNoiseMasks ($9210-$921F) with $FF and then
|
|
; knocks four randomly chosen bits out of each byte. drawRadarGlyph ANDs the eight rows of a glyph
|
|
; with eight consecutive entries of this table (starting at a random offset 0-7, which is why the
|
|
; table is 16 and not 8 bytes long) to turn an unspotted enemy blip into a speckled 'ghost'.
|
|
; In: the game RNG state $57-$59
|
|
; Out: radarNoiseMasks ($9210-$921F); zp_1A = 0; Y = $FF; A/X clobbered
|
|
; Called from: drawDroneRadarFrame ($6F89), drawDroneRadarWindow ($742D), redrawEnemyUnitsOnRadar
|
|
; ($7926) and drawRadarMap ($7BA2) - i.e. the noise pattern is re-rolled on every radar redraw
|
|
; ----------------------------------------------------------------------
|
|
buildRadarNoiseMasks:
|
|
ldy #$0F ; 727A 16 masks, index 15 down to 0
|
|
lda #$FF ; 727C start from 'all bits set' = no noise
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Clear the table to $FF.
|
|
; ----------------------------------------------------------------------
|
|
L_727E:
|
|
sta radarNoiseMasks,y ; 727E radarNoiseMasks[Y] = $FF
|
|
dey ; 7281 next entry down
|
|
bpl L_727E ; 7282 until index 0 has been written
|
|
ldy #$0F ; 7284 second pass, again from index 15
|
|
|
|
; ----------------------------------------------------------------------
|
|
; For every mask byte, punch out four random bits (they may collide, so between one and four bits
|
|
; actually end up clear).
|
|
; ----------------------------------------------------------------------
|
|
L_7286:
|
|
lda #$04 ; 7286 four bits to clear per mask byte
|
|
sta scratch1A ; 7288 zp_1A = per-byte bit counter
|
|
L_728A:
|
|
jsr nextGameRandom ; 728A step the shared 24-bit game RNG (both machines run the same sequence)
|
|
and #$07 ; 728D keep a bit number 0-7
|
|
L_728F:
|
|
tax ; 728F use it as the index into clearBitMaskTable
|
|
lda clearBitMaskTable,x ; 7290 AND mask that clears just that bit
|
|
and radarNoiseMasks,y ; 7293 combine with the mask built so far
|
|
sta radarNoiseMasks,y ; 7296 store it back
|
|
dec scratch1A ; 7299 one of the four bits done
|
|
bne L_728A ; 729B repeat until four have been cleared
|
|
dey ; 729D next mask byte
|
|
bpl L_7286 ; 729E until all 16 are done
|
|
rts ; 72A0 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; applyDroneBlast - Resolves a drone detonation: damages every unit standing in the 2x2 block of map
|
|
; cells whose top-left corner is (cmdParam1, cmdParam2) - (x,y), (x+1,y), (x+1,y+1) and (x,y+1) - by
|
|
; calling applyDroneBlastToCell four times. The blast strength arrives in A and is written into the
|
|
; LDA # operand at $7310+1, so each cell's damage call ends with 'LDA #strength / JSR applyHitToUnit'.
|
|
; In: A = blast strength (cmdDroneDetonate at $536C passes $19 = 25), cmdParam1/cmdParam2
|
|
; (zp_74/zp_75) = the top-left map cell of the blast
|
|
; Out: up to four units damaged and stunned; the operand at $7311 is patched; cmdParam1 is restored to
|
|
; x but cmdParam2 is left at y+1, which is what the caller then uses to place the explosion marker
|
|
; sprite
|
|
; Called from: cmdDroneDetonate in the main program ($536C) only
|
|
; ----------------------------------------------------------------------
|
|
applyDroneBlast:
|
|
sta L_7310+1 ; 72A1 self-modifying code: store the blast strength into the operand byte of the 'lda #$FF' at $7310
|
|
jsr applyDroneBlastToCell ; 72A4 cell (x, y) - the top-left of the 2x2 blast
|
|
inc cmdParam1 ; 72A7 step to column x+1
|
|
jsr applyDroneBlastToCell ; 72A9 cell (x+1, y)
|
|
inc cmdParam2 ; 72AC step to row y+1
|
|
jsr applyDroneBlastToCell ; 72AE cell (x+1, y+1)
|
|
L_72B1:
|
|
dec cmdParam1 ; 72B1 back to column x; falls straight into the fourth call for cell (x, y+1)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; applyDroneBlastToCell - Applies the drone blast to the single map cell (cmdParam1, cmdParam2). It
|
|
; returns at once if the cell is outside the 40x40 map or holds no unit. Otherwise, if the blast
|
|
; strength is at least 15 and the game type is not THE BOMB (type 2), the unit's stun counter is set
|
|
; to 15 and haltStunnedUnit stops it where it stands. If the unit is the player's own comcen, the
|
|
; cloak lock is refreshed and - with the DAMAGE option on ($0BA3 bit 4) and not in a solo/film game -
|
|
; all three comcen systems are marked damaged and comcenStunStatus is set to $FF, which triggers the
|
|
; knock-out cut-scene at $0709. Without DAMAGE the 'COMCEN STUNNED!' message (id $0C) is queued
|
|
; instead. Finally the unit takes the blast damage.
|
|
; In: cmdParam1/cmdParam2 = map cell, blast strength in the operand at $7311, gameTypeOptions
|
|
; ($0BA3), filmPlaybackMode ($0B9B), comcenUnit ($9236)
|
|
; Out: unit stun/halt, possibly the comcen knock-out flags, and applyHitToUnit with A = strength, Y =
|
|
; the unit index
|
|
; Called from: applyDroneBlast ($72A4/$72A9/$72AE plus the fall-through at $72B1)
|
|
; Note: the labels L_72B1, L_72D3, L_72D5, L_72E1, L_72E5, L_72F2, L_72F5, sub_72F6 and L_730D are
|
|
; XREF artefacts of overlay variant A sharing these addresses; only sub_72F6 ($72F6) is also a real
|
|
; branch target here.
|
|
; ----------------------------------------------------------------------
|
|
applyDroneBlastToCell:
|
|
ldx cmdParam1 ; 72B3 map column of the cell
|
|
cpx #$28 ; 72B5 the map is 40 cells wide
|
|
bcs L_7315 ; 72B7 off the map - nothing to damage
|
|
ldy cmdParam2 ; 72B9 map row of the cell
|
|
cpy #$28 ; 72BB the map is 40 cells high
|
|
bcs L_7315 ; 72BD off the map - nothing to damage
|
|
jsr readMapCell ; 72BF read map[$F000 + row*40 + col]
|
|
bpl L_7315 ; 72C2 bit 7 clear = terrain only, no unit standing here
|
|
and #$7F ; 72C4 strip the 'unit here' bit...
|
|
tay ; 72C6 ...leaving the unit index 0-99 in Y
|
|
lda L_7310+1 ; 72C7 the blast strength patched in by applyDroneBlast
|
|
cmp #$0F ; 72CA weaker than 15?
|
|
bcc L_7310 ; 72CC yes - just apply damage, no stun
|
|
lda gameTypeOptions ; 72CE game options byte
|
|
and #$07 ; 72D1 bits 0-2 = game type
|
|
L_72D3:
|
|
cmp #$02 ; 72D3 type 2 = THE BOMB
|
|
L_72D5:
|
|
beq L_7310 ; 72D5 in THE BOMB nothing gets stunned - just apply damage
|
|
lda unitStunTable,y ; 72D7 the unit's stun / dug-in counter
|
|
ora #$0F ; 72DA set the low nibble to 15 = fully stunned
|
|
sta unitStunTable,y ; 72DC store it back
|
|
jsr haltStunnedUnit ; 72DF cancel the unit's orders so it stops where it is
|
|
cpy comcenUnit ; 72E2 is this the local player's own comcen?
|
|
L_72E5:
|
|
bne L_7310 ; 72E5 no - just apply damage
|
|
jsr updateComcenCloakLock ; 72E7 refresh comcenCloakedFlag ($9245) from the comcen's cloak bit
|
|
lda gameTypeOptions ; 72EA game options byte
|
|
and #$10 ; 72ED bit 4 = the DAMAGE option
|
|
beq queueComcenStunnedMessage; 72EF DAMAGE off - only announce the stun
|
|
lda filmPlaybackMode ; 72F1 solo trainer / film playback flag
|
|
bpl L_7300 ; 72F4 bit 7 clear = real linked game, so knock the comcen out
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Soft outcome: announce the stun on the status line and skip the knock-out.
|
|
; ----------------------------------------------------------------------
|
|
queueComcenStunnedMessage:
|
|
lda #$0C ; 72F6 message id $0C = 'COMCEN STUNNED!'
|
|
jsr queueMessage ; 72F8 queue it in the $C000 overlay's sorted message ring
|
|
ldy comcenUnit ; 72FB reload the unit index for applyHitToUnit
|
|
bne L_7310 ; 72FE always taken: comcenUnit is 49 or 99, never 0 - so the knock-out block below is skipped
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Hard outcome: the comcen is knocked out. All three of its subsystems are marked damaged and
|
|
; comcenStunStatus is set, which the main loop turns into the black-out cut-scene at $0709.
|
|
; ----------------------------------------------------------------------
|
|
L_7300:
|
|
lda #$80 ; 7300 $80 = system damaged
|
|
sta comcenBattleStatus ; 7302 comcenSystemStatus[1]: the overview map drawing
|
|
sta comcenRadarStatus ; 7305 comcenSystemStatus[2]: screen 1, the radar/missile scanner
|
|
sta comcenDroneStatus ; 7308 comcenSystemStatus[3]: the drone system
|
|
lda #$FF ; 730B $FF = comcen stunned
|
|
L_730D:
|
|
sta comcenStunStatus ; 730D comcenStunStatus ($922A); the knock-out sequence polls this
|
|
L_7310:
|
|
lda #$FF ; 7310 operand $7311 is patched by applyDroneBlast with the blast strength (25 for a drone)
|
|
jsr applyHitToUnit ; 7312 apply the damage to unit Y (halved and score-adjusted inside applyHitToUnit)
|
|
L_7315:
|
|
rts ; 7315 done
|
|
|
|
|
|
; Maps a clockwise compass heading 0-7 (0 N, 1 NE, 2 E, 3 SE, 4 S, 5 SW, 6 W, 7 NW) to the engine's
|
|
; direction code 0-7 (0 N, 1 S, 2 E, 3 W, 4 NW, 5 NE, 6 SE, 7 SW), which is what directionDeltaTable
|
|
; ($20F0) and diagonalDirectionTable ($1FB2) are indexed with. joyDirToHeadingTable ($7FCE) is the
|
|
; inverse mapping.
|
|
headingToDirectionTable:
|
|
.byte $00,$05,$02,$06 ; 7316 .... headings N,NE,E,SE -> direction codes 0,5,2,6
|
|
L_731A:
|
|
.byte $01 ; 731A headings S,SW,W,NW -> direction codes 1,7,3,4
|
|
.byte $07,$03,$04 ; 731B ...
|
|
|
|
; ----------------------------------------------------------------------
|
|
; steerDrone - Advances the locally flown drone by one map cell. Called once per command exchange from
|
|
; sendOutgoingPacket ($5639) while droneControlFlags ($920D) bit 6 says the drone is under local
|
|
; control, just before the $8D heading,col,row report is queued. It turns droneHeading ($920B) at most
|
|
; one 45-degree step towards the heading the joystick is asking for, taking the shorter way round and
|
|
; playing sound $10 on each turn, then converts the heading to a direction code and adds that
|
|
; direction's dx/dy to droneViewOriginCol/Row ($92F6/$92F7).
|
|
; In: droneJoyDirection (zp_5E) = direction code of the last joystick reading, droneHeading ($920B),
|
|
; droneViewOriginCol/Row ($92F6/$92F7)
|
|
; Out: droneHeading turned by at most one step and kept in 0-7; the drone's map position advanced by
|
|
; one cell; sound $10 when it turned; A/X/Y clobbered
|
|
; Called from: sendOutgoingPacket ($5639) in the main program
|
|
; ----------------------------------------------------------------------
|
|
steerDrone:
|
|
ldy droneJoyDirection ; 731E the direction code the joystick last produced (0 N, 1 S, 2 E, 3 W, 4 NW, 5 NE, 6 SE, 7 SW)
|
|
lda joyDirToHeadingTable,y ; 7320 convert it to a clockwise heading 0-7 through joyDirToHeadingTable ($7FCE)
|
|
sec ; 7323 prepare the subtraction
|
|
sbc droneHeading ; 7324 how far is that from the heading we are flying?
|
|
beq L_734C ; 7327 already on heading - no turn
|
|
bcc L_7335 ; 7329 wanted heading is numerically smaller - handle the negative case
|
|
ldx #$FF ; 732B assume a turn of -1 (anticlockwise)
|
|
cmp #$04 ; 732D is the difference 4 or more?
|
|
bcs L_733D ; 732F yes - going anticlockwise is the short way round
|
|
ldx #$01 ; 7331 difference 1-3: turn +1, clockwise, is shorter
|
|
bne L_733D ; 7333 always taken
|
|
L_7335:
|
|
ldx #$01 ; 7335 negative difference: assume +1 (clockwise)
|
|
cmp #$FC ; 7337 difference of -4..-1 (i.e. $FC..$FF)?
|
|
bcc L_733D ; 7339 no - the gap is bigger than 4, so clockwise is shorter
|
|
ldx #$FF ; 733B -1..-4: turn anticlockwise
|
|
L_733D:
|
|
txa ; 733D the turn step (+1 or -1) into A
|
|
L_733E:
|
|
clc ; 733E prepare the addition
|
|
adc droneHeading ; 733F apply it to the current heading
|
|
and #$07 ; 7342 wrap into the range 0-7
|
|
sta droneHeading ; 7344 the drone has swung 45 degrees
|
|
lda #$10 ; 7347 sound effect $10 = the turn
|
|
jsr playSound ; 7349 play it
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Translate the (possibly just updated) heading into a map step and move the drone.
|
|
; ----------------------------------------------------------------------
|
|
L_734C:
|
|
ldy droneHeading ; 734C current heading 0-7
|
|
lda headingToDirectionTable,y; 734F -> direction code 0-7
|
|
ldy #$00 ; 7352 Y will hold the row delta, default 0
|
|
ldx #$00 ; 7354 X will hold the column delta, default 0
|
|
cmp #$04 ; 7356 codes 4-7 are the diagonals
|
|
bcs L_736E ; 7358 handle a diagonal
|
|
cmp #$02 ; 735A codes 2-3 are east/west
|
|
bcs L_7366 ; 735C handle a pure column move
|
|
tay ; 735E codes 0-1 (N/S): index directionDeltaTable with the code
|
|
lda directionDeltaTable,y ; 735F $20F0[0] = -1 for north, [1] = +1 for south
|
|
L_7362:
|
|
tay ; 7362 that is the row delta
|
|
jmp L_7384 ; 7363 go and apply the step
|
|
|
|
L_7366:
|
|
tax ; 7366 codes 2-3 (E/W): index directionDeltaTable with the code
|
|
lda directionDeltaTable,x ; 7367 $20F0[2] = +1 for east, [3] = -1 for west
|
|
tax ; 736A that is the column delta
|
|
L_736B:
|
|
jmp L_7384 ; 736B go and apply the step
|
|
|
|
L_736E:
|
|
tay ; 736E codes 4-7 (NW/NE/SE/SW): use the packed table
|
|
lda diagonalDirectionTable,y; 736F $1FB2[4..7] = $30,$20,$21,$31 - high nibble = column index, low nibble = row index
|
|
pha ; 7372 keep the packed byte
|
|
and #$0F ; 7373 low nibble = index into directionDeltaTable for the row
|
|
tay ; 7375 into Y
|
|
lda directionDeltaTable,y ; 7376 fetch the row delta
|
|
tay ; 7379 Y = row delta
|
|
pla ; 737A packed byte back
|
|
lsr a ; 737B shift the high nibble down, 1 of 4
|
|
lsr a ; 737C 2 of 4
|
|
lsr a ; 737D 3 of 4
|
|
lsr a ; 737E 4 of 4 - now the column index
|
|
tax ; 737F into X
|
|
lda directionDeltaTable,x ; 7380 fetch the column delta
|
|
tax ; 7383 X = column delta
|
|
L_7384:
|
|
txa ; 7384 column delta into A
|
|
clc ; 7385 prepare the addition
|
|
adc droneViewOriginCol ; 7386 the drone's current map column ($92F6, the camera window's centre)
|
|
sta droneViewOriginCol ; 7389 move it one cell
|
|
tya ; 738C row delta into A
|
|
clc ; 738D prepare the addition
|
|
adc droneViewOriginRow ; 738E the drone's current map row ($92F7)
|
|
sta droneViewOriginRow ; 7391 move it one cell
|
|
L_7394:
|
|
rts ; 7394 done - the caller now queues the $8D heading,col,row report
|
|
|
|
|
|
; radarGlyphSavedY: byteTable, 1 bytes. Saved Y register of drawRadarGlyph
|
|
radarGlyphSavedY:
|
|
.byte $00 ; 7395 . scratch byte holding drawRadarGlyph's entry Y (the colour-cell offset), restored on exit
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawRadarGlyph - Plots one 8x8 map glyph into the bitmap cell pointed at by bitmapPtr (zp_B4/zp_B5)
|
|
; and writes its colour through screenPtr (zp_B6/zp_B7) at offset Y. The glyph source is the
|
|
; 8-bytes-per-tile set at $9C6E, selected by setTerrainTilePtr from A & $7F. Two effects can be
|
|
; applied: if radarHalfToneFlag ($922D) is set (a cloaked unit) the even bitmap rows are blanked and
|
|
; only the odd rows copied, so the unit shows up as a half-tone; if bit 7 of A is set (a noise
|
|
; 'ghost') every row is ANDed with eight consecutive entries of radarNoiseMasks starting at a random
|
|
; offset 0-7. The colour byte in tileColour (zp_32) is written raw on screen 1 - where the caller has
|
|
; biased screenPtr into colour RAM at $D800 - and as (tileColour << 4) | cellBackgroundColour on
|
|
; screen 2, where screenPtr points into screen RAM at $8C00 and supplies both multicolour nibbles.
|
|
; In: A = glyph index (bit 7 = draw with noise), Y = colour-cell offset, X free, tileColour (zp_32),
|
|
; bitmapPtr, screenPtr, radarHalfToneFlag ($922D), currentScreen ($90FB), cellBackgroundColour
|
|
; ($9226), radarNoiseMasks ($9210)
|
|
; Out: 8 bitmap bytes and 1 colour byte written; bitmapPtr advanced 8 bytes to the next cell to the
|
|
; right; radarHalfToneFlag cleared; A = glyph & $7F, X and Y restored via self-modified operands;
|
|
; radarGlyphSavedY = entry Y
|
|
; Called from: drawDroneRadarWindow ($7474), drawRadarMap ($7C0E) and the main program's
|
|
; drawScreen1Cell ($5DF5)
|
|
; Dead code: the 'lda textDrawParams / beq' test at $73F9 can never take the non-zero path, because
|
|
; setTerrainTilePtr at $73A4 always clears textDrawParams first - so the forced colour 8 at $73FE is
|
|
; unreachable.
|
|
; ----------------------------------------------------------------------
|
|
drawRadarGlyph:
|
|
sta L_73B6+1 ; 7396 self-modifying: save the raw glyph byte (bit 7 = noise flag) into the 'lda #' operand at $73B7
|
|
and #$7F ; 7399 strip the noise flag
|
|
sta L_7414+1 ; 739B save the plain glyph index into the 'lda #' operand at $7415 so it can be returned in A
|
|
sty radarGlyphSavedY ; 739E save the caller's Y (the colour-cell offset within the row)
|
|
stx L_7419+1 ; 73A1 save the caller's X into the 'ldx #' operand at $741A
|
|
jsr setTerrainTilePtr ; 73A4 glyphPtr (zp_B2/B3) = $9C6E + glyph*8; also clears textDrawParams
|
|
lda radarHalfToneFlag ; 73A7 half-tone flag, set by getRadarCellGlyph for a cloaked unit
|
|
bpl L_73B6 ; 73AA not cloaked - leave all eight rows to the copy loop
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Cloaked unit: blank the four even bitmap rows first; the copy loop below will then only write the
|
|
; odd rows, so the unit appears on alternate scanlines.
|
|
; ----------------------------------------------------------------------
|
|
L_73AC:
|
|
ldy #$06 ; 73AC start at bitmap row 6
|
|
lda #$00 ; 73AE blank byte
|
|
L_73B0:
|
|
sta (bitmapPtr),y ; 73B0 clear this row of the cell
|
|
dey ; 73B2 step two rows...
|
|
dey ; 73B3 ...i.e. rows 6, 4, 2, 0
|
|
bpl L_73B0 ; 73B4 until row 0 has been cleared
|
|
L_73B6:
|
|
lda #$FF ; 73B6 operand patched at $7396 with the glyph byte including its bit 7
|
|
bmi L_73CB ; 73B8 bit 7 set = draw this blip as noise
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Plain copy of the glyph into the bitmap cell, bottom row upwards. When the half-tone flag is set the
|
|
; loop steps two rows at a time (odd rows only).
|
|
; ----------------------------------------------------------------------
|
|
ldy #$07 ; 73BA bitmap rows 7 down to 0
|
|
L_73BC:
|
|
lda (glyphPtr),y ; 73BC glyph row from the $9C6E tile set
|
|
sta (bitmapPtr),y ; 73BE into the bitmap cell
|
|
lda radarHalfToneFlag ; 73C0 cloaked?
|
|
bpl L_73C6 ; 73C3 no - step one row
|
|
dey ; 73C5 cloaked: skip a row so only odd rows are written
|
|
L_73C6:
|
|
dey ; 73C6 next row up
|
|
bpl L_73BC ; 73C7 until row 0 is done
|
|
bmi L_73E4 ; 73C9 always taken - go to the colour write
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Noise variant: the glyph is ANDed row by row with eight consecutive noise masks, starting at a
|
|
; random offset 0-7 within the 16-byte table. That is why the table holds 16 masks: the walk of eight
|
|
; never runs off the end.
|
|
; ----------------------------------------------------------------------
|
|
L_73CB:
|
|
jsr nextGameRandom ; 73CB random number from the shared game RNG
|
|
and #$07 ; 73CE start offset 0-7 into radarNoiseMasks
|
|
tax ; 73D0 X = mask index
|
|
ldy #$07 ; 73D1 bitmap rows 7 down to 0
|
|
L_73D3:
|
|
lda (glyphPtr),y ; 73D3 glyph row
|
|
and radarNoiseMasks,x ; 73D5 knock out four random bits of it
|
|
sta (bitmapPtr),y ; 73D8 store the speckled row
|
|
inx ; 73DA next mask for the next row
|
|
lda radarHalfToneFlag ; 73DB cloaked as well?
|
|
bpl L_73E1 ; 73DE no - step one row
|
|
dey ; 73E0 cloaked: skip a row
|
|
L_73E1:
|
|
dey ; 73E1 next row up
|
|
bpl L_73D3 ; 73E2 until row 0 is done
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Common tail: clear the one-shot half-tone flag, step the bitmap pointer to the next cell and write
|
|
; the cell's colour.
|
|
; ----------------------------------------------------------------------
|
|
L_73E4:
|
|
ldy #$00 ; 73E4 Y = 0 for the flag clear
|
|
sty radarHalfToneFlag ; 73E6 radarHalfToneFlag is per cell - clear it for the next one
|
|
clc ; 73E9 prepare the 16-bit add
|
|
lda bitmapPtr ; 73EA bitmap pointer low byte
|
|
adc #$08 ; 73EC one character cell = 8 bytes to the right
|
|
storeGlyphBitmapPtr:
|
|
sta bitmapPtr ; 73EE store it back (label sub_73EE is a variant-A entry point only)
|
|
lda bitmapPtrHi ; 73F0 bitmap pointer high byte
|
|
adc #$00 ; 73F2 propagate the carry
|
|
sta bitmapPtrHi ; 73F4 bitmapPtr now addresses the next cell of the row
|
|
ldy radarGlyphSavedY ; 73F6 restore the colour-cell offset
|
|
lda useDrawColourFlag ; 73F9 dead test: setTerrainTilePtr cleared textDrawParams at $73A4, so this is always 0
|
|
beq L_7402 ; 73FC always taken
|
|
lda #$08 ; 73FE unreachable: forced colour 8
|
|
sta tileColour ; 7400 unreachable
|
|
L_7402:
|
|
lda tileColour ; 7402 the colour getRadarCellGlyph chose for this cell
|
|
ldx currentScreen ; 7404 which screen are we drawing?
|
|
cpx #$01 ; 7407 screen 1 = the RADAR / MISSILE screen
|
|
beq L_7412 ; 7409 yes - screenPtr already points into colour RAM, store the raw nibble
|
|
asl a ; 740B screen 2: move the colour into the high nibble, shift 1...
|
|
asl a ; 740C ...2...
|
|
asl a ; 740D ...3...
|
|
asl a ; 740E ...4 - it is now the %01 pixel colour
|
|
ora cellBackgroundColour ; 740F the %10 pixel colour comes from cellBackgroundColour ($0A on the drone screen)
|
|
L_7412:
|
|
sta (screenPtr),y ; 7412 write the cell's colour byte
|
|
L_7414:
|
|
lda #$FF ; 7414 operand patched at $739B - return the glyph index in A
|
|
ldy radarGlyphSavedY ; 7416 restore the caller's Y
|
|
L_7419:
|
|
ldx #$FF ; 7419 operand patched at $73A1 - restore the caller's X
|
|
rts ; 741B done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawDroneRadarWindow - Redraws the whole 14x12 drone camera window (character rows 6-17, columns
|
|
; 13-26, one character per map cell) from the map around the view origin zp_A5/zp_A6. It sets the
|
|
; three radar colours, re-rolls the noise masks, walks the map row by row and calls getRadarCellGlyph
|
|
; followed by drawRadarGlyph for every cell. The bitmap pointer is built from the $9300 address tables
|
|
; biased to row 6 / column 13 (D_9306/D_931F and D_933F/D_9367) and the screen RAM pointer from the
|
|
; screen row table biased to row 6 (D_9388/D_93A1) plus column 13. It waits for raster line $90 before
|
|
; starting so the redraw is not torn.
|
|
; In: viewOriginCol/Row (zp_A5/zp_A6), the 40x40 map at $F000, the unit arrays, droneRadarOwnColour
|
|
; ($9224) and droneRadarEnemyColour ($9225)
|
|
; Out: the camera window redrawn; radarTerrainColour ($9239) = 8, radarOwnUnitColour ($9237) and
|
|
; radarEnemyUnitColour ($9238) set; noise masks re-rolled; drawCellSaveCol/Row ($90F6/$90F7) hold
|
|
; the saved view origin, which is restored on exit; zp_48/49, zp_B4-zp_B7 clobbered
|
|
; Called from: advanceDroneCamera ($8281), i.e. once per lock-step command exchange while the drone
|
|
; flies
|
|
; ----------------------------------------------------------------------
|
|
drawDroneRadarWindow:
|
|
lda #$08 ; 741C terrain colour 8 = orange for the %01 pixels of every terrain cell
|
|
sta radarTerrainColour ; 741E radarTerrainColour ($9239)
|
|
lda droneRadarOwnColour ; 7421 own-side unit colour for the drone camera ($0B dark grey, set by overlay A at $7904)
|
|
sta radarOwnUnitColour ; 7424 radarOwnUnitColour ($9237)
|
|
lda droneRadarEnemyColour ; 7427 enemy unit colour for the drone camera ($09 brown, set by overlay A at $78FB)
|
|
L_742A:
|
|
sta radarEnemyUnitColour ; 742A radarEnemyUnitColour ($9238)
|
|
jsr buildRadarNoiseMasks ; 742D re-roll the noise masks so ghost blips shimmer between frames
|
|
jsr getViewportOriginPointer; 7430 zp_48/49 = map address of the cell at (zp_A5, zp_A6), handling negative origins
|
|
lda viewOriginCol ; 7433 the view origin column...
|
|
sta drawCellSaveCol ; 7435 ...is saved so the per-cell walk can trample zp_A5
|
|
lda viewOriginRow ; 7438 the view origin row...
|
|
sta drawCellSaveRow ; 743A ...likewise
|
|
clc ; 743D prepare the 16-bit add
|
|
lda bitmapRowLoTableFrom6 ; 743E bitmap row table entry for row 6 ($9300 + 6)
|
|
adc bitmapColLoTableFrom13 ; 7441 plus the column offset for column 13 ($9332 + 13 = col*8)
|
|
storeRadarBitmapPtr:
|
|
sta bitmapPtr ; 7444 bitmapPtr low (label sub_7444 is a variant-A entry point only)
|
|
lda bitmapRowHiTableFrom6 ; 7446 high byte of the row address
|
|
adc bitmapColHiTableFrom13 ; 7449 high byte of the column offset
|
|
sta bitmapPtrHi ; 744C bitmapPtr now addresses bitmap cell (6,13)
|
|
clc ; 744E prepare the 16-bit add
|
|
lda screenRowLoTableFrom6 ; 744F screen RAM row table entry for row 6 ($9382 + 6, base $8C00)
|
|
adc #$0D ; 7452 plus column 13
|
|
sta screenPtr ; 7454 screenPtr low
|
|
lda screenRowHiTableFrom6 ; 7456 high byte of the screen row address
|
|
adc #$00 ; 7459 propagate the carry
|
|
sta screenPtrHi ; 745B screenPtr addresses screen RAM cell (6,13)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Wait until the raster is below the visible picture (line $90) so the whole window is redrawn between
|
|
; two displayed frames.
|
|
; ----------------------------------------------------------------------
|
|
L_745D:
|
|
lda VIC_RASTER ; 745D read the raster line counter
|
|
L_7460:
|
|
cmp #$90 ; 7460 line $90
|
|
bne L_745D ; 7462 keep polling
|
|
ldx #$00 ; 7464 X = camera row 0
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Outer loop over the 12 camera rows; inner loop over the 14 cells of a row. zp_A5/zp_A6 are walked
|
|
; cell by cell so getRadarCellGlyph can tell whether the cell is off the 40x40 map.
|
|
; ----------------------------------------------------------------------
|
|
L_7466:
|
|
ldy #$00 ; 7466 Y = camera column 0
|
|
tya ; 7468 column index into A
|
|
clc ; 7469 prepare the add
|
|
adc drawCellSaveCol ; 746A plus the saved origin column
|
|
sta viewOriginCol ; 746D zp_A5 = map column of this cell
|
|
L_746F:
|
|
lda (mapCellPtr),y ; 746F the map byte for this cell
|
|
jsr getRadarCellGlyph ; 7471 classify it into a glyph index (A) and a colour (zp_32)
|
|
jsr drawRadarGlyph ; 7474 plot it; this also advances bitmapPtr to the next cell
|
|
iny ; 7477 next camera column
|
|
inc viewOriginCol ; 7478 and the matching map column
|
|
tya ; 747A column counter into A
|
|
L_747B:
|
|
cmp #$0E ; 747B the window is 14 cells wide
|
|
bcc L_746F ; 747D more cells on this row
|
|
inc viewOriginRow ; 747F row finished - step the map row
|
|
L_7481:
|
|
lda mapCellPtr ; 7481 map pointer low
|
|
clc ; 7483 prepare the add
|
|
adc #$28 ; 7484 one map row = 40 bytes
|
|
sta mapCellPtr ; 7486 store it
|
|
lda mapCellPtrHi ; 7488 map pointer high
|
|
adc #$00 ; 748A propagate the carry
|
|
sta mapCellPtrHi ; 748C zp_48/49 now addresses the next map row
|
|
clc ; 748E prepare the add
|
|
lda screenPtr ; 748F screen RAM pointer low
|
|
adc #$28 ; 7491 one screen row = 40 bytes
|
|
sta screenPtr ; 7493 store it
|
|
lda screenPtrHi ; 7495 screen RAM pointer high
|
|
adc #$00 ; 7497 propagate the carry
|
|
sta screenPtrHi ; 7499 zp_B6/B7 now addresses the next screen row
|
|
inx ; 749B next camera row
|
|
ldy #$00 ; 749C back to column 0 for the bitmap address
|
|
L_749E:
|
|
clc ; 749E prepare the add
|
|
lda bitmapRowLoTableFrom6,x ; 749F bitmap row address for row 6+X
|
|
adc bitmapColLoTableFrom13,y; 74A2 plus the column offset for column 13+0
|
|
sta bitmapPtr ; 74A5 bitmapPtr low
|
|
lda bitmapRowHiTableFrom6,x ; 74A7 high byte of the row address
|
|
adc bitmapColHiTableFrom13,y; 74AA high byte of the column offset
|
|
sta bitmapPtrHi ; 74AD bitmapPtr = first cell of the next camera row
|
|
cpx #$0C ; 74AF 12 rows in the window
|
|
bcc L_7466 ; 74B1 more rows to draw
|
|
lda #$00 ; 74B3 already 0 - setTerrainTilePtr clears it on every glyph
|
|
sta useDrawColourFlag ; 74B5 redundant clear of textDrawParams
|
|
lda drawCellSaveCol ; 74B8 restore the view origin column...
|
|
sta viewOriginCol ; 74BB ...into zp_A5
|
|
L_74BD:
|
|
lda drawCellSaveRow ; 74BD restore the view origin row...
|
|
sta viewOriginRow ; 74C0 ...into zp_A6
|
|
rts ; 74C2 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; getRadarCellGlyph - Turns one map cell byte into the glyph index and colour that drawRadarGlyph
|
|
; needs. X and Y are preserved through self-modified operands, so it can be called from inside a
|
|
; rendering loop. Cells outside the 40x40 map become one of four hatch glyphs ($28-$2B, chosen by
|
|
; (row+column) & 3) in the terrain colour. Terrain cells become glyph (cell - $40) in
|
|
; radarTerrainColour ($9239). A cell holding a unit ($80 | index) becomes unitGlyphBaseTable[type] +
|
|
; facing: in radarOwnUnitColour ($9237) for the local player's units, and for the opponent's units in
|
|
; radarEnemyUnitColour ($9238) only while the unit is spotted (unitEnergyTable bit 7). An unspotted
|
|
; enemy normally shows the terrain saved under it, except that roughly every 1-8th one (countdown
|
|
; radarGhostCountdown $920F, reseeded from the RNG) is drawn as a speckled 'ghost' blip (glyph | $80)
|
|
; in the terrain colour. A dead unit (unitTypeTable bit 7) also shows the terrain hidden under it. A
|
|
; cloaked unit (unitPathColTable bit 7) sets radarHalfToneFlag so it is drawn on alternate scanlines.
|
|
; In: A = the map cell byte, zp_A5/zp_A6 = that cell's map coordinates,
|
|
; radarOwnUnitColour/radarEnemyUnitColour/radarTerrainColour, radarGhostCountdown, unit arrays
|
|
; $F708/$F76C/$F898/$F9C4/$FAF0, unitGlyphBaseTable ($0799)
|
|
; Out: A = glyph index (bit 7 set = draw with noise), tileColour (zp_32) = colour byte,
|
|
; radarHalfToneFlag ($922D) = $FF for a cloaked unit, radarGhostCountdown updated; X and Y
|
|
; restored
|
|
; Called from: drawDroneRadarWindow ($7471), drawRadarMap ($7C0B) and the main program's
|
|
; drawScreen1Cell ($5DF2)
|
|
; Self-modifying: $750A is NOT a constant BCS. loadOverlayB in the main program copies the
|
|
; side-dependent opcode from $206B into it right after loading this overlay ($10A5): $B0 (BCS) when
|
|
; the local player is side 0 so units 50-99 are the enemy, $90 (BCC) when the local player is side 1
|
|
; so units 0-49 are. Treat $750A as a variable. Quirk: a dead unit's terrain is emitted with
|
|
; tileColour still set to radarOwnUnitColour (set at $74F6) rather than the terrain colour; on screen
|
|
; 1 both are $0B so it is invisible, on the drone camera it tints dead units' ground dark grey instead
|
|
; of orange. If the saved terrain byte happens to be 0 the 'bne' at $7503 falls through and the dead
|
|
; unit is drawn as a live GRUNT - terrain 0 does not occur on a real map ($40 is blank).
|
|
; ----------------------------------------------------------------------
|
|
getRadarCellGlyph:
|
|
stx L_754C+1 ; 74C3 self-modifying: save the caller's X into the 'ldx #' operand at $754D
|
|
sty L_754E+1 ; 74C6 and the caller's Y into the 'ldy #' operand at $754F
|
|
L_74C9:
|
|
tay ; 74C9 keep the map cell byte in Y while the coordinates are range-checked
|
|
lda viewOriginCol ; 74CA this cell's map column
|
|
cmp #$28 ; 74CC the map is 40 columns wide
|
|
bcs L_74D6 ; 74CE off the map - draw hatching
|
|
lda viewOriginRow ; 74D0 this cell's map row
|
|
L_74D2:
|
|
cmp #$28 ; 74D2 the map is 40 rows high
|
|
bcc L_74E1 ; 74D4 on the map - use the real cell byte
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Off-map cell: pick one of the four hatch patterns so the border round the battlefield gets a woven
|
|
; look instead of a flat block.
|
|
; ----------------------------------------------------------------------
|
|
L_74D6:
|
|
lda viewOriginRow ; 74D6 row...
|
|
clc ; 74D8 prepare the add
|
|
adc viewOriginCol ; 74D9 ...plus column
|
|
and #$03 ; 74DB pick one of four patterns
|
|
L_74DD:
|
|
clc ; 74DD prepare the add
|
|
adc #$68 ; 74DE map codes $68-$6B are the four hatch tiles
|
|
L_74E0:
|
|
tay ; 74E0 pretend the map held that code
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Default: the cell is plain terrain, drawn in the terrain colour.
|
|
; ----------------------------------------------------------------------
|
|
L_74E1:
|
|
ldx radarTerrainColour ; 74E1 terrain colour ($9239): 8 on the drone camera, $0B on screen 1
|
|
stx tileColour ; 74E4 zp_32 is the colour drawRadarGlyph will write
|
|
tya ; 74E6 the cell byte back into A
|
|
bmi L_74F3 ; 74E7 bit 7 set = a unit stands on this cell
|
|
L_74E9:
|
|
sec ; 74E9 terrain codes start at $40...
|
|
sbc #$40 ; 74EA ...so the glyph index in the $9C6E tile set is cell - $40
|
|
L_74EC:
|
|
bcs L_754C ; 74EC normal terrain - return the glyph
|
|
glyphFromLowCellCode:
|
|
adc #$20 ; 74EE cell below $40 (should not happen): wrap to cell - $20
|
|
jmp L_754C ; 74F0 return the glyph
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; A unit stands here. Work out its glyph from type and facing, and its colour from the side, whether
|
|
; it is spotted, dead or cloaked.
|
|
; ----------------------------------------------------------------------
|
|
L_74F3:
|
|
and #$7F ; 74F3 strip the 'unit here' bit...
|
|
tay ; 74F5 ...leaving the unit index 0-99 in Y
|
|
unitCellToGlyph:
|
|
lda radarOwnUnitColour ; 74F6 assume it is one of ours
|
|
sta tileColour ; 74F9 zp_32 = own unit colour
|
|
lda unitTypeTable,y ; 74FB the unit's type byte (bit 7 = destroyed)
|
|
testUnitAliveForRadar:
|
|
bpl L_7505 ; 74FE alive - carry on (label sub_74FE is a variant-A entry point only)
|
|
lda unitTerrainUnderTable,y ; 7500 dead: show the terrain that was hidden under it
|
|
bne L_74E9 ; 7503 and render it as terrain (note: the colour is still the own-unit colour)
|
|
L_7505:
|
|
and #$07 ; 7505 type 0 GRUNT, 1 RIDER, 2 BOOMER, 3 SPY, 4 COMCEN
|
|
tax ; 7507 X = unit type, the index into unitGlyphBaseTable
|
|
cpy #$32 ; 7508 units 0-49 are side 0, 50-99 are side 1
|
|
radarSideBranchOpcode:
|
|
bcs L_7518 ; 750A SIDE-DEPENDENT OPCODE: patched by loadOverlayB ($10A5) from $206B - $B0 (BCS) for side 0, $90 (BCC) for side 1; taken when this unit belongs to the opponent
|
|
L_750C:
|
|
lda unitFlagsTable,y ; 750C the unit's flags byte
|
|
and #$03 ; 750F bits 0-1 = facing
|
|
clc ; 7511 prepare the add
|
|
adc unitGlyphBaseTable,x ; 7512 base glyph per type ($2C GRUNT, $30 RIDER, $34 BOOMER, $38 SPY, $3C COMCEN) plus the facing
|
|
jmp L_7542 ; 7515 go and check the cloak flag
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; The unit belongs to the opponent. It is only drawn if we can currently see it; otherwise the terrain
|
|
; under it is shown, with an occasional random 'ghost' blip to keep the radar noisy.
|
|
; ----------------------------------------------------------------------
|
|
L_7518:
|
|
lda radarEnemyUnitColour ; 7518 enemy colour (1 = white on screen 1, so the sweep can find these cells; $09 brown on the camera)
|
|
sta tileColour ; 751B zp_32 = enemy colour
|
|
lda unitEnergyTable,y ; 751D the unit's energy byte; bit 7 = currently visible/spotted
|
|
bmi L_750C ; 7520 spotted - draw it normally in the enemy colour
|
|
hiddenEnemyCell:
|
|
lda unitTerrainUnderTable,y ; 7522 not spotted: fall back to the terrain hidden under it
|
|
dec radarGhostCountdown ; 7525 count down towards the next ghost blip
|
|
bpl L_74E0 ; 7528 not due yet - just show the terrain
|
|
jsr nextGameRandom ; 752A due: reseed the countdown from the shared RNG...
|
|
and #$07 ; 752D ...with 0-7, so about one unspotted enemy in five is ghosted
|
|
sta radarGhostCountdown ; 752F radarGhostCountdown ($920F)
|
|
lda unitFlagsTable,y ; 7532 the unit's flags byte
|
|
and #$03 ; 7535 bits 0-1 = facing
|
|
clc ; 7537 prepare the add
|
|
adc unitGlyphBaseTable,x ; 7538 the unit's real glyph
|
|
ora #$80 ; 753B bit 7 = tell drawRadarGlyph to speckle it with the noise masks
|
|
ldx radarTerrainColour ; 753D and draw the ghost in the terrain colour, not the enemy colour
|
|
stx tileColour ; 7540 zp_32 = terrain colour
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Cloak test, reached for every unit glyph: a cloaked unit is drawn on alternate scanlines only.
|
|
; ----------------------------------------------------------------------
|
|
L_7542:
|
|
ldx unitPathColTable,y ; 7542 the unit's waypoint-column byte, whose bit 7 is the CLOAK flag
|
|
bpl L_754C ; 7545 not cloaked - done
|
|
ldx #$FF ; 7547 $FF = half-tone
|
|
stx radarHalfToneFlag ; 7549 radarHalfToneFlag ($922D); drawRadarGlyph clears it again after the cell
|
|
L_754C:
|
|
ldx #$FF ; 754C operand patched at $74C3 - restore the caller's X
|
|
L_754E:
|
|
ldy #$FF ; 754E operand patched at $74C6 - restore the caller's Y
|
|
rts ; 7550 done; A = glyph index, zp_32 = colour
|
|
|
|
|
|
; Work byte of playStrikeZoomAnimation (see below). In overlay A the same address holds unrelated
|
|
; map-generator data, so the label is unit-scoped.
|
|
zoomStepCounter:
|
|
.byte $01 ; 7551 . zoom step counter: loaded with 3 and counted down to $FF (four magnification passes)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; Four magnification passes: 12, 10, 8 and 6 game frames are run between them.
|
|
; ----------------------------------------------------------------------
|
|
playStrikeZoomAnimation:
|
|
lda #$03 ; 7552 four zoom passes: the counter runs 3,2,1,0
|
|
sta zoomStepCounter ; 7554 pass counter for the loop below
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
L_7557:
|
|
lda #$09 ; 7557 fillRow = character row 9
|
|
sta windowOriginRow ; 7559 row of the source block magnifyWindow2x reads
|
|
lda #$10 ; 755C fillColumn = character column 16
|
|
sta windowOriginCol ; 755E column of the source block
|
|
lda #$07 ; 7561 source block is 7 cells wide
|
|
sta frameWidth ; 7563 windowWidth for magnifyWindow2x
|
|
lda #$06 ; 7566 source block is 6 cells high
|
|
sta frameHeight ; 7568 windowHeight for magnifyWindow2x
|
|
jsr magnifyWindow2x ; 756B blow those 7x6 cells up 2x2 in place, filling the whole 14x12 camera window
|
|
lda zoomStepCounter ; 756E pass number 3..0
|
|
clc ; 7571 clear carry before the add
|
|
adc #$03 ; 7572 pass+3 = 6,5,4,3
|
|
asl a ; 7574 doubled: 12,10,8,6 game frames of pause after this pass
|
|
jsr runGameFrames ; 7575 keep the battle running while the picture is held
|
|
dec zoomStepCounter ; 7578 one pass done
|
|
bpl L_7557 ; 757B branch while zoom passes remain (counter 3..0; it leaves the loop at $FF)
|
|
lda #$06 ; 757D hold the final, fully magnified picture
|
|
jsr runGameFrames ; 757F for 6 more game frames
|
|
jmp playDroneLostAnimation ; 7582 continue with the drone-destroyed animation (white flash, iris wipe, shrinking icon)
|
|
|
|
|
|
; Two state bytes of the missile screen.
|
|
missileScreenLocked:
|
|
.byte $00 ; 7585 . bit 7 set while the missile screen is inoperative (SCRIMAGE game type, or our comcen is cloaked)
|
|
|
|
; cursorMovedFlag: byteTable, 1 bytes. 0 until the missile cursor was moved, then $7F
|
|
cursorMovedFlag:
|
|
.byte $00 ; 7586 . 0 until the target cursor has been moved once, then $7F; firing is refused while it is 0
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
runMissileScreen:
|
|
lda #$00 ; 7587 clear the interlock
|
|
sta cursorMovedFlag ; 7589 a fire press does nothing until the cursor has been moved
|
|
lda missilesLeft ; 758C missiles the local player still has
|
|
sta radarMissilesLeft ; 758F display copy shown in the MISSILES box and decremented as they are launched
|
|
lda #$70 ; 7592 low byte of the packed sprite shapes at $FD70
|
|
ldy #$FD ; 7594 high byte: $FD70 in the resident high-memory block
|
|
jsr unpackRleTo0200 ; 7596 unpack the eight direction/aircraft silhouettes to $0200 for loadDirectionShapeSprite
|
|
jsr initRadarScreen ; 7599 draw the frames, the MISSILES/FIRE/DRONE ALERT panels and the 21x17 radar map
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
missileScreenResume:
|
|
lda gameTypeOptions ; 759C game option byte
|
|
and #$07 ; 759F bits 0-2 = game type (0 SCRIMAGE .. 6 DEFENDER)
|
|
bne L_75A5 ; 75A1 branch unless the type is 0 (SCRIMAGE), which is played without comcens
|
|
lda #$80 ; 75A3 no comcen: mark the missile screen inoperative
|
|
L_75A5:
|
|
ora comcenCloakedFlag ; 75A5 bit 7 of $9245 is set while our own comcen is cloaked (radar off the air)
|
|
sta missileScreenLocked ; 75A8 bit 7 = screen locked
|
|
bmi L_75B9 ; 75AB branch if locked: keep ticking the game but draw nothing
|
|
jsr refreshMissileScreen ; 75AD enemy blips, missile count and the DRONE ALERT panel
|
|
jsr drawRadarMap ; 75B0 the 21x17 cell radar map centred on our comcen
|
|
jsr restartRadarSweep ; 75B3 start a fresh sweep from the top row
|
|
jsr showComcenCrossMarker ; 75B6 sprite 2 = the target cross, parked on the comcen at grid cell (10,8)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Main loop. One pass per game frame. Everything below the 'locked' test is skipped while the screen
|
|
; is inoperative.
|
|
; ----------------------------------------------------------------------
|
|
L_75B9:
|
|
jsr updateGameFrame ; 75B9 one game tick: IRQ counters, unit updates, link traffic
|
|
lda droneArrivalFlags ; 75BC drone status flags
|
|
and #$01 ; 75BF bit 0 = our own $92 'drone destroyed' command is queued but not yet acknowledged
|
|
bne L_75C6 ; 75C1 branch if it is still pending - do not leave the screen before it is confirmed
|
|
jsr switchToRequestedScreen ; 75C3 F1/F3/F5/F7 may switch to another screen (never returns if it does)
|
|
L_75C6:
|
|
lda markerBlinkTimer ; 75C6 IRQ frame countdown for the button blink
|
|
bne L_75E7 ; 75C8 branch until the 9-frame blink interval has expired
|
|
lda #$09 ; 75CA next blink in 9 frames
|
|
sta markerBlinkTimer ; 75CC restart the countdown
|
|
lda #$0A ; 75CE character row 10
|
|
sta windowOriginRow ; 75D0 row of the 2x2 FIRE button icon
|
|
lda #$20 ; 75D3 character column 32
|
|
sta windowOriginCol ; 75D5 column of the FIRE button icon
|
|
lda #$08 ; 75D8 EOR mask 8: flips bit 3 of the %10 colour nibble, so green 5 <-> light green $D
|
|
jsr xorColourBlock2x2 ; 75DA toggle the 4 video-matrix cells of the button, making it pulse
|
|
lda radarMissilesLeft ; 75DD missiles left on the display
|
|
cmp #$02 ; 75E0 compare with 2
|
|
bcs L_75E7 ; 75E2 branch while at least 2 missiles remain
|
|
jsr blinkMissileCount ; 75E4 down to the last missile: flash the count box as well
|
|
L_75E7:
|
|
lda missileScreenLocked ; 75E7 screen lock flag
|
|
bmi L_75B9 ; 75EA branch if locked - nothing but the frame tick and the blink runs
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
L_75EC:
|
|
lda exchangeAppliedFlag ; 75EC $80 after processCommandExchange has applied a turn's commands
|
|
beq L_75F7 ; 75EF branch unless a new exchange has arrived
|
|
jsr refreshMissileScreen ; 75F1 redraw the enemy blips, the count and the DRONE ALERT panel
|
|
jsr showDroneKillBurst ; 75F4 if our missile killed the drone, play the burst now
|
|
L_75F7:
|
|
jsr updateRadarSweep ; 75F7 advance the sweep band by one step if its 2-frame slot is due
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
jsr checkFirePressedInteractive; 75FA returns A = 0 (Z set) only for a fresh, accepted press
|
|
bne L_760E ; 75FD no press this frame - go and read the joystick instead
|
|
lda cursorMovedFlag ; 75FF has the cursor ever been moved?
|
|
beq L_760E ; 7602 no - ignore the press (guards against firing at the comcen's own cell)
|
|
lda comcenRadarStatus ; 7604 non-zero once the comcen radar/fire control has been knocked out
|
|
beq L_763F ; 7607 undamaged: the shot always goes off
|
|
jsr nextGameRandom ; 7609 damaged: draw a random bit
|
|
bmi L_763F ; 760C about half the presses still get through
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
L_760E:
|
|
lda joyRepeatTimer ; 760E IRQ auto-repeat countdown
|
|
L_7610:
|
|
bne L_75B9 ; 7610 branch back while the repeat delay is still running
|
|
ldy joyDirection ; 7612 active-low joystick nibble latched by the IRQ ($0F = centred)
|
|
lda joystickDirectionTable,y; 7614 -> direction code 0-7 (N,S,E,W,NW,NE,SE,SW), $FF when centred
|
|
bmi L_75B9 ; 7617 stick centred - nothing to do this frame
|
|
ldy #$0A ; 7619 10 frames before the first auto-repeat
|
|
pha ; 761B stash the direction code across the timing code
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.)
|
|
; ----------------------------------------------------------------------
|
|
radarCursorRepeatStep:
|
|
inc joyHoldCounter ; 761C count the frames the stick has been held
|
|
lda joyHoldCounter ; 761F reload it
|
|
and joyRepeatMask ; 7622 mask = 0 on the very first deflection, $7F while the stick stays deflected
|
|
sta joyHoldCounter ; 7625 store the masked counter back
|
|
cmp #$01 ; 7628 still zero?
|
|
bcc L_762E ; 762A yes - first step, keep the long 10-frame delay
|
|
radarCursorRepeatFast:
|
|
ldy #$04 ; 762C held down: repeat every 4 frames
|
|
L_762E:
|
|
sty joyRepeatTimer ; 762E arm the IRQ countdown
|
|
lda #$7F ; 7630 mask $7F
|
|
sta joyRepeatMask ; 7632 from now on the hold counter free-runs, so the fast repeat sticks
|
|
sta cursorMovedFlag ; 7635 $7F also marks 'the cursor has been moved', which unlocks firing
|
|
pla ; 7638 recover the direction code
|
|
jsr moveRadarCursorByDirection; 7639 step the cursor one grid cell that way
|
|
jmp L_75B9 ; 763C back to the top of the loop
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Fire accepted: spend a missile, light the button, queue command $90 (missile strike at col,row) and
|
|
; play the flight and impact animation.
|
|
; ----------------------------------------------------------------------
|
|
L_763F:
|
|
lda radarMissilesLeft ; 763F missiles left
|
|
checkMissilesRemain:
|
|
beq L_760E ; 7642 none left - the press does nothing
|
|
dec radarMissilesLeft ; 7644 spend one
|
|
jsr drawMissileCount ; 7647 redraw the two-digit count
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Light the FIRE button while the missile is away.
|
|
; ----------------------------------------------------------------------
|
|
launchMissileEffect:
|
|
lda #$0A ; 764A character row 10
|
|
sta windowOriginRow ; 764C row of the button icon
|
|
lda #$20 ; 764F character column 32
|
|
L_7651:
|
|
sta windowOriginCol ; 7651 column of the button icon
|
|
lda #$05 ; 7654 $05 = %01 pixels black, %10 pixels green: button lit
|
|
ldx #$02 ; 7656 2 cells wide
|
|
ldy #$02 ; 7658 2 cells high
|
|
jsr fillScreenColourRect ; 765A write the 4 video-matrix cells
|
|
lda #$0F ; 765D sound $0F = missile launch
|
|
jsr playSound ; 765F play it
|
|
jsr radarCursorToMapCoords ; 7662 cmdParam1/cmdParam2 = the map cell under the target cursor
|
|
lda #$90 ; 7665 command $90 = missile strike at col,row
|
|
jsr queueCmd3 ; 7667 queue it for this turn's packet (and for the game film)
|
|
jsr animateMissileFlight ; 766A fly sprite 3 from the comcen to the target cell
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Impact: put the ring-burst sprite 1 exactly where the target cross (sprite 2) is.
|
|
; ----------------------------------------------------------------------
|
|
L_766D:
|
|
lda sprite2XShadow ; 766D target cross X
|
|
sta sprite1XShadow ; 7670 -> ring burst X
|
|
lda sprite2YShadow ; 7673 target cross Y
|
|
sta sprite1YShadow ; 7676 -> ring burst Y
|
|
lda #$03 ; 7679 3 radius steps
|
|
jsr animateRingBurst ; 767B small expanding ring at the impact point
|
|
jsr checkStrikeHitsDrone ; 767E did the shot hit an airborne drone?
|
|
lda #$0A ; 7681 10 frames
|
|
sta fireDebounceTimer ; 7683 swallow further fire presses for that long
|
|
jmp L_75B9 ; 7685 back to the top of the loop
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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)
|
|
; ----------------------------------------------------------------------
|
|
refreshMissileScreen:
|
|
lda #$00 ; 7688 clear the request
|
|
sta exchangeAppliedFlag ; 768A the refresh has been served
|
|
jsr redrawEnemyUnitsOnRadar ; 768D repaint all 50 enemy units with a new random dropout pattern
|
|
lda gameEndReason ; 7690 0 while a game is running; 1 = aborted, $08 = time up, $A0/$C0 = a comcen was knocked out
|
|
beq L_7698 ; 7693 branch while a game is running - so the count is only refreshed once the game is already over,
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
L_7695:
|
|
jsr drawMissileCount ; 7695 redraw the two-digit missile count
|
|
|
|
; ----------------------------------------------------------------------
|
|
; DRONE ALERT panel: flashing while any drone is up, dark otherwise.
|
|
; ----------------------------------------------------------------------
|
|
L_7698:
|
|
lda droneControlFlags ; 7698 drone control flags
|
|
and #$01 ; 769B bit 0 is set by cmdDroneMove ($52E9) while a drone - ours or theirs - is in the air
|
|
bne L_76AF ; 769D a drone is up: flash the panel
|
|
lda #$01 ; 769F sprite 1 = the ring burst
|
|
jsr clearSpriteShape ; 76A1 blank its shape
|
|
lda spriteEnableShadow ; 76A4 sprite enable shadow
|
|
and #$FC ; 76A7 clear bits 0 and 1: drone marker (sprite 0) and ring burst (sprite 1) off
|
|
L_76A9:
|
|
sta spriteEnableShadow ; 76A9 store it back
|
|
jmp resetDroneAlertPanel ; 76AC print DRONE / ALERT dark and return through it
|
|
|
|
L_76AF:
|
|
jmp flashDroneAlertPanel ; 76AF toggle the panel colour and return through it
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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)
|
|
; ----------------------------------------------------------------------
|
|
moveRadarCursorByDirection:
|
|
ldy #$00 ; 76B2 row delta defaults to 0
|
|
ldx #$00 ; 76B4 column delta defaults to 0
|
|
cmp #$04 ; 76B6 codes 0-3 are orthogonal, 4-7 diagonal
|
|
L_76B8:
|
|
bcs L_76CE ; 76B8 branch for the four diagonals
|
|
cmp #$02 ; 76BA codes 0/1 = N/S, 2/3 = E/W
|
|
bcs L_76C6 ; 76BC branch for the two horizontal directions
|
|
L_76BE:
|
|
tay ; 76BE N/S: use the code as the table index
|
|
lda directionDeltaTable,y ; 76BF $20F0: index 0 -> $FF (up), 1 -> $01 (down)
|
|
tay ; 76C2 that is the row delta
|
|
jmp offsetRadarCursor ; 76C3 apply it (column delta stays 0)
|
|
|
|
L_76C6:
|
|
tax ; 76C6 E/W: use the code as the table index
|
|
lda directionDeltaTable,x ; 76C7 $20F0: index 2 -> $01 (right), 3 -> $FF (left)
|
|
tax ; 76CA that is the column delta
|
|
jmp offsetRadarCursor ; 76CB apply it (row delta stays 0)
|
|
|
|
L_76CE:
|
|
tay ; 76CE diagonal: index the packed table with the code
|
|
lda diagonalDirectionTable,y; 76CF $1FB2+4..7 = $30,$20,$21,$31 = (column index << 4) | row index
|
|
pha ; 76D2 keep the packed byte
|
|
and #$0F ; 76D3 low nibble = row index into directionDeltaTable
|
|
tay ; 76D5 as index
|
|
lda directionDeltaTable,y ; 76D6 -1 or +1
|
|
tay ; 76D9 row delta
|
|
pla ; 76DA packed byte back
|
|
lsr a ; 76DB shift the
|
|
lsr a ; 76DC high nibble
|
|
lsr a ; 76DD down into
|
|
lsr a ; 76DE bits 0-3
|
|
tax ; 76DF column index into directionDeltaTable
|
|
lda directionDeltaTable,x ; 76E0 -1 or +1
|
|
tax ; 76E3 column delta
|
|
lda joyRepeatTimer ; 76E4 current auto-repeat delay
|
|
lsr a ; 76E6 half of it
|
|
clc ; 76E7 clear carry before the add
|
|
adc joyRepeatTimer ; 76E8 delay + delay/2
|
|
sta joyRepeatTimer ; 76EA a diagonal step costs 1.5 repeat delays, so diagonal travel is not faster
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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)
|
|
; ----------------------------------------------------------------------
|
|
offsetRadarCursor:
|
|
txa ; 76EC column delta
|
|
clc ; 76ED clear carry before the add
|
|
adc gridCursorCol ; 76EE current cursor column 0-20
|
|
tax ; 76F0 candidate column in X for the range test
|
|
stx setCursorColImm+1 ; 76F1 park it in the LDA # operand at $7701 (self-modifying code)
|
|
tya ; 76F4 row delta
|
|
clc ; 76F5 clear carry before the add
|
|
adc gridCursorRow ; 76F6 current cursor row 0-16
|
|
tay ; 76F8 candidate row in Y for the range test
|
|
sty setCursorRowImm+1 ; 76F9 park it in the LDA # operand at $7705 (self-modifying code)
|
|
jsr isRadarGridCellInRange ; 76FC C = 0 only when the cell lies inside the 21x17 grid
|
|
bcs L_7712 ; 76FF off the grid - leave the cursor where it was
|
|
setCursorColImm:
|
|
lda #$FF ; 7701 operand patched at $76F1: the accepted column
|
|
sta gridCursorCol ; 7703 new cursor column
|
|
setCursorRowImm:
|
|
lda #$FF ; 7705 operand patched at $76F9: the accepted row
|
|
sta gridCursorRow ; 7707 new cursor row
|
|
jsr gridCellToSpriteCoords ; 7709 grid cell -> sprite coordinates (column*8+$27, row*8+$43)
|
|
stx sprite2XShadow ; 770C move the target cross horizontally
|
|
sty sprite2YShadow ; 770F and vertically
|
|
L_7712:
|
|
rts ; 7712 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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) 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.
|
|
; ----------------------------------------------------------------------
|
|
animateMissileFlight:
|
|
lda #$0A ; 7713 the comcen is always drawn at grid column 10
|
|
ldy gridCursorCol ; 7715 target column 0-20
|
|
|
|
; ----------------------------------------------------------------------
|
|
; $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).
|
|
; ----------------------------------------------------------------------
|
|
missileLineFromComcen:
|
|
jsr rebasePairToMin ; 7717 X = min of the two, A and Y = both values minus that min
|
|
stx scratch20 ; 771A left column of the bounding box, in grid cells
|
|
sta lineCurCol ; 771C line start column, relative to the box
|
|
sta scratch1C ; 771E keep a copy: the line is walked twice
|
|
sty lineEndCol ; 7720 line end column, relative to the box
|
|
sty scratch1E ; 7722 copy
|
|
lda #$08 ; 7724 the comcen is always drawn at grid row 8
|
|
ldy gridCursorRow ; 7726 target row 0-16
|
|
jsr rebasePairToMin ; 7728 same rebasing for the rows
|
|
stx scratch21 ; 772B top row of the bounding box
|
|
sta lineCurRow ; 772D line start row
|
|
sta scratch1D ; 772F copy
|
|
sty lineEndRow ; 7731 line end row
|
|
sty scratch1F ; 7733 copy
|
|
tya ; 7735 end row
|
|
ora scratch1E ; 7736 OR in the end column
|
|
ora scratch1D ; 7738 OR in the start row
|
|
L_773A:
|
|
ora scratch1C ; 773A OR in the start column
|
|
bne L_7741 ; 773C branch if any of the four is non-zero
|
|
jmp L_7846 ; 773E all four zero: the cursor sits on the comcen itself, nothing to fly
|
|
|
|
L_7741:
|
|
cmp #$04 ; 7741 is the longest side at least 4 cells?
|
|
bcs L_7755 ; 7743 yes - the coordinates already have enough resolution
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Very short shot: scale the four coordinates by 4 so that the three-step probe below still finds the
|
|
; true direction of the line.
|
|
; ----------------------------------------------------------------------
|
|
asl lineCurCol ; 7745 start column x2
|
|
asl lineCurCol ; 7747 x4
|
|
asl lineCurRow ; 7749 start row x2
|
|
asl lineCurRow ; 774B x4
|
|
asl lineEndCol ; 774D end column x2
|
|
asl lineEndCol ; 774F x4
|
|
L_7751:
|
|
asl lineEndRow ; 7751 end row x2
|
|
asl lineEndRow ; 7753 x4
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
L_7755:
|
|
lda lineCurCol ; 7755 line start column
|
|
sta scratch1A ; 7757 remember it
|
|
lda lineCurRow ; 7759 line start row
|
|
sta scratch1B ; 775B remember it
|
|
L_775D:
|
|
jsr initLineStepper ; 775D set up the Bresenham walk from (start) to (end)
|
|
jsr stepLine ; 7760 step 1
|
|
jsr stepLine ; 7763 step 2
|
|
jsr stepLine ; 7766 step 3 - far enough to know the direction
|
|
lda scratch1A ; 7769 segment start column
|
|
ldy lineCurCol ; 776B segment end column after 3 steps
|
|
jsr rebasePairToMin ; 776D rebase the short segment to its own origin
|
|
sta scratch1A ; 7770 segment start column, 0-based
|
|
sty lineEndCol ; 7772 segment end column
|
|
lda scratch1B ; 7774 segment start row
|
|
ldy lineCurRow ; 7776 segment end row after 3 steps
|
|
jsr rebasePairToMin ; 7778 rebase the rows the same way
|
|
sta scratch1B ; 777B segment start row, 0-based
|
|
sty lineEndRow ; 777D segment end row
|
|
lda #$03 ; 777F sprite 3 is the missile
|
|
jsr clearSpriteShape ; 7781 blank its 64-byte shape
|
|
jsr plotMissileTrailSegment ; 7784 plot the 4-pixel dash
|
|
inc scratch1A ; 7787 shift the start one pixel right
|
|
inc lineEndCol ; 7789 and the end with it
|
|
jsr plotMissileTrailSegment ; 778B plot it again: the dash is now 2 pixels thick
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
ldy #$03 ; 778E index of the last of the four saved coordinates ($1C..$1F)
|
|
L_7790:
|
|
lda a:scratch1C,y ; 7790 one saved coordinate (absolute addressing forced so it is a 3-byte instruction)
|
|
asl a ; 7793 x2
|
|
asl a ; 7794 x4
|
|
L_7795:
|
|
asl a ; 7795 x8 - one grid cell is 8 pixels
|
|
sta a:scratch1C,y ; 7796 write it back
|
|
dey ; 7799 next coordinate
|
|
bpl L_7790 ; 779A loop over start column, start row, end column, end row
|
|
lda scratch1C ; 779C start column in pixels
|
|
sta lineCurCol ; 779E into the line stepper
|
|
lda scratch1D ; 77A0 start row in pixels
|
|
sta lineCurRow ; 77A2 into the line stepper
|
|
lda scratch1E ; 77A4 end column in pixels
|
|
sta lineEndCol ; 77A6 into the line stepper
|
|
lda scratch1F ; 77A8 end row in pixels
|
|
sta lineEndRow ; 77AA into the line stepper
|
|
jsr initLineStepper ; 77AC set the walk up again, now measured in pixels
|
|
jsr stepLine ; 77AF skip the first pixel
|
|
jsr stepLine ; 77B2 and the second, so the missile starts clear of the comcen
|
|
ldx scratch20 ; 77B5 bounding box origin column (grid cells)
|
|
ldy scratch21 ; 77B7 bounding box origin row
|
|
jsr isRadarGridCellInRange ; 77B9 the returned carry is never tested - a leftover range check
|
|
jsr gridCellToSpriteCoords ; 77BC -> sprite coordinates of that corner cell
|
|
txa ; 77BF sprite X of the corner
|
|
clc ; 77C0 clear carry before the add
|
|
adc #$0B ; 77C1 +11 pixels centres the dash inside the cell
|
|
sta missileSpriteXOrigin+1 ; 77C3 patch the ADC # operand at $77DF (self-modifying code)
|
|
tya ; 77C6 sprite Y of the corner
|
|
clc ; 77C7 clear carry before the add
|
|
adc #$09 ; 77C8 +9 pixels centres the dash vertically
|
|
sta missileSpriteYOrigin+1 ; 77CA patch the ADC # operand at $77E7 (self-modifying code)
|
|
lda #$00 ; 77CD restart the frame counter
|
|
sta missileFlightTick ; 77CF $92F8 is incremented once per frame by the IRQ ($129C)
|
|
lda #$06 ; 77D2 first move after 6 frames
|
|
sta missileTickThresholdCmp+1; 77D4 patch the CMP # operand at $7807 (self-modifying code)
|
|
lda #$00 ; 77D7 no moves made yet
|
|
sta missileStepIndexLoad+1 ; 77D9 patch the LDA # operand at $782A (self-modifying code)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Per-move: place sprite 3 at the current pixel, then wait 6 frames while the game keeps running.
|
|
; ----------------------------------------------------------------------
|
|
L_77DC:
|
|
lda lineCurCol ; 77DC current pixel column along the line
|
|
clc ; 77DE clear carry before the add
|
|
missileSpriteXOrigin:
|
|
adc #$FF ; 77DF operand patched at $77C3: sprite X of the bounding box corner + 11
|
|
sta sprite3XShadow ; 77E1 sprite 3 X position
|
|
lda lineCurRow ; 77E4 current pixel row along the line
|
|
clc ; 77E6 clear carry before the add
|
|
missileSpriteYOrigin:
|
|
adc #$FF ; 77E7 operand patched at $77CA: sprite Y of the bounding box corner + 9
|
|
sta sprite3YShadow ; 77E9 sprite 3 Y position
|
|
lda #$01 ; 77EC colour 1 = white
|
|
sta sprite3ColourShadow ; 77EE sprite 3 colour
|
|
lda #$08 ; 77F1 sprite 3 enable bit
|
|
jsr enableSprites ; 77F3 switch it on
|
|
jsr saveLineStepperState ; 77F6 the game frame below reuses the line stepper ($8C-$97), so save it
|
|
L_77F9:
|
|
jsr updateRadarSweep ; 77F9 keep the radar sweep running during the flight
|
|
jsr updateGameFrame ; 77FC one game tick: units keep moving, the link keeps talking
|
|
L_77FF:
|
|
lda gameEndReason ; 77FF 0 while a game is running
|
|
bne L_783E ; 7802 the game ended under us - drop the animation
|
|
L_7804:
|
|
lda missileFlightTick ; 7804 free-running frame counter
|
|
missileTickThresholdCmp:
|
|
cmp #$FF ; 7807 operand patched at $77D4/$781F: the frame at which the next move is due
|
|
bcc L_77F9 ; 7809 not yet - run another frame
|
|
lda missileTickThresholdCmp+1; 780B current threshold
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
L_780E:
|
|
bpl L_781C ; 780E still below $80 - no wrap needed
|
|
and #$7F ; 7810 fold the threshold back into 0-$7F
|
|
L_7812:
|
|
tax ; 7812 keep it
|
|
lda missileFlightTick ; 7813 frame counter
|
|
and #$7F ; 7816 fold it back the same way
|
|
sta missileFlightTick ; 7818 store it
|
|
txa ; 781B threshold back into A
|
|
L_781C:
|
|
clc ; 781C clear carry before the add
|
|
adc #$06 ; 781D the next move is due 6 frames later
|
|
sta missileTickThresholdCmp+1; 781F patch the CMP # operand at $7807
|
|
jsr restoreLineStepperState ; 7822 put the line stepper state back the way the game frame found it
|
|
inc missileStepIndexLoad+1 ; 7825 count this move (patches the LDA # operand at $782A)
|
|
L_7828:
|
|
ldx #$00 ; 7828 at least one pixel step per move
|
|
missileStepIndexLoad:
|
|
lda #$FF ; 782A operand patched at $77D9/$7825: the move number
|
|
bmi L_7834 ; 782C never taken in practice - the flight ends long before 128 moves
|
|
lsr a ; 782E move/2
|
|
lsr a ; 782F move/4
|
|
tay ; 7830 as table index
|
|
ldx squaresTable,y ; 7831 n*n extra steps: the missile accelerates as it travels
|
|
L_7834:
|
|
jsr stepLine ; 7834 advance one pixel along the line
|
|
beq L_783E ; 7837 returns 0 when the line is exhausted - the missile has arrived
|
|
dex ; 7839 one of the extra steps used
|
|
bpl L_7834 ; 783A keep stepping while X >= 0
|
|
bmi L_77DC ; 783C always taken: redraw the sprite at its new position
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Common exit: switch the missile sprite off again.
|
|
; ----------------------------------------------------------------------
|
|
L_783E:
|
|
lda spriteEnableShadow ; 783E sprite enable shadow
|
|
|
|
; ----------------------------------------------------------------------
|
|
; $7841 is only a fall-through point in this overlay (the same address is printMapName in the
|
|
; overlay A variant).
|
|
; ----------------------------------------------------------------------
|
|
hideMissileSprite:
|
|
and #$F7 ; 7841 clear bit 3 = sprite 3 off
|
|
sta spriteEnableShadow ; 7843 store it back
|
|
L_7846:
|
|
rts ; 7846 done (also the early exit for a zero-length shot)
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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)
|
|
; ----------------------------------------------------------------------
|
|
plotMissileTrailSegment:
|
|
lda scratch1A ; 7847 segment start column
|
|
sta lineCurCol ; 7849 into the line stepper
|
|
lda scratch1B ; 784B segment start row
|
|
sta lineCurRow ; 784D into the line stepper
|
|
jsr initLineStepper ; 784F set the Bresenham walk up
|
|
jsr plotPathSpritePixel ; 7852 plot pixel 1 (the start)
|
|
jsr stepAndPlotTrailPixel ; 7855 step and plot pixel 2
|
|
L_7858:
|
|
jsr stepAndPlotTrailPixel ; 7858 step and plot pixel 3
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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)
|
|
; ----------------------------------------------------------------------
|
|
stepAndPlotTrailPixel:
|
|
jsr stepLine ; 785B advance one pixel
|
|
jmp plotPathSpritePixel ; 785E set that pixel and return to the caller
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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)
|
|
; ----------------------------------------------------------------------
|
|
saveLineStepperState:
|
|
ldy #$0B ; 7861 12 bytes: lineErrRow $8C .. lineStepCol $97
|
|
L_7863:
|
|
lda a:lineErrRow,y ; 7863 read one byte of the live stepper state (absolute addressing so Y indexes past $FF safely)
|
|
sta savedLineState,y ; 7866 park it in the $92CA save area
|
|
dey ; 7869 next byte
|
|
bpl L_7863 ; 786A loop over all 12
|
|
rts ; 786C done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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)
|
|
; ----------------------------------------------------------------------
|
|
restoreLineStepperState:
|
|
ldy #$0B ; 786D 12 bytes
|
|
L_786F:
|
|
lda savedLineState,y ; 786F read the saved byte
|
|
|
|
; ----------------------------------------------------------------------
|
|
; $7872 is only a fall-through point here (the same address is randomTileVariant in the overlay A
|
|
; variant).
|
|
; ----------------------------------------------------------------------
|
|
restoreLineStateStore:
|
|
sta a:lineErrRow,y ; 7872 write it back into the live stepper state
|
|
dey ; 7875 next byte
|
|
bpl L_786F ; 7876 loop over all 12
|
|
rts ; 7878 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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)
|
|
; ----------------------------------------------------------------------
|
|
rebasePairToMin:
|
|
sty scratch18 ; 7879 keep the second value
|
|
cmp scratch18 ; 787B which of the two is smaller?
|
|
L_787D:
|
|
bcs L_788A ; 787D branch if A >= Y
|
|
sta scratch18 ; 787F A is the smaller one - it becomes the offset
|
|
tya ; 7881 the larger value
|
|
sec ; 7882 set carry for the subtract
|
|
sbc scratch18 ; 7883 Y - A
|
|
tay ; 7885 back into Y
|
|
lda #$00 ; 7886 and A itself becomes 0
|
|
beq L_788E ; 7888 always taken
|
|
L_788A:
|
|
sbc scratch18 ; 788A A - Y (carry is already set by the CMP at $787B)
|
|
ldy #$00 ; 788C and Y becomes 0
|
|
L_788E:
|
|
ldx scratch18 ; 788E return the offset that was subtracted
|
|
|
|
; ----------------------------------------------------------------------
|
|
; $7890 carries a label only because the overlay A variant writes to this byte; here it is just the
|
|
; RTS of rebasePairToMin.
|
|
; ----------------------------------------------------------------------
|
|
rebasePairToMinRts:
|
|
rts ; 7890 done
|
|
|
|
|
|
; 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.)
|
|
radarSweepStep:
|
|
.byte $00 ; 7891 . sweep step 0-30; steps 0-16 light grid row = step, steps 14-30 re-hide row = step-14
|
|
radarSweepRow:
|
|
.byte $00 ; 7892 . grid row paintRadarSweepRow is currently working on
|
|
radarSweepDarkFlag:
|
|
.byte $00 ; 7893 . $FF = this whole sweep stays dark (a damaged radar drops roughly every other sweep)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
updateRadarSweep:
|
|
lda radarSweepTick ; 7894 free-running frame counter, incremented once per frame by the IRQ
|
|
radarSweepThresholdCmp:
|
|
cmp #$FF ; 7897 operand patched at $78A1/$78D6: the frame number of the next sweep step
|
|
bcc L_78CA ; 7899 not yet time for the next step
|
|
lda radarSweepThresholdCmp+1; 789B current threshold
|
|
clc ; 789E clear carry before the add
|
|
adc #$02 ; 789F one sweep step every 2 frames
|
|
sta radarSweepThresholdCmp+1; 78A1 patch the CMP # operand at $7897
|
|
lda radarSweepStep ; 78A4 current step number
|
|
cmp #$1F ; 78A7 31 steps make one full sweep
|
|
bcs restartRadarSweep ; 78A9 sweep finished - start the next one (and return through it)
|
|
sta radarSweepRow ; 78AB the row to light is the step number itself
|
|
cmp #$11 ; 78AE the grid has 17 rows
|
|
bcs L_78B9 ; 78B0 steps 17-30 are past the bottom, so there is no leading row left to light
|
|
jsr paintRadarSweepRow ; 78B2 light grid row = step
|
|
lda radarSweepStep ; 78B5 step number again
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Reached with carry already set from the BCS at $78B0; the SEC only matters on the fall-through.
|
|
; ----------------------------------------------------------------------
|
|
D_78B8:
|
|
sec ; 78B8 set carry for the subtract
|
|
L_78B9:
|
|
sbc #$0E ; 78B9 trailing edge of the band = step - 14 (a 14-row bright band)
|
|
sta radarSweepRow ; 78BB row to re-hide
|
|
bcc L_78C7 ; 78BE still negative - the band has not fully entered the window yet
|
|
cmp #$11 ; 78C0 17 grid rows
|
|
bcs L_78C7 ; 78C2 past the bottom row - nothing to re-hide
|
|
jsr paintRadarSweepRow ; 78C4 darken that row again
|
|
L_78C7:
|
|
inc radarSweepStep ; 78C7 next step
|
|
L_78CA:
|
|
rts ; 78CA done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
restartRadarSweep:
|
|
lda #$00 ; 78CB zero
|
|
sta radarSweepStep ; 78CD sweep starts at grid row 0
|
|
sta radarSweepDarkFlag ; 78D0 assume a normal, visible sweep
|
|
sta radarSweepTick ; 78D3 restart the frame counter
|
|
sta radarSweepThresholdCmp+1; 78D6 threshold 0 = the first step happens on the next call
|
|
ldx comcenRadarStatus ; 78D9 non-zero once the comcen radar has been knocked out
|
|
beq L_78E6 ; 78DC radar intact - always sweep normally
|
|
jsr nextGameRandom ; 78DE damaged: draw a random byte
|
|
bmi L_78E6 ; 78E1 about half the draws still give a normal sweep
|
|
dec radarSweepDarkFlag ; 78E3 0 -> $FF: this whole sweep will show nothing
|
|
L_78E6:
|
|
rts ; 78E6 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
paintRadarSweepRow:
|
|
tay ; 78E7 grid row 0-16 as table index
|
|
clc ; 78E8 clear carry before the add
|
|
lda screenRowLoTableFrom3,y ; 78E9 $9382+3: low byte of the screen address of row+3
|
|
adc #$03 ; 78EC +3 characters: the radar window starts at column 3
|
|
sta blockPtr ; 78EE low byte of the video matrix pointer
|
|
sta screenPtr ; 78F0 and of the colour RAM pointer
|
|
lda screenRowHiTableFrom3,y ; 78F2 $939B+3: high byte of the screen address of row+3
|
|
adc #$00 ; 78F5 propagate the carry from the +3
|
|
sta blockPtrHi ; 78F7 blockPtr now points into the video matrix at $8C00
|
|
clc ; 78F9 clear carry before the add
|
|
adc #$4C ; 78FA +$4C pages: $8C00 + $4C00 = colour RAM $D800
|
|
sta screenPtrHi ; 78FC screenPtr now points at the matching colour RAM cell
|
|
ldy #$00 ; 78FE start at the left edge of the row
|
|
|
|
; ----------------------------------------------------------------------
|
|
; One cell per pass; 21 cells make up the width of the radar window.
|
|
; ----------------------------------------------------------------------
|
|
L_7900:
|
|
lda (screenPtr),y ; 7900 colour RAM byte = the colour of this cell's %11 pixels
|
|
and #$0F ; 7902 only the low nibble is real
|
|
cmp #$01 ; 7904 colour 1 (white) is what drawRadarMap gives a spotted enemy unit
|
|
bne L_790D ; 7906 not an enemy blip - it only shows while the band passes
|
|
lda comcenRadarStatus ; 7908 is the comcen radar still working?
|
|
beq L_791C ; 790B yes - a spotted enemy stays lit permanently
|
|
L_790D:
|
|
lda #$00 ; 790D default: both nibbles black, the cell is hidden
|
|
ldx radarSweepDarkFlag ; 790F is this sweep a dead one?
|
|
bmi L_791E ; 7912 yes - leave everything dark
|
|
ldx radarSweepRow ; 7914 the row being painted
|
|
cpx radarSweepStep ; 7917 is it the leading (bright) edge of the band?
|
|
bne L_791E ; 791A no - this call is the trailing edge, so hide the row
|
|
L_791C:
|
|
lda #$F0 ; 791C $F0 = %01 pixels light grey, %10 pixels black: the cell shows
|
|
L_791E:
|
|
sta (blockPtr),y ; 791E write the video matrix byte
|
|
iny ; 7920 next cell
|
|
cpy #$15 ; 7921 21 cells across
|
|
bcc L_7900 ; 7923 loop
|
|
rts ; 7925 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
redrawEnemyUnitsOnRadar:
|
|
jsr buildRadarNoiseMasks ; 7926 new random dropout mask set for the unit glyphs
|
|
lda comcenUnit ; 7929 49 for player 0, 99 for player 1
|
|
eor #$52 ; 792C $31 EOR $52 = $63 and $63 EOR $52 = $31: the opposing side's comcen index
|
|
sta scratch18 ; 792E unit index, walked downwards from there
|
|
lda #$32 ; 7930 50 units per side
|
|
sta scratch19 ; 7932 loop counter
|
|
|
|
; ----------------------------------------------------------------------
|
|
; One enemy unit per pass, from the enemy comcen down to that side's first unit.
|
|
; ----------------------------------------------------------------------
|
|
L_7934:
|
|
ldy scratch18 ; 7934 current unit index
|
|
lda unitColTable,y ; 7936 unitColTable[unit] = map column
|
|
sta savedRegX ; 7939 drawScreen1Cell takes the column in $55
|
|
L_793B:
|
|
lda unitRowTable,y ; 793B unitRowTable[unit] = map row (the next 100-byte array, $64 further on)
|
|
sta savedRegY ; 793E drawScreen1Cell takes the row in $56
|
|
tya ; 7940 unit index
|
|
ora #$80 ; 7941 map cell code $80|index = 'this unit stands on this cell'
|
|
ldx unitTypeTable,y ; 7943 unitTypeTable[unit]; bit 7 set = the unit is dead
|
|
bpl L_794D ; 7946 alive - draw the unit itself
|
|
lda unitTerrainUnderTable,y ; 7948 dead: draw the terrain the corpse was standing on instead
|
|
bmi L_7952 ; 794B $FF = the unit is not on the map at all, skip it
|
|
L_794D:
|
|
sta savedRegA ; 794D drawScreen1Cell takes the cell value in $54
|
|
jsr drawScreen1Cell ; 794F plot that one cell of the radar window
|
|
L_7952:
|
|
dec scratch18 ; 7952 previous unit index
|
|
dec scratch19 ; 7954 one unit done
|
|
bne L_7934 ; 7956 loop over all 50 units of the enemy side
|
|
rts ; 7958 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
scrollRadarOnComcenMove:
|
|
jsr getComcenCentredViewOrigin; 7959 X = comcenCol - 10, Y = comcenRow - 8: the map cell shown at the top-left of the window
|
|
cpx viewOriginCol ; 795C same column as the window currently shows?
|
|
bne L_7964 ; 795E no - the comcen has moved sideways
|
|
cpy viewOriginRow ; 7960 same row?
|
|
L_7962:
|
|
beq L_7986 ; 7962 origin unchanged - the comcen just repainted itself, nothing to do
|
|
L_7964:
|
|
jsr isRadarCursorOnComcen ; 7964 is the target cursor sitting on the comcen cell (10,8)?
|
|
beq L_797E ; 7967 yes - leave it there so it rides along with the comcen
|
|
stx subNewOriginCol+1 ; 7969 park the new origin column in the SBC # operand at $796F
|
|
lda viewOriginCol ; 796C old origin column
|
|
sec ; 796E set carry for the subtract
|
|
subNewOriginCol:
|
|
sbc #$FF ; 796F operand patched at $7969: the new origin column
|
|
tax ; 7971 column shift = old - new
|
|
sty subNewOriginRow+1 ; 7972 park the new origin row in the SBC # operand at $7978
|
|
lda viewOriginRow ; 7975 old origin row
|
|
sec ; 7977 set carry for the subtract
|
|
subNewOriginRow:
|
|
sbc #$FF ; 7978 operand patched at $7972: the new origin row
|
|
tay ; 797A row shift = old - new
|
|
jsr offsetRadarCursor ; 797B shift the cursor so it keeps pointing at the same map cell (refused if it would leave the grid)
|
|
L_797E:
|
|
jsr drawRadarMap ; 797E redraw the whole 21x17 window and set zp_A5/zp_A6 to the new origin
|
|
lda #$00 ; 7981 zero
|
|
sta radarSweepStep ; 7983 restart the sweep at the top row of the new picture
|
|
L_7986:
|
|
rts ; 7986 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
isRadarCursorOnComcen:
|
|
lda gridCursorCol ; 7987 cursor column 0-20
|
|
cmp #$0A ; 7989 the comcen is always drawn at grid column 10
|
|
bne L_7991 ; 798B different column - return with Z clear
|
|
lda gridCursorRow ; 798D cursor row 0-16
|
|
cmp #$08 ; 798F the comcen is always drawn at grid row 8; Z set = cursor is on it
|
|
L_7991:
|
|
rts ; 7991 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
radarCursorToMapCoords:
|
|
lda gridCursorCol ; 7992 cursor column inside the grid
|
|
clc ; 7994 clear carry before the add
|
|
adc viewOriginCol ; 7995 map column of the window's left edge
|
|
sta cmdParam1 ; 7997 command argument 1 = map column of the target cell
|
|
lda gridCursorRow ; 7999 cursor row inside the grid
|
|
clc ; 799B clear carry before the add
|
|
adc viewOriginRow ; 799C map row of the window's top edge
|
|
sta cmdParam2 ; 799E command argument 2 = map row of the target cell
|
|
rts ; 79A0 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
checkStrikeHitsDrone:
|
|
lda droneControlFlags ; 79A1 drone flags: 0 = no drone in the air
|
|
beq L_79D7 ; 79A4 no drone - nothing to hit
|
|
lda droneArrivalFlags ; 79A6 non-zero once the drone has been hit, detonated or has landed
|
|
bne L_79D7 ; 79A9 the drone is already out of the game
|
|
jsr radarCursorToMapCoords ; 79AB cmdParam1/2 = map cell the missile was aimed at
|
|
lda droneViewOriginCol ; 79AE the drone's own map column
|
|
sec ; 79B1 set carry for the subtract
|
|
sbc cmdParam1 ; 79B2 difference to the targeted column
|
|
cmp #$FF ; 79B4 compare with -1
|
|
bcs L_79BC ; 79B6 difference is -1: the target is the drone's right-hand cell, still a hit
|
|
cmp #$01 ; 79B8 compare with +1
|
|
bcs L_79D7 ; 79BA difference is not 0 or -1 - the shot missed
|
|
L_79BC:
|
|
lda droneViewOriginRow ; 79BC the drone's own map row
|
|
L_79BF:
|
|
sec ; 79BF set carry for the subtract
|
|
sbc cmdParam2 ; 79C0 difference to the targeted row
|
|
L_79C2:
|
|
cmp #$FF ; 79C2 compare with -1
|
|
bcs L_79CA ; 79C4 difference is -1: the drone's lower cell, still a hit
|
|
cmp #$01 ; 79C6 compare with +1
|
|
bcs L_79D7 ; 79C8 difference is not 0 or -1 - the shot missed
|
|
L_79CA:
|
|
lda #$92 ; 79CA command $92 = the drone is destroyed / its flight is over
|
|
jsr queueCmdBytePending ; 79CC queue the single command byte for this turn's packet
|
|
lda droneArrivalFlags ; 79CF drone flags
|
|
ora #$01 ; 79D2 bit 0 = our own $92 is queued and not yet acknowledged
|
|
sta droneArrivalFlags ; 79D4 store it back
|
|
L_79D7:
|
|
rts ; 79D7 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
showDroneKillBurst:
|
|
lda filmPlaybackMode ; 79D8 bit 7 = a recorded game film is being played back
|
|
bmi L_7A11 ; 79DB during playback the effect is skipped
|
|
lda droneArrivalFlags ; 79DD drone flags
|
|
and #$10 ; 79E0 bit 4 = cmdDroneArrived executed our own $92
|
|
beq L_7A11 ; 79E2 not our kill - nothing to show
|
|
lda droneArrivalFlags ; 79E4 drone flags again
|
|
eor #$10 ; 79E7 clear bit 4
|
|
ora #$20 ; 79E9 set bit 5 instead, so the burst plays exactly once
|
|
sta droneArrivalFlags ; 79EB store it back
|
|
lda #$00 ; 79EE zero
|
|
sta droneControlFlags ; 79F0 there is no drone under control any more
|
|
lda #$17 ; 79F3 sound $17 = explosion
|
|
jsr playSound ; 79F5 play it
|
|
lda #$09 ; 79F8 sound $09 = debris
|
|
jsr playSound ; 79FA play it
|
|
ldx droneViewOriginCol ; 79FD the drone's map column
|
|
ldy droneViewOriginRow ; 7A00 and map row
|
|
jsr getUnitSpriteCentreOnGrid; 7A03 -> sprite coordinates of that cell's centre (C = 1 if off the window; not tested here)
|
|
stx sprite1XShadow ; 7A06 ring burst sprite 1 X
|
|
sty sprite1YShadow ; 7A09 ring burst sprite 1 Y
|
|
lda #$0B ; 7A0C 11 radius steps = a big burst
|
|
jsr animateRingBurst ; 7A0E play the expanding/contracting ring
|
|
L_7A11:
|
|
rts ; 7A11 done
|
|
|
|
|
|
; Single unreferenced $00 byte between the two routines (alignment filler).
|
|
padding7A12:
|
|
.byte $00 ; 7A12 . filler
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
mapCellToRadarGrid:
|
|
tya ; 7A13 map row
|
|
clc ; 7A14 clear carry before the add
|
|
adc #$08 ; 7A15 the comcen is drawn at grid row 8
|
|
sec ; 7A17 set carry for the subtract
|
|
ldy comcenUnit ; 7A18 the local comcen's unit index (49 or 99)
|
|
sbc unitRowTable,y ; 7A1B minus the comcen's map row
|
|
L_7A1E:
|
|
tay ; 7A1E Y = grid row
|
|
txa ; 7A1F map column
|
|
clc ; 7A20 clear carry before the add
|
|
adc #$0A ; 7A21 the comcen is drawn at grid column 10
|
|
sec ; 7A23 set carry for the subtract
|
|
L_7A24:
|
|
ldx comcenUnit ; 7A24 the local comcen's unit index again
|
|
sbc unitColTable,x ; 7A27 minus the comcen's map column
|
|
tax ; 7A2A X = grid column; fall into the range test
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
isRadarGridCellInRange:
|
|
cpy #$11 ; 7A2B 17 grid rows
|
|
bcs L_7A31 ; 7A2D off the bottom, or negative (which wraps above $80)
|
|
cpx #$15 ; 7A2F 21 grid columns; C = 0 only if the column is inside too
|
|
L_7A31:
|
|
rts ; 7A31 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
initRadarScreen:
|
|
lda #$00 ; 7A32 zero
|
|
sta useDrawColourFlag ; 7A34 useFixedColourFlag = 0: glyphs take their colour from fontColorTable
|
|
sta drawColourByte ; 7A37 fixed draw colour byte = 0
|
|
sta blockFillFlag ; 7A3A block fill flag = 0
|
|
sta blockFillChar ; 7A3D block fill character = 0
|
|
lda #$0C ; 7A40 colour $0C = medium grey
|
|
sta vicBorderShadow ; 7A42 border colour shadow (the IRQ copies it to $D020)
|
|
sta vicBg0Shadow ; 7A45 background colour shadow ($D021)
|
|
lda #$00 ; 7A48 zero
|
|
sta cellBackgroundColour ; 7A4A screen 1 writes glyph colours straight to colour RAM, so no background nibble is ORed in
|
|
lda #$01 ; 7A4D 1
|
|
sta screenBuiltFlag ; 7A4F every screen builder sets this flag; nothing reads it
|
|
lda #$00 ; 7A51 zero
|
|
sta blockFillFlag ; 7A53 redundant - D_2A2B was already cleared at $7A3A
|
|
L_7A56:
|
|
jsr drawMissilePanel ; 7A56 MISSILES panel, bottom right
|
|
jsr drawMissileCount ; 7A59 the two-digit count inside it
|
|
jsr drawFireButton ; 7A5C FIRE button panel
|
|
jsr drawDroneAlertPanel ; 7A5F DRONE ALERT panel, top right
|
|
jsr redrawRadarMapWindow ; 7A62 the two frames plus the 21x17 radar map itself
|
|
jsr drawMainWindowFrame ; 7A65 the bottom status/message frame shared by all screens
|
|
jmp redrawStatusMessage ; 7A68 reprint the current status-line message and return through it
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
redrawRadarMapWindow:
|
|
ldx #$05 ; 7A6B window 5 = the outer frame, 25x21 cells at (column 1, row 1)
|
|
jsr drawWindowFrame ; 7A6D draw it
|
|
ldx #$06 ; 7A70 window 6 = the inner frame, 23x19 cells at (column 2, row 2)
|
|
jsr drawWindowFrame ; 7A72 draw it
|
|
jmp drawRadarMap ; 7A75 fill it with the 21x17 cell radar map and return through it
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
drawMissilePanel:
|
|
jsr setFullScreenWindow ; 7A78 text window = the whole screen, so the coordinates below are absolute
|
|
ldx #$07 ; 7A7B window 7 = 12x7 cells at column 27, row 15
|
|
jsr drawWindowFrame ; 7A7D draw the frame
|
|
jsr drawWindowInnerCorners ; 7A80 put the four inner corner marks (character $38) inside it
|
|
L_7A83:
|
|
ldx #$08 ; 7A83 window 8 = 4x4 cells at column 31, row 16
|
|
jsr drawWindowFrame ; 7A85 draw the inner frame around the counter
|
|
lda #$11 ; 7A88 character row 17
|
|
sta windowOriginRow ; 7A8A row of the counter box
|
|
lda #$20 ; 7A8D character column 32
|
|
sta windowOriginCol ; 7A8F column of the counter box
|
|
lda #$30 ; 7A92 $30 = %01 pixels cyan, %10 pixels black
|
|
ldx #$02 ; 7A94 2 cells wide
|
|
ldy #$02 ; 7A96 2 cells high
|
|
jsr fillScreenColourRect ; 7A98 paint the counter box background
|
|
lda #$CF ; 7A9B colour byte for the caption glyphs
|
|
sta textColour ; 7A9D text colour
|
|
ldy #$01 ; 7A9F bitmap draw mode 1
|
|
jsr setBitmapDrawMode ; 7AA1 OR the glyphs into the bitmap so the frame keeps showing through
|
|
lda #$1D ; 7AA4 column 29
|
|
sta textCursorCol ; 7AA6 text cursor column
|
|
lda #$13 ; 7AA8 row 19
|
|
sta textCursorRow ; 7AAA text cursor row
|
|
jsr updateCursorPointers ; 7AAC recompute the bitmap/screen pointers from the cursor
|
|
lda #$9E ; 7AAF low byte of the string address
|
|
ldy #$CC ; 7AB1 $CC9E = 'MISSILES' in the $C000 text overlay
|
|
jsr printString ; 7AB3 print it
|
|
jmp resetDrawModes ; 7AB6 back to replace mode and return through it
|
|
|
|
|
|
; Colour byte the missile counter box is currently painted with.
|
|
missileCountColour:
|
|
.byte $00 ; 7AB9 . $30 (cyan digits on black) or $03 (black digits on cyan) while it flashes
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
drawMissileCount:
|
|
lda radarMissilesLeft ; 7ABA missiles left
|
|
jsr splitDecimalDigits ; 7ABD split into $C44E hundreds, $C44F tens, $C450 ones
|
|
lda #$11 ; 7AC0 character row 17
|
|
sta scratch18 ; 7AC2 drawTallGlyph's row
|
|
lda #$20 ; 7AC4 character column 32
|
|
sta scratch19 ; 7AC6 drawTallGlyph's column
|
|
lda #$FF ; 7AC8 non-zero
|
|
sta useDrawColourFlag ; 7ACA useFixedColourFlag: take the colour from $2A2A, not from the font colour table
|
|
L_7ACD:
|
|
ldx #$30 ; 7ACD $30 = %01 pixels cyan, %10 pixels black
|
|
stx drawColourByte ; 7ACF the fixed draw colour
|
|
stx missileCountColour ; 7AD2 remember it so blinkMissileCount can toggle it
|
|
lda decimalTens ; 7AD5 tens digit
|
|
jsr drawTallGlyph ; 7AD8 draw it as a two-character-high glyph pair
|
|
inc scratch19 ; 7ADB next column (33)
|
|
lda decimalOnes ; 7ADD ones digit
|
|
jsr drawTallGlyph ; 7AE0 draw it
|
|
lda #$00 ; 7AE3 zero
|
|
sta useDrawColourFlag ; 7AE5 back to per-character colours
|
|
sta drawColourByte ; 7AE8 and clear the fixed colour byte
|
|
rts ; 7AEB done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
blinkMissileCount:
|
|
lda missileCountColour ; 7AEC current box colour
|
|
eor #$33 ; 7AEF EOR $33 swaps the two nibbles' colours: $30 <-> $03
|
|
sta missileCountColour ; 7AF1 keep the new one
|
|
ldx #$11 ; 7AF4 character row 17
|
|
stx windowOriginRow ; 7AF6 row of the counter box
|
|
ldx #$20 ; 7AF9 character column 32
|
|
stx windowOriginCol ; 7AFB column of the counter box
|
|
ldx #$02 ; 7AFE 2 cells wide
|
|
ldy #$02 ; 7B00 2 cells high
|
|
jmp fillScreenColourRect ; 7B02 repaint them (the draw mode is 'replace' here) and return through it
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
drawFireButton:
|
|
ldx #$09 ; 7B05 window 9 = 8x7 cells at column 29, row 8
|
|
jsr drawWindowFrame ; 7B07 draw the frame
|
|
jsr drawWindowInnerCorners ; 7B0A add the four inner corner marks
|
|
lda #$0A ; 7B0D character row 10
|
|
sta windowOriginRow ; 7B0F row of the button icon
|
|
lda #$20 ; 7B12 character column 32
|
|
sta windowOriginCol ; 7B14 column of the button icon
|
|
lda #$04 ; 7B17 low byte of the block address
|
|
ldy #$98 ; 7B19 $9804 = a 2x2 character block (width, height, then glyphs $45-$48)
|
|
jsr drawCharBlock ; 7B1B stamp the icon into the bitmap
|
|
lda #$05 ; 7B1E $05 = %01 pixels black, %10 pixels green
|
|
ldx #$02 ; 7B20 2 cells wide
|
|
ldy #$02 ; 7B22 2 cells high
|
|
jsr fillScreenColourRect ; 7B24 paint the icon's colour cells
|
|
lda #$CD ; 7B27 colour byte for the caption glyphs
|
|
sta textColour ; 7B29 text colour
|
|
jsr setFullScreenWindow ; 7B2B text window = the whole screen again
|
|
lda #$1F ; 7B2E column 31
|
|
sta textCursorCol ; 7B30 text cursor column
|
|
lda #$0C ; 7B32 row 12
|
|
sta textCursorRow ; 7B34 text cursor row
|
|
jsr updateCursorPointers ; 7B36 recompute the bitmap/screen pointers
|
|
L_7B39:
|
|
lda #$9D ; 7B39 low byte of the string address
|
|
ldy #$86 ; 7B3B $869D = 'FIRE'
|
|
jsr printString ; 7B3D print it
|
|
jmp resetDrawModes ; 7B40 back to replace mode and return through it
|
|
|
|
|
|
; Flash colour of the DRONE ALERT panel.
|
|
droneAlertColour:
|
|
.byte $00 ; 7B43 . $02 (red text area) or $20 (inverted), toggled by flashDroneAlertPanel
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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).
|
|
; ----------------------------------------------------------------------
|
|
drawDroneAlertPanel:
|
|
jsr setFullScreenWindow ; 7B44 text window = the whole screen
|
|
ldx #$0A ; 7B47 window $0A = the outer frame, 11x8 cells at column 28, row 0
|
|
jsr drawWindowFrame ; 7B49 draw it
|
|
jsr drawWindowInnerCorners ; 7B4C add the four inner corner marks
|
|
ldx #$0B ; 7B4F window $0B = the middle frame, 9x6 cells at column 29, row 1
|
|
jsr drawWindowFrame ; 7B51 draw it
|
|
ldx #$0C ; 7B54 window $0C = the inner frame, 7x4 cells at column 30, row 2
|
|
L_7B56:
|
|
jsr drawWindowFrame ; 7B56 draw it and fall into the text/colour part
|
|
|
|
; ----------------------------------------------------------------------
|
|
; 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.
|
|
; ----------------------------------------------------------------------
|
|
resetDroneAlertPanel:
|
|
ldy #$00 ; 7B59 colour draw mode 0
|
|
jsr setColourDrawMode ; 7B5B replace the colour cells instead of ORing/EORing into them
|
|
lda #$0B ; 7B5E colour byte $0B for the label glyphs
|
|
sta textColour ; 7B60 text colour
|
|
lda #$02 ; 7B62 $02 = the first flash phase
|
|
L_7B64:
|
|
sta droneAlertColour ; 7B64 reset the flash state so the next alert starts red
|
|
lda #$1F ; 7B67 column 31
|
|
sta textWindowLeft ; 7B69 make it the left edge of the text window, so the label wraps inside the panel
|
|
lda #$00 ; 7B6B column 0 relative to that
|
|
sta textCursorCol ; 7B6D text cursor column
|
|
lda #$03 ; 7B6F row 3
|
|
sta textCursorRow ; 7B71 text cursor row
|
|
jsr updateCursorPointers ; 7B73 recompute the bitmap/screen pointers
|
|
lda #$A1 ; 7B76 low byte of the string address
|
|
ldy #$86 ; 7B78 $86A1 = 'DRONE' + CR + 'ALERT'
|
|
jsr printString ; 7B7A print both lines
|
|
lda #$0B ; 7B7D 11 frames
|
|
sta keyRepeatTimer ; 7B7F delays the keyboard auto-repeat countdown; nothing here depends on it (leftover)
|
|
lda #$0B ; 7B81 $0B = %01 pixels black, %10 pixels dark grey: the panel is dark/inactive
|
|
jmp fillDroneAlertPanel ; 7B83 fill the 7x4 interior with it and return through it
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; flashDroneAlertPanel - Toggles the DRONE ALERT panel between its two colour states and repaints the
|
|
; 7x4 colour cells behind the two-line 'DRONE / ALERT' text, so the panel blinks red while an opponent
|
|
; drone is airborne. droneAlertColour ($7B43) alternates between $02 (red as the %10 bit pair) and
|
|
; $20 (red as the %01 bit pair), which swaps the text and its background.
|
|
; In: droneAlertColour $7B43
|
|
; Out: $7B43 toggled, 28 screen RAM colour cells at row 2 column 30 rewritten, draw modes reset,
|
|
; D_2A2A = the new colour
|
|
; Called from: JMP at $76AF in the screen-1 refresh routine sub_7688, taken when droneControlFlags
|
|
; ($920D) bit 0 says an enemy drone is in the air (the other path JMPs to resetDroneAlertPanel)
|
|
; ----------------------------------------------------------------------
|
|
flashDroneAlertPanel:
|
|
jsr resetDrawModes ; 7B86 put the bitmap and colour draw modes back to plain replace
|
|
lda droneAlertColour ; 7B89 current state of the alert panel ($02 or $20)
|
|
eor #$22 ; 7B8C EOR $22 swaps the two red nibbles: $02 <-> $20
|
|
sta droneAlertColour ; 7B8E remember the new state for the next refresh
|
|
|
|
; ----------------------------------------------------------------------
|
|
; fillDroneAlertPanel - Shared tail of resetDroneAlertPanel and flashDroneAlertPanel: fills the 7 x 4
|
|
; character interior of DRONE ALERT window $0C (top-left corner row 2, column 30) with the colour byte
|
|
; in A.
|
|
; In: A = screen RAM colour byte
|
|
; Out: 28 colour cells written, D_2A2D = 2, D_2A2E = $1E, D_2A2A = A
|
|
; Called from: JMP at $7B83 (resetDroneAlertPanel) and by falling through from flashDroneAlertPanel
|
|
; ----------------------------------------------------------------------
|
|
fillDroneAlertPanel:
|
|
ldx #$02 ; 7B91 colour rectangle starts at character row 2
|
|
stx windowOriginRow ; 7B93 D_2A2D = fill origin row
|
|
ldx #$1E ; 7B96 character column 30
|
|
stx windowOriginCol ; 7B98 D_2A2E = fill origin column
|
|
ldx #$07 ; 7B9B 7 character columns wide
|
|
ldy #$04 ; 7B9D 4 character rows high
|
|
jmp fillScreenColourRect ; 7B9F paint them and return straight to the panel routine's caller
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawRadarMap - Redraws the whole 21 x 17 cell strategic map of screen 1 (the RADAR/MISSILE screen)
|
|
; at character rows 3-19, columns 3-23, one 8x8 character per map cell, centred so that the player's
|
|
; own comcen sits at grid cell (10,8). Terrain and own units are dark grey, a spotted enemy is white;
|
|
; the actual per-cell decision is made by getRadarCellGlyph and the plotting by drawRadarGlyph. The
|
|
; colour bytes go to colour RAM $D800 (the %11 bit pair); paintRadarSweepRow later writes $F0/$00 into
|
|
; screen RAM $8C00 to reveal or hide each row as the sweep passes.
|
|
; In: comcenUnit $9236, the 40x40 map at $F000, the unit arrays at $F640+
|
|
; Out: 357 bitmap cells and colour cells written; zp_A5/zp_A6 restored; $90F6/$90F7 = the saved view
|
|
; origin; $9237/$9239 = $0B, $9238 = 1; zp_48/49, zp_B4-zp_B7 clobbered; textDrawParams = 0
|
|
; Called from: $75B0 (screen-1 build), $797E (comcen moved, via scrollRadarOnComcenMove) and by the
|
|
; JMP at $7A75 in redrawRadarMapWindow
|
|
; ----------------------------------------------------------------------
|
|
drawRadarMap:
|
|
jsr buildRadarNoiseMasks ; 7BA2 new random noise masks so unspotted enemy 'ghost' blips shimmer on every redraw
|
|
lda #$0B ; 7BA5 $0B = dark grey
|
|
sta radarOwnUnitColour ; 7BA7 $9237: colour used for our own units
|
|
sta radarTerrainColour ; 7BAA $9239: and for plain terrain
|
|
lda #$01 ; 7BAD 1 = white
|
|
sta radarEnemyUnitColour ; 7BAF $9238: a spotted enemy blip - the sweep only lights up colour-1 cells
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Blank the window, then work out where the map has to be scrolled to keep the comcen centred.
|
|
; ----------------------------------------------------------------------
|
|
drawRadarMapBody:
|
|
lda #$03 ; 7BB2 colour fill origin: character row 3 (top edge of the radar window)
|
|
sta windowOriginRow ; 7BB4 D_2A2D = fill origin row
|
|
lda #$03 ; 7BB7 character column 3 (left edge)
|
|
sta windowOriginCol ; 7BB9 D_2A2E = fill origin column
|
|
L_7BBC:
|
|
ldx #$15 ; 7BBC 21 cells across
|
|
ldy #$11 ; 7BBE 17 cells down
|
|
lda #$00 ; 7BC0 colour 0 = both multicolour nibbles black
|
|
jsr fillScreenColourRect ; 7BC2 clear the screen RAM colour cells of the whole window first
|
|
jsr getComcenCentredViewOrigin; 7BC5 X/Y = map origin that puts the comcen at grid cell (10,8)
|
|
stx viewOriginCol ; 7BC8 zp_A5 = map column of the left edge
|
|
sty viewOriginRow ; 7BCA zp_A6 = map row of the top edge
|
|
jsr getViewportOriginPointer; 7BCC zp_48/zp_49 = address in the $F000 map of that top-left cell (handles negative origins)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Save the origin - the drawing loop walks zp_A5/zp_A6 cell by cell so that getRadarCellGlyph can test
|
|
; for off-map coordinates - and build the two output pointers for character cell (row 3, column 3).
|
|
; ----------------------------------------------------------------------
|
|
L_7BCF:
|
|
lda viewOriginCol ; 7BCF keep the origin column ...
|
|
sta drawCellSaveCol ; 7BD1 ... in $90F6
|
|
lda viewOriginRow ; 7BD4 keep the origin row ...
|
|
sta drawCellSaveRow ; 7BD6 ... in $90F7
|
|
clc ; 7BD9 no carry into the address addition
|
|
lda bitmapRowLoTableFrom3 ; 7BDA bitmap row table (real base $9300) entry for character row 3, low byte
|
|
adc bitmapColLoTableFrom3 ; 7BDD plus 8*column table (real base $9332) entry for character column 3
|
|
sta bitmapPtr ; 7BE0 zp_B4 = low byte of the first bitmap cell
|
|
lda bitmapRowHiTableFrom3 ; 7BE2 same tables, high bytes ($9319+3 and $935A+3)
|
|
adc bitmapColHiTableFrom3 ; 7BE5 add with the carry from the low half
|
|
sta bitmapPtrHi ; 7BE8 zp_B5 = high byte; zp_B4/B5 now point into the $A000 bitmap
|
|
clc ; 7BEA clear carry for the screen RAM address
|
|
lda screenRowLoTableFrom3 ; 7BEB screen RAM row table (real base $9382) entry for character row 3, low byte
|
|
adc #$03 ; 7BEE +3 characters for column 3
|
|
sta screenPtr ; 7BF0 zp_B6 = low byte
|
|
lda screenRowHiTableFrom3 ; 7BF2 row 3 high byte from the $939B table (points into screen RAM $8C00)
|
|
adc #$00 ; 7BF5 propagate the carry of the column addition
|
|
sta screenPtrHi ; 7BF7 dead store: zp_B7 is overwritten three instructions later with the colour RAM value
|
|
radarMapColourRamPtr:
|
|
clc ; 7BF9 clear carry for the +$4C00
|
|
adc #$4C ; 7BFA $8C00 + $4C00 = $D800: turn the screen RAM address into the colour RAM address
|
|
sta screenPtrHi ; 7BFC zp_B6/B7 now address colour RAM; on screen 1 drawRadarGlyph stores the raw colour there
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Outer loop over the 17 radar rows (X), inner loop over the 21 columns (Y). Y is also the offset
|
|
; added to zp_B6/B7, while zp_B4/B5 is advanced 8 bytes per cell by drawRadarGlyph itself.
|
|
; ----------------------------------------------------------------------
|
|
ldx #$00 ; 7BFE X = radar row 0..16 (character row 3+X)
|
|
L_7C00:
|
|
ldy #$00 ; 7C00 Y = radar column 0..20 (character column 3+Y)
|
|
tya ; 7C02 A = 0
|
|
L_7C03:
|
|
clc ; 7C03 clear carry for the column reset
|
|
adc drawCellSaveCol ; 7C04 restart the running map column at the left edge of the window
|
|
sta viewOriginCol ; 7C07 zp_A5 = map column of the cell about to be drawn
|
|
L_7C09:
|
|
lda (mapCellPtr),y ; 7C09 map byte of this cell: $80|unitIndex, or a terrain tile
|
|
jsr getRadarCellGlyph ; 7C0B classify it: A = glyph index (bit 7 = draw through the noise masks), zp_32 = colour
|
|
jsr drawRadarGlyph ; 7C0E plot the 8x8 glyph, store zp_32 at (zp_B6),Y and step zp_B4/B5 on by one character
|
|
iny ; 7C11 next character column of the window
|
|
L_7C12:
|
|
inc viewOriginCol ; 7C12 and the matching map column, for the off-map test in getRadarCellGlyph
|
|
cpy #$15 ; 7C14 21 columns done?
|
|
bcc L_7C09 ; 7C16 no - draw the next cell of this row
|
|
|
|
; ----------------------------------------------------------------------
|
|
; End of a row: advance the map pointer, the colour RAM pointer and the bitmap pointer by one row.
|
|
; ----------------------------------------------------------------------
|
|
inc viewOriginRow ; 7C18 the next window row shows the next map row
|
|
lda mapCellPtr ; 7C1A map pointer low byte
|
|
radarMapNextMapRow:
|
|
clc ; 7C1C clear carry
|
|
adc #$28 ; 7C1D one map row = 40 bytes
|
|
sta mapCellPtr ; 7C1F zp_48 updated
|
|
lda mapCellPtrHi ; 7C21 map pointer high byte
|
|
adc #$00 ; 7C23 add the carry
|
|
sta mapCellPtrHi ; 7C25 zp_49 updated
|
|
clc ; 7C27 clear carry
|
|
lda screenPtr ; 7C28 colour RAM pointer low byte
|
|
adc #$28 ; 7C2A one character row = 40 bytes
|
|
sta screenPtr ; 7C2C zp_B6 updated
|
|
radarMapScreenPtrCarry:
|
|
lda screenPtrHi ; 7C2E colour RAM pointer high byte
|
|
adc #$00 ; 7C30 add the carry
|
|
sta screenPtrHi ; 7C32 zp_B7 updated
|
|
inx ; 7C34 next radar row
|
|
ldy #$00 ; 7C35 column index 0 = character column 3 again
|
|
clc ; 7C37 clear carry
|
|
lda bitmapRowLoTableFrom3,x ; 7C38 bitmap row table entry for character row 3+X, low byte
|
|
adc bitmapColLoTableFrom3,y ; 7C3B plus the column-3 offset
|
|
L_7C3E:
|
|
sta bitmapPtr ; 7C3E zp_B4 = start of the new row
|
|
lda bitmapRowHiTableFrom3,x ; 7C40 row 3+X high byte
|
|
adc bitmapColHiTableFrom3,y ; 7C43 plus the column-3 high byte
|
|
sta bitmapPtrHi ; 7C46 zp_B5 = start of the new row
|
|
cpx #$11 ; 7C48 17 rows done?
|
|
bcc L_7C00 ; 7C4A no - draw the next row
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Finished: drop any fixed-colour override and put the view origin back the way the caller left it.
|
|
; ----------------------------------------------------------------------
|
|
lda #$00 ; 7C4C 0 = use each glyph's own colour again
|
|
sta useDrawColourFlag ; 7C4E clear the fixed-colour flag $2A29
|
|
lda drawCellSaveCol ; 7C51 reload the saved origin column
|
|
L_7C54:
|
|
sta viewOriginCol ; 7C54 zp_A5 restored
|
|
lda drawCellSaveRow ; 7C56 reload the saved origin row
|
|
sta viewOriginRow ; 7C59 zp_A6 restored
|
|
rts ; 7C5B back to the caller
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; getComcenCentredViewOrigin - Returns the map origin that puts the player's own comcen in the middle
|
|
; of the 21 x 17 cell strategic window of screen 1: column = unitCol[comcenUnit] - 10, row =
|
|
; unitRow[comcenUnit] - 8. Either value may be negative when the comcen is near an edge of the 40x40
|
|
; map; getViewportOriginPointer copes with that.
|
|
; In: comcenUnit $9236 (49 for side 0, 99 for side 1), unitColTable $F640, unitRowTable $F6A4
|
|
; Out: X = origin column, Y = origin row; A clobbered
|
|
; Called from: $7959 (scrollRadarOnComcenMove) and $7BC5 (drawRadarMap)
|
|
; ----------------------------------------------------------------------
|
|
getComcenCentredViewOrigin:
|
|
ldy comcenUnit ; 7C5C $9236 = index of this side's comcen (49 or 99)
|
|
lda unitColTable,y ; 7C5F its map column ($F640 + unit)
|
|
sec ; 7C62 no borrow
|
|
sbc #$0A ; 7C63 the comcen is drawn at grid column 10 of the 21-wide window
|
|
tax ; 7C65 return the origin column in X
|
|
lda unitRowTable,y ; 7C66 its map row ($F6A4 + unit; the arrays are $64 apart)
|
|
sec ; 7C69 no borrow
|
|
sbc #$08 ; 7C6A and at grid row 8 of the 17-high window
|
|
tay ; 7C6C return the origin row in Y
|
|
rts ; 7C6D done
|
|
|
|
|
|
; comcenSchematicRle - 864 bytes of RLE-compressed hires picture (20 x 10 characters = 160 x 80
|
|
; pixels, 1600 bytes when unpacked): the side view of the tracked comcen vehicle shown on the REPAIR
|
|
; tab. Stream format as used by unpackRleTo0200: byte 0 is the escape marker ($13), afterwards any
|
|
; byte other than the marker is a literal and 'marker, count, value' emits value count times (count 0
|
|
; means 256). The stream ends at $7FCB with $13,$13,$00 = 256 zero bytes. drawComcenSchematic
|
|
; ($8632) is the only reader; it unpacks straight into the bitmap at character row 10, column 13 with
|
|
; a 320-byte row stride. Every sub_/L_/D_ label inside this block is an artefact of XREF merging the
|
|
; two $6F00 overlay variants: those addresses are code only in the map generator overlay.
|
|
comcenSchematicRle:
|
|
.byte $13,$13,$1C,$00,$13,$04,$04,$13; 7C6E ........
|
|
.byte $06,$00,$01,$41,$13,$07,$00,$10; 7C76 ...A....
|
|
.byte $13,$14,$00,$01,$03,$06,$44,$13; 7C7E ......D.
|
|
D_7C86:
|
|
.byte $04,$00,$80,$13,$6B,$00,$04,$04; 7C86 ....k...
|
|
.byte $04,$07,$13,$04,$04,$41,$41,$E1; 7C8E .....AA.
|
|
.byte $FF,$E1,$41,$41,$41,$14,$14,$14; 7C96 ..AAA...
|
|
.byte $FF,$14 ; 7C9E ..
|
|
L_7CA0:
|
|
.byte $14,$14 ; 7CA0 ..
|
|
sub_7CA2:
|
|
.byte $10,$00,$00,$40,$C0,$40,$13,$04; 7CA2 ...@.@..
|
|
.byte $00,$01,$13 ; 7CAA ...
|
|
L_7CAD:
|
|
.byte $06 ; 7CAD .
|
|
sub_7CAE:
|
|
.byte $00,$AC,$FF,$AC,$E4,$A6,$A3,$A1; 7CAE ........
|
|
.byte $A0,$00,$80,$13 ; 7CB6 ....
|
|
L_7CBA:
|
|
.byte $04,$00,$80,$13,$58 ; 7CBA ....X
|
|
sub_7CBF:
|
|
.byte $00 ; 7CBF .
|
|
D_7CC0:
|
|
.byte $03,$13,$07,$00,$FF,$13,$07,$00; 7CC0 ........
|
|
.byte $FF,$04,$04,$04,$00,$00,$00; 7CC8 .......
|
|
L_7CCF:
|
|
.byte $01,$FF ; 7CCF ..
|
|
L_7CD1:
|
|
.byte $41,$13,$04,$40,$E0,$F0,$FF,$13; 7CD1 A..@....
|
|
.byte $07,$00,$FF,$00,$00,$00,$1F,$20; 7CD9 .......
|
|
.byte $40,$80,$00,$01,$02,$07,$FF,$13; 7CE1 @.......
|
|
.byte $04,$00,$F0,$08,$FC,$FF,$13,$07; 7CE9 ........
|
|
.byte $00,$FF,$00 ; 7CF1 ...
|
|
L_7CF4:
|
|
.byte $0F,$04,$04,$00,$00,$00,$80,$80; 7CF4 ........
|
|
.byte $13,$07,$00,$18,$14,$12,$11,$13; 7CFC ........
|
|
L_7D04:
|
|
.byte $40,$00,$0C,$10,$20,$40,$40,$40; 7D04 @... @@@
|
|
.byte $4F,$48,$13,$06,$00,$FF,$13,$07; 7D0C OH......
|
|
.byte $00,$F0,$10,$13,$30,$00,$02,$02; 7D14 ....0...
|
|
.byte $01,$01,$13,$08,$00,$80,$80,$40; 7D1C .......@
|
|
.byte $40,$10,$10,$20,$2F,$20,$1F,$00; 7D24 @.. / ..
|
|
.byte $00,$80,$7F,$00,$00 ; 7D2C .....
|
|
L_7D31:
|
|
.byte $38,$D7,$44,$44 ; 7D31 8.DD
|
|
L_7D35:
|
|
.byte $00,$FF,$00,$7F,$00,$FF,$00,$00; 7D35 ........
|
|
.byte $00,$FF,$00,$00,$38,$D7,$44,$44; 7D3D ....8.DD
|
|
.byte $00,$F0,$0F,$00,$0F,$F0,$13,$05; 7D45 ........
|
|
.byte $00 ; 7D4D .
|
|
L_7D4E:
|
|
.byte $E0,$13,$1C,$00,$4A,$48,$48,$49; 7D4E ....JHHI
|
|
.byte $49,$48,$49,$49,$00,$00,$FF,$00; 7D56 IHII....
|
|
.byte $00,$81,$00,$00,$50,$10,$10,$90; 7D5E ....P...
|
|
.byte $90,$10,$90,$90,$13,$06,$00,$1F; 7D66 ........
|
|
.byte $60,$13 ; 7D6E `.
|
|
sub_7D70:
|
|
.byte $06,$00 ; 7D70 ..
|
|
L_7D72:
|
|
.byte $FF,$13,$07,$00,$FF,$13,$07,$00; 7D72 ........
|
|
.byte $FF,$13,$07 ; 7D7A ...
|
|
sub_7D7D:
|
|
.byte $00,$FF,$13,$07 ; 7D7D ....
|
|
L_7D81:
|
|
.byte $00,$FF,$13,$07,$00,$FF,$00,$20; 7D81 .......
|
|
.byte $20,$1F ; 7D89 .
|
|
sub_7D8B:
|
|
.byte $20,$40,$80,$FF,$00,$00,$01,$FF; 7D8B @......
|
|
.byte $00,$00,$00,$FF,$00 ; 7D93 .....
|
|
sub_7D98:
|
|
.byte $88,$10 ; 7D98 ..
|
|
L_7D9A:
|
|
.byte $FF,$00,$00,$00,$FF,$00,$00,$01; 7D9A ........
|
|
.byte $FF,$00,$00,$00,$FF ; 7DA2 .....
|
|
sub_7DA7:
|
|
.byte $00,$88,$10,$FF,$00,$00,$00,$FF; 7DA7 ........
|
|
.byte $00,$00,$00,$80,$40,$40,$40,$F0; 7DAF ....@@@.
|
|
L_7DB7:
|
|
.byte $0C ; 7DB7 .
|
|
L_7DB8:
|
|
.byte $13,$07,$00,$01,$13,$05,$00,$60; 7DB8 .......`
|
|
.byte $C0,$8E,$13,$10,$00,$48,$49,$49; 7DC0 .....HII
|
|
L_7DC8:
|
|
.byte $48,$49,$49,$48,$49,$81,$00,$00; 7DC8 HIIHI...
|
|
.byte $81,$00,$00,$81,$00,$90,$91,$91; 7DD0 ........
|
|
.byte $12,$92,$92,$12,$92,$80,$13,$5F; 7DD8 ......._
|
|
.byte $00,$03,$13 ; 7DE0 ...
|
|
L_7DE3:
|
|
.byte $07,$00,$03,$C6,$3C,$0C,$03,$00; 7DE3 ....<...
|
|
.byte $00,$00,$16,$23,$45,$88,$11,$E2; 7DEB ...#E...
|
|
.byte $34 ; 7DF3 4
|
|
L_7DF4:
|
|
.byte $0C,$00,$80,$80,$E0,$60,$38,$58; 7DF4 .....`8X
|
|
.byte $88,$13,$08,$00,$49,$48,$49,$49; 7DFC ....IHII
|
|
.byte $48,$48,$4A,$48,$00,$81,$00,$00; 7E04 HHJH....
|
|
.byte $FF,$00,$00 ; 7E0C ...
|
|
D_7E0F:
|
|
.byte $00,$92,$12,$92,$92,$12,$12,$52; 7E0F .......R
|
|
.byte $12,$13,$70,$00,$03,$13,$07,$00; 7E17 ..p.....
|
|
.byte $10,$E0,$30,$0C,$03 ; 7E1F ..0..
|
|
L_7E24:
|
|
.byte $13,$08,$00,$C0,$30,$0C ; 7E24 ....0.
|
|
L_7E2A:
|
|
.byte $4F ; 7E2A O
|
|
L_7E2B:
|
|
.byte $7F,$13,$05,$80,$61,$FF,$FF,$00; 7E2B ....a...
|
|
.byte $00,$00,$3F,$C0,$00,$F2 ; 7E33 ..?...
|
|
L_7E39:
|
|
.byte $FF,$00,$00,$00,$FF,$00,$00,$00; 7E39 ........
|
|
.byte $FF ; 7E41 .
|
|
L_7E42:
|
|
.byte $00,$00,$00,$FF,$00,$00,$00,$FF; 7E42 ........
|
|
.byte $00,$00,$00,$FF,$00 ; 7E4A .....
|
|
sub_7E4F:
|
|
.byte $00,$00,$FF,$00,$00,$00,$FF,$00; 7E4F ........
|
|
.byte $00,$00,$FF,$00,$00,$00,$FF,$00; 7E57 ........
|
|
.byte $00,$00,$FF,$00 ; 7E5F ....
|
|
L_7E63:
|
|
.byte $00,$00,$FF,$00,$00,$00,$FF,$00; 7E63 ........
|
|
.byte $00,$00 ; 7E6B ..
|
|
L_7E6D:
|
|
.byte $F8,$07,$00 ; 7E6D ...
|
|
L_7E70:
|
|
.byte $00 ; 7E70 .
|
|
L_7E71:
|
|
.byte $FF,$13,$05 ; 7E71 ...
|
|
sub_7E74:
|
|
.byte $00,$C0,$00,$FF,$13,$07,$00,$FF; 7E74 ........
|
|
.byte $13,$04,$00,$3F,$C0,$00,$FF,$13; 7E7C ...?....
|
|
.byte $04,$00,$FF,$00,$00,$FF,$13; 7E84 .......
|
|
L_7E8B:
|
|
.byte $04,$00,$FF,$00,$00,$FF,$13,$04; 7E8B ........
|
|
.byte $00,$FF,$00,$00,$FF,$13,$04,$00; 7E93 ........
|
|
.byte $FF,$00 ; 7E9B ..
|
|
L_7E9D:
|
|
.byte $00,$FF,$13 ; 7E9D ...
|
|
sub_7EA0:
|
|
.byte $04,$00,$FF,$00,$00,$FF,$13,$04; 7EA0 ........
|
|
.byte $00,$FC,$03,$00,$FF,$13,$06,$00; 7EA8 ........
|
|
L_7EB0:
|
|
.byte $02,$FF,$13,$06,$01,$1D,$12,$0A; 7EB0 ........
|
|
.byte $0B,$06,$02,$02,$03,$00,$00,$FF; 7EB8 ........
|
|
.byte $00,$1F,$26,$26,$E0,$00,$00,$FF; 7EC0 ..&&....
|
|
.byte $00,$FF,$41 ; 7EC8 ..A
|
|
L_7ECB:
|
|
.byte $41,$40,$00,$00,$FF,$00,$FF,$68; 7ECB A@.....h
|
|
.byte $08,$F0,$00,$00 ; 7ED3 ....
|
|
L_7ED7:
|
|
.byte $FF,$00,$FF,$5A,$42,$3C,$00,$00; 7ED7 ...ZB<..
|
|
.byte $FF,$00,$FF,$16,$10,$0F,$00,$00; 7EDF ........
|
|
.byte $FF,$00,$FF,$85,$84,$03,$00,$00; 7EE7 ........
|
|
.byte $FF,$00,$FF,$A0,$20,$C0,$00,$00; 7EEF .... ...
|
|
.byte $FF,$00,$FE,$99,$99,$81,$2F,$10; 7EF7 ....../.
|
|
.byte $D0,$30,$10,$10,$1F,$F0,$FD,$02; 7EFF .0......
|
|
.byte $02,$03,$02,$02,$FE,$03,$00,$00; 7F07 ........
|
|
.byte $FF,$00,$1F,$26,$26,$E0,$00,$00; 7F0F ...&&...
|
|
.byte $FF,$00,$FF,$41,$41 ; 7F17 ...AA
|
|
L_7F1C:
|
|
.byte $40,$00,$00,$FF,$00,$FF,$68,$08; 7F1C @.....h.
|
|
.byte $F0,$00,$00,$FF,$00,$FF,$5A,$42; 7F24 ......ZB
|
|
.byte $3C,$00,$00,$FF,$00,$FF,$16,$10; 7F2C <.......
|
|
.byte $0F,$00,$00,$FF,$00,$FF,$82,$82; 7F34 ........
|
|
.byte $02,$00,$00 ; 7F3C ...
|
|
sub_7F3F:
|
|
.byte $FF,$00,$F8,$64,$64,$07,$BF,$40; 7F3F ...dd..@
|
|
.byte $40,$C3,$7C,$40,$40,$C0,$FE,$06; 7F47 @.|@@...
|
|
.byte $38,$C0,$13,$0C,$00,$10,$0F,$06; 7F4F 8.......
|
|
L_7F57:
|
|
.byte $03,$01,$00,$00,$00,$9E,$21,$40; 7F57 ......!@
|
|
.byte $CC,$CC,$C0,$61,$1F,$07,$08,$90; 7F5F ...a....
|
|
.byte $93,$F3,$90,$08,$FF,$81,$42,$24; 7F67 ......B$
|
|
.byte $24,$3C,$24,$42,$FF,$E0,$10,$09; 7F6F $<$B....
|
|
L_7F77:
|
|
.byte $C9,$CF,$09,$10,$FF,$78,$84,$02; 7F77 .....x..
|
|
.byte $32,$33,$02,$FC,$FF,$1E,$21,$40; 7F7F 23....!@
|
|
.byte $4C,$CC,$40,$21,$FE,$42,$3C,$98; 7F87 L.@!.B<.
|
|
.byte $B0,$E0,$C0,$80,$13,$11,$00,$10; 7F8F ........
|
|
.byte $0F,$06,$03,$01,$00,$00,$00; 7F97 .......
|
|
L_7F9E:
|
|
.byte $9E,$21,$40 ; 7F9E .!@
|
|
D_7FA1:
|
|
.byte $4C ; 7FA1 L
|
|
D_7FA2:
|
|
.byte $CC ; 7FA2 .
|
|
D_7FA3:
|
|
.byte $C0,$61,$1F ; 7FA3 .a.
|
|
sub_7FA6:
|
|
.byte $07,$08,$90,$93,$F3,$90,$08,$FF; 7FA6 ........
|
|
.byte $81,$42,$24,$24,$3C,$24,$42,$FF; 7FAE .B$$<$B.
|
|
.byte $E0,$10,$09,$C9 ; 7FB6 ....
|
|
L_7FBA:
|
|
.byte $CF,$09,$10,$FF,$79,$84,$02,$32; 7FBA ....y..2
|
|
.byte $33,$03,$86,$F8,$08,$F0,$60,$C0; 7FC2 3.....`.
|
|
L_7FCA:
|
|
.byte $80 ; 7FCA .
|
|
sub_7FCB:
|
|
.byte $13,$13,$00 ; 7FCB ...
|
|
|
|
; Three 8-byte lookup tables for the drone heading indicator, indexed 0-7.
|
|
joyDirToHeadingTable:
|
|
.byte $00,$04,$02,$06,$07,$01,$03,$05; 7FCE ........ joyDirToHeadingTable: joystick direction code (0 N, 1 S, 2 E, 3 W, 4 NW, 5 NE, 6 SE, 7 SW) -> clockwise heading (0 N, 1 NE, 2 E, 3 SE, 4 S, 5 SW, 6 W, 7 NW); also read by steerDrone at $7320 and by launchDrone at $8092
|
|
|
|
; headingLineEndColTable: byteTable, 8 bytes. 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).
|
|
headingLineEndColTable:
|
|
.byte $0C,$13,$16,$13 ; 7FD6 .... headingLineEndColTable[0..3]: end column of the pointer line inside the 24x21 sprite for headings N, NE, E, SE (the line starts at column 12)
|
|
L_7FDA:
|
|
.byte $0C ; 7FDA headingLineEndColTable[4..6]: headings S, SW, W
|
|
.byte $05,$03 ; 7FDB ..
|
|
sub_7FDD:
|
|
.byte $05 ; 7FDD headingLineEndColTable[7]: heading NW
|
|
|
|
; headingLineEndRowTable: byteTable, 8 bytes. 00 03 0A 11 14 11 0A 03: end row of the heading pointer
|
|
; line for headings 0-7 (centre row 10).
|
|
headingLineEndRowTable:
|
|
.byte $00,$03,$0A,$11,$14,$11,$0A,$03; 7FDE ........ headingLineEndRowTable[0..7]: end row of the pointer line for headings N..NW (the line starts at row 10)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; enterDroneScreen - Entry point of game screen 2, the DRONE screen (F5). Copies this side's drone
|
|
; stock into the display counter, unpacks the eight aircraft silhouettes into the $0200 sprite shape
|
|
; buffer, builds the whole screen and falls into resetDroneScreen.
|
|
; In: dronesLeft $92AE
|
|
; Out: radarDronesLeft $9233; $0200-$03FF = 8 sprite shapes of 64 bytes; screen 2 drawn; never returns
|
|
; (the idle loop is entered by falling through)
|
|
; Called from: JMP at $06FC in switchToRequestedScreen when the requested screen becomes 2
|
|
; ----------------------------------------------------------------------
|
|
enterDroneScreen:
|
|
lda dronesLeft ; 7FE6 $92AE = drones this side still owns
|
|
sta radarDronesLeft ; 7FE9 $9233 = the digit painted in the count box at row 5 column 34
|
|
L_7FEC:
|
|
lda #$24 ; 7FEC low byte of the compressed shape set
|
|
ldy #$FE ; 7FEE $FE24 in the high-memory block = eight aircraft silhouettes, one per heading
|
|
jsr unpackRleTo0200 ; 7FF0 decompress them into $0200-$03FF (64 bytes per heading)
|
|
jsr buildDroneScreen ; 7FF3 draw the frames, panels, buttons, fuel read-out and the empty camera window
|
|
|
|
; ----------------------------------------------------------------------
|
|
; resetDroneScreen - (Re)initialises the drone screen's sprites and read-outs and drops into the idle
|
|
; loop that waits for a launch. Parks sprite 0 (white heading line) at ($37,$64), sprite 3 (the same
|
|
; line one pixel down-right, used as its shadow) at ($38,$65) and sprite 5 (aircraft silhouette) at
|
|
; ($38,$64), all over the compass panel, and shows a full tank if a drone is still available.
|
|
; In: dronesLeft $92AE
|
|
; Out: $9233, droneFuel $9220 (100 or 0), zp_5E = 0, sprite shadows $9008-$901D, spriteEnableShadow
|
|
; $9022 = $09, activeSpriteIndex $91CC = 5; falls into droneScreenIdleLoop
|
|
; Called from: fall-through from enterDroneScreen, JMP at $81FB (endDroneFlight) and JMP at $076A
|
|
; (the comcen knock-out sequence returning to this screen)
|
|
; ----------------------------------------------------------------------
|
|
resetDroneScreen:
|
|
lda dronesLeft ; 7FF6 reload the drone stock ...
|
|
sta radarDronesLeft ; 7FF9 ... into the on-screen counter
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Park the three compass-panel sprites. Sprite 0 is white and sits one pixel up and to the left of
|
|
; sprite 3, so the dark line of sprite 3 reads as its shadow.
|
|
; ----------------------------------------------------------------------
|
|
ldx #$37 ; 7FFC sprite X = $37 (left panel, low byte only - MSB stays clear)
|
|
stx spriteXShadow ; 7FFE $9010 = sprite 0 X shadow
|
|
inx ; 8001 $38 = one pixel further right
|
|
stx sprite5XShadow ; 8002 $9015 = sprite 5 (aircraft silhouette) X
|
|
stx sprite3XShadow ; 8005 $9013 = sprite 3 (heading line) X
|
|
ldy #$64 ; 8008 sprite Y = $64
|
|
sty spriteYShadow ; 800A $9018 = sprite 0 Y
|
|
sty sprite5YShadow ; 800D $901D = sprite 5 Y
|
|
L_8010:
|
|
iny ; 8010 $65 = one pixel further down
|
|
sty sprite3YShadow ; 8011 $901B = sprite 3 Y, so sprite 3 trails sprite 0 by (1,1)
|
|
lda #$01 ; 8014 colour 1 = white
|
|
sta spriteColourShadow ; 8016 $9008 = sprite 0 colour (the highlight of the pointer line)
|
|
lda #$02 ; 8019 colour 2 = red
|
|
sta sprite5ColourShadow ; 801B $900D = sprite 5 colour (the aircraft)
|
|
lda #$00 ; 801E direction code 0 = due north
|
|
sta droneJoyDirection ; 8020 zp_5E = the steer direction the pointer shows
|
|
jsr drawDroneHeadingIndicator; 8022 build the pointer line into sprites 3 and 0
|
|
lda #$05 ; 8025 5 = sprite 5 is the shape target
|
|
sta activeSpriteIndex ; 8027 $91CC selects sprite 5 inside loadDirectionShapeSprite
|
|
jsr loadDirectionShapeSprite; 802A probable bug: A is still 5, so silhouette 5 (SW) is loaded although the pointer shows north
|
|
lda #$09 ; 802D bit 0 + bit 3 = sprites 0 and 3
|
|
sta spriteEnableShadow ; 802F assigned, not ORed - this also switches sprite 5 back off, so the idle panel shows only the pointer
|
|
ldx #$64 ; 8032 a full tank is 100 exchanges of fuel
|
|
L_8034:
|
|
lda radarDronesLeft ; 8034 any drone left to launch?
|
|
bne L_803A ; 8037 yes - keep 100
|
|
tax ; 8039 no - A is 0, so show an empty tank
|
|
L_803A:
|
|
stx droneFuel ; 803A $9220 = fuel value printed by drawDroneFuelDigits
|
|
|
|
; ----------------------------------------------------------------------
|
|
; droneScreenIdleLoop - Main loop of the DRONE screen while nothing is flying. Each frame it runs the
|
|
; game engine and honours a screen change; every 11 frames it flashes the LAUNCH button and, with
|
|
; fewer than two drones left, the count box. A fresh fire press launches, unless the own comcen is
|
|
; cloaked (never) or the drone bay is damaged (then only one press in four gets through).
|
|
; In: zp_6C blink countdown, radarDronesLeft $9233, comcenDroneStatus $9244, comcenCloakedFlag $9245,
|
|
; the fire button
|
|
; Out: never returns - either jumps to launchDrone or leaves through switchToRequestedScreen /
|
|
; updateGameFrame
|
|
; Called from: fall-through from resetDroneScreen; looped back to from $805E, $8071, $8082 and $808A
|
|
; ----------------------------------------------------------------------
|
|
droneScreenIdleLoop:
|
|
jsr updateGameFrame ; 803D one game frame: engine step, message line, sound, timers
|
|
L_8040:
|
|
jsr switchToRequestedScreen ; 8040 act on an F1/F3/F5/F7 request - does not return if the screen changes
|
|
jsr checkFirePressedInteractive; 8043 Z=1 only for a fresh press, and only in a live (non-solo, non-film) game
|
|
bne L_805C ; 8046 no press - fall through to the blinking
|
|
lda comcenCloakedFlag ; 8048 $9245 bit 7 = our comcen is cloaked
|
|
bmi L_805C ; 804B a cloaked comcen cannot launch drones
|
|
lda comcenDroneStatus ; 804D $9244 != 0 = the drone bay has been knocked out
|
|
beq L_8059 ; 8050 undamaged - accept the launch
|
|
jsr nextGameRandom ; 8052 damaged: roll the game RNG
|
|
and #$03 ; 8055 keep two bits
|
|
bne L_805C ; 8057 three presses out of four are swallowed
|
|
L_8059:
|
|
jmp L_8085 ; 8059 go and check that a drone is actually in stock
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Blinking, once every 11 frames.
|
|
; ----------------------------------------------------------------------
|
|
L_805C:
|
|
lda joyRepeatTimer ; 805C zp_6C is decremented once per frame by the IRQ
|
|
L_805E:
|
|
bne droneScreenIdleLoop ; 805E not expired yet - keep polling
|
|
lda radarDronesLeft ; 8060 how many drones are left?
|
|
cmp #$02 ; 8063 fewer than two?
|
|
bcs L_806A ; 8065 no - leave the count box alone
|
|
jsr blinkDroneCountBox ; 8067 yes - pulse the count box as a warning
|
|
L_806A:
|
|
lda #$0B ; 806A 11 frames ...
|
|
sta joyRepeatTimer ; 806C ... until the next blink
|
|
lda radarDronesLeft ; 806E drones left?
|
|
beq droneScreenIdleLoop ; 8071 none - leave the LAUNCH button dark
|
|
lda #$0C ; 8073 character row 12 ...
|
|
sta windowOriginRow ; 8075 ... into the colour-fill origin row
|
|
lda #$22 ; 8078 character column 34 = the LAUNCH button ...
|
|
sta windowOriginCol ; 807A ... into the colour-fill origin column
|
|
lda #$08 ; 807D colour 8 (orange)
|
|
jsr xorColourBlock2x2 ; 807F XOR it into the 2x2 button cells so the button flashes
|
|
jmp droneScreenIdleLoop ; 8082 round again
|
|
|
|
L_8085:
|
|
lda radarDronesLeft ; 8085 drones in stock?
|
|
L_8088:
|
|
bne launchDrone ; 8088 yes - go
|
|
jmp droneScreenIdleLoop ; 808A no - ignore the press
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; launchDrone - Sends a drone on its way: latches the joystick heading, queues command $97 (drone
|
|
; launch) for the engine and the opponent, lights the LAUNCH button, fills the tank with 100, drops
|
|
; the stock by one and puts the 14 x 12 camera window over our own comcen, where the drone starts.
|
|
; Then it loads the targeting reticle into sprite 2, the aircraft silhouette into sprite 5, plays the
|
|
; launch sound and opens the camera with a grey iris wipe.
|
|
; In: zp_5D joystick, comcenUnit $9236, unitColTable/unitRowTable, the reticle shape at $FF93
|
|
; Out: droneHeading $920B, droneControlFlags $920D |= $C0, droneFuel $9220 = 100, $9233 decremented,
|
|
; droneViewOriginCol/Row $92F6/$92F7 and zp_A5/zp_A6 = comcen - (6,5), sprites 2/4/5, command $97
|
|
; queued; falls into droneFlightLoop
|
|
; Called from: branch at $8088 in droneScreenIdleLoop
|
|
; ----------------------------------------------------------------------
|
|
launchDrone:
|
|
jsr readDroneHeadingFromJoystick; 808D sample the stick that was held while fire was pressed
|
|
ldy droneJoyDirection ; 8090 zp_5E = direction code 0-7
|
|
lda joyDirToHeadingTable,y ; 8092 translate it into a clockwise heading 0-7 starting at north
|
|
sta droneHeading ; 8095 $920B: the heading steerDrone turns towards and $8D reports every exchange
|
|
L_8098:
|
|
lda #$97 ; 8098 command $97 = drone launched (one byte, no arguments)
|
|
jsr queueCmdBytePending ; 809A queue it so the engine and the opponent both see it
|
|
lda #$0C ; 809D character row 12 ...
|
|
sta windowOriginRow ; 809F ... fill origin row
|
|
lda #$22 ; 80A2 character column 34 = the LAUNCH button ...
|
|
sta windowOriginCol ; 80A4 ... fill origin column
|
|
L_80A7:
|
|
lda #$05 ; 80A7 colour 5 (green)
|
|
ldx #$02 ; 80A9 2 character columns
|
|
ldy #$02 ; 80AB 2 character rows
|
|
jsr fillScreenColourRect ; 80AD light the button up solid
|
|
lda #$64 ; 80B0 100 = a full tank (one unit per lock-step exchange)
|
|
sta droneFuel ; 80B2 $9220
|
|
jsr drawDroneFuelDigits ; 80B5 print it as three tall digits at row 15
|
|
dec radarDronesLeft ; 80B8 one drone fewer in stock
|
|
jsr drawDroneCountDigit ; 80BB reprint the count digit
|
|
lda droneControlFlags ; 80BE $920D drone state flags
|
|
ora #$C0 ; 80C1 bit 7 = one of our drones is airborne, bit 6 = we are steering it
|
|
sta droneControlFlags ; 80C3 store back (bit 0, the enemy-drone alert, is left alone)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Put the camera window over our comcen: the window is 14 x 12 cells and keeps the drone in the
|
|
; middle, so its origin is the drone cell minus (6,5).
|
|
; ----------------------------------------------------------------------
|
|
ldy comcenUnit ; 80C6 own comcen index
|
|
lda unitColTable,y ; 80C9 its map column
|
|
sec ; 80CC no borrow
|
|
sbc #$06 ; 80CD 6 columns to the left of the drone
|
|
sta viewOriginCol ; 80CF zp_A5 = view origin column
|
|
sta droneViewOriginCol ; 80D1 $92F6 = the drone's own camera origin, stepped by steerDrone
|
|
lda unitRowTable,y ; 80D4 comcen map row
|
|
sec ; 80D7 no borrow
|
|
sbc #$05 ; 80D8 5 rows above the drone
|
|
sta viewOriginRow ; 80DA zp_A6 = view origin row
|
|
sta droneViewOriginRow ; 80DC $92F7
|
|
ldx #$00 ; 80DF leftover: drawComcenRingSprite reads nothing from X
|
|
L_80E1:
|
|
jsr drawComcenRingSprite ; 80E1 sprite 4 = the two concentric rings marking our comcen under the drone
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Load the targeting reticle into sprite 2 and park it in the centre of the camera window.
|
|
; ----------------------------------------------------------------------
|
|
L_80E4:
|
|
lda #$00 ; 80E4 colour 0 = black
|
|
sta sprite2ColourShadow ; 80E6 $900A = sprite 2 colour
|
|
ldy #$3F ; 80E9 64 shape bytes, copied backwards
|
|
L_80EB:
|
|
lda droneViewfinderSprite,y ; 80EB $FF93 in the high-memory block holds the reticle shape (the last byte read, $FFD2, is one past the loaded block but is the unused 64th sprite byte)
|
|
sta sprite2Shape,y ; 80EE sprite 2 data at $8A80
|
|
dey ; 80F1 next byte down
|
|
bpl L_80EB ; 80F2 until byte 0 has been copied
|
|
lda #$AC ; 80F4 X = $AC, the middle of the camera window
|
|
sta sprite2XShadow ; 80F6 $9012 = sprite 2 X
|
|
lda #$88 ; 80F9 Y = $88
|
|
L_80FB:
|
|
sta sprite2YShadow ; 80FB $901A = sprite 2 Y
|
|
lda #$04 ; 80FE bit 2 = sprite 2
|
|
jsr enableSprites ; 8100 OR it into the sprite enable shadow
|
|
L_8103:
|
|
lda #$05 ; 8103 5 = sprite 5
|
|
sta activeSpriteIndex ; 8105 $91CC selects sprite 5 for the shape load
|
|
lda droneHeading ; 8108 silhouette index = the launch heading
|
|
jsr loadDirectionShapeSprite; 810B copy that shape to $8B40 and enable sprite 5
|
|
jsr resetDrawModes ; 810E back to plain replace draw modes
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Launch effect: sound, grey static in the camera window, then the iris opens.
|
|
; ----------------------------------------------------------------------
|
|
lda #$0F ; 8111 sound $0F = launch
|
|
jsr playSound ; 8113 play it
|
|
jsr clearRadarWindowGrey ; 8116 fill the camera window with $BB (dark grey) and set radarWipeColour so the iris animates slowly
|
|
lda #$AA ; 8119 $AA = light red in both multicolour nibbles
|
|
sta useDrawColourFlag ; 811B $2A29 != 0 forces every following draw to use one fixed colour ...
|
|
sta drawColourByte ; 811E ... and D_2A2A holds it
|
|
jsr drawRadarIrisWipe ; 8121 shrinking concentric frames over the camera window
|
|
|
|
; ----------------------------------------------------------------------
|
|
; droneFlightLoop - Per-frame loop while our drone is in the air. A fire press jumps to
|
|
; strikeWithDrone; every 40 frames the STRIKE button (and, below two drones, the count box) flashes;
|
|
; when the joystick lockout has expired the heading is re-read. The camera only advances when
|
|
; exchangeAppliedFlag ($920E) bit 7 says a lock-step command exchange has been applied, which keeps
|
|
; both machines in step. The flight ends when the tank runs dry, when the game ends, or when the
|
|
; command handlers clear droneControlFlags bit 7 (drone arrived / detonated by the other side).
|
|
; In: zp_6C, zp_6E, exchangeAppliedFlag $920E, droneControlFlags $920D, droneFuel $9220,
|
|
; gameEndReason $0BA8, the fire button
|
|
; Out: camera redrawn one step per exchange; falls into endDroneFlight
|
|
; Called from: fall-through from launchDrone; looped back to from the JMP at $8171
|
|
; ----------------------------------------------------------------------
|
|
droneFlightLoop:
|
|
jsr updateGameFrame ; 8124 run one game frame
|
|
jsr checkFirePressed ; 8127 Z=1 on a fresh fire press (this variant also fires during solo/film)
|
|
bne L_812F ; 812A no press - carry on flying
|
|
jmp strikeWithDrone ; 812C fire during flight = detonate here
|
|
|
|
L_812F:
|
|
lda joyRepeatTimer ; 812F blink countdown
|
|
bne L_8150 ; 8131 not expired - skip the blinking
|
|
lda #$28 ; 8133 40 frames ...
|
|
sta joyRepeatTimer ; 8135 ... until the next blink (slower than the idle loop's 11)
|
|
lda radarDronesLeft ; 8137 drones still in stock?
|
|
cmp #$02 ; 813A fewer than two?
|
|
L_813C:
|
|
bcs L_8141 ; 813C no - leave the count box
|
|
jsr blinkDroneCountBox ; 813E pulse the count box
|
|
L_8141:
|
|
lda #$10 ; 8141 character row 16 ...
|
|
flightStrikeBoxOrigin:
|
|
sta windowOriginRow ; 8143 ... fill origin row
|
|
lda #$22 ; 8146 character column 34 = the STRIKE button ...
|
|
sta windowOriginCol ; 8148 ... fill origin column
|
|
lda #$08 ; 814B colour 8 (orange)
|
|
L_814D:
|
|
jsr xorColourBlock2x2 ; 814D XOR it into the 2x2 button cells
|
|
L_8150:
|
|
lda menuBlinkTimer ; 8150 zp_6E = the 20-frame lockout between joystick reads
|
|
bne L_8157 ; 8152 still locked out - do not re-read the stick
|
|
jsr readDroneHeadingFromJoystick; 8154 latch a new steer direction and redraw the pointer
|
|
|
|
; ----------------------------------------------------------------------
|
|
; The camera moves exactly one cell per lock-step command exchange, never per frame.
|
|
; ----------------------------------------------------------------------
|
|
L_8157:
|
|
lda exchangeAppliedFlag ; 8157 $920E bit 7 = an exchange was applied this frame
|
|
L_815A:
|
|
bmi L_815F ; 815A yes - step the camera
|
|
jmp L_8167 ; 815C no - a plain BPL $8167 would have done; the JMP is two bytes of slack
|
|
|
|
L_815F:
|
|
jsr advanceDroneCamera ; 815F redraw the camera window and burn one unit of fuel
|
|
lda droneFuel ; 8162 tank empty?
|
|
beq L_8174 ; 8165 yes - hand the drone back to the engine
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Decide whether the flight goes on for another frame.
|
|
; ----------------------------------------------------------------------
|
|
L_8167:
|
|
lda droneControlFlags ; 8167 $920D drone state
|
|
bpl L_8177 ; 816A bit 7 cleared by cmdDroneArrived ($92) or a remote detonation - end without sending $8F
|
|
lda gameEndReason ; 816C $0BA8 != 0 = the game has ended
|
|
bne L_8174 ; 816F then release the drone as well
|
|
jmp droneFlightLoop ; 8171 otherwise keep flying
|
|
|
|
L_8174:
|
|
jsr releaseDroneControl ; 8174 clear bits 7-6 and queue command $8F so the opponent stops tracking the drone
|
|
L_8177:
|
|
jsr playDroneLostAnimation ; 8177 white flash, iris close and the shrinking disc
|
|
lda #$1C ; 817A sound $1C = explosion ...
|
|
jsr playSound ; 817C ... which endDroneFlight immediately plays a second time
|
|
jmp endDroneFlight ; 817F common tail
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; strikeWithDrone - Fire pressed while the drone is flying: stop steering it, mark a detonation as
|
|
; pending (droneControlFlags bit 5), light the STRIKE button and queue command $8E col,row with the
|
|
; drone's current map cell. Then it waits, still flying the camera one step per exchange, until the
|
|
; command handler executes our own $8E and clears bit 5; the strike then gets its sound and the
|
|
; zoom-in animation.
|
|
; In: droneControlFlags $920D, exchangeAppliedFlag $920E, droneViewOriginCol/Row $92F6/$92F7
|
|
; Out: command $8E queued with cmdParam1/2, $920D bit 6 cleared and bit 5 set, sound and animation;
|
|
; falls into endDroneFlight
|
|
; Called from: JMP at $812C in droneFlightLoop
|
|
; ----------------------------------------------------------------------
|
|
strikeWithDrone:
|
|
lda droneControlFlags ; 8182 $920D drone state
|
|
and #$BF ; 8185 clear bit 6 - we stop steering from now on
|
|
ora #$20 ; 8187 set bit 5 - our own detonate command is queued but not yet executed
|
|
sta droneControlFlags ; 8189 store back
|
|
lda #$10 ; 818C character row 16 ...
|
|
sta windowOriginRow ; 818E ... fill origin row
|
|
lda #$22 ; 8191 character column 34 = the STRIKE button ...
|
|
sta windowOriginCol ; 8193 ... fill origin column
|
|
lda #$02 ; 8196 colour 2 (red)
|
|
ldx #$02 ; 8198 2 character columns
|
|
ldy #$02 ; 819A 2 character rows
|
|
jsr fillScreenColourRect ; 819C light the STRIKE button solid
|
|
jsr getDroneMapPosition ; 819F zp_74/zp_75 = the drone's own map cell
|
|
lda #$8E ; 81A2 command $8E = detonate a drone at col,row
|
|
jsr queueCmd3 ; 81A4 queue the 3-byte command
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Wait for our own $8E to come back out of the command stream; keep the camera moving (with a rumble)
|
|
; on every exchange until then.
|
|
; ----------------------------------------------------------------------
|
|
L_81A7:
|
|
jsr updateGameFrame ; 81A7 one game frame
|
|
lda droneControlFlags ; 81AA $920D drone state
|
|
bpl endDroneFlight ; 81AD bit 7 gone - the drone was destroyed before the command landed
|
|
and #$20 ; 81AF test the 'detonation pending' bit
|
|
beq L_81C3 ; 81B1 cleared = cmdDroneDetonate has run our command
|
|
lda exchangeAppliedFlag ; 81B3 $920E bit 7 = an exchange was applied
|
|
strikeWaitForExchange:
|
|
bpl L_81A7 ; 81B6 not yet - keep waiting
|
|
jsr advanceDroneCamera ; 81B8 another camera step
|
|
lda #$1C ; 81BB sound $1C ...
|
|
jsr playSound ; 81BD ... on every step, so the drone rumbles towards the target
|
|
jmp L_81A7 ; 81C0 keep waiting
|
|
|
|
L_81C3:
|
|
lda droneControlFlags ; 81C3 dead test: bit 7 was already proved set at $81AD and nothing here clears it
|
|
bpl endDroneFlight ; 81C6 never taken
|
|
D_81C8:
|
|
lda #$11 ; 81C8 sound $11 = the strike itself
|
|
jsr playSound ; 81CA play it
|
|
jsr playStrikeZoomAnimation ; 81CD four 2x zooms into the camera centre, then the drone-lost animation
|
|
|
|
; ----------------------------------------------------------------------
|
|
; endDroneFlight - Common tail of every drone flight: explosion sound, empty the tank, clear the
|
|
; 'airborne' and 'we steer it' bits, put the camera window back over our own comcen, blank it without
|
|
; static and restart the idle screen.
|
|
; In: comcenUnit $9236, unitColTable/unitRowTable, droneControlFlags $920D
|
|
; Out: droneFuel $9220 = 0 and reprinted, $920D &= $3F, zp_A5/zp_A6 = comcen - (6,5), camera window
|
|
; black; jumps to resetDroneScreen
|
|
; Called from: JMP at $817F and branches at $81AD / $81C6, plus fall-through from strikeWithDrone
|
|
; ----------------------------------------------------------------------
|
|
endDroneFlight:
|
|
lda #$1C ; 81D0 sound $1C = explosion
|
|
jsr playSound ; 81D2 play it
|
|
lda #$00 ; 81D5 no fuel left
|
|
sta droneFuel ; 81D7 $9220
|
|
jsr drawDroneFuelDigits ; 81DA read-out back to 000
|
|
L_81DD:
|
|
lda droneControlFlags ; 81DD $920D drone state
|
|
and #$3F ; 81E0 clear bit 7 (airborne) and bit 6 (locally steered); bit 5 and bit 0 survive
|
|
sta droneControlFlags ; 81E2 store back
|
|
ldy comcenUnit ; 81E5 own comcen index
|
|
lda unitColTable,y ; 81E8 its map column
|
|
sec ; 81EB no borrow
|
|
sbc #$06 ; 81EC the 14-wide window is centred 6 columns to the right of its origin
|
|
sta viewOriginCol ; 81EE zp_A5
|
|
lda unitRowTable,y ; 81F0 comcen map row
|
|
sec ; 81F3 no borrow
|
|
endFlightRowOffset:
|
|
sbc #$05 ; 81F4 the 12-high window is centred 5 rows below its origin
|
|
sta viewOriginRow ; 81F6 zp_A6
|
|
jsr clearRadarWindowBlack ; 81F8 blank the camera window to colour 0 (no grey static this time)
|
|
jmp resetDroneScreen ; 81FB back to the idle screen, ready for the next launch
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; getDroneMapPosition - Converts the camera origin into the drone's own map cell: the 14 x 12 window
|
|
; is kept centred on the drone, so the drone stands at origin + (6,5).
|
|
; In: droneViewOriginCol $92F6, droneViewOriginRow $92F7
|
|
; Out: cmdParam1 zp_74 = drone map column, cmdParam2 zp_75 = drone map row
|
|
; Called from: $819F (strikeWithDrone, for the $8E detonate command) and $563C in sendOutgoingPacket
|
|
; (for the $8D heading,col,row move report sent every exchange)
|
|
; ----------------------------------------------------------------------
|
|
getDroneMapPosition:
|
|
lda droneViewOriginCol ; 81FE $92F6 = map column of the window's left edge
|
|
clc ; 8201 no carry in
|
|
L_8202:
|
|
adc #$06 ; 8202 the drone sits 6 columns into the 14-wide window
|
|
sta cmdParam1 ; 8204 zp_74 = command argument 1
|
|
lda droneViewOriginRow ; 8206 $92F7 = map row of the window's top edge
|
|
clc ; 8209 no carry in
|
|
L_820A:
|
|
adc #$05 ; 820A and 5 rows into the 12-high window
|
|
sta cmdParam2 ; 820C zp_75 = command argument 2
|
|
rts ; 820E done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; showDroneAlert - Notification that the opponent has launched a drone: plays the alert sound and, in
|
|
; a real two-player game, queues the 'DRONE ALERT!' status message. In a solo or film game the
|
|
; message is suppressed because there is nobody to warn.
|
|
; In: filmPlaybackMode $0B9B bit 7 (set = solo trainer or film playback)
|
|
; Out: sound 7 played, message $20 queued
|
|
; Called from: $53AF, the cmdDroneLaunch handler of command $97 in the main program
|
|
; ----------------------------------------------------------------------
|
|
showDroneAlert:
|
|
lda #$07 ; 820F sound 7 = alert
|
|
jsr playSound ; 8211 play it
|
|
lda filmPlaybackMode ; 8214 $0B9B bit 7 = solo trainer or film playback
|
|
bmi L_821E ; 8217 then say nothing
|
|
lda #$20 ; 8219 message id $20 = 'DRONE ALERT!'
|
|
jsr queueMessage ; 821B insert it into the status message queue
|
|
L_821E:
|
|
rts ; 821E done (also the RTS shared by releaseDroneControl)
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; fillDroneViewWindow - Fills the drone camera window (14 columns x 12 rows starting at character row
|
|
; 6, column 13) with one screen RAM colour byte.
|
|
; In: A = colour byte
|
|
; Out: 168 colour cells written, D_2A2D = 6, D_2A2E = $0D, D_2A2A = A
|
|
; Called from: $7141 (the shared radar-clear tail), $8290 (static when the drone bay is damaged) and
|
|
; $2E07 in the main program
|
|
; ----------------------------------------------------------------------
|
|
fillDroneViewWindow:
|
|
ldy #$06 ; 821F camera window starts at character row 6
|
|
sty windowOriginRow ; 8221 D_2A2D = fill origin row
|
|
ldy #$0D ; 8224 ... and character column 13
|
|
sty windowOriginCol ; 8226 D_2A2E = fill origin column
|
|
ldx #$0E ; 8229 14 character columns = 14 map cells
|
|
ldy #$0C ; 822B 12 character rows = 12 map cells
|
|
jmp fillScreenColourRect ; 822D fill and return to the caller
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; releaseDroneControl - Gives an airborne drone back to the engine: clears the 'airborne' and 'locally
|
|
; steered' bits and queues the single-byte command $8F so the opponent stops tracking it. Does
|
|
; nothing when no drone of ours is up.
|
|
; In: droneControlFlags $920D
|
|
; Out: $920D &= $3F, command $8F queued
|
|
; Called from: $8174 (fuel out or game over) and $0709 (the comcen knock-out sequence)
|
|
; ----------------------------------------------------------------------
|
|
releaseDroneControl:
|
|
lda droneControlFlags ; 8230 $920D drone state
|
|
bpl L_821E ; 8233 bit 7 clear - nothing of ours is airborne, so nothing to release
|
|
and #$3F ; 8235 clear bit 7 (airborne) and bit 6 (steered by us)
|
|
sta droneControlFlags ; 8237 store back
|
|
lda #$8F ; 823A command $8F = drone released
|
|
jmp queueCmdBytePending ; 823C queue it and return through the queue routine
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawDroneHeadingIndicator - Redraws the compass pointer of the drone screen's left panel. It clears
|
|
; sprite 3, draws a line inside it from the sprite centre (column 12, row 10) out to the end point
|
|
; looked up for the current heading, colours it, then copies the identical shape into sprite 0.
|
|
; Because sprite 0 is white and sits one pixel up and left of sprite 3, the pointer appears as a white
|
|
; line with a dark shadow.
|
|
; In: droneJoyDirection zp_5E, joyDirToHeadingTable $7FCE, headingLineEndColTable $7FD6,
|
|
; headingLineEndRowTable $7FDE
|
|
; Out: sprite 3 shape $8AC0 and sprite 0 shape $8A00 redrawn, sprite3ColourShadow $900B, sprites 0 and
|
|
; 3 enabled; zp_92-zp_95 and the line stepper state clobbered
|
|
; Called from: $8022 (resetDroneScreen) and $82B1 (readDroneHeadingFromJoystick)
|
|
; ----------------------------------------------------------------------
|
|
drawDroneHeadingIndicator:
|
|
lda #$03 ; 823F sprite 3 = the path/route sprite at $8AC0
|
|
jsr clearSpriteShape ; 8241 zero its 64 shape bytes
|
|
lda #$0A ; 8244 sprite row 10 ...
|
|
sta lineCurRow ; 8246 ... zp_92 = line start row (centre of the 24x21 sprite)
|
|
lda #$0C ; 8248 sprite column 12 ...
|
|
sta lineCurCol ; 824A ... zp_93 = line start column
|
|
lda #$0B ; 824C default line colour $0B (dark grey)
|
|
L_824E:
|
|
ldy droneJoyDirection ; 824E current steer direction code
|
|
cpy #$02 ; 8250 codes 0 and 1 are due north and due south
|
|
bcs L_8256 ; 8252 any other direction keeps dark grey
|
|
lda #$06 ; 8254 N/S get colour 6 (blue) instead
|
|
L_8256:
|
|
sta sprite3ColourShadow ; 8256 $900B = sprite 3 colour
|
|
ldx joyDirToHeadingTable,y ; 8259 direction code -> clockwise heading 0-7
|
|
lda headingLineEndRowTable,x; 825C end row of the pointer for that heading
|
|
sta lineEndRow ; 825F zp_94 = line end row
|
|
lda headingLineEndColTable,x; 8261 end column of the pointer
|
|
L_8264:
|
|
sta lineEndCol ; 8264 zp_95 = line end column
|
|
jsr drawLineInPathSprite ; 8266 A still holds the end column ($03-$16), so bit 7 is clear and the line is drawn solid
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Sprite 0 carries an identical copy of the line, one pixel up and left and in white.
|
|
; ----------------------------------------------------------------------
|
|
ldy #$3F ; 8269 64 shape bytes, copied backwards
|
|
L_826B:
|
|
lda sprite3Shape,y ; 826B read sprite 3's shape
|
|
sta sprite0Shape,y ; 826E write sprite 0's shape at $8A00
|
|
dey ; 8271 next byte down
|
|
bpl L_826B ; 8272 until byte 0
|
|
lda #$09 ; 8274 bit 0 + bit 3 = sprites 0 and 3
|
|
L_8276:
|
|
jmp enableSprites ; 8276 OR them into the enable shadow and return
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; advanceDroneCamera - One drone step on the DRONE screen, run once per lock-step command exchange. It
|
|
; consumes the exchange flag, redraws the 14 x 12 camera window from the map around the drone,
|
|
; replaces half of the pictures with static when the drone bay is damaged, burns one unit of fuel and
|
|
; reloads the silhouette for the drone's current heading. The drone itself is moved by steerDrone,
|
|
; which sendOutgoingPacket calls just before the $8D report goes out.
|
|
; In: exchangeAppliedFlag $920E, droneHeading $920B, droneFuel $9220, comcenDroneStatus $9244,
|
|
; droneViewOriginCol/Row $92F6/$92F7
|
|
; Out: $920E = 0, $9220 decremented, camera window redrawn, sprite 5 reloaded, fuel read-out reprinted
|
|
; Called from: $815F (droneFlightLoop) and $81B8 (strikeWithDrone waiting for its $8E)
|
|
; ----------------------------------------------------------------------
|
|
advanceDroneCamera:
|
|
lda #$00 ; 8279 clear the flag ...
|
|
sta exchangeAppliedFlag ; 827B ... so only one camera step happens per exchange
|
|
jsr updateExplosionMarker ; 827E in this build this only switches sprite 4 (the comcen ring) off again
|
|
jsr drawDroneRadarWindow ; 8281 redraw the 14x12 camera window from the map around zp_A5/zp_A6
|
|
lda comcenDroneStatus ; 8284 $9244 != 0 = the drone bay has been knocked out
|
|
beq L_8293 ; 8287 undamaged - show a clean picture
|
|
jsr nextGameRandom ; 8289 roll the game RNG
|
|
bmi L_8293 ; 828C bit 7 set - this step still shows a clean picture
|
|
lda #$AA ; 828E $AA = light red in both nibbles
|
|
jsr fillDroneViewWindow ; 8290 otherwise bury the window in static
|
|
L_8293:
|
|
dec droneFuel ; 8293 one unit of fuel per exchange, so 100 exchanges of flight
|
|
lda #$05 ; 8296 5 = sprite 5
|
|
sta activeSpriteIndex ; 8298 $91CC selects sprite 5
|
|
lda droneHeading ; 829B current heading, which steerDrone turns one step per exchange
|
|
D_829E:
|
|
jsr loadDirectionShapeSprite; 829E reload the matching silhouette and enable sprite 5
|
|
jmp drawDroneFuelDigits ; 82A1 reprint the fuel digits and return through them
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; readDroneHeadingFromJoystick - Latches the joystick direction as the drone's new steer direction and
|
|
; redraws the compass pointer. A centred stick ($FF from joystickDirectionTable) leaves the current
|
|
; heading alone; after a successful read the stick is ignored for 20 frames so the pointer cannot
|
|
; spin.
|
|
; In: joyDirection zp_5D (the IRQ-latched joystick 2 nibble), joystickDirectionTable $1FA6
|
|
; Out: droneJoyDirection zp_5E, menuBlinkTimer zp_6E = 20, sprites 0 and 3 redrawn
|
|
; Called from: $808D (launchDrone) and $8154 (droneFlightLoop, once the lockout has expired)
|
|
; ----------------------------------------------------------------------
|
|
readDroneHeadingFromJoystick:
|
|
ldy joyDirection ; 82A4 zp_5D = joystick 2 direction bits latched by the IRQ
|
|
lda joystickDirectionTable,y; 82A6 $1FA6 turns them into a direction code 0-7, or $FF when centred
|
|
bmi L_82B4 ; 82A9 stick centred - keep steering the way we were
|
|
ldy #$14 ; 82AB 20 frames ...
|
|
sty menuBlinkTimer ; 82AD ... of lockout before the stick is read again
|
|
sta droneJoyDirection ; 82AF zp_5E = the new steer direction
|
|
jsr drawDroneHeadingIndicator; 82B1 redraw the compass pointer to match
|
|
L_82B4:
|
|
rts ; 82B4 done
|
|
|
|
|
|
; shotLineWorkVars - the eight work bytes of drawShotLineInViewport, zero in the loaded image.
|
|
shotLineWorkVars:
|
|
.byte $00 ; 82B5 . viewLeft: map column of the viewport's left edge; later rebased to 0 and multiplied by 8 (fat pixels)
|
|
D_82B6:
|
|
.byte $00 ; 82B6 column bound used by the endpoint test = viewLeft + 7 (the window is 7 cells wide)
|
|
D_82B7:
|
|
.byte $00 ; 82B7 viewTop: map row of the viewport's top edge; later rebased and multiplied by 8
|
|
D_82B8:
|
|
.byte $00 ; 82B8 row bound used by the endpoint test = viewTop + 5 (the window is 5 cells high)
|
|
D_82B9:
|
|
.byte $00 ; 82B9 endpoint visibility: $FF = only the shooter is in view, $02 = only the target, $01 = both, 0 = neither
|
|
D_82BA:
|
|
.byte $00 ; 82BA shift added to every column when the view origin is negative (= -viewOriginCol)
|
|
D_82BB:
|
|
.byte $00 ; 82BB shift added to every row when the view origin is negative (= -viewOriginRow)
|
|
D_82BC:
|
|
.byte $00 ; 82BC countdown of the muzzle gap: 4 down to 1 = skip, 0 = draw the diamond, negative = draw the line
|
|
|
|
; unusedBytes82BD - $00,$60: not referenced from anywhere in this overlay variant, padding in front of
|
|
; drawShotLineInViewport.
|
|
unusedBytes82BD:
|
|
.byte $00,$60 ; 82BD .` unreferenced filler
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawShotLineInViewport - Draws the tracer of one shot inside the 7 x 5 cell zoomed battlefield
|
|
; viewport (2 x 2 characters per cell at character row 1, column 25; 8 'fat' multicolour pixels per
|
|
; map cell, fat-pixel origin X=100, Y=4). It first forces the %01 colour of all 14 x 10 viewport
|
|
; cells to white, then decides whether the shooter and/or the target cell is inside the window,
|
|
; rebases and scales the coordinates, walks the line with the high-memory stepper and plots colour-1
|
|
; fat pixels. The first four steps out of the muzzle are left blank, the fifth carries a small
|
|
; diamond, and a BOOMER shot ($923D = 2) advances three cells per plotted pixel, which makes it
|
|
; dashed. Finally viewRedrawTimer is armed so the view is redrawn - erasing the line - 15 frames on.
|
|
; In: zp_92/zp_93 = shooter row/column, zp_94/zp_95 = target row/column, zp_A5/zp_A6 = view origin,
|
|
; attackerType $923D, the screen row tables at $9382/$939B
|
|
; Out: bitmap pixels and viewport colour nibbles changed, viewRedrawTimer $9234 = $8F, $82B5-$82BC
|
|
; used, zp_92-zp_95 and the line stepper state clobbered, zp_B6/zp_B7 clobbered
|
|
; Called from: $3A30, resolveAttack in the main program. The only user of plotFatPixel ($FC58).
|
|
; Force the %01 bit pair of every character cell of the viewport (rows 1-10, columns 25-38) to white
|
|
; so the line about to be plotted is visible whatever the terrain colours were.
|
|
; ----------------------------------------------------------------------
|
|
drawShotLineInViewport:
|
|
ldy #$01 ; 82BF screen row table index 1 = character row 1, the top of the viewport
|
|
lda screenRowLoTable,y ; 82C1 screen RAM ($8C00) row address, low byte
|
|
sta screenPtr ; 82C4 zp_B6
|
|
lda screenRowHiTable,y ; 82C6 row address high byte
|
|
sta screenPtrHi ; 82C9 zp_B7 - the loop writes screen RAM, not colour RAM
|
|
ldx #$0A ; 82CB 10 character rows = 5 map cells
|
|
L_82CD:
|
|
ldy #$26 ; 82CD start at the rightmost character column, 38
|
|
L_82CF:
|
|
lda (screenPtr),y ; 82CF current colour byte of the cell
|
|
and #$0F ; 82D1 keep the low nibble = the %10 colour
|
|
L_82D3:
|
|
ora #$10 ; 82D3 force the high nibble to 1 = white for the %01 bit pair
|
|
sta (screenPtr),y ; 82D5 store it back
|
|
dey ; 82D7 next column to the left
|
|
cpy #$19 ; 82D8 leftmost viewport column is 25
|
|
bcs L_82CF ; 82DA keep going while Y >= 25
|
|
dex ; 82DC one row done
|
|
beq L_82EC ; 82DD all ten rows done
|
|
clc ; 82DF clear carry
|
|
lda screenPtr ; 82E0 screen pointer low byte
|
|
adc #$28 ; 82E2 one character row = 40 bytes
|
|
sta screenPtr ; 82E4 zp_B6
|
|
bcc L_82CD ; 82E6 no carry - next row
|
|
inc screenPtrHi ; 82E8 carry into the high byte
|
|
bne L_82CD ; 82EA always taken - next row
|
|
|
|
; ----------------------------------------------------------------------
|
|
; The 7x5 window may hang off the left or top edge of the map, in which case zp_A5/zp_A6 are negative.
|
|
; Shift the origin and both endpoints right/down by -origin so all the following arithmetic stays
|
|
; unsigned, and work out the window bounds.
|
|
; ----------------------------------------------------------------------
|
|
L_82EC:
|
|
lda #$00 ; 82EC no endpoint seen yet
|
|
sta D_82B9 ; 82EE $82B9 = endpoint visibility flags
|
|
lda viewOriginCol ; 82F1 view origin column
|
|
bpl L_8313 ; 82F3 non-negative - use it as it is
|
|
eor #$FF ; 82F5 negate it: EOR $FF ...
|
|
L_82F7:
|
|
clc ; 82F7 ... clear carry ...
|
|
adc #$01 ; 82F8 ... +1 gives -viewOriginCol
|
|
sta D_82BA ; 82FA $82BA = the column shift
|
|
clc ; 82FD clear carry
|
|
lda lineCurCol ; 82FE shooter column
|
|
adc D_82BA ; 8300 shift it
|
|
sta lineCurCol ; 8303 zp_93 updated
|
|
clc ; 8305 clear carry
|
|
lda lineEndCol ; 8306 target column
|
|
adc D_82BA ; 8308 shift it
|
|
sta lineEndCol ; 830B zp_95 updated
|
|
clc ; 830D clear carry
|
|
lda viewOriginCol ; 830E the origin itself
|
|
adc D_82BA ; 8310 shifted origin = 0
|
|
L_8313:
|
|
sta shotLineWorkVars ; 8313 $82B5 = left edge of the window (now never negative)
|
|
clc ; 8316 clear carry
|
|
adc #$07 ; 8317 the window is 7 cells wide
|
|
D_8319:
|
|
sta D_82B6 ; 8319 $82B6 = right bound used below (note it is left+7, while the value tested against it is already relative to left - too permissive for a non-zero origin; the fat-pixel clip in the plot loop catches the difference)
|
|
lda viewOriginRow ; 831C view origin row
|
|
bpl L_833E ; 831E non-negative - use it as it is
|
|
eor #$FF ; 8320 negate it ...
|
|
L_8322:
|
|
clc ; 8322 ... clear carry ...
|
|
adc #$01 ; 8323 ... +1 gives -viewOriginRow
|
|
sta D_82BB ; 8325 $82BB = the row shift
|
|
clc ; 8328 clear carry
|
|
L_8329:
|
|
lda lineCurRow ; 8329 shooter row
|
|
L_832B:
|
|
adc D_82BB ; 832B shift it
|
|
sta lineCurRow ; 832E zp_92 updated
|
|
clc ; 8330 clear carry
|
|
lda lineEndRow ; 8331 target row
|
|
adc D_82BB ; 8333 shift it
|
|
sta lineEndRow ; 8336 zp_94 updated
|
|
L_8338:
|
|
clc ; 8338 clear carry
|
|
L_8339:
|
|
lda viewOriginRow ; 8339 the origin itself
|
|
adc D_82BB ; 833B shifted origin = 0
|
|
L_833E:
|
|
sta D_82B7 ; 833E $82B7 = top edge of the window
|
|
clc ; 8341 clear carry
|
|
adc #$05 ; 8342 the window is 5 cells high
|
|
sta D_82B8 ; 8344 $82B8 = bottom bound (same +top skew as $82B6)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Is the shooter's cell inside the 7x5 window?
|
|
; ----------------------------------------------------------------------
|
|
sec ; 8347 no borrow
|
|
lda lineCurCol ; 8348 shooter column
|
|
sbc shotLineWorkVars ; 834A make it relative to the left edge
|
|
bcc testTargetEndpointInView; 834D left of the window - not visible
|
|
cmp D_82B6 ; 834F compare against the right bound
|
|
bcs testTargetEndpointInView; 8352 right of the window - not visible
|
|
sec ; 8354 no borrow
|
|
lda lineCurRow ; 8355 shooter row
|
|
sbc D_82B7 ; 8357 make it relative to the top edge
|
|
bcc testTargetEndpointInView; 835A above the window - not visible
|
|
cmp D_82B8 ; 835C compare against the bottom bound
|
|
bcs testTargetEndpointInView; 835F below the window - not visible
|
|
dec D_82B9 ; 8361 flags = $FF: the shooter is in view
|
|
|
|
; ----------------------------------------------------------------------
|
|
; ... and is the target's cell inside it?
|
|
; ----------------------------------------------------------------------
|
|
testTargetEndpointInView:
|
|
sec ; 8364 no borrow
|
|
lda lineEndCol ; 8365 target column
|
|
sbc shotLineWorkVars ; 8367 relative to the left edge
|
|
bcc L_8384 ; 836A outside
|
|
cmp D_82B6 ; 836C right bound
|
|
bcs L_8384 ; 836F outside
|
|
sec ; 8371 no borrow
|
|
lda lineEndRow ; 8372 target row
|
|
sbc D_82B7 ; 8374 relative to the top edge
|
|
bcc L_8384 ; 8377 outside
|
|
cmp D_82B8 ; 8379 bottom bound
|
|
bcs L_8384 ; 837C outside
|
|
inc D_82B9 ; 837E flags += 1 ...
|
|
inc D_82B9 ; 8381 ... twice, so $02 = only the target, $01 = both endpoints in view
|
|
L_8384:
|
|
lda D_82B9 ; 8384 any endpoint in view?
|
|
bne L_838C ; 8387 yes - draw it
|
|
jmp L_840E ; 8389 neither - nothing to draw, and no redraw to schedule
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Rebase the window origin and both endpoints so the smallest value is 0, then scale everything from
|
|
; map cells to fat pixels (8 per cell). Rebasing is what stops the x8 from overflowing a byte.
|
|
; ----------------------------------------------------------------------
|
|
L_838C:
|
|
jsr rebaseLineCoordsToMin ; 838C subtract the minimum column and the minimum row from all three values
|
|
lda shotLineWorkVars ; 838F window left edge, in cells
|
|
asl a ; 8392 x2 ...
|
|
asl a ; 8393 x4 ...
|
|
asl a ; 8394 x8 = fat pixels per map cell
|
|
sta shotLineWorkVars ; 8395 $82B5 now in fat pixels
|
|
L_8398:
|
|
lda D_82B7 ; 8398 window top edge, in cells
|
|
asl a ; 839B x2 ...
|
|
asl a ; 839C x4 ...
|
|
asl a ; 839D x8
|
|
sta D_82B7 ; 839E $82B7 now in fat pixels
|
|
jsr scaleLineCoordsToFatPixels; 83A1 scale zp_92-zp_95 the same way and add the half-cell offsets (+4 rows, +3 columns)
|
|
jsr initLineStepper ; 83A4 set up the Bresenham walk from (zp_93,zp_92) to (zp_95,zp_94)
|
|
lda #$04 ; 83A7 4 steps of muzzle gap ...
|
|
sta D_82BC ; 83A9 ... in $82BC
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Walk the line one fat pixel step at a time. A point is plotted only when it lies strictly inside
|
|
; the window - relative coordinate 0 is skipped, so the left column and top row are never drawn.
|
|
; ----------------------------------------------------------------------
|
|
L_83AC:
|
|
sec ; 83AC no borrow
|
|
lda lineCurCol ; 83AD current column of the walk, in fat pixels
|
|
sbc shotLineWorkVars ; 83AF relative to the window's left edge
|
|
beq L_83E9 ; 83B2 exactly on the left edge - skip it
|
|
bcc L_83E9 ; 83B4 left of the window - skip
|
|
cmp #$38 ; 83B6 7 cells x 8 fat pixels = 56
|
|
bcs L_83E9 ; 83B8 right of the window - skip
|
|
clc ; 83BA clear carry
|
|
adc #$64 ; 83BB character column 25 = fat pixel column 100
|
|
tax ; 83BD X = fat pixel column on screen
|
|
sec ; 83BE no borrow
|
|
lda lineCurRow ; 83BF current row of the walk
|
|
sbc D_82B7 ; 83C1 relative to the window's top edge
|
|
beq L_83E9 ; 83C4 exactly on the top edge - skip
|
|
bcc L_83E9 ; 83C6 above the window - skip
|
|
cmp #$28 ; 83C8 5 cells x 8 fat pixels = 40
|
|
bcs L_83E9 ; 83CA below the window - skip
|
|
clc ; 83CC clear carry
|
|
adc #$04 ; 83CD character row 1 = fat pixel row 4
|
|
tay ; 83CF Y = fat pixel row on screen
|
|
lda D_82B9 ; 83D0 endpoint visibility flags
|
|
cmp #$02 ; 83D3 only the target is in view?
|
|
beq L_83E4 ; 83D5 then the shooter is off screen and the whole visible line is solid
|
|
lda D_82BC ; 83D7 muzzle gap countdown
|
|
bmi L_83E4 ; 83DA already negative - past the gap, plot the line
|
|
bne L_83E9 ; 83DC still positive - inside the 4-step gap, plot nothing
|
|
jsr plotDiamondMarker ; 83DE exactly 0 - this is the muzzle, draw the little diamond
|
|
jmp L_83E9 ; 83E1 and step on
|
|
|
|
L_83E4:
|
|
lda #$01 ; 83E4 colour 1 = white
|
|
jsr plotFatPixel ; 83E6 plot one fat multicolour pixel at (X,Y)
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Advance the walk. A BOOMER's shot ($923D = 2) takes three steps for every plotted pixel, so its
|
|
; tracer comes out dashed.
|
|
; ----------------------------------------------------------------------
|
|
L_83E9:
|
|
dec D_82BC ; 83E9 one step of the gap countdown used up
|
|
lda lineStepsLeft ; 83EC steps left on the line
|
|
beq L_8409 ; 83EE line finished
|
|
jsr stepLine ; 83F0 advance zp_92/zp_93 by one cell along the line
|
|
lda attackerType ; 83F3 $923D = type of the shooting unit
|
|
cmp #$02 ; 83F6 2 = BOOMER
|
|
bne L_8406 ; 83F8 anything else plots every step
|
|
dec D_82BC ; 83FA BOOMER: burn a second gap count ...
|
|
jsr stepLine ; 83FD ... and a second step ...
|
|
dec D_82BC ; 8400 ... and a third gap count ...
|
|
jsr stepLine ; 8403 ... and a third step, leaving gaps in the tracer
|
|
L_8406:
|
|
jmp L_83AC ; 8406 back for the next point
|
|
|
|
L_8409:
|
|
lda #$8F ; 8409 $8F = redraw pending, 15 frames of countdown (the IRQ stops at $80)
|
|
sta viewRedrawTimer ; 840B $9234: the view is repainted 15 frames from now, erasing the line
|
|
L_840E:
|
|
rts ; 840E done (also the early exit when neither endpoint is visible)
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; rebaseLineCoordsToMin - Subtracts the smallest of the three column values (window left $82B5,
|
|
; shooter zp_93, target zp_95) from all three, and likewise the smallest of the three row values
|
|
; ($82B7, zp_92, zp_94), so the smallest becomes 0. That is what keeps the following multiply by 8
|
|
; inside a byte.
|
|
; In: $82B5, $82B7, zp_92-zp_95
|
|
; Out: the same six values, rebased
|
|
; Called from: $838C (drawShotLineInViewport)
|
|
; ----------------------------------------------------------------------
|
|
rebaseLineCoordsToMin:
|
|
lda shotLineWorkVars ; 840F window left edge
|
|
cmp lineCurCol ; 8412 compare with the shooter column
|
|
bcs L_841A ; 8414 left >= shooter, so the minimum is one of the two endpoints
|
|
cmp lineEndCol ; 8416 left < shooter: compare with the target column
|
|
bcc L_844C ; 8418 left is smaller than both - it is the minimum
|
|
L_841A:
|
|
lda lineCurCol ; 841A shooter column
|
|
cmp lineEndCol ; 841C compare with the target column
|
|
bcc L_8436 ; 841E shooter is the minimum
|
|
sec ; 8420 no borrow
|
|
lda shotLineWorkVars ; 8421 target column is the minimum: rebase the window edge ...
|
|
sbc lineEndCol ; 8424 ... by subtracting it ...
|
|
sta shotLineWorkVars ; 8426 ... and store it back
|
|
sec ; 8429 no borrow
|
|
lda lineCurCol ; 842A rebase the shooter column ...
|
|
sbc lineEndCol ; 842C ... the same way ...
|
|
sta lineCurCol ; 842E ... and store it
|
|
lda #$00 ; 8430 the minimum itself becomes 0
|
|
L_8432:
|
|
sta lineEndCol ; 8432 zp_95 = 0
|
|
beq L_8461 ; 8434 always taken - now do the rows
|
|
L_8436:
|
|
sec ; 8436 no borrow
|
|
lda shotLineWorkVars ; 8437 shooter column is the minimum: rebase the window edge
|
|
sbc lineCurCol ; 843A subtract it
|
|
sta shotLineWorkVars ; 843C store it back
|
|
sec ; 843F no borrow
|
|
lda lineEndCol ; 8440 rebase the target column
|
|
sbc lineCurCol ; 8442 subtract it
|
|
sta lineEndCol ; 8444 store it back
|
|
lda #$00 ; 8446 the minimum becomes 0
|
|
sta lineCurCol ; 8448 zp_93 = 0
|
|
beq L_8461 ; 844A always taken - now do the rows
|
|
L_844C:
|
|
sec ; 844C no borrow
|
|
lda lineCurCol ; 844D window edge is the minimum: rebase the shooter column
|
|
D_844F:
|
|
sbc shotLineWorkVars ; 844F subtract it
|
|
sta lineCurCol ; 8452 store it back
|
|
sec ; 8454 no borrow
|
|
lda lineEndCol ; 8455 rebase the target column
|
|
sbc shotLineWorkVars ; 8457 subtract it
|
|
sta lineEndCol ; 845A store it back
|
|
L_845C:
|
|
lda #$00 ; 845C the minimum becomes 0
|
|
sta shotLineWorkVars ; 845E $82B5 = 0
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Exactly the same three-way minimum for the rows.
|
|
; ----------------------------------------------------------------------
|
|
L_8461:
|
|
lda D_82B7 ; 8461 window top edge
|
|
cmp lineCurRow ; 8464 compare with the shooter row
|
|
bcs L_846C ; 8466 top >= shooter, so an endpoint holds the minimum
|
|
cmp lineEndRow ; 8468 compare with the target row
|
|
D_846A:
|
|
bcc L_849C ; 846A the top edge is smaller than both
|
|
L_846C:
|
|
lda lineCurRow ; 846C shooter row
|
|
cmp lineEndRow ; 846E compare with the target row
|
|
bcc L_8487 ; 8470 shooter row is the minimum
|
|
sec ; 8472 no borrow
|
|
lda D_82B7 ; 8473 target row is the minimum: rebase the top edge
|
|
sbc lineEndRow ; 8476 subtract it
|
|
sta D_82B7 ; 8478 store it back
|
|
sec ; 847B no borrow
|
|
lda lineCurRow ; 847C rebase the shooter row
|
|
L_847E:
|
|
sbc lineEndRow ; 847E subtract it
|
|
sta lineCurRow ; 8480 store it back
|
|
lda #$00 ; 8482 the minimum becomes 0
|
|
sta lineEndRow ; 8484 zp_94 = 0
|
|
rts ; 8486 done
|
|
|
|
L_8487:
|
|
sec ; 8487 no borrow
|
|
lda D_82B7 ; 8488 shooter row is the minimum: rebase the top edge
|
|
sbc lineCurRow ; 848B subtract it
|
|
sta D_82B7 ; 848D store it back
|
|
sec ; 8490 no borrow
|
|
lda lineEndRow ; 8491 rebase the target row
|
|
D_8493:
|
|
sbc lineCurRow ; 8493 subtract it
|
|
sta lineEndRow ; 8495 store it back
|
|
lda #$00 ; 8497 the minimum becomes 0
|
|
sta lineCurRow ; 8499 zp_92 = 0
|
|
rts ; 849B done
|
|
|
|
L_849C:
|
|
sec ; 849C no borrow
|
|
lda lineCurRow ; 849D top edge is the minimum: rebase the shooter row
|
|
sbc D_82B7 ; 849F subtract it
|
|
sta lineCurRow ; 84A2 store it back
|
|
L_84A4:
|
|
sec ; 84A4 no borrow
|
|
lda lineEndRow ; 84A5 rebase the target row
|
|
sbc D_82B7 ; 84A7 subtract it
|
|
L_84AA:
|
|
sta lineEndRow ; 84AA store it back
|
|
lda #$00 ; 84AC the minimum becomes 0
|
|
sta D_82B7 ; 84AE $82B7 = 0
|
|
rts ; 84B1 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; scaleLineCoordsToFatPixels - Converts the two line endpoints from map cells to fat pixels: row*8+4
|
|
; and column*8+3, i.e. the middle of the cell in the 8x8-fat-pixel grid the viewport uses. Three of
|
|
; the four cases saturate to $FF on overflow; the zp_92 (shooter row) case is missing its LDA #$FF, so
|
|
; it keeps the wrapped value instead - harmless because rebaseLineCoordsToMin has already brought all
|
|
; four values below 32 cells in practice.
|
|
; In: zp_92-zp_95 in map cells
|
|
; Out: zp_92-zp_95 in fat pixels
|
|
; Called from: $83A1 (drawShotLineInViewport)
|
|
; ----------------------------------------------------------------------
|
|
scaleLineCoordsToFatPixels:
|
|
lda lineEndRow ; 84B2 target row, in cells
|
|
asl a ; 84B4 x2 ...
|
|
asl a ; 84B5 x4 ...
|
|
asl a ; 84B6 x8 fat pixels per cell
|
|
bcc L_84BD ; 84B7 no overflow - add the half-cell offset
|
|
lda #$FF ; 84B9 overflow - clamp to the last fat pixel row
|
|
bmi L_84C0 ; 84BB always taken ($FF is negative)
|
|
L_84BD:
|
|
clc ; 84BD clear carry
|
|
adc #$04 ; 84BE +4 = the vertical centre of the cell
|
|
L_84C0:
|
|
sta lineEndRow ; 84C0 zp_94
|
|
lda lineEndCol ; 84C2 target column, in cells
|
|
asl a ; 84C4 x2 ...
|
|
asl a ; 84C5 x4 ...
|
|
asl a ; 84C6 x8
|
|
L_84C7:
|
|
bcc L_84CD ; 84C7 no overflow
|
|
lda #$FF ; 84C9 clamp
|
|
bmi L_84D0 ; 84CB always taken
|
|
L_84CD:
|
|
clc ; 84CD clear carry
|
|
adc #$03 ; 84CE +3 = the horizontal centre of the cell
|
|
L_84D0:
|
|
sta lineEndCol ; 84D0 zp_95
|
|
lda lineCurRow ; 84D2 shooter row, in cells
|
|
asl a ; 84D4 x2 ...
|
|
asl a ; 84D5 x4 ...
|
|
asl a ; 84D6 x8
|
|
bcc L_84DB ; 84D7 no overflow - add the offset
|
|
bmi L_84DE ; 84D9 overflowed: the LDA #$FF of the other three cases is missing here, so the wrapped value is kept
|
|
L_84DB:
|
|
clc ; 84DB clear carry
|
|
adc #$04 ; 84DC +4 = the vertical centre of the cell
|
|
L_84DE:
|
|
sta lineCurRow ; 84DE zp_92
|
|
lda lineCurCol ; 84E0 shooter column, in cells
|
|
asl a ; 84E2 x2 ...
|
|
asl a ; 84E3 x4 ...
|
|
asl a ; 84E4 x8
|
|
bcc L_84EB ; 84E5 no overflow
|
|
lda #$FF ; 84E7 clamp
|
|
bmi L_84EE ; 84E9 always taken
|
|
L_84EB:
|
|
clc ; 84EB clear carry
|
|
scaleColAddHalfCell:
|
|
adc #$03 ; 84EC +3 = the horizontal centre of the cell
|
|
L_84EE:
|
|
sta lineCurCol ; 84EE zp_93
|
|
rts ; 84F0 done
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; plotDiamondMarker - Plots the four colour-1 fat pixels at (X-1,Y-1), (X+1,Y-1), (X-1,Y+1) and
|
|
; (X+1,Y+1): the small diamond drawn at the muzzle end of a shot tracer. X and Y are left exactly as
|
|
; they were on entry.
|
|
; In: X = fat pixel column, Y = fat pixel row
|
|
; Out: four bitmap pixels set; X and Y preserved
|
|
; Called from: $83DE (drawShotLineInViewport, on the fifth step out of the muzzle)
|
|
; ----------------------------------------------------------------------
|
|
plotDiamondMarker:
|
|
dey ; 84F1 one fat pixel up
|
|
dex ; 84F2 one fat pixel left
|
|
lda #$01 ; 84F3 colour 1 = white
|
|
jsr plotFatPixel ; 84F5 top-left corner of the diamond
|
|
inx ; 84F8 two to the right ...
|
|
inx ; 84F9 ... = one right of the centre
|
|
lda #$01 ; 84FA colour 1
|
|
jsr plotFatPixel ; 84FC top-right corner
|
|
dex ; 84FF two to the left ...
|
|
dex ; 8500 ... back to one left of the centre
|
|
iny ; 8501 two down ...
|
|
iny ; 8502 ... = one below the centre
|
|
lda #$01 ; 8503 colour 1
|
|
jsr plotFatPixel ; 8505 bottom-left corner
|
|
inx ; 8508 two to the right ...
|
|
inx ; 8509 ... = one right of the centre
|
|
lda #$01 ; 850A colour 1
|
|
jsr plotFatPixel ; 850C bottom-right corner
|
|
dex ; 850F back to the entry column ...
|
|
dey ; 8510 ... and the entry row
|
|
plotDiamondDone:
|
|
rts ; 8511 done, X and Y unchanged
|
|
|
|
|
|
; comSpeedDisplayTable - the COMSPEED figure shown on the STATS tab, indexed by the comcen speed class
|
|
; 0-5 held in $92B4 (us) and $92B5 (them). Class 4 is the immobile comcen and prints 0.
|
|
comSpeedDisplayTable:
|
|
.byte $01,$02,$02,$02,$00,$03 ; 8512 ...... class 0 -> 1, class 1-3 -> 2, class 4 -> 0 (immobile), class 5 -> 3
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawStatsTab - Fills in the numbers of the console's STATS panel. With the text window left edge at
|
|
; column 10 it prints two columns - US at column 9 (relative), THEM at column 14 - of: living GRUNTS,
|
|
; RIDERS, BOOMERS and SPIES (rows 9-12, counted by scanning the 50 unit slots downwards from each
|
|
; side's comcen), DRONES (row 14), MISSILES (row 15), COMSPEED (row 17), TER PTS (row 18), RECYCLER
|
|
; (row 19) and the five-character map name (row 20). Clearing exchangeAppliedFlag $920E marks the
|
|
; panel as refreshed for this turn.
|
|
; In: comcenUnit $9236, unitTypeTable $F76C, $92AE/$92AF drones, $92B0/$92B1 missiles, $92B4/$92B5
|
|
; comcen speed, $92AC terrain points, $92A6 recycler mode, playerSide $0B9F, map name $0BA9
|
|
; Out: the STATS text drawn; $920E = 0; zp_18, zp_1A, zp_20, zp_21, zp_3B, zp_3D, zp_3F, zp_40 used
|
|
; Called from: $1460 and $14D3 in the main program (console tab drawing and refresh)
|
|
; ----------------------------------------------------------------------
|
|
drawStatsTab:
|
|
lda #$0A ; 8518 text window starts at screen column 10
|
|
sta textWindowLeft ; 851A zp_3B, added to every text column below
|
|
lda #$00 ; 851C 0 = refreshed
|
|
sta exchangeAppliedFlag ; 851E $920E: consume the 'a turn was exchanged, redraw the stats' request
|
|
lda #$61 ; 8521 colour byte $61: %01 pixels blue, %10 pixels white
|
|
sta textColour ; 8523 zp_3D = colour written with every printed character
|
|
lda #$09 ; 8525 first data row
|
|
sta textCursorRow ; 8527 zp_40 = text cursor row 9 (GRUNTS)
|
|
lda #$00 ; 8529 unit type 0 = GRUNT
|
|
sta scratch20 ; 852B zp_20 = the type being counted
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Outer loop over the four fighting unit types (0 GRUNT, 1 RIDER, 2 BOOMER, 3 SPY); one text row each.
|
|
; Inner pass counts our side first (column 9) and then the opponent's (column 14).
|
|
; ----------------------------------------------------------------------
|
|
L_852D:
|
|
ldx #$01 ; 852D one more column to print after this one
|
|
stx scratch21 ; 852F zp_21 = columns still to do
|
|
lda #$09 ; 8531 column 9 = the US column
|
|
ldy comcenUnit ; 8533 start the scan at our own comcen (49 or 99)
|
|
L_8536:
|
|
jsr setTextColumn ; 8536 position the text cursor at that column
|
|
lda #$00 ; 8539 count = 0
|
|
sta scratch18 ; 853B zp_18 = the running count
|
|
lda #$32 ; 853D 50 unit slots per side
|
|
sta scratch1A ; 853F zp_1A = slots still to scan
|
|
L_8541:
|
|
lda unitTypeTable,y ; 8541 type/flags byte of this unit ($F76C + unit)
|
|
bmi L_854E ; 8544 bit 7 set = the unit is dead, skip it
|
|
and #$07 ; 8546 type is in bits 0-2 (0 GRUNT, 1 RIDER, 2 BOOMER, 3 SPY, 4 COMCEN)
|
|
cmp scratch20 ; 8548 is it the type being counted?
|
|
bne L_854E ; 854A no - next slot
|
|
inc scratch18 ; 854C yes - one more of this type alive
|
|
L_854E:
|
|
dey ; 854E next unit index downwards (comcen is the highest of its side)
|
|
dec scratch1A ; 854F one slot fewer
|
|
bne L_8541 ; 8551 50 slots not done yet
|
|
ldx textCursorCol ; 8553 X = the column just set
|
|
lda scratch18 ; 8555 the count
|
|
jsr printNumberRightAligned ; 8557 print it right-aligned in a 3-character field
|
|
dec scratch21 ; 855A one column done
|
|
L_855C:
|
|
bmi L_8568 ; 855C both columns done - next unit type
|
|
lda comcenUnit ; 855E own comcen index
|
|
eor #$52 ; 8561 EOR $52 swaps 49 <-> 99, i.e. the other side's comcen
|
|
tay ; 8563 scan starts there
|
|
lda #$0E ; 8564 column 14 = the THEM column
|
|
bne L_8536 ; 8566 always taken - print the second column
|
|
L_8568:
|
|
inc textCursorRow ; 8568 next text row
|
|
inc scratch20 ; 856A next unit type
|
|
lda scratch20 ; 856C which type is next?
|
|
cmp #$04 ; 856E types 0-3 only; type 4 (COMCEN) is not listed
|
|
bcc L_852D ; 8570 more types to do
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Fixed rows below the unit table: DRONES (14), MISSILES (15), COMSPEED (17), TER PTS (18), RECYCLER
|
|
; (19) and the map name (20).
|
|
; ----------------------------------------------------------------------
|
|
inc textCursorRow ; 8572 skip a row - DRONES is on row 14
|
|
ldx #$09 ; 8574 US column
|
|
lda dronesLeft ; 8576 $92AE = our drones left
|
|
jsr printNumberRightAligned ; 8579 print it
|
|
ldx #$0E ; 857C THEM column
|
|
lda opponentDronesLeft ; 857E $92AF = the opponent's drones left
|
|
jsr printNumberRightAligned ; 8581 print it
|
|
inc textCursorRow ; 8584 row 15 = MISSILES
|
|
ldx #$09 ; 8586 US column
|
|
lda missilesLeft ; 8588 $92B0 = our missiles left
|
|
jsr printNumberRightAligned ; 858B print it
|
|
ldx #$0E ; 858E THEM column
|
|
lda opponentMissilesLeft ; 8590 $92B1 = the opponent's missiles left
|
|
jsr printNumberRightAligned ; 8593 print it
|
|
inc textCursorRow ; 8596 row 16 ...
|
|
inc textCursorRow ; 8598 ... row 17 = COMSPEED
|
|
ldx #$09 ; 859A US column
|
|
ldy comcenSpeedClass ; 859C $92B4 = our comcen speed class 0-5
|
|
lda comSpeedDisplayTable,y ; 859F translate it into the printed figure
|
|
jsr printNumberRightAligned ; 85A2 print it
|
|
ldx #$0E ; 85A5 THEM column
|
|
ldy opponentComcenSpeedClass; 85A7 $92B5 = the opponent's speed class
|
|
lda comSpeedDisplayTable,y ; 85AA translate it
|
|
L_85AD:
|
|
jsr printNumberRightAligned ; 85AD print it
|
|
inc textCursorRow ; 85B0 row 18 = TER PTS
|
|
ldx #$09 ; 85B2 US column
|
|
lda playerSide ; 85B4 $0B9F = which side we are
|
|
eor #$01 ; 85B7 the OTHER side's index - note this is the opposite indexing to the RECYCLER row below, so one of the two rows is probably swapped
|
|
L_85B9:
|
|
tay ; 85B9 index the per-side table
|
|
lda terrainPointsBySide,y ; 85BA $92AC[side EOR 1]
|
|
jsr printNumberRightAligned ; 85BD print it
|
|
ldx #$0E ; 85C0 THEM column
|
|
ldy playerSide ; 85C2 our own side index
|
|
lda terrainPointsBySide,y ; 85C5 $92AC[side]
|
|
jsr printNumberRightAligned ; 85C8 print it
|
|
inc textCursorRow ; 85CB row 19 = RECYCLER
|
|
lda #$09 ; 85CD US column
|
|
jsr setTextColumn ; 85CF position the cursor there
|
|
ldy playerSide ; 85D2 our side
|
|
lda recyclerMode,y ; 85D5 $92A6[side] = FULL / HALF / NONE
|
|
jsr printOptionWord ; 85D8 print the 4-character option word
|
|
statsPrintThemRecycler:
|
|
lda #$0E ; 85DB THEM column
|
|
jsr setTextColumn ; 85DD position the cursor there
|
|
lda playerSide ; 85E0 our side ...
|
|
eor #$01 ; 85E3 ... EOR 1 = the opponent
|
|
tay ; 85E5 index the table
|
|
lda recyclerMode,y ; 85E6 $92A6[side EOR 1]
|
|
jsr printOptionWord ; 85E9 print the option word
|
|
D_85EC:
|
|
inc textCursorRow ; 85EC row 20 = the map name
|
|
lda #$09 ; 85EE column 9 for side 0 ...
|
|
ldy playerSide ; 85F0 which side are we?
|
|
beq L_85F7 ; 85F3 side 0 - use column 9
|
|
lda #$0E ; 85F5 side 1 - the name is printed under the THEM column instead
|
|
L_85F7:
|
|
jsr setTextColumn ; 85F7 position the cursor
|
|
lda #$A9 ; 85FA $0BA9 = the five-character map name, terminated at $0BAE
|
|
ldy #$0B ; 85FC high byte of the string address
|
|
jmp printString ; 85FE print it and return through the text engine
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; printNumberRightAligned - prints the unsigned byte in A as decimal, right-aligned in a
|
|
; three-character field that starts at text column X of the current text row. It works out how many
|
|
; blank glyphs have to go in front (2 for 0-9, 1 for 10-99, none for 100-255), patches that count and
|
|
; the value into three immediate operands further down ($8622, $8627, $8629), positions the text
|
|
; cursor and then prints padding + number through the resident $C000 text engine. Only the low byte is
|
|
; ever printed: the LDY #$00 at the entry wipes whatever the caller left in Y, which makes the CPY
|
|
; #$00 at $8607 and the whole $8626/$8627 patch vestiges of an earlier 16-bit version of this routine
|
|
; (see the notes on those lines).
|
|
; In: A = value 0-255, X = column relative to textWindowLeft (zp_3B). Also uses zp_40 (text row),
|
|
; zp_3D (colour), zp_3E (fill glyph, 0 = blank) and needs decimalValueHigh zp_7E to be 0 - it
|
|
; always is, because convertToDecimalDigits clears it at $C1DD after every conversion.
|
|
; Out: up to 3 cells printed and the cursor left just past the number; zp_3F = X; the three immediate
|
|
; operands at $8622/$8627/$8629 patched; A, X, Y and zp_18-zp_1F clobbered (printDecimal uses
|
|
; them).
|
|
; Called from: drawStatsTab only - $8557 (the GRUNTS/RIDERS/BOOMERS/SPIES counts, both columns),
|
|
; $8579/$8581 (DRONES), $858B/$8593 (MISSILES), $85A2/$85AD (COMSPEED), $85BD/$85C8 (TER PTS).
|
|
; XREF also lists the same call sites under game/mapGenerator6F00; that is a label-merge artifact
|
|
; - variant A has completely different code at $8557 and at $8601 (bytes 'a9 fe 8d f8 90' there).
|
|
; Number printer for the console STATS tab. Right alignment is done the cheap way: count the digits
|
|
; first, print that many blanks, then let printDecimal ($C17D) emit the digits with no leading zeros.
|
|
; The value and the pad count are carried to the print calls in self-modified immediate operands
|
|
; rather than on the stack, because updateCursorPointers ($C099) preserves only X and Y, not A.
|
|
; ----------------------------------------------------------------------
|
|
printNumberRightAligned:
|
|
ldy #$00 ; 8601 high byte of the number to print: always 0 here - the STATS fields are all 8-bit counts
|
|
stx textCursorCol ; 8603 X held the target column (relative to textWindowLeft zp_3B); make it the text cursor column zp_3F
|
|
ldx #$00 ; 8605 X now counts the blank glyphs to print in front of the number; start by assuming none
|
|
cpy #$00 ; 8607 dead test: would leave the pad count at 0 when a 16-bit caller passed a non-zero high byte in Y, but $8601 has just forced Y = 0
|
|
bne emitPaddedNumber ; 8609 branch never taken (Y is always 0) - leftover of the 16-bit version of this routine
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Pad count: how many blanks are needed so that the digits end at the right edge of the
|
|
; three-character field. X = 3 - number of decimal digits. In variant B this is just an internal
|
|
; label; the JSRs to $860B in XREF come from variant A, where a different routine (its OFF/ON option
|
|
; editor) starts on this byte.
|
|
; ----------------------------------------------------------------------
|
|
countNumberPadChars:
|
|
cmp #$64 ; 860B is the value 100 or more, i.e. does it already fill all three columns?
|
|
bcs emitPaddedNumber ; 860D 100-255: three digits, no padding at all
|
|
inx ; 860F under 100: at least one blank in front
|
|
cmp #$0A ; 8610 is the value 10 or more?
|
|
bcs emitPaddedNumber ; 8612 10-99: two digits, so the one blank counted above is enough
|
|
inx ; 8614 0-9: a single digit, so two blanks in front
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Self-modifying code. The three immediates below are written here on every call:
|
|
; $8622 (operand of the LDA at $8621) = number of blanks to print
|
|
; $8627 (operand of the LDY at $8626) = middle byte handed to printDecimal - always 0
|
|
; $8629 (operand of the LDA at $8628) = the value to print
|
|
; Nothing else in either overlay writes these three bytes.
|
|
; ----------------------------------------------------------------------
|
|
emitPaddedNumber:
|
|
stx padCountPatch+1 ; 8615 patch the blank count into the LDA #imm at $8621 that feeds printFillChars
|
|
sty valueMidPatch+1 ; 8618 patch the (always zero) middle byte into the LDY #imm at $8626 that feeds printDecimal
|
|
sta valueLowPatch+1 ; 861B patch the value itself into the LDA #imm at $8628
|
|
jsr updateCursorPointers ; 861E $C099: absolute column = zp_3F + textWindowLeft zp_3B, row = zp_40; sets the bitmap pointer zp_B4/B5 and the screen pointer zp_B6/B7. It preserves X and Y but destroys A - which is why the value had to be patched in above
|
|
padCountPatch:
|
|
lda #$FF ; 8621 operand $8622 patched at $8615: 0, 1 or 2 leading blanks
|
|
jsr bootReceiveSectorData ; 8623 printFillChars at $C0EF of the resident $C000 text engine (the listing shows the boot loader's name for the same address): prints the fill glyph zp_3E - normally 0, the blank - A times, advancing the cursor one cell each time (also textEngineC000:printFillChars)
|
|
valueMidPatch:
|
|
ldy #$FF ; 8626 operand $8627 patched at $8618: middle byte of printDecimal's 24-bit value, always 0
|
|
valueLowPatch:
|
|
lda #$FF ; 8628 operand $8629 patched at $861B: low byte = the number to print
|
|
jmp printDecimal ; 862A tail-call printDecimal $C17D (A = low, Y = middle, zp_7E = high): converts by repeated division by 10 and prints the digits without leading zeros, then returns to drawStatsTab
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; setTextColumn - moves the text cursor to column A of the current row and recomputes the bitmap and
|
|
; screen-RAM pointers for that character cell by tail-calling updateCursorPointers ($C099), which
|
|
; leaves X and Y untouched.
|
|
; In: A = column relative to textWindowLeft (zp_3B; 10 while the STATS tab is being drawn), zp_40 =
|
|
; text row.
|
|
; Out: zp_3F = A, zp_B4/B5 = bitmap address of the cell, zp_B6/B7 = screen RAM address; A clobbered, X
|
|
; and Y preserved.
|
|
; Called from: drawStatsTab only - $8536 (the US/THEM column of each unit-count row), $85CF and $85DD
|
|
; (the two RECYCLER words) and $85F7 (the MAP ID column). The game/mapGenerator6F00 callers in
|
|
; XREF are label-merge artifacts; variant A holds unrelated code at $862D.
|
|
; Two-instruction helper used wherever the STATS tab jumps to a fixed column without printing a
|
|
; number: column 9 is the US column, column 14 the THEM column (both relative to textWindowLeft 10).
|
|
; ----------------------------------------------------------------------
|
|
setTextColumn:
|
|
sta textCursorCol ; 862D new cursor column, counted from the left edge of the text window (zp_3B)
|
|
jmp updateCursorPointers ; 862F $C099 recomputes zp_B4/B5 and zp_B6/B7 for cell (zp_3B + zp_3F, zp_40) and returns straight to the caller
|
|
|
|
|
|
; ----------------------------------------------------------------------
|
|
; drawComcenSchematic - unpacks the RLE-compressed comcen picture at comcenSchematicRle ($7C6E)
|
|
; straight into the hires bitmap for the console REPAIR tab: a 20x10 character (160x80 pixel) side
|
|
; view of the tracked comcen vehicle with its dish and antenna, drawn at character row 10, column 13.
|
|
; Stream format (the same one unpackRleTo0200 $67CB uses): byte 0 is the escape marker ($13 here);
|
|
; afterwards any byte other than the marker is a literal, and 'marker, count, value' emits value count
|
|
; times, with count 0 meaning 256. Runs are allowed to cross the 160-byte row boundary, which is why
|
|
; the run value is stacked over the pointer advance at $8678/$8686.
|
|
; In: the compressed stream at $7C6E-$7FCD. No registers.
|
|
; Out: 10 x 160 bytes written into the bitmap from $A000 + 10*320 + 13*8 = $ACE8 with a 320-byte row
|
|
; stride; rleMarkerByte $9246 = $13, rleUnpackFlag $9247 = 0, fetchRleByte's self-modified operand
|
|
; $681B/$681C left just past the end of the stream; zp_1A (row counter), zp_1B (run count),
|
|
; zp_B4/B5 (bitmap pointer) and zp_B6/B7 (set but unused) clobbered.
|
|
; Called from: drawRepairTab ($176B) in the main program, its only caller. Since the routine lives in
|
|
; overlay B, the console REPAIR tab implicitly requires variant B to be resident.
|
|
; Set up the decoder: escape marker, source pointer and destination cell. Note that this routine does
|
|
; not use the general unpacker at $67CB - that one always writes 512 bytes to $0200 - but it does
|
|
; share its byte fetcher (fetchRleByte $681A) and its marker byte ($9246).
|
|
; ----------------------------------------------------------------------
|
|
drawComcenSchematic:
|
|
ldy #$00 ; 8632 index 0 of the stream, and the zero about to be stored in $9247
|
|
sty rleUnpackFlag ; 8634 $9247 = 0, exactly as unpackRleTo0200 does at $67D3 - but no code in any unit ever reads this byte, so it is a dead leftover
|
|
lda comcenSchematicRle,y ; 8637 byte 0 of the compressed stream is the escape marker ($13 for this picture)
|
|
sta rleMarkerByte ; 863A keep the marker in $9246 for the compare at $865E (shared with the main program's unpacker)
|
|
ldy #$7C ; 863D high byte of the source address...
|
|
sty D_681C ; 863F ...poked into the high half of fetchRleByte's self-modified 'lda $XXXX' operand at $681B/$681C
|
|
lda #$6F ; 8642 low byte: decoding starts at $7C6F, one past the marker byte
|
|
sta D_681B ; 8644 complete the patched source pointer = $7C6F
|
|
ldx #$0D ; 8647 left edge of the picture: character column 13, just right of the 22-column map panel
|
|
ldy #$0A ; 8649 top edge: character row 10, under the REPAIR tab heading
|
|
jsr calcCellPointersXY ; 864B $2AA3: zp_B4/B5 = $A000 + row*320 + col*8 (bitmap cell), zp_B6/B7 = $8C00 + row*40 + col (screen RAM cell, not used here)
|
|
lda #$0A ; 864E the picture is 10 character rows tall
|
|
sta scratch1A ; 8650 zp_1A counts the rows still to emit
|
|
ldy #$00 ; 8652 Y = byte offset inside the current character row, 0..159
|
|
|
|
; ----------------------------------------------------------------------
|
|
; Decoder loop. X is the number of copies of A still to store: it is decremented on entry to each
|
|
; pass, and only when it reaches 0 is a new item pulled from the stream. Y walks the 160 bytes of one
|
|
; picture row (20 cells x 8 scanlines, stored cell by cell), zp_1A counts the rows. primeRleRunCounter
|
|
; is entered once, by falling through from $8652; the per-row restart at $8689 jumps back to
|
|
; rleEmitLoop with X and A intact. It is a branch target only in variant A.
|
|
; ----------------------------------------------------------------------
|
|
primeRleRunCounter:
|
|
ldx #$01 ; 8654 prime the run counter with 1 so that the very first pass fetches from the stream
|
|
rleEmitLoop:
|
|
dex ; 8656 one byte of the current run consumed
|
|
bne rleStoreByte ; 8657 run not exhausted: store the same value again without touching the stream
|
|
jsr fetchRleByte ; 8659 $681A: fetch the next stream byte into A (and X) and bump the source pointer
|
|
ldx #$01 ; 865C assume a literal byte: exactly one store
|
|
cmp rleMarkerByte ; 865E is this byte the escape marker that introduces a run?
|
|
bne rleStoreByte ; 8661 not the marker: plain literal, store it once
|
|
jsr fetchRleByte ; 8663 marker seen - the next byte is the repeat count (0 counts as 256)
|
|
rleSaveRunCount:
|
|
sta scratch1B ; 8666 park the repeat count while the value byte is fetched (this label is a branch target only in variant A)
|
|
jsr fetchRleByte ; 8668 third byte of the run: the value to repeat
|
|
ldx scratch1B ; 866B X = repeat count; this pass stores the value once and X-1 further passes repeat it
|
|
rleStoreByte:
|
|
sta (bitmapPtr),y ; 866D write one bitmap byte; eight consecutive bytes are the eight scanlines of one character cell
|
|
iny ; 866F next byte of the picture row
|
|
cpy #$A0 ; 8670 160 bytes = 20 character cells x 8 scanlines = one full picture row
|
|
bcc rleEmitLoop ; 8672 row not finished yet, keep decoding
|
|
|
|
; ----------------------------------------------------------------------
|
|
; End of a picture row: step the bitmap pointer down one character row. A (the current run value) and
|
|
; X (the remaining repeat count) are carried across untouched, so a run may span rows.
|
|
; ----------------------------------------------------------------------
|
|
dec scratch1A ; 8674 one of the 10 picture rows done
|
|
beq schematicDone ; 8676 all 10 rows drawn - the picture is complete
|
|
pha ; 8678 save the current run value across the pointer arithmetic
|
|
clc ; 8679 clear carry for the 16-bit add below
|
|
lda bitmapPtr ; 867A advance the bitmap pointer...
|
|
adc #$40 ; 867C ...by $0140 = 320 bytes = one character row of the 40-column bitmap ($40 into the low byte...
|
|
sta bitmapPtr ; 867E ...store the new low byte
|
|
lda bitmapPtrHi ; 8680 ...carry into the high byte...
|
|
adc #$01 ; 8682 ...plus 1, completing the +$140
|
|
sta bitmapPtrHi ; 8684 zp_B4/B5 now points at the leftmost cell of the next picture row (still column 13)
|
|
pla ; 8686 restore the run value so a run that crossed the row boundary keeps storing the same byte
|
|
ldy #$00 ; 8687 back to the left edge of the picture row
|
|
beq rleEmitLoop ; 8689 always taken (Y was just loaded with 0): continue decoding into the new row
|
|
schematicDone:
|
|
rts ; 868B return to drawRepairTab, which then paints the colour cells over the picture
|
|
|
|
|
|
; ================================================================== From here to the end of the
|
|
; overlay ($868C-$87FE) variant B contains no code at all: five user- interface captions, the 79-byte
|
|
; STATS tab label blob, a 12-byte word table for the film status line and 16 bit-7-terminated message
|
|
; strings, then six padding zeroes and the variant flag. Every routine-looking label inside this area
|
|
; belongs to variant A, which keeps its custom- scenario dialog code at $868C-$879E - XREF merges both
|
|
; variants into one namespace, so names such as setDestroyModeMessage ($86AA),
|
|
; showCustomSummaryMessage ($86C3), setPersistentMessage ($8722), appendOnOffWord ($872E),
|
|
; showScenarioBannerAndWaitForButton ($8756) and printCentredTableEntry ($8795), as well as the D_/L_
|
|
; labels sprinkled through the strings, mean nothing here and only mark where variant A branches or
|
|
; stores. The only two interior addresses that matter with variant B resident are $8726 and $8730,
|
|
; both patched by the film replay key handler at $40B2. A byte compare of extracted/runtimeImage_A.bin
|
|
; and _B.bin shows $868C-$879E differing,
|
|
; $879F-$87FE identical in both variants and $87FF differing ($00 in A, $FF in B).
|
|
; ==================================================================
|
|
;
|
|
; strDrone - 'DRONE', bit 7 on the final E. Caption of the drones-left box on the DRONE screen,
|
|
; printed by overlay B code at $7018-$7033 with textColour $CF at column 32, row 7.
|
|
strDrone:
|
|
.byte "D" ; 868C 'D' of DRONE
|
|
D_868D:
|
|
.byte $52 ; 868D 'R' - label from variant A, which stores here at $8213/$86AC
|
|
L_868E:
|
|
.byte $4F ; 868E 'ONE' with bit 7 on the E as the string terminator
|
|
.byte $4E,$C5 ; 868F N.
|
|
|
|
; strLaunch - 'LAUNCH', caption under the 2x2 LAUNCH button icon of the DRONE screen; printed at
|
|
; $70B6-$70C5 at column 32, row 14.
|
|
strLaunch:
|
|
.byte "LAUNC",'H'|$80 ; 8691 6 bytes, terminated by 'H' | $80
|
|
|
|
; strStrike - 'STRIKE', caption under the 2x2 STRIKE button icon of the DRONE screen; printed by the
|
|
; code at $70D9-$70EF with textColour $C7 at column 32, row 18.
|
|
strStrike:
|
|
.byte "STR" ; 8697 'STR'
|
|
D_869A:
|
|
.byte $49 ; 869A 'I' - variant A writes here ($8221/$86BA); no meaning in variant B
|
|
D_869B:
|
|
.byte $4B ; 869B 'KE' with bit 7 on the E
|
|
.byte $C5 ; 869C .
|
|
|
|
; strFire - 'FIRE', caption of the missile FIRE button on the main game screen; printed at
|
|
; $7B2E-$7B3D at column 31, row 12.
|
|
strFire:
|
|
.byte "FIR",'E'|$80 ; 869D 4 bytes, terminated by 'E' | $80
|
|
|
|
; strDroneAlertTwoLine - 'DRONE' + CR + 'ALERT': the two-line label of the top-right DRONE ALERT
|
|
; panel, printed at $7B67-$7B7A with textWindowLeft 31, column 0, row 3. The $0D is the text engine's
|
|
; newline, so the two words land on consecutive rows. What flashes is the panel colour
|
|
; (droneAlertColour $7B43, $02 or $20), not this text.
|
|
strDroneAlertTwoLine:
|
|
.byte "DRONE",$0D,"ALE" ; 86A1 'DRONE', carriage return, 'ALE'
|
|
strDroneAlertEnd:
|
|
.byte $52 ; 86AA 'RT' with bit 7 on the T (terminator)
|
|
.byte $D4 ; 86AB .
|
|
|
|
; statsTabLabels - the entire left-hand label column of the console STATS tab as ONE bit-7- terminated
|
|
; string with embedded carriage returns ($0D), printed by a single printString call at
|
|
; $161D-$1621 in the main program (textWindowLeft 10, column 3, right after ownerNameString $0B7F).
|
|
; Each $0D drops one row, so the leading $0D lands the first label on row 9:
|
|
; row 9 GRUNTS, 10 RIDERS, 11 BOOMERS, 12 SPIES, (13 blank), 14 DRONES, 15 MISSILES,
|
|
; (16 blank), 17 COMSPEED, 18 TER PTS, 19 RECYCLER, 20 MAP ID.
|
|
; The matching numbers are printed separately by drawStatsTab ($8518) into column 9 (US) and column 14
|
|
; (THEM). 79 bytes, $86AC-$86FA.
|
|
statsTabLabels:
|
|
.byte $0D,"GRUNTS",$0D,"RIDERS",$0D,"BOOMERS",$0D; 86AC CR, 'GRUNTS', CR, 'RIDERS', CR, 'BOOMERS', CR
|
|
statsLabelsSpies:
|
|
.byte $53 ; 86C3 'SP' - split here only because variant A has a routine entry on this byte
|
|
.byte $50 ; 86C4 P
|
|
L_86C5:
|
|
.byte $49 ; 86C5 'IES' CR CR 'DRO' - end of the SPIES row, a blank row, then DRONES
|
|
.byte $45,$53,$0D,$0D,$44,$52,$4F; 86C6 ES..DRO
|
|
.byte $4E ; 86CD 'NES' CR 'MISS'
|
|
.byte $45,$53,$0D,$4D,$49,$53,$53; 86CE ES.MISS
|
|
.byte $49 ; 86D5 'ILES' CR CR 'CO' - end of MISSILES, a blank row, then COMSPEED
|
|
.byte $4C,$45,$53,$0D,$0D,$43,$4F; 86D6 LES..CO
|
|
.byte $4D ; 86DD 'MS'
|
|
.byte $53 ; 86DE S
|
|
L_86DF:
|
|
.byte $50 ; 86DF 'PEED' CR 'TER'
|
|
.byte $45,$45,$44,$0D,$54,$45,$52; 86E0 EED.TER
|
|
.byte $20 ; 86E7 ' PTS' CR 'REC'
|
|
.byte $50,$54,$53,$0D,$52,$45,$43; 86E8 PTS.REC
|
|
.byte $59 ; 86EF 'YCLE'
|
|
.byte $43,$4C,$45 ; 86F0 CLE
|
|
L_86F3:
|
|
.byte $52 ; 86F3 'R' CR 'MAP I' + 'D' | $80: last label, bit 7 ends the whole blob
|
|
.byte $0D,$4D,$41,$50,$20,$49,$C4; 86F4 .MAP I.
|
|
|
|
; filmViewWordTable - three fixed 4-byte words used to complete the 'SHOW ****' status line during
|
|
; game-film playback: 'US' (padded with two zeroes), 'THEM', 'BOTH'. The film key handler indexes it
|
|
; with (playModeFlags $0B9B & 3) * 4 at $4116-$4120 and copies four bytes over $8726-$8729 ($4123)
|
|
; before queueing message 6. Each word carries its own bit-7 terminator, so the two padding zeroes of
|
|
; the 'US' entry are copied but never printed. Only indices 0-2 occur: the CLR/ HOME handler wraps
|
|
; playModeFlags from $83 back to $80 at $4110-$4116.
|
|
filmViewWordTable:
|
|
.byte $55,$D3,$00,$00 ; 86FB U... entry 0 'US' | $80 plus two pad bytes, then entry 1 'THEM' | $80
|
|
.byte $54,$48,$45,$CD ; 86FF THE.
|
|
.byte $42 ; 8703 entry 2 'BOTH' with bit 7 on the H
|
|
.byte $4F,$54,$C8 ; 8704 OT.
|
|
|
|
; Message strings. From here on every block is one entry of the message table at $0500 (low bytes) /
|
|
; $0524 (high bytes), with the sound played for it in the parallel table at $0612 ($80 = silent).
|
|
; Messages are queued by id through queueMessage ($C566) or queueMessageUnlessFilm ($C560), the latter
|
|
; dropping the message during solo-trainer or film playback.
|
|
;
|
|
; msgEndOfFilm - 'END OF FILM.', message id 2, sound 3; queued at $59FC after setFilmPtrToEnd when a
|
|
; recorded game film has been replayed to its last frame.
|
|
msgEndOfFilm:
|
|
.byte "END OF FILM" ; 8707 'END OF FILM'
|
|
D_8712:
|
|
.byte $AE ; 8712 '.' with bit 7 set = terminator
|
|
|
|
; msgUnitRecycled - 'UNIT RECYCLED.', message id 5, sound 4; queued with queueMessageUnlessFilm at
|
|
; $3EA3 after initUnitRecord, but only when the recycled unit's owner (zp_1A) is the local player.
|
|
msgUnitRecycled:
|
|
.byte "U" ; 8713 'U'
|
|
L_8714:
|
|
.byte $4E ; 8714 'NIT RECY'
|
|
.byte $49,$54,$20,$52,$45,$43,$59; 8715 IT RECY
|
|
.byte $43 ; 871C 'CLED' + '.' | $80
|
|
.byte $4C,$45,$44,$AE ; 871D LED.
|
|
|
|
; msgShowViewTemplate - 'SHOW ****', message id 6, sound 3: the film playback status line. The four
|
|
; '*' at $8726-$8729 are overwritten with a filmViewWordTable entry (US / THEM / BOTH) at
|
|
; $4123 before the message is queued at $412E, and that entry supplies the bit-7 terminator.
|
|
; As stored on disk the template has NO terminator, so printing it unpatched would run straight on
|
|
; through 'SPEED *' and only stop at the '!' of 'SPOTTED RECYCLER!' ($8741).
|
|
msgShowViewTemplate:
|
|
.byte "S" ; 8721 'S'
|
|
msgShowViewBody:
|
|
.byte $48 ; 8722 'HOW ' - the space before the patched word
|
|
.byte $4F,$57,$20 ; 8723 OW
|
|
msgShowViewWord:
|
|
.byte $2A ; 8726 four-byte patch field: receives 'US'+$80+2 pad / 'THEM'|$80 / 'BOTH'|$80 from filmViewWordTable, written by the film key handler at $4123
|
|
.byte $2A,$2A,$2A ; 8727 ***
|
|
|
|
; msgSpeedTemplate - 'SPEED *', message id 7, sound 3: shown when a digit key changes the film replay
|
|
; speed. Pressing '1'-'9' sets stepIntervalFrames $92E3 = 8 - (key - $B1) and stores the raw key code
|
|
; $B1-$B9 into $8730 at $40DE; because those codes already have bit 7 set they double as the string
|
|
; terminator. Unterminated as stored on disk, like the SHOW template above.
|
|
msgSpeedTemplate:
|
|
.byte "SPEE" ; 872A 'SPEE'
|
|
msgSpeedBody:
|
|
.byte $44 ; 872E 'D' and a space
|
|
.byte $20 ; 872F
|
|
msgSpeedDigit:
|
|
.byte $2A ; 8730 one-byte patch field: the pressed digit's key code $B1-$B9 (screen code with bit 7 = terminator), written at $40DE
|
|
|
|
; msgSpottedRecycler - 'SPOTTED RECYCLER!', message id 8, sound 4; queued with queueMessageUnlessFilm
|
|
; at $3F8C right after drawRecyclerOnMap / drawMapCell, i.e. the moment the recycler cell first
|
|
; becomes visible to the local player.
|
|
msgSpottedRecycler:
|
|
.byte "SPO" ; 8731 'SPO'
|
|
L_8734:
|
|
.byte $54 ; 8734 'TTED REC'
|
|
.byte $54,$45,$44,$20,$52,$45,$43; 8735 TED REC
|
|
.byte $59 ; 873C 'YCLE'
|
|
.byte $43,$4C,$45 ; 873D CLE
|
|
L_8740:
|
|
.byte $52 ; 8740 'R' + '!' | $80
|
|
.byte $A1 ; 8741 .
|
|
|
|
; msgComcenStunned - 'COMCEN STUNNED!', message id $0C, sound 4; queued at $0741-$0743 by the
|
|
; game-tick code in the $0400 block when $9162 reads $FF. The same text is also printed straight into
|
|
; the console status area at column 5 by $17C4-$17D4 for as long as comcenStunnedFlag ($922A) bit 7 is
|
|
; set.
|
|
msgComcenStunned:
|
|
.byte "COMCEN STUNNED",'!'|$80; 8742 15 bytes, terminated by '!' | $80
|
|
|
|
; msgSpottedEnemyComcen - 'SPOTTED ENEMY COMCEN!', message id $0D, sound 4; queued with
|
|
; queueMessageUnlessFilm at $36EF when a newly visible enemy unit's type field ($F76C,y & 7) is 4
|
|
; (COMCEN). Types 0-2 (GRUNT, RIDER, BOOMER) are spotted silently.
|
|
msgSpottedEnemyComcen:
|
|
.byte "SPOT" ; 8751 'SPOT'
|
|
L_8755:
|
|
.byte $54 ; 8755 'T'
|
|
msgSpottedComcenBody:
|
|
.byte $45 ; 8756 'ED ENEMY'
|
|
.byte $44,$20,$45,$4E,$45,$4D,$59; 8757 D ENEMY
|
|
.byte $20 ; 875E ' COMCEN'
|
|
.byte $43,$4F,$4D,$43,$45,$4E ; 875F COMCEN
|
|
L_8765:
|
|
.byte $A1 ; 8765 '!' | $80 terminator
|
|
|
|
; msgSpyUnderAttack - 'SPY UNDER ATTACK!', message id $0E, sound 4; queued with queueMessageUnlessFilm
|
|
; at $3B3B when the unit being hit is type 3 (SPY) and its energy field ($F9C4,y & $3F) is still $32 =
|
|
; 50, i.e. full - so it fires once, on the first hit only.
|
|
msgSpyUnderAttack:
|
|
.byte "SPY UNDER ATTACK",'!'|$80; 8766 17 bytes, terminated by '!' | $80
|
|
|
|
; msgSpottedEnemySpy - 'SPOTTED ENEMY SPY!', message id $0F, sound 4; queued with
|
|
; queueMessageUnlessFilm at $36E7, the sibling of the COMCEN case above in the visibility code.
|
|
msgSpottedEnemySpy:
|
|
.byte "SPOTTED ENEMY SPY",'!'|$80; 8777 18 bytes, terminated by '!' | $80
|
|
|
|
; The five end-of-game result messages. computeGameResult ($28F1) picks one of them into
|
|
; persistentMessageId and queues it at $29A0.
|
|
;
|
|
; msgWeLostOnPoints - 'WE LOST ON POINTS!', message id $11, sound $0E: the time-out result when the
|
|
; LOSING side ($2963-$296B: X, the side whose accumulated damage score is higher) is the local player.
|
|
; If it is the other side the code does INY and picks $12 instead. Variant A stores 22 bytes of its
|
|
; own code over $8789-$879E, so this string is only intact while variant B is resident - which is
|
|
; fine, because results are only shown during the battle.
|
|
msgWeLostOnPoints:
|
|
.byte "W" ; 8789 'W'
|
|
L_878A:
|
|
.byte $45 ; 878A 'E LOST O'
|
|
.byte $20,$4C,$4F,$53,$54,$20,$4F; 878B LOST O
|
|
.byte $4E ; 8792 'N P'
|
|
.byte $20,$50 ; 8793 P
|
|
msgWeLostOnPointsEnd:
|
|
.byte $4F ; 8795 'OINTS' + '!' | $80
|
|
.byte $49,$4E,$54,$53,$A1 ; 8796 INTS.
|
|
|
|
; msgWeWonOnPoints - 'WE WON ON POINTS!', message id $12, sound $0D: the time-out win (losing side !=
|
|
; local player). Its first four bytes 'WE W' ($879B-$879E) are the last of the bytes variant A
|
|
; overlays; from $879F on the two overlay variants are byte-identical to the end of the block.
|
|
msgWeWonOnPoints:
|
|
.byte "WE W" ; 879B 'WE W' - the last four bytes that variant A clobbers
|
|
.byte "ON ON POINTS",'!'|$80 ; 879F 'ON ON POINTS' + '!' | $80; identical in both overlay variants
|
|
|
|
; msgItWasADraw - 'IT WAS A DRAW.', message id $13 and the only result message with no sound ($80 in
|
|
; the sound table). Chosen when the 16-bit score difference is exactly zero at time-out ($2917-$2929)
|
|
; or when gameOverFlags bits 5-6 are both set, i.e. both comcens were knocked out ($297A); both sides
|
|
; are then given 250 points.
|
|
msgItWasADraw:
|
|
.byte "IT WAS A DRAW",'.'|$80 ; 87AC 14 bytes, terminated by '.' | $80
|
|
|
|
; msgWeWereKnockedOut - 'WE WERE KNOCKED OUT!', message id $14, sound $0E; selected at $298D-$2995
|
|
; when the knocked-out side equals the local player. The loser is given 500 points, the winner 150.
|
|
msgWeWereKnockedOut:
|
|
.byte "WE WERE KNOCKED OUT",'!'|$80; 87BA 20 bytes, terminated by '!' | $80
|
|
|
|
; msgWeWonByAKnockout - 'WE WON BY A KNOCKOUT!', message id $15, sound $0D: the enemy comcen was
|
|
; destroyed (the INY sibling of message $14). The solo trainer's stores to 'msgWeWonByAKnockout,y' at
|
|
; $EB29/$EB53 do NOT corrupt this string: there Y is an absolute unit index of the AI side, 50-99, so
|
|
; the writes land at $8800-$8831 - the trainer's per-unit order-timestamp table, which it addresses
|
|
; through the base $87CE.
|
|
msgWeWonByAKnockout:
|
|
.byte "WE WON BY A KNOCKOUT",'!'|$80; 87CE 21 bytes, terminated by '!' | $80
|
|
|
|
; msgGameOver - 'GAME OVER.', message id $16, sound $19; queued first at $2901-$2903 on the time-out
|
|
; path, ahead of the points result message.
|
|
msgGameOver:
|
|
.byte "GAME OVER",'.'|$80 ; 87E3 10 bytes, terminated by '.' | $80
|
|
|
|
; msgDroneAlert - 'DRONE ALERT!'. This one has no fixed id: overlay A installs its address into
|
|
; run-time message slot $20 at $7BA6-$7BAC (ldx #$20 / lda #$ED / ldy #$87 / jsr setMessageSlot
|
|
; $C924) just before jumping into the opponent module's battle-start entry. The pointer survives
|
|
; the swap to overlay B because this string sits in the tail that both variants hold identically.
|
|
; During the battle showDroneAlert ($820F) plays sound 7 and queues id $20 whenever the opponent
|
|
; launches a drone, unless playModeFlags $0B9B bit 7 (solo trainer / film playback) is set.
|
|
msgDroneAlert:
|
|
.byte "DRONE ALERT",'!'|$80 ; 87ED 12 bytes, terminated by '!' | $80
|
|
|
|
; overlayTailPadding - six unused $00 bytes between the last string and the variant flag. Nothing
|
|
; reads them; they are identical in both overlay variants and exist only to push the flag to the very
|
|
; last byte of the block.
|
|
overlayTailPadding:
|
|
.byte $00,$00,$00,$00,$00,$00 ; 87F9 ...... filler up to $87FE
|
|
|
|
; overlayVariantFlag - identity byte of whichever $6F00-$87FF overlay is currently resident. The
|
|
; overlay loader tests bit 7 at $1036 and $1080 to decide whether the variant it wants is already in
|
|
; memory or has to be pulled from disk (variant A = track 31 s0-16 + track 32 s0-7, variant B = track
|
|
; 33 s0-16 + track 32 s8-15).
|
|
overlayVariantFlag:
|
|
.byte $FF ; 87FF . $FF = variant B (comcen missile and drone screens) is resident; variant A (map generator and setup) stores $00 here
|