From 8a09de294ad0b789aceb3d3e2be55039ec6bf855 Mon Sep 17 00:00:00 2001 From: Polaro4 Date: Wed, 6 May 2026 15:46:37 -0300 Subject: [PATCH 1/3] impl mapper002 logic --- src/memory/mappers/ines_mapper002.rs | 93 ++++++++++++++++++++++++++++ src/memory/mappers/mod.rs | 2 + 2 files changed, 95 insertions(+) create mode 100644 src/memory/mappers/ines_mapper002.rs diff --git a/src/memory/mappers/ines_mapper002.rs b/src/memory/mappers/ines_mapper002.rs new file mode 100644 index 0000000..5b10c6b --- /dev/null +++ b/src/memory/mappers/ines_mapper002.rs @@ -0,0 +1,93 @@ +use crate::memory::{ + game_save::GameSave, + mapper_base::{Mapper, Mirroring} +}; + + +///https://www.nesdev.org/wiki/UxROM +pub struct InesMapper002 { + game_save: GameSave, + + prg_rom: Box<[u8]>, + + ///**InesMapper002 / UxROM** doesn't have a chr_rom, instead, it uses a chr_ram(usually 8kb) + ///that starts empty and the game will write the tiles there before it tries to render the screen + chr_ram: Box<[u8]>, + + mirroring: Mirroring, + + /// Bank select ($8000-$FFFF) + /// ```text + /// 7 bit 0 + /// ---- ---- + /// xxxx pPPP + /// |||| + /// ++++- Select 16 KB PRG ROM bank for CPU $8000-$BFFF + /// (UNROM uses bits 2-0; UOROM uses bits 3-0) + /// ``` + /// Emulator implementations of iNES mapper 2 treat this as a full 8-bit bank select register, without bus conflicts. This allows the mapper to be used for similar boards that are compatible. + /// + /// To make use of all 8-bits for a 4 MB PRG ROM, an NES 2.0 header must be used (iNES can only effectively go to 2 MB). + /// + /// The original UxROM boards used by Nintendo were subject to bus conflicts, + /// and the relevant games all work around this in software. Some emulators (notably FCEUX) will have bus conflicts by default, + /// but others have none. NES 2.0 submappers were assigned to accurately specify whether the game should be emulated with bus conflicts. + bank_select: u8, +} +impl InesMapper002 { + pub fn new(prg_rom: Box<[u8]>, mirroring: Mirroring, game_save: GameSave) -> Self { + + Self { + game_save, + + prg_rom, + chr_ram: vec![0; 8192].into_boxed_slice(), + + mirroring, + + bank_select: 0, + } + } +} + +impl Mapper for InesMapper002 { + + fn read(&self, addr: u16) -> u8 { + match addr { + 0x6000..=0x7FFF => { + self.game_save.read(addr) + } + 0x8000..=0xBFFF => { + let addr = (self.bank_select as usize * 0x4000) + (addr as usize - 0x8000); + self.prg_rom[addr] + } + 0xC000..=0xFFFF => { + let addr = (self.prg_rom.len() - 0x4000) + (addr as usize - 0xC000); + self.prg_rom[addr] + } + _ => 0 + } + + } + fn write(&mut self, addr: u16, val: u8) { + match addr { + 0x6000..=0x7FFF => { + self.game_save.write(addr, val); + } + 0x8000..=0xFFFF => { + self.bank_select = val; + } + _ => {} + } + + } + fn read_chr(&self, addr: u16) -> u8 { + self.chr_ram[addr as usize] + } + fn write_chr(&mut self, addr: u16, val: u8) { + self.chr_ram[addr as usize] = val; + } + fn mirroring(&self) -> Mirroring { + self.mirroring + } +} diff --git a/src/memory/mappers/mod.rs b/src/memory/mappers/mod.rs index eab76d5..1dffe4f 100644 --- a/src/memory/mappers/mod.rs +++ b/src/memory/mappers/mod.rs @@ -1,5 +1,6 @@ pub mod ines_mapper000; pub mod ines_mapper001; +pub mod ines_mapper002; pub mod ines_mapper004; pub mod ines_mapper163; @@ -7,5 +8,6 @@ pub mod dummy_mapper; pub use self::ines_mapper000::InesMapper000; pub use self::ines_mapper001::InesMapper001; +pub use self::ines_mapper002::InesMapper002; pub use self::ines_mapper004::InesMapper004; pub use self::ines_mapper163::InesMapper163; \ No newline at end of file From 754ff45d237debd14153ef8e93f6df5e42dc65c7 Mon Sep 17 00:00:00 2001 From: Polaro4 Date: Wed, 6 May 2026 15:47:12 -0300 Subject: [PATCH 2/3] update .nes reading function --- src/memory/bus.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/memory/bus.rs b/src/memory/bus.rs index deaa8af..a3fc101 100644 --- a/src/memory/bus.rs +++ b/src/memory/bus.rs @@ -1,7 +1,6 @@ use std::path::Path; use crate::engine::console::{self, LogType}; -use crate::memory::mappers::InesMapper163; use crate::memory::{mappers, mapper_base::*}; use crate::{ @@ -237,6 +236,8 @@ where T: Mapper + 'static { ) } +const NES_SIGNATURE: [u8; 4] = [0x4E, 0x45, 0x53, 0x1A]; + /// Loads an iNES ROM file and returns the appropriate "mapper" for the cartridge. /// /// The "Mappers" in this codebase are a customized 'struct/data format' with all the data of the cartridge on it @@ -286,9 +287,18 @@ pub fn load_rom_from_file(path: &Path) -> Result>, Box> 4); + let header = &rom_data[0..16]; + + let mapper_match = (header[7] & 0xF0) | (header[6] >> 4); - let has_trainer = (rom_data[6] & 0b0000_0100) != 0; + let has_trainer = (header[6] & 0b0000_0100) != 0; + + if header[0..4] != NES_SIGNATURE { + console::print_logs( + LogType::Warning, + format!("THE ROM HEADER DOESN'T HAVE A NES SIGNATURE, THIS ROM MIGHT BE INVALID") + ); + } console::print_logs(LogType::Info, format!("--- ROM HEADER INFO ---")); console::print_logs(LogType::Info, format!("Byte 4 (PRG Banks): {}", rom_data[4])); @@ -328,8 +338,9 @@ pub fn load_rom_from_file(path: &Path) -> Result>, Box Ok(wrap_in_pointers(mappers::InesMapper000::new(prg_rom_data, chr_rom_data, mirroring_type))), 1 => Ok(wrap_in_pointers(mappers::InesMapper001::new(prg_rom_data, chr_rom_data, GameSave::new(path)))), + 2 => Ok(wrap_in_pointers(mappers::InesMapper002::new(prg_rom_data, mirroring_type, GameSave::new(path)))), 4 => Ok(wrap_in_pointers(mappers::InesMapper004::new(prg_rom_data, chr_rom_data, mirroring_type, GameSave::new(path)))), - 163 => Ok(wrap_in_pointers(InesMapper163::new(prg_rom_data, chr_rom_data, mirroring_type, GameSave::new(path)))), + 163 => Ok(wrap_in_pointers(mappers::InesMapper163::new(prg_rom_data, chr_rom_data, mirroring_type, GameSave::new(path)))), _ => Err(format!("Mapper {} is not supported yet", mapper_match).into()) } From 4e99de0e75983d75763c5fa0c79a93411688be0e Mon Sep 17 00:00:00 2001 From: Miguel <67387789+Polar-404@users.noreply.github.com> Date: Wed, 6 May 2026 22:15:33 -0300 Subject: [PATCH 3/3] sync with master branch (#13) * update README for the v0.1.0-alpha (#12) * Rename workflow from Rust Tests to Cargo Tests, Nestest * Revise README for SelectNES with badges and updates Updated project name and improved README structure with badges and additional information. * Adjust image sizes in README Showcase * Update README with image display formatting --- .github/workflows/test.yml | 2 +- README.md | 94 +++++++++++++++++++++----------------- 2 files changed, 53 insertions(+), 43 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b67c782..43a4c94 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,4 +1,4 @@ -name: Rust Tests +name: Cargo Tests, Nestest on: push: diff --git a/README.md b/README.md index def3be5..65f8ac9 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,22 @@ -# NES Emulator +
+ +# SelectNES -**A Nintendo Entertainment System (NES) emulator built in Rust.** +[![Rust Tests](https://github.com/Polar-404/SelectNes/actions/workflows/test.yml/badge.svg)](https://github.com/Polar-404/SelectNes/actions/workflows/test.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Rust](https://img.shields.io/badge/Rust-Stable-orange.svg)](https://www.rust-lang.org/) -![Rust](https://img.shields.io/badge/rust-%23000000.svg?style=for-the-badge&logo=rust&logoColor=white) ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge) +**A Nintendo Entertainment System (NES) emulator built in Rust.** +
---- ## Showcase +
-

