# neser
NESER - NES Emulator in Rust
## Building
```bash
cargo build --release --features sdl
```
## Development Setup
After cloning, activate the shared git hooks (runs `cargo fmt` automatically before each commit):
```bash
git config core.hooksPath .githooks
```
## Running
```bash
cargo run --release --features sdl
```
### Autorunner
Record or play back joypad input alongside a ROM:
```bash
cargo run --release --features sdl --bin autorunner -- --record roms/games/pac-man.nes
cargo run --release --features sdl --bin autorunner -- --playback roms/games/pac-man.nes
cargo run --release --features sdl --bin autorunner -- --playback --headless roms/games/pac-man.nes
```
Or after building:
```bash
./target/release/neser
```
## Configuration
NESER can be configured via config files and/or command-line arguments.
### Config File Locations & Priority
Configuration is loaded with the following priority (highest overrides lowest):
1. Default values (built-in)
2. `~/.neser/neser.conf` (user-wide settings, if exists)
3. `./neser.conf` (project/directory-specific, if exists)
4. `--config <file>` (if specified, replaces steps 2 and 3)
5. Command-line arguments (highest priority)
See [neser.conf.example](neser.conf.example) for all available options and documentation.
### Quick Start
Copy the example config to get started:
```bash
# For user-wide settings
mkdir -p ~/.neser
cp neser.conf.example ~/.neser/neser.conf
# Or for directory-specific settings
cp neser.conf.example neser.conf
```
### Command-Line Options
```text
Options:
--help Show help message
-h Show help message (short)
--pal Use PAL TV system (default: NTSC)
--no-audio Disable audio output
--trace Enable CPU trace output (level 1)
--trace-nestest Enable CPU trace output (nestest.log format)
--trace-cpu[=N] Enable CPU trace output at level N (default: 1)
--trace-ppu[=N] Enable PPU trace output at level N (default: 1)
--trace-apu[=N] Enable APU trace output at level N (default: 1)
--trace-mapper[=N] Enable Mapper trace output at level N (default: 1)
#
# Trace level N (0 = off, 5 = VERY verbose/detailed)
# Example: --trace-cpu=2 or --trace-ppu=3
--disable-pulse1 Mute pulse 1 channel
--disable-pulse2 Mute pulse 2 channel
--disable-triangle Mute triangle channel
--disable-noise Mute noise channel
--disable-dmc Mute DMC channel
--no-vsync Disable VSync (default: enabled)
--no-gamepads Disable gamepad/joystick support
--start-in-debugger Open debugger windows (CPU/PPU/APU) on startup (starts paused)
--load-state <path> Load a save state on startup
--fullscreen Run emulator in fullscreen mode
--display <N> Select display index for fullscreen (default: 0)
--window-height <N> Window height in pixels, windowed mode only (e.g., 720)
```
## Controller Mapping
NESER supports two controller ports (port 1 and port 2), each of which can be configured with different controller types.
### Supported Controller Types
- **Joypad** - Standard NES controller (default for both ports)
- **Paddle** - Arkanoid-style paddle controller with analog position and trigger button
### Per-Port Controller Selection
Each port can be independently configured in the configuration file or via the auto-detection mechanism:
**Port 1** (Controller 1):
- Default: Joypad
- Can be configured as Paddle for games that use paddle on port 1
**Port 2** (Controller 2):
- Default: Joypad
- Can be configured as Paddle for games that use paddle on port 2
### Four Score (4-player) mode
Enable Four Score with CLI `--enable-4-score` or config key `enable_4_score=true`.
When enabled, input is assigned as follows:
- 2 physical gamepads connected: gamepads control emulated players 1-2, keyboard controls players 3-4
- 1 physical gamepad connected: gamepad controls player 1, keyboard controls players 2-3
- 0 physical gamepads connected: keyboard controls players 1-2
### Auto-Detection
NESER automatically detects known games that require special controllers (for example, paddle or Zapper) by ROM CRC32 and configures the appropriate port **only when neither controller port is explicitly configured**:
- **Arkanoid** (CRC: 0x32FB0583) → Paddle on **port 2**
- **PaddleTest3** (CRC: 0x47F9F410) → Paddle on **port 1**
- Known Zapper-based games are similarly detected and configured with a Zapper on the appropriate port
**Important**: Any explicit controller configuration (`controller_port1` and/or `controller_port2`, including CLI overrides) disables CRC-based auto-detection so your configured controllers are always used.
When a known ROM is loaded and no controller ports are explicitly configured, you'll see a message like:
```
Enabling Arkanoid controller on port 2 for inserted cartridge
```
### Paddle Controller Input (SDL Frontend)
When a paddle controller is enabled on either port:
- **Mouse X position** controls the paddle position (non-linear curve with faster edge response)
- **Left mouse button** controls the paddle trigger
- The mouse input is routed to all ports configured as paddles
- Keyboard/gamepad input for that port is suppressed while paddle mode is active
### Manual Configuration Examples
Manual controller configuration via config file is supported and will override auto-detection. Configure controller types in your config file before loading a ROM:
**Example 1: Paddle on Port 1**
```text
controller_port1=arkanoid
controller_port2=joypad
```
**Example 2: Paddle on Port 2 (Arkanoid)**
```text
controller_port1=joypad
controller_port2=arkanoid
```
**Example 3: Paddles on Both Ports**
```text
controller_port1=arkanoid
controller_port2=arkanoid
```
**Note**: When you explicitly configure controllers, auto-detection is disabled, giving you full control over controller configuration regardless of which ROM is loaded.
### Validation Steps
1. Run the SDL frontend with a paddle-enabled ROM:
- roms/games/arkanoid.nes (if available)
- roms/automated_tests/PaddleTest3 (if available)
2. Move the mouse left/right and confirm the paddle moves across the full range.
3. Verify fine control near the center and faster response near the edges.
4. Click and release the left mouse button and confirm the trigger is detected.
5. Confirm keyboard/controller input for that port does not interfere while paddle mode is active.
### Blargg NES Test Suite Status
NESER is tested against Blargg’s (and Blargg compatible) NES test ROMs to verify CPU, PPU, APU, DMA, and mapper correctness. The following test suites are currently passing in the main branch:
**Test Suites (by directory and ROM file):**
- **apu_mixer/**
- dmc.nes
- noise.nes
- triangle.nes
- square.nes
- **apu_reset/**
- 4015_cleared.nes
- 4017_timing.nes
- 4017_written.nes
- irq_flag_cleared.nes
- len_ctrs_enabled.nes
- works_immediately.nes
- **apu_test/rom_singles/**
- 1-len_ctr.nes
- 2-len_table.nes
- 3-irq_flag.nes
- 4-jitter.nes
- 5-len_timing.nes
- 6-irq_flag_timing.nes
- 7-dmc_basics.nes
- 8-dmc_rates.nes
- **dmc_dma_during_read4/**
- dma_2007_read.nes
- dma_2007_write.nes
- dma_4016_read.nes
- double_2007_read.nes
- read_write_2007.nes
- **blargg_ppu_tests_2005.09.15b/**
- palette_ram.nes
- sprite_ram.nes
- vbl_clear_time.nes
- vram_access.nes
- **branch_timing_tests/**
- 1.Branch_Basics.nes
- 2.Backward_Branch.nes
- 3.Forward_Branch.nes
- **cpu_dummy_reads/**
- cpu_dummy_reads.nes
- **cpu_dummy_writes/**
- cpu_dummy_writes_oam.nes
- cpu_dummy_writes_ppumem.nes
- **cpu_exec_space/**
- test_cpu_exec_space_ppuio.nes
- test_cpu_exec_space_apu.nes
- **cpu_interrupts_v2/rom_singles/**
- 1-cli_latency.nes
- 2-nmi_and_brk.nes
- 3-nmi_and_irq.nes
- 4-irq_and_dma.nes
- 5-branch_delays_irq.nes
- **cpu_reset/**
- registers.nes
- ram_after_reset.nes
- **cpu_timing_test6/**
- cpu_timing_test.nes
- **instr_misc/rom_singles/**
- 01-abs_x_wrap.nes
- 02-branch_wrap.nes
- 03-dummy_reads.nes
- 04-dummy_reads_apu.nes
- **instr_test-v5/rom_singles/**
- 01-basics.nes
- 02-implied.nes
- 03-immediate.nes
- 04-zero_page.nes
- 05-zp_xy.nes
- 06-absolute.nes
- 07-abs_xy.nes
- 08-ind_x.nes
- 09-ind_y.nes
- 10-branches.nes
- 11-stack.nes
- 12-jmp_jsr.nes
- 13-rts.nes
- 14-rti.nes
- 15-brk.nes
- 16-special.nes
- **instr_timing/rom_singles/**
- 1-instr_timing.nes
- 2-branch_timing.nes
- **mmc3_irq_tests/**
- 1.Clocking.nes
- 2.Details.nes
- 3.A12_clocking.nes
- 4.Scanline_timing.nes
- 5.MMC3_rev_A.nes
- 6.MMC3_rev_B.nes
- **mmc3_test_2/rom_singles/**
- 1-clocking.nes
- 2-details.nes
- 3-A12_clocking.nes
- 4-scanline_timing.nes
- 5-MMC3.nes
- 6-MMC3_alt.nes
- **oam_read/**
- oam_read.nes
- **oam_stress/**
- oam_stress.nes
- **ppu_open_bus/**
- ppu_open_bus.nes
- **ppu_read_buffer/**
- test_ppu_read_buffer.nes
- **ppu_sprite_hit/rom_singles/**
- 01-basics.nes
- 02-alignment.nes
- 03-corners.nes
- 04-flip.nes
- 05-left_clip.nes
- 06-right_edge.nes
- 07-screen_bottom.nes
- 08-double_height.nes
- 09-timing.nes
- 10-timing_order.nes
- **ppu_sprite_overflow/rom_singles/**
- 01-basics.nes
- 02-details.nes
- 03-timing.nes
- 04-obscure.nes
- 05-emulator.nes
- **ppu_vbl_nmi/rom_singles/**
- 01-vbl_basics.nes
- 02-vbl_set_time.nes
- 03-vbl_clear_time.nes
- 04-nmi_control.nes
- 05-nmi_timing.nes
- 06-suppression.nes
- 07-nmi_on_timing.nes
- 08-nmi_off_timing.nes
- 09-even_odd_frames.nes
- 10-even_odd_timing.nes
- **sprdma_and_dmc_dma/**
- sprdma_and_dmc_dma.nes
- sprdma_and_dmc_dma_512.nes
> See src/blargg_tests.rs for the full list of passing tests and ROM paths.
## Shaders
NESER supports shader filters for visual effects:
- `crt` - CRT simulation (scanlines, shadow mask, bloom)
- `ntsc` - NTSC composite video simulation
- `smooth` - Smooth pixel upscaling (xBRZ)
- `none` - No effect (raw pixels)
Example:
```bash
neser --filter crt
```
Or in config file:
```text
filter=crt
```