{ "_comment": "generated by tools/mergeSurvey.py from survey/*.json - do not edit by hand", "notes": { "0400": { "unit": "game/scratchAndMessages0400", "block": [ "checksumXorConstant: byteTable, 1 bytes. $B0 - the constant that verifyProgramChecksum XORs into the final sum before storing it at $0B7E. Excluded from the checksummed range (which starts at $0402)." ] }, "0401": { "unit": "game/scratchAndMessages0400", "block": [ "mapNameSeedTable: byteTable, 96 bytes. 32 x 3 bytes used by the manual look-up protection at $09A6: the random seed ($09C3, the immediate operand of the LDA at $09C2) AND $1F picks an entry, the triple is copied to $0B95-$0B97, generateBattlefieldMap draws that map and the player must type its name from the manual; the typed name is packed into zp_5A-zp_5C and compared with the same three bytes. So the triple is both the map generator seed and the expected answer. Also the first byte checksummed is $0402, so entry 0's first byte is outside the sum." ] }, "04AE": { "unit": "game/scratchAndMessages0400", "block": [ "scratchPageTail: unknown, 82 bytes. $00 filler on disk; at run time part of the shared $0400 scratch page (tile buffer at $04C8-$04E7, statistics accumulators, blob stacks)." ] }, "0500": { "unit": "game/scratchAndMessages0400", "block": [ "messagePtrLoTable: byteTable, 36 bytes. Low bytes of the 36 status-message string pointers, indexed by message id $00-$23; read at $C536/$C5E5 and patched at run time by sub_C924 (ids $1E, $20, $21 are $FFFF placeholders on disk)." ] }, "0524": { "unit": "game/scratchAndMessages0400", "block": [ "messagePtrHiTable: byteTable, 36 bytes. High bytes of messagePtrLoTable. Ids resolve to strings in this file ($0548-$0615), in overlay B ($8707-$87F5) and in the loaded message block ($8858-$88FF)." ] }, "0548": { "unit": "game/scratchAndMessages0400", "block": [ "chatMessageLine: text, 36 bytes. Message id $00: the chat/typed line. $0548 is a leading marker glyph ($1E cursor; $9E by sub_C62A to terminate immediately = blank line), $0549-$056B hold up to 35 characters mirrored from $9253 with bit 7 set on the last one. On disk it is 35 spaces plus $A0." ] }, "056C": { "unit": "game/scratchAndMessages0400", "block": [ "msgWorking: text, 10 bytes. Message id $01 'WORKING...' (bit-7 terminated), sound $80 = silent." ] }, "0576": { "unit": "game/scratchAndMessages0400", "block": [ "msgGameAborted: text, 13 bytes. Message id $03 'GAME ABORTED.', sound $03." ] }, "0583": { "unit": "game/scratchAndMessages0400", "block": [ "msgNotAGameFilm: text, 16 bytes. Message id $09 'NOT A GAME FILM!', sound $03 (film load rejected)." ] }, "0593": { "unit": "game/scratchAndMessages0400", "block": [ "msgSetupUnitsPressF1WhenDone: text, 33 bytes. Message id $0A 'SETUP UNITS. PRESS F 1 WHEN DONE.', silent." ] }, "05B4": { "unit": "game/scratchAndMessages0400", "block": [ "msgTimeoutRunStopToResume: text, 28 bytes. Message id $19 'TIMEOUT. RUN/STOP TO RESUME.', sound $19." ] }, "05D0": { "unit": "game/scratchAndMessages0400", "block": [ "msgResumed: text, 7 bytes. Message id $1B 'RESUMED', sound $19." ] }, "05D7": { "unit": "game/scratchAndMessages0400", "block": [ "msgTheBattleBegins: text, 18 bytes. Message id $1C 'THE BATTLE BEGINS!', sound $03." ] }, "05E9": { "unit": "game/scratchAndMessages0400", "block": [ "msgLastQtrWarning: text, 17 bytes. Message id $1D 'LAST QTR WARNING!', sound $19 (the game is played in quarters)." ] }, "05FA": { "unit": "game/scratchAndMessages0400", "block": [ "msgPhoneTrouble: text, 14 bytes. Message id $22 'PHONE TROUBLE.', sound $15 (modem link failure)." ] }, "0608": { "unit": "game/scratchAndMessages0400", "block": [ "msgTryAgain: text, 10 bytes. Message id $23 'TRY AGAIN.', sound $03." ] }, "0612": { "unit": "game/scratchAndMessages0400", "block": [ "messageSoundTable: byteTable, 36 bytes. Sound effect number played when message id X is displayed (read at $C557, played through sub_C89A); $80 = no sound. Notable: $0E for the two 'we lost' messages, $0D for the two 'we won' messages, $19 for GAME OVER / TIMEOUT / RESUMED / LAST QTR WARNING, $15 for PHONE TROUBLE." ] }, "0636": { "unit": "game/scratchAndMessages0400", "block": [ "optionSummaryTemplate: text, 23 bytes. ' MENUS XXXX DAMAGE XXX.' (bit-7 terminated) - a rules summary line whose XXXX/XXX fields are overwritten in place by overlay A/B ($870C, $8739-$8751) with OFF,/ON, and FULL/HALF/NONE before it is displayed." ] }, "064D": { "unit": "game/scratchAndMessages0400", "block": [ "optionWordTable: text, 17 bytes. Five bit-7 terminated option words at fixed offsets used to fill optionSummaryTemplate: +0 'OFF', +3 'ON', +5 'FULL', +9 'HALF', +13 'NONE'. XREF.txt calls this label eaCopyrightMessage, which is wrong." ] }, "065E": { "unit": "game/scratchAndMessages0400", "block": [ "msgClicks: text, 10 bytes. ' CLICKS:' - the score header printed by $C6BC (lda #$5E / ldy #$06 / jsr sub_C133). 'Clicks' are Modem Wars' score units." ] }, "0668": { "unit": "game/scratchAndMessages0400", "block": [ "msgUs: text, 7 bytes. ' US:' - printed straight after msgClicks by sub_C137, followed by the local player's 16-bit score from $92A2/$92A4." ] }, "066F": { "unit": "game/scratchAndMessages0400", "block": [ "msgThem: text, 8 bytes. ' THEM:' - the opponent's score label, printed by the same $C6B6 routine." ] }, "0677": { "unit": "game/scratchAndMessages0400", "block": [ "msgTypeMapName: text, 40 bytes. Three lines separated by $0D: 'TYPE MAP NAME' / ' (F7 RANDOM)' / ' (F5 SYM/RND)' - the prompt of enterMapName (also used by the manual look-up protection at $09EF)." ] }, "069F": { "unit": "game/scratchAndMessages0400", "block": [ "msgBtnToAccept: text, 27 bytes. Two lines separated by $0D: 'BTN TO ACCEPT' / '(F 1 TO FLIP)' - shown while the generated map is offered for acceptance." ] }, "0770": { "unit": "game/scratchAndMessages0400", "block": [ "tileVariantMaskTable: byteTable, 40 bytes. Indexed by map cell value minus $40 (cells below $40 use cell-$20), covering cells $40-$67. The value is a mask ANDed with a scenario random number and added to the base tile by randomTileVariant ($7872, overlay A): cells $49 and $4B and $65 -> mask 1, cells $4D, $51, $59, $5D, $61 -> mask 3, everything else 0 (no variation)." ] }, "0798": { "unit": "game/scratchAndMessages0400", "block": [ "tileVariantPad: unknown, 1 bytes. $00 pad between tileVariantMaskTable and unitGlyphBaseTable." ] }, "0799": { "unit": "game/scratchAndMessages0400", "block": [ "unitGlyphBaseTable: byteTable, 5 bytes. Base glyph index of the 1-character overview-map symbol per unit type: $2C GRUNT, $30 RIDER, $34 BOOMER, $38 SPY, $3C COMCEN. The facing (unit flags AND 3) is added, then $2C is subtracted and the result doubled to index the glyph pairs ($28A0, $7512, $7538)." ] }, "079F": { "unit": "game/scratchAndMessages0400", "block": [ "terrainClassTable: byteTable, 75 bytes. Map cell value -> terrain class 0-8. It is addressed as D_077E,y with the raw cell value, so the body starts at $077E+$21 = $079F and ends at $077E+$6B = $07E9: cells $21-$34 -> 3 (lower contour), $35-$3F -> $FF (invalid), $40 -> 0 (blank), $41-$54 -> 5 (upper contour), $55-$5C -> 1 (river), $5D-$60 -> 2 (forest), $61-$64 -> 4 (lower hill), $65-$66 -> 6 (summit), $67 -> 7 (recycler), $68-$6B -> 8. The same bytes are also read as D_07BE,x by $5BC8 with the index already reduced to cell-$40 (clamped to < $21)." ] }, "07EA": { "unit": "game/scratchAndMessages0400", "block": [ "terrainColourTable: byteTable, 9 bytes. Colour-RAM nibble per terrain class 0-8: 7,3,5,8,8,8,8,0,0 (yellow plain, cyan river, green forest, orange contours/hills/summit, black for classes 7-8). Read at $27AB and $5BCB right after a terrainClassTable lookup." ] }, "07F3": { "unit": "game/scratchAndMessages0400", "block": [ "table0400Tail: unknown, 13 bytes. $00 filler to the end of the page." ] }, "0461": { "routine": [ "verifyProgramChecksum - Boot-time integrity check, entered by the boot loader's obfuscated RTS (the stack was rigged to hold $0460). Saves randomSeed ($09C3) in zp_19 and zeroes it so the sum is seed independent, then runs a self-modifying ADC/EOR/ROL checksum over $0402-$87FF (132 pages; the operand high bytes at $047C and $0484 are incremented per page and end at $88), adds the carry counter zp_1B, stores the sum at $8800, stores sum EOR $0400 ($B0) in programChecksum ($0B7E), restores randomSeed and does JMP $0800 with Y=2. The ADC carry is deliberately chained from one word to the next (the CLC at $0479 is outside the loop) and the PHP/ASL/BCC/ORA/PLP block is a ROL that preserves that carry.", "In: memory $0402-$87FF, $09C3 (random seed, zeroed during the sum), $0400 = $B0", "Out: $8800 = raw sum ($69 on this disk image), $0B7E = sum EOR $B0 ($D9), $09C3 restored, zp_19/zp_1A/zp_1B clobbered, $047C/$0484 left at $88, JMP $0800 with Y=2 and S=$FE" ], "unit": "game/scratchAndMessages0400" }, "06BA": { "routine": [ "switchToRequestedScreen - Called from every screen's main loop to honour an F1/F3/F5/F7 screen change. Returns immediately when the chat editor is active (D_929C != 0), when zp_66 has bit 7 set (no request) or when the request equals the current screen. Otherwise it makes the request current, resets the stack to $FD (so it never returns to the caller), turns all sprites off (VIC_SPR_ENA and the shadows $9020/$9022/$9023/$9024), clears $92F9 unless $0B9B bit 7 is set, redraws the bitmap frame and jumps to the new screen's entry point.", "In: zp_66 (requested screen 0-3, $FF = none), D_90FB (current screen), D_929C (chat state), D_0B9B bit 7", "Out: D_90FB updated, sprites disabled, S=$FD, never returns: JMPs to enterMapScreen (0, also sets $9020=$C4), runMissileScreen (1), enterDroneScreen (2) or enterMainMenu (3)" ], "unit": "game/scratchAndMessages0400" }, "0709": { "routine": [ "runComcenStunnedBlackout - The 'COMCEN STUNNED!' cut-scene, entered by JMP from $4546 when the command centre status byte D_922A has reached $FF and the game is not in solo/film mode. It releases drone control, wipes the message line and screen, resets the stack, then runs the engine unattended (updateGameFrame) with input locked out and D_922A bit 6 set (which keeps sprites off) until unitOrderIndex ($9162) comes back to the value it had on entry, i.e. exactly one full 100-unit update round. It then queues message $0C ('COMCEN STUNNED!'), clears the lockout, masks D_922A with $8F and re-enters whichever screen was active.", "In: D_9162 (captured into the CMP operand at $073E), D_922A, D_90FB (current screen), D_92C9 and D_0BA8 (abort conditions), D_90FC (per-frame gate)", "Out: one blacked-out engine round; inputLockoutTimer set then cleared; D_922A bits 4-6 cleared; message $0C queued via sub_C566; never returns: jumps to runMapMainLoop (after drawOverviewMap/drawViewport), missileScreenResume, resetDroneScreen or enterMainMenuKeepSelection", "(confidence: medium)" ], "unit": "game/scratchAndMessages0400" } } }