502 lines
31 KiB
ArmAsm
502 lines
31 KiB
ArmAsm
; ============================================================================
|
|
; $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 ........
|
|
|