Draw the sprites the chip drops, from a Hacks menu or --hack - #37
Merged
Conversation
The 2C02 has eight sprite output units, so a scanline that wants a ninth loses one -- which is why so many games flicker their sprites, rotating which of them is dropped so that all of them are visible half the time. A new **Hacks** menu, and `--hack unlimited-sprites` beside it, draws the dropped ones as well. Nothing a game can observe changes. The dot-accurate machinery is untouched: evaluation, secondary OAM, the eight real units and every bus cycle they make run exactly as they did, and this runs afterwards on the results. The overflow flag still rises, $2004 still answers with whatever the sprite hardware is holding, and sprite 0 hit is still sprite 0's. OAM is read through `OAM.peek` and the patterns through `VRAM.peek`, so no row of OAM is refreshed that would have decayed and MMC3's scanline counter never sees an address that would not have been there -- `PPUAddressBusTests` holds the whole mapper-visible address sequence over a nine-sprite line against the same line with the hack off. It is one nested class in `PPU`. `ExtraSprites` owns fifty six `SpriteUnit`s, scans OAM once a line at dot 320 for whatever the evaluation had no room for, and the multiplexer asks it for a pixel only once all eight real units have come out transparent -- every extra sprite is later in OAM than every real one, so first-opaque-wins still means what it did. The walk starts where the evaluation started rather than at sprite 0, so a game that left OAMADDR somewhere odd does not get sprites resurrected from in front of where the hardware began looking. The units travel in a save state even though the switch does not, because a state can be taken half way down a scanline and resuming from one has to draw the rest of that line the way running straight through would have. Off everywhere unless it is asked for, and `run.hacks` in the report reads it back off the machine rather than off the command line -- so a `hack unlimited-sprites on` part way through an interactive session is reported as the run ended rather than as it started. It joins `run.region` and `run.state.startedFromPowerOn` in what to check before diffing two reports. Which turned up something in the test that had to change for it. `SaveStateCompletenessTests` named the elements of an array after their class, so the PPU's two `SpriteUnit[]` arrays would both have produced `SpriteUnit[0..7]`: the second walked would have overwritten the first and eight real units would have gone silently uncompared. They are named after the field now. No cartridge demonstrates this. A game is written to stay under eight sprites a line and they mostly manage it -- Punch-Out!!'s first fight peaks at seven and Battletoads' first level at eight -- so `sprite-limit.nes` is vendored for it, with the Java that assembles it and the asm6 source it reads as. A test holds the cartridge against the generator, so the bytes cannot outlive the code that explains them, and the build still needs no assembler. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The 2C02 draws eight sprites a scanline, which is why so many games flicker theirs; a new Hacks menu,
--hack unlimited-spritesand ahack NAME on|offREPL command draw the ones the chip would have dropped.Nothing a game can observe changes — the evaluation, the eight real units and every bus cycle they make are untouched, the overflow flag still rises, and OAM and the patterns are read through
peek, so MMC3's scanline counter never sees an address that would not have been there; it is one nested class inPPU, off unless asked for, with its output units in the save state but not its switch, andrun.hacksin the report is read back off the machine so a flip part way through a session is reported truthfully.It also fixes
SaveStateCompletenessTests, which named array elements after their class and so would have left eight of the PPU's sprite output units silently uncompared once there were twoSpriteUnit[]arrays to name.No real cartridge overflows anywhere a test can reach — Punch-Out!!'s first fight peaks at seven sprites on a line and Battletoads' first level at eight — so
sprite-limit.nesis vendored along with the Java that assembles it and the asm6 source it reads as, held together by a test;mvn -B clean testis green at 1630 tests, and the desktop menu is the one thing not exercised by hand.🤖 Generated with Claude Code