modemwars/annotations/20_game_comcenScreens6F00_8601.json
2026-08-23 02:09:40 -05:00

878 lines
34 KiB
JSON

{
"chunk": "game_comcenScreens6F00_8601",
"unit": "game/comcenScreens6F00",
"unitLabels": {
"game/comcenScreens6F00": {
"8601": "printNumberRightAligned",
"860B": "countNumberPadChars",
"8615": "emitPaddedNumber",
"8621": "padCountPatch",
"8626": "valueMidPatch",
"8628": "valueLowPatch",
"862D": "setTextColumn",
"8632": "drawComcenSchematic",
"8654": "primeRleRunCounter",
"8656": "rleEmitLoop",
"8666": "rleSaveRunCount",
"866D": "rleStoreByte",
"868B": "schematicDone",
"868C": "strDrone",
"8691": "strLaunch",
"8697": "strStrike",
"869D": "strFire",
"86A1": "strDroneAlertTwoLine",
"86AA": "strDroneAlertEnd",
"86AC": "statsTabLabels",
"86C3": "statsLabelsSpies",
"86FB": "filmViewWordTable",
"8707": "msgEndOfFilm",
"8713": "msgUnitRecycled",
"8721": "msgShowViewTemplate",
"8722": "msgShowViewBody",
"8726": "msgShowViewWord",
"872A": "msgSpeedTemplate",
"872E": "msgSpeedBody",
"8730": "msgSpeedDigit",
"8731": "msgSpottedRecycler",
"8742": "msgComcenStunned",
"8751": "msgSpottedEnemyComcen",
"8756": "msgSpottedComcenBody",
"8766": "msgSpyUnderAttack",
"8777": "msgSpottedEnemySpy",
"8789": "msgWeLostOnPoints",
"8795": "msgWeLostOnPointsEnd",
"879B": "msgWeWonOnPoints",
"87AC": "msgItWasADraw",
"87BA": "msgWeWereKnockedOut",
"87CE": "msgWeWonByAKnockout",
"87E3": "msgGameOver",
"87ED": "msgDroneAlert",
"87F9": "overlayTailPadding",
"87FF": "overlayVariantFlag"
}
},
"dataTypes": {
"868C": [
"text",
5
],
"8691": [
"text",
6
],
"8697": [
"text",
6
],
"869D": [
"text",
4
],
"86A1": [
"text",
11
],
"86AC": [
"text",
79
],
"86FB": [
"grid",
12,
4
],
"8707": [
"text",
12
],
"8713": [
"text",
14
],
"8721": [
"text",
9
],
"872A": [
"text",
7
],
"8731": [
"text",
17
],
"8742": [
"text",
15
],
"8751": [
"text",
21
],
"8766": [
"text",
17
],
"8777": [
"text",
18
],
"8789": [
"text",
18
],
"879B": [
"text",
17
],
"87AC": [
"text",
14
],
"87BA": [
"text",
20
],
"87CE": [
"text",
21
],
"87E3": [
"text",
10
],
"87ED": [
"text",
12
],
"87F9": [
"byte",
6
],
"87FF": [
"byte",
1
]
},
"zpComments": {
"1A": "general scratch: third math temp, right column of a frame box, low byte of the trainer's 16-bit scores; also the 10-row counter of drawComcenSchematic ($8650)",
"1B": "general scratch: bottom row of a frame box, division remainder (lo), max column returned by computeGroupBounds; also holds the RLE repeat count while drawComcenSchematic decodes a run ($8666)",
"7E": "high (third) byte of the 24-bit value passed to printDecimal; the conversion clears it again ($C1DD), which is why printNumberRightAligned ($8601) can print an 8-bit value without ever setting it"
},
"notes": {
"8601": {
"unit": "game/comcenScreens6F00",
"routine": [
"printNumberRightAligned - prints the unsigned byte in A as decimal, right-aligned in a three-character field that starts at text column X of the current text row. It works out how many blank glyphs have to go in front (2 for 0-9, 1 for 10-99, none for 100-255), patches that count and the value into three immediate operands further down ($8622, $8627, $8629), positions the text cursor and then prints padding + number through the resident $C000 text engine.",
"Only the low byte is ever printed: the LDY #$00 at the entry wipes whatever the caller left in Y, which makes the CPY #$00 at $8607 and the whole $8626/$8627 patch vestiges of an earlier 16-bit version of this routine (see the notes on those lines).",
"In: A = value 0-255, X = column relative to textWindowLeft (zp_3B). Also uses zp_40 (text row), zp_3D (colour), zp_3E (fill glyph, 0 = blank) and needs decimalValueHigh zp_7E to be 0 - it always is, because convertToDecimalDigits clears it at $C1DD after every conversion.",
"Out: up to 3 cells printed and the cursor left just past the number; zp_3F = X; the three immediate operands at $8622/$8627/$8629 patched; A, X, Y and zp_18-zp_1F clobbered (printDecimal uses them).",
"Called from: drawStatsTab only - $8557 (the GRUNTS/RIDERS/BOOMERS/SPIES counts, both columns), $8579/$8581 (DRONES), $858B/$8593 (MISSILES), $85A2/$85AD (COMSPEED), $85BD/$85C8 (TER PTS). XREF also lists the same call sites under game/mapGenerator6F00; that is a label-merge artifact - variant A has completely different code at $8557 and at $8601 (bytes 'a9 fe 8d f8 90' there)."
],
"block": [
"Number printer for the console STATS tab. Right alignment is done the cheap way: count the",
"digits first, print that many blanks, then let printDecimal ($C17D) emit the digits with no",
"leading zeros. The value and the pad count are carried to the print calls in self-modified",
"immediate operands rather than on the stack, because updateCursorPointers ($C099) preserves only",
"X and Y, not A."
],
"line": "high byte of the number to print: always 0 here - the STATS fields are all 8-bit counts"
},
"8603": {
"unit": "game/comcenScreens6F00",
"line": "X held the target column (relative to textWindowLeft zp_3B); make it the text cursor column zp_3F"
},
"8605": {
"unit": "game/comcenScreens6F00",
"line": "X now counts the blank glyphs to print in front of the number; start by assuming none"
},
"8607": {
"unit": "game/comcenScreens6F00",
"line": "dead test: would leave the pad count at 0 when a 16-bit caller passed a non-zero high byte in Y, but $8601 has just forced Y = 0"
},
"8609": {
"unit": "game/comcenScreens6F00",
"line": "branch never taken (Y is always 0) - leftover of the 16-bit version of this routine"
},
"860B": {
"unit": "game/comcenScreens6F00",
"block": [
"Pad count: how many blanks are needed so that the digits end at the right edge of the",
"three-character field. X = 3 - number of decimal digits.",
"In variant B this is just an internal label; the JSRs to $860B in XREF come from variant A,",
"where a different routine (its OFF/ON option editor) starts on this byte."
],
"line": "is the value 100 or more, i.e. does it already fill all three columns?"
},
"860D": {
"unit": "game/comcenScreens6F00",
"line": "100-255: three digits, no padding at all"
},
"860F": {
"unit": "game/comcenScreens6F00",
"line": "under 100: at least one blank in front"
},
"8610": {
"unit": "game/comcenScreens6F00",
"line": "is the value 10 or more?"
},
"8612": {
"unit": "game/comcenScreens6F00",
"line": "10-99: two digits, so the one blank counted above is enough"
},
"8614": {
"unit": "game/comcenScreens6F00",
"line": "0-9: a single digit, so two blanks in front"
},
"8615": {
"unit": "game/comcenScreens6F00",
"block": [
"Self-modifying code. The three immediates below are written here on every call:",
" $8622 (operand of the LDA at $8621) = number of blanks to print",
" $8627 (operand of the LDY at $8626) = middle byte handed to printDecimal - always 0",
" $8629 (operand of the LDA at $8628) = the value to print",
"Nothing else in either overlay writes these three bytes."
],
"line": "patch the blank count into the LDA #imm at $8621 that feeds printFillChars"
},
"8618": {
"unit": "game/comcenScreens6F00",
"line": "patch the (always zero) middle byte into the LDY #imm at $8626 that feeds printDecimal"
},
"861B": {
"unit": "game/comcenScreens6F00",
"line": "patch the value itself into the LDA #imm at $8628"
},
"861E": {
"unit": "game/comcenScreens6F00",
"line": "$C099: absolute column = zp_3F + textWindowLeft zp_3B, row = zp_40; sets the bitmap pointer zp_B4/B5 and the screen pointer zp_B6/B7. It preserves X and Y but destroys A - which is why the value had to be patched in above"
},
"8621": {
"unit": "game/comcenScreens6F00",
"line": "operand $8622 patched at $8615: 0, 1 or 2 leading blanks"
},
"8623": {
"unit": "game/comcenScreens6F00",
"line": "printFillChars at $C0EF of the resident $C000 text engine (the listing shows the boot loader's name for the same address): prints the fill glyph zp_3E - normally 0, the blank - A times, advancing the cursor one cell each time"
},
"8626": {
"unit": "game/comcenScreens6F00",
"line": "operand $8627 patched at $8618: middle byte of printDecimal's 24-bit value, always 0"
},
"8628": {
"unit": "game/comcenScreens6F00",
"line": "operand $8629 patched at $861B: low byte = the number to print"
},
"862A": {
"unit": "game/comcenScreens6F00",
"line": "tail-call printDecimal $C17D (A = low, Y = middle, zp_7E = high): converts by repeated division by 10 and prints the digits without leading zeros, then returns to drawStatsTab"
},
"862D": {
"unit": "game/comcenScreens6F00",
"routine": [
"setTextColumn - moves the text cursor to column A of the current row and recomputes the bitmap and screen-RAM pointers for that character cell by tail-calling updateCursorPointers ($C099), which leaves X and Y untouched.",
"In: A = column relative to textWindowLeft (zp_3B; 10 while the STATS tab is being drawn), zp_40 = text row.",
"Out: zp_3F = A, zp_B4/B5 = bitmap address of the cell, zp_B6/B7 = screen RAM address; A clobbered, X and Y preserved.",
"Called from: drawStatsTab only - $8536 (the US/THEM column of each unit-count row), $85CF and $85DD (the two RECYCLER words) and $85F7 (the MAP ID column). The game/mapGenerator6F00 callers in XREF are label-merge artifacts; variant A holds unrelated code at $862D."
],
"block": [
"Two-instruction helper used wherever the STATS tab jumps to a fixed column without printing a",
"number: column 9 is the US column, column 14 the THEM column (both relative to textWindowLeft 10)."
],
"line": "new cursor column, counted from the left edge of the text window (zp_3B)"
},
"862F": {
"unit": "game/comcenScreens6F00",
"line": "$C099 recomputes zp_B4/B5 and zp_B6/B7 for cell (zp_3B + zp_3F, zp_40) and returns straight to the caller"
},
"8632": {
"unit": "game/comcenScreens6F00",
"routine": [
"drawComcenSchematic - unpacks the RLE-compressed comcen picture at comcenSchematicRle ($7C6E) straight into the hires bitmap for the console REPAIR tab: a 20x10 character (160x80 pixel) side view of the tracked comcen vehicle with its dish and antenna, drawn at character row 10, column 13.",
"Stream format (the same one unpackRleTo0200 $67CB uses): byte 0 is the escape marker ($13 here); afterwards any byte other than the marker is a literal, and 'marker, count, value' emits value count times, with count 0 meaning 256. Runs are allowed to cross the 160-byte row boundary, which is why the run value is stacked over the pointer advance at $8678/$8686.",
"In: the compressed stream at $7C6E-$7FCD. No registers.",
"Out: 10 x 160 bytes written into the bitmap from $A000 + 10*320 + 13*8 = $ACE8 with a 320-byte row stride; rleMarkerByte $9246 = $13, rleUnpackFlag $9247 = 0, fetchRleByte's self-modified operand $681B/$681C left just past the end of the stream; zp_1A (row counter), zp_1B (run count), zp_B4/B5 (bitmap pointer) and zp_B6/B7 (set but unused) clobbered.",
"Called from: drawRepairTab ($176B) in the main program, its only caller. Since the routine lives in overlay B, the console REPAIR tab implicitly requires variant B to be resident."
],
"block": [
"Set up the decoder: escape marker, source pointer and destination cell. Note that this routine",
"does not use the general unpacker at $67CB - that one always writes 512 bytes to $0200 - but it",
"does share its byte fetcher (fetchRleByte $681A) and its marker byte ($9246)."
],
"line": "index 0 of the stream, and the zero about to be stored in $9247"
},
"8634": {
"unit": "game/comcenScreens6F00",
"line": "$9247 = 0, exactly as unpackRleTo0200 does at $67D3 - but no code in any unit ever reads this byte, so it is a dead leftover"
},
"8637": {
"unit": "game/comcenScreens6F00",
"line": "byte 0 of the compressed stream is the escape marker ($13 for this picture)"
},
"863A": {
"unit": "game/comcenScreens6F00",
"line": "keep the marker in $9246 for the compare at $865E (shared with the main program's unpacker)"
},
"863D": {
"unit": "game/comcenScreens6F00",
"line": "high byte of the source address..."
},
"863F": {
"unit": "game/comcenScreens6F00",
"line": "...poked into the high half of fetchRleByte's self-modified 'lda $XXXX' operand at $681B/$681C"
},
"8642": {
"unit": "game/comcenScreens6F00",
"line": "low byte: decoding starts at $7C6F, one past the marker byte"
},
"8644": {
"unit": "game/comcenScreens6F00",
"line": "complete the patched source pointer = $7C6F"
},
"8647": {
"unit": "game/comcenScreens6F00",
"line": "left edge of the picture: character column 13, just right of the 22-column map panel"
},
"8649": {
"unit": "game/comcenScreens6F00",
"line": "top edge: character row 10, under the REPAIR tab heading"
},
"864B": {
"unit": "game/comcenScreens6F00",
"line": "$2AA3: zp_B4/B5 = $A000 + row*320 + col*8 (bitmap cell), zp_B6/B7 = $8C00 + row*40 + col (screen RAM cell, not used here)"
},
"864E": {
"unit": "game/comcenScreens6F00",
"line": "the picture is 10 character rows tall"
},
"8650": {
"unit": "game/comcenScreens6F00",
"line": "zp_1A counts the rows still to emit"
},
"8652": {
"unit": "game/comcenScreens6F00",
"line": "Y = byte offset inside the current character row, 0..159"
},
"8654": {
"unit": "game/comcenScreens6F00",
"block": [
"Decoder loop. X is the number of copies of A still to store: it is decremented on entry to each",
"pass, and only when it reaches 0 is a new item pulled from the stream. Y walks the 160 bytes of",
"one picture row (20 cells x 8 scanlines, stored cell by cell), zp_1A counts the rows.",
"primeRleRunCounter is entered once, by falling through from $8652; the per-row restart at $8689 jumps back to rleEmitLoop with X and A intact. It is a branch target only in variant A."
],
"line": "prime the run counter with 1 so that the very first pass fetches from the stream"
},
"8656": {
"unit": "game/comcenScreens6F00",
"line": "one byte of the current run consumed"
},
"8657": {
"unit": "game/comcenScreens6F00",
"line": "run not exhausted: store the same value again without touching the stream"
},
"8659": {
"unit": "game/comcenScreens6F00",
"line": "$681A: fetch the next stream byte into A (and X) and bump the source pointer"
},
"865C": {
"unit": "game/comcenScreens6F00",
"line": "assume a literal byte: exactly one store"
},
"865E": {
"unit": "game/comcenScreens6F00",
"line": "is this byte the escape marker that introduces a run?"
},
"8661": {
"unit": "game/comcenScreens6F00",
"line": "not the marker: plain literal, store it once"
},
"8663": {
"unit": "game/comcenScreens6F00",
"line": "marker seen - the next byte is the repeat count (0 counts as 256)"
},
"8666": {
"unit": "game/comcenScreens6F00",
"line": "park the repeat count while the value byte is fetched (this label is a branch target only in variant A)"
},
"8668": {
"unit": "game/comcenScreens6F00",
"line": "third byte of the run: the value to repeat"
},
"866B": {
"unit": "game/comcenScreens6F00",
"line": "X = repeat count; this pass stores the value once and X-1 further passes repeat it"
},
"866D": {
"unit": "game/comcenScreens6F00",
"line": "write one bitmap byte; eight consecutive bytes are the eight scanlines of one character cell"
},
"866F": {
"unit": "game/comcenScreens6F00",
"line": "next byte of the picture row"
},
"8670": {
"unit": "game/comcenScreens6F00",
"line": "160 bytes = 20 character cells x 8 scanlines = one full picture row"
},
"8672": {
"unit": "game/comcenScreens6F00",
"line": "row not finished yet, keep decoding"
},
"8674": {
"unit": "game/comcenScreens6F00",
"block": [
"End of a picture row: step the bitmap pointer down one character row. A (the current run value)",
"and X (the remaining repeat count) are carried across untouched, so a run may span rows."
],
"line": "one of the 10 picture rows done"
},
"8676": {
"unit": "game/comcenScreens6F00",
"line": "all 10 rows drawn - the picture is complete"
},
"8678": {
"unit": "game/comcenScreens6F00",
"line": "save the current run value across the pointer arithmetic"
},
"8679": {
"unit": "game/comcenScreens6F00",
"line": "clear carry for the 16-bit add below"
},
"867A": {
"unit": "game/comcenScreens6F00",
"line": "advance the bitmap pointer..."
},
"867C": {
"unit": "game/comcenScreens6F00",
"line": "...by $0140 = 320 bytes = one character row of the 40-column bitmap ($40 into the low byte..."
},
"867E": {
"unit": "game/comcenScreens6F00",
"line": "...store the new low byte"
},
"8680": {
"unit": "game/comcenScreens6F00",
"line": "...carry into the high byte..."
},
"8682": {
"unit": "game/comcenScreens6F00",
"line": "...plus 1, completing the +$140"
},
"8684": {
"unit": "game/comcenScreens6F00",
"line": "zp_B4/B5 now points at the leftmost cell of the next picture row (still column 13)"
},
"8686": {
"unit": "game/comcenScreens6F00",
"line": "restore the run value so a run that crossed the row boundary keeps storing the same byte"
},
"8687": {
"unit": "game/comcenScreens6F00",
"line": "back to the left edge of the picture row"
},
"8689": {
"unit": "game/comcenScreens6F00",
"line": "always taken (Y was just loaded with 0): continue decoding into the new row"
},
"868B": {
"unit": "game/comcenScreens6F00",
"line": "return to drawRepairTab, which then paints the colour cells over the picture"
},
"868C": {
"unit": "game/comcenScreens6F00",
"block": [
"==================================================================",
"From here to the end of the overlay ($868C-$87FE) variant B contains no code at all: five user-",
"interface captions, the 79-byte STATS tab label blob, a 12-byte word table for the film status",
"line and 16 bit-7-terminated message strings, then six padding zeroes and the variant flag.",
"Every routine-looking label inside this area belongs to variant A, which keeps its custom-",
"scenario dialog code at $868C-$879E - XREF merges both variants into one namespace, so names",
"such as setDestroyModeMessage ($86AA), showCustomSummaryMessage ($86C3), setPersistentMessage",
"($8722), appendOnOffWord ($872E), showScenarioBannerAndWaitForButton ($8756) and",
"printCentredTableEntry ($8795), as well as the D_/L_ labels sprinkled through the strings, mean",
"nothing here and only mark where variant A branches or stores. The only two interior addresses",
"that matter with variant B resident are $8726 and $8730, both patched by the film replay key",
"handler at $40B2.",
"A byte compare of extracted/runtimeImage_A.bin and _B.bin shows $868C-$879E differing,",
"$879F-$87FE identical in both variants and $87FF differing ($00 in A, $FF in B).",
"==================================================================",
"",
"strDrone - 'DRONE', bit 7 on the final E. Caption of the drones-left box on the DRONE screen,",
"printed by overlay B code at $7018-$7033 with textColour $CF at column 32, row 7."
],
"line": "'D' of DRONE"
},
"868D": {
"unit": "game/comcenScreens6F00",
"line": "'R' - label from variant A, which stores here at $8213/$86AC"
},
"868E": {
"unit": "game/comcenScreens6F00",
"line": "'ONE' with bit 7 on the E as the string terminator"
},
"8691": {
"unit": "game/comcenScreens6F00",
"block": [
"strLaunch - 'LAUNCH', caption under the 2x2 LAUNCH button icon of the DRONE screen; printed at",
"$70B6-$70C5 at column 32, row 14."
],
"line": "6 bytes, terminated by 'H' | $80"
},
"8697": {
"unit": "game/comcenScreens6F00",
"block": [
"strStrike - 'STRIKE', caption under the 2x2 STRIKE button icon of the DRONE screen; printed by",
"the code at $70D9-$70EF with textColour $C7 at column 32, row 18."
],
"line": "'STR'"
},
"869A": {
"unit": "game/comcenScreens6F00",
"line": "'I' - variant A writes here ($8221/$86BA); no meaning in variant B"
},
"869B": {
"unit": "game/comcenScreens6F00",
"line": "'KE' with bit 7 on the E"
},
"869D": {
"unit": "game/comcenScreens6F00",
"block": [
"strFire - 'FIRE', caption of the missile FIRE button on the main game screen; printed at",
"$7B2E-$7B3D at column 31, row 12."
],
"line": "4 bytes, terminated by 'E' | $80"
},
"86A1": {
"unit": "game/comcenScreens6F00",
"block": [
"strDroneAlertTwoLine - 'DRONE' + CR + 'ALERT': the two-line label of the top-right DRONE ALERT",
"panel, printed at $7B67-$7B7A with textWindowLeft 31, column 0, row 3. The $0D is the text",
"engine's newline, so the two words land on consecutive rows. What flashes is the panel colour",
"(droneAlertColour $7B43, $02 or $20), not this text."
],
"line": "'DRONE', carriage return, 'ALE'"
},
"86AA": {
"unit": "game/comcenScreens6F00",
"line": "'RT' with bit 7 on the T (terminator)"
},
"86AC": {
"unit": "game/comcenScreens6F00",
"block": [
"statsTabLabels - the entire left-hand label column of the console STATS tab as ONE bit-7-",
"terminated string with embedded carriage returns ($0D), printed by a single printString call at",
"$161D-$1621 in the main program (textWindowLeft 10, column 3, right after ownerNameString $0B7F).",
"Each $0D drops one row, so the leading $0D lands the first label on row 9:",
" row 9 GRUNTS, 10 RIDERS, 11 BOOMERS, 12 SPIES, (13 blank), 14 DRONES, 15 MISSILES,",
" (16 blank), 17 COMSPEED, 18 TER PTS, 19 RECYCLER, 20 MAP ID.",
"The matching numbers are printed separately by drawStatsTab ($8518) into column 9 (US) and",
"column 14 (THEM). 79 bytes, $86AC-$86FA."
],
"line": "CR, 'GRUNTS', CR, 'RIDERS', CR, 'BOOMERS', CR"
},
"86C3": {
"unit": "game/comcenScreens6F00",
"line": "'SP' - split here only because variant A has a routine entry on this byte"
},
"86C5": {
"unit": "game/comcenScreens6F00",
"line": "'IES' CR CR 'DRO' - end of the SPIES row, a blank row, then DRONES"
},
"86CD": {
"unit": "game/comcenScreens6F00",
"line": "'NES' CR 'MISS'"
},
"86D5": {
"unit": "game/comcenScreens6F00",
"line": "'ILES' CR CR 'CO' - end of MISSILES, a blank row, then COMSPEED"
},
"86DD": {
"unit": "game/comcenScreens6F00",
"line": "'MS'"
},
"86DF": {
"unit": "game/comcenScreens6F00",
"line": "'PEED' CR 'TER'"
},
"86E7": {
"unit": "game/comcenScreens6F00",
"line": "' PTS' CR 'REC'"
},
"86EF": {
"unit": "game/comcenScreens6F00",
"line": "'YCLE'"
},
"86F3": {
"unit": "game/comcenScreens6F00",
"line": "'R' CR 'MAP I' + 'D' | $80: last label, bit 7 ends the whole blob"
},
"86FB": {
"unit": "game/comcenScreens6F00",
"block": [
"filmViewWordTable - three fixed 4-byte words used to complete the 'SHOW ****' status line during",
"game-film playback: 'US' (padded with two zeroes), 'THEM', 'BOTH'. The film key handler indexes",
"it with (playModeFlags $0B9B & 3) * 4 at $4116-$4120 and copies four bytes over $8726-$8729",
"($4123) before queueing message 6. Each word carries its own bit-7 terminator, so the two",
"padding zeroes of the 'US' entry are copied but never printed. Only indices 0-2 occur: the CLR/",
"HOME handler wraps playModeFlags from $83 back to $80 at $4110-$4116."
],
"line": "entry 0 'US' | $80 plus two pad bytes, then entry 1 'THEM' | $80"
},
"8703": {
"unit": "game/comcenScreens6F00",
"line": "entry 2 'BOTH' with bit 7 on the H"
},
"8707": {
"unit": "game/comcenScreens6F00",
"block": [
"Message strings. From here on every block is one entry of the message table at $0500 (low bytes)",
"/ $0524 (high bytes), with the sound played for it in the parallel table at $0612 ($80 = silent).",
"Messages are queued by id through queueMessage ($C566) or queueMessageUnlessFilm ($C560), the",
"latter dropping the message during solo-trainer or film playback.",
"",
"msgEndOfFilm - 'END OF FILM.', message id 2, sound 3; queued at $59FC after setFilmPtrToEnd when",
"a recorded game film has been replayed to its last frame."
],
"line": "'END OF FILM'"
},
"8712": {
"unit": "game/comcenScreens6F00",
"line": "'.' with bit 7 set = terminator"
},
"8713": {
"unit": "game/comcenScreens6F00",
"block": [
"msgUnitRecycled - 'UNIT RECYCLED.', message id 5, sound 4; queued with queueMessageUnlessFilm at",
"$3EA3 after initUnitRecord, but only when the recycled unit's owner (zp_1A) is the local player."
],
"line": "'U'"
},
"8714": {
"unit": "game/comcenScreens6F00",
"line": "'NIT RECY'"
},
"871C": {
"unit": "game/comcenScreens6F00",
"line": "'CLED' + '.' | $80"
},
"8721": {
"unit": "game/comcenScreens6F00",
"block": [
"msgShowViewTemplate - 'SHOW ****', message id 6, sound 3: the film playback status line. The",
"four '*' at $8726-$8729 are overwritten with a filmViewWordTable entry (US / THEM / BOTH) at",
"$4123 before the message is queued at $412E, and that entry supplies the bit-7 terminator.",
"As stored on disk the template has NO terminator, so printing it unpatched would run straight on",
"through 'SPEED *' and only stop at the '!' of 'SPOTTED RECYCLER!' ($8741)."
],
"line": "'S'"
},
"8722": {
"unit": "game/comcenScreens6F00",
"line": "'HOW ' - the space before the patched word"
},
"8726": {
"unit": "game/comcenScreens6F00",
"line": "four-byte patch field: receives 'US'+$80+2 pad / 'THEM'|$80 / 'BOTH'|$80 from filmViewWordTable, written by the film key handler at $4123"
},
"872A": {
"unit": "game/comcenScreens6F00",
"block": [
"msgSpeedTemplate - 'SPEED *', message id 7, sound 3: shown when a digit key changes the film",
"replay speed. Pressing '1'-'9' sets stepIntervalFrames $92E3 = 8 - (key - $B1) and stores the raw",
"key code $B1-$B9 into $8730 at $40DE; because those codes already have bit 7 set they double as",
"the string terminator. Unterminated as stored on disk, like the SHOW template above."
],
"line": "'SPEE'"
},
"872E": {
"unit": "game/comcenScreens6F00",
"line": "'D' and a space"
},
"8730": {
"unit": "game/comcenScreens6F00",
"line": "one-byte patch field: the pressed digit's key code $B1-$B9 (screen code with bit 7 = terminator), written at $40DE"
},
"8731": {
"unit": "game/comcenScreens6F00",
"block": [
"msgSpottedRecycler - 'SPOTTED RECYCLER!', message id 8, sound 4; queued with",
"queueMessageUnlessFilm at $3F8C right after drawRecyclerOnMap / drawMapCell, i.e. the moment the",
"recycler cell first becomes visible to the local player."
],
"line": "'SPO'"
},
"8734": {
"unit": "game/comcenScreens6F00",
"line": "'TTED REC'"
},
"873C": {
"unit": "game/comcenScreens6F00",
"line": "'YCLE'"
},
"8740": {
"unit": "game/comcenScreens6F00",
"line": "'R' + '!' | $80"
},
"8742": {
"unit": "game/comcenScreens6F00",
"block": [
"msgComcenStunned - 'COMCEN STUNNED!', message id $0C, sound 4; queued at $0741-$0743 by the",
"game-tick code in the $0400 block when $9162 reads $FF. The same text is also printed straight",
"into the console status area at column 5 by $17C4-$17D4 for as long as comcenStunnedFlag ($922A)",
"bit 7 is set."
],
"line": "15 bytes, terminated by '!' | $80"
},
"8751": {
"unit": "game/comcenScreens6F00",
"block": [
"msgSpottedEnemyComcen - 'SPOTTED ENEMY COMCEN!', message id $0D, sound 4; queued with",
"queueMessageUnlessFilm at $36EF when a newly visible enemy unit's type field ($F76C,y & 7) is 4",
"(COMCEN). Types 0-2 (GRUNT, RIDER, BOOMER) are spotted silently."
],
"line": "'SPOT'"
},
"8755": {
"unit": "game/comcenScreens6F00",
"line": "'T'"
},
"8756": {
"unit": "game/comcenScreens6F00",
"line": "'ED ENEMY'"
},
"875E": {
"unit": "game/comcenScreens6F00",
"line": "' COMCEN'"
},
"8765": {
"unit": "game/comcenScreens6F00",
"line": "'!' | $80 terminator"
},
"8766": {
"unit": "game/comcenScreens6F00",
"block": [
"msgSpyUnderAttack - 'SPY UNDER ATTACK!', message id $0E, sound 4; queued with",
"queueMessageUnlessFilm at $3B3B when the unit being hit is type 3 (SPY) and its energy field",
"($F9C4,y & $3F) is still $32 = 50, i.e. full - so it fires once, on the first hit only."
],
"line": "17 bytes, terminated by '!' | $80"
},
"8777": {
"unit": "game/comcenScreens6F00",
"block": [
"msgSpottedEnemySpy - 'SPOTTED ENEMY SPY!', message id $0F, sound 4; queued with",
"queueMessageUnlessFilm at $36E7, the sibling of the COMCEN case above in the visibility code."
],
"line": "18 bytes, terminated by '!' | $80"
},
"8789": {
"unit": "game/comcenScreens6F00",
"block": [
"The five end-of-game result messages. computeGameResult ($28F1) picks one of them into",
"persistentMessageId and queues it at $29A0.",
"",
"msgWeLostOnPoints - 'WE LOST ON POINTS!', message id $11, sound $0E: the time-out result when the",
"LOSING side ($2963-$296B: X, the side whose accumulated damage score is higher) is the local",
"player. If it is the other side the code does INY and picks $12 instead.",
"Variant A stores 22 bytes of its own code over $8789-$879E, so this string is only intact while",
"variant B is resident - which is fine, because results are only shown during the battle."
],
"line": "'W'"
},
"878A": {
"unit": "game/comcenScreens6F00",
"line": "'E LOST O'"
},
"8792": {
"unit": "game/comcenScreens6F00",
"line": "'N P'"
},
"8795": {
"unit": "game/comcenScreens6F00",
"line": "'OINTS' + '!' | $80"
},
"879B": {
"unit": "game/comcenScreens6F00",
"block": [
"msgWeWonOnPoints - 'WE WON ON POINTS!', message id $12, sound $0D: the time-out win (losing side",
"!= local player). Its first four bytes 'WE W' ($879B-$879E) are the last of the bytes variant A",
"overlays; from $879F on the two overlay variants are byte-identical to the end of the block."
],
"line": "'WE W' - the last four bytes that variant A clobbers"
},
"879F": {
"unit": "game/comcenScreens6F00",
"line": "'ON ON POINTS' + '!' | $80; identical in both overlay variants"
},
"87AC": {
"unit": "game/comcenScreens6F00",
"block": [
"msgItWasADraw - 'IT WAS A DRAW.', message id $13 and the only result message with no sound",
"($80 in the sound table). Chosen when the 16-bit score difference is exactly zero at time-out",
"($2917-$2929) or when gameOverFlags bits 5-6 are both set, i.e. both comcens were knocked out",
"($297A); both sides are then given 250 points."
],
"line": "14 bytes, terminated by '.' | $80"
},
"87BA": {
"unit": "game/comcenScreens6F00",
"block": [
"msgWeWereKnockedOut - 'WE WERE KNOCKED OUT!', message id $14, sound $0E; selected at $298D-$2995",
"when the knocked-out side equals the local player. The loser is given 500 points, the winner",
"150."
],
"line": "20 bytes, terminated by '!' | $80"
},
"87CE": {
"unit": "game/comcenScreens6F00",
"block": [
"msgWeWonByAKnockout - 'WE WON BY A KNOCKOUT!', message id $15, sound $0D: the enemy comcen was",
"destroyed (the INY sibling of message $14).",
"The solo trainer's stores to 'msgWeWonByAKnockout,y' at $EB29/$EB53 do NOT corrupt this string:",
"there Y is an absolute unit index of the AI side, 50-99, so the writes land at $8800-$8831 - the",
"trainer's per-unit order-timestamp table, which it addresses through the base $87CE."
],
"line": "21 bytes, terminated by '!' | $80"
},
"87E3": {
"unit": "game/comcenScreens6F00",
"block": [
"msgGameOver - 'GAME OVER.', message id $16, sound $19; queued first at $2901-$2903 on the",
"time-out path, ahead of the points result message."
],
"line": "10 bytes, terminated by '.' | $80"
},
"87ED": {
"unit": "game/comcenScreens6F00",
"block": [
"msgDroneAlert - 'DRONE ALERT!'. This one has no fixed id: overlay A installs its address into",
"run-time message slot $20 at $7BA6-$7BAC (ldx #$20 / lda #$ED / ldy #$87 / jsr setMessageSlot",
"$C924) just before jumping into the opponent module's battle-start entry. The pointer survives",
"the swap to overlay B because this string sits in the tail that both variants hold identically.",
"During the battle showDroneAlert ($820F) plays sound 7 and queues id $20 whenever the opponent",
"launches a drone, unless playModeFlags $0B9B bit 7 (solo trainer / film playback) is set."
],
"line": "12 bytes, terminated by '!' | $80"
},
"87F9": {
"unit": "game/comcenScreens6F00",
"block": [
"overlayTailPadding - six unused $00 bytes between the last string and the variant flag. Nothing",
"reads them; they are identical in both overlay variants and exist only to push the flag to the",
"very last byte of the block."
],
"line": "filler up to $87FE"
},
"87FF": {
"unit": "game/comcenScreens6F00",
"block": [
"overlayVariantFlag - identity byte of whichever $6F00-$87FF overlay is currently resident. The",
"overlay loader tests bit 7 at $1036 and $1080 to decide whether the variant it wants is already",
"in memory or has to be pulled from disk (variant A = track 31 s0-16 + track 32 s0-7, variant B =",
"track 33 s0-16 + track 32 s8-15)."
],
"line": "$FF = variant B (comcen missile and drone screens) is resident; variant A (map generator and setup) stores $00 here"
}
}
}