; ============================================================================
; $0200-$03FF - terrain tile rule table and start-up code (track 18 sectors 15-16)
; ============================================================================
; Loaded by $10BB. The 256-byte table at $0200 maps a neighbourhood bit mask (bit i = neighbour i, going
; NW, N, NE, E, SE, S, SW, W) to the contour tile the map generator must draw; $0300 holds start-up code
; that the main program calls at $0A87. The area is reused as a sprite-shape decompression buffer once
; the battle starts.

        .setcpu "6502"
        .include "c64.inc"
        .include "zeropage.inc"

; ---- references to code/data outside this file ----
resetGameVariables       := $0C1C
initBitmapScreen         := $2D41
clearAllSpriteData       := $2FA5
spriteEnableShadow       := $9022
mapRowAddrLoTable        := $BFB0
mapRowAddrHiTable        := $BFD8
advanceFillPointer       := $C3F9

; Contents
; --------
;    $0300  initGameSystems              the one-shot start-up routine that travels with the tile-rule block on track 18
;                                        sector 16.
;    $031B  invertTextFonts              EORs the 944 bytes at $98BE-$9C6D with $FF, in place.
;    $0339  buildMapRowAddressTable      fills the two 40-entry row-address tables of the battlefield map: for row 0..39
;                                        the address $F000+40*row is split into $BFD8+row (high byte) and $BFB0+row (low byte).
;    $035E  resetSpritesAndVicBank       silences the sprite hardware and puts the VIC in the bank the game draws in.

        .org $0200


