github lukexor/tetanes tetanes-core-v0.15.0
TetaNES Core v0.15.0

latest release: tetanes-v0.15.0
2 hours ago

⛰️ 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_codes is replaced by Bus::patches, and a console state no longer carries cheats.
  • (core) [breaking] Default to a 48 kHz sample rate - (7f2071a)
    BREAKING: Apu::DEFAULT_SAMPLE_RATE is 48 kHz, so a deck that does not call set_sample_rate produces 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_banks and update_chr_banks are gone; MMC5 serves its memory from page tables and republishes banking through Map::update_banks. Cart::chr_size goes 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_banks is gone, along with the board's own update_prg_banks/update_chr_banks. MMC3 republishes its banking through Map::update_banks from the register file, and four-screen carts allocate 4K of CIRAM rather than a separate ex_ram buffer.
  • (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_frame returns Result<Clocked> rather than Result<()> and clocks at most one NES frame per call; drive it with while 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::Fk23C is no longer boxed, and a mapper number no board serves is now mapper::Error::Unimplemented rather than a silent Mapper::none() that read as open bus. Tools that survey ROMs rather than run them should use Cart::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::SunsoftFme7 is now boxed. The boards! 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_sram are replaced by sync_battery/restore_battery, MemoryLayout gains battery_ext, and ControlDeck::sram/save_sram take &mut self.
  • (core) [breaking] Take readers and writers, not paths - (04bc7ae)
    BREAKING: fs and ControlDeck save/load now take Read/Write, the path-taking forms are *_path, fs::load_bytes is gone since &[u8] is already a reader, and Config::data_dir is replaced by Config::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_debugger are now set_debugger/clear_debugger, and the latter takes no argument.
  • (core) [breaking] Make memory::Buffer crate-internal - (9ef4227)
    BREAKING: memory::Buffer and 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::peek are read_port/peek_port and no longer take a &Ppu; Bus::input_read/input_peek are what read $4016/$4017. Bus::cpu_bus_read/cpu_bus_write/cpu_bus_peek and ppu_bus_read/ppu_bus_write are now crate-internal; use Bus::peek, Bus::ppu_bus_peek, Bus::chr_peek or Bus::copy_ppu_bus to observe.
  • (core) [breaking] Flatten ownership so Bus holds the console - (c34e8b8)
    BREAKING: ControlDeck::cpu() returns registers only; the console is ControlDeck::bus(), and save states, rewind and replay are now Bus rather than Cpu. ControlDeck::load_cpu is load_bus. Mapper and Memory moved from Ppu to Bus, as did the debugger. PpuDebugger is Debugger and its callback takes &Bus. Ppu::load_nametables/load_pattern_tables/load_oam take the CHR window to read from. The memory::Read and memory::Write traits 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::Frame no longer derefs to Vec<u8>. Callers using it as a Vec should use as_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::sync is Map::update_banks, Ppu::sync_mapper is Ppu::rebuild_mapper_state, and MMC5's colliding update_chr_banks is select_chr_set. Frame buffers are fixed-size: Ppu::frame_buffer and ControlDeck::frame_buffer_raw return &[u16; ppu::size::FRAME], ControlDeck::frame_buffer returns &[u8; Frame::SIZE], and frame_buffer_into takes &mut [u8; Frame::SIZE]. ControlDeck::apu_mut returns &mut Apu, joypad takes &self, and wram returns &[u8; bus::size::WRAM]. Clocking clears the previous call's audio samples instead of accumulating; opt out with Config::clear_audio_on_clock = false. The five older clock_* entry points are deprecated shims rather than removals.
  • (core) [breaking] Delete the convention-only traits from common.rs - (61cedf4)
    BREAKING: the Clock, Reset, Regional, Sample and Sram traits are gone, and the prelude drops them. clock, reset, region, set_region, output, save and load are inherent methods on each component and forward exactly as before, so a call like deck.cpu_mut().clock() is unchanged - only the trait import goes away.
  • (core) [breaking] Delete the four single-purpose traits - (9e9bee0)
    BREAKING: the PpuAddr, InputRegisters, TimerCycle and Consume traits are gone. PpuAddr becomes the free functions ppu::is_attr and ppu::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::CIRam and the mem module are gone. mem::Memory<D> is now memory::Buffer<D>, and memory::Memory is the page-table address space. The Cart::prg_rom and Cart::chr_rom fields become the Cart::memory arena, with prg_rom() and chr_rom() accessors returning the unpadded bytes. Ppu::ciram is gone - nametables are page entries. Map's read hooks lose their _hook suffix: 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: Map no longer requires Clock + Regional + Reset + Sram. clock, reset, region and set_region are defaulted methods on Map itself, and impl Map for Mapper becomes inherent methods on Mapper, so Map no longer needs importing to call them.
  • (mapper) [breaking] Unify per-board dispatch flags into MapperOps - (8abfaee)
    BREAKING: Map::watches_ppu_bus, Map::serves_prg_reads and Map::serves_chr_reads are replaced by a single Map::mapper_ops returning MapperOps bitflags, which also declares CLOCKED, IRQ, AUDIO and DMA. 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::Mask and ppu::status::Status are gone; read the ctrl_*, mask_* and status_* fields on Ppu instead.

📚 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_counter and Apu::mapper_outputs are gone, along with apu::filter's Fir, windowed_sinc_kernel and the decimation half of FilterChain.
  • (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::SampledFilter and FilterKind::Identity are removed, and FilterChain's fields are now the named stages rather than a [SampledFilter; 6], which changes the save-state layout - covered by the SAVE_VERSION bump 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::load and ControlDeck::load_cpu are fallible, returning cpu::StateMismatch when 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_PALETTE is a &'static [u8; N] of red, green, blue triples baked in at build time, not a OnceLock<Vec<u32>> packing each color into one u32.
  • (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)

⚙️ Miscellaneous Tasks

  • (bench) Allow sweeping a ROM library for load and clock failures - (a12c552)

  • (bench) Benchmark a ROM corpus and report variance - (2d9452e)

  • Clear the must_use, const fn and rustdoc warnings - (5ef3404)

Don't miss a new tetanes release

NewReleases is sending notifications on new releases.