skippable bootrom

This commit is contained in:
Alex Janka 2023-04-26 12:23:12 +10:00
parent fced7af144
commit ef795d810b
8 changed files with 86 additions and 40 deletions

View file

@ -61,6 +61,10 @@ struct Args {
/// Force CGB mode
#[arg(short, long)]
cgb: bool,
/// Skip BootROM
#[arg(long)]
skip: bool,
}
fn main() {
@ -106,7 +110,7 @@ fn main() {
} else {
SerialTarget::None
})
.with_bootrom(args.bootrom.map(RomFile::Path))
.with_bootrom(args.bootrom.map(RomFile::Path), args.skip)
.with_no_save(args.no_save)
.with_tile_window(tile_window)
.with_cgb_mode(args.cgb);

View file

@ -266,7 +266,7 @@ impl Plugin for GameboyEmu {
#[cfg(not(feature = "savestate"))]
let mut emulator_core = {
let options = EmulatorOptions::new(window, rom, output)
.with_bootrom(bootrom)
.with_bootrom(bootrom, true)
.with_serial_target(serial_target)
.force_no_save();

View file

@ -169,6 +169,7 @@ where
pub(crate) camera: C,
pub(crate) no_save: bool,
pub(crate) bootrom: Option<RomFile>,
pub(crate) skip_bootrom: bool,
pub(crate) serial_target: SerialTarget,
pub(crate) cgb_mode: bool,
spooky: PhantomData<ColourFormat>,
@ -189,6 +190,7 @@ where
camera: NoCamera::default(),
no_save: false,
bootrom: None,
skip_bootrom: false,
serial_target: SerialTarget::None,
cgb_mode: false,
spooky: PhantomData,
@ -212,6 +214,7 @@ where
camera,
no_save: false,
bootrom: None,
skip_bootrom: false,
serial_target: SerialTarget::None,
cgb_mode: false,
spooky: PhantomData,
@ -233,8 +236,9 @@ where
self
}
pub fn with_bootrom(mut self, bootrom: Option<RomFile>) -> Self {
pub fn with_bootrom(mut self, bootrom: Option<RomFile>, skip_bootrom: bool) -> Self {
self.bootrom = bootrom;
self.skip_bootrom = skip_bootrom;
self
}

View file

@ -10,7 +10,7 @@ use connect::{
};
use processor::{
memory::{mmio::gpu::Colour, rom::CgbRomType, OutputTargets, Rom},
Cpu, CpuSaveState,
Cpu, CpuSaveState, RunBootrom,
};
use std::{
fs::{self},
@ -86,7 +86,15 @@ where
if cgb { "CGB" } else { "DMG" }
));
let bootrom_enabled = options.bootrom.is_some();
let bootrom_enabled = if options.bootrom.is_some() {
if options.skip_bootrom {
RunBootrom::Skip
} else {
RunBootrom::Yes
}
} else {
RunBootrom::No
};
let bootrom: Option<Vec<u8>> = options.bootrom.map(|v| match v {
RomFile::Path(path) => match fs::read(path) {
Ok(data) => data,

View file

@ -236,6 +236,10 @@ where
}
}
pub(crate) fn has_bootrom(&self) -> bool {
self.bootrom.is_some()
}
pub(crate) fn get<T>(&self, address: T) -> u8
where
T: Into<Address>,
@ -549,14 +553,14 @@ where
self.memory.interrupts.set_interrupt(Interrupt::Timer, true);
}
self.memory.apu.tick(logical_steps);
self.memory.apu.tick(logical_steps, !self.is_skipping);
let serial_interrupt = self.memory.serial.tick(steps, self.memory.ime);
self.memory
.interrupts
.set_interrupt(Interrupt::Serial, serial_interrupt);
let gpu_interrupts = self.memory.gpu.tick(logical_steps);
let gpu_interrupts = self.memory.gpu.tick(logical_steps, !self.is_skipping);
self.memory
.interrupts

View file

@ -130,26 +130,33 @@ impl Apu {
}
}
pub fn tick(&mut self, steps: usize) {
self.buffer.append(
&mut izip!(
self.channels.one.tick(steps).into_iter(),
self.channels.two.tick(steps).into_iter(),
self.channels.three.tick(steps).into_iter(),
self.channels.four.tick(steps).into_iter()
)
.map(|(one, two, three, four)| DacSample {
one,
two,
three,
four,
})
.collect(),
);
if self.buffer.len() >= CYCLES_PER_FRAME {
self.next_audio();
} else if !self.out_buffer.is_empty() {
self.push_audio();
pub fn tick(&mut self, steps: usize, output: bool) {
if output {
self.buffer.append(
&mut izip!(
self.channels.one.tick(steps).into_iter(),
self.channels.two.tick(steps).into_iter(),
self.channels.three.tick(steps).into_iter(),
self.channels.four.tick(steps).into_iter()
)
.map(|(one, two, three, four)| DacSample {
one,
two,
three,
four,
})
.collect(),
);
if self.buffer.len() >= CYCLES_PER_FRAME {
self.next_audio();
} else if !self.out_buffer.is_empty() {
self.push_audio();
}
} else {
self.channels.one.tick(steps);
self.channels.two.tick(steps);
self.channels.three.tick(steps);
self.channels.four.tick(steps);
}
}

View file

@ -202,7 +202,7 @@ where
self.stat.mode
}
pub fn tick(&mut self, steps: usize) -> GpuInterrupts {
pub fn tick(&mut self, steps: usize, output: bool) -> GpuInterrupts {
let mut interrupts = GpuInterrupts::default();
if self.lcdc.enable {
self.mode_clock += steps;
@ -213,7 +213,7 @@ where
self.mode_clock = 0;
self.scanline += 1;
if self.scanline == 143 {
self.enter_vblank();
self.enter_vblank(output);
interrupts.vblank = true;
} else {
self.stat.mode = DrawMode::Mode2;
@ -226,7 +226,7 @@ where
self.mode_clock = 0;
self.scanline += 1;
if self.scanline == 153 {
self.exit_vblank();
self.exit_vblank(output);
}
}
}
@ -242,7 +242,7 @@ where
// generate scanline
if self.mode_clock >= 172 {
self.mode_clock = 0;
self.enter_hblank();
self.enter_hblank(output);
}
}
}
@ -290,23 +290,27 @@ where
}
}
fn enter_hblank(&mut self) {
fn enter_hblank(&mut self, output: bool) {
self.stat.mode = DrawMode::HBlank;
self.render_scanline(self.scanline);
if output {
self.render_scanline(self.scanline);
}
}
fn enter_vblank(&mut self) {
fn enter_vblank(&mut self, output: bool) {
self.stat.mode = DrawMode::VBlank;
self.render_window();
if output {
self.render_window();
}
}
fn exit_vblank(&mut self) {
fn exit_vblank(&mut self, output: bool) {
self.stat.mode = DrawMode::Mode2;
self.scanline = 0;
self.window_lc = 0;
self.has_window_been_enabled = false;
let is_cgb_mode = self.is_cgb_mode();
if let Some(tile_window) = &mut self.tile_window {
if let Some(tile_window) = &mut self.tile_window && output {
tile_window.draw_sprite_window(self.bg_palette, &self.vram, is_cgb_mode);
}
}

View file

@ -20,6 +20,13 @@ pub(crate) enum Direction {
Right,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(crate) enum RunBootrom {
No,
Yes,
Skip,
}
pub struct Cpu<ColourFormat, R, C>
where
ColourFormat: From<Colour> + Clone,
@ -33,6 +40,7 @@ where
halted: bool,
should_halt_bug: bool,
pub(super) cycle_count: usize,
pub(crate) is_skipping: bool,
}
#[derive(Serialize, Deserialize)]
@ -74,18 +82,20 @@ where
R: Renderer<ColourFormat>,
C: PocketCamera + Send + 'static,
{
pub fn new(mut memory: Memory<ColourFormat, R, C>, run_bootrom: bool) -> Self {
if !run_bootrom {
pub(crate) fn new(mut memory: Memory<ColourFormat, R, C>, run_bootrom: RunBootrom) -> Self {
let is_running_bootrom = run_bootrom != RunBootrom::No;
if !is_running_bootrom {
memory.cpu_ram_init();
}
Self {
memory,
reg: Registers::init(run_bootrom),
reg: Registers::init(is_running_bootrom),
last_instruction: 0x0,
last_instruction_addr: 0x0,
halted: false,
should_halt_bug: false,
cycle_count: 0,
is_skipping: run_bootrom == RunBootrom::Skip,
}
}
@ -97,6 +107,10 @@ where
return;
}
if self.is_skipping && !self.memory.has_bootrom() {
self.is_skipping = false;
}
// double this if in double speed mode
let vram_dma_cycles = self.memory.vram_dma_tick();
if vram_dma_cycles > 0 {
@ -199,6 +213,7 @@ where
halted: state.halted,
should_halt_bug: state.should_halt_bug,
cycle_count: state.cycle_count,
is_skipping: false,
}
}
}