; contourRuleTable - the 256-byte lookup table that drives the contour (edge) pass of the random
; battlefield generator.  applyContourRuleAtCell ($7444, overlay A) walks the eight neighbours of the
; cell it is standing on, sets one mask bit per neighbour whose terrain class is at least the class of
; the hill being contoured, clears the bits that would point off the map (AND $D6 in column 0, $6B in
; column 39, $F8 in row 0, $1F in row 39) and then reads contourRuleTable,Y with that mask. Bit order,
; from neighbourOffsetTable ($74EE = cell offsets 0,1,2,$28,$2A,$50,$51,$52 relative to the north-west
; neighbour): bit0 $01 NW, bit1 $02 N, bit2 $04 NE, bit3 $08 W, bit4 $10 E, bit5 $20 SW, bit6 $40 S,
; bit7 $80 SE - reading order with the centre cell skipped.  The centre cell itself is written through
; the same pointer with Y=$29 (+41). What the code at $7484-$74ED does with each value:
;   $00      do nothing (index 0 only - no neighbour is higher).
;   $01-$7F  a contour tile for the centre cell: contourTileOffset ($0324) is subtracted (0 while the
; summit ring is drawn, $20 while the lower-hill ring is drawn) and randomTileVariant ($7872) adds a
; random variant; returns C=0.  Only $41-$51 occur: $51 north edge, $4D south edge, $4B west edge, $49
; east edge, $45 N+W corner, $46 N+E, $47 W+S, $48 E+S and $44/$43/$42/$41 for a lone higher
; NW/NE/SW/SE diagonal.
;   $80-$8F  ragged edge: on a 50% random bit (a scenario random byte shifted out one bit per call,
; refilled every 7 calls) erase the neighbour named by the low three bits - write $40 = bare ground,
; but only if that neighbour's terrain class is exactly the hill class; if the erase does not happen,
; fill the centre cell with the interior tile ($0312) instead.  Either way C=1, which makes
; applyContourTiles back the scan up two columns and two rows and look at the neighbourhood again.
; 105 entries.
;   $90-$FE  the same random erase, but when it does not fire the cell is left untouched (C=0).  Just
; three entries: $91 at mask $05, $97 at $84, $95 at $A0.
;   $FF      fill the centre cell with the interior tile and back the scan up (C=1).  93 entries,
; including mask $FF (completely surrounded).
; Correction to the survey: the sense of the $80-$FE case was inverted there.  The BCS at $74C1 sends
; values >= $90 to the do-nothing exit, so it is the $80-$8F entries that fall through into the fill,
; not the $90+ ones. Three entries name a neighbour that their own mask says is not higher, so their
; erase can never fire: mask $05 -> $91 (always leaves the cell alone) and masks $71/$F5 -> $81
; (always fill).  They look like typos in the table; the behaviour is well defined either way. Loaded
; from track 18 sector 15 by loadSetupBlock ($10BB) at boot and again by loadMapAndSetup ($10AB)
; before every game session; nothing ever writes it back to disk (writeSectors has only two callers,
; both film related).  The same 256 bytes are reused as the BAM sector buffer of checkDiskId ($0FB1 -
; see bamDiskIdChar0 and bamDiskSerial below), are scanned at $7E63 to prove a data disk is blank
; ($0203-$028F must all be zero) and are the first half of the 512-byte RLE sprite-shape buffer that
; unpackRleTo0200 ($67CB) fills and $CD15 / $3340 read.  So the table is only valid between the load
; at $0B35 and whatever reuses the page next. The rows below are commented as: mask range, which
; neighbours the fixed part of the mask says are higher, and the rule for each individual mask.
contourRuleTable:
        .byte   $00,$44,$51,$51,$43,$91,$51,$51; 0200  .DQQC.QQ  masks $00-$07 (only NW/N/NE can be higher): 00 no action, 01 $44 NW diagonal, 02/03/06/07 $51 north edge, 04 $43 NE diagonal, 05 $91 erase N else leave  [$05 names N, which this mask says is NOT higher, so the erase can never fire and the entry always leaves the cell - probable table typo]
        .byte   $4B,$4B,$45,$45,$FF,$FF,$45,$45; 0208  KKEE..EE  masks $08-$0F (W higher, NW/N/NE vary): 08/09 $4B west edge, 0A/0B/0E/0F $45 N+W corner, 0C/0D $FF fill
        .byte   $49,$84,$46,$46,$49,$80,$46,$46; 0210  I.FFI.FF  masks $10-$17 (E higher, NW/N/NE vary): 10/14 $49 east edge, 11 $84 erase E else fill, 12/13/16/17 $46 N+E corner, 15 $80 erase NW else fill
        .byte   $83,$84,$83,$84,$83,$84,$83,$84; 0218  ........  masks $18-$1F (W+E higher, NW/N/NE vary): 18/1A/1C/1E $83 erase W else fill, 19/1B/1D/1F $84 erase E else fill
        .byte   $42,$80,$81,$85,$82,$82,$85,$85; 0220  B.......  masks $20-$27 (SW higher, NW/N/NE vary): 20 $42 SW diagonal, 21 $80 erase NW else fill, 22 $81 erase N else fill, 23/26/27 $85 erase SW else fill, 24/25 $82 erase NE else fill
        .byte   $4B,$4B,$45,$45,$83,$82,$81,$45; 0228  KKEE...E  masks $28-$2F (W+SW higher, NW/N/NE vary): 28/29 $4B west edge, 2A/2B/2F $45 N+W corner, 2C $83 erase W else fill, 2D $82 erase NE else fill, 2E $81 erase N else fill
        .byte   $84,$80,$85,$85,$85,$84,$85,$85; 0230  ........  masks $30-$37 (E+SW higher, NW/N/NE vary): 30/35 $84 erase E else fill, 31 $80 erase NW else fill, 32/33/34/36/37 $85 erase SW else fill
        .byte   $47,$84,$81,$84,$82,$85,$83,$84; 0238  G.......  masks $38-$3F (W+E+SW higher, NW/N/NE vary): 38 $47 W+S corner, 39/3B/3F $84 erase E else fill, 3A $81 erase N else fill, 3C $82 erase NE else fill, 3D $85 erase SW else fill, 3E $83 erase W else fill
        .byte   $4D,$86,$81,$86,$86,$80,$86,$86; 0240  M.......  masks $40-$47 (S higher, NW/N/NE vary): 40 $4D south edge, 41/43/44/46/47 $86 erase S else fill, 42 $81 erase N else fill, 45 $80 erase NW else fill
        .byte   $47,$47,$83,$86,$82,$FF,$FF,$86; 0248  GG......  masks $48-$4F (W+S higher, NW/N/NE vary): 48/49 $47 W+S corner, 4A $83 erase W else fill, 4B/4F $86 erase S else fill, 4C $82 erase NE else fill, 4D/4E $FF fill
        .byte   $48,$84,$81,$86,$48,$FF,$86,$86; 0250  H...H...  masks $50-$57 (E+S higher, NW/N/NE vary): 50/54 $48 E+S corner, 51 $84 erase E else fill, 52 $81 erase N else fill, 53/56/57 $86 erase S else fill, 55 $FF fill
        .byte   $84,$86,$FF,$FF,$83,$FF,$FF,$FF; 0258  ........  masks $58-$5F (W+E+S higher, NW/N/NE vary): 58 $84 erase E else fill, 59 $86 erase S else fill, 5A/5B/5D/5E/5F $FF fill, 5C $83 erase W else fill
        .byte   $4D,$80,$81,$FF,$82,$FF,$FF,$FF; 0260  M.......  masks $60-$67 (SW+S higher, NW/N/NE vary): 60 $4D south edge, 61 $80 erase NW else fill, 62 $81 erase N else fill, 63/65/66/67 $FF fill, 64 $82 erase NE else fill
        .byte   $47,$47,$81,$86,$82,$82,$FF,$86; 0268  GG......  masks $68-$6F (W+SW+S higher, NW/N/NE vary): 68/69 $47 W+S corner, 6A $81 erase N else fill, 6B/6F $86 erase S else fill, 6C/6D $82 erase NE else fill, 6E $FF fill
        .byte   $48,$81,$FF,$FF,$48,$FF,$FF,$FF; 0270  H...H...  masks $70-$77 (E+SW+S higher, NW/N/NE vary): 70/74 $48 E+S corner, 71 $81 erase N else fill, 72/73/75/76/77 $FF fill  [$71 names N, not higher here, so it can never erase and always fills - probable table typo]
        .byte   $84,$84,$FF,$FF,$FF,$FF,$FF,$FF; 0278  ........  masks $78-$7F (W+E+SW+S higher, NW/N/NE vary): 78/79 $84 erase E else fill, 7A/7B/7C/7D/7E/7F $FF fill
        .byte   $41,$80,$87,$87,$97,$48,$87,$87; 0280  A....H..  masks $80-$87 (SE higher, NW/N/NE vary): 80 $41 SE diagonal, 81 $80 erase NW else fill, 82/83/86/87 $87 erase SE else fill, 84 $97 erase SE else leave, 85 $48 E+S corner
        .byte   $83,$87,$FF,$87,$FF,$FF,$FF,$87; 0288  ........  masks $88-$8F (W+SE higher, NW/N/NE vary): 88 $83 erase W else fill, 89/8B/8F $87 erase SE else fill, 8A/8C/8D/8E $FF fill
        .byte   $49,$80,$46,$46,$49,$80,$46,$46; 0290  I.FFI.FF  masks $90-$97 (E+SE higher, NW/N/NE vary): 90/94 $49 east edge, 91/95 $80 erase NW else fill, 92/93/96/97 $46 N+E corner
        .byte   $83,$FF,$FF,$FF,$83,$FF,$FF,$83; 0298  ........  masks $98-$9F (W+E+SE higher, NW/N/NE vary): 98/9C/9F $83 erase W else fill, 99/9A/9B/9D/9E $FF fill
        .byte   $95,$FF                 ; 02A0  ..  masks $A0-$A1: A0 $95 erase SW else leave, A1 $FF fill
