- Decoded the VramWrite ($E1CC) stream format: <addr hi> <addr lo> <count> <bytes>, $00 terminator - Named all 9 payload streams ($E4CC-$E7C2, no gaps/overlaps) plus VramWriteSourcePointerTable at $F000 - Split PadBitTableLives into Player1TileTable ($E4B4) and Player2TileTable ($E4C0), 12 entries each (Y=0..11) - Decoded CHR font (digits $30-$39, letters $3A-$52, space $5F; 'W' missing from the A-Z run) and recovered on-screen text including 'PRESS A TO TEST SIDE-A' / 'SIDE-B' - Documented that the 16 KiB PRG is two byte-identical 8 KiB halves; $C000 copy is dead - Documented 7 unreached subroutines inside the live code region (kept as .byte per Pass-1 policy) - Fixed a stray duplicate sentence and stale _wip paths in root OVERVIEW.md Byte-identical ROM verified.
97 lines
10 KiB
Plaintext
97 lines
10 KiB
Plaintext
# MUSCLE (U) routine purpose notes - Pass 2
|
|
# addr<TAB>purpose
|
|
0x8000 Power-on entry: stack set up, PPU blanked through two vblank waits, APU frame counter silenced, $0300-$032C cleared, then the initial nametable stream is decoded into $0400 and the first stage is started.
|
|
0x800E Spin on $2002 until bit 7 is set, so the write to $2000 just before it has taken effect.
|
|
0x8019 Second vblank wait, this time before touching $4017/$4010.
|
|
0x802C Reset the game-state bytes at $0326/$032B/$032C and zero page $FF, clear $0300-$032C, then decode the compressed nametable pointed at by SourcePointerLo/Hi.
|
|
0x8052 Decode the compressed nametable stream at (SourcePointerLo/Hi) into (DestinationPointerLo/Hi). A byte with bit 7 clear is a run of (byte & $7F) copies of the next stream byte; a byte with bit 7 set is a literal, written only when it differs from what is already at the destination. $0346 is set whenever VRAM actually changed.
|
|
0x80AC Stage start-up: reset the animation and actor counters, clear the OAM shadow, initialise the sound engine, upload the HUD palette row, and hand control to MainGameLoop.
|
|
0x8132 Push or drop the two flash sprites (palette tiles $82) depending on PlayerCount and the fade state.
|
|
0x8164 Round initialisation: clear the shadow arrays at zero page $00-$BF, reset the controllers and the OAM shadow, collect the $81/$82 type sprites, set the per-player palette and sprite bases, and start the actors.
|
|
0x8226 Main game loop. Waits for the NMI handler to raise $0321, services the two frame timers, reads the controllers, and restarts the stage or updates actor positions. $0321/$0320 form the vblank handshake with the NMI.
|
|
0x82AC Restoring binary division of the 8-bit value in A by the constant 20 ($14). Eight steps shift the divisor right, conditionally subtract it from the 16-bit remainder, and roll the quotient bit into $0349. Quotient -> $0349, remainder -> $0347/$0348.
|
|
0x82E1 Point sound channels 1 and 3 at the silent sequence at $B1A1/$B1B0 with step 0.
|
|
0x82ED Point sound channel 3 at $B1B0 with step 0 (the tail of SilenceSoundChannels1And3).
|
|
0x82FA Set the $14-frame transition timer and start the intro music sequence for all four channels.
|
|
0x82FF Start a four-channel music sequence from the $B2B9/$B3B2 tables.
|
|
0x832A Draw the two score/tally markers at $20BB and $24A6 when their flags at $0338/$0339 are set.
|
|
0x8353 Redraw the score row at $20EC and $210C with the three current digits.
|
|
0x8372 Write one score digit: blank the cell unless it matches PlayerCount, then stamp the tile.
|
|
0x8399 Copy the 32-byte OAM shadow at $9EF4 into $0300 and upload it.
|
|
0x83A7 Advance both actors: add the per-actor scroll delta to the position in $0340-$0342, then step the round counter and redraw the tally markers.
|
|
0x845C Per-round timer and state update: redraw the score, start the round-change music, and either loop again or advance the round and prepare the transition objects.
|
|
0x856C Start the short round-change jingle and wait for the NMI handshake.
|
|
0x8596 Compare the actor position in $0340-$0342 against the screen bounds in $0328-$032A and latch the tighter limit.
|
|
0x8639 Clear the vblank handshake ($0321, $0320) and re-enable rendering.
|
|
0x8644 Force rendering off: PPUCTRL=$10, PPUMASK=0, updating both shadow bytes.
|
|
0x8653 Force rendering off and immediately signal the main loop by setting $0321.
|
|
0x865D Positive game-state branch of the NMI: update the flash counters and actors, detect a button edge, and bump the frame-skip counter in zero page $FE.
|
|
0x8751 Finish the NMI: clear the vblank latch unless $0321 says work is outstanding, poll $2002 to release the PPU address latch, and run the sound frame update.
|
|
0x8759 NMI epilogue: poll $2002, update the sound engine, restore Y/X/A, and RTI at $8764.
|
|
0x8765 Point sound channel 1 at the $B1EB sequence.
|
|
0x877C Main game-state dispatcher. Counts down the transition timer at zero page $8D and runs one of fourteen subsystem updates chosen by that counter.
|
|
0x87F2 Advance the screen-flash counter against the $2C-frame limit.
|
|
0x8837 Play the player-death sequence: load the palette, upload the sound command, clear the attribute area, and respawn the actors.
|
|
0x8848 Test the start/select bits of the raw controller byte.
|
|
0x885B Read the controllers and advance the frame counter.
|
|
0x887C Signal the main loop that the NMI frame is finished.
|
|
0x8882 Read the controllers and test the round-transition flag at $0326.
|
|
0x88A4 Build the OAM list for the active player: blank every slot, then emit one sprite per visible actor row.
|
|
0x88B0 Emit a single OAM entry built from the actor record.
|
|
0x8936 Find the next free OAM slot by scanning the $032E source list for a value matching the running sprite index.
|
|
0x8986 Compact the OAM list, dropping entries whose actor has moved off screen.
|
|
0x89CE Read the controllers, reset the per-player bookkeeping, and initialise the object list.
|
|
0x8A28 Update the stage actors: pick the animation for each player from the palette index pair and hand off to SpawnStageActors.
|
|
0x8AE8 Rewrite the attribute table entry at $209C through the compressed stream writer.
|
|
0x8B20 Write one tile into the nametable at the address in zero page $10/$11 plus the per-round offset.
|
|
0x8B92 Render the HUD digits: set up the palette copy, clear the digit cells, then run each digit descriptor.
|
|
0x8BF8 Re-enable rendering: PPUCTRL=$10 with PPUMASK left at zero and the shadow updated.
|
|
0x8C05 Advance the animation slot below the current one.
|
|
0x8C0E Queue a VRAM descriptor built from the PlayerCount-relative index.
|
|
0x8C65 Execute the queued VRAM transfer: copy bytes from (BytecodeStreamPointerLo/Hi) through the destination pointer and the two block counters.
|
|
0x8CB8 Push a VRAM descriptor and bump the queue index.
|
|
0x9AE8 Bytecode interpreter entry. Reads a token byte through (BytecodeStreamPointerLo/Hi); tokens $C8-$EF are converted to an index and dispatched through the handler table at $9237 into jmp ($001C); anything else is an operand consumed by the current handler.
|
|
0x9B05 jmp ($001C) - the handler table and the $9F2B table both land here to re-enter the interpreter at the next token.
|
|
0x9B25 Main bytecode program loop: advance the program counter, dispatch the token, and resume.
|
|
0x9CBB Reset the per-invocation script state at $034D/$034E and the flags in zero page $4D/$4E.
|
|
0x9E7F Queue a nametable write described by a two-byte descriptor and advance the write pointer.
|
|
0x9F4B Update actor movement from the object script, dispatching through jmp ($001C).
|
|
0x9F54 Move an actor by one opcode: read the displacement, apply it, and redraw.
|
|
0xA01D Process one bytecode opcode against the actor record, advancing the script pointer.
|
|
0xA17F Run an actor script: set up the source/destination pointers, then walk the descriptor stream.
|
|
0xA250 Read one byte of the actor configuration block, scaled by four.
|
|
0xA253 Compute the byte offset of an actor configuration record.
|
|
0xA4FF Flip the low bit of A, turning a player index into its partner (0<->1, 2<->3).
|
|
0xA508 Resolve an object index through the per-player tables at $2A and $26.
|
|
0xA512 Walk the object slots looking for one whose countdown at $45 is non-zero.
|
|
0xA68E Update the whole object list, then the second half of it.
|
|
0xA6F8 Update object collisions and the object scroll test.
|
|
0xA8D1 Advance an object animation frame from the table at ($75),y.
|
|
0xA949 Update both players' object animations.
|
|
0xAA56 Read both controller ports: strobe $4016, shift eight bits per port into zero page $FA/$FB, save the previous byte in $F4/$F5, and derive the face/direction masks in $F6-$F9.
|
|
0xAA63 Shift one controller port's eight bits in from $4016/4017 and run the edge-detection post-processing.
|
|
0xAACD Enable rendering: PPUMASK=$18, PPUCTRL=$90, both shadows updated.
|
|
0xAADC Disable rendering, then clear both nametables to tile $30.
|
|
0xAAEF Set the PPU address from A and write $C0/$40 bytes of tile $30.
|
|
0xAB68 Load the HUD palette and the per-round object configuration into the $0300 shadow.
|
|
0xABB1 Upload the 32-byte palette from $0300 to $3F00, then restore PPUMASK.
|
|
0xABE4 Write the latched scroll (PpuScrollX/PpuScrollY) and PpuCtrlShadow to the PPU.
|
|
0xABFC Clear the 256-byte OAM shadow at $0200 to $F0.
|
|
0xAC0D Select and run the level's VRAM writer from the three-entry table at $AC35, indexed by the level byte at $0326.
|
|
0xAC82 Decode one compressed VRAM token: below $B8 is a literal, $B8-$C3 is a run whose fill value comes from the table at $ACBD and whose length is the next stream byte, $C0 additionally skips a byte first, and $C4 or above is a literal.
|
|
0xAC88 Advance the compressed-stream source pointer (SourcePointerLo/Hi) by one byte.
|
|
0xACF5 Write a block of compressed data to VRAM: the first byte is the block count, then each block supplies a 16-bit PPU address followed by a token run.
|
|
0xAD19 Compute the collision bit flags for the active player and store them in zero page $15.
|
|
0xAF25 Silence the APU: $4015=0, the enable mask cleared, and all four channels marked finished.
|
|
0xAF3B Sound frame update: walk all four channels and update the APU.
|
|
0xAF40 Update all four sound channels in turn.
|
|
0xAF43 Update one sound channel: restart it from its sequence when finished, otherwise count the note timer down and step the sequence.
|
|
0xAFAB Advance a sound channel's sequence pointer by step*2 from its base.
|
|
0xAFC5 Compute the envelope mask for a channel from its envelope mode.
|
|
0xAFD9 Compute the tone period for a channel from its note table and write the period registers.
|
|
0xB03A Read a note's duration from the sequence and store it, doubling it for looped notes.
|
|
0xB06C Apply one byte of a sound sequence: below $A0 is ignored, $A0-$AF patches the $4000 control byte, and $B0+ handles note and sustain data.
|
|
0xB0C6 Decrement the channel's sustain counter and advance the sequence.0x8644 Set PPUCTRL to $10 and PPUMASK to 0, updating both shadow bytes. Provably unreachable: no word in the image points at it except the jsr at $8654, and $8653 itself is unreferenced. Kept as code because the shape is unambiguous.
|
|
0x8653 Call ForceRenderingOff, signal the main loop through SignalVblankComplete, then clear the vblank latch at $0320. Provably unreachable (no word in the image points at $8653). Kept as code because the shape is unambiguous.
|
|
0x9CAD Decrement the 16-bit word in Zp14/Zp15 by one. A textbook "dec 16-bit" helper, but Zp14/Zp15 are otherwise scratch, and no word in the image points here. Provably unreachable; kept as code.
|