⛰️ Features
- (apu) Add band-limited step synthesis - (a6f0df0)
- (bench) TETANES_BENCH_FILTER selects the output filter - (36b2646)
- (core) [breaking] One patch table for every kind of cheat - (5214d3b)
BREAKING:Bus::genie_codesis replaced byBus::patches, and a console state no longer carries cheats. - (core) [breaking] Default to a 48 kHz sample rate - (7f2071a)
BREAKING:Apu::DEFAULT_SAMPLE_RATEis 48 kHz, so a deck that does not callset_sample_rateproduces different samples than before. - (core) Serialize console state into a caller's buffer - (52e00af)
- (core) Add page-based cartridge memory translation - (398793e)
- (libretro) Expose the console's memory to the frontend - (1538183)
- (mapper) Add Taito TC0190/TC0350 and TC0690 (Mappers 33, 48) - (b127fe6)
- (mapper) Add VRC2/VRC4 (Mappers 021, 022, 023, 025) - (6cde160)
- (mapper) Report board registers for a debugger pane - (571d870)
- (mapper) Implement MMC5's vertical split screen - (bdfd9e8)
- (mapper) [breaking] Serve MMC5 from page tables - (1299ca9)
BREAKING:Exrom::read_ex_ram,write_ex_ram,rom_select,set_prg_bank_range,update_prg_banksandupdate_chr_banksare gone; MMC5 serves its memory from page tables and republishes banking throughMap::update_banks.Cart::chr_sizegoes with the last board that needed it. - (mapper) Add a PPU read hook for boards that synthesise CHR reads - (1a0a68e)
- (mapper) Serve FK23C from page tables - (98fb652)
- (mapper) Serve BandaiFCG from page tables - (53db1d7)
- (mapper) Serve Namco163 from page tables - (7e121ec)
- (mapper) Serve MMC2 and MMC4 from page tables - (91aa8c5)
- (mapper) Serve NES-EVENT from page tables - (bde36ff)
- (mapper) Serve VRC6 from page tables - (621acb0)
- (mapper) Serve FME7 and Jaleco SS88006 from page tables - (cd60771)
- (mapper) [breaking] Serve MMC3 from page tables - (c3af9a0)
BREAKING:Mmc3::set_chr_banksis gone, along with the board's ownupdate_prg_banks/update_chr_banks. MMC3 republishes its banking throughMap::update_banksfrom the register file, and four-screen carts allocate 4K of CIRAM rather than a separateex_rambuffer. - (mapper) Serve MMC1 from page tables - (00a62b4)
- (mapper) Serve tier 3a bank-switchers from page tables - (9b63b40)
- (mapper) Serve NROM from page tables - (9e7e71e)
🐛 Bug Fixes
-
(apu) Size the BLEP buffer from the rate it is tuned to - (8e5179b)
-
(audio) Pace on a deadline and hold the buffer with rate control - (8d27d27)
-
(cart) Load Dream Master as mapper 210 - (05fdef3)
-
(cart) Load Famicom Jump II as mapper 153 - (ea94174)
-
(cart) Read the NES 2.0 ram size bytes as two nibbles - (2110e9f)
-
(cart) Correct Top Rider's mapper and stop the game db self-referencing - (fa6098f)
-
(control-deck) Stop a restored state reverting the player's settings - (e3b64a8)
-
(control-deck) Refresh the frame when the console moves without a clock - (ea4575f)
-
(core) Power-cycle cart RAM on a hard reset - (9baa46e)
-
(core) [breaking] Match a save state to its cart by ROM CRC, not by size - (52632c2)
BREAKING: save states record the cart's ROM CRC32; states written by earlier builds no longer load. -
(core) [breaking] Hand out one nes frame per clock_frame call - (7cca0af)
BREAKING:ControlDeck::clock_framereturnsResult<Clocked>rather thanResult<()>and clocks at most one NES frame per call; drive it withwhile deck.clock_frame()? == Clocked::Continue {}to clock a whole display frame as before. -
(core) Keep the player's settings out of restored states - (8afede0)
-
(core) Keep load_bytes on the save version - (be52911)
-
(core) Wrap page offsets within their region - (fa7f637)
-
(fs) Report header versions as text - (7e2795b)
-
(mapper) Stop FK23C write-enabling its unmapped WRAM windows - (e070fde)
-
(mapper) Decode VRC2's mirroring register at all four indices - (0f5c29f)
-
(mapper) Decode FME-7's R:8 select bit - (878b82e)
-
(mapper) Apply mapper 210's mirroring - (138f134)
-
(mapper) Correct MMC5's split prefetch scroll and $5204 readback - (7b7b190)
-
(mapper) Let mapper 153 read its own PRG-RAM - (d53b2db)
-
(mapper) [breaking] Tag serialized boards by a stable id, not declaration order - (8440bb3)
BREAKING:Mapper::Fk23Cis no longer boxed, and a mapper number no board serves is nowmapper::Error::Unimplementedrather than a silentMapper::none()that read as open bus. Tools that survey ROMs rather than run them should useCart::from_rom_unmapped/Cart::from_path_unmapped. The on-disk format is byte-for-byte unchanged by this commit. -
(mapper) Honour MMC5's sprite-size rule for CHR bank sets - (4218c4d)
-
(mapper) Ignore FK23C's CHR-RAM select when the cart has no CHR-RAM - (69e754f)
-
(mapper) Apply Namco163 nametable select on every board variant - (0bf93f4)
-
(mapper) Make VRC6 helpers const - (79d3766)
-
(mapper) Restore nametable mirroring in sync, and unpin the game db - (c47a60c)
-
(mapper) Rebuild page tables when a save state is loaded - (9de76c8)
-
(ppu) Read the dummy NT bytes on dots 337 and 339 only - (e38ecb6)
-
(ppu) Stop rotating the palette latches on the dummy NT fetches - (bd2a8db)
-
(ppu) [breaking] Forward the region to the mapper, and box SunsoftFme7 - (f421867)
BREAKING:Mapper::SunsoftFme7is now boxed. Theboards!stable ids become each board's primary mapper number, which changes the tag written into save states - riding the same undeployed SAVE_VERSION 2 bump as the rest of this release. -
(sram) Back up a save that cannot be read - (5b2b671)
-
(sram) Version battery saves independently of save states - (7f23d4b)
-
(test) Stop UPDATE_SNAPSHOT clobbering parallel snapshot writes - (fcb1e0d)
-
Drop Game Genie codes when the cart is swapped - (3ae10ff)
🚜 Refactor
- (core) [breaking] Give a cart's battery one contiguous slice - (6d3586b)
BREAKING:Map::save_sram/load_sramare replaced bysync_battery/restore_battery,MemoryLayoutgainsbattery_ext, andControlDeck::sram/save_sramtake&mut self. - (core) [breaking] Take readers and writers, not paths - (04bc7ae)
BREAKING:fsandControlDecksave/load now takeRead/Write, the path-taking forms are*_path,fs::load_bytesis gone since&[u8]is already a reader, andConfig::data_diris replaced byConfig::sram_dir: Option<PathBuf>. - (core) [breaking] Drop the accessors that only return a public field - (4c8891d)
BREAKING: Cart::region, Cart::ram_state, Bus::region, Ppu::region, Apu::region, Dmc::region, Noise::region, Joypad::index, Zapper::x, Zapper::y and Mmc3::irq_pending are removed; read the field of the same name. - (core) Put run-ahead's rewind through the restore funnel - (4748ffe)
- (core) [breaking] Name the debugger accessors for the one debugger there is - (1902979)
BREAKING:ControlDeck::add_debugger/remove_debuggerare nowset_debugger/clear_debugger, and the latter takes no argument. - (core) [breaking] Make memory::Buffer crate-internal - (9ef4227)
BREAKING:memory::Bufferand its unused constructors are no longer public. - (core) Drop the unused BandLimited accessors - (9875f48)
- (core) Move the mapper and debugger bus methods out of ppu.rs - (b13767e)
- (core) [breaking] Consolidate the Bus surface after flattening - (7afe6cc)
BREAKING:Input::read/Input::peekareread_port/peek_portand no longer take a&Ppu;Bus::input_read/input_peekare what read $4016/$4017.Bus::cpu_bus_read/cpu_bus_write/cpu_bus_peekandppu_bus_read/ppu_bus_writeare now crate-internal; useBus::peek,Bus::ppu_bus_peek,Bus::chr_peekorBus::copy_ppu_busto observe. - (core) [breaking] Flatten ownership so Bus holds the console - (c34e8b8)
BREAKING:ControlDeck::cpu()returns registers only; the console isControlDeck::bus(), and save states, rewind and replay are nowBusrather thanCpu.ControlDeck::load_cpuisload_bus.MapperandMemorymoved fromPputoBus, as did the debugger.PpuDebuggerisDebuggerand its callback takes&Bus.Ppu::load_nametables/load_pattern_tables/load_oamtake the CHR window to read from. Thememory::Readandmemory::Writetraits are removed in favor of inherent methods. Save states from earlier builds will not load. - (core) [breaking] Make video::Frame a fixed-size buffer - (b79a2fe)
BREAKING:video::Frameno longer derefs toVec<u8>. Callers using it as aVecshould useas_slice,as_array, or indexing; callers that resized it have no replacement, as the size is now part of the type. - (core) [breaking] Collapse the clocking API and clean up public surface - (2b4fd1e)
BREAKING: MSRV is now 1.88; 1.85 could not build the crate at all.Map::syncisMap::update_banks,Ppu::sync_mapperisPpu::rebuild_mapper_state, and MMC5's collidingupdate_chr_banksisselect_chr_set. Frame buffers are fixed-size:Ppu::frame_bufferandControlDeck::frame_buffer_rawreturn&[u16; ppu::size::FRAME],ControlDeck::frame_bufferreturns&[u8; Frame::SIZE], andframe_buffer_intotakes&mut [u8; Frame::SIZE].ControlDeck::apu_mutreturns&mut Apu,joypadtakes&self, andwramreturns&[u8; bus::size::WRAM]. Clocking clears the previous call's audio samples instead of accumulating; opt out withConfig::clear_audio_on_clock = false. The five olderclock_*entry points are deprecated shims rather than removals. - (core) [breaking] Delete the convention-only traits from common.rs - (61cedf4)
BREAKING: theClock,Reset,Regional,SampleandSramtraits are gone, and the prelude drops them.clock,reset,region,set_region,output,saveandloadare inherent methods on each component and forward exactly as before, so a call likedeck.cpu_mut().clock()is unchanged - only the trait import goes away. - (core) [breaking] Delete the four single-purpose traits - (9e9bee0)
BREAKING: thePpuAddr,InputRegisters,TimerCycleandConsumetraits are gone.PpuAddrbecomes the free functionsppu::is_attrandppu::is_palette; the rest become inherent methods on the types that had the impls. - (core) [breaking] Delete the pre-page-table memory path - (9ca8ae6)
BREAKING:mapper::Banks,mapper::BankAccess,ppu::CIRamand thememmodule are gone.mem::Memory<D>is nowmemory::Buffer<D>, andmemory::Memoryis the page-table address space. TheCart::prg_romandCart::chr_romfields become theCart::memoryarena, withprg_rom()andchr_rom()accessors returning the unpadded bytes.Ppu::ciramis gone - nametables are page entries.Map's read hooks lose their_hooksuffix:prg_read,prg_peek,chr_read,chr_peek. - (mapper) Drop unused VRC6 nametable setters and MMC3 chr_window - (1354e7a)
- (mapper) Match the documented polarity of mapper 071's mirroring bit - (421acec)
- (mapper) Generate the per-board plumbing from one boards! table - (c872dd9)
- (mapper) [breaking] Give Map every method a board needs, drop the supertraits - (7a6439a)
BREAKING:Mapno longer requiresClock + Regional + Reset + Sram.clock,reset,regionandset_regionare defaulted methods onMapitself, andimpl Map for Mapperbecomes inherent methods onMapper, soMapno longer needs importing to call them. - (mapper) [breaking] Unify per-board dispatch flags into MapperOps - (8abfaee)
BREAKING:Map::watches_ppu_bus,Map::serves_prg_readsandMap::serves_chr_readsare replaced by a singleMap::mapper_opsreturningMapperOpsbitflags, which also declaresCLOCKED,IRQ,AUDIOandDMA. A board that does not declare a hook no longer has it called. - (ppu) Derive fine Y from v instead of caching it - (c44bca0)
- (ppu) [breaking] Hold the PPU registers as fields, not structs - (145a954)
BREAKING:ppu::ctrl::Ctrl,ppu::mask::Maskandppu::status::Statusare gone; read thectrl_*,mask_*andstatus_*fields onPpuinstead.
📚 Documentation
-
(bench) Rewrite the benchmark README around running it - (84357fc)
-
(bench) Record Raspberry Pi 5 on-target results - (fae0367)
-
(bench) Decline the MesenCE sprite-shifter port with reasons - (89fa791)
-
(bench) Scope the two-layout rule to perf verdicts, record fine_y and dummy-fetch results - (b2e26d8)
-
(bench) Record the dispatch-reshape layout draw - (96ef17a)
-
(bench) Record the shifter-reload layout result - (54e6b4d)
-
(bench) Measure candidates at two code layouts - (f8e4282)
-
(bench) Correct profile shares read from the wrong denominator - (4166301)
-
(bench) Correct the boxing conclusion with FME7 ROMs measured - (97e5da7)
-
(bench) Record the trait-diet and boards! table results - (c07971d)
-
(bench) Record the MapperOps results and correct the catch-up conclusion - (13cae45)
-
(bench) Record the PPU results and the catch-up investigation - (9fe62c0)
-
(bench) Record the frame times after deleting the old memory path - (17596d1)
-
(bench) Record frame times after the mapper rework - (3968038)
-
(core) Correct the mapper, PPU peek and Bus-state docs - (dfb4207)
-
(core) Correct stale board, filter and audio-profile references - (fff8802)
-
(core) Describe the code as it stands, not its history - (5a5b65b)
-
(core) Turn on missing_docs and say which half is API - (4511ddf)
-
(mapper) Link the save-state rebuild to Bus, not Ppu - (3e24c14)
-
(mapper) Correct what FK23C's $A001 handler relies on - (5e2a93e)
-
(mapper) Record that Color Dreams boards have no bus conflicts - (fceecca)
-
(mapper) Drop the transitional phrasing from the Map trait - (ac1725d)
-
(memory) Say why the region wrap is a divide - (878c8d0)
-
Correct stale facts in CLAUDE.md, the mapper docs and the README table - (b702137)
-
Fix stale references and drop history from the branch's comments - (97a2850)
⚡ Performance
- (apu) [breaking] Synthesise the output from channel deltas - (cb9a45b)
BREAKING:Apu::sample_period,Apu::sample_counterandApu::mapper_outputsare gone, along withapu::filter'sFir,windowed_sinc_kerneland the decimation half ofFilterChain. - (apu) Run the channels between waveform steps, not every cycle - (9b9699c)
- (apu) Flush denormals and mix only when the filter chain samples - (9934043)
- (apu) [breaking] Run the filter chain at two rates instead of six - (fa2966e)
BREAKING:apu::filter::Filter,apu::filter::SampledFilterandFilterKind::Identityare removed, andFilterChain's fields are now the named stages rather than a[SampledFilter; 6], which changes the save-state layout - covered by theSAVE_VERSIONbump already on this branch. - (apu) Mix from integer channel levels in the MMC5 hot path - (31186ed)
- (apu) Avoid a libm call in the FIR filter hot loop - (f91f59e)
- (core) Snapshot run-ahead into last frame's console - (42e9378)
- (core) Snapshot run-ahead with a clone instead of bincode - (605f917)
- (core) [breaking] Keep the cart's ROM out of save states - (12156e5)
BREAKING: save states and rewind snapshots no longer carry the cart's ROM, so a state can only be applied to the game it came from.Cpu::loadandControlDeck::load_cpuare fallible, returningcpu::StateMismatchwhen the state does not belong to the loaded cart. - (core) Hoist per-pixel invariants in the NTSC filter - (bf6dd6e)
- (core) Un-box Ppu::sprites; document why wram/frame buffer stay boxed - (5fac025)
- (core) Split Ppu::clock's branch chain and flag-gate the debugger - (8656742)
- (mapper) Re-bank only on writes that change banking - (f1d6f9a)
- (mapper) Only re-map the CHR window MMC2/MMC4's latch changed - (5b2371a)
- (ppu) Reload BG shifters only on real tile fetches - (82ad8af)
- (ppu) Gate the pixel path on a dot threshold, not on mask flags - (41d8ea5)
- (ppu) Fold greyscale and emphasis in by the run, not per pixel - (71b7ff1)
- (ppu) Visit only the sprites covering a dot - (341f832)
- (video) [breaking] Bake the NTSC palette at build time - (830b850)
BREAKING:video::NTSC_PALETTEis a&'static [u8; N]of red, green, blue triples baked in at build time, not aOnceLock<Vec<u32>>packing each color into oneu32. - (wasm) Store local-storage payloads as base64, not json arrays - (abb4488)
🧪 Testing
- (apu) Assert audio with a coarse profile instead of a sample hash - (e411661)
- (core) Implement the bus routing tests and drop the two unassertable ROMs - (dc60b4b)
- (core) Assert blargg result codes and report tones instead of ignoring - (5c2e4e6)
- (core) Cover the save-state and SRAM round trips - (1b8c0d8)
- (input) Assert the zapper through the channel each ROM reports on - (c483467)
- (mapper) Make the board tests take the bus's route - (e260d05)
- (mapper) Cover the last 13 boards in the table - (9a113c3)
- (mapper) Cover Jaleco SS88006, Bandai FCG and FK23C - (a63ddf8)
- (mapper) Cover Namco163, Sunsoft FME7 and MMC1 banking and IRQs - (32ea7dc)
- (mapper) Cover the VRC IRQ counter and VRC6, fixing two overflow panics - (e42aebf)
- (ppu) Assert the field placement the dot loop depends on - (b063118)