bamDiskIdChar0:
        .byte   $FF                     ; 02A2  .  masks $A2: A2 $FF fill  -- and, while the page holds the BAM of track 18 sector 0, this byte is the first character of the disk id, tested by the CMP at $0FDC whose operand byte ($0FDC+1) checkDiskId patches from diskIdTable ('O' for the game disk, 'E' for a data disk)
bamDiskIdChar1:
        .byte   $FF,$FF,$FF,$FF,$85,$87,$87,$FF; 02A3  ........  masks $A3-$AA: A3/A4/A5/A6/AA $FF fill, A7 $85 erase SW else fill, A8/A9 $87 erase SE else fill  -- $02A3 is also the second character of the BAM disk id, tested by the CMP at $0FE3 whose operand byte ($0FE3+1) is patched with 'Z' ('OZ' = game disk) or 'A' ('EA' = data disk)
bamDiskSerial:
        .byte   $87,$FF,$FF,$87,$87,$85,$FF,$FF; 02AB  ........  masks $AB-$B2: AB/AE/AF $87 erase SE else fill, AC/AD/B1/B2 $FF fill, B0 $85 erase SW else fill  -- $02AB is also BAM offset $AB, the ten-byte disk name/serial that $0FF3 copies to ownPlayerName ($FFDE-$FFE7) so both players can prove they run the same disk
        .byte   $FF                     ; 02B3  masks $B3-$B7: B3/B4/B5 $FF fill, B6/B7 $85 erase SW else fill
        .byte   $FF,$FF,$85,$85         ; 02B4  ....
