Major refactor
- CPU is now it's own module - Memory object is now shared to support mapper chips - ROM is now stored as `Arc<[u8]>` to support mapper chips
This commit is contained in:
100
src/main.rs
100
src/main.rs
@@ -1,24 +1,26 @@
|
||||
use std::{collections::HashMap, fmt, time::Duration};
|
||||
|
||||
use iced::{
|
||||
Color, Element, Font,
|
||||
Color, Element,
|
||||
Length::{Fill, Shrink},
|
||||
Point, Rectangle, Renderer, Size, Subscription, Task, Theme, mouse,
|
||||
Point, Renderer, Size, Subscription, Task, Theme,
|
||||
advanced::graphics::compositor::Display,
|
||||
mouse,
|
||||
widget::{
|
||||
Action, Canvas, button,
|
||||
Canvas,
|
||||
canvas::{Frame, Program},
|
||||
column, container, mouse_area, row, text,
|
||||
column, container, row,
|
||||
},
|
||||
window::{self, Id, Settings},
|
||||
};
|
||||
use nes_emu::{
|
||||
NES, PPU,
|
||||
NES,
|
||||
debugger::{DbgImage, DebuggerMessage, DebuggerState, dbg_image},
|
||||
header_menu::header_menu,
|
||||
hex_view::{HexEvent, HexView},
|
||||
resize_watcher::resize_watcher,
|
||||
};
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::{io::AsyncWriteExt, runtime::Runtime};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "even_odd.nes");
|
||||
@@ -26,8 +28,9 @@ use tracing_subscriber::EnvFilter;
|
||||
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "ppu_fill_red.nes");
|
||||
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "ppu_fill_name_table.nes");
|
||||
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "int_nmi_exit_timing.nes");
|
||||
// const ROM_FILE: &str = "./Super Mario Bros. (World).nes";
|
||||
const ROM_FILE: &str = "./cpu_timing_test.nes";
|
||||
const ROM_FILE: &str = "./Super Mario Bros. (World).nes";
|
||||
// const ROM_FILE: &str = "./cpu_timing_test.nes";
|
||||
// const ROM_FILE: &str = "../nes-test-roms/instr_test-v5/official_only.nes";
|
||||
|
||||
extern crate nes_emu;
|
||||
|
||||
@@ -44,12 +47,18 @@ fn main() -> Result<(), iced::Error> {
|
||||
.run()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum MemoryTy {
|
||||
Cpu,
|
||||
PPU,
|
||||
}
|
||||
|
||||
impl fmt::Display for MemoryTy {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Export {self:?} Memory")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
enum WindowType {
|
||||
Main,
|
||||
@@ -67,6 +76,7 @@ enum HeaderButton {
|
||||
// OpenTileViewer,
|
||||
// OpenDebugger,
|
||||
Open(WindowType),
|
||||
OpenRom,
|
||||
Reset,
|
||||
PowerCycle,
|
||||
}
|
||||
@@ -83,6 +93,7 @@ impl fmt::Display for HeaderButton {
|
||||
Self::Open(WindowType::Main) => write!(f, "Create new Main window"),
|
||||
Self::Reset => write!(f, "Reset"),
|
||||
Self::PowerCycle => write!(f, "Power Cycle"),
|
||||
Self::OpenRom => write!(f, "Open ROM file"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,6 +107,7 @@ struct Emulator {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
OpenRom(NES),
|
||||
Tick(usize),
|
||||
Frame,
|
||||
DMA,
|
||||
@@ -107,6 +119,7 @@ enum Message {
|
||||
Hex(Id, HexEvent),
|
||||
Debugger(DebuggerMessage),
|
||||
SetSize(window::Id, Size),
|
||||
Export(MemoryTy),
|
||||
}
|
||||
|
||||
impl Emulator {
|
||||
@@ -180,6 +193,22 @@ impl Emulator {
|
||||
Message::Header(HeaderButton::Open(w)) => {
|
||||
return self.open(w);
|
||||
}
|
||||
Message::Header(HeaderButton::OpenRom) => {
|
||||
return Task::future(
|
||||
rfd::AsyncFileDialog::new()
|
||||
.set_directory(".")
|
||||
.add_filter("NES", &["nes"])
|
||||
.set_title("Open NES Rom file")
|
||||
.pick_file(),
|
||||
)
|
||||
.and_then(|p| {
|
||||
Task::future(async move {
|
||||
// println!("Opening: {}", p.path().display());
|
||||
NES::async_load_nes_file(p.path()).await.ok()
|
||||
})
|
||||
})
|
||||
.and_then(|n| Task::done(Message::OpenRom(n)));
|
||||
}
|
||||
Message::Hex(id, val) => {
|
||||
if let Some(WindowType::Memory(_, view)) = self.windows.get_mut(&id) {
|
||||
return view.update(val).map(move |e| Message::Hex(id, e));
|
||||
@@ -203,6 +232,33 @@ impl Emulator {
|
||||
})
|
||||
.then(move |_| iced::window::resize(id, size));
|
||||
}
|
||||
Message::OpenRom(nes) => {
|
||||
self.nes = nes;
|
||||
self.nes.power_cycle();
|
||||
}
|
||||
Message::Export(ty) => {
|
||||
let raw: Vec<_> = match ty {
|
||||
MemoryTy::Cpu => (0..=0xFFFF).map(|i| self.nes.mem().peek_cpu(i).unwrap_or(0)).collect(),
|
||||
MemoryTy::PPU => (0..=0xFFFF).map(|i| self.nes.mem().peek_ppu(i).unwrap_or(0)).collect(),
|
||||
};
|
||||
return Task::future(async move {
|
||||
if let Some(file) = rfd::AsyncFileDialog::new()
|
||||
.set_directory(".")
|
||||
.set_file_name("output.dmp")
|
||||
.set_title("Save memory dump")
|
||||
.save_file()
|
||||
.await
|
||||
{
|
||||
tokio::fs::File::create(file.path())
|
||||
.await
|
||||
.expect("Failed to save file")
|
||||
.write_all(&raw)
|
||||
.await
|
||||
.expect("Failed to write dump");
|
||||
}
|
||||
})
|
||||
.discard();
|
||||
}
|
||||
}
|
||||
// self.image.0.clone_from(self.nes.image());
|
||||
Task::none()
|
||||
@@ -234,20 +290,26 @@ impl Emulator {
|
||||
Some(WindowType::Memory(ty, view)) => {
|
||||
let hex = match ty {
|
||||
MemoryTy::Cpu => view
|
||||
.render(self.nes.cpu_mem())
|
||||
.render(self.nes.mem(), false)
|
||||
.map(move |e| Message::Hex(win, e)),
|
||||
MemoryTy::PPU => view
|
||||
.render(self.nes.ppu().mem())
|
||||
.render(self.nes.mem(), true)
|
||||
.map(move |e| Message::Hex(win, e)),
|
||||
};
|
||||
let content = column![hex].width(Fill).height(Fill);
|
||||
let content = column![row![header_menu("Export", [*ty], Message::Export)], hex]
|
||||
.width(Fill)
|
||||
.height(Fill);
|
||||
container(content).width(Fill).height(Fill).into()
|
||||
}
|
||||
Some(WindowType::TileMap) => dbg_image(DbgImage::NameTable(self.nes.ppu())).into(),
|
||||
Some(WindowType::TileViewer) => {
|
||||
dbg_image(DbgImage::PatternTable(self.nes.ppu())).into()
|
||||
Some(WindowType::TileMap) => {
|
||||
dbg_image(DbgImage::NameTable(self.nes.mem(), self.nes.ppu())).into()
|
||||
}
|
||||
Some(WindowType::TileViewer) => {
|
||||
dbg_image(DbgImage::PatternTable(self.nes.mem(), self.nes.ppu())).into()
|
||||
}
|
||||
Some(WindowType::Palette) => {
|
||||
dbg_image(DbgImage::Palette(self.nes.mem(), self.nes.ppu())).into()
|
||||
}
|
||||
Some(WindowType::Palette) => dbg_image(DbgImage::Palette(self.nes.ppu())).into(),
|
||||
Some(WindowType::Debugger) => {
|
||||
container(self.debugger.view(&self.nes).map(Message::Debugger))
|
||||
.width(Fill)
|
||||
@@ -263,7 +325,11 @@ impl Emulator {
|
||||
row![
|
||||
header_menu(
|
||||
"Console",
|
||||
[HeaderButton::Reset, HeaderButton::PowerCycle,],
|
||||
[
|
||||
HeaderButton::OpenRom,
|
||||
HeaderButton::Reset,
|
||||
HeaderButton::PowerCycle,
|
||||
],
|
||||
Message::Header
|
||||
),
|
||||
header_menu(
|
||||
|
||||
Reference in New Issue
Block a user