SMB Gameplay with Debug Info

+| Super Mario Bros. | Kirby's Adventure | +| :---: | :---: | +| | | +
> [!NOTE] > This project was created purely as a hobby, a personal challenge, and an opportunity to practice low-level programming and system architecture in Rust. So for the foreseeable future it is not intended to compete with established emulators but rather serves as a proof of concept and a deep learning experience regarding low-level programming and the NES hardware. @@ -22,10 +30,10 @@ Make sure you have [Rust and Cargo](https://www.rust-lang.org/tools/install) ins ```bash # Clone the repository -git clone https://github.com/Polar-404/NES_Emulator.git +git clone https://github.com/Polar-404/SelectNES.git # Navigate to the directory -cd NES_Emulator +cd SelectNES # Compile and run cargo run --release @@ -33,23 +41,16 @@ cargo run --release ## Controls -| NES Button | Primary | Secondary | -| :-------------- | :-----: | :-----------: | -| **D-Pad Up** | `W` | `Up Arrow` | -| **D-Pad Down** | `S` | `Down Arrow` | -| **D-Pad Left** | `A` | `Left Arrow` | -| **D-Pad Right** | `D` | `Right Arrow` | -| **A** | `J` | `Z` | -| **B** | `K` | `X` | -| **Select** | `N` | `C` | -| **Start** | `M` | `V` | - -**System Commands:** -* **Volume Up:** `+` -* **Volume Down:** `-` -* **Pause/Menu:** `Esc` -* **Change Color Palette:** `.` (Period) -* **Paste ROM Path:** `Ctrl + V` +| NES Button | Primary | +| :-------------- | :-----------: | +| **D-Pad Up** | `Up Arrow` | +| **D-Pad Down** | `Down Arrow` | +| **D-Pad Left** | `Left Arrow` | +| **D-Pad Right** | `Right Arrow` | +| **A** | `Z` | +| **B** | `X` | +| **Select** | `C` | +| **Start** | `V` | --- ## Current Features @@ -67,38 +68,45 @@ cargo run --release - NROM (Mapper 0) - MMC1 (Mapper 1) - + + - MMC3 (Mapper 4) + +- Debug Tools: (Pattern Table viewer, Palette viewer e Hex Memory viewer) + --- ## Tech Stack -- **[Rust](https://www.rust-lang.org/):** Main language used for the project. - -- **[Macroquad](https://macroquad.rs/):** Core library for graphics rendering and input handling. - -- **[Cpal](https://github.com/RustAudio/cpal):** Library for audio processing and output. - -- **[Ringbuf](https://crates.io/crates/ringbuf):** Lock-free circular buffer for audio. - -- **[Arboard](https://crates.io/crates/arboard):** System clipboard access. - -- **Others:** `image`, `lazy_static`, `sysinfo`. - +- **[Rust](https://www.rust-lang.org/):** Main language used for the project, ensuring memory safety and high performance. + +- **Graphics & UI:** + - **[Glow](https://github.com/grovesNL/glow):** "GL on Whatever" — used for cross-platform OpenGL bindings. + - **[Egui](https://github.com/emilk/egui):** Immediate mode GUI library used for the debugging tools (PPU, Memory, and CPU viewers). + - **OpenGL:** Low-level rendering for the NES screen and implementation of custom shaders. + +- **[Cpal](https://github.com/RustAudio/cpal):** Low-level library for audio processing and output. + +- **[Ringbuf](https://crates.io/crates/ringbuf):** Lock-free circular buffer for efficient audio synchronization. +- **[Arboard](https://crates.io/crates/arboard):** Native system clipboard access for easy ROM path pasting. + +- **Others:** `image`, `rfd`, `serde`. + + --- ## Roadmap / To-Do As this is an ongoing learning project, several areas still need improvement: -- [ ] **User Interface:** Improve the start menu and add more graphics and audio configuration options. +- [x] **User Interface:** Improve the start menu and add more graphics and audio configuration options. - [ ] **Audio (APU):** Implement the DMC (Delta Modulation Channel). - [ ] **Synchronization:** Sync audio with FPS to maintain a more stable frame rate, faithful to the original console. -- [ ] **Mappers:** Add support for more mappers (e.g., MMC3) to increase game compatibility. +- [ ] **Mappers:** Add support for more mappers to increase game compatibility. -- [ ] **Saves:** Implement Save/Load states functionality. +- [x] **Saves:** Implement Save/Load states functionality. (only in-game saves so far, saving the emulator state isn't implemented yet) - [ ] **Code Quality:** Refactor and clean up the codebase, and potentially add documentation and internationalization (EN/PT-BR). @@ -108,15 +116,16 @@ As this is an ongoing learning project, several areas still need improvement: - [ ] **Scripting:** Implement user script support with Lua. -- [ ] **More Palettes:** Implement the ability for the user to insert their own palettes via interface and/or a designated folder with `.pal` files (maybe even `.hex` files). +- [x] **More Palettes:** Implement the ability for the user to insert their own palettes via interface and/or a designated folder with `.pal` files (maybe even `.hex` files). -- [ ] **Custom Graphics Pipeline:** Transition from Macroquad to OpenGL/Glow for +- [x] **Custom Graphics Pipeline:** Transition from Macroquad to OpenGL/Glow for - lower input latency - better frame synchronization. - Custom CRT/NTSC shaders. - [ ] **WebAssembly (WASM):** Browser-based emulation — play directly without installing anything. + --- ## Acknowledgments & References @@ -128,6 +137,7 @@ This project would not have been possible without the incredible emulation commu - **[bugzmanov/nes_ebook](https://github.com/bugzmanov/nes_ebook):** The e-book "Writing NES Emulator in Rust" was a fundamental reference. Parts of the CPU implementation and Design Patterns were heavily based on his code to understand Rust's nuances applied to emulation. + --- ## Licenses