Compare commits
	
		
			1 Commits
		
	
	
		
			3ab512d663
			...
			refactor
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9baa15050e | 
							
								
								
									
										43
									
								
								src/bus.rs
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/bus.rs
									
									
									
									
									
								
							@@ -7,14 +7,13 @@ use crate::ppu::{Ppu, PpuMode};
 | 
				
			|||||||
use crate::serial::Serial;
 | 
					use crate::serial::Serial;
 | 
				
			||||||
use crate::timer::Timer;
 | 
					use crate::timer::Timer;
 | 
				
			||||||
use crate::work_ram::{VariableWorkRam, WorkRam};
 | 
					use crate::work_ram::{VariableWorkRam, WorkRam};
 | 
				
			||||||
use std::{fs::File, io::Read};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const BOOT_ROM_SIZE: usize = 0x100;
 | 
					pub(crate) const BOOT_SIZE: usize = 0x100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub struct Bus {
 | 
					pub struct Bus {
 | 
				
			||||||
    boot: Option<[u8; BOOT_ROM_SIZE]>, // Boot ROM is 256b long
 | 
					    boot: Option<[u8; BOOT_SIZE]>, // Boot ROM is 256b long
 | 
				
			||||||
    cartridge: Option<Cartridge>,
 | 
					    cart: Option<Cartridge>,
 | 
				
			||||||
    pub(crate) ppu: Ppu,
 | 
					    pub(crate) ppu: Ppu,
 | 
				
			||||||
    work_ram: WorkRam,
 | 
					    work_ram: WorkRam,
 | 
				
			||||||
    var_ram: VariableWorkRam,
 | 
					    var_ram: VariableWorkRam,
 | 
				
			||||||
@@ -30,7 +29,7 @@ impl Default for Bus {
 | 
				
			|||||||
    fn default() -> Self {
 | 
					    fn default() -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            boot: None,
 | 
					            boot: None,
 | 
				
			||||||
            cartridge: None,
 | 
					            cart: None,
 | 
				
			||||||
            ppu: Default::default(),
 | 
					            ppu: Default::default(),
 | 
				
			||||||
            work_ram: Default::default(),
 | 
					            work_ram: Default::default(),
 | 
				
			||||||
            var_ram: Default::default(),
 | 
					            var_ram: Default::default(),
 | 
				
			||||||
@@ -45,25 +44,19 @@ impl Default for Bus {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Bus {
 | 
					impl Bus {
 | 
				
			||||||
    pub(crate) fn with_boot(path: &str) -> anyhow::Result<Self> {
 | 
					    pub(crate) fn with_boot(rom: [u8; 256]) -> Self {
 | 
				
			||||||
        let mut file = File::open(path)?;
 | 
					        Self {
 | 
				
			||||||
        let mut boot_rom = [0u8; 256];
 | 
					            boot: Some(rom),
 | 
				
			||||||
 | 
					 | 
				
			||||||
        file.read_exact(&mut boot_rom)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(Self {
 | 
					 | 
				
			||||||
            boot: Some(boot_rom),
 | 
					 | 
				
			||||||
            ..Default::default()
 | 
					            ..Default::default()
 | 
				
			||||||
        })
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn load_cartridge(&mut self, path: &str) -> std::io::Result<()> {
 | 
					    pub(crate) fn load_cart(&mut self, rom: Vec<u8>) {
 | 
				
			||||||
        self.cartridge = Some(Cartridge::new(path)?);
 | 
					        self.cart = Some(Cartridge::new(rom));
 | 
				
			||||||
        Ok(())
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn rom_title(&self) -> Option<&str> {
 | 
					    pub(crate) fn cart_title(&self) -> Option<&str> {
 | 
				
			||||||
        self.cartridge.as_ref()?.title()
 | 
					        self.cart.as_ref()?.title()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[allow(dead_code)]
 | 
					    #[allow(dead_code)]
 | 
				
			||||||
@@ -104,13 +97,13 @@ impl Bus {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                match self.cartridge.as_ref() {
 | 
					                match self.cart.as_ref() {
 | 
				
			||||||
                    Some(cart) => cart.read_byte(addr),
 | 
					                    Some(cart) => cart.read_byte(addr),
 | 
				
			||||||
                    None => panic!("Tried to read from a non-existent cartridge"),
 | 
					                    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.cartridge.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 => panic!("Tried to read from a non-existent cartridge"),
 | 
					                None => panic!("Tried to read from a non-existent cartridge"),
 | 
				
			||||||
@@ -157,7 +150,7 @@ impl BusIo for Bus {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                match self.cartridge.as_ref() {
 | 
					                match self.cart.as_ref() {
 | 
				
			||||||
                    Some(cart) => cart.read_byte(addr),
 | 
					                    Some(cart) => cart.read_byte(addr),
 | 
				
			||||||
                    None => panic!("Tried to read from a non-existent cartridge"),
 | 
					                    None => panic!("Tried to read from a non-existent cartridge"),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -169,7 +162,7 @@ impl BusIo for Bus {
 | 
				
			|||||||
                    _ => self.ppu.read_byte(addr),
 | 
					                    _ => self.ppu.read_byte(addr),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            0xA000..=0xBFFF => match self.cartridge.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 => panic!("Tried to read from a non-existent cartridge"),
 | 
					                None => panic!("Tried to read from a non-existent cartridge"),
 | 
				
			||||||
@@ -262,7 +255,7 @@ 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)
 | 
				
			||||||
                match self.cartridge.as_mut() {
 | 
					                match self.cart.as_mut() {
 | 
				
			||||||
                    Some(cart) => cart.write_byte(addr, byte),
 | 
					                    Some(cart) => cart.write_byte(addr, byte),
 | 
				
			||||||
                    None => panic!("Tried to write into non-existent cartridge"),
 | 
					                    None => panic!("Tried to write into non-existent cartridge"),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -276,7 +269,7 @@ impl BusIo for Bus {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
            0xA000..=0xBFFF => {
 | 
					            0xA000..=0xBFFF => {
 | 
				
			||||||
                // 8KB External RAM
 | 
					                // 8KB External RAM
 | 
				
			||||||
                match self.cartridge.as_mut() {
 | 
					                match self.cart.as_mut() {
 | 
				
			||||||
                    Some(cart) => cart.write_byte(addr, byte),
 | 
					                    Some(cart) => cart.write_byte(addr, byte),
 | 
				
			||||||
                    None => panic!("Tried to write into non-existent cartridge"),
 | 
					                    None => panic!("Tried to write into non-existent cartridge"),
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,3 @@
 | 
				
			|||||||
use std::fs::File;
 | 
					 | 
				
			||||||
use std::io::{self, Read};
 | 
					 | 
				
			||||||
use std::path::Path;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
use crate::bus::BusIo;
 | 
					use crate::bus::BusIo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const RAM_SIZE_ADDRESS: usize = 0x0149;
 | 
					const RAM_SIZE_ADDRESS: usize = 0x0149;
 | 
				
			||||||
@@ -17,19 +13,15 @@ pub(crate) struct Cartridge {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Cartridge {
 | 
					impl Cartridge {
 | 
				
			||||||
    pub(crate) fn new<P: AsRef<Path> + ?Sized>(path: &P) -> io::Result<Self> {
 | 
					    pub(crate) fn new(memory: Vec<u8>) -> Self {
 | 
				
			||||||
        let mut memory = vec![];
 | 
					 | 
				
			||||||
        let mut rom = File::open(path)?;
 | 
					 | 
				
			||||||
        rom.read_to_end(&mut memory)?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let title = Self::find_title(&memory);
 | 
					        let title = Self::find_title(&memory);
 | 
				
			||||||
        eprintln!("Cartridge Title: {:?}", title);
 | 
					        eprintln!("Cartridge Title: {:?}", title);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Ok(Self {
 | 
					        Self {
 | 
				
			||||||
            mbc: Self::detect_mbc(&memory),
 | 
					            mbc: Self::detect_mbc(&memory),
 | 
				
			||||||
            title,
 | 
					            title,
 | 
				
			||||||
            memory,
 | 
					            memory,
 | 
				
			||||||
        })
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> {
 | 
					    fn detect_mbc(memory: &[u8]) -> Box<dyn MBCIo> {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										35
									
								
								src/cpu.rs
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								src/cpu.rs
									
									
									
									
									
								
							@@ -1,9 +1,6 @@
 | 
				
			|||||||
use crate::apu::Apu;
 | 
					use crate::bus::{Bus, BusIo, BOOT_SIZE};
 | 
				
			||||||
use crate::bus::{Bus, BusIo};
 | 
					 | 
				
			||||||
use crate::instruction::Instruction;
 | 
					use crate::instruction::Instruction;
 | 
				
			||||||
use crate::interrupt::{InterruptEnable, InterruptFlag};
 | 
					use crate::interrupt::{InterruptEnable, InterruptFlag};
 | 
				
			||||||
use crate::joypad::Joypad;
 | 
					 | 
				
			||||||
use crate::ppu::Ppu;
 | 
					 | 
				
			||||||
use crate::Cycle;
 | 
					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};
 | 
				
			||||||
@@ -18,7 +15,7 @@ pub struct Cpu {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Cpu {
 | 
					impl Cpu {
 | 
				
			||||||
    pub fn new() -> Self {
 | 
					    pub(crate) fn without_boot() -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            reg: Registers {
 | 
					            reg: Registers {
 | 
				
			||||||
                a: 0x01,
 | 
					                a: 0x01,
 | 
				
			||||||
@@ -36,11 +33,11 @@ impl Cpu {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn boot_new(path: &str) -> anyhow::Result<Self> {
 | 
					    pub(crate) fn with_boot(rom: [u8; BOOT_SIZE]) -> Self {
 | 
				
			||||||
        Ok(Self {
 | 
					        Self {
 | 
				
			||||||
            bus: Bus::with_boot(path)?,
 | 
					            bus: Bus::with_boot(rom),
 | 
				
			||||||
            ..Default::default()
 | 
					            ..Default::default()
 | 
				
			||||||
        })
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub(crate) fn ime(&self) -> ImeState {
 | 
					    pub(crate) fn ime(&self) -> ImeState {
 | 
				
			||||||
@@ -72,14 +69,6 @@ impl Cpu {
 | 
				
			|||||||
            _ => None,
 | 
					            _ => None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn load_cartridge(&mut self, path: &str) -> std::io::Result<()> {
 | 
					 | 
				
			||||||
        self.bus.load_cartridge(path)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub fn rom_title(&self) -> Option<&str> {
 | 
					 | 
				
			||||||
        self.bus.rom_title()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Cpu {
 | 
					impl Cpu {
 | 
				
			||||||
@@ -167,16 +156,12 @@ impl BusIo for Cpu {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Cpu {
 | 
					impl Cpu {
 | 
				
			||||||
    pub fn ppu(&mut self) -> &Ppu {
 | 
					    pub(crate) fn bus(&self) -> &Bus {
 | 
				
			||||||
        &self.bus.ppu
 | 
					        &self.bus
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn apu_mut(&mut self) -> &mut Apu {
 | 
					    pub(crate) fn bus_mut(&mut self) -> &mut Bus {
 | 
				
			||||||
        &mut self.bus.apu
 | 
					        &mut self.bus
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pub(crate) fn joypad_mut(&mut self) -> &mut Joypad {
 | 
					 | 
				
			||||||
        &mut self.bus.joypad
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn handle_ei(&mut self) {
 | 
					    fn handle_ei(&mut self) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										146
									
								
								src/emu.rs
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								src/emu.rs
									
									
									
									
									
								
							@@ -1,8 +1,7 @@
 | 
				
			|||||||
use crate::cpu::Cpu as SM83;
 | 
					use crate::apu::gen::SampleProducer;
 | 
				
			||||||
use crate::joypad;
 | 
					use crate::cpu::Cpu;
 | 
				
			||||||
use crate::ppu::Ppu;
 | 
					use crate::joypad::{self, Joypad};
 | 
				
			||||||
use crate::Cycle;
 | 
					use crate::{Cycle, GB_HEIGHT, GB_WIDTH};
 | 
				
			||||||
use anyhow::Result;
 | 
					 | 
				
			||||||
use gilrs::Gilrs;
 | 
					use gilrs::Gilrs;
 | 
				
			||||||
use std::time::Duration;
 | 
					use std::time::Duration;
 | 
				
			||||||
use winit_input_helper::WinitInputHelper;
 | 
					use winit_input_helper::WinitInputHelper;
 | 
				
			||||||
@@ -12,56 +11,111 @@ 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 init(boot_path: Option<&str>, rom_path: &str) -> Result<SM83> {
 | 
					pub fn run_frame(emu: &mut Emulator, gamepad: &mut Gilrs, key: &WinitInputHelper) -> Cycle {
 | 
				
			||||||
    let mut cpu = match boot_path {
 | 
					 | 
				
			||||||
        Some(path) => SM83::boot_new(path)?,
 | 
					 | 
				
			||||||
        None => SM83::new(),
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    eprintln!("Initialized GB Emulator");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cpu.load_cartridge(rom_path)?;
 | 
					 | 
				
			||||||
    Ok(cpu)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub fn rom_title(game_boy: &SM83) -> &str {
 | 
					 | 
				
			||||||
    game_boy.rom_title().unwrap_or(DEFAULT_TITLE)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub fn run(
 | 
					 | 
				
			||||||
    game_boy: &mut SM83,
 | 
					 | 
				
			||||||
    gamepad: &mut Gilrs,
 | 
					 | 
				
			||||||
    input: &WinitInputHelper,
 | 
					 | 
				
			||||||
    target: Cycle,
 | 
					 | 
				
			||||||
) -> Cycle {
 | 
					 | 
				
			||||||
    let mut elapsed = 0;
 | 
					    let mut elapsed = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if let Some(event) = gamepad.next_event() {
 | 
					    if let Some(event) = gamepad.next_event() {
 | 
				
			||||||
        joypad::handle_gamepad_input(game_boy.joypad_mut(), event);
 | 
					        joypad::handle_gamepad_input(emu.joyp_mut(), event);
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    joypad::handle_keyboard_input(game_boy.joypad_mut(), input);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    while elapsed < target {
 | 
					 | 
				
			||||||
        elapsed += game_boy.step();
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    elapsed
 | 
					    joypad::handle_keyboard_input(emu.joyp_mut(), key);
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub fn run_frame(game_boy: &mut SM83, gamepad: &mut Gilrs, input: &WinitInputHelper) -> Cycle {
 | 
					 | 
				
			||||||
    let mut elapsed = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if let Some(event) = gamepad.next_event() {
 | 
					 | 
				
			||||||
        joypad::handle_gamepad_input(game_boy.joypad_mut(), event);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    joypad::handle_keyboard_input(game_boy.joypad_mut(), input);
 | 
					 | 
				
			||||||
    while elapsed < CYCLES_IN_FRAME {
 | 
					    while elapsed < CYCLES_IN_FRAME {
 | 
				
			||||||
        elapsed += game_boy.step();
 | 
					        elapsed += emu.step();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    elapsed
 | 
					    elapsed
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn draw(ppu: &Ppu, frame: &mut [u8]) {
 | 
					pub fn draw_frame(emu: &Emulator, buf: &mut [u8; GB_HEIGHT * GB_WIDTH * 4]) {
 | 
				
			||||||
    ppu.copy_to_gui(frame);
 | 
					    buf.copy_from_slice(emu.cpu.bus().ppu.frame_buf());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Emulator {
 | 
				
			||||||
 | 
					    cpu: Cpu,
 | 
				
			||||||
 | 
					    timestamp: Cycle,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl Emulator {
 | 
				
			||||||
 | 
					    fn new(cpu: Cpu) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            cpu,
 | 
				
			||||||
 | 
					            timestamp: Default::default(),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn step(&mut self) -> Cycle {
 | 
				
			||||||
 | 
					        self.cpu.step()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn load_cart(&mut self, rom: Vec<u8>) {
 | 
				
			||||||
 | 
					        self.cpu.bus_mut().load_cart(rom)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn joyp_mut(&mut self) -> &mut Joypad {
 | 
				
			||||||
 | 
					        &mut self.cpu.bus_mut().joypad
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn set_prod(&mut self, prod: SampleProducer<f32>) {
 | 
				
			||||||
 | 
					        self.cpu.bus_mut().apu.attach_producer(prod)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn title(&self) -> &str {
 | 
				
			||||||
 | 
					        self.cpu.bus().cart_title().unwrap_or(DEFAULT_TITLE)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub mod build {
 | 
				
			||||||
 | 
					    use std::fs::File;
 | 
				
			||||||
 | 
					    use std::io::{Read, Result};
 | 
				
			||||||
 | 
					    use std::path::Path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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) => Cpu::with_boot(rom),
 | 
				
			||||||
 | 
					                None => Cpu::without_boot(),
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if let Some(rom) = self.cart.take() {
 | 
				
			||||||
 | 
					                emu.load_cart(rom)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            emu
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										38
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										38
									
								
								src/main.rs
									
									
									
									
									
								
							@@ -1,5 +1,8 @@
 | 
				
			|||||||
 | 
					use std::convert::TryInto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use anyhow::{anyhow, Result};
 | 
					use anyhow::{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::build::EmulatorBuilder;
 | 
				
			||||||
use gb::{AudioSPSC, Cycle, GB_HEIGHT, GB_WIDTH};
 | 
					use gb::{AudioSPSC, Cycle, GB_HEIGHT, GB_WIDTH};
 | 
				
			||||||
use gilrs::Gilrs;
 | 
					use gilrs::Gilrs;
 | 
				
			||||||
use pixels::{PixelsBuilder, SurfaceTexture};
 | 
					use pixels::{PixelsBuilder, SurfaceTexture};
 | 
				
			||||||
@@ -38,13 +41,15 @@ fn main() -> Result<()> {
 | 
				
			|||||||
        )
 | 
					        )
 | 
				
			||||||
        .get_matches();
 | 
					        .get_matches();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let rom_path = m
 | 
					    let mut emu_build =
 | 
				
			||||||
        .value_of("rom")
 | 
					        EmulatorBuilder::new().with_cart(m.value_of("rom").expect("ROM path provided"))?;
 | 
				
			||||||
        .expect("Required value 'rom' was provided");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut game_boy =
 | 
					    if let Some(path) = m.value_of("boot") {
 | 
				
			||||||
        gb::emu::init(m.value_of("boot"), rom_path).expect("Initialize DMG-01 Emulator");
 | 
					        emu_build = emu_build.with_boot(path)?;
 | 
				
			||||||
    let rom_title = gb::emu::rom_title(&game_boy);
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let mut emu = emu_build.finish();
 | 
				
			||||||
 | 
					    let rom_title = emu.title();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let mut gamepad = Gilrs::new().expect("Initialize Controller Support");
 | 
					    let mut gamepad = Gilrs::new().expect("Initialize Controller Support");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -68,10 +73,14 @@ fn main() -> Result<()> {
 | 
				
			|||||||
    if AUDIO_ENABLED {
 | 
					    if AUDIO_ENABLED {
 | 
				
			||||||
        let spsc: AudioSPSC<f32> = Default::default();
 | 
					        let spsc: AudioSPSC<f32> = Default::default();
 | 
				
			||||||
        let (prod, cons) = spsc.init();
 | 
					        let (prod, cons) = spsc.init();
 | 
				
			||||||
        let sink = Sink::try_new(&stream_handle)?;
 | 
					        let sink = {
 | 
				
			||||||
        sink.append(cons);
 | 
					            let s = Sink::try_new(&stream_handle)?;
 | 
				
			||||||
        sink.set_volume(0.1); // TODO: Is this the right way to go about this?
 | 
					            s.append(cons);
 | 
				
			||||||
        game_boy.apu_mut().attach_producer(prod);
 | 
					            s.set_volume(0.1);
 | 
				
			||||||
 | 
					            s
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        emu.set_prod(prod);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::thread::spawn(move || {
 | 
					        std::thread::spawn(move || {
 | 
				
			||||||
            sink.sleep_until_end();
 | 
					            sink.sleep_until_end();
 | 
				
			||||||
@@ -102,12 +111,17 @@ fn main() -> Result<()> {
 | 
				
			|||||||
                pixels.resize_surface(size.width, size.height);
 | 
					                pixels.resize_surface(size.width, size.height);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            cycle_count += gb::emu::run_frame(&mut game_boy, &mut gamepad, &input);
 | 
					            cycle_count += gb::emu::run_frame(&mut emu, &mut gamepad, &input);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if cycle_count >= gb::emu::CYCLES_IN_FRAME {
 | 
					            if cycle_count >= gb::emu::CYCLES_IN_FRAME {
 | 
				
			||||||
                cycle_count %= gb::emu::CYCLES_IN_FRAME;
 | 
					                cycle_count %= gb::emu::CYCLES_IN_FRAME;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                gb::emu::draw(game_boy.ppu(), pixels.get_frame());
 | 
					                let buf: &mut [u8; GB_WIDTH * GB_HEIGHT * 4] = pixels
 | 
				
			||||||
 | 
					                    .get_frame()
 | 
				
			||||||
 | 
					                    .try_into()
 | 
				
			||||||
 | 
					                    .expect("Size of Pixel Buffer is GB_WIDTH * GB_HEIGHT * 4");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                gb::emu::draw_frame(&emu, buf);
 | 
				
			||||||
                window.request_redraw();
 | 
					                window.request_redraw();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -418,8 +418,8 @@ impl Ppu {
 | 
				
			|||||||
        self.frame_buf.swap_with_slice(&mut blank);
 | 
					        self.frame_buf.swap_with_slice(&mut blank);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn copy_to_gui(&self, frame: &mut [u8]) {
 | 
					    pub(crate) fn frame_buf(&self) -> &[u8; GB_HEIGHT * GB_WIDTH * 4] {
 | 
				
			||||||
        frame.copy_from_slice(self.frame_buf.as_ref());
 | 
					        &self.frame_buf
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn clock_fifo(&mut self) -> Option<GrayShade> {
 | 
					    fn clock_fifo(&mut self) -> Option<GrayShade> {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user