{ "chunk": "data_ovl_8800", "unit": "game/ovl_8800", "routines": [], "variables": [], "dataBlocks": [ { "addr": "8800", "length": 28, "type": "text", "name": "msgGetOpponentOnPhoneAnd", "description": "Bit-7-terminated message text \"GET OPPONENT ON PHONE AND...\". Message slot $20 while a modem game is being set up: main $0B1D patches messagePtrLoTable/HiTable entry $20 to point here (A=$00, Y=$88) right before waitForAnswerOriginateKey." }, { "addr": "881C", "length": 27, "type": "text", "name": "msgPressAOrOAndSetModem", "description": "Bit-7-terminated message text \"PRESS A OR O AND SET MODEM.\". Message slot $21, patched next to it at $0B26 (A=$1C, Y=$88). Tells the player to press A(nswer) or O(riginate)." }, { "addr": "8837", "length": 23, "type": "text", "name": "msgPressSpaceWhenReady", "description": "Bit-7-terminated message text \"PRESS SPACE WHEN READY.\". Message slot $21 set by showModemSetupPrompt ($0C8C, called from overlay A $79E3), which puts the caller's string in slot $20 and this one in slot $21." }, { "addr": "884E", "length": 10, "type": "text", "name": "msgTrainerName", "description": "Bit-7-terminated message text \"TRAINER \". Ten characters, read by exchangePlayerNamesAndSettings ($ED4A in the modem-driver build): in solo-trainer mode ($0BA5 bit 7) the whole name handshake is skipped and this string is copied into opponentName $FFE8 instead." }, { "addr": "8858", "length": 15, "type": "text", "name": "msgOpponentReady", "description": "Bit-7-terminated message text \"OPPONENT READY.\". Message id $04 (messagePtrLoTable[$04]=$58, HiTable[$04]=$88)." }, { "addr": "8867", "length": 23, "type": "text", "name": "msgWaitingForOpponent", "description": "Bit-7-terminated message text \"WAITING FOR OPPONENT...\". Message id $0B." }, { "addr": "887E", "length": 25, "type": "text", "name": "msgWaitingForConnection", "description": "Bit-7-terminated message text \"WAITING FOR CONNECTION...\". Message id $10." }, { "addr": "8897", "length": 31, "type": "text", "name": "msgPickUpPhoneThenPressSpace", "description": "Bit-7-terminated message text \"PICK UP PHONE THEN PRESS SPACE.\". Message id $17." }, { "addr": "88B6", "length": 32, "type": "text", "name": "msgPressSpaceWaitHangupPhone", "description": "Bit-7-terminated message text \"PRESS SPACE, WAIT, HANGUP PHONE.\". Message id $18." }, { "addr": "88D6", "length": 26, "type": "text", "name": "msgOpponentPickingGameMap", "description": "Bit-7-terminated message text \"OPPONENT PICKING GAME/MAP.\". Message id $1A - shown to player 1 while it waits for player 0's map seed." }, { "addr": "88F0", "length": 28, "type": "text", "name": "msgOpponentReadyForNewGame", "description": "Bit-7-terminated message text \"OPPONENT READY FOR NEW GAME.\". Message id $1F." }, { "addr": "895E", "length": 33, "type": "text", "name": "msgOpponentUsingNonStandardGame", "description": "Bit-7-terminated message text \"OPPONENT USING NON-STANDARD GAME.\". Shown when the program-checksum byte exchanged during the identity handshake does not match: overlay A stores the expected value $DB at $79CC, compares it after exchangePlayerNamesAndSettings and, on a mismatch, does LDA #$5E / LDY #$89 / JSR showModemSetupPrompt ($79DF-$79E3), which puts this string in message slot $20 and \"PRESS SPACE WHEN READY.\" ($8837) in slot $21. Because the address is built from two immediates the disassembler produced no reference, which is why XREF shows only trainer writes here." }, { "addr": "890C", "length": 32, "type": "byteTable", "name": "mapNameAlphabet", "description": "The 32-character alphabet ' ABCDEFGHIJKLMNOPQRSTUVWXYZ+-*/=' used to convert between a five-character map name and the 24-bit map seed. The map-name editor in overlay A searches it backwards from index $1F at $779B to validate a typed character, and packs the name at $7804 by shifting zp_5A/5B/5C left five times per character and ORing in the index, so name = seed in base 32 (5 chars x 5 bits). $785D uses the same table to print a seed back as a name. This is also what makes the copy-protection work: the 32 x 3-byte table at $0401 holds seed triples, and the player types the matching name out of the manual." }, { "addr": "892C", "length": 25, "type": "byteTable", "name": "gameTypeDefaultsSide0", "description": "Side 0 half of the game-type parameter defaults: five rows of five bytes, read through selectSideDefaultsTable ($7CAE) and applied by applyGameTypeDefaults ($7C2E). Rows are TERRAIN PTS (-> $92AC), RECYCLER (-> $92A6), COMCEN SPEED (-> $92B4), DRONES (-> $92AE) and MISSILES (-> $92B0); columns are the game type folded to 0-4 (0 SCRIMAGE, 1 QB SNEAK, 2 THE BOMB/FACE-OFF/SLUGGERS, 3 FULL WAR, 4 DEFENDER). Values: 01 00 01 01 01 / 02 02 02 01 02 / 02 03 02 01 00 / 00 00 03 05 02 / 00 08 0C 14 14, i.e. SCRIMAGE has no drones and no missiles while FULL WAR gives 5 drones and 20 missiles." }, { "addr": "8945", "length": 25, "type": "byteTable", "name": "gameTypeDefaultsSide1", "description": "Side 1 half of the same 5x5 table: 01 01 01 01 00 / 02 02 02 01 01 / 02 02 02 01 01 / 00 00 03 05 05 / 00 08 0C 14 08. The only asymmetric column is DEFENDER (4), where side 0 gets 2 drones and 20 missiles (the attacker) and side 1 gets 5 drones and 8 missiles (the defender). Note that the trainer AI's buildDirectionFan ($E7E3) later reads its eight compass offsets from $894A, which is the middle of this table and is never rewritten - see the insight below." }, { "addr": "897F", "length": 129, "type": "unknown", "name": "messageBlockTail", "description": "129 unreferenced bytes to the end of track 29 sector 10. They are a 50 percent dither: pairs of $00/$FF whose phase flips every eight bytes, plus a few stray values ($03,$F3,$BF,$0C,$DC,$B3,$3F,$C0,$CC,$01,$DF). The tail of the $9300 graphics block ($9FD1-$9FFF, track 28 sector 12) contains the same pattern, so both look like the same stale bitmap fragment from the mastering machine rather than game data. (confidence: medium)" } ], "misclassified": [], "insights": [ "$8800 \"GET OPPONENT ON PHONE AND...\"", "$881C \"PRESS A OR O AND SET MODEM.\"", "$8837 \"PRESS SPACE WHEN READY.\"", "$884E \"TRAINER \" (ten characters, the solo opponent's name)", "$8858 \"OPPONENT READY.\"", "$8867 \"WAITING FOR OPPONENT...\"", "$887E \"WAITING FOR CONNECTION...\"", "$8897 \"PICK UP PHONE THEN PRESS SPACE.\"", "$88B6 \"PRESS SPACE, WAIT, HANGUP PHONE.\"", "$88D6 \"OPPONENT PICKING GAME/MAP.\"", "$88F0 \"OPPONENT READY FOR NEW GAME.\"", "$895E \"OPPONENT USING NON-STANDARD GAME.\"", "The 512-byte block from track 29 sectors 9-10 is only about half message strings ($8800-$890B and $895E-$897E, 12 strings, 316 bytes). The middle holds two real tables the strings have nothing to do with - the 32-character map-name alphabet at $890C and the two 5x5 game-type default tables at $892C/$8945 - and the last 129 bytes are stale dither.", "Message ids resolve through the pointer tables at $0500/$0524: ids $04, $0B, $10, $17, $18, $1A and $1F point into this block; ids $20/$21 are $FFFF on disk and are patched at run time, to $8800/$881C by mainProgram $0B1D/$0B26 and later to the modem driver's own strings at $EFxx by $EED6/$EEFF.", "The map name really is the map seed: the five typed characters are packed 5 bits each (index into the $890C alphabet) into zp_5A/5B/5C, which is exactly the 24-bit seed generateBattlefieldMap uses, and exactly what the 32 x 3-byte manual-protection table at $0401 stores.", "Confirms the original bug already suspected in game_ovl_E000_T35_E000: the trainer's buildDirectionFan reads eight direction offsets from $894A, which is row 1 columns 0-4 plus row 2 columns 0-2 of gameTypeDefaultsSide1 (02 02 02 01 01 02 02 02). Nothing ever writes that area, so on a real machine the AI's eight 'direction ranks' are always base+2 or base+1.", "Almost every D_88xx / D_89xx label in messages8800.s and in XREF.txt is NOT a string boundary: they are the addresses the solo trainer writes when it uses this whole page as work RAM after the message block is dead. That is why the disassembler chopped e.g. msgPressAOrOAndSetModem into $881C + D_8831 + D_8832 and msgOpponentUsingNonStandardGame into 30 one-byte labels. The AI meanings of those addresses are documented in game_ovl_E000_T35_* ($8800 replan clock per unit, $8832 group id, $8864 penetration flags, $8896 command staging, $88A0 order queue, $88D2/$88DE/$88EA/$88F6/$8902/$890E/$891A/$8926/$8932/$893E per-group arrays, $8952-$897C scalars).", "$890C is labelled only as a trainer variable in XREF (written at $E5DC); its real, mastered content is the 32-character map-name alphabet read by the map generator at $779B/$7804/$785D." ] }