skippable bootrom
This commit is contained in:
parent
fced7af144
commit
ef795d810b
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue