; 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