{
 "_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"
  }
 }
}