contourRuleMaskB8:
        .byte   $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF; 02B8  ........  masks $B8-$BF (W+E+SW+SE higher, NW/N/NE vary): B8/B9/BA/BB/BC/BD/BE/BF $FF fill  -- the label here is only a leftover: during booting this address held the stage-1 loader eaLoadGame of boot/eaBootFile
        .byte   $4D,$80,$81,$FF,$82,$FF,$FF,$FF; 02C0  M.......  masks $C0-$C7 (S+SE higher, NW/N/NE vary): C0 $4D south edge, C1 $80 erase NW else fill, C2 $81 erase N else fill, C3/C5/C6/C7 $FF fill, C4 $82 erase NE else fill
        .byte   $47,$47,$81,$FF,$82,$FF,$FF,$FF; 02C8  GG......  masks $C8-$CF (W+S+SE higher, NW/N/NE vary): C8/C9 $47 W+S corner, CA $81 erase N else fill, CB/CD/CE/CF $FF fill, CC $82 erase NE else fill
        .byte   $48,$80,$FF,$FF,$48     ; 02D0  H...H  masks $D0-$D4: D0/D4 $48 E+S corner, D1 $80 erase NW else fill, D2/D3 $FF fill
contourRuleMaskD5:
        .byte   $80,$81,$FF,$83,$FF,$FF,$FF,$83; 02D5  ........  masks $D5-$DC: D5 $80 erase NW else fill, D6 $81 erase N else fill, D7/D9/DA/DB $FF fill, D8/DC $83 erase W else fill
        .byte   $FF,$FF,$FF,$4D,$FF,$FF,$FF,$FF; 02DD  ...M....  masks $DD-$E4: DD/DE/DF/E1/E2/E3/E4 $FF fill, E0 $4D south edge
        .byte   $FF,$FF,$FF,$47,$47,$FF,$81; 02E5  ...GG..  masks $E5-$EB: E5/E6/E7/EA $FF fill, E8/E9 $47 W+S corner, EB $81 erase N else fill
