; ============================================================================ ; $8800-$89FF - modem status message strings (track 29 sectors 9-10) ; ============================================================================ ; Only used while a modem game is being set up; the solo trainer AI reuses the whole block as work RAM. .setcpu "6502" .include "c64.inc" .include "zeropage.inc" .org $8800 ; $8800-$89FF - THE MODEM STATUS MESSAGE BLOCK. Track 29 sectors 9-10, 512 bytes, pure data: no ; code, no pointers, nothing in it is relocated. It is read by loadStringsAndCommTail ($1060), which ; is entered by fall-through from loadOverlayA ($1047) at start-up and called directly from ; ensureOverlayAResident ($103B) every time the game returns to the main menu, so the whole block is ; re-read from disk before any of it can be needed again. ; ; Contents (every byte of the file is accounted for): ; $8800-$890B eleven bit-7-terminated message strings (the twelfth is at $895E) ; $890C-$892B mapNameAlphabet - the 32 characters a map name / map seed is written in ; $892C-$8944 gameTypeDefaultsSide0 - 5 x 5 scenario parameter defaults for side 0 ; $8945-$895D gameTypeDefaultsSide1 - the same 5 x 5 table for side 1 ; $895E-$897E the twelfth string, 'OPPONENT USING NON-STANDARD GAME.' ; $897F-$89FF 129 bytes of unreferenced filler (see messageBlockTail) ; ; Strings are bit-7-terminated (the last character has $80 added: $AE = '.', $A0 = ' ') and are ; reached by message id through the split pointer pair messagePtrLoTable ($0500) / messagePtrHiTable ; ($0524) in the $0400 block: ids $04, $0B, $10, $17, $18, $1A and $1F point in here permanently, and ; the two run-time slots $20/$21 (stored as $FFFF on disk) are aimed at strings in this block by ; setMessageSlot ($C924). $895E is not an id at all - its address is built from two immediates at ; $79DF/$79E1. ; ; WARNING - almost every D_88xx / D_89xx label in this listing is a SOLO TRAINER address, not a string ; boundary. Once a solo game starts, initTrainerAi ($EC0F) and the resident AI at $E000 use ; $8800-$897C as their work RAM and overwrite this whole block: ; $8800+ord 50 bytes replan / last-order clock per AI unit (ordinal = unit index - 50) ; $8832+ord 50 bytes group id per AI unit ($FF = none); addressed as $8800,absoluteUnitIndex ; $8864+ord 50 bytes 'has reached the enemy end zone' flag; = $8832,absoluteUnitIndex ; $8896 10 bytes staged command ($8896-$8899) and the 8-way threat/direction ring ; $88A0 50 bytes the outgoing order queue ; $88D2 $88DE $88EA $88F6 $8902 $890E $891A $8926 $8932 $893E ten 12-entry per-group arrays ; $894A 8 bytes heading search order - read at $E7F4 but never written by anything ; $8952-$897C the AI's scalar variables ; The AI stops at $897C: $897D-$89FF is the only part of the block it never touches. Clobbering the ; rest is safe because a modem game never loads the trainer, the map-name alphabet is only used by the ; setup overlay (which has finished by then), applyGameTypeDefaults has already read the two 5x5 ; tables, and the menu path re-reads the block from disk. The per-byte notes below give both readings ; wherever a trainer label lands inside a string or a table row. ; ; Boot-time scratch: verifyProgramChecksum ($0461, in the $0400 block) parks its raw checksum sum in ; $8800 at $049D. That happens long before this block is loaded over it and nothing reads it back. ; ; msgGetOpponentOnPhoneAnd - 'GET OPPONENT ON PHONE AND...', 28 bytes. Text of run-time message slot ; $20 while a modem game is being set up: startGameFromSetup patches slot $20 to $8800 at ; $0B17-$0B1D and slot $21 to $881C, then calls waitForAnswerOriginateKey ($0CBD). That only ; happens when the previous game was a solo game and this one is a modem game. msgGetOpponentOnPhoneAnd: .byte "GET OPPONENT ON PHONE AND..",'.'|$80; 8800 ; msgPressAOrOAndSetModem - 'PRESS A OR O AND SET MODEM.', 27 bytes. The second line of the same ; prompt: run-time message slot $21, patched at $0B20-$0B26. A = answer, O = originate; the modem ; driver's hot keys pick which end dials. The string is cut in three below because the trainer's own ; tables start at $8831 and $8832. msgPressAOrOAndSetModem: .byte "PRESS A OR O AND SET " ; 881C D_8831: .byte "M" ; 8831 'M'. Also the trainer's replan clock for AI unit ordinal 49 - its COMCEN - the last entry of the 50-byte array based at $8800 D_8832: .byte "ODEM",'.'|$80 ; 8832 'ODEM' + $AE terminator. Also the base of the trainer's 50-byte group-id array ($8832-$8863), which the resident AI addresses as $8800,absoluteUnitIndex ; msgPressSpaceWhenReady - 'PRESS SPACE WHEN READY.', 23 bytes. showModemSetupPrompt ($0C8C) puts the ; caller's string into message slot $20 and this one into slot $21 ($0C93: LDA #$37 / LDY #$88) before ; waiting for the space bar. Its only caller is the version-mismatch path at $79E3. msgPressSpaceWhenReady: .byte "PRESS SPACE WHEN READY",'.'|$80; 8837 ; msgTrainerName - 'TRAINER ', 10 bytes ($A0 = the trailing space that carries the terminator). ; Not a status message: it is the opponent's name. exchangePlayerNamesAndSettings ($ED43 in the modem ; driver) skips the whole name handshake in solo mode and copies these ten characters into ; opponentPlayerName ($FFE8) with the loop at $ED4A, so the console shows TRAINER where a human ; opponent's typed name would be. Exactly ten bytes because the name fields are ten characters. msgTrainerName: .byte "TRAINER ",$A0 ; 884E ; msgOpponentReady - 'OPPONENT READY.', 15 bytes. Message id $04 (messagePtrLoTable[$04] = $58, ; messagePtrHiTable[$04] = $88). Queued by getPauseClearMask at $52C0 when a session-control or ; setup-ready command from the opponent clears his 'not ready' bit (bit 1 of the pause byte). msgOpponentReady: .byte "OPPONENT RE" ; 8858 D_8863: .byte "A" ; 8863 'A'. Also the last entry (ordinal 49, the AI COMCEN) of the trainer's group-id array D_8864: .byte "DY",'.'|$80 ; 8864 'DY' + $AE. Also the base of the trainer's 50-byte 'reached the enemy end zone' flags ($8864-$8895) ; msgWaitingForOpponent - 'WAITING FOR OPPONENT...', 23 bytes. Message id $0B, pinned as the ; persistent status line ($92A1) at $572E in the packet-exchange wait and at $C7CC in the text engine; ; cleared again at $5760 / $7C18. msgWaitingForOpponent: .byte "WAITING FOR OPPONENT..",'.'|$80; 8867 ; msgWaitingForConnection - 'WAITING FOR CONNECTION...', 25 bytes. Message id $10, pinned by ; waitForConnection ($1B7C) while it spins in waitForCarrier waiting for the modem to report carrier. ; On a solo game that routine returns before ever showing it. msgWaitingForConnection: .byte "WAITING FOR CONNECTION."; 887E D_8895: .byte "." ; 8895 '.'. Also the last entry (ordinal 49) of the trainer's end-zone flag array D_8896: .byte '.'|$80 ; 8896 '.' + $80 = the string terminator. Also the trainer's scratch area: the staged command at $8896-$8899 and the 8-way threat/direction ring at $8896-$889D ; msgPickUpPhoneThenPressSpace - 'PICK UP PHONE THEN PRESS SPACE.', 31 bytes. Message id $17, shown ; by hangUpModemSetState ($1B42) through showMessageWaitForSpace on the modem build only (commBuildId ; $E030 negative = trainer, and then the prompt is skipped). This is the voice-pause / hang-up ; direction: the player picks the handset up, then space releases the modem. msgPickUpPhoneThenPressSpace: .byte "P" ; 8897 D_8898: .byte "I" ; 8898 'I'. Also a byte of the trainer's staged command ($8896 opcode, $8897-$8899 arguments) D_8899: .byte "CK UP P" ; 8899 'CK UP P'. $8899 is also the last argument byte of the trainer's staged command D_88A0: .byte "HONE THEN PRESS SPACE",'.'|$80; 88A0 'HONE THEN PRESS SPACE.' - the rest of the string. Also the base of the trainer's 50-byte outgoing order queue ($88A0-$88D1) ; msgPressSpaceWaitHangupPhone - 'PRESS SPACE, WAIT, HANGUP PHONE.', 32 bytes. Message id $18, shown ; by openCommLink ($1B69) before it brings the line up: space puts the modem into data mode, the ; player waits for it to take the line and then puts the handset down. The opposite direction to ; message $17 above - the two are easy to mix up and the earlier survey text had them swapped. msgPressSpaceWaitHangupPhone: .byte "PRESS SPACE, WAIT, HANGUP PH"; 88B6 D_88D2: .byte "ONE",'.'|$80 ; 88D2 'ONE' + $AE. Also the base of the trainer's 12-entry group target-column array ($88D2-$88DD) ; msgOpponentPickingGameMap - 'OPPONENT PICKING GAME/MAP.', 26 bytes. Message id $1A, pinned at ; $7996 in the setup overlay while side 1 waits for side 0's map seed (command $8B/$A6) to arrive; ; cleared at $7C23 as soon as the seed is in. msgOpponentPickingGameMap: .byte "OPPONENT" ; 88D6 D_88DE: .byte " PICKING GAM" ; 88DE ' PICKING GAM'. Also the trainer's 12-entry group target-row array ($88DE-$88E9) D_88EA: .byte "E/MAP",'.'|$80 ; 88EA 'E/MAP' + $AE. Also the trainer's 12-entry group centre-column array ($88EA-$88F5) ; msgOpponentReadyForNewGame - 'OPPONENT READY FOR NEW GAME.', 28 bytes. Message id $1F, queued at ; $56E8 when a session-control packet with reason 2 arrives; the frame counter zp_46 is used as a ; crude rate limit so the announcement cannot repeat more than about every 768 frames. msgOpponentReadyForNewGame: .byte "OPPONE" ; 88F0 D_88F6: .byte "NT READY FOR" ; 88F6 'NT READY FOR'. Also the trainer's 12-entry group centre-row array ($88F6-$8901) D_8902: .byte " NEW GAME",'.'|$80 ; 8902 ' NEW GAME.' - the rest of the string. Also the trainer's 12-entry group member-count array ($8902-$890D), whose last two entries fall on the first two characters of mapNameAlphabet ; mapNameAlphabet - the 32-character alphabet ' ABCDEFGHIJKLMNOPQRSTUVWXYZ+-*/=', one character per ; 5-bit code 0-31. A map's name IS its 24-bit seed written in base 32: enterMapName searches this ; table backwards from index $1F to validate each typed character ($779B), packNameIntoSeed shifts the ; five 5-bit codes into scenarioSeed0/1/2 ($7804) and seedToMapName spells a seed back out through the ; same table ($785D). Five characters x 5 bits = 25 bits, so the top bit of the first character is ; dropped when the name is packed. The manual's map list and the 32 x 3-byte protection table at ; $0401 hold the same numbers, which is why 'IDENTIFY THIS MAP' works and why two players can compare ; a five-letter name to be sure of playing the same battlefield. All three readers live in the setup ; overlay, which is gone before the trainer reuses these bytes. mapNameAlphabet: .byte " A" ; 890C D_890E: .byte "BCDEFGHIJKLM" ; 890E alphabet entries 2-13, 'BCDEFGHIJKLM'. Also the first of the trainer's four 12-entry group formation-span arrays ($890E-$8919) D_891A: .byte "NOPQRSTUVWXY" ; 891A alphabet entries 14-25, 'NOPQRSTUVWXY'. Also the trainer's second group formation-span array ($891A-$8925) D_8926: .byte "Z+-*/=" ; 8926 alphabet entries 26-31, 'Z+-*/='. Also the trainer's third group formation-span array ($8926-$8931) ; gameTypeDefaultsSide0 - the scenario parameter defaults for side 0: five rows of five bytes, ; row-major, read a row at a time by readDefaultAndAdvanceRow ($7CBF), which indexes the row with the ; game-type column in Y and then adds 5 to the pointer. selectSideDefaultsTable ($7CAE) points ; zp_48/zp_49 here for side 0 and at $8945 for side 1; applyGameTypeDefaults ($7C2E) runs the whole ; table twice, once per side, right after both machines have agreed the game type ($79E9). ; ; Column = the game type ($0BA3 bits 0-2) folded to 0-4 by the code at $7C34-$7C44: ; 0 SCRIMAGE 1 QB SNEAK 2 THE BOMB / FACE-OFF / SLUGGERS 3 FULL WAR 4 DEFENDER ; The same column also indexes setupPhaseModeTable ($7C29) -> setupPhaseMode $92C6. ; Row 0 -> terrainPointsBySide $92AC[absolute side] TERRAIN PTS, 0 disables territory scoring ; Row 1 -> recyclerMode $92A6[absolute side] 0 FULL, 1 HALF, 2 NONE ; Row 2 -> comcenSpeedClass $92B4[relative side] COMCEN SPEED setting 0-3, mapped to engine ; class 4/0/1/5 at $7B6E; 0 = comcen immobile ; Row 3 -> dronesLeft $92AE[relative side] number of drones ; Row 4 -> missilesLeft $92B0[relative side] number of missiles ; (rows 0-1 are stored under the absolute side number, rows 2-4 under 0 = this machine, 1 = the ; opponent). Side 0's full table: ; SCRIMAGE QB SNEAK BOMB/FACE-OFF/SLUG FULL WAR DEFENDER ; TERRAIN PTS 1 0 1 1 1 ; RECYCLER NONE NONE NONE HALF NONE ; COMCEN SPEED 2 3 2 1 0 ; DRONES 0 0 3 5 2 ; MISSILES 0 8 12 20 20 ; The rows are chopped up below because four trainer tables start in the middle of them. gameTypeDefaultsSide0: .byte $01,$00,$01,$01,$01 ; 892C ..... row 0, TERRAIN PTS by game type -> $92AC[0] .byte $02 ; 8931 . row 1 column 0: RECYCLER for SCRIMAGE = 2 (NONE) D_8932: .byte $02,$02,$01,$02 ; 8932 .... row 1 columns 1-4: RECYCLER = NONE, NONE, HALF, NONE -> $92A6[0]. Also the trainer's fourth 12-entry group formation-span array ($8932-$893D) .byte $02,$03,$02,$01,$00 ; 8936 ..... row 2, COMCEN SPEED setting 0-3 -> $92B4; QB SNEAK gives side 0 the fastest comcen (3) and DEFENDER an immobile one (0) .byte $00,$00,$03 ; 893B ... row 3 columns 0-2: DRONES = 0, 0, 3 D_893E: .byte $05,$02 ; 893E .. row 3 columns 3-4: DRONES = 5 (FULL WAR), 2 (DEFENDER) -> $92AE. Also the base of the trainer's 12-entry group flag / start-delay array ($893E-$8949) .byte $00,$08,$0C,$14,$14 ; 8940 ..... row 4, MISSILES 0, 8, 12, 20, 20 -> $92B0 ; gameTypeDefaultsSide1 - the same 5 x 5 table for side 1; same rows, same columns, same readers ; (selectSideDefaultsTable loads $8945 when X = 1). Values: ; SCRIMAGE QB SNEAK BOMB/FACE-OFF/SLUG FULL WAR DEFENDER ; TERRAIN PTS 1 1 1 1 0 ; RECYCLER NONE NONE NONE HALF HALF ; COMCEN SPEED 2 2 2 1 1 ; DRONES 0 0 3 5 5 ; MISSILES 0 8 12 20 8 ; Two columns are asymmetric, not one: QB SNEAK (side 0 gets no terrain point and a speed-3 comcen, ; side 1 one terrain point and a speed-2 comcen - the sneaking side is the one that has to move fast) ; and DEFENDER (side 0: immobile comcen, no recycler, 2 drones and 20 missiles; side 1: a moving ; comcen, a HALF recycler, 5 drones and 8 missiles - so side 0 is probably the dug-in defender and ; side 1 the attacker). The earlier survey text called DEFENDER the only asymmetric column, which the ; bytes disprove. ; ; This table is also the source of the trainer's oddest bug. buildDirectionPreference ($E7E3) reads ; its eight compass offsets from $894A, but nothing in the whole disassembly ever writes ; $894A-$8951: at run time they still hold row 1 and the first three bytes of row 2 of this table, ; $02,$02,$02,$01,$01,$02,$02,$02, so all eight 'candidate headings' collapse to heading+1 and ; heading+2 and the AI never spirals its search outwards. $897A (the comcen recall threshold) is the ; other byte the resident AI only reads, but that one is initialised to 5 by the track-29 $EC00 setup ; overlay at $EC44, so only $894A is really left holding message-block data. gameTypeDefaultsSide1: .byte $01,$01,$01,$01 ; 8945 .... row 0 columns 0-3: TERRAIN PTS = 1, 1, 1, 1 D_8949: .byte $00 ; 8949 . row 0 column 4: DEFENDER TERRAIN PTS = 0 -> $92AC[1]. Also the last entry of the trainer's group flag array D_894A: .byte $02,$02,$02,$01,$01 ; 894A ..... row 1, RECYCLER = NONE, NONE, NONE, HALF, HALF -> $92A6[1]. $894A-$8951 (this row plus the first three bytes of the next) is also the trainer's 8-entry heading search table, which nothing ever writes - see the block comment above .byte $02,$02,$02 ; 894F ... row 2 columns 0-2: COMCEN SPEED = 2, 2, 2 D_8952: .byte $01 ; 8952 row 2 column 3 (FULL WAR): COMCEN SPEED 1. Trainer scalar: length of the outgoing order queue at $88A0 D_8953: .byte $01 ; 8953 row 2 column 4 (DEFENDER): COMCEN SPEED 1 -> $92B4[1]. Trainer scalar: highest group id allocated so far D_8954: .byte $00 ; 8954 row 3 column 0 (SCRIMAGE): DRONES 0. Trainer scalar: number of groups D_8955: .byte $00 ; 8955 row 3 column 1 (QB SNEAK): DRONES 0. Trainer scalar: id of the group currently being planned D_8956: .byte $03 ; 8956 row 3 column 2 (BOMB/FACE-OFF/SLUGGERS): DRONES 3. Trainer scalar: low byte of that group's combat value D_8957: .byte $05 ; 8957 row 3 column 3 (FULL WAR): DRONES 5. Trainer scalar: high byte of the group combat value D_8958: .byte $05 ; 8958 row 3 column 4 (DEFENDER): DRONES 5 -> $92AE[1]. Trainer scalar: the clamped column delta of a group's target .byte $00 ; 8959 . row 4 column 0 (SCRIMAGE): MISSILES 0. No label here because the trainer never touches this byte D_895A: .byte $08 ; 895A row 4 column 1 (QB SNEAK): MISSILES 8. Trainer scalar: 'this group contains slow units' flag D_895B: .byte $0C,$14 ; 895B .. row 4 columns 2-3: MISSILES 12 and 20. $895B is also the trainer's desired advance distance; $895C is another byte the trainer never touches D_895D: .byte $08 ; 895D row 4 column 4 (DEFENDER): MISSILES 8 -> $92B0[1]. Trainer scalar: the group its comcen escorts ; msgOpponentUsingNonStandardGame - 'OPPONENT USING NON-STANDARD GAME.', 33 bytes, the twelfth and ; last string of the block. It has no message id: checkOpponentVersion loads its address from two ; immediates (LDA #$5E / LDY #$89 at $79DF) and hands it to showModemSetupPrompt, which shows it in ; slot $20 above 'PRESS SPACE WHEN READY.' ($8837). It appears when the build stamp exchanged by ; exchangePlayerNamesAndSettings comes back different from the $DB stored at $79CC - i.e. the two ; players are running different builds, or one of them a cracked/patched disk. ; ; The listing below breaks into one byte per line because the trainer's scalar variables sit on top of ; the text, one label per byte. The ASCII in the address column still spells the message out. As ; documented in game/trainerAiE000 / game/trainerSetupEC00 - a few of them only at medium confidence ; there - they are: ; $895E/$895F centre column/row of the group the AI comcen escorts ; $8960 best heading $8961 enemy proximity $8962 threat score $8963 0 attack / $FF defend ; $8964 home-defence group $8965 comcen action budget $8966 skill/play level, ; also reused as scratch for group flags and a per-round budget ; $8967 comcen action budget (repair path) $8968 orders issued this round ; $8969/$896A rally point column/row $896B rally refresh counter (probably) ; $896C 'the human comcen has been seen' ; $896D replan urgency $896E alert clock $896F drone heading ; $8970 missile reload timer $8971 comcen damage $8972 unit currently shooting the comcen ; $8973 drone fuel $8974/$8975 drone column/row $8976 drone target unit ; $8977 drone-overlay state $8978 drone detonate threshold $8979 strike score threshold ; $897A comcen recall threshold (set to 5 by $EC44; the resident AI only reads it) ; $897B comcen hit $897C comcen hit recently / comcen is moving msgOpponentUsingNonStandardGame: .byte "O" ; 895E D_895F: .byte $50 ; 895F P D_8960: .byte $50 ; 8960 P D_8961: .byte $4F ; 8961 O D_8962: .byte $4E ; 8962 N D_8963: .byte $45 ; 8963 E D_8964: .byte $4E ; 8964 N D_8965: .byte $54 ; 8965 T D_8966: .byte $20 ; 8966 D_8967: .byte $55 ; 8967 U D_8968: .byte $53 ; 8968 S D_8969: .byte $49 ; 8969 I D_896A: .byte $4E ; 896A N D_896B: .byte $47 ; 896B G D_896C: .byte $20 ; 896C D_896D: .byte $4E ; 896D N D_896E: .byte $4F ; 896E O D_896F: .byte $4E ; 896F N D_8970: .byte $2D ; 8970 - D_8971: .byte $53 ; 8971 S D_8972: .byte $54 ; 8972 T D_8973: .byte $41 ; 8973 A D_8974: .byte $4E ; 8974 N D_8975: .byte $44 ; 8975 D D_8976: .byte $41 ; 8976 A D_8977: .byte $52 ; 8977 R D_8978: .byte $44 ; 8978 D D_8979: .byte $20 ; 8979 D_897A: .byte $47 ; 897A G D_897B: .byte $41 ; 897B A D_897C: .byte $4D ; 897C M .byte "E",'.'|$80 ; 897D 'E' + $AE terminator - the last two bytes of the mastered message data, and the first two bytes of the block that the solo trainer's work area (which ends at $897C) does not overwrite ; messageBlockTail - 129 unreferenced bytes, the rest of track 29 sector 10 after the last string. No ; code anywhere reads or writes them, and they are re-loaded from disk with the block every time. They ; are not random: from $8980 the bytes are 8-byte groups of alternating $00/$FF, i.e. exactly what a ; 50-percent horizontal-stripe dither looks like in a C64 bitmap laid out in 8-byte character cells, ; with the phase inverting every 16 bytes (every two cells) and a dozen stray bytes ($03, ; $F3, $BF, $0C, $DC, $B3, $3F, $C0, $CC, $01, $DF) that look like the edge of a shape drawn over ; it. It is rendered as bit art below so the pattern can be seen. Probably a fragment of screen ; memory left in the mastering machine's buffer when the sector was written; the same kind of dither ; (different bytes - the two do not match) ends the $9300 data block at $9FD1. The pattern occurs ; nowhere else on the disk. messageBlockTail: .byte $00 ; 897F . $00 - odd byte before the first whole character cell; the mastered message data ended at $897E ; 16 consecutive 8-byte character cells of dither, unused .byte $00 ; 8980 ........ .byte $FF ; 8981 ######## .byte $00 ; 8982 ........ .byte $FF ; 8983 ######## .byte $03 ; 8984 ......## .byte $F3 ; 8985 ####..## .byte $00 ; 8986 ........ .byte $BF ; 8987 #.###### .byte $00 ; 8988 ........ .byte $FF ; 8989 ######## .byte $00 ; 898A ........ .byte $FF ; 898B ######## .byte $00 ; 898C ........ .byte $FF ; 898D ######## .byte $00 ; 898E ........ .byte $FF ; 898F ######## .byte $FF ; 8990 ######## .byte $0C ; 8991 ....##.. .byte $FF ; 8992 ######## .byte $0C ; 8993 ....##.. .byte $DC ; 8994 ##.###.. .byte $00 ; 8995 ........ .byte $BF ; 8996 #.###### .byte $80 ; 8997 #....... .byte $FF ; 8998 ######## .byte $00 ; 8999 ........ .byte $FF ; 899A ######## .byte $00 ; 899B ........ .byte $FF ; 899C ######## .byte $00 ; 899D ........ .byte $FF ; 899E ######## .byte $00 ; 899F ........ .byte $00 ; 89A0 ........ .byte $B3 ; 89A1 #.##..## .byte $00 ; 89A2 ........ .byte $FF ; 89A3 ######## .byte $00 ; 89A4 ........ .byte $FF ; 89A5 ######## .byte $80 ; 89A6 #....... .byte $FF ; 89A7 ######## .byte $00 ; 89A8 ........ .byte $FF ; 89A9 ######## .byte $00 ; 89AA ........ .byte $FF ; 89AB ######## .byte $00 ; 89AC ........ .byte $3F ; 89AD ..###### .byte $00 ; 89AE ........ .byte $FF ; 89AF ######## .byte $FF ; 89B0 ######## .byte $0C ; 89B1 ....##.. .byte $FF ; 89B2 ######## .byte $00 ; 89B3 ........ .byte $FF ; 89B4 ######## .byte $00 ; 89B5 ........ .byte $FF ; 89B6 ######## .byte $00 ; 89B7 ........ .byte $FF ; 89B8 ######## .byte $00 ; 89B9 ........ .byte $FF ; 89BA ######## .byte $00 ; 89BB ........ .byte $FF ; 89BC ######## .byte $00 ; 89BD ........ .byte $FF ; 89BE ######## .byte $00 ; 89BF ........ .byte $00 ; 89C0 ........ .byte $FF ; 89C1 ######## .byte $00 ; 89C2 ........ .byte $FF ; 89C3 ######## .byte $00 ; 89C4 ........ .byte $FF ; 89C5 ######## .byte $00 ; 89C6 ........ .byte $3F ; 89C7 ..###### .byte $00 ; 89C8 ........ .byte $FF ; 89C9 ######## .byte $00 ; 89CA ........ .byte $FF ; 89CB ######## .byte $00 ; 89CC ........ .byte $FF ; 89CD ######## .byte $00 ; 89CE ........ .byte $FF ; 89CF ######## .byte $FF ; 89D0 ######## .byte $00 ; 89D1 ........ .byte $FF ; 89D2 ######## .byte $00 ; 89D3 ........ .byte $FF ; 89D4 ######## .byte $C0 ; 89D5 ##...... .byte $FF ; 89D6 ######## .byte $C0 ; 89D7 ##...... .byte $FF ; 89D8 ######## .byte $00 ; 89D9 ........ .byte $FF ; 89DA ######## .byte $00 ; 89DB ........ .byte $FF ; 89DC ######## .byte $00 ; 89DD ........ .byte $FF ; 89DE ######## .byte $CC ; 89DF ##..##.. .byte $00 ; 89E0 ........ .byte $FF ; 89E1 ######## .byte $00 ; 89E2 ........ .byte $FF ; 89E3 ######## .byte $00 ; 89E4 ........ .byte $FF ; 89E5 ######## .byte $01 ; 89E6 .......# .byte $3F ; 89E7 ..###### .byte $00 ; 89E8 ........ .byte $FF ; 89E9 ######## .byte $00 ; 89EA ........ .byte $FF ; 89EB ######## .byte $00 ; 89EC ........ .byte $FF ; 89ED ######## .byte $00 ; 89EE ........ .byte $FF ; 89EF ######## .byte $FF ; 89F0 ######## .byte $C0 ; 89F1 ##...... .byte $FF ; 89F2 ######## .byte $C0 ; 89F3 ##...... .byte $FF ; 89F4 ######## .byte $C0 ; 89F5 ##...... .byte $DF ; 89F6 ##.##### .byte $00 ; 89F7 ........ .byte $FF ; 89F8 ######## .byte $00 ; 89F9 ........ .byte $FF ; 89FA ######## .byte $00 ; 89FB ........ .byte $FF ; 89FC ######## .byte $00 ; 89FD ........ .byte $FF ; 89FE ######## .byte $00 ; 89FF ........