modemwars/annotations/20_game_tileRules0200_0200.json
2026-08-23 02:09:40 -05:00

619 lines
No EOL
27 KiB
JSON

{
"chunk": "game_tileRules0200_0200",
"unit": "game/tileRules0200",
"unitLabels": {
"game/tileRules0200": {
"02A2": "bamDiskIdChar0",
"02A3": "bamDiskIdChar1",
"02AB": "bamDiskSerial",
"02B8": "contourRuleMaskB8",
"02D5": "contourRuleMaskD5",
"02EC": "contourRuleMaskEC",
"030F": "storeRngSeedByte1",
"0311": "loadRngSeedByte2",
"0313": "storeRngSeedByte2",
"0325": "storeFontEndLo",
"0329": "storeFontEndHi",
"032D": "invertFontByteLoop",
"0343": "buildRowAddressLoop",
"0362": "clearSpriteCoordLoop",
"0373": "clearSpriteControlLoop"
}
},
"notes": {
"0200": {
"unit": "game/tileRules0200",
"block": [
"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."
],
"line": "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]"
},
"0208": {
"unit": "game/tileRules0200",
"line": "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"
},
"0210": {
"unit": "game/tileRules0200",
"line": "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"
},
"0218": {
"unit": "game/tileRules0200",
"line": "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"
},
"0220": {
"unit": "game/tileRules0200",
"line": "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"
},
"0228": {
"unit": "game/tileRules0200",
"line": "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"
},
"0230": {
"unit": "game/tileRules0200",
"line": "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"
},
"0238": {
"unit": "game/tileRules0200",
"line": "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"
},
"0240": {
"unit": "game/tileRules0200",
"line": "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"
},
"0248": {
"unit": "game/tileRules0200",
"line": "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"
},
"0250": {
"unit": "game/tileRules0200",
"line": "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"
},
"0258": {
"unit": "game/tileRules0200",
"line": "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"
},
"0260": {
"unit": "game/tileRules0200",
"line": "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"
},
"0268": {
"unit": "game/tileRules0200",
"line": "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"
},
"0270": {
"unit": "game/tileRules0200",
"line": "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]"
},
"0278": {
"unit": "game/tileRules0200",
"line": "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"
},
"0280": {
"unit": "game/tileRules0200",
"line": "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"
},
"0288": {
"unit": "game/tileRules0200",
"line": "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"
},
"0290": {
"unit": "game/tileRules0200",
"line": "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"
},
"0298": {
"unit": "game/tileRules0200",
"line": "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"
},
"02A0": {
"unit": "game/tileRules0200",
"line": "masks $A0-$A1: A0 $95 erase SW else leave, A1 $FF fill"
},
"02A2": {
"unit": "game/tileRules0200",
"line": "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)"
},
"02A3": {
"unit": "game/tileRules0200",
"line": "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)"
},
"02AB": {
"unit": "game/tileRules0200",
"line": "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"
},
"02B3": {
"unit": "game/tileRules0200",
"line": "masks $B3-$B7: B3/B4/B5 $FF fill, B6/B7 $85 erase SW else fill"
},
"02B8": {
"unit": "game/tileRules0200",
"line": "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"
},
"02C0": {
"unit": "game/tileRules0200",
"line": "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"
},
"02C8": {
"unit": "game/tileRules0200",
"line": "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"
},
"02D0": {
"unit": "game/tileRules0200",
"line": "masks $D0-$D4: D0/D4 $48 E+S corner, D1 $80 erase NW else fill, D2/D3 $FF fill"
},
"02D5": {
"unit": "game/tileRules0200",
"line": "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"
},
"02DD": {
"unit": "game/tileRules0200",
"line": "masks $DD-$E4: DD/DE/DF/E1/E2/E3/E4 $FF fill, E0 $4D south edge"
},
"02E5": {
"unit": "game/tileRules0200",
"line": "masks $E5-$EB: E5/E6/E7/EA $FF fill, E8/E9 $47 W+S corner, EB $81 erase N else fill"
},
"02EC": {
"unit": "game/tileRules0200",
"line": "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"
},
"02F0": {
"unit": "game/tileRules0200",
"line": "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]"
},
"02F8": {
"unit": "game/tileRules0200",
"line": "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"
},
"0300": {
"unit": "game/tileRules0200",
"routine": [
"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."
],
"line": "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"
},
"0303": {
"unit": "game/tileRules0200",
"line": "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"
},
"0306": {
"unit": "game/tileRules0200",
"line": "turn the two 59-glyph fonts of the $C000 text engine ($98BE-$9C6D) into negatives"
},
"0309": {
"unit": "game/tileRules0200",
"line": "byte 0 of the fixed 24-bit RNG seed"
},
"030B": {
"unit": "game/tileRules0200",
"line": "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"
},
"030D": {
"unit": "game/tileRules0200",
"line": "byte 1 of the seed"
},
"030F": {
"unit": "game/tileRules0200",
"line": "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)"
},
"0311": {
"unit": "game/tileRules0200",
"line": "byte 2 of the seed"
},
"0313": {
"unit": "game/tileRules0200",
"line": "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)"
},
"0315": {
"unit": "game/tileRules0200",
"line": "build the 40 row addresses of the $F000 map at $BFB0/$BFD8 before anything reads or writes a map cell"
},
"0318": {
"unit": "game/tileRules0200",
"line": "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"
},
"031B": {
"unit": "game/tileRules0200",
"routine": [
"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"
],
"line": "low byte of the first font byte..."
},
"031D": {
"unit": "game/tileRules0200",
"line": "...into the walking source pointer zp_48/49; $98BE is row 0 of glyph 0 of the multicolour set"
},
"031F": {
"unit": "game/tileRules0200",
"line": "high byte of $98BE"
},
"0321": {
"unit": "game/tileRules0200",
"line": "pointer now $98BE"
},
"0323": {
"unit": "game/tileRules0200",
"line": "low byte of the last font byte..."
},
"0325": {
"unit": "game/tileRules0200",
"line": "...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)"
},
"0327": {
"unit": "game/tileRules0200",
"line": "high byte of $9C6D"
},
"0329": {
"unit": "game/tileRules0200",
"line": "limit now $9C6D"
},
"032B": {
"unit": "game/tileRules0200",
"line": "the pointer itself walks, so the index stays 0 for the whole loop"
},
"032D": {
"unit": "game/tileRules0200",
"block": [
"Inversion loop: one font byte per pass. Both the pointer step and the end test live in advanceFillPointer ($C3F9)."
],
"line": "fetch one 8-pixel glyph row"
},
"032F": {
"unit": "game/tileRules0200",
"line": "invert all eight pixels of the row"
},
"0331": {
"unit": "game/tileRules0200",
"line": "write it back over the original"
},
"0333": {
"unit": "game/tileRules0200",
"line": "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"
},
"0336": {
"unit": "game/tileRules0200",
"line": "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"
},
"0338": {
"unit": "game/tileRules0200",
"line": "back to initGameSystems ($0309)"
},
"0339": {
"unit": "game/tileRules0200",
"routine": [
"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"
],
"line": "low byte of the map base..."
},
"033B": {
"unit": "game/tileRules0200",
"line": "...into the running address zp_48/49"
},
"033D": {
"unit": "game/tileRules0200",
"line": "high byte: the map is $F000-$F63F, 40x40 bytes, one per battlefield cell"
},
"033F": {
"unit": "game/tileRules0200",
"line": "running address now $F000 = row 0, column 0"
},
"0341": {
"unit": "game/tileRules0200",
"line": "start at row 0"
},
"0343": {
"unit": "game/tileRules0200",
"block": [
"One pass per map row: publish the current address in both tables, then step it on by one row."
],
"line": "high byte of the address of row Y"
},
"0345": {
"unit": "game/tileRules0200",
"line": "store it in the high-byte table $BFD8+row (40 entries; readMapCell fetches it at $2603)"
},
"0348": {
"unit": "game/tileRules0200",
"line": "low byte of the same address"
},
"034A": {
"unit": "game/tileRules0200",
"line": "store it in the low-byte table $BFB0+row (fetched at $25FC)"
},
"034D": {
"unit": "game/tileRules0200",
"line": "A still holds the low byte - clear carry for the row step"
},
"034E": {
"unit": "game/tileRules0200",
"line": "one map row is $28 = 40 cells wide"
},
"0350": {
"unit": "game/tileRules0200",
"line": "new low byte"
},
"0352": {
"unit": "game/tileRules0200",
"line": "carry the row step into the high byte"
},
"0354": {
"unit": "game/tileRules0200",
"line": "add 0 plus carry"
},
"0356": {
"unit": "game/tileRules0200",
"line": "address now points at the start of the next row"
},
"0358": {
"unit": "game/tileRules0200",
"line": "next row"
},
"0359": {
"unit": "game/tileRules0200",
"line": "the battlefield has $28 = 40 rows"
},
"035B": {
"unit": "game/tileRules0200",
"line": "loop until row 39 has been stored"
},
"035D": {
"unit": "game/tileRules0200",
"line": "return with zp_48/49 = $F640, one row past the map - which is also where the 100-entry unit record arrays begin"
},
"035E": {
"unit": "game/tileRules0200",
"routine": [
"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"
],
"line": "the value written to every register below"
},
"0360": {
"unit": "game/tileRules0200",
"line": "highest index of the block $D000-$D010"
},
"0362": {
"unit": "game/tileRules0200",
"block": [
"Blank the sprite position registers $D000-$D010."
],
"line": "$D000+Y = 0: the eight sprite X/Y coordinate pairs and, at Y=$10, the sprite X bit-8 register $D010"
},
"0365": {
"unit": "game/tileRules0200",
"line": "next register down"
},
"0366": {
"unit": "game/tileRules0200",
"line": "until $D000 itself has been written"
},
"0368": {
"unit": "game/tileRules0200",
"line": "$D015 = 0: every sprite disabled"
},
"036B": {
"unit": "game/tileRules0200",
"line": "keep the software copy of $D015 in step - the game only ever ORs bits into $9022 and copies that byte to the chip"
},
"036E": {
"unit": "game/tileRules0200",
"line": "$D017 = 0: no sprite is stretched vertically"
},
"0371": {
"unit": "game/tileRules0200",
"line": "highest index of the block $D01A-$D01F"
},
"0373": {
"unit": "game/tileRules0200",
"block": [
"Blank the sprite control registers and the VIC interrupt mask that shares the block."
],
"line": "$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"
},
"0376": {
"unit": "game/tileRules0200",
"line": "next register down"
},
"0377": {
"unit": "game/tileRules0200",
"line": "until $D01A itself has been written"
},
"0379": {
"unit": "game/tileRules0200",
"block": [
"Select the VIC bank the game draws in."
],
"line": "current data direction of CIA2 port A"
},
"037C": {
"unit": "game/tileRules0200",
"line": "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"
},
"037E": {
"unit": "game/tileRules0200",
"line": "write the direction register back"
},
"0381": {
"unit": "game/tileRules0200",
"line": "%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"
},
"0383": {
"unit": "game/tileRules0200",
"line": "write CIA2 port A - from here on the VIC fetches from $8000-$BFFF"
},
"0386": {
"unit": "game/tileRules0200",
"line": "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)"
},
"0389": {
"unit": "game/tileRules0200",
"block": [
"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."
],
"line": "119 zero filler bytes to the end of the sector; not code and not data the game reads"
}
},
"dataTypes": {
"0200": [
"byte",
8
],
"0208": [
"byte",
8
],
"0210": [
"byte",
8
],
"0218": [
"byte",
8
],
"0220": [
"byte",
8
],
"0228": [
"byte",
8
],
"0230": [
"byte",
8
],
"0238": [
"byte",
8
],
"0240": [
"byte",
8
],
"0248": [
"byte",
8
],
"0250": [
"byte",
8
],
"0258": [
"byte",
8
],
"0260": [
"byte",
8
],
"0268": [
"byte",
8
],
"0270": [
"byte",
8
],
"0278": [
"byte",
8
],
"0280": [
"byte",
8
],
"0288": [
"byte",
8
],
"0290": [
"byte",
8
],
"0298": [
"byte",
8
],
"02A0": [
"byte",
2
],
"02A2": [
"byte",
1
],
"02A3": [
"byte",
8
],
"02AB": [
"byte",
8
],
"02B8": [
"byte",
8
],
"02C0": [
"byte",
8
],
"02C8": [
"byte",
8
],
"02D0": [
"byte",
5
],
"02D5": [
"byte",
8
],
"02DD": [
"byte",
8
],
"02E5": [
"byte",
7
],
"02F0": [
"byte",
8
],
"02F8": [
"byte",
8
],
"0389": [
"byte",
119
]
}
}