; zeropage.inc - zero-page variables used by Modem Wars (auto-generated from annotations)

cpuPortDdr               := $00    ; 6510 data direction register (listings show CPU_DDR); the film code drives it together with $01
cpuPortReg               := $01    ; 6510 port (CPU_PORT): $35 = I/O visible, no ROMs; briefly $34 while the film buffer under $D000 is read/written
zp_03                    := $03
oldLoadDestLo            := $04    ; destination pointer (lo) of an earlier build of the fast loader; only touched by the unreachable leftoverCheckDiskId at $C3C4
oldLoadDestHi            := $05    ; high byte of oldLoadDestLo (dead leftover)
zp_06                    := $06
zp_09                    := $09
zp_0A                    := $0A
zp_0B                    := $0B
zp_0C                    := $0C
zp_0D                    := $0D
zp_0E                    := $0E
zp_0F                    := $0F
zp_10                    := $10
zp_11                    := $11
zp_13                    := $13
zp_15                    := $15
zp_16                    := $16
scratch18                := $18    ; general scratch, used by every unit: multiply/divide operand ($FCCD/$FD0A), character row in the text plotters, the film header checksum accumulated by $5900, the overview character row in drawOverviewMap, loop counters and sums
scratch19                := $19    ; general scratch, partner of scratch18: product/quotient, character column in the text plotters, the page counter of the film unit snapshot ($5967), the overview character column in drawOverviewMap
scratch1A                := $1A    ; general scratch: third math temp, right column of a frame box, low byte of the trainer's 16-bit scores; in finishRound the stack pointer of the pending-unit stack at $0464
scratch1B                := $1B    ; general scratch: bottom row of a frame box, division remainder (lo), max column returned by computeGroupBounds; in finishRound the cursor into unitBaseOrder, in processUnitStep the direction of the best fire target
scratch1C                := $1C    ; general scratch: frame tile code, division remainder (mid), member count / max row of a group; also the running best score of the trainer's 2x2 bombardment search
scratch1D                := $1D    ; general scratch: bounds mode flag of computeGroupBounds (bit 7 = use destinations), division remainder (hi); the trainer's bombardment search keeps the chosen target unit here
scratch1E                := $1E    ; general scratch: running terrain height in the line-of-sight walk; computeGroupCentroid leaves the group's average ordered destination column here
scratch1F                := $1F    ; general scratch: line-of-sight rise flag, divide bit counter; computeGroupCentroid leaves the group's average ordered destination row here
scratch20                := $20    ; general scratch: half width in magnifyWindow2x, the pass flag of scanVisibleEnemies (bit 7 set = attack-target pass, clear = spotting pass), unit type being counted on the STATS tab
scratch21                := $21    ; general scratch: half height in magnifyWindow2x, index of the unit doing the scan in scanVisibleEnemies, terrain neighbour mask in the map generator
cellColA                 := $22    ; map column of point A for distanceSquared/cellDistance; the trainer also uses it as the group's wanted target column, as the direction-rank index in findTrainerAdvanceCell and as the unit loop counter in issueTrainerGroupOrders
scratch23                := $23    ; general scratch: first (clipped) column of the area scan square; distance/direction accumulator in the trainer's move search, and the trainer's reluctance-to-fire score in runAiMissileTurn ($ED46)
cellRowA                 := $24    ; map row of point A for distanceSquared/cellDistance; the trainer's wanted target row and, while computeTrainerGroupStats runs, the group's rightmost ordered destination column
scanColCount             := $25    ; columns left to scan on the current row of an area scan; computeTrainerGroupStats reuses it as the group's bottom-most ordered destination row
scanRowCount             := $26    ; rows left to scan in an area scan; the trainer keeps the direction chosen by findTrainerAdvanceCell here
cellColB                 := $27    ; map column of point B (the reference point) for distanceSquared/cellDistance; the trainer's search origin column
cellRowB                 := $28    ; map row of point B (the reference point); the trainer's search origin row
scanListIndex            := $29    ; index of the last entry written to the area-scan result lists at $0400/$0432/$0464 ($FF = empty); the trainer's missile code parks the pending reload time here ($ED49)
scratch2A                := $2A    ; scratch: hill-site index in the map generator; in the trainer the group's column movement delta, and the candidate distance inside the direction search
scratch2B                := $2B    ; general scratch: lane or cluster-seed counter in the map generator, group destination row delta in the trainer, and the 'AI comcen is stunned' flag of runAiMissileTurn ($ED31, $FF = stunned)
zp_2C                    := $2C
contourScanRow           := $2D    ; map generator: row of the cell being examined by applyContourTiles
contourScanCol           := $2E    ; map generator: column of the cell being examined by applyContourTiles
scanResultCount          := $2F    ; index of the last entry of the area-scan result lists (count-1, $FF = none); drawViewport reuses it as the counter of the 5 cell rows of the tactical view
magnifySrcRow            := $30    ; source row counter of magnifyWindow2x
magnifySrcCol            := $31    ; source column counter of magnifyWindow2x; drawViewport reuses it as the counter of the 7 cells across the tactical view and as the offset of the cell inside the map row
tileColour               := $32    ; colour byte of the map tile being rendered: first the terrain colour written into both nybbles of the screen RAM cells, then the unit colour (blue/red/white) written into colour RAM
spriteFlashPhase         := $33    ; frame counter rotated right twice inside the raster IRQ so bit 7 mirrors bit 0 of frameCounter; gates the two-colour sprite flashing
zp_34                    := $34    ; not a real variable: the only reference is the stray byte pair $CE,$34 at the very end of the text engine ($CFFE)
zp_35                    := $35
zp_36                    := $36
zp_37                    := $37
irqInProgress            := $38    ; re-entrancy guard of rasterIrqHandler (non-zero while the handler runs with interrupts enabled)
textWindowTop            := $39    ; text engine: first screen row of the text window
textWindowBottom         := $3A    ; text engine: row just below the text window; the window clears fill while the cursor row is less than this
textWindowLeft           := $3B    ; text engine: left screen column of the text window; added to textCursorCol to get the absolute column
textWindowWidth          := $3C    ; text engine: window width in characters (wrap point) and the default centring width; also the width of a menu highlight bar
textColour               := $3D    ; text engine: colour byte written for each printed character cell, and the EOR mask used for the menu highlight
textFillGlyph            := $3E    ; text engine: glyph number used as the blank/fill character by the clear, pad and backspace routines (glyph 0 = blank)
textCursorCol            := $3F    ; text engine: cursor column relative to textWindowLeft; magnifyWindow2x borrows it as the destination left column of the 2x zoom
textCursorRow            := $40    ; text engine: absolute cursor row (0-24); magnifyWindow2x borrows it as the destination top row of the 2x zoom
screenBuiltFlag          := $41    ; set to 1 by every screen builder and never read anywhere; a spare slot of the 12-byte text state saved to savedTextWindow
textPadColour            := $42    ; text engine: colour used for the blanks that pad a centred string
rawGlyphMode             := $43    ; text engine: bit 7 set = printChar takes A as a glyph number, skipping the ASCII translation and the control codes
doubleSizeText           := $44    ; text engine: bit 7 set = characters are drawn 2x2 cells and the cursor advances two columns/rows
frameCounter             := $45    ; low byte of the free-running 24-bit frame counter incremented by the raster IRQ
frameCounterMid          := $46    ; middle byte of frameCounter
frameCounterHi           := $47    ; high byte of frameCounter
mapCellPtr               := $48    ; pointer (lo) to a cell of the $F000 battlefield map, set by readMapCell ($25FA); also the running pointer of the block fill/copy loops, and in the trainer setup overlay the pointer to the 50 group ids of the selected opening play inside the playbook at $0200
mapCellPtrHi             := $49    ; high byte of mapCellPtr
fillEndPtr               := $4A    ; end address (lo) compared by advanceFillPointer ($C3F9); reused as the film-directory entry pointer, the trainer's plan column pointer and the 99..0 unit loop counter of moveGroupKeepingFormation ($4A52)
fillEndPtrHi             := $4B    ; high byte of fillEndPtr
planRowPtr               := $4C    ; trainer setup: pointer (lo) to the 50 destination rows of the selected opening play inside the loaded playbook
planRowPtrHi             := $4D    ; high byte of planRowPtr
fillCounter              := $4E    ; 16-bit counter incremented as a side effect by advanceFillPointer ($C3F9); nothing reads it
fillCounterHi            := $4F    ; high byte of fillCounter
zp_50                    := $50
zp_51                    := $51
savedRegA                := $54    ; A save slot of saveRegsAXY/restoreRegsAXY ($C0E8/$C030), so printChar can preserve the caller's registers
savedRegX                := $55    ; X save slot of saveRegsAXY/restoreRegsAXY; the map drawing code uses it as the cell column
savedRegY                := $56    ; Y save slot of saveRegsAXY/restoreRegsAXY; the map drawing code uses it as the cell row
gameRngState0            := $57    ; byte 0 of the 24-bit game RNG stepped by nextGameRandom ($FD5D); seeded with $49 at $030B so both machines match
gameRngState1            := $58    ; byte 1 of the game RNG state (seeded $19)
gameRngState2            := $59    ; byte 2 of the game RNG state (seeded $02)
scenarioSeed0            := $5A    ; byte 0 of the 24-bit scenario/map seed stepped by nextScenarioRandom ($FD4A); also the first answer byte of the manual look-up check, and the value the sound driver adds to a voice frequency for the bit-7 'jitter' script command ($66BF)
scenarioSeed1            := $5B    ; byte 1 of the scenario seed; its bit 7 gives the sign in addRandomJitter; second manual answer byte
scenarioSeed2            := $5C    ; byte 2 of the scenario seed; third manual answer byte
joyDirection             := $5D    ; joystick 2 direction nibble latched by the IRQ, active low ($0F = centred); indexes joystickDirectionTable
droneJoyDirection        := $5E    ; direction code 0-7 of the last joystick steer on the drone screen; steerDrone maps it to a clockwise heading through joyDirToHeadingTable ($7FCE)
joyFireUp                := $5F    ; joystick 2 fire state latched by the IRQ: 0 = button down, 1 = released
joyDirectionCopy         := $60    ; second copy of joyDirection written by the IRQ at $1216; no reader anywhere (dead store)
joyFireUpCopy            := $61    ; second copy of joyFireUp written by the IRQ at $1210; no reader anywhere (dead store)
zp_63                    := $63
zp_64                    := $64
requestedScreenKey       := $66    ; last key delivered by pollKeyboardEvent: $FF = none, 0-3 = F1/F3/F5/F7 = the screen the player asked for (the only values switchToRequestedScreen acts on), any other value = the PETSCII|$80 code of the key just typed, which reads as 'no screen change wanted' and so cancels a stale F-key request
gridCursorCol            := $67    ; missile screen: column 0-20 of the target cursor in the 21x17 radar grid (comcen at 10)
gridCursorRow            := $68    ; missile screen: row 0-16 of the target cursor (comcen at 8)
mergeFillIndex           := $69    ; number of bytes already placed in mergedCmdBuffer while the own and the received packet are merged into one turn; also the index at which the second player's block starts
packetReadIndex          := $6A    ; read offset into the packet currently being merged; advancePacketCursor ($4F5E) sets it to the offset of the next command
targetDirection          := $6B    ; direction 0-3 of the unit's chosen target, copied into the facing bits of unitFlagsTable; processUnitStep pre-loads it at $439A with the unit's current facing so the sight and attack scans prefer that quadrant; the map generator reuses it as the blob's tried-direction mask
joyRepeatTimer           := $6C    ; first of the six IRQ frame countdowns $6C-$71: joystick auto-repeat delay, also used as the 4-frame step delay of the animated mini-map path marker ($2506)
fireDebounceTimer        := $6D    ; frame countdown after an accepted fire press; clicks are ignored while it is non-zero
menuBlinkTimer           := $6E    ; frame countdown between menu-highlight toggles and console-tab blinks; scrollViewToUnit reuses it as the delay between animated scroll steps
markerBlinkTimer         := $6F    ; frame countdown for the repair-marker and missile-button blink; printUnitStatusLines also loads it with 20 to hold FIRED in the WEAPON field of the info panel ($2424)
keyRepeatTimer           := $70    ; frame countdown before a held key produces another key event
frameTimer71             := $71    ; sixth of the IRQ frame countdowns $6C-$71; nothing ever loads it (spare slot)
messageTimer             := $72    ; frames the current status-line message stays up, decremented every 4th frame by updateFrameTimers; 0 = ready for the next message. handleMessageReviewKeys also uses it as the minimum airtime of a scrolled-back message (it only steps on once the timer has fallen below $23)
cmdParam0                := $73    ; first argument byte of the command being queued or executed: unit index, group byte, session-control reason, recycler mode or map-seed byte 0
cmdParam1                := $74    ; second argument byte of the command: usually the map column, but also the unit index of the 3-byte group commands ($87, $88, $9A, $A3) and map-seed byte 1
cmdParam2                := $75    ; third argument byte of the command: usually the map row, but also the target unit, group byte, option value or map-seed byte 2
cmdFromOpponent          := $76    ; bit 7 set while the command being merged or executed came from the opponent: its coordinates are mirrored with 39-v, its side is playerSide EOR 1, and most sound/message side effects are suppressed for our own copy
ownUnitColour            := $77    ; colour RAM value for the local player's units (6 = blue)
enemyUnitColour          := $78    ; colour RAM value for the opponent's units (2 = red)
fireButtonArmed          := $79    ; fire-button edge latch: set when checkFirePressed/isFireHeld see the button released, cleared when a press is accepted, so one press produces exactly one click
pendingMessageId         := $7A    ; id of the message the status bar should print next; bit 7 set = already consumed, $80 = fully idle
shownMessageId           := $7B    ; id of the message currently on the status bar; bit 7 set = the bar is free
zp_7C                    := $7C
unusedByte7D             := $7D    ; written with 0 by animateRingBurst ($3D2A) and read nowhere (dead store)
decimalValueHigh         := $7E    ; high (third) byte of the 24-bit value printDecimal converts; cleared by convertToDecimalDigits
linkWaitTimer            := $7F    ; countdown decremented every 8th frame; completeExchange and the link waits load it with $4B (about ten seconds) and hang the modem up if the exchange has not completed by then
loaderTimingPad          := $80    ; never written: BIT $80 is used purely as a 3-cycle delay in the fast-loader receive loops ($096F here, $C10E in the boot loader)
zp_81                    := $81
zp_82                    := $82
zp_83                    := $83
zp_84                    := $84
zp_85                    := $85
zp_86                    := $86
zp_87                    := $87
zp_88                    := $88
zp_89                    := $89
zp_8A                    := $8A
lineErrRow               := $8C    ; line stepper: Bresenham error accumulator of the row axis, stepped by |dRow| and wrapped at the major length $8E; initLineStepper preloads it with majorLen/2 when the column is the major axis; the map generator reuses it as the random-bit count
lineErrCol               := $8D    ; line stepper: error accumulator of the column axis, stepped by |dCol|; preloaded with majorLen/2 when the row is the major axis; the map generator reuses it as the random bit buffer
lineMajorLen             := $8E    ; line stepper: length of the line along its major axis, max(|dRow|,|dCol|); the contour passes reuse it as the terrain class that counts as high ground (6 summit, 4 lower hill)
lineDeltaRow             := $8F    ; line stepper: |end row - start row|
lineDeltaCol             := $90    ; line stepper: |end column - start column|
lineStepsLeft            := $91    ; line stepper: steps still to walk (0 = finished); returned in A by initLineStepper/stepLine; the contour passes reuse it as the terrain class of the ring being redrawn (5 or 3)
lineCurRow               := $92    ; line stepper: current map row; computeTrainerGroupStats leaves the group centre row here for the trainer's line and search code
lineCurCol               := $93    ; line stepper: current map column; computeTrainerGroupStats leaves the group centre column here
lineEndRow               := $94    ; line stepper: target map row; the trainer's wanted group destination row before it is copied into zp_24
lineEndCol               := $95    ; line stepper: target map column; the trainer's wanted group destination column before it is copied into zp_22
lineStepRow              := $96    ; line stepper: row increment per major step ($01 or $FF)
lineStepCol              := $97    ; line stepper: column increment per major step ($01 or $FF)
spritePlotPtr            := $98    ; pointer (lo) to the sprite shape byte being modified by plotSpritePixel; loadDirectionShapeSprite reuses it as the $0200 + shape*64 copy source
spritePlotPtrHi          := $99    ; high byte of spritePlotPtr ($8A/$90)
kernalMsgFlag            := $9D    ; KERNAL MSGFLG; the ea boot file stores 0 so the KERNAL LOAD stays silent (not a game variable)
zp_9E                    := $9E
zp_9F                    := $9F
zp_A0                    := $A0
zp_A1                    := $A1
stringPtr                := $A2    ; pointer (lo) to the bit-7 terminated string being printed; left just past the terminator
stringPtrHi              := $A3    ; high byte of stringPtr
zp_A4                    := $A4
viewOriginCol            := $A5    ; map column of the left edge of the 7x5 battlefield view (may be negative); the cursor cell is viewOriginCol+3; the map generator reuses it as the column of the cell being painted or scanned
viewOriginRow            := $A6    ; map row of the top edge of the battlefield view; the cursor cell is viewOriginRow+2; the map generator reuses it as the row of the cell being painted or scanned
blobOriginCol            := $A7    ; map generator: column where the current terrain blob started
blobOriginRow            := $A8    ; map generator: row where the current terrain blob started
isDiagonalStep           := $A9    ; set to 1 by stepLine when the step moved along both axes, 0 for an orthogonal step and 0 when the line was already finished; a diagonal step costs 8 extra movement points at $45DF
rasterIrqLine            := $AA    ; raster line the IRQ handler re-arms VIC_RASTER with ($FA)
irqVectorShadowLo        := $AB    ; low byte of the IRQ vector that rasterIrqHandler writes back to $FFFE on every interrupt
irqVectorShadowHi        := $AC    ; high byte of the IRQ vector written back to $FFFF on every interrupt
circleDxPtr              := $AD    ; pointer (lo) to the dx list of the circle radius being drawn into a sprite
circleDxPtrHi            := $AE    ; high byte of circleDxPtr
circleDyPtr              := $AF    ; pointer (lo) to the dy list of the circle radius being drawn
circleDyPtrHi            := $B0    ; high byte of circleDyPtr
gamePhase                := $B1    ; game phase: 0 only while the battle runs. Bit 7 = still waiting for the map seed (cleared by command $8B/$A6), bit 5 = side selection still open (cleared to $DF by command $9D), bits 0-1 = the two sides' 'not ready' bits cleared one at a time by command $9E through getPauseClearMask
glyphPtr                 := $B2    ; pointer (lo) to the 8 bytes of the glyph/tile being drawn; also the blob stack index and the RLE destination
glyphPtrHi               := $B3    ; high byte of glyphPtr; growTerrainBlob reuses it as the count of cells painted so far
bitmapPtr                := $B4    ; pointer (lo) to the current 8x8 cell in the $A000 bitmap, from the row/column tables at $9300
bitmapPtrHi              := $B5    ; high byte of bitmapPtr
screenPtr                := $B6    ; pointer (lo) to the colour byte of the current cell in screen RAM $8C00; renderMapTile adds $4C to the high byte on the way out, so it points into colour RAM $D800 when a unit was drawn
screenPtrHi              := $B7    ; high byte of screenPtr
blockPtr                 := $B8    ; pointer (lo) to the character-block/frame-tile data being drawn; paintRadarSweepRow reuses it as the video-matrix ($8C00) row pointer while screenPtr addresses the matching colour RAM
blockPtrHi               := $B9    ; high byte of blockPtr
magnifyEdgeMask          := $BA    ; quadrant suppression mask of plotGlyphDoubleSize: bit 0 skip the top cells, bit 1 the bottom, bit 2 the left, bit 3 the right
cursorTargetMode         := $BB    ; target cursor mode: 0 = move order, >0 = range-limited attack cursor, <0 = free cell pick reported in pickedCellCol/Row; the group menus also use it as the group-editor mode (0 = MEMBERSHIP, $80 = custom FORMATION)
filmPtr                  := $BC    ; pointer (lo) into the game film buffer in the RAM under the I/O area ($D000-$DFEF); reached only through readFilmByte/writeFilmByte with Y as the offset, so it doubles as the header base ($D000) and as the write position of the command stream
filmPtrHi                := $BD    ; high byte of filmPtr; compareFilmPtrToEnd stops recording when the pair reaches filmEndPtr $92F1/$92F2
pauseState               := $BE    ; pause/disconnect state: bit 0 own pause, bit 1 opponent pause, 3 after a timeout, bit 7 = voice pause / hang-up. handlePauseKey toggles bit 0 directly in a solo or film game and otherwise waits for the command interpreter to set it from the opponent's $8C packet
zp_C0                    := $C0
zp_C1                    := $C1
zp_C2                    := $C2
zp_C3                    := $C3
zp_C4                    := $C4
zp_C5                    := $C5
zp_C6                    := $C6
zp_C7                    := $C7
zp_C8                    := $C8
zp_C9                    := $C9
zp_CA                    := $CA
zp_CC                    := $CC
zp_CD                    := $CD
zp_CE                    := $CE
zp_D0                    := $D0
zp_D4                    := $D4
soundScriptPtr           := $D8    ; sound script read pointer (lo) of SID voice 0; the driver indexes it with voice*2, so voices 1 and 2 use $DA/$DB and $DC/$DD
soundScriptPtrHi         := $D9    ; high byte of soundScriptPtr (voice 0)
soundScriptPtr1          := $DA    ; sound script read pointer (lo) of SID voice 1
soundScriptPtr1Hi        := $DB    ; high byte of soundScriptPtr1
soundScriptPtr2          := $DC    ; sound script read pointer (lo) of SID voice 2
soundScriptPtr2Hi        := $DD    ; high byte of soundScriptPtr2
zp_DE                    := $DE
ldTrack                  := $E0    ; fast loader: track of the sector being transferred
ldSector                 := $E1    ; fast loader: sector being transferred, incremented after each good sector
ldDestPtr                := $E2    ; fast loader: destination pointer (lo) of the 256-byte receive loop
ldDestPtrHi              := $E3    ; fast loader: destination pointer (hi), advanced one page per sector
ldCia2Bits               := $E4    ; fast loader: CIA2 port A bits 0-1 (VIC bank) rewritten on every handshake strobe and EORed out of the assembled byte
wrSrcPtr                 := $E5    ; fast loader: source pointer (lo) of the sector being written back to disk
wrSrcPtrHi               := $E6    ; fast loader: source pointer (hi) of the sector being written
ldCount                  := $E7    ; fast loader: sectors left to transfer
zp_E8                    := $E8
zp_E9                    := $E9
zp_EA                    := $EA
zp_EB                    := $EB
zp_EC                    := $EC
zp_ED                    := $ED
zp_EE                    := $EE
zp_EF                    := $EF
scrollDeltaCol           := $F0    ; column step applied to the battlefield view: set to -1/0/+1 by scrollViewBy (0 when the step was clamped), then biased to a table index 0/1/2 by scrollViewBitmap, which leaves it that way
scrollDeltaRow           := $F1    ; row step applied to the battlefield view, same encoding as scrollDeltaCol
bitmapRowStride          := $F2    ; signed 16-bit stride (lo) added between bitmap rows while the view scrolls (+320 or -320)
bitmapRowStrideHi        := $F3    ; high byte of bitmapRowStride
screenRowStride          := $F4    ; signed 16-bit stride (lo) between screen RAM rows while the view scrolls (+40 or -40)
screenRowStrideHi        := $F5    ; high byte of screenRowStride
scrollRowCount           := $F6    ; number of character rows to copy while the view scrolls (8 or 10)
redrawCellCount          := $F7    ; cells left to redraw along the edge that a scroll exposed: 5 for a column, 7 for a row
viewRowLimit             := $F8    ; exclusive map-row limit for the centre cell of the battlefield view: 40 during the battle, 20 while gamePhase is non-zero so a player only sees his own half
zp_F9                    := $F9
driveCmdStrPtr           := $FB    ; C128 boot sector: pointer (lo) to the DOS command string being sent; not used by the game
driveCmdStrPtrHi         := $FC    ; high byte of driveCmdStrPtr (always $0B)
zp_FF                    := $FF
