Compare commits
No commits in common. "080c1e7518fcb6282a21e1d6feebd645061488e5" and "e19a5406505b13a776c1643ea7ad26012fbc0947" have entirely different histories.
080c1e7518
...
e19a540650
18
src/bus.rs
18
src/bus.rs
|
@ -123,14 +123,14 @@ impl Bus {
|
||||||
|
|
||||||
match self.cart.as_ref() {
|
match self.cart.as_ref() {
|
||||||
Some(cart) => cart.read_byte(addr),
|
Some(cart) => cart.read_byte(addr),
|
||||||
None => 0xFF,
|
None => panic!("Tried to read from a non-existent cartridge"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM
|
0x8000..=0x9FFF => self.ppu.read_byte(addr), // 8KB Video RAM
|
||||||
0xA000..=0xBFFF => match self.cart.as_ref() {
|
0xA000..=0xBFFF => match self.cart.as_ref() {
|
||||||
// 8KB External RAM
|
// 8KB External RAM
|
||||||
Some(cart) => cart.read_byte(addr),
|
Some(cart) => cart.read_byte(addr),
|
||||||
None => 0xFF,
|
None => panic!("Tried to read from a non-existent cartridge"),
|
||||||
},
|
},
|
||||||
0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
|
0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
|
||||||
0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N
|
0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N
|
||||||
|
@ -176,7 +176,7 @@ impl BusIo for Bus {
|
||||||
|
|
||||||
match self.cart.as_ref() {
|
match self.cart.as_ref() {
|
||||||
Some(cart) => cart.read_byte(addr),
|
Some(cart) => cart.read_byte(addr),
|
||||||
None => 0xFF,
|
None => panic!("Tried to read from a non-existent cartridge"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x8000..=0x9FFF => {
|
0x8000..=0x9FFF => {
|
||||||
|
@ -189,7 +189,7 @@ impl BusIo for Bus {
|
||||||
0xA000..=0xBFFF => match self.cart.as_ref() {
|
0xA000..=0xBFFF => match self.cart.as_ref() {
|
||||||
// 8KB External RAM
|
// 8KB External RAM
|
||||||
Some(cart) => cart.read_byte(addr),
|
Some(cart) => cart.read_byte(addr),
|
||||||
None => 0xFF,
|
None => panic!("Tried to read from a non-existent cartridge"),
|
||||||
},
|
},
|
||||||
0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
|
0xC000..=0xCFFF => self.work_ram.read_byte(addr), // 4KB Work RAM Bank 0
|
||||||
0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N
|
0xD000..=0xDFFF => self.var_ram.read_byte(addr), // 4KB Work RAM Bank 1 -> N
|
||||||
|
@ -279,8 +279,9 @@ impl BusIo for Bus {
|
||||||
0x0000..=0x7FFF => {
|
0x0000..=0x7FFF => {
|
||||||
// 16KB ROM bank 00 (ends at 0x3FFF)
|
// 16KB ROM bank 00 (ends at 0x3FFF)
|
||||||
// and 16KB ROM Bank 01 -> NN (switchable via MB)
|
// and 16KB ROM Bank 01 -> NN (switchable via MB)
|
||||||
if let Some(cart) = self.cart.as_mut() {
|
match self.cart.as_mut() {
|
||||||
cart.write_byte(addr, byte);
|
Some(cart) => cart.write_byte(addr, byte),
|
||||||
|
None => panic!("Tried to write into non-existent cartridge"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0x8000..=0x9FFF => {
|
0x8000..=0x9FFF => {
|
||||||
|
@ -292,8 +293,9 @@ impl BusIo for Bus {
|
||||||
}
|
}
|
||||||
0xA000..=0xBFFF => {
|
0xA000..=0xBFFF => {
|
||||||
// 8KB External RAM
|
// 8KB External RAM
|
||||||
if let Some(cart) = self.cart.as_mut() {
|
match self.cart.as_mut() {
|
||||||
cart.write_byte(addr, byte);
|
Some(cart) => cart.write_byte(addr, byte),
|
||||||
|
None => panic!("Tried to write into non-existent cartridge"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
0xC000..=0xCFFF => self.work_ram.write_byte(addr, byte), // 4KB Work RAM Bank 0
|
0xC000..=0xCFFF => self.work_ram.write_byte(addr, byte), // 4KB Work RAM Bank 0
|
||||||
|
|
104
src/emu.rs
104
src/emu.rs
|
@ -1,5 +1,4 @@
|
||||||
use crate::apu::gen::SampleProducer;
|
use crate::apu::gen::SampleProducer;
|
||||||
use crate::bus::BOOT_SIZE;
|
|
||||||
use crate::cpu::Cpu;
|
use crate::cpu::Cpu;
|
||||||
use crate::joypad::{self, Joypad};
|
use crate::joypad::{self, Joypad};
|
||||||
use crate::{Cycle, GB_HEIGHT, GB_WIDTH};
|
use crate::{Cycle, GB_HEIGHT, GB_WIDTH};
|
||||||
|
@ -7,7 +6,7 @@ use clap::crate_name;
|
||||||
use gilrs::Gilrs;
|
use gilrs::Gilrs;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use winit_input_helper::WinitInputHelper;
|
use winit_input_helper::WinitInputHelper;
|
||||||
|
|
||||||
|
@ -40,47 +39,21 @@ pub struct Emulator {
|
||||||
timestamp: Cycle,
|
timestamp: Cycle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Emulator {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Emulator {
|
impl Emulator {
|
||||||
pub fn new() -> Self {
|
fn new(cpu: Cpu) -> Self {
|
||||||
Self {
|
Self {
|
||||||
cpu: Cpu::with_boot(*include_bytes!("../bin/bootix_dmg.bin")),
|
cpu,
|
||||||
timestamp: Default::default(),
|
timestamp: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_boot_rom<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
|
|
||||||
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]> {
|
|
||||||
let mut buf = [0; BOOT_SIZE];
|
|
||||||
let mut file = File::open(path.as_ref())?;
|
|
||||||
|
|
||||||
file.read_exact(&mut buf)?;
|
|
||||||
Ok(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn step(&mut self) -> Cycle {
|
fn step(&mut self) -> Cycle {
|
||||||
let cycles = self.cpu.step();
|
let cycles = self.cpu.step();
|
||||||
self.timestamp += cycles;
|
self.timestamp += cycles;
|
||||||
cycles
|
cycles
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_game_rom<P: AsRef<Path>>(&mut self, path: P) -> std::io::Result<()> {
|
fn load_cart(&mut self, rom: Vec<u8>) {
|
||||||
self.load_rom(std::fs::read(path.as_ref())?);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_rom(&mut self, rom: Vec<u8>) {
|
|
||||||
self.cpu.bus_mut().load_cart(rom);
|
self.cpu.bus_mut().load_cart(rom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,11 +92,10 @@ impl Emulator {
|
||||||
save_path.push(title);
|
save_path.push(title);
|
||||||
save_path.set_extension("sav");
|
save_path.set_extension("sav");
|
||||||
|
|
||||||
if let Ok(mut file) = File::open(&save_path) {
|
if let Ok(mut file) = File::open(save_path) {
|
||||||
tracing::info!("Load {:?}", save_path);
|
|
||||||
|
|
||||||
let mut memory = Vec::new();
|
let mut memory = Vec::new();
|
||||||
file.read_to_end(&mut memory)?;
|
file.read_to_end(&mut memory)?;
|
||||||
|
|
||||||
cart.write_ext_ram(memory);
|
cart.write_ext_ram(memory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,3 +115,67 @@ impl Emulator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod build {
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Result};
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
use crate::bus::BOOT_SIZE;
|
||||||
|
use crate::cpu::Cpu;
|
||||||
|
|
||||||
|
use super::Emulator;
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct EmulatorBuilder {
|
||||||
|
boot: Option<[u8; BOOT_SIZE]>,
|
||||||
|
cart: Option<Vec<u8>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EmulatorBuilder {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_boot<P: AsRef<Path>>(mut self, path: P) -> Result<Self> {
|
||||||
|
let mut file = File::open(path.as_ref())?;
|
||||||
|
|
||||||
|
let mut buf = [0x00; BOOT_SIZE];
|
||||||
|
file.read_exact(&mut buf)?;
|
||||||
|
|
||||||
|
self.boot = Some(buf);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_cart<P: AsRef<Path>>(mut self, path: P) -> Result<Self> {
|
||||||
|
let mut file = File::open(path.as_ref())?;
|
||||||
|
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
file.read_to_end(&mut buf)?;
|
||||||
|
|
||||||
|
self.cart = Some(buf);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(mut self) -> Emulator {
|
||||||
|
let mut emu = Emulator::new(match self.boot {
|
||||||
|
Some(rom) => {
|
||||||
|
info!("User-provided Boot ROM");
|
||||||
|
Cpu::with_boot(rom)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
info!("Built-in Boot ROM");
|
||||||
|
Cpu::with_boot(*include_bytes!("../bin/bootix_dmg.bin"))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(rom) = self.cart.take() {
|
||||||
|
emu.load_cart(rom)
|
||||||
|
}
|
||||||
|
|
||||||
|
emu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -2,7 +2,8 @@ use std::convert::TryInto;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
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 gb::emu::{Emulator, CYCLES_IN_FRAME};
|
use gb::emu::build::EmulatorBuilder;
|
||||||
|
use gb::emu::CYCLES_IN_FRAME;
|
||||||
use gb::{Cycle, GB_HEIGHT, GB_WIDTH};
|
use gb::{Cycle, GB_HEIGHT, GB_WIDTH};
|
||||||
use gilrs::Gilrs;
|
use gilrs::Gilrs;
|
||||||
use pixels::{PixelsBuilder, SurfaceTexture};
|
use pixels::{PixelsBuilder, SurfaceTexture};
|
||||||
|
@ -29,6 +30,7 @@ fn main() -> Result<()> {
|
||||||
Arg::with_name("rom")
|
Arg::with_name("rom")
|
||||||
.value_name("ROM_FILE")
|
.value_name("ROM_FILE")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
|
.required(true)
|
||||||
.index(1)
|
.index(1)
|
||||||
.help("Path to the Game ROM"),
|
.help("Path to the Game ROM"),
|
||||||
)
|
)
|
||||||
|
@ -51,23 +53,17 @@ fn main() -> Result<()> {
|
||||||
.with_env_filter(EnvFilter::from_default_env())
|
.with_env_filter(EnvFilter::from_default_env())
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
let mut emu = match m.value_of("boot") {
|
let mut emu_build =
|
||||||
Some(path) => {
|
EmulatorBuilder::new().with_cart(m.value_of("rom").expect("ROM path provided"))?;
|
||||||
tracing::info!("User-provided boot ROM");
|
|
||||||
Emulator::from_boot_rom(path)?
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
tracing::info!("Built-in boot ROM");
|
|
||||||
Emulator::new()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(path) = m.value_of("rom") {
|
if let Some(path) = m.value_of("boot") {
|
||||||
tracing::info!("User-provided cartridge ROM");
|
emu_build = emu_build.with_boot(path)?;
|
||||||
emu.read_game_rom(path)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut emu = emu_build.finish();
|
||||||
|
|
||||||
// Load Save file if it exists
|
// Load Save file if it exists
|
||||||
|
info!("Attempt to load .sav");
|
||||||
emu.try_load_sav().expect("Load save if exists");
|
emu.try_load_sav().expect("Load save if exists");
|
||||||
let rom_title = emu.title();
|
let rom_title = emu.title();
|
||||||
|
|
||||||
|
|
|
@ -369,7 +369,7 @@ impl Ppu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToFifoA => {
|
ToFifoA => {
|
||||||
if self.fetch.send_to_fifo(&mut self.fifo).is_ok() {
|
if let Ok(_) = self.fetch.send_to_fifo(&mut self.fifo) {
|
||||||
self.fetch.x_pos += 1;
|
self.fetch.x_pos += 1;
|
||||||
self.fetch.back.state = ToFifoB;
|
self.fetch.back.state = ToFifoB;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue