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