contourRuleMaskEC:
        .byte   $82,$82,$FF,$FF         ; 02EC  masks $EC-$EF: EC/ED $82 erase NE else fill, EE/EF $FF fill  -- during booting these four bytes were the file name 'LOAD' of boot/eaBootFile, which is why they are still typed as text
        .byte   $48,$80,$81,$FF,$48,$81,$48,$FF; 02F0  H...H.H.  masks $F0-$F7 (E+SW+S+SE higher, NW/N/NE vary): F0/F4/F6 $48 E+S corner, F1 $80 erase NW else fill, F2/F5 $81 erase N else fill, F3/F7 $FF fill  [$F5 names N, not higher here, so it can never erase and always fills - probable table typo]
        .byte   $84,$FF,$FF,$FF,$FF,$FF,$FF,$FF; 02F8  ........  masks $F8-$FF (W+E+SW+S+SE higher, NW/N/NE vary): F8 $84 erase E else fill, F9/FA/FB/FC/FD/FE/FF $FF fill

; ----------------------------------------------------------------------
; initGameSystems - the one-shot start-up routine that travels with the tile-rule block on track 18
; sector 16.  It puts the machine into the state the battle engine expects: every game variable
; cleared, the sprite hardware silenced and VIC bank 2 selected, the text engine's fonts inverted, the
; 24-bit game RNG seeded and the map row-address tables built; then it hands over to the bitmap screen
; builder. It runs exactly once, from mainStart ($0A87), immediately after loadSetupBlock ($10BB) has
; read this very block from disk.  That matters twice: invertTextFonts is not idempotent, and the
; first run of the map generator overwrites $0300-$0332 with its own scratch variables, so this code
; no longer exists after that.  The block is reloaded before every game session ($0B35 -> $10AB), but
; only the rule table at $0200 is wanted then - $0300 is never called again.
; In:  no registers; runs with $01=$35 (ROMs out, I/O in), interrupts already enabled and the IRQ/NMI
;     vector high bytes just set to $12 by mainStart
; Out: zero page $15-$7F and $8C-$FF plus $9008-$92FF cleared and the message queue reset
;     (resetGameVariables); all sprite registers, the sprite-enable shadow $9022 and the shapes at
;     $8A00-$8BFF cleared; CIA2 set to VIC bank 2; $98BE-$9C6D inverted; gameRngState0/1/2 ($57-$59) =
;     $49/$19/$02; row tables at $BFB0/$BFD8 built; the bitmap screen initialised by the closing JMP.
;     zp_48-zp_4B and zp_4E/4F are clobbered.
; Called from: mainProgram0800 mainStart ($0A87), the only caller.  XREF also lists 'from
;     boot/eaBootFile: call:0300', which belongs to the BASIC vector table that occupied this address
;     while the game was still loading.
; ----------------------------------------------------------------------
initGameSystems:
        jsr     resetGameVariables      ; 0300  clear the world first: resetGameVariables ($0C1C) zeroes zero page $15-$7F and $8C-$FF and the variable block $9008-$92FF, blanks the eight message-queue slots, rewinds the film pointer and sets currentScreen = 3.  It has to come first because it also wipes the RNG state written just below
        jsr     resetSpritesAndVicBank  ; 0303  sprites off, sprite shapes cleared and the VIC pointed at bank 2 ($8000-$BFFF), the bank that holds bitmap $A000, video matrix $8C00 and the sprite shapes at $8A00
        jsr     invertTextFonts         ; 0306  turn the two 59-glyph fonts of the $C000 text engine ($98BE-$9C6D) into negatives
        lda     #$49                    ; 0309  byte 0 of the fixed 24-bit RNG seed
        sta     gameRngState0           ; 030B  gameRngState0 ($57).  nextGameRandom ($FD5D) shifts $57-$59 and returns $57 EOR $58; the seed is a constant on purpose, so both machines of a modem game draw the same sequence from the first frame
        lda     #$19                    ; 030D  byte 1 of the seed
storeRngSeedByte1:
        sta     gameRngState1           ; 030F  gameRngState1 ($58).  The label on this instruction is an artefact of the page's other life: once overlay A is resident, $030F is one of the map generator's scratch variables (the river column at $756F/$757A)
loadRngSeedByte2:
        lda     #$02                    ; 0311  byte 2 of the seed
