chore: remove Emulator struct

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-11-21 05:27:04 -04:00
parent 939c25ce1a
commit 2405fd027f
4 changed files with 80 additions and 129 deletions

View File

@ -19,7 +19,7 @@ pub struct Bus {
var_ram: VariableWorkRam, var_ram: VariableWorkRam,
timer: Timer, timer: Timer,
int: Interrupt, int: Interrupt,
apu: Apu, pub(crate) apu: Apu,
high_ram: HighRam, high_ram: HighRam,
serial: Serial, serial: Serial,
pub(crate) joyp: Joypad, pub(crate) joyp: Joypad,
@ -89,11 +89,6 @@ impl Bus {
self.oam_write_byte(dest_addr, byte); self.oam_write_byte(dest_addr, byte);
} }
} }
#[inline]
pub(crate) fn apu_mut(&mut self) -> &mut Apu {
&mut self.apu
}
} }
impl Bus { impl Bus {

View File

@ -5,7 +5,7 @@ use crate::Cycle;
use bitfield::bitfield; use bitfield::bitfield;
use std::fmt::{Display, Formatter, Result as FmtResult}; use std::fmt::{Display, Formatter, Result as FmtResult};
#[derive(Debug, Default)] #[derive(Debug)]
pub struct Cpu { pub struct Cpu {
pub(crate) bus: Bus, pub(crate) bus: Bus,
reg: Registers, reg: Registers,
@ -15,29 +15,13 @@ pub struct Cpu {
} }
impl Cpu { impl Cpu {
#[allow(dead_code)] pub(crate) fn new(rom: [u8; BOOT_SIZE]) -> Self {
pub(crate) fn without_boot() -> Self {
Self {
reg: Registers {
a: 0x01,
b: 0x00,
c: 0x13,
d: 0x00,
e: 0xD8,
h: 0x01,
l: 0x4D,
sp: 0xFFFE,
pc: 0x0100,
},
flags: 0xb0.into(),
..Default::default()
}
}
pub(crate) fn with_boot(rom: [u8; BOOT_SIZE]) -> Self {
Self { Self {
bus: Bus::with_boot(rom), bus: Bus::with_boot(rom),
..Default::default() reg: Default::default(),
flags: Default::default(),
ime: Default::default(),
state: Default::default(),
} }
} }
@ -69,6 +53,12 @@ impl Cpu {
} }
} }
impl Default for Cpu {
fn default() -> Self {
Cpu::new(*include_bytes!("../bin/bootix_dmg.bin"))
}
}
impl Cpu { impl Cpu {
/// Fetch an [Instruction] from the memory bus /// Fetch an [Instruction] from the memory bus
/// (4 cycles) /// (4 cycles)

View File

@ -15,125 +15,91 @@ pub const CYCLES_IN_FRAME: Cycle = 456 * 154; // 456 Cycles times 154 scanlines
pub(crate) const SM83_CLOCK_SPEED: u64 = 0x40_0000; // Hz which is 4.194304Mhz pub(crate) const SM83_CLOCK_SPEED: u64 = 0x40_0000; // Hz which is 4.194304Mhz
const DEFAULT_TITLE: &str = "DMG-01 Emulator"; const DEFAULT_TITLE: &str = "DMG-01 Emulator";
pub fn run_frame(emu: &mut Emulator, gamepad: &mut Gilrs, key: KeyboardInput) -> Cycle { pub fn run_frame(cpu: &mut Cpu, gamepad: &mut Gilrs, key: KeyboardInput) -> Cycle {
let mut elapsed = 0; let mut elapsed = 0;
if let Some(event) = gamepad.next_event() { if let Some(event) = gamepad.next_event() {
crate::joypad::handle_gamepad_input(&mut emu.cpu.bus.joyp, event); crate::joypad::handle_gamepad_input(&mut cpu.bus.joyp, event);
} }
crate::joypad::handle_keyboard_input(&mut emu.cpu.bus.joyp, key); crate::joypad::handle_keyboard_input(&mut cpu.bus.joyp, key);
while elapsed < CYCLES_IN_FRAME { while elapsed < CYCLES_IN_FRAME {
elapsed += emu.step(); elapsed += cpu.step();
} }
elapsed elapsed
} }
pub fn pixel_buf(emu: &Emulator) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] { pub fn pixel_buf(cpu: &Cpu) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] {
emu.cpu.bus.ppu.frame_buf.as_ref() cpu.bus.ppu.frame_buf.as_ref()
} }
pub struct Emulator { pub fn from_boot_rom<P: AsRef<Path>>(path: P) -> std::io::Result<Cpu> {
cpu: Cpu, Ok(Cpu::new(read_boot(path)?))
timestamp: Cycle,
} }
impl Default for Emulator { pub fn read_game_rom<P: AsRef<Path>>(cpu: &mut Cpu, path: P) -> std::io::Result<()> {
fn default() -> Self { Ok(cpu.bus.load_cart(std::fs::read(path.as_ref())?))
Self::new()
}
} }
impl Emulator { pub fn set_audio_producer(cpu: &mut Cpu, producer: SampleProducer<f32>) {
pub fn new() -> Self { cpu.bus.apu.attach_producer(producer);
Self { }
cpu: Cpu::with_boot(*include_bytes!("../bin/bootix_dmg.bin")),
timestamp: Default::default(), pub fn rom_title(cpu: &Cpu) -> &str {
cpu.bus.cart_title().unwrap_or(DEFAULT_TITLE)
}
pub fn write_save(cpu: &Cpu) -> std::io::Result<()> {
if let Some(ext_ram) = cpu.bus.cart.as_ref().map(|c| c.ext_ram()).flatten() {
if let Some(title) = cpu.bus.cart_title() {
let mut save_path = data_path().unwrap_or_else(|| PathBuf::from("."));
save_path.push(title);
save_path.set_extension("sav");
let mut file = File::create(save_path)?;
file.write_all(ext_ram)?;
} }
} }
pub fn from_boot_rom<P: AsRef<Path>>(path: P) -> std::io::Result<Self> { Ok(())
Ok(Self { }
cpu: Cpu::with_boot(Self::read_boot(path)?),
timestamp: Default::default(),
})
}
fn read_boot<P: AsRef<Path>>(path: P) -> std::io::Result<[u8; BOOT_SIZE]> { pub fn load_save(cpu: &mut Cpu) -> std::io::Result<()> {
let mut buf = [0; BOOT_SIZE]; if let Some(cart) = &mut cpu.bus.cart {
let mut file = File::open(path.as_ref())?; if let Some(title) = cart.title() {
let mut save_path = data_path().unwrap_or_else(|| PathBuf::from("."));
save_path.push(title);
save_path.set_extension("sav");
file.read_exact(&mut buf)?; if let Ok(mut file) = File::open(&save_path) {
Ok(buf) tracing::info!("Load {:?}", save_path);
}
fn step(&mut self) -> Cycle { let mut memory = Vec::new();
let cycles = self.cpu.step(); file.read_to_end(&mut memory)?;
self.timestamp += cycles; cart.write_ext_ram(memory);
cycles
}
pub fn read_game_rom<P: AsRef<Path>>(&mut self, path: P) -> std::io::Result<()> {
self.load_rom(std::fs::read(path.as_ref())?);
Ok(())
}
fn load_rom(&mut self, rom: Vec<u8>) {
self.cpu.bus.load_cart(rom);
}
pub fn set_prod(&mut self, prod: SampleProducer<f32>) {
self.cpu.bus.apu_mut().attach_producer(prod)
}
pub fn title(&self) -> &str {
self.cpu.bus.cart_title().unwrap_or(DEFAULT_TITLE)
}
pub fn try_write_sav(&self) -> std::io::Result<()> {
if let Some(ext_ram) = self.cpu.bus.cart.as_ref().map(|c| c.ext_ram()).flatten() {
if let Some(title) = self.cpu.bus.cart_title() {
let mut save_path = Self::data_path().unwrap_or_else(|| PathBuf::from("."));
save_path.push(title);
save_path.set_extension("sav");
let mut file = File::create(save_path)?;
file.write_all(ext_ram)?;
} }
} }
Ok(())
} }
pub fn try_load_sav(&mut self) -> std::io::Result<()> { Ok(())
if let Some(cart) = &mut self.cpu.bus.cart { }
if let Some(title) = cart.title() {
let mut save_path = Self::data_path().unwrap_or_else(|| PathBuf::from("."));
save_path.push(title);
save_path.set_extension("sav");
if let Ok(mut file) = File::open(&save_path) { fn read_boot<P: AsRef<Path>>(path: P) -> std::io::Result<[u8; BOOT_SIZE]> {
tracing::info!("Load {:?}", save_path); let mut buf = [0; BOOT_SIZE];
let mut file = File::open(path.as_ref())?;
let mut memory = Vec::new(); file.read_exact(&mut buf)?;
file.read_to_end(&mut memory)?; Ok(buf)
cart.write_ext_ram(memory); }
}
} fn data_path() -> Option<PathBuf> {
} match directories_next::ProjectDirs::from("dev", "musuka", crate_name!()) {
Some(dirs) => {
Ok(()) let data_local = dirs.data_local_dir();
} std::fs::create_dir_all(data_local).ok()?;
Some(data_local.to_path_buf())
fn data_path() -> Option<PathBuf> {
match directories_next::ProjectDirs::from("dev", "musuka", crate_name!()) {
Some(dirs) => {
let data_local = dirs.data_local_dir();
std::fs::create_dir_all(data_local).ok()?;
Some(data_local.to_path_buf())
}
None => None,
} }
None => None,
} }
} }

View File

@ -2,7 +2,7 @@ use std::time::Instant;
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
use egui_wgpu_backend::RenderPass; use egui_wgpu_backend::RenderPass;
use gb::emu::Emulator; use gb::emu;
use gb::gui::GuiState; use gb::gui::GuiState;
use gilrs::Gilrs; use gilrs::Gilrs;
use rodio::{OutputStream, Sink}; use rodio::{OutputStream, Sink};
@ -63,33 +63,33 @@ fn main() {
// We interrupt your boiler plate to initialize the emulator so that // We interrupt your boiler plate to initialize the emulator so that
// we can copy it's empty pixel buffer to the GPU // we can copy it's empty pixel buffer to the GPU
let mut emu = match m.value_of("boot") { let mut cpu = match m.value_of("boot") {
Some(path) => { Some(path) => {
tracing::info!("User-provided boot ROM"); tracing::info!("User-provided boot ROM");
Emulator::from_boot_rom(path).expect("initialize emulator with custom boot rom") emu::from_boot_rom(path).expect("initialize emulator with custom boot rom")
} }
None => { None => {
tracing::info!("Built-in boot ROM"); tracing::info!("Built-in boot ROM");
Emulator::new() Default::default()
} }
}; };
// Set up the WGPU (and then EGUI) texture we'll be working with. // Set up the WGPU (and then EGUI) texture we'll be working with.
let texture_size = gb::gui::texture_size(); let texture_size = gb::gui::texture_size();
let texture = gb::gui::create_texture(&device, texture_size); let texture = gb::gui::create_texture(&device, texture_size);
gb::gui::write_to_texture(&queue, &texture, gb::emu::pixel_buf(&emu), texture_size); gb::gui::write_to_texture(&queue, &texture, gb::emu::pixel_buf(&cpu), texture_size);
let texture_id = gb::gui::expose_texture_to_egui(&mut render_pass, &device, &texture); let texture_id = gb::gui::expose_texture_to_egui(&mut render_pass, &device, &texture);
// Load ROM if filepath was provided // Load ROM if filepath was provided
if let Some(path) = m.value_of("rom") { if let Some(path) = m.value_of("rom") {
tracing::info!("User-provided cartridge ROM"); tracing::info!("User-provided cartridge ROM");
emu.read_game_rom(path).expect("read game rom from path"); emu::read_game_rom(&mut cpu, path).expect("read game rom from path");
} }
// Load Save File if it exists // Load Save File if it exists
// FIXME: Shouldn't the API be better than this? // FIXME: Shouldn't the API be better than this?
emu.try_load_sav().expect("Load save if exists"); emu::load_save(&mut cpu).expect("Load save if exists");
let rom_title = emu.title().to_string(); let rom_title = emu::rom_title(&cpu).to_string();
tracing::info!("Initialize Gamepad"); tracing::info!("Initialize Gamepad");
let mut gamepad = Gilrs::new().expect("Initialize Controller Support"); let mut gamepad = Gilrs::new().expect("Initialize Controller Support");
@ -106,7 +106,7 @@ fn main() {
s s
}; };
emu.set_prod(prod); emu::set_audio_producer(&mut cpu, prod);
tracing::info!("Spawn Audio Thread"); tracing::info!("Spawn Audio Thread");
std::thread::spawn(move || { std::thread::spawn(move || {
@ -130,14 +130,14 @@ fn main() {
*control_flow = ControlFlow::Exit; *control_flow = ControlFlow::Exit;
} }
gb::emu::run_frame(&mut emu, &mut gamepad, last_key); gb::emu::run_frame(&mut cpu, &mut gamepad, last_key);
window.request_redraw(); window.request_redraw();
} }
Event::RedrawRequested(..) => { Event::RedrawRequested(..) => {
platform.update_time(start_time.elapsed().as_secs_f64()); platform.update_time(start_time.elapsed().as_secs_f64());
let data = gb::emu::pixel_buf(&emu); let data = gb::emu::pixel_buf(&cpu);
gb::gui::write_to_texture(&queue, &texture, data, texture_size); gb::gui::write_to_texture(&queue, &texture, data, texture_size);
let output_frame = match surface.get_current_texture() { let output_frame = match surface.get_current_texture() {