67cfa183f8
wowie this was a trickyboi to figure out, but I think I managed to get a decent-working solution. The algorithm consists of 2 parts: 1) detecting scene transitions, and 2) figuring out if the new scene is a duplicate. Part 1) was solved using perceptual hashing, and watching the delta of the frame hash between frames. When it spikes, odds are that's a scene change. Part 2) was a bit trickier, but boils down to hashing every frame and associating it with a scene + scroll values. That way, when a scene transition is detected, I can check the hash-maps to see if the "new" frame is actually one I've seen before. There is a bit of extra work that gets the technique working with elaborate fade-in animations / animated backgrounds, but it's not magic or anything. What's left? Well, 1) needs to be improved, since the current delta threshold value i'm using has been pulled out of thin air, and while it gives reasonable results on many games, ideally, it should be more robust 2) is pretty good though! All that I need to do now to be super happy with it is serialize the scene data and enable loading / saving it to disk! Once that's done, the initial vision of being able to automatically map out a game will have come true! exciting! |
||
---|---|---|
cmake | ||
research | ||
resources | ||
roms | ||
scripts | ||
src | ||
thirdparty | ||
.gitattributes | ||
.gitignore | ||
.travis.yml | ||
_config.yml | ||
appveyor.yml | ||
CMakeLists.txt | ||
LICENSE | ||
README.md | ||
wideNES.md |
ANESE (Another NES Emulator) is a Nintendo Entertainment System Emulator written for fun and learning.
Accuracy and performance are long-term goals, but the primary focus is getting popular titles up and running. There are still a lot of bugs, but many games are working quite well already.
ANESE is cross-platform, and is regularly tested on macOS, Windows, and Linux.
ANESE core uses clean and interesting C++11, emphasizing readability, maintainability, and approachability. It is well commented, providing in-line sources and insights for much of the implementation. It is also dependency free (aside from stdlib), making it easy to embed in other projects.
WideNES
wideNES is a novel technique that can automatically "map-out" levels and worlds in NES games. Check out the wideNES Readme for details.
A GIF is worth a 1000 words:
Pretty cool huh? Here's another one:
Downloads
Official releases of ANESE can be found on the Releases tab on GitHub.
Alternatively, for the most up-to-date version of ANESE, nightly builds are available. These are compiled directly from the latest ANESE commit, so there may/will be bugs.
Windows: You can download builds of ANESE from AppVeyor's build artifacts page.
macOS: Travis uploads ANESE.app bundles to this GDrive folder.
Building
Dependencies
ANESE's emulation core (src/nes) doesn't have any major dependencies, but the UI does use a couple. Most of these dependencies are bundled with ANESE (see: /thirdparty), although some require additional installation:
- SDL2 (video/audio/controls)
- Linux:
apt-get install libsdl2-dev
(on Ubuntu) - MacOS:
brew install SDL2
- Windows:
- Download dev libs from here and unzip them somewhere.
- EITHER: Set the
SDL
environment variable to point to the dev libs - OR: Unzip them to
C:\sdl2\
(Where I put them) - OR: Modify the
SDL2_MORE_INCLUDE_DIR
variable inCMakeLists.txt
to point to the SDL2 dev libs
- Linux:
Generating + Compiling
ANESE builds with CMake
On macOS / Linux
# in ANESE root
mkdir build
cd build
cmake ..
make
make install # on macOS: creates ANESE.app in ANESE/bin/
On Windows:
mkdir build
cd build
cmake ..
msbuild anese.sln /p:Configuration=Release
Running
ANESE opens to a directory-browser, from which ROMs can be launched.
ANESE can run from the shell using anese [rom.nes]
syntax. Certain features
are only accessible from the command-line at the moment (e.g: movie recording
/ playback, PPU timing hacks). For a full list of switches, run anese -h
Windows Users: make sure the executable can find SDL2.dll
! Download the
runtime DLLs from the SDL website, and plop them in the same directory as
anese.exe
Mappers
Most popular Mappers are implemented:
# | Name | Some Games |
---|---|---|
000 | NROM | Super Mario Bros. 1, Donkey Kong, Duck Hunt |
001 | MMC1 | Legend of Zelda, Dr. Mario, Metroid |
002 | UxROM | Megaman, Contra, Castlevania |
003 | CNROM | Arkanoid, Cybernoid, Solomon's Key |
004 | MMC3 | Super Mario Bros 2 & 3, Kirby's Adventure |
007 | AxROM | Marble Madness, Battletoads |
009 | MMC2 | Punch Out!! |
Feel free to open a PR for any mappers you implement :)
Controls
Currently hard-coded to the following:
Button | Key | Controller |
---|---|---|
A | Z | X |
B | X | A |
Start | Enter | Start |
Select | Right Shift | Select |
Up | Up arrow | D-Pad |
Down | Down arrow | D-Pad |
Left | Left arrow | D-Pad |
Right | Right arrow | D-Pad |
Any xbox-compatible controller should work.
There are also a couple of emulator actions:
Action | Key | Controller |
---|---|---|
Pause / Open Menu | Esc | Left Thumbstick Button |
Reset | Ctrl - R | |
Power Cycle | Ctrl - P | |
Toggle CPU logging | Ctrl - C | |
Speed +25% | Ctrl - = | |
Speed -25% | Ctrl - - | |
Fast-Forward | Space | Right Thumbstick Button |
Make Save-State | Ctrl - (1-4) | |
Load Save-State | Ctrl - Shift - (1-4) |
(there are 4 save-state slots)
DISCLAIMERS
- ANESE is not the best emulator out there, far from it! Expect bugs!
- My APU uses a naive sampling algorithm with a basic lookup table grafted from
the nesdev wiki. The
blargg-apu
branch has an older version of ANESE that uses Blargg's awesomenes_snd_emu
library for the APU, and while my integration was a bit unstable at times, it did sound a lot better when it did work. - The CPU is instruction-cycle accurate, but not sub-instruction cycle
accurate. While this inaccuracy doesn't affect most games, there are some that
that rely on sub-instruction level timings (eg: Solomon's Key).
- The
--alt-nmi-timing
flag might fix some of these games.
- The
TODO
These are features that will add major value to ANESE:
- Implement: Cycle accurate CPU (will probably fix many bugs)
- Implement: Better menu (not just fs, also config)
- CMake: more robust macOS bundles (good way to get SDL2.0 packaged?)
- Implement: LibRetro Core
- Implement: Get the Light-gun working
- Debugging: Add debug GUI
- All objects implementing the Memory interface must also implement
peek
, i.e: aconst
read. As such, a debugger could easily inspect any/all memory locations with no side effects!
- All objects implementing the Memory interface must also implement
Here's a couple that have been crossed off already:
- Implement: My own APU (don't use Blarrg's)
- Refactor: Modularize
main.cc
- push everything intosrc/ui/
- Refactor: Split
gui.cc
into more files!
- Refactor: Split
- Refactor: Push common mapper behavior to Base Mapper (eg: bank chunking)
And here are some ongoing low-priority goals:
- Refactor: Roll-my-own Sound_Queue (SDL_QueueAudio?)
- Cleanup: Unify naming conventions (either camelCase or snake_case)
- Cleanup: Comment the codebase even more
- Security: Actually bounds-check files lol
- Cleanup: Conform to the
.fm2
movie format better - Cleanup: Remove fatal asserts (?)
- Cleanup: Switch to a better logging system (*cough* not fprintf *cough*)
Roadmap
Key Milestones
- Parse iNES files
- Create Cartridges (iNES + Mapper interface)
- CPU
- Set Up Memory Map
- Hardware Structures (registers)
- Core Loop / Basic Functionality
- Read / Write RAM
- Addressing Modes
- Fetch - Decode - Execute
- Official Opcodes Implemented
- Handle Interrupts
- PPU
- Set Up Basic Rendering Context (SDL)
- Implement Registers + Memory Map them
- Implement DMA
- Generate NMI -> CPU
- Core rendering loop
- Background Rendering
- Sprite Rendering - currently not hardware accurate
- Proper Background / Foreground blending
- Sprite Zero Hit
- Misc PPU flags (emphasize RGB, Greyscale, etc...)
- APU
- Implement Registers + Memory Map them
- Frame Timer IRQ - kinda
- Set Up Basic Sound Output Context (SDL)
- Channels
- Pulse 1
- Pulse 2
- Triangle
- Noise
- DMC
- DMC DMA
- Joypads
- Basic Controller
- Zapper - still needs work
- NES Four Score
Secondary Milestones
- Loading Files with picker
- Reset / Power-cycle
- Fast Forward
- Run / Pause
- Saving
- Battery Backed RAM - Saves to
.sav
- Save-states
- Dump to file
- Battery Backed RAM - Saves to
- Config File
- Preserve ROM path
- Window size
- Controls
- Running NESTEST (behind a flag)
- Controller support - currently very basic
- A SDL GUI
- SDL-based ROM picker
- Options menu
Tertiary Milestones (Fun Features!)
- Zipped ROM support
- Rewind
- Game Genie
- Movie recording and playback
- More ROM formats (not just iNES)
- Proper PAL handling?
- Proper NTSC artifacting?
- Multiple Front-ends
- SDL Standalone
- LibRetro
- Debugger!
- CPU
- Step through instructions
- PPU Views
- Static Palette
- Palette Memory
- Pattern Tables
- Nametables
- OAM memory
- CPU
Accuracy & Compatibility
- More Mappers! Always more mappers!
- Add automatic testing
- Screenshots: compare power-on with 30 seconds of button mashing
- Test ROMs: Parse debug outputs
- CPU
- Implement Unofficial Opcodes
- Pass More Tests
- (Stretch) Switch to sub-instruction level cycle-based emulation (vs instruction level)
- PPU
- Make the sprite rendering pipeline more accurate (fetch-timings)
- Pass More Tests
- Make value in PPU <-> CPU bus decay?
Attributions
- A big shout-out to LaiNES and fogleman/nes, two solid NES emulators that I referenced while implementing some particularly tricky parts of the PPU). While I actively avoided looking at the source codes of other NES emulators while writing my initial implementations of the CPU and PPU, I did sneak a peek at how others solved some problems once I got stuck.
- These awesome libraries make ANESE a lot nicer to use:
- sdl2 - A/V and Input
- SDL_inprint - SDL fonts, without SDL_ttf
- clara - argument Parsing
- miniz - zipped ROM support
- cute_headers - cross-platform directory browsing
- cfg_path - cross-platform config file
- simpleini - ini config parsing / saving