modemwars/survey/game_main_0800_0800.json
2026-08-23 02:09:40 -05:00

1822 lines
No EOL
61 KiB
JSON
Generated

{
"chunk": "game_main_0800_0800",
"unit": "game/main_0800",
"routines": [
{
"addr": "0800",
"name": "gameEntry",
"summary": "Entry point jumped to by the boot code at $02B8; just JMPs to mainStart.",
"inputs": "none",
"outputs": "none",
"confidence": "high"
},
{
"addr": "0804",
"name": "loadSectors",
"summary": "Fast-loader read: reads A consecutive sectors of track Y starting at sector X into the address set by setLoadDest (one page per sector). On a drive error it shows msgDiskError via $C3D8 and retries the same sector forever; once a retried read succeeds it calls $C6FF with $92A1 to restore the message area.",
"inputs": "A=sector count, Y=track, X=first sector, loadDestLo/Hi",
"outputs": "C=0, memory filled, loadDestHi advanced, ldTrack/ldSector/ldCount",
"confidence": "high"
},
{
"addr": "0843",
"name": "writeSectors",
"summary": "Fast-loader write: writes A consecutive sectors to track Y from sector X, source address from setLoadDest. Used by the overlay-A save routines ($7E85, $8194).",
"inputs": "A=count, Y=track, X=first sector, loadDestLo/Hi",
"outputs": "C=1 on failure, C=0 on success",
"confidence": "high"
},
{
"addr": "085C",
"name": "setLoadDest",
"summary": "Stores X/Y as the low/high byte of the next transfer address (self-modifying operands loadDestLo/loadDestHi).",
"inputs": "X=lo, Y=hi",
"outputs": "loadDestLo, loadDestHi",
"confidence": "high"
},
{
"addr": "0863",
"name": "writeOneSector",
"summary": "Sends command $60, track and sector to the drive, stops CIA1 timer A during the transfer, sends 256 bytes and reads the status byte.",
"inputs": "Y=track, X=sector, loadDestLo/Hi",
"outputs": "C=1 on error (from finishTransfer)",
"confidence": "high"
},
{
"addr": "088F",
"name": "sendSectorData",
"summary": "Sends the 256 bytes at (wrSrcPtr) to the drive one byte at a time, then falls into finishTransfer to read the status.",
"inputs": "wrSrcPtr",
"outputs": "C=1 on error",
"confidence": "high"
},
{
"addr": "089C",
"name": "sendByteToDrive",
"summary": "Clocks one byte to the drive MSB first through CIA2 port A bit 5 with a handshake on bits 4/6/7; simultaneously shifts in a byte from the drive into recvByteShift, which is returned in A (used to fetch the status byte).",
"inputs": "A=byte",
"outputs": "A=byte received from the drive, C=0, X=0",
"confidence": "high"
},
{
"addr": "08EB",
"name": "readOneSector",
"summary": "Requests track Y sector X with command $80 and receives 256 bytes into the load destination; CIA1 timer A is stopped for the duration.",
"inputs": "Y=track, X=sector, loadDestLo/Hi",
"outputs": "C=1 on error, ldTrack/ldSector",
"confidence": "high"
},
{
"addr": "08FE",
"name": "readSectorBody",
"summary": "Sends the read command, saves and clears the sprite enable register, computes the bad-line avoidance constant ($D011&7 + $2F) into the operand at $095F, then receives the sector and the status byte.",
"inputs": "ldTrack, ldSector, loadDestLo/Hi",
"outputs": "C=1 on error, savedSpriteEnable, ldCia2Bits",
"confidence": "high"
},
{
"addr": "0943",
"name": "finishTransfer",
"summary": "Restores the sprite enable register and reads the drive status byte; C=1 if the status is nonzero.",
"inputs": "savedSpriteEnable",
"outputs": "C, A=status",
"confidence": "high"
},
{
"addr": "0950",
"name": "receiveSectorData",
"summary": "Timing-critical 2-bit-at-a-time receive loop for 256 bytes; waits for a raster line that is not a bad line before each byte.",
"inputs": "ldDestPtr, ldCia2Bits",
"outputs": "256 bytes at (ldDestPtr)",
"confidence": "high"
},
{
"addr": "09A6",
"name": "manualLookupCheck",
"summary": "Copy protection (disabled in this build: the JSR at $0A9B was patched to BIT). Saves mapSeed ($0B95-$0B97) and $0BA9-$0BAE, picks answer entry ($09C3 index & $1F) from the 32x3 table at $0401 into mapSeed, draws the map (sub_6F04, sub_5AD7, sub_261C) and prints msgIdentifyMap; the player gets 3 tries ($92C7) to type the 3 answer bytes into $5A-$5C, otherwise memory is wiped from $C000 down and the machine hangs. On success it restores the saved bytes.",
"inputs": "$0401 table, $5A-$5C player input",
"outputs": "$0B95-$0B97 and $0BA9-$0BAE restored, zp_3D/zp_42=0",
"confidence": "high"
},
{
"addr": "0A6C",
"name": "mainStart",
"summary": "Game start-up: disables CIA IRQs, points NMI/IRQ at irqRti ($1298), loads the runtime overlays ($C000, overlay A, comm module), loads the setup block, runs the setup-block code at $0300, initialises IRQ/input state, sets currentScreen=0, calls sub_2636/sub_5E03, (manual check disabled), sets playerSide=$FE and falls into returnToMainMenu.",
"inputs": "none",
"outputs": "whole system initialised",
"confidence": "high"
},
{
"addr": "0AA3",
"name": "returnToMainMenu",
"summary": "Re-entry point used to abort to the main menu (also jumped to from $5A3F, $C876, $EE0C and by the IRQ handler on SHIFT+C=+F7): VIC IRQs off, sprites off, stack reset to $FD, sub_2D41, initIrqAndInputState, message queue reset, loadingDepth=0, currentScreen=3, showMainMenu, then continues at L_1A86 (link status / menu flow).",
"inputs": "none",
"outputs": "currentScreen=3, loadingDepth=0, stack reset",
"confidence": "medium"
},
{
"addr": "0AC8",
"name": "startGameFromSetup",
"summary": "Jumped to from overlay A ($7E3F) when setup is complete. Resets the game variables, waits for the game disk, reloads the comm module if the link type flag changed ($0BA5 vs $E030), initialises the comm module, shows the modem prompt and waits for A/O when switching to modem mode, loads the map and setup block, reloads overlay B if needed, builds the owner name string and jumps to sub_1FD0 (battle start).",
"inputs": "$0B9B, $0B9C, $0BA5, $0BA6, $87FF, $E030",
"outputs": "map at $F000, setup at $0200, comm module configured, loadingDepth=0",
"confidence": "medium"
},
{
"addr": "0B4F",
"name": "loadCommTailBuild1",
"summary": "Inside startGameFromSetup: when $0BA5 bit 7 is set, loads $EC00-$EFFF from track 35 s12-15 (comm build 1 tail). Not a separate routine (the setLoadDest arguments are set up at $0B4B).",
"inputs": "X=0,Y=$EC",
"outputs": "$EC00-$EFFF",
"confidence": "high"
},
{
"addr": "0BB5",
"name": "initIrqAndInputState",
"summary": "Silences the sound, installs the raster IRQ, enables sub_3F57 (patches its first byte to NOP), sets the input lockout to $9E frames, $0549=$9E, clears $92C9/$92E4/$922A and sets currentKey=$FF.",
"inputs": "none",
"outputs": "IRQ installed, inputLockoutTimer, currentKey",
"confidence": "medium"
},
{
"addr": "0BD8",
"name": "disableCiaInterrupts",
"summary": "Writes $7F to both CIA ICRs and reads them back to acknowledge pending interrupts.",
"inputs": "none",
"outputs": "CIA interrupts disabled",
"confidence": "high"
},
{
"addr": "0BE7",
"name": "initInterruptVectors",
"summary": "Resets the BRK-exit operand of the IRQ handler ($10FF/$1100) to irqExit ($1293), and sets the comm module's NMI vector copy ($E031/$E032) and the hardware NMI vector to irqRti ($1298).",
"inputs": "none",
"outputs": "$10FF/$1100, $E031/$E032, $FFFA/$FFFB",
"confidence": "medium"
},
{
"addr": "0C02",
"name": "setNmiVectorRti",
"summary": "Sets the NMI vector to irqRti ($1298); jumped to after loading comm build 1.",
"inputs": "none",
"outputs": "$FFFA/$FFFB",
"confidence": "high"
},
{
"addr": "0C0D",
"name": "jumpToCd00IfPatched",
"summary": "If $CD00 holds a JMP opcode ($4C) it disables VIC IRQs and jumps there, else returns via the RTS at $0C0C. Not referenced by any traced code; at run time $CD00 holds data in the $C000 overlay, so this looks like dead/debug code.",
"inputs": "$CD00",
"outputs": "VIC_IRQ_MASK=0 when taken",
"confidence": "low"
},
{
"addr": "0C1C",
"name": "resetGameVariables",
"summary": "Clears zero page $15-$7F and $8C-$FF and the variable area $9008-$92FF, sets $9248-$9250, $90EA and $B1 to $FF, calls sub_58D5, sets currentScreen=3 and $0548=$9E (via $C62A). Also called from the setup-block code at $0300.",
"inputs": "none",
"outputs": "variables cleared",
"confidence": "medium"
},
{
"addr": "0C66",
"name": "installRasterIrq",
"summary": "Installs rasterIrqHandler ($10F1) as the IRQ vector (and its shadow irqVectorLo/Hi), sets the raster line to $FA, clears irqInProgress, acknowledges and enables the VIC raster interrupt.",
"inputs": "none",
"outputs": "$FFFE/$FFFF, zp_AA-zp_AC, VIC_RASTER, VIC_IRQ_MASK",
"confidence": "high"
},
{
"addr": "0C8C",
"name": "showModemSetupPrompt",
"summary": "Stores the caller's string (A/Y) in message slot $20 and 'PRESS SPACE WHEN READY.' ($8837) in slot $21, calls resetMessagesAndBeep and jumps into overlay A at $7DB8. Called from overlay A ($79E3).",
"inputs": "A/Y=string address",
"outputs": "message slots $20/$21",
"confidence": "medium"
},
{
"addr": "0CA0",
"name": "resetMessagesAndBeep",
"summary": "Resets the message queue, queues message $20, plays sound 3 twice with a 20-frame pause and sets messageState ($92A1) to $21.",
"inputs": "none",
"outputs": "$92A1=$21",
"confidence": "medium"
},
{
"addr": "0CBD",
"name": "waitForAnswerOriginateKey",
"summary": "After resetMessagesAndBeep, loops (stirring the RNG) until the last typed key ($9248) is 'A' ($C1) or 'O' ($CF), stores 0 (answer) or 1 (originate) in the comm module flag $E011 and resets the message queue.",
"inputs": "$9248 key from IRQ",
"outputs": "$E011 answer/originate, $0B7D=0",
"confidence": "high"
},
{
"addr": "0CE8",
"name": "buildOwnerNameString",
"summary": "Builds the 21-character ownerNameString at $0B7F from the two 10-byte name fields at $FFDE and $FFE8 (first field right-aligned, second left-aligned, spaces squeezed) and terminates it with bit 7. If the first field is blank ($A0) it is replaced by 'OURS'.",
"inputs": "$FFDE-$FFF1",
"outputs": "$0B7F-$0B93",
"confidence": "medium"
},
{
"addr": "0D49",
"name": "setJoystickRepeatDelay",
"summary": "Joystick auto-repeat: increments joyHoldCounter ($91CA) masked by joyCenteredMask ($91C9, cleared by the IRQ when the stick is centred) and sets timer zp_6C to 16 frames for the first step or 6 frames for repeats; preserves A.",
"inputs": "$91C9, $91CA",
"outputs": "zp_6C, $91CA, $91C9=$7F",
"confidence": "medium"
},
{
"addr": "0DB7",
"name": "scanKeyboard",
"summary": "Keyboard matrix scanner installed into the comm module vector $E013/$E014 and called through $E012/$E015. Returns no key if a joystick is disturbing either CIA1 port, tests RSHIFT, LSHIFT and C= as modifiers, then scans rows 7..0 using the masks at $2F8D/$2F85 and translates the first pressed key through keyMatrixCodeTable ($FE entries are skipped, $FF = CTRL aborts).",
"inputs": "CIA1 ports",
"outputs": "A=key code ($FF none), Y=$80 if shift held, X=$80 if C= held",
"confidence": "high"
},
{
"addr": "0E45",
"name": "testKeyMask",
"summary": "Selects keyboard row Y on CIA1 port A, reads port B debounced and ANDs it with mask A (self-modified operand). Z=1 when that key is pressed.",
"inputs": "Y=row mask, A=column bit",
"outputs": "A/Z",
"confidence": "high"
},
{
"addr": "0E56",
"name": "pollKeyboardEvent",
"summary": "Called from the IRQ every frame: calls the comm module key entry $E015, stores raw key/shift/C= in $92A0/$92E4/$92E5, handles key repeat with a 20-frame timer (zp_70), sets currentKey (zp_66), lastKeyCode ($90E9) and newKeyEvent ($90EA); for codes >= $80 also stores lastTypedKey ($9248) and applies shiftedKeyTable to $AC-$BB when shift is held.",
"inputs": "$E015 result",
"outputs": "zp_66, zp_70, $90E9, $90EA, $9248, $92A0, $92E4, $92E5",
"confidence": "medium"
},
{
"addr": "0EB1",
"name": "checkFirePressed",
"summary": "Edge-detects a new fire button press: returns A=0 (Z=1) when fire is down (zp_5F=0) after having been released (zp_79), starting a 10-frame debounce (zp_6D) and playing click sound 1; otherwise A=1. Also fails when sub_59B3 returns C=1. Callers loop on BNE to wait for the button.",
"inputs": "zp_5F, zp_79, zp_6D",
"outputs": "A/Z, zp_79, zp_6D",
"confidence": "medium"
},
{
"addr": "0ED5",
"name": "checkFirePressedNotSolo",
"summary": "Like checkFirePressed but when a press is detected returns soloModeFlag ($0B9B) if negative, else $923F & $40 - i.e. A=0 only for a fresh press in non-solo mode with $923F bit 6 clear.",
"inputs": "zp_5F, $0B9B, $923F",
"outputs": "A/Z",
"confidence": "low"
},
{
"addr": "0EE5",
"name": "getSoloOrFlag923F",
"summary": "Orphan entry (A=0 then jumps into checkFirePressedNotSolo at $0EDA): returns $0B9B if negative else $923F & $40. Not referenced by traced code.",
"inputs": "$0B9B, $923F",
"outputs": "A/Z",
"confidence": "low"
},
{
"addr": "0EEA",
"name": "isFireHeld",
"summary": "Returns A=0 while the fire button is held (after one release), without debounce or sound; A=1 otherwise.",
"inputs": "zp_5F, zp_79, zp_6D",
"outputs": "A/Z, zp_79",
"confidence": "medium"
},
{
"addr": "0EFE",
"name": "stopAllSound",
"summary": "Plays 'sound 0' = silences all voices ($C8A8 with A=0). NOTE: the seed labelled this loadRuntimeOverlays, but the overlay loading actually starts at $0F03.",
"inputs": "none",
"outputs": "$92E7-$92EA",
"confidence": "high"
},
{
"addr": "0F03",
"name": "loadRuntimeOverlays",
"summary": "Called once from mainStart: $C000 <- track 30 s0-15, then loadOverlayA (+strings+comm tail), then falls into loadCommModule.",
"inputs": "$0BA5",
"outputs": "$C000-$CFFF, $6F00-$89FF, $E000-$EFFF",
"confidence": "high"
},
{
"addr": "0F16",
"name": "loadCommModule",
"summary": "Loads the communications module to $E000: build 1 from track 35 s0-11 when linkTypeFlag ($0BA5) bit 7 is set (then NMI vector -> RTI), otherwise build 2 from track 18 s7 + track 34 s1-11; ends in initCommModule.",
"inputs": "$0BA5",
"outputs": "$E000-$EBFF, comm module initialised",
"confidence": "high"
},
{
"addr": "0F31",
"name": "loadCommBuild2",
"summary": "Loads comm build 2: track 18 sector 7 to $E000 then track 34 s1-11 to $E100.",
"inputs": "loadDest=$E000",
"outputs": "$E000-$EBFF",
"confidence": "high"
},
{
"addr": "0F41",
"name": "loadCommFinish",
"summary": "Shared tail: loadSectors with the prepared registers, then initCommModule.",
"inputs": "A,X,Y for loadSectors",
"outputs": "comm module loaded and initialised",
"confidence": "high"
},
{
"addr": "0F44",
"name": "initCommModule",
"summary": "Calls comm entry $E003 with X=1 (initialise), installs scanKeyboard ($0DB7) into the comm module's keyboard vector $E013/$E014 and enables interrupts.",
"inputs": "comm module at $E000",
"outputs": "$E013/$E014, I flag cleared",
"confidence": "high"
},
{
"addr": "0F55",
"name": "loadSectorsDelayed",
"summary": "Waits 8 frames ($C37F is a raster-timed frame delay) then performs loadSectors. Seed name loadSectorsRetry.",
"inputs": "A=count, Y=track, X=sector",
"outputs": "as loadSectors",
"confidence": "high"
},
{
"addr": "0F5F",
"name": "suspendCommModule",
"summary": "If the link is active ($E03B != 0) writes request $C0 to $E039 and waits until $E03A bit 7 is set (comm module acknowledges it is paused); preserves A. Used before disk access, which needs interrupts off.",
"inputs": "$E03B",
"outputs": "$E039=$C0",
"confidence": "medium"
},
{
"addr": "0F71",
"name": "resumeCommModule",
"summary": "If the link is active writes 0 to $E039 and waits until $E03A bit 7 clears (comm module resumed).",
"inputs": "$E03B",
"outputs": "$E039=0",
"confidence": "medium"
},
{
"addr": "0FB1",
"name": "checkDiskId",
"summary": "Reads the BAM (track 18 sector 0) to $0200 and compares the disk id at $02A2/$02A3 with diskIdTable entry X (X=0 'OZ' game disk, X=2 'EA' data disk; X saved in $92F0). For the game disk in non-solo mode copies the 10-byte name/serial at $02AB to $FFDE. A on entry is remembered: bit 7 set = fail silently, otherwise shows 'INSERT GAME DISK.' or 'NOT A DATA DISK!'.",
"inputs": "A=silent flag, X=id index",
"outputs": "C=0 ok, C=1 wrong disk/error, $FFDE-$FFE7, $92F0",
"confidence": "high"
},
{
"addr": "1018",
"name": "diskErrorExit",
"summary": "Shows 'DISK ERROR.' through showDiskMessageAndFail.",
"inputs": "none",
"outputs": "C=1",
"confidence": "high"
},
{
"addr": "101C",
"name": "showDiskMessageAndFail",
"summary": "Plays sound 3, shows the message at A/Y, waits 100 frames, clears the message area ($C39F) and returns C=1.",
"inputs": "A/Y=message",
"outputs": "C=1",
"confidence": "medium"
},
{
"addr": "1030",
"name": "reloadOverlayForVariant",
"summary": "Suspends the comm module, waits for the game disk, then reloads according to overlayVariantFlag ($87FF): bit 7 clear -> strings and comm tail only (variant B stays), set -> overlay A (+strings+tail); finally resumes the comm module.",
"inputs": "$87FF",
"outputs": "overlays reloaded",
"confidence": "medium"
},
{
"addr": "1047",
"name": "loadOverlayA",
"summary": "$6F00 <- track 31 s0-16, $8000 <- track 32 s0-7, then falls into loadStringsAndCommTail.",
"inputs": "none",
"outputs": "$6F00-$87FF variant A",
"confidence": "high"
},
{
"addr": "1060",
"name": "loadStringsAndCommTail",
"summary": "$8800 <- track 29 s9-10 (message strings) then falls into loadCommTailBuild2.",
"inputs": "none",
"outputs": "$8800-$89FF, $EC00-$EFFF",
"confidence": "high"
},
{
"addr": "1070",
"name": "loadCommTailBuild2",
"summary": "$EC00 <- track 34 s12-15 (comm build 2 tail). Also called from overlay A ($7B33) to restore it after a sub-overlay was loaded there.",
"inputs": "none",
"outputs": "$EC00-$EFFF",
"confidence": "high"
},
{
"addr": "1080",
"name": "loadOverlayBIfNeeded",
"summary": "Loads overlay B when overlayVariantFlag ($87FF) bit 7 is clear (i.e. the resident overlay should be B), else returns.",
"inputs": "$87FF",
"outputs": "see loadOverlayB",
"confidence": "high"
},
{
"addr": "1086",
"name": "loadOverlayB",
"summary": "Suspends the comm module, $6F00 <- track 33 s0-16, $8000 <- track 32 s8-15, patches overlay byte $750A with the value at $206B, then resumes the comm module.",
"inputs": "$206B",
"outputs": "$6F00-$87FF variant B, $750A",
"confidence": "high"
},
{
"addr": "10AB",
"name": "loadMapAndSetup",
"summary": "$F000 <- track 18 s8-14 (40x40 battlefield map) then falls into loadSetupBlock.",
"inputs": "none",
"outputs": "$F000-$F6FF, $0200-$03FF",
"confidence": "high"
},
{
"addr": "10BB",
"name": "loadSetupBlock",
"summary": "$0200 <- track 18 s15-16 (saved setup block including the code at $0300).",
"inputs": "none",
"outputs": "$0200-$03FF",
"confidence": "high"
},
{
"addr": "10CB",
"name": "suspendCommForDiskAccess",
"summary": "suspendCommModule then $C853 (sets messageState=1 and shows the message line - presumably a loading notice).",
"inputs": "none",
"outputs": "$92A1=1",
"confidence": "medium"
},
{
"addr": "10D1",
"name": "resumeCommAfterDiskAccess",
"summary": "resumeCommModule, clears messageState ($92A1) and redraws/clears the message area via $C39F.",
"inputs": "none",
"outputs": "$92A1=0",
"confidence": "medium"
},
{
"addr": "10DC",
"name": "waitForGameDiskInserted",
"summary": "Suspends the comm module and loops: checkDiskId(X=0, verbose); on failure calls $C85B (shows 'press button' prompt and waits for fire) and retries until the game disk is present.",
"inputs": "none",
"outputs": "returns only when the OZ disk is in the drive",
"confidence": "high"
},
{
"addr": "10F1",
"name": "rasterIrqHandler",
"summary": "Raster IRQ at line $FA (and BRK handler). Ignores BRK (jumps through the patchable operand at $10FF). Re-arms the raster line and vector, guards against re-entry with irqInProgress; copies the VIC shadow registers ($9000-$9006) and calls comm module entries $E000 (X=0) and $E00C; returns early if loadingDepth bit 7 is set. Then copies the sprite shadows (colours with 2-nibble flashing, positions plus fine-scroll offsets from $92F4/$92F5 when $92C8 != 0, MSB, expand, enable), bumps the 24-bit frame counter, enables IRQs, and unless inputLockoutTimer is counting down polls the keyboard (pollKeyboardEvent) and joystick port 2 into joyDirection/joyFireReleased. Updates joystick idle/centre counters, the RNG, six countdown timers zp_6C-zp_71, detects SHIFT+C=+F7 (abort to main menu), blinks the cursor colour, updates frame timers, handles chat send/receive, sound tick ($C8E9) and overlay hooks $6728/$6637.",
"inputs": "VIC, CIA1, shadows at $9000-$9024, comm module",
"outputs": "VIC registers, zp_45-zp_47, zp_5D/zp_5F/zp_60/zp_61, zp_66, timers, chat buffers",
"confidence": "high"
},
{
"addr": "1293",
"name": "irqExit",
"summary": "Restores Y, X, A and RTIs; also the BRK exit target patched into $10FF/$1100.",
"inputs": "stack",
"outputs": "none",
"confidence": "high"
},
{
"addr": "1298",
"name": "irqRti",
"summary": "A bare RTI used as the NMI vector target (and the IRQ vector during start-up).",
"inputs": "none",
"outputs": "none",
"confidence": "high"
},
{
"addr": "129C",
"name": "updateFrameTimers",
"summary": "Per-frame housekeeping: increments $92F8/$92FB; every 8 frames bumps $9223 (while negative, saturating) and $92C9 (while positive, up to $80) and decrements zp_7F; every 4 frames decrements zp_72 and $9163; every frame decrements $90FC, $91C8, $9234 (unless $80) and $923E while nonzero.",
"inputs": "zp_45, zp_B1",
"outputs": "the timers listed",
"confidence": "medium"
},
{
"addr": "130A",
"name": "stepRandomSeed",
"summary": "Seeds the 24-bit RNG state zp_57-zp_59 with the joystick direction when it is all zero, then steps the LFSR (sub_FD5D).",
"inputs": "zp_57-zp_59, zp_5D",
"outputs": "zp_57-zp_59, A=random byte",
"confidence": "medium"
},
{
"addr": "131B",
"name": "blinkCursorColor",
"summary": "Every 30 frames (counter at $131A) toggles bit 0 of sprite colour shadow $9008 and copies it to $900A - a blinking cursor sprite colour; only run when currentScreen=0 and zp_B1 is non-negative.",
"inputs": "$131A, $9008",
"outputs": "$9008, $900A",
"confidence": "medium"
},
{
"addr": "1331",
"name": "handleChatSend",
"summary": "Chat/message-typing state machine run each frame when the link is active ($E03B) and $92E6 is zero: RETURN ($8D in newKeyEvent) or chatState $7F enters typing mode (chatState=$C0, $9278=$9E, sound 2); when chatState bit 7 is set it sends the pending character count/marker to the remote via $C7D5 and comm entry $E009. Clears chatState when the link is down.",
"inputs": "$929C, $90EA, $E03B, $E02E, $92E6",
"outputs": "$929C, $9278, $90EA, $9248",
"confidence": "low"
},
{
"addr": "1399",
"name": "receiveChatCharacter",
"summary": "Fetches one received byte from the comm module ($E006; C=1 none) and, for codes >= $80, handles RETURN (marks message complete, $9277=$80, sound 5), DEL (backspace in the incoming message) or appends the character via appendChatCharacter.",
"inputs": "$E006 result, $9277",
"outputs": "$9253 buffer, $9277, zp_72",
"confidence": "medium"
},
{
"addr": "13C4",
"name": "appendChatCharacter",
"summary": "Appends character A to the 36-byte incoming message buffer at $9253 (index $9277, scrolling the buffer left when full) and rewrites the text with bit 7 set into the display line at $0549 (with $0548=$1E).",
"inputs": "A=char, $9277",
"outputs": "$9253-$9276, $0548-$056C, $9277",
"confidence": "medium"
},
{
"addr": "1414",
"name": "enterMainMenuKeepSelection",
"summary": "Main-menu entry from the F-key screen switch at $076D: picks the default item (selectDefaultMenuItem), clears the text window and joins mainMenuLoop at the item-changed handler.",
"inputs": "none",
"outputs": "menuSelection",
"confidence": "medium"
},
{
"addr": "141D",
"name": "enterMainMenu",
"summary": "Main-menu entry from the F7 screen switch at $0706: sets $1412=$7F, menuSelection from linkState bit 7, draws the menu (showMainMenu) and enters mainMenuLoop.",
"inputs": "$922A",
"outputs": "menuSelection, screen",
"confidence": "medium"
},
{
"addr": "1439",
"name": "mainMenuLoop",
"summary": "Main menu: calls sub_40B2/sub_16B7 (highlight blink), redraws link-dependent text, checks F-key screen switches (sub_06BA), moves the selection with joystick left/right among 3 items (20-frame repeat, sound 2) and on fire dispatches through menuItemHandlerTable; when the selection changes it draws the item panel (item 0 = game setup summary, 1 = sub_175A, 2 = sub_19F3).",
"inputs": "zp_5D, zp_6C, $90F9",
"outputs": "$90F9 menuSelection, screen",
"confidence": "medium"
},
{
"addr": "14F0",
"name": "snapshotSetupId",
"summary": "Copies $9241-$9243 to $92D7-$92D9 and map-table byte $F9C4[$9236] & $3F to $92D6 - remembers the identity of the current setup.",
"inputs": "$9241-$9243, $9236",
"outputs": "$92D6-$92D9",
"confidence": "low"
},
{
"addr": "1507",
"name": "selectDefaultMenuItem",
"summary": "Chooses the default main-menu item: 2 if $0BA8 is nonzero, else 1 if linkState bit 7 is set or the current setup differs from the snapshot in $92D6-$92D9, else 2.",
"inputs": "$0BA8, $922A, $9241-$9243, $92D6-$92D9",
"outputs": "$90F9",
"confidence": "medium"
},
{
"addr": "153B",
"name": "showMainMenu",
"summary": "Draws the main-menu screen: clears $2A29-$2A2C, background colour shadow $9004=$0C, $9226=0, zp_41=1, draws the title/credits text (sub_1643), the menu frame (drawMenuFrame) and continues at $C5D7.",
"inputs": "none",
"outputs": "screen",
"confidence": "medium"
},
{
"addr": "1565",
"name": "drawMenuFrame",
"summary": "Draws two frame elements (sub_2C7F with X=$18 and $19) then falls into clearMenuTextWindow.",
"inputs": "none",
"outputs": "screen",
"confidence": "low"
},
{
"addr": "156F",
"name": "clearMenuTextWindow",
"summary": "Sets the menu text attribute and window bounds and clears the window ($C06A).",
"inputs": "$922A, $90F9",
"outputs": "zp_39-zp_40, zp_3D",
"confidence": "low"
},
{
"addr": "1575",
"name": "setMenuTextWindow",
"summary": "Sets the text attribute from link state (sub_16EB) then the menu window bounds.",
"inputs": "$922A",
"outputs": "zp_3D, zp_39-zp_40",
"confidence": "low"
},
{
"addr": "1578",
"name": "setMenuWindowBounds",
"summary": "Text window for the menu panel: cursor col 0, top row 7, cursor row 7, left column 2, bottom 22, right column 36.",
"inputs": "none",
"outputs": "zp_39=7, zp_3A=$16, zp_3B=2, zp_3C=$24, zp_3F=0, zp_40=7",
"confidence": "medium"
},
{
"addr": "158F",
"name": "clearMenuWindowPlain",
"summary": "Attribute 0, menu window bounds, clear window; jumped to from $2E0A.",
"inputs": "none",
"outputs": "zp_3D=0, screen",
"confidence": "low"
},
{
"addr": "1599",
"name": "drawGameSetupSummary",
"summary": "Main-menu item 0 panel: prints 'STANDARD' or 'CUSTOM' (gameOptions $0BA3 bit 6), the game type name from the 8-byte table at $CCC8 (Scrimmage/QB Sneak/The Bomb/Face-Off/Sluggers/Full War/Defender, $0BA3 & 7), '1ST PLAY'/'2ND PLAY' from firstPlayFlag ($0BA1), the owner name string and overlay text $86AC, and for custom games the text at $0636.",
"inputs": "$0BA3, $0BA1, $0B7F",
"outputs": "screen, zp_3B/zp_3D/zp_3F",
"confidence": "medium"
}
],
"variables": [
{
"addr": "18",
"scope": "zp",
"name": "tempByte18",
"meaning": "scratch (seen-nonspace flag in buildOwnerNameString, map index in manualLookupCheck, string lo in $C100)",
"confidence": "medium"
},
{
"addr": "33",
"scope": "zp",
"name": "spriteFlashPhase",
"meaning": "frame counter bits 0/1 rotated into bits 7/6 by the IRQ; bit 7 gates the two-colour sprite flashing",
"confidence": "medium"
},
{
"addr": "38",
"scope": "zp",
"name": "irqInProgress",
"meaning": "re-entrancy guard of rasterIrqHandler (nonzero while the handler runs with interrupts enabled)",
"confidence": "high"
},
{
"addr": "39",
"scope": "zp",
"name": "textWindowTop",
"meaning": "text window top row used by the $C000 text routines ($C06A copies it to zp_40)",
"confidence": "medium"
},
{
"addr": "3A",
"scope": "zp",
"name": "textWindowBottom",
"meaning": "text window bottom row / height ($16 for the menu panel)",
"confidence": "low"
},
{
"addr": "3B",
"scope": "zp",
"name": "textWindowLeft",
"meaning": "text window left column; $C099 positions the cursor at zp_3B+zp_3F",
"confidence": "medium"
},
{
"addr": "3C",
"scope": "zp",
"name": "textWindowRight",
"meaning": "text window right column ($24)",
"confidence": "low"
},
{
"addr": "3D",
"scope": "zp",
"name": "textAttribute",
"meaning": "current text colour/attribute byte (e.g. $6F, $61, $21); saved and replaced by zp_42 in $C133 when a message is active",
"confidence": "medium"
},
{
"addr": "3F",
"scope": "zp",
"name": "textCursorCol",
"meaning": "cursor column relative to the window left edge",
"confidence": "medium"
},
{
"addr": "40",
"scope": "zp",
"name": "textCursorRow",
"meaning": "cursor row",
"confidence": "medium"
},
{
"addr": "41",
"scope": "zp",
"name": "flag41",
"meaning": "set to 1 by showMainMenu, cleared by sub_1FD0 (battle start); menu-vs-battle state flag",
"confidence": "low"
},
{
"addr": "42",
"scope": "zp",
"name": "messageAttribute",
"meaning": "text attribute used while a message is displayed",
"confidence": "low"
},
{
"addr": "45",
"scope": "zp",
"name": "frameCounter",
"meaning": "low byte of the 24-bit frame counter incremented by the IRQ (zp_45-zp_47)",
"confidence": "high"
},
{
"addr": "46",
"scope": "zp",
"name": "frameCounterMid",
"meaning": "middle byte of the frame counter",
"confidence": "high"
},
{
"addr": "47",
"scope": "zp",
"name": "frameCounterHi",
"meaning": "high byte of the frame counter",
"confidence": "high"
},
{
"addr": "48",
"scope": "zp",
"name": "fillPtr",
"meaning": "pointer (lo) used for memory fills (resetGameVariables) and screen writes",
"confidence": "medium"
},
{
"addr": "49",
"scope": "zp",
"name": "fillPtrHi",
"meaning": "pointer (hi)",
"confidence": "medium"
},
{
"addr": "4A",
"scope": "zp",
"name": "fillEndPtr",
"meaning": "end address (lo) compared by $C3F9",
"confidence": "medium"
},
{
"addr": "4B",
"scope": "zp",
"name": "fillEndPtrHi",
"meaning": "end address (hi)",
"confidence": "medium"
},
{
"addr": "57",
"scope": "zp",
"name": "rngState",
"meaning": "24-bit LFSR random state (zp_57-zp_59) stepped by sub_FD5D; seeded from the joystick direction when zero",
"confidence": "high"
},
{
"addr": "58",
"scope": "zp",
"name": "rngStateMid",
"meaning": "random state byte 2",
"confidence": "high"
},
{
"addr": "59",
"scope": "zp",
"name": "rngStateHi",
"meaning": "random state byte 3",
"confidence": "high"
},
{
"addr": "5A",
"scope": "zp",
"name": "manualAnswer0",
"meaning": "first byte of the player's answer to the manual look-up check (typed via sub_7717)",
"confidence": "medium"
},
{
"addr": "5B",
"scope": "zp",
"name": "manualAnswer1",
"meaning": "second answer byte",
"confidence": "medium"
},
{
"addr": "5C",
"scope": "zp",
"name": "manualAnswer2",
"meaning": "third answer byte",
"confidence": "medium"
},
{
"addr": "5D",
"scope": "zp",
"name": "joyDirection",
"meaning": "joystick port 2 direction bits, active low: bit0 up, bit1 down, bit2 left, bit3 right; $0F = centred (also $0F when a key < $80 is held or a key event is pending)",
"confidence": "high"
},
{
"addr": "5F",
"scope": "zp",
"name": "joyFireReleased",
"meaning": "1 when the fire button is up, 0 when pressed (CIA bit 4)",
"confidence": "high"
},
{
"addr": "60",
"scope": "zp",
"name": "joyDirectionCopy",
"meaning": "second copy of joyDirection written by the IRQ at the same time (separate consumer, probably the comm/transmit side)",
"confidence": "low"
},
{
"addr": "61",
"scope": "zp",
"name": "joyFireReleasedCopy",
"meaning": "second copy of joyFireReleased",
"confidence": "low"
},
{
"addr": "66",
"scope": "zp",
"name": "currentKey",
"meaning": "key code from the latest poll ($FF none; 0-3 = F1/F3/F5/F7, >= $80 PETSCII-like); forced to $FF while loading",
"confidence": "high"
},
{
"addr": "6C",
"scope": "zp",
"name": "joyRepeatTimer",
"meaning": "frame countdown (IRQ decrements zp_6C-zp_71): joystick auto-repeat delay",
"confidence": "high"
},
{
"addr": "6D",
"scope": "zp",
"name": "fireDebounceTimer",
"meaning": "frame countdown set to 10 after a fire press",
"confidence": "high"
},
{
"addr": "6E",
"scope": "zp",
"name": "menuBlinkTimer",
"meaning": "frame countdown (10) used by sub_16B7 to blink the highlighted menu item",
"confidence": "medium"
},
{
"addr": "6F",
"scope": "zp",
"name": "countdownTimer6F",
"meaning": "general frame countdown decremented by the IRQ",
"confidence": "low"
},
{
"addr": "70",
"scope": "zp",
"name": "keyRepeatTimer",
"meaning": "frame countdown (20) before a held key repeats",
"confidence": "high"
},
{
"addr": "71",
"scope": "zp",
"name": "countdownTimer71",
"meaning": "general frame countdown decremented by the IRQ",
"confidence": "low"
},
{
"addr": "72",
"scope": "zp",
"name": "messageTimer72",
"meaning": "countdown decremented every 4 frames; cleared when the message queue is reset or a chat message completes",
"confidence": "low"
},
{
"addr": "79",
"scope": "zp",
"name": "fireWasReleased",
"meaning": "latch used by checkFirePressed to detect a new press (nonzero after the button was seen released)",
"confidence": "medium"
},
{
"addr": "7F",
"scope": "zp",
"name": "slowTimer7F",
"meaning": "countdown decremented every 8 frames",
"confidence": "low"
},
{
"addr": "AA",
"scope": "zp",
"name": "rasterIrqLine",
"meaning": "raster line re-armed by the IRQ handler ($FA)",
"confidence": "high"
},
{
"addr": "AB",
"scope": "zp",
"name": "irqVectorLo",
"meaning": "IRQ handler address (lo) re-installed into $FFFE each interrupt",
"confidence": "high"
},
{
"addr": "AC",
"scope": "zp",
"name": "irqVectorHi",
"meaning": "IRQ handler address (hi) re-installed into $FFFF each interrupt",
"confidence": "high"
},
{
"addr": "B1",
"scope": "zp",
"name": "gameRunState",
"meaning": "$FF after resetGameVariables, 0 at game start; bit 7 set suppresses the cursor blink; compared with $FF in updateFrameTimers",
"confidence": "low"
},
{
"addr": "E0",
"scope": "zp",
"name": "ldTrack",
"meaning": "loader: current track",
"confidence": "high"
},
{
"addr": "E1",
"scope": "zp",
"name": "ldSector",
"meaning": "loader: current sector",
"confidence": "high"
},
{
"addr": "E7",
"scope": "zp",
"name": "ldCount",
"meaning": "loader: sectors left",
"confidence": "high"
},
{
"addr": "0548",
"scope": "abs",
"name": "chatLineMarker",
"meaning": "byte before the displayed chat line: $1E after a received character, $9E when reset ($C62A, initIrqAndInputState)",
"confidence": "low"
},
{
"addr": "0549",
"scope": "abs",
"name": "chatDisplayLine",
"meaning": "36-byte copy of the incoming chat message with bit 7 set on every character",
"confidence": "medium"
},
{
"addr": "0B7D",
"scope": "abs",
"name": "inputLockoutTimer",
"meaning": "frames during which the IRQ skips keyboard/joystick polling (set to $9E at init, $FF on abort)",
"confidence": "high"
},
{
"addr": "0B7E",
"scope": "abs",
"name": "setupByte0B7E",
"meaning": "settings-block byte ($DB) read by the $EC00 sub-overlay at $ED89",
"confidence": "low"
},
{
"addr": "0B7F",
"scope": "abs",
"name": "ownerNameString",
"meaning": "21-byte owner name built from $FFDE/$FFE8, bit-7 terminated at $0B93; printed in the setup summary",
"confidence": "medium"
},
{
"addr": "0B94",
"scope": "abs",
"name": "setupByte0B94",
"meaning": "settings-block byte read by $5829/$58F2/$ED83",
"confidence": "low"
},
{
"addr": "0B95",
"scope": "abs",
"name": "mapSeed",
"meaning": "3 bytes ($0B95-$0B97) that the map drawing code (sub_6F04 etc.) consumes; manualLookupCheck temporarily replaces them with the manual answer entry so the protection map can be drawn",
"confidence": "low"
},
{
"addr": "0B9B",
"scope": "abs",
"name": "soloModeFlag",
"meaning": "bit 7 set = no-opponent/solo mode: skips modem handling, disk-serial copy and link messages (initial $FF)",
"confidence": "medium"
},
{
"addr": "0B9C",
"scope": "abs",
"name": "linkOptionFlag",
"meaning": "bit 7 set skips comm module initialisation and the modem prompt at game start (related start-up option, exact meaning unknown)",
"confidence": "low"
},
{
"addr": "0B9F",
"scope": "abs",
"name": "playerSide",
"meaning": "this player's side index (0/1; $FE = not chosen yet); overlay code uses it EOR 1 as the opponent index",
"confidence": "medium"
},
{
"addr": "0BA0",
"scope": "abs",
"name": "playerSideSetup",
"meaning": "side chosen in the setup screens, copied to playerSide at game start",
"confidence": "low"
},
{
"addr": "0BA1",
"scope": "abs",
"name": "firstPlayFlag",
"meaning": "nonzero = this player moves first ('1ST PLAY' in the setup summary)",
"confidence": "medium"
},
{
"addr": "0BA3",
"scope": "abs",
"name": "gameOptions",
"meaning": "bits 0-2 = game type index into the names at $CCC8 (0 Scrimmage, 1 QB Sneak, 2 The Bomb, 3 Face-Off, 4 Sluggers, 5 Full War, 6 Defender), bit 6 = custom rules",
"confidence": "medium"
},
{
"addr": "0BA5",
"scope": "abs",
"name": "linkTypeFlag",
"meaning": "bit 7 selects the comm module build (set = build 1 from track 35; clear = build 2, the Hayes modem build with the Answer/Originate prompt); compared with $E030 to detect a needed reload",
"confidence": "medium"
},
{
"addr": "0BA6",
"scope": "abs",
"name": "prevLinkTypeFlag",
"meaning": "previous value of linkTypeFlag; a change from set to clear triggers the 'GET OPPONENT ON PHONE' prompt",
"confidence": "medium"
},
{
"addr": "0BA7",
"scope": "abs",
"name": "loadingDepth",
"meaning": "nonzero while disk loads are in progress (keys suppressed); bit 7 makes the IRQ return right after servicing the comm module",
"confidence": "medium"
},
{
"addr": "0BA8",
"scope": "abs",
"name": "resumeGameFlag",
"meaning": "nonzero makes menu item 2 the default and alters many screens; probably 'a game is in progress / saved'",
"confidence": "low"
},
{
"addr": "0BA9",
"scope": "abs",
"name": "setupName0BA9",
"meaning": "6 bytes (shifted spaces by default) saved/restored around the manual check and copied from $0BAF by sub_1C87; a setup text field",
"confidence": "low"
},
{
"addr": "0BAF",
"scope": "abs",
"name": "setupName0BAF",
"meaning": "6-byte backup of setupName0BA9",
"confidence": "low"
},
{
"addr": "0C8A",
"scope": "abs",
"name": "perSideValueTable",
"meaning": "2 bytes ($31,$63) indexed by player side in sub_3F57 and others",
"confidence": "low"
},
{
"addr": "10EF",
"scope": "abs",
"name": "spriteEnableScratch",
"meaning": "copy of the sprite enable shadow written by the IRQ each frame",
"confidence": "medium"
},
{
"addr": "10FF",
"scope": "abs",
"name": "irqBrkVector",
"meaning": "operand of the JMP taken when the interrupt was caused by BRK (normally irqExit $1293)",
"confidence": "high"
},
{
"addr": "131A",
"scope": "abs",
"name": "cursorBlinkTimer",
"meaning": "30-frame countdown for blinkCursorColor",
"confidence": "high"
},
{
"addr": "1412",
"scope": "abs",
"name": "menuFlag1412",
"meaning": "set to $7F by enterMainMenu; not read inside this chunk",
"confidence": "low"
},
{
"addr": "1413",
"scope": "abs",
"name": "menuLinkStateCopy",
"meaning": "copy of linkState ($922A) taken when the menu panel was drawn; a bit-7 change triggers a redraw",
"confidence": "medium"
},
{
"addr": "9000",
"scope": "abs",
"name": "vicCtrl1Shadow",
"meaning": "shadow of $D011 copied by the IRQ",
"confidence": "high"
},
{
"addr": "9001",
"scope": "abs",
"name": "vicCtrl2Shadow",
"meaning": "shadow of $D016",
"confidence": "high"
},
{
"addr": "9002",
"scope": "abs",
"name": "vicMemPtrShadow",
"meaning": "shadow of $D018",
"confidence": "high"
},
{
"addr": "9003",
"scope": "abs",
"name": "borderColorShadow",
"meaning": "shadow of $D020",
"confidence": "high"
},
{
"addr": "9004",
"scope": "abs",
"name": "bgColorShadow",
"meaning": "shadow of $D021",
"confidence": "high"
},
{
"addr": "9005",
"scope": "abs",
"name": "bgColor1Shadow",
"meaning": "shadow of $D022",
"confidence": "high"
},
{
"addr": "9006",
"scope": "abs",
"name": "bgColor2Shadow",
"meaning": "shadow of $D023",
"confidence": "high"
},
{
"addr": "9008",
"scope": "abs",
"name": "spriteColorShadow",
"meaning": "8 bytes -> $D027-$D02E; values >= $10 hold two colours in the nibbles that alternate every other frame pair (flashing)",
"confidence": "high"
},
{
"addr": "9010",
"scope": "abs",
"name": "spriteXShadow",
"meaning": "8 sprite X positions (low byte), copied to $D000.. plus the fine-scroll X offset",
"confidence": "high"
},
{
"addr": "9018",
"scope": "abs",
"name": "spriteYShadow",
"meaning": "8 sprite Y positions, copied to $D001.. plus the fine-scroll Y offset",
"confidence": "high"
},
{
"addr": "9020",
"scope": "abs",
"name": "spriteXMsbShadow",
"meaning": "shadow of $D010",
"confidence": "high"
},
{
"addr": "9022",
"scope": "abs",
"name": "spriteEnableShadow",
"meaning": "shadow of $D015",
"confidence": "high"
},
{
"addr": "9023",
"scope": "abs",
"name": "spriteXExpandShadow",
"meaning": "shadow of $D01D",
"confidence": "high"
},
{
"addr": "9024",
"scope": "abs",
"name": "spriteYExpandShadow",
"meaning": "shadow of $D017",
"confidence": "high"
},
{
"addr": "90E9",
"scope": "abs",
"name": "lastKeyCode",
"meaning": "key code seen on the previous poll (repeat detection), $FF none",
"confidence": "high"
},
{
"addr": "90EA",
"scope": "abs",
"name": "newKeyEvent",
"meaning": "key code of a fresh press/repeat this frame, $FF none; consumers reset it to $FF",
"confidence": "high"
},
{
"addr": "90EE",
"scope": "abs",
"name": "joyIdleCounter",
"meaning": "counts frames with the stick centred and fire released (saturates at $FF); reset while the stick is deflected",
"confidence": "medium"
},
{
"addr": "90F9",
"scope": "abs",
"name": "menuSelection",
"meaning": "main-menu item index 0-2",
"confidence": "high"
},
{
"addr": "90FB",
"scope": "abs",
"name": "currentScreen",
"meaning": "active screen selected by F1/F3/F5/F7 key codes 0-3 (0 = battlefield, 1 = overlay screen $7587, 2 = overlay screen $7FE6, 3 = main menu)",
"confidence": "medium"
},
{
"addr": "90FC",
"scope": "abs",
"name": "countdown90FC",
"meaning": "per-frame countdown timer",
"confidence": "medium"
},
{
"addr": "9163",
"scope": "abs",
"name": "countdown9163",
"meaning": "countdown decremented every 4 frames",
"confidence": "medium"
},
{
"addr": "91C8",
"scope": "abs",
"name": "countdown91C8",
"meaning": "per-frame countdown timer",
"confidence": "medium"
},
{
"addr": "91C9",
"scope": "abs",
"name": "joyCenteredMask",
"meaning": "cleared by the IRQ while the stick is centred, set to $7F by setJoystickRepeatDelay; masks joyHoldCounter",
"confidence": "medium"
},
{
"addr": "91CA",
"scope": "abs",
"name": "joyHoldCounter",
"meaning": "number of consecutive auto-repeat steps for a held joystick direction",
"confidence": "medium"
},
{
"addr": "9223",
"scope": "abs",
"name": "counter9223",
"meaning": "incremented every 8 frames while negative (saturates at $FF)",
"confidence": "low"
},
{
"addr": "922A",
"scope": "abs",
"name": "linkState",
"meaning": "bit 7 = link/modem session state used to choose menu text colours and default item; bits 6/4-5 set in the $0709 screen code",
"confidence": "low"
},
{
"addr": "9234",
"scope": "abs",
"name": "countdown9234",
"meaning": "per-frame countdown that stops at 0 or $80",
"confidence": "low"
},
{
"addr": "9236",
"scope": "abs",
"name": "currentMapIndex",
"meaning": "index into the table at $F9C4 (low 6 bits of the entry identify the map/setup)",
"confidence": "low"
},
{
"addr": "923E",
"scope": "abs",
"name": "countdown923E",
"meaning": "per-frame countdown timer",
"confidence": "medium"
},
{
"addr": "9248",
"scope": "abs",
"name": "lastTypedKey",
"meaning": "last key code >= $80 (PETSCII-like, shift-translated); menus poll it; $FF none",
"confidence": "high"
},
{
"addr": "9251",
"scope": "abs",
"name": "messageQueueHead",
"meaning": "message queue index (9-entry queue at $9249-$9251 managed by $C566/$C6EF)",
"confidence": "low"
},
{
"addr": "9252",
"scope": "abs",
"name": "messageQueueTail",
"meaning": "message queue index",
"confidence": "low"
},
{
"addr": "9253",
"scope": "abs",
"name": "chatInputBuffer",
"meaning": "36-byte buffer of the incoming chat message",
"confidence": "medium"
},
{
"addr": "9277",
"scope": "abs",
"name": "chatInputLength",
"meaning": "length/cursor of the incoming chat message; $80 = message complete",
"confidence": "medium"
},
{
"addr": "9278",
"scope": "abs",
"name": "chatFlag9278",
"meaning": "set to $9E when chat typing mode is entered",
"confidence": "low"
},
{
"addr": "929C",
"scope": "abs",
"name": "chatState",
"meaning": "chat typing state: 0 idle, $C0 typing, $7F pending, bit 7 = pending send",
"confidence": "low"
},
{
"addr": "92A0",
"scope": "abs",
"name": "rawKeyCode",
"meaning": "raw result of the comm module key poll ($E015)",
"confidence": "high"
},
{
"addr": "92A1",
"scope": "abs",
"name": "messageState",
"meaning": "message-line state (0 none, 1 loading notice, $21 modem prompt) passed to $C6FF after a retried load",
"confidence": "low"
},
{
"addr": "92C7",
"scope": "abs",
"name": "retryCounter",
"meaning": "manual-check tries left; cleared at game start",
"confidence": "medium"
},
{
"addr": "92C8",
"scope": "abs",
"name": "spriteScrollActive",
"meaning": "nonzero = add the fine-scroll offsets from $92F4/$92F5 to the sprite positions",
"confidence": "medium"
},
{
"addr": "92C9",
"scope": "abs",
"name": "counter92C9",
"meaning": "incremented every 8 frames while positive, saturating at $80",
"confidence": "low"
},
{
"addr": "92D6",
"scope": "abs",
"name": "setupIdSnapshot",
"meaning": "4 bytes: map id and $9241-$9243 copied by snapshotSetupId, compared by selectDefaultMenuItem",
"confidence": "low"
},
{
"addr": "92E4",
"scope": "abs",
"name": "shiftKeyFlag",
"meaning": "$80 when a shift key is held (from scanKeyboard)",
"confidence": "high"
},
{
"addr": "92E5",
"scope": "abs",
"name": "commodoreKeyFlag",
"meaning": "$80 when the C= key is held",
"confidence": "high"
},
{
"addr": "92E6",
"scope": "abs",
"name": "chatDisabled",
"meaning": "nonzero disables chat typing",
"confidence": "low"
},
{
"addr": "92F0",
"scope": "abs",
"name": "diskIdIndex",
"meaning": "diskIdTable index passed to checkDiskId (0 game disk, 2 data disk)",
"confidence": "high"
},
{
"addr": "92F4",
"scope": "abs",
"name": "scrollFineX",
"meaning": "low 3 bits = sprite X fine-scroll offset",
"confidence": "medium"
},
{
"addr": "92F5",
"scope": "abs",
"name": "scrollFineY",
"meaning": "low 3 bits minus 3 = sprite Y fine-scroll offset",
"confidence": "medium"
},
{
"addr": "92F8",
"scope": "abs",
"name": "frameTick92F8",
"meaning": "incremented every frame by updateFrameTimers",
"confidence": "high"
},
{
"addr": "92FB",
"scope": "abs",
"name": "frameTick92FB",
"meaning": "incremented every frame by updateFrameTimers",
"confidence": "high"
},
{
"addr": "92FE",
"scope": "abs",
"name": "linkTypeAtStart",
"meaning": "copy of linkTypeFlag taken at game start",
"confidence": "low"
},
{
"addr": "E011",
"scope": "abs",
"name": "commAnswerOriginate",
"meaning": "comm module flag: 0 = answer ('A'), 1 = originate ('O')",
"confidence": "high"
},
{
"addr": "E013",
"scope": "abs",
"name": "commKeyScanVector",
"meaning": "operand of the JMP at $E012; the game points it at scanKeyboard ($0DB7)",
"confidence": "high"
},
{
"addr": "E030",
"scope": "abs",
"name": "commBuildFlag",
"meaning": "bit 7 identifies which comm build is resident; copied to linkTypeFlag",
"confidence": "medium"
},
{
"addr": "E031",
"scope": "abs",
"name": "commNmiVector",
"meaning": "2-byte NMI vector copy kept by the comm module, set to irqRti",
"confidence": "medium"
},
{
"addr": "E039",
"scope": "abs",
"name": "commRequest",
"meaning": "request byte to the comm module: $C0 = pause, 0 = resume",
"confidence": "medium"
},
{
"addr": "E03A",
"scope": "abs",
"name": "commStatus",
"meaning": "bit 7 set while the comm module is paused (acknowledge)",
"confidence": "medium"
},
{
"addr": "E03B",
"scope": "abs",
"name": "commLinkActive",
"meaning": "nonzero when the comm link is up (pause/resume and chat only then)",
"confidence": "medium"
},
{
"addr": "E02E",
"scope": "abs",
"name": "commPendingCount",
"meaning": "comm module counter checked before sending chat (must be 0 / < $10)",
"confidence": "low"
},
{
"addr": "FFDE",
"scope": "abs",
"name": "ownerName1",
"meaning": "10-byte first name field copied from the BAM (offset $AB) of the game disk",
"confidence": "medium"
},
{
"addr": "FFE8",
"scope": "abs",
"name": "ownerName2",
"meaning": "10-byte second name field (written by overlay code)",
"confidence": "low"
}
],
"dataBlocks": [
{
"addr": "0803",
"length": 1,
"type": "byteTable",
"name": "loaderStatus",
"description": "bit 7 = a read error occurred"
},
{
"addr": "08E8",
"length": 1,
"type": "byteTable",
"name": "sendByteShift",
"description": "shift register for the byte being sent"
},
{
"addr": "08E9",
"length": 2,
"type": "byteTable",
"name": "recvByteShift",
"description": "shift register for the byte received during a send; $08EA unused"
},
{
"addr": "0993",
"length": 15,
"type": "text",
"name": "strBlockExecuteLeftover",
"description": "'B-E 2 0 1 17',0,'#',0 - leftover drive command strings from the shared EA loader, unused by the game"
},
{
"addr": "09A2",
"length": 1,
"type": "byteTable",
"name": "loadDestLo",
"description": "transfer address low byte (self-modified)"
},
{
"addr": "09A3",
"length": 2,
"type": "byteTable",
"name": "loadDestHi",
"description": "transfer address high byte; $09A4 unused"
},
{
"addr": "09A5",
"length": 1,
"type": "byteTable",
"name": "savedSpriteEnable",
"description": "sprite enable saved during a sector transfer"
},
{
"addr": "0A47",
"length": 1,
"type": "unknown",
"name": "padA47",
"description": "single $20 byte before msgIdentifyMap"
},
{
"addr": "0A48",
"length": 36,
"type": "text",
"name": "msgIdentifyMap",
"description": "copy-protection prompt"
},
{
"addr": "0B7D",
"length": 2,
"type": "byteTable",
"name": "inputLockoutTimer",
"description": "$0B7D lockout timer, $0B7E settings byte"
},
{
"addr": "0B7F",
"length": 21,
"type": "text",
"name": "ownerNameString",
"description": "owner name string built at run time"
},
{
"addr": "0B94",
"length": 33,
"type": "byteTable",
"name": "gameSettingsBlock",
"description": "$0B94-$0BB4: game settings/state bytes (mapSeed $0B95-97, flags $0B9B-$0BA8, two 6-byte text fields $0BA9/$0BAF)"
},
{
"addr": "0C8A",
"length": 2,
"type": "byteTable",
"name": "perSideValueTable",
"description": "$31,$63 indexed by player side"
},
{
"addr": "0CE4",
"length": 4,
"type": "text",
"name": "strOurs",
"description": "'OURS' default owner name"
},
{
"addr": "0D67",
"length": 64,
"type": "byteTable",
"name": "keyMatrixCodeTable",
"description": "8x8 keyboard matrix -> key code (row = CIA1 PA line 0-7, column = PB bit 0-7); letters/digits are PETSCII|$80, F1/F3/F5/F7 = 0-3, DEL $88, RETURN $8D, CRSR $8E/$8B, SPACE $A0, $FE = modifier (ignored), $FF = CTRL (no key)"
},
{
"addr": "0DA7",
"length": 16,
"type": "byteTable",
"name": "shiftedKeyTable",
"description": "shifted replacements for key codes $AC-$BB (',' '-' '.' '/' '0'-'9' ':' ';')"
},
{
"addr": "0F81",
"length": 16,
"type": "text",
"name": "msgNotADataDisk",
"description": "'NOT A DATA DISK!'"
},
{
"addr": "0F91",
"length": 17,
"type": "text",
"name": "msgInsertGameDisk",
"description": "'INSERT GAME DISK.'"
},
{
"addr": "0FA2",
"length": 11,
"type": "text",
"name": "msgDiskError",
"description": "'DISK ERROR.'"
},
{
"addr": "0FAD",
"length": 4,
"type": "text",
"name": "diskIdTable",
"description": "'OZ','EA' disk ids (first char at $0FAD+X, second at $0FAE+X)"
},
{
"addr": "10EF",
"length": 2,
"type": "byteTable",
"name": "spriteEnableScratch",
"description": "$10EF written by the IRQ with the sprite enable shadow; $10F0 unused"
},
{
"addr": "1299",
"length": 3,
"type": "unknown",
"name": "padIrq1299",
"description": "$00,$09,$13 between irqRti and updateFrameTimers; unreferenced"
},
{
"addr": "131A",
"length": 1,
"type": "byteTable",
"name": "cursorBlinkTimer",
"description": "30-frame countdown used by blinkCursorColor"
},
{
"addr": "1412",
"length": 2,
"type": "byteTable",
"name": "menuFlag1412",
"description": "$1412 menu flag, $1413 link-state copy"
},
{
"addr": "1535",
"length": 6,
"type": "addrTable",
"name": "menuItemHandlerTable",
"description": "3 handler addresses for fire on menu item 0-2: $158E (RTS), $17DA, $1A49"
},
{
"addr": "163A",
"length": 3,
"type": "byteTable",
"name": "menuItemColumnTable",
"description": "$0B,$13,$1B - column/position of the three menu items"
},
{
"addr": "163D",
"length": 3,
"type": "byteTable",
"name": "menuItemNormalAttrTable",
"description": "attribute bytes for unhighlighted menu items"
},
{
"addr": "1640",
"length": 3,
"type": "byteTable",
"name": "menuItemHighlightAttrTable",
"description": "attribute bytes for the highlighted menu item (0 = use normal)"
}
],
"misclassified": [
{
"addr": "0C0D",
"length": 15,
"actual": "code",
"evidence": "AD 00 CD C9 4C D0 F8 A9 00 8D 1A D0 4C 00 CD = lda $CD00 / cmp #$4C / bne $0C0C / lda #0 / sta $D01A / jmp $CD00; unreferenced, probably dead"
},
{
"addr": "0DB7",
"length": 250,
"actual": "code",
"evidence": "scanKeyboard/testKeyMask/pollKeyboardEvent: reached via the vector $E013/$E014 written by sub_0F44 (= $0DB7) and jsr $0E56 at $11E7 inside the untraced IRQ handler; disassembles cleanly (CIA1 matrix scan with self-modifying operands at $0E2B/$0E3A/$0E41/$0E43/$0E54) and ends with RTS at $0EB0"
},
{
"addr": "0EE5",
"length": 5,
"actual": "code",
"evidence": "A9 00 4C DA 0E = lda #0 / jmp $0EDA (into sub_0ED5); unreferenced alternate entry"
},
{
"addr": "10F1",
"length": 424,
"actual": "code",
"evidence": "raster IRQ handler installed by sub_0C66 ($AB/$AC = $10F1 -> $FFFE/$FFFF); starts PHA TXA PHA TYA PHA TSX LDA $0104,X AND #$10 (BRK test), ends with PLA TAY PLA TAX PLA RTI at $1293-$1298"
},
{
"addr": "129C",
"length": 110,
"actual": "code",
"evidence": "called by jsr $129C at $127A inside the IRQ handler; timer updates ending RTS at $1309"
},
{
"addr": "130A",
"length": 16,
"actual": "code",
"evidence": "called by jsr $130A at $1238; lda $57 / ora $58 / ora $59 ... jsr $FD5D / rts"
},
{
"addr": "131B",
"length": 22,
"actual": "code",
"evidence": "called by jsr $131B at $1277; dec $131A ... rts at $1330 ($131A is a data byte)"
},
{
"addr": "1331",
"length": 225,
"actual": "code",
"evidence": "jsr $1331 at $127D and jsr $1399 at $1280; chat send/receive code using comm entries $E006/$E009 and $C7D5/$C8A8, ends RTS at $1411"
}
],
"insights": [
"The raster IRQ handler is at $10F1 (line $FA), installed by $0C66 with the vector shadowed in zp $AB/$AC and re-written into $FFFE/$FFFF every interrupt; $1298 is the RTI used for NMI. The handler also receives BRK and routes it through the patchable JMP operand at $10FF/$1100 (normally to the plain exit $1293) - the comm module contains BRK instructions at $E01D/$E039.",
"VIC shadow registers live at $9000-$9024: $9000 D011, $9001 D016, $9002 D018, $9003-$9006 border/background colours, $9008-$900F sprite colours (>= $10 means two nibble colours that flash), $9010 sprite X, $9018 sprite Y, $9020 X MSB, $9022 enable, $9023 X expand, $9024 Y expand. When $92C8 is nonzero the sprite positions get fine-scroll offsets from $92F4 (x & 7) and $92F5 ((y & 7) - 3). Game code should write the shadows, never the VIC directly.",
"Input model: the IRQ polls the keyboard through the comm module entry $E015 (which calls the game's scanKeyboard at $0DB7 via the vector $E013/$E014) and joystick port 2 ($DC00 with all rows deselected). Results: zp_5D direction (active-low bits, $0F centred), zp_5F fire released (1 = up), zp_66 current key, $90EA new key event, $9248 last typed PETSCII key, $92E4/$92E5 shift and C= flags. F1/F3/F5/F7 produce key codes 0-3 and sub_06BA switches screens on them ($90FB). SHIFT + C= + F7 aborts to the main menu ($0AA3).",
"Six frame countdown timers at zp $6C-$71 are decremented by the IRQ: $6C joystick auto-repeat (16 then 6 frames via sub_0D49), $6D fire debounce (10), $6E menu blink (10), $70 key repeat (20). The 24-bit frame counter is zp $45-$47. sub_FD5D in high memory is a 24-bit LFSR RNG on zp $57-$59.",
"Disk access protocol with the comm module: before any load the game calls suspendCommModule ($0F5F: $E039=$C0, wait for $E03A bit 7) and afterwards resumeCommModule ($0F71). $E03B is the link-active flag, $E011 answer/originate (0/1), $E030 identifies the resident comm build, $E006 = get received byte (C=1 none), $E009 = send byte, $E000/$E00C are called every IRQ.",
"The settings block $0B7D-$0BB4 holds the start-up/game options: $0B9B solo mode (bit 7), $0B9C link option, $0B9F/$0BA0 player side (0/1, $FE unset), $0BA1 first play, $0BA3 game type (bits 0-2 index the names at $CCC8: Scrimmage, QB Sneak, The Bomb, Face-Off, Sluggers, Full War, Defender; bit 6 custom), $0BA5/$0BA6 link type (comm build), $0BA7 loading depth, $0BA8 resume flag. $0B7F is a 21-char owner name built from the disk's personalised BAM bytes ($FFDE/$FFE8), default 'OURS'.",
"Seed label corrections: $0EFE is stopAllSound (lda #0 / jmp $C89A), the overlay loader described in the seed note is sub_0F03; $10A4 (seed loadMapAndSetup) is not an instruction boundary - the routine is sub_10AB; $1081 (seed loadOverlayBIfNeeded) is mid-instruction - the routine starts at $1080; $0F55 does an 8-frame delay ($C37F) rather than a retry. Calls rendered as bootReceiveSectorData ($C0EF) from game code actually hit the runtime $C000 overlay routine at $C0EF (print A newlines), not the boot loader.",
"Incoming chat text is buffered at $9253 (36 chars, length in $9277) and mirrored with bit 7 set at $0549 (marker $0548); RETURN on a live link enters typing mode ($929C=$C0). Message slots for the status line are a pointer table at $0500/$0524 (lo/hi, index X) written by $C924, and a 9-entry message queue at $9249-$9252 managed by $C566/$C6EF."
]
}