storeRngSeedByte2:
        sta     gameRngState2           ; 0313  gameRngState2 ($59) - $49/$19/$02 is never all zero, which would lock the shift register.  ($0313 carries the stale global name commandDispatchLoop, which belongs to the 1541 fast loader running at $0300-$05FF inside the drive, and is also map-generator scratch at run time)
        jsr     buildMapRowAddressTable ; 0315  build the 40 row addresses of the $F000 map at $BFB0/$BFD8 before anything reads or writes a map cell
        jmp     initBitmapScreen        ; 0318  tail-jump into initBitmapScreen ($2D41): full-screen text window, border and background medium grey ($0C), colour RAM $D800-$DBFF and video matrix $8C00-$8FFF filled, bitmap $A000 cleared.  Its RTS returns to mainStart at $0A8A


; ----------------------------------------------------------------------
; invertTextFonts - EORs the 944 bytes at $98BE-$9C6D with $FF, in place.  That range is exactly the
; two 59-glyph 8x8 character sets the $C000 text engine plots with: the multicolour / double-width set
; at $98BE and the hires set at $9A96 ($98BE+$1D8 = 59*8), picked at $C20B by bit 4 (multicolour) of
; the $D016 shadow $9001.  On disk the glyphs are positives (1 bits = ink); afterwards they are
; negatives, which is the polarity the plotter's patched load/store at $C22B (ORA/EOR/AND modes set by
; setBitmapDrawMode) expects.
; In:  nothing - the range is hardcoded in the four immediate loads below
; Out: $98BE-$9C6D inverted; zp_48/49 left at $9C6E, zp_4A/4B = $9C6D, zp_4E/4F bumped 944 times
;     (nothing reads that counter)
; Called from: initGameSystems ($0306) only - and it must stay a one-shot, because a second call would
;     turn the fonts back into positives
; ----------------------------------------------------------------------
invertTextFonts:
        lda     #$BE                    ; 031B  low byte of the first font byte...
        sta     mapCellPtr              ; 031D  ...into the walking source pointer zp_48/49; $98BE is row 0 of glyph 0 of the multicolour set
        lda     #$98                    ; 031F  high byte of $98BE
        sta     mapCellPtrHi            ; 0321  pointer now $98BE
        lda     #$6D                    ; 0323  low byte of the last font byte...
storeFontEndLo:
        sta     fillEndPtr              ; 0325  ...into the limit zp_4A/4B that advanceFillPointer compares against; $9C6D is the last row of the last hires glyph ($9A96 + 59*8 - 1).  ($0325 is also a map-generator scratch byte once overlay A is loaded)
        lda     #$9C                    ; 0327  high byte of $9C6D
storeFontEndHi:
        sta     fillEndPtrHi            ; 0329  limit now $9C6D
        ldy     #$00                    ; 032B  the pointer itself walks, so the index stays 0 for the whole loop

; ----------------------------------------------------------------------
; Inversion loop: one font byte per pass.  Both the pointer step and the end test live in
; advanceFillPointer ($C3F9).
; ----------------------------------------------------------------------
invertFontByteLoop:
        lda     (mapCellPtr),y          ; 032D  fetch one 8-pixel glyph row
        eor     #$FF                    ; 032F  invert all eight pixels of the row
        sta     (mapCellPtr),y          ; 0331  write it back over the original
        jsr     advanceFillPointer      ; 0333  advanceFillPointer ($C3F9): compares zp_48/49 against the limit (C=1 once it has reached $9C6D), then increments the pointer and the never-read counter zp_4E/4F
        bcc     invertFontByteLoop      ; 0336  loop while the pointer is still below the limit.  The compare happens before the increment, so $9C6D is inverted too: 944 bytes = 118 glyphs = two sets of 59
        rts                             ; 0338  back to initGameSystems ($0309)


