150 lines
4.4 KiB
Rust
150 lines
4.4 KiB
Rust
mod cartridge;
|
|
mod bus;
|
|
mod cpu;
|
|
mod mapper;
|
|
mod palette;
|
|
mod render;
|
|
mod ppu;
|
|
mod dump;
|
|
mod input;
|
|
|
|
use crate::bus::{Bus, NesBus};
|
|
use crate::cartridge::Cartridge;
|
|
use crate::cpu::Cpu;
|
|
use crate::render::{PpmRenderer, Renderer};
|
|
use eframe::egui;
|
|
use crate::render::WindowRenderer;
|
|
|
|
|
|
fn main() {
|
|
let args: Vec<String> = std::env::args().collect();
|
|
match args.len() {
|
|
1 => run_nestest(),
|
|
3 if args[1] == "blargg" => run_blargg(&args[2]),
|
|
3 if args[1] == "dbg" => run_dbg(&args[2]),
|
|
3 if args[1] == "gui" => run_gui(&args[2]),
|
|
2 => run_cart(&args[1]),
|
|
_ => eprintln!("usage: rnes [<rom> | blargg <rom> | dbg <rom> | gui <rom>]"),
|
|
}
|
|
}
|
|
|
|
fn run_cart(path: &str) {
|
|
let cart = Cartridge::from_file(path).unwrap();
|
|
let mut bus = NesBus::new(cart);
|
|
let mut cpu = Cpu::new();
|
|
cpu.reset(&mut bus);
|
|
let mut renderer = PpmRenderer::new();
|
|
let frames: u32 = 180;
|
|
|
|
for _ in 0..frames {
|
|
run_one_frame(&mut bus, &mut cpu);
|
|
renderer.present(bus.framebuffer(), 256, 240);
|
|
}
|
|
}
|
|
|
|
fn run_blargg(path: &str) {
|
|
let cart = Cartridge::from_file(path).unwrap();
|
|
let mut bus = NesBus::new(cart);
|
|
let mut cpu = Cpu::new();
|
|
cpu.reset(&mut bus); // boots from $FFFC-$FFFD via MMC1 PRG banking
|
|
|
|
let cycle_cap: u64 = 5_000_000; // blargg tests complete well under this; tune if needed
|
|
while cpu.cycles < cycle_cap {
|
|
cpu.step(&mut bus);
|
|
}
|
|
|
|
let r0 = bus.read(0x6000);
|
|
let r1 = bus.read(0x6001);
|
|
let r2 = bus.read(0x6002);
|
|
let r3 = bus.read(0x6003);
|
|
println!("result: $6000={:02X} $6001={:02X} $6002={:02X} $6003={:02X}", r0, r1, r2, r3);
|
|
if r0 == 0 { println!("PASS"); } else { println!("FAIL (error {:#04X})", r0); }
|
|
}
|
|
|
|
fn run_nestest() {
|
|
let cart: Cartridge = Cartridge::from_file("roms/test/cpu/nestest/nestest.nes").unwrap();
|
|
let mut bus: NesBus = NesBus::new(cart);
|
|
let mut cpu = Cpu::new();
|
|
cpu.set_test_start(0xC000, 7);
|
|
loop {
|
|
println!("{}", cpu.trace(&mut bus));
|
|
cpu.step(&mut bus);
|
|
}
|
|
}
|
|
|
|
fn run_dbg(path: &str) {
|
|
let cart = Cartridge::from_file(path).unwrap();
|
|
let mut bus = NesBus::new(cart);
|
|
let mut cpu = Cpu::new();
|
|
cpu.reset(&mut bus);
|
|
let mut dumper = dump::Dumper::new();
|
|
let mut renderer = PpmRenderer::new(); // <-- add this
|
|
let frames: u32 = 1800;
|
|
|
|
for f in 0..frames {
|
|
while !bus.frame_done() {
|
|
let cycles = cpu.step(&mut bus);
|
|
bus.tick_ppu(cycles as u64);
|
|
if bus.take_nmi() { cpu.set_nmi(); }
|
|
}
|
|
renderer.present(bus.framebuffer(), 256, 240); // <-- and this
|
|
dumper.snapshot(f, &bus, &cpu);
|
|
bus.begin_frame();
|
|
}
|
|
println!("wrote debug_out/ + renders/, {frames} frames");
|
|
}
|
|
|
|
fn run_one_frame(bus: &mut NesBus, cpu: &mut Cpu) {
|
|
while !bus.frame_done() {
|
|
let cycles = cpu.step(bus);
|
|
bus.tick_ppu(cycles as u64);
|
|
if bus.take_nmi() { cpu.set_nmi(); }
|
|
}
|
|
bus.begin_frame();
|
|
}
|
|
|
|
struct GuiApp {
|
|
bus: NesBus,
|
|
cpu: Cpu,
|
|
renderer: WindowRenderer,
|
|
}
|
|
|
|
impl GuiApp {
|
|
fn new(cc: &eframe::CreationContext<'_>, cart: Cartridge) -> Self {
|
|
let mut bus = NesBus::new(cart);
|
|
let mut cpu = Cpu::new();
|
|
cpu.reset(&mut bus);
|
|
let renderer = WindowRenderer::new(cc, 256, 240);
|
|
Self { bus, cpu, renderer }
|
|
}
|
|
}
|
|
|
|
impl eframe::App for GuiApp {
|
|
// Non-UI work: emulation runs here, before painting.
|
|
fn logic(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
|
self.renderer.poll_input(self.bus.controller_mut()); // keyboard -> $4016/$4017
|
|
run_one_frame(&mut self.bus, &mut self.cpu); // one emulated frame
|
|
self.renderer.present(self.bus.framebuffer(), 256, 240);
|
|
ctx.request_repaint(); // keep it running at 60fps
|
|
}
|
|
|
|
// Painting: draw the framebuffer texture.
|
|
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
|
|
self.renderer.draw(ui);
|
|
}
|
|
}
|
|
|
|
fn run_gui(path: &str) {
|
|
let cart = Cartridge::from_file(path).unwrap();
|
|
let options = eframe::NativeOptions {
|
|
viewport: egui::ViewportBuilder::default()
|
|
.with_inner_size([1024.0, 960.0])
|
|
.with_title("rnes"),
|
|
..Default::default()
|
|
};
|
|
eframe::run_native(
|
|
"rnes",
|
|
options,
|
|
Box::new(move |cc| Ok(Box::new(GuiApp::new(cc, cart)))),
|
|
).expect("failed to run GUI");
|
|
} |