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