; ----------------------------------------------------------------------
; buildMapRowAddressTable - fills the two 40-entry row-address tables of the battlefield map: for row
; 0..39 the address $F000+40*row is split into $BFD8+row (high byte) and $BFB0+row (low byte).
; readMapCell ($25FA, which reads $BFB0,y at $25FC and $BFD8,y at $2603) and every other map accessor
; turn a (column,row) pair into a pointer with these two tables, so nothing may touch the map before
; this has run. The tables sit at $BFB0-$BFFF, in the 192 bytes of VIC bank 2 left over above the
; bitmap (which ends at $BF3F) - anything that clears the bitmap has to stop there.
; In:  nothing; the map base $F000 and the row stride $28 = 40 cells are hardcoded
; Out: $BFB0-$BFD7 = row address low bytes, $BFD8-$BFFF = high bytes; zp_48/49 left at $F640
; Called from: initGameSystems ($0315) only
; ----------------------------------------------------------------------
buildMapRowAddressTable:
        lda     #$00                    ; 0339  low byte of the map base...
        sta     mapCellPtr              ; 033B  ...into the running address zp_48/49
        lda     #$F0                    ; 033D  high byte: the map is $F000-$F63F, 40x40 bytes, one per battlefield cell
        sta     mapCellPtrHi            ; 033F  running address now $F000 = row 0, column 0
        ldy     #$00                    ; 0341  start at row 0

; ----------------------------------------------------------------------
; One pass per map row: publish the current address in both tables, then step it on by one row.
; ----------------------------------------------------------------------
buildRowAddressLoop:
        lda     mapCellPtrHi            ; 0343  high byte of the address of row Y
        sta     mapRowAddrHiTable,y     ; 0345  store it in the high-byte table $BFD8+row (40 entries; readMapCell fetches it at $2603)
        lda     mapCellPtr              ; 0348  low byte of the same address
        sta     mapRowAddrLoTable,y     ; 034A  store it in the low-byte table $BFB0+row (fetched at $25FC)
        clc                             ; 034D  A still holds the low byte - clear carry for the row step
        adc     #$28                    ; 034E  one map row is $28 = 40 cells wide
        sta     mapCellPtr              ; 0350  new low byte
        lda     mapCellPtrHi            ; 0352  carry the row step into the high byte
        adc     #$00                    ; 0354  add 0 plus carry
        sta     mapCellPtrHi            ; 0356  address now points at the start of the next row
        iny                             ; 0358  next row
        cpy     #$28                    ; 0359  the battlefield has $28 = 40 rows
        bcc     buildRowAddressLoop     ; 035B  loop until row 39 has been stored
        rts                             ; 035D  return with zp_48/49 = $F640, one row past the map - which is also where the 100-entry unit record arrays begin


; ----------------------------------------------------------------------
; resetSpritesAndVicBank - silences the sprite hardware and puts the VIC in the bank the game draws
; in.  It zeroes $D000-$D010 (the eight sprite X/Y pairs and the sprite X bit-8 register), $D015
; sprite enable together with its shadow $9022, $D017 Y expand and the six registers $D01A-$D01F (VIC
; IRQ mask, sprite/background priority, sprite multicolour, X expand and the two collision latches),
; then makes CIA2 port A bits 0-1 outputs and writes $05 - VIC bank 2 ($8000-$BFFF) with the user-port
; RS-232 transmit line left high. Bank 2 is what makes bitmap $A000, video matrix $8C00 and the sprite
; shapes at $8A00 visible to the VIC; the boot loader's title screen used the same bank, but the game
; selects it again here rather than trusting the loader.
; In:  nothing
; Out: $D000-$D010, $D015, $D017, $D01A-$D01F and $9022 all 0; CIA2_DDRA bits 0-1 = output; CIA2_PRA =
;     $05; $8A00-$8BFF cleared by the closing JMP; A = 0, Y = $FF
; Called from: initGameSystems ($0303) only
; ----------------------------------------------------------------------
resetSpritesAndVicBank:
        lda     #$00                    ; 035E  the value written to every register below
        ldy     #$10                    ; 0360  highest index of the block $D000-$D010

