633 lines
25 KiB
JSON
633 lines
25 KiB
JSON
{
|
|
"chunk": "game_messages8800_8800",
|
|
"unit": "game/messages8800",
|
|
"unitLabels": {
|
|
"game/messages8800": {
|
|
"8800": "msgGetOpponentOnPhoneAnd",
|
|
"881C": "msgPressAOrOAndSetModem",
|
|
"8837": "msgPressSpaceWhenReady",
|
|
"884E": "msgTrainerName",
|
|
"8858": "msgOpponentReady",
|
|
"8867": "msgWaitingForOpponent",
|
|
"887E": "msgWaitingForConnection",
|
|
"8897": "msgPickUpPhoneThenPressSpace",
|
|
"88B6": "msgPressSpaceWaitHangupPhone",
|
|
"88D6": "msgOpponentPickingGameMap",
|
|
"88F0": "msgOpponentReadyForNewGame",
|
|
"890C": "mapNameAlphabet",
|
|
"892C": "gameTypeDefaultsSide0",
|
|
"8945": "gameTypeDefaultsSide1",
|
|
"895E": "msgOpponentUsingNonStandardGame",
|
|
"897F": "messageBlockTail"
|
|
}
|
|
},
|
|
"notes": {
|
|
"8800": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"$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."
|
|
]
|
|
},
|
|
"881C": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
]
|
|
},
|
|
"8831": {
|
|
"unit": "game/messages8800",
|
|
"line": "'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"
|
|
},
|
|
"8832": {
|
|
"unit": "game/messages8800",
|
|
"line": "'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"
|
|
},
|
|
"8837": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
]
|
|
},
|
|
"884E": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
]
|
|
},
|
|
"8858": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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)."
|
|
]
|
|
},
|
|
"8863": {
|
|
"unit": "game/messages8800",
|
|
"line": "'A'. Also the last entry (ordinal 49, the AI COMCEN) of the trainer's group-id array"
|
|
},
|
|
"8864": {
|
|
"unit": "game/messages8800",
|
|
"line": "'DY' + $AE. Also the base of the trainer's 50-byte 'reached the enemy end zone' flags ($8864-$8895)"
|
|
},
|
|
"8867": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
]
|
|
},
|
|
"887E": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
]
|
|
},
|
|
"8895": {
|
|
"unit": "game/messages8800",
|
|
"line": "'.'. Also the last entry (ordinal 49) of the trainer's end-zone flag array"
|
|
},
|
|
"8896": {
|
|
"unit": "game/messages8800",
|
|
"line": "'.' + $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"
|
|
},
|
|
"8897": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
]
|
|
},
|
|
"8898": {
|
|
"unit": "game/messages8800",
|
|
"line": "'I'. Also a byte of the trainer's staged command ($8896 opcode, $8897-$8899 arguments)"
|
|
},
|
|
"8899": {
|
|
"unit": "game/messages8800",
|
|
"line": "'CK UP P'. $8899 is also the last argument byte of the trainer's staged command"
|
|
},
|
|
"88A0": {
|
|
"unit": "game/messages8800",
|
|
"line": "'HONE THEN PRESS SPACE.' - the rest of the string. Also the base of the trainer's 50-byte outgoing order queue ($88A0-$88D1)"
|
|
},
|
|
"88B6": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
]
|
|
},
|
|
"88D2": {
|
|
"unit": "game/messages8800",
|
|
"line": "'ONE' + $AE. Also the base of the trainer's 12-entry group target-column array ($88D2-$88DD)"
|
|
},
|
|
"88D6": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
]
|
|
},
|
|
"88DE": {
|
|
"unit": "game/messages8800",
|
|
"line": "' PICKING GAM'. Also the trainer's 12-entry group target-row array ($88DE-$88E9)"
|
|
},
|
|
"88EA": {
|
|
"unit": "game/messages8800",
|
|
"line": "'E/MAP' + $AE. Also the trainer's 12-entry group centre-column array ($88EA-$88F5)"
|
|
},
|
|
"88F0": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
]
|
|
},
|
|
"88F6": {
|
|
"unit": "game/messages8800",
|
|
"line": "'NT READY FOR'. Also the trainer's 12-entry group centre-row array ($88F6-$8901)"
|
|
},
|
|
"8902": {
|
|
"unit": "game/messages8800",
|
|
"line": "' 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"
|
|
},
|
|
"890C": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
]
|
|
},
|
|
"890E": {
|
|
"unit": "game/messages8800",
|
|
"line": "alphabet entries 2-13, 'BCDEFGHIJKLM'. Also the first of the trainer's four 12-entry group formation-span arrays ($890E-$8919)"
|
|
},
|
|
"891A": {
|
|
"unit": "game/messages8800",
|
|
"line": "alphabet entries 14-25, 'NOPQRSTUVWXY'. Also the trainer's second group formation-span array ($891A-$8925)"
|
|
},
|
|
"8926": {
|
|
"unit": "game/messages8800",
|
|
"line": "alphabet entries 26-31, 'Z+-*/='. Also the trainer's third group formation-span array ($8926-$8931)"
|
|
},
|
|
"892C": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
],
|
|
"line": "row 0, TERRAIN PTS by game type -> $92AC[0]"
|
|
},
|
|
"8931": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 1 column 0: RECYCLER for SCRIMAGE = 2 (NONE)"
|
|
},
|
|
"8932": {
|
|
"unit": "game/messages8800",
|
|
"line": "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)"
|
|
},
|
|
"8936": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 2, COMCEN SPEED setting 0-3 -> $92B4; QB SNEAK gives side 0 the fastest comcen (3) and DEFENDER an immobile one (0)"
|
|
},
|
|
"893B": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 3 columns 0-2: DRONES = 0, 0, 3"
|
|
},
|
|
"893E": {
|
|
"unit": "game/messages8800",
|
|
"line": "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)"
|
|
},
|
|
"8940": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 4, MISSILES 0, 8, 12, 20, 20 -> $92B0"
|
|
},
|
|
"8945": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
],
|
|
"line": "row 0 columns 0-3: TERRAIN PTS = 1, 1, 1, 1"
|
|
},
|
|
"8949": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 0 column 4: DEFENDER TERRAIN PTS = 0 -> $92AC[1]. Also the last entry of the trainer's group flag array"
|
|
},
|
|
"894A": {
|
|
"unit": "game/messages8800",
|
|
"line": "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"
|
|
},
|
|
"894F": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 2 columns 0-2: COMCEN SPEED = 2, 2, 2"
|
|
},
|
|
"8952": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 2 column 3 (FULL WAR): COMCEN SPEED 1. Trainer scalar: length of the outgoing order queue at $88A0"
|
|
},
|
|
"8953": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 2 column 4 (DEFENDER): COMCEN SPEED 1 -> $92B4[1]. Trainer scalar: highest group id allocated so far"
|
|
},
|
|
"8954": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 3 column 0 (SCRIMAGE): DRONES 0. Trainer scalar: number of groups"
|
|
},
|
|
"8955": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 3 column 1 (QB SNEAK): DRONES 0. Trainer scalar: id of the group currently being planned"
|
|
},
|
|
"8956": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 3 column 2 (BOMB/FACE-OFF/SLUGGERS): DRONES 3. Trainer scalar: low byte of that group's combat value"
|
|
},
|
|
"8957": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 3 column 3 (FULL WAR): DRONES 5. Trainer scalar: high byte of the group combat value"
|
|
},
|
|
"8958": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 3 column 4 (DEFENDER): DRONES 5 -> $92AE[1]. Trainer scalar: the clamped column delta of a group's target"
|
|
},
|
|
"8959": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 4 column 0 (SCRIMAGE): MISSILES 0. No label here because the trainer never touches this byte"
|
|
},
|
|
"895A": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 4 column 1 (QB SNEAK): MISSILES 8. Trainer scalar: 'this group contains slow units' flag"
|
|
},
|
|
"895B": {
|
|
"unit": "game/messages8800",
|
|
"line": "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"
|
|
},
|
|
"895D": {
|
|
"unit": "game/messages8800",
|
|
"line": "row 4 column 4 (DEFENDER): MISSILES 8 -> $92B0[1]. Trainer scalar: the group its comcen escorts"
|
|
},
|
|
"895E": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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"
|
|
]
|
|
},
|
|
"897D": {
|
|
"unit": "game/messages8800",
|
|
"line": "'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"
|
|
},
|
|
"897F": {
|
|
"unit": "game/messages8800",
|
|
"block": [
|
|
"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."
|
|
],
|
|
"line": "$00 - odd byte before the first whole character cell; the mastered message data ended at $897E"
|
|
},
|
|
"8980": {
|
|
"unit": "game/messages8800",
|
|
"line": "16 consecutive 8-byte character cells of dither, unused"
|
|
}
|
|
},
|
|
"dataTypes": {
|
|
"8800": [
|
|
"text",
|
|
28
|
|
],
|
|
"881C": [
|
|
"text",
|
|
21
|
|
],
|
|
"8831": [
|
|
"text",
|
|
1
|
|
],
|
|
"8832": [
|
|
"text",
|
|
5
|
|
],
|
|
"8837": [
|
|
"text",
|
|
23
|
|
],
|
|
"884E": [
|
|
"text",
|
|
10
|
|
],
|
|
"8858": [
|
|
"text",
|
|
11
|
|
],
|
|
"8863": [
|
|
"text",
|
|
1
|
|
],
|
|
"8864": [
|
|
"text",
|
|
3
|
|
],
|
|
"8867": [
|
|
"text",
|
|
23
|
|
],
|
|
"887E": [
|
|
"text",
|
|
23
|
|
],
|
|
"8895": [
|
|
"text",
|
|
1
|
|
],
|
|
"8896": [
|
|
"text",
|
|
1
|
|
],
|
|
"8897": [
|
|
"text",
|
|
1
|
|
],
|
|
"8898": [
|
|
"text",
|
|
1
|
|
],
|
|
"8899": [
|
|
"text",
|
|
7
|
|
],
|
|
"88A0": [
|
|
"text",
|
|
22
|
|
],
|
|
"88B6": [
|
|
"text",
|
|
28
|
|
],
|
|
"88D2": [
|
|
"text",
|
|
4
|
|
],
|
|
"88D6": [
|
|
"text",
|
|
8
|
|
],
|
|
"88DE": [
|
|
"text",
|
|
12
|
|
],
|
|
"88EA": [
|
|
"text",
|
|
6
|
|
],
|
|
"88F0": [
|
|
"text",
|
|
6
|
|
],
|
|
"88F6": [
|
|
"text",
|
|
12
|
|
],
|
|
"8902": [
|
|
"text",
|
|
10
|
|
],
|
|
"890C": [
|
|
"text",
|
|
2
|
|
],
|
|
"890E": [
|
|
"text",
|
|
12
|
|
],
|
|
"891A": [
|
|
"text",
|
|
12
|
|
],
|
|
"8926": [
|
|
"text",
|
|
6
|
|
],
|
|
"895E": [
|
|
"text",
|
|
1
|
|
],
|
|
"897D": [
|
|
"text",
|
|
2
|
|
],
|
|
"892C": [
|
|
"grid",
|
|
5,
|
|
5
|
|
],
|
|
"8931": [
|
|
"grid",
|
|
1,
|
|
1
|
|
],
|
|
"8932": [
|
|
"grid",
|
|
4,
|
|
4
|
|
],
|
|
"8936": [
|
|
"grid",
|
|
5,
|
|
5
|
|
],
|
|
"893B": [
|
|
"grid",
|
|
3,
|
|
3
|
|
],
|
|
"893E": [
|
|
"grid",
|
|
2,
|
|
2
|
|
],
|
|
"8940": [
|
|
"grid",
|
|
5,
|
|
5
|
|
],
|
|
"8945": [
|
|
"grid",
|
|
4,
|
|
4
|
|
],
|
|
"8949": [
|
|
"grid",
|
|
1,
|
|
1
|
|
],
|
|
"894A": [
|
|
"grid",
|
|
5,
|
|
5
|
|
],
|
|
"894F": [
|
|
"grid",
|
|
3,
|
|
3
|
|
],
|
|
"8959": [
|
|
"grid",
|
|
1,
|
|
1
|
|
],
|
|
"895B": [
|
|
"grid",
|
|
2,
|
|
2
|
|
],
|
|
"897F": [
|
|
"byte",
|
|
1
|
|
],
|
|
"8980": [
|
|
"bitmap",
|
|
128
|
|
]
|
|
}
|
|
}
|