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