; ----------------------------------------------------------------------
; Blank the sprite position registers $D000-$D010.
; ----------------------------------------------------------------------
clearSpriteCoordLoop:
        sta     VIC_SPR0_X,y            ; 0362  $D000+Y = 0: the eight sprite X/Y coordinate pairs and, at Y=$10, the sprite X bit-8 register $D010
        dey                             ; 0365  next register down
        bpl     clearSpriteCoordLoop    ; 0366  until $D000 itself has been written
        sta     VIC_SPR_ENA             ; 0368  $D015 = 0: every sprite disabled
        sta     spriteEnableShadow      ; 036B  keep the software copy of $D015 in step - the game only ever ORs bits into $9022 and copies that byte to the chip
        sta     VIC_SPR_EXP_Y           ; 036E  $D017 = 0: no sprite is stretched vertically
        ldy     #$05                    ; 0371  highest index of the block $D01A-$D01F

; ----------------------------------------------------------------------
; Blank the sprite control registers and the VIC interrupt mask that shares the block.
; ----------------------------------------------------------------------
clearSpriteControlLoop:
        sta     VIC_IRQ_MASK,y          ; 0373  $D01A+Y = 0: $D01A no VIC interrupt source enabled, $D01B every sprite in front of the bitmap, $D01C no multicolour sprite, $D01D no horizontal stretch.  The writes to $D01E/$D01F (sprite-sprite and sprite-bitmap collision) do nothing on a real VIC - those latches clear when they are read - so the loop simply starts two registers too high
        dey                             ; 0376  next register down
        bpl     clearSpriteControlLoop  ; 0377  until $D01A itself has been written

; ----------------------------------------------------------------------
; Select the VIC bank the game draws in.
; ----------------------------------------------------------------------
        lda     CIA2_DDRA               ; 0379  current data direction of CIA2 port A
        ora     #$03                    ; 037C  force bits 0-1 to output - they are the VIC bank select lines; every other bit (user port and RS-232 handshake lines) keeps the direction the comm module gave it
        sta     CIA2_DDRA               ; 037E  write the direction register back
        lda     #$05                    ; 0381  %00000101: bank select %01 = VIC bank 2 ($8000-$BFFF), because the two bits are inverted (3 = bank 0), and bit 2 = user-port PA2 = RS-232 TXD held high, the idle/mark level, so a connected modem does not see a start bit
        sta     CIA2_PRA                ; 0383  write CIA2 port A - from here on the VIC fetches from $8000-$BFFF
        jmp     clearAllSpriteData      ; 0386  tail-jump into clearAllSpriteData ($2FA5), which zeroes $8A00-$8BFF, the eight 64-byte sprite shape slots of this bank; its RTS returns to initGameSystems ($0306)


; setupBlockPadding - the 119 bytes $0389-$03FF that follow the start-up code are zero on disk (the
; tail of track 18 sector 16) and nothing reads them as data or executes them. They are not idle at
; run time though: $0200-$03FF doubles as the 512-byte RLE sprite-shape buffer (eight 64-byte shapes,
; so this tail is the second half of shape 6 and the whole of shape 7), as the first page of the film
; directory ($0300-$04FF from track 18 sectors 2-3) and, in a solo game, as part of the trainer's
; playbook ($0200-$04FF from track 29 sectors 6-8).  The map generator's own variables stop at $0332,
; so this part of the page is free while it runs.
setupBlockPadding:
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 0389  ........  119 zero filler bytes to the end of the sector; not code and not data the game reads
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 0391  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 0399  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03A1  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03A9  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03B1  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03B9  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03C1  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03C9  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03D1  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03D9  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03E1  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03E9  ........
        .byte   $00,$00,$00,$00,$00,$00,$00,$00; 03F1  ........
        .byte   $00,$00,$00,$00,$00,$00,$00; 03F9  .......
