This project is STILL EXPERIMENTAL and WIP. There are LOTS OF BUGS(01/24/2021).
Modern C++ NES developing toolkits for My Own Learning Porpose. The implementation is NOT fully faithfull to the original NES hardware though, but CPU instruction implementations are cycle accurate.
- PPU Foregound/Sprites Rendering In original NES hardware archetecture, sprite gathering, sprite clearing and sprite evaluation for the next scanline, natural on the digital circuit, these things are distributed in parallel, but in my implementation, these are embedded in the PPU cycle along with the background rendering. This may limit compatibility with some games.
The following is a checklist of features and their progress:
- Console
- NTSC
- PAL
- Dendy
- Headless mode
- CPU
- Official Instructions
- Unofficial Instructions
- Interrupts
- PPU
- VRAM
- Background
- Sprites
- NTSC TV Artifact Effects
- Emphasize RGB/Grayscale
- APU
- Pulse Channels
- Triangle Channels
- Noise Channels
- Delta Mulation Channel
- Inputs
- Memory
- Cartridge
- Battery-backed Save RAM
- iNES Format
- NES 2.0 Format
- Mappers
- NROM (Mapper 0)
- SxROM/MMC1 (Mapper 1)
- UxROM (Mapper 2)
- CNROM (Mapper 3)
- TxROM/MMC3 (Mapper 4)
- ExROM/MMC5 (Mapper 5)
- AxROM (Mapper 7)
- PxROM/MMC2 (Mapper 9)
- Testing/Debugging/Documentation
- Unit/Integration tests
- Test ROMs
- Logging
NesDev builds with CMake
On macOS / Linux
# in NesDev root
mkdir build
cd build
cmake ..
make
make install
NesDev library (libnesdev) is a static library for developing NES emulators, so libnesdev it self does NOT contain any media layer implementations. All you have to do is implememnt media backends with your favorite library, such as SDL2 and hook the NesDev's Framebuffer API for PPU and Sampling API for APU respectively.
#include <filesystem>
#include <fstream>
#include <functional>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <SDL.h>
#include <SDL_ttf.h>
#include <nesdev/core.h>
#include "backend.h"
#include "cli.h"
#include "utility.h"
namespace nc = nesdev::core;
int main(int argc, char** argv) {
Utility::Init();
CLI cli(argc, argv);
std::string rom;
if ((rom = std::filesystem::absolute(cli.Get("--rom"))).empty()) {
std::cerr << "iNES file must be specified" << std::endl;
exit(1);
}
try {
std::ifstream ifs(rom, std::ifstream::binary);
nc::NES nes(nc::ROMFactory::NROM(ifs));
ifs.close();
Utility::ShowHeader(nes);
Backend sdl(nes, nes.controller_1.get(), nes.controller_2.get());
nes.ppu->Framebuffer([&sdl](std::int16_t x, std::int16_t y, nc::ARGB rgba) {
sdl.Pixel(x, y, rgba);
});
//nes->apu->Sampling([&sdl]() {
// /* This is a placeholder for APU API. */
//});
if (cli.Defined("--chr_rom")) {
while (sdl.IsRunning()) {
Utility::RenderCHRRom(nes, sdl);
sdl.Update();
}
} else {
while (sdl.IsRunning()) {
nes.Tick();
if (/*nes.cpu->IsIdle() &&*/ (nes.cycle % 3 == 0))
Utility::Trace(nes);
if (nes.ppu->IsPostRenderLine() && nes.ppu->Cycle() == 0) {
sdl.Update();
}
}
}
} catch (const std::exception& e) {
Utility::ShowStackTrace();
std::cerr << e.what() << std::endl;
}
return 0;
}
Contributions are more than welcome! Everything from code to examples and documentation are all equally valuable so please don't feel you can't contribute. To contribute please fork the project make your changes and submit a pull request. We will do our best to work through any issues with you and get your code merged into the main branch.