modemwars/disassembly/game/tileRules0200.s
2026-08-23 02:09:40 -05:00

302 lines
28 KiB
ArmAsm

; ============================================================================
; $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 .......