feat: improve debugger
This commit is contained in:
parent
c37d5d5e84
commit
a2156ec7f4
31 changed files with 615 additions and 144 deletions
|
@ -46,6 +46,10 @@ jobs:
|
|||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/daa.gb test -m 100000000 -s meowgb-tests/expected_output/daa.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite di_timing-GS)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/di_timing-GS.gb test -m 100000000 -s meowgb-tests/expected_output/di_timing-GS.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite div_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/div_timing.gb test -m 100000000 -s meowgb-tests/expected_output/div_timing.bin
|
||||
|
@ -66,6 +70,18 @@ jobs:
|
|||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime0_ei.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime0_ei.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite halt_ime0_nointr_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime0_nointr_timing.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime0_nointr_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite halt_ime1_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime1_timing.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime1_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite halt_ime1_timing2-GS)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime1_timing2-GS.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime1_timing2-GS.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite intr_1_2_timing-GS)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/intr_1_2_timing-GS.gb test -m 100000000 -s meowgb-tests/expected_output/intr_1_2_timing-GS.bin
|
||||
|
@ -90,6 +106,14 @@ jobs:
|
|||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/oam_dma_timing.gb test -m 100000000 -s meowgb-tests/expected_output/oam_dma_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite pop_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/pop_timing.gb test -m 100000000 -s meowgb-tests/expected_output/pop_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite push_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/push_timing.gb test -m 100000000 -s meowgb-tests/expected_output/push_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite rapid_di_ei)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/rapid_di_ei.gb test -m 100000000 -s meowgb-tests/expected_output/rapid_di_ei.bin
|
||||
|
@ -106,6 +130,10 @@ jobs:
|
|||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/reg_read.gb test -m 100000000 -s meowgb-tests/expected_output/reg_read.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite rst_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/rst_timing.gb test -m 100000000 -s meowgb-tests/expected_output/rst_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite stat_irq_blocking)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/stat_irq_blocking.gb test -m 100000000 -s meowgb-tests/expected_output/stat_irq_blocking.bin
|
||||
|
|
28
.github/workflows/action.yml
vendored
28
.github/workflows/action.yml
vendored
|
@ -46,6 +46,10 @@ jobs:
|
|||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/daa.gb test -m 100000000 -s meowgb-tests/expected_output/daa.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite di_timing-GS)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/di_timing-GS.gb test -m 100000000 -s meowgb-tests/expected_output/di_timing-GS.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite div_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/div_timing.gb test -m 100000000 -s meowgb-tests/expected_output/div_timing.bin
|
||||
|
@ -66,6 +70,18 @@ jobs:
|
|||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime0_ei.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime0_ei.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite halt_ime0_nointr_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime0_nointr_timing.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime0_nointr_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite halt_ime1_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime1_timing.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime1_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite halt_ime1_timing2-GS)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime1_timing2-GS.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime1_timing2-GS.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite intr_1_2_timing-GS)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/intr_1_2_timing-GS.gb test -m 100000000 -s meowgb-tests/expected_output/intr_1_2_timing-GS.bin
|
||||
|
@ -90,6 +106,14 @@ jobs:
|
|||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/oam_dma_timing.gb test -m 100000000 -s meowgb-tests/expected_output/oam_dma_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite pop_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/pop_timing.gb test -m 100000000 -s meowgb-tests/expected_output/pop_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite push_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/push_timing.gb test -m 100000000 -s meowgb-tests/expected_output/push_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite rapid_di_ei)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/rapid_di_ei.gb test -m 100000000 -s meowgb-tests/expected_output/rapid_di_ei.bin
|
||||
|
@ -106,6 +130,10 @@ jobs:
|
|||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/reg_read.gb test -m 100000000 -s meowgb-tests/expected_output/reg_read.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite rst_timing)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/rst_timing.gb test -m 100000000 -s meowgb-tests/expected_output/rst_timing.bin
|
||||
|
||||
- name: Run test ROM (mooneye-test-suite stat_irq_blocking)
|
||||
if: always()
|
||||
run: ./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/stat_irq_blocking.gb test -m 100000000 -s meowgb-tests/expected_output/stat_irq_blocking.bin
|
||||
|
|
|
@ -4,6 +4,8 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
instr-dbg = []
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.14"
|
||||
|
|
|
@ -24,6 +24,8 @@ use self::{
|
|||
serial::{Serial, SerialWriter},
|
||||
sound::Sound,
|
||||
};
|
||||
#[cfg(feature = "instr-dbg")]
|
||||
use crate::ringbuffer::RingBuffer;
|
||||
|
||||
pub struct Gameboy<S: SerialWriter> {
|
||||
pub ppu: Ppu,
|
||||
|
@ -43,6 +45,12 @@ pub struct Gameboy<S: SerialWriter> {
|
|||
pub stop: bool,
|
||||
|
||||
pub tick_count: u8,
|
||||
|
||||
pub last_read: Option<(u16, u8)>,
|
||||
pub last_write: Option<(u16, u8)>,
|
||||
|
||||
#[cfg(feature = "instr-dbg")]
|
||||
pub pc_history: RingBuffer<u16, 0x1000>,
|
||||
}
|
||||
|
||||
impl<S: SerialWriter> Gameboy<S> {
|
||||
|
@ -66,6 +74,10 @@ impl<S: SerialWriter> Gameboy<S> {
|
|||
used_halt_bug: false,
|
||||
stop: false,
|
||||
tick_count: 0,
|
||||
last_read: None,
|
||||
last_write: None,
|
||||
#[cfg(feature = "instr-dbg")]
|
||||
pc_history: RingBuffer::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,10 +233,7 @@ impl<S: SerialWriter> Gameboy<S> {
|
|||
}
|
||||
0xFF41 => self.ppu.set_stat(&mut self.interrupts, value),
|
||||
0xFF42 => self.ppu.registers.scy = value,
|
||||
0xFF43 => {
|
||||
// println!("Setting SCX to {} from {}", value, self.ppu.registers.scx);
|
||||
self.ppu.registers.scx = value;
|
||||
},
|
||||
0xFF43 => self.ppu.registers.scx = value,
|
||||
0xFF44 => {} // LY is read only
|
||||
0xFF45 => self.ppu.set_lyc(&mut self.interrupts, value),
|
||||
0xFF46 => self.dma.init_request(value),
|
||||
|
@ -312,6 +321,10 @@ impl<S: SerialWriter> Gameboy<S> {
|
|||
}
|
||||
|
||||
pub fn cpu_read_u8(&mut self, address: u16) {
|
||||
self.cpu_read_u8_internal(address, false);
|
||||
}
|
||||
|
||||
pub fn cpu_read_u8_internal(&mut self, address: u16, is_next_pc: bool) {
|
||||
assert!(!self.registers.mem_op_happened);
|
||||
assert!(self.registers.mem_read_hold.is_none());
|
||||
self.registers.mem_op_happened = true;
|
||||
|
@ -342,12 +355,16 @@ impl<S: SerialWriter> Gameboy<S> {
|
|||
0xFFFF => self.interrupts.interrupt_enable,
|
||||
},
|
||||
};
|
||||
if !is_next_pc {
|
||||
self.last_read = Some((address, value));
|
||||
}
|
||||
self.registers.mem_read_hold = Some(value);
|
||||
}
|
||||
|
||||
pub fn cpu_write_u8(&mut self, address: u16, value: u8) {
|
||||
assert!(!self.registers.mem_op_happened);
|
||||
self.registers.mem_op_happened = true;
|
||||
self.last_write = Some((address, value));
|
||||
|
||||
match self.ppu.dma_occuring {
|
||||
true => match address {
|
||||
|
|
|
@ -142,6 +142,8 @@ impl Registers {
|
|||
|
||||
pub fn tick_cpu(state: &mut Gameboy<impl SerialWriter>) {
|
||||
state.registers.mem_op_happened = false;
|
||||
state.last_read = None;
|
||||
state.last_write = None;
|
||||
|
||||
if state.joypad.interrupt_triggered {
|
||||
state.joypad.interrupt_triggered = false;
|
||||
|
@ -162,7 +164,6 @@ pub fn tick_cpu(state: &mut Gameboy<impl SerialWriter>) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Interrupts
|
||||
if state.registers.cycle == 0 && state.interrupts.ime {
|
||||
if state.interrupts.read_ie_vblank() && state.interrupts.read_if_vblank() {
|
||||
state.registers.in_interrupt_vector = Some(0);
|
||||
|
@ -244,7 +245,7 @@ pub fn tick_cpu(state: &mut Gameboy<impl SerialWriter>) {
|
|||
opcode
|
||||
}
|
||||
None => {
|
||||
state.cpu_read_u8(state.registers.pc);
|
||||
state.cpu_read_u8_internal(state.registers.pc, true);
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
@ -533,7 +534,7 @@ pub fn tick_cpu(state: &mut Gameboy<impl SerialWriter>) {
|
|||
}
|
||||
|
||||
if !state.registers.mem_op_happened {
|
||||
state.cpu_read_u8(state.registers.pc);
|
||||
state.cpu_read_u8_internal(state.registers.pc, true);
|
||||
}
|
||||
|
||||
state.registers.cycle = 0;
|
||||
|
|
|
@ -38,25 +38,25 @@ impl DmaState {
|
|||
|
||||
if self.remaining_cycles > 0 {
|
||||
let offset = 0xA0 - self.remaining_cycles;
|
||||
let read_address = ((self.original_base as usize) << 8) | offset as usize;
|
||||
|
||||
let value = if self.original_base <= 0x7F {
|
||||
match cartridge {
|
||||
Some(cart) => cart.read_rom_u8((self.base as u16) << 8 | offset as u16),
|
||||
Some(cart) => cart.read_rom_u8(read_address as u16),
|
||||
None => 0xFF,
|
||||
}
|
||||
} else if self.original_base <= 0x9F {
|
||||
let address = (((self.original_base as usize) << 8) | offset as usize) - 0x8000;
|
||||
let address = read_address - 0x8000;
|
||||
ppu.vram[address]
|
||||
} else if self.original_base <= 0xDF {
|
||||
let address = ((self.original_base as usize) << 8 | offset as usize) - 0xC000;
|
||||
let address = read_address - 0xC000;
|
||||
memory.wram[address]
|
||||
} else if self.original_base <= 0xFD {
|
||||
let address = ((self.original_base as usize) << 8 | offset as usize) - 0xE000;
|
||||
let address = read_address - 0xE000;
|
||||
memory.wram[address]
|
||||
} else {
|
||||
0xFF
|
||||
};
|
||||
|
||||
ppu.dma_write_oam(offset, value);
|
||||
self.remaining_cycles -= 1;
|
||||
}
|
||||
|
|
|
@ -41,17 +41,15 @@ impl Mapper for NoMBC {
|
|||
fn write_rom_u8(&mut self, _address: u16, _value: u8) {}
|
||||
|
||||
fn read_eram_u8(&self, address: u16) -> u8 {
|
||||
let decoded_address = address - 0xA000;
|
||||
match &self.ram {
|
||||
Some(ram) => ram[decoded_address as usize],
|
||||
Some(ram) => ram[address as usize],
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn write_eram_u8(&mut self, address: u16, value: u8) {
|
||||
let decoded_address = address - 0xA000;
|
||||
if let Some(ram) = &mut self.ram {
|
||||
ram[decoded_address as usize] = value;
|
||||
ram[address as usize] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -550,8 +550,10 @@ impl Ppu {
|
|||
}
|
||||
|
||||
fn set_scanline(&mut self, interrupts: &mut Interrupts, scanline: u8) {
|
||||
// println!("LY incrementing: {} cycles since last incrementation. cycles since: {:?}", self.registers.cycles_since_last_ly_increment, self.registers.cycles_since_last_last_mode_start_increment.iter().enumerate().map(|(idx, value)| {
|
||||
// let idx_enum = match idx {
|
||||
// println!("LY incrementing: {} cycles since last incrementation. cycles since:
|
||||
// {:?}", self.registers.cycles_since_last_ly_increment,
|
||||
// self.registers.cycles_since_last_last_mode_start_increment.iter().
|
||||
// enumerate().map(|(idx, value)| { let idx_enum = match idx {
|
||||
// 0 => PPUMode::HBlank,
|
||||
// 1 => PPUMode::VBlank,
|
||||
// 2 => PPUMode::SearchingOAM,
|
||||
|
@ -638,7 +640,6 @@ impl Ppu {
|
|||
self.total_dots += 1;
|
||||
|
||||
if self.current_dot == self.dot_target {
|
||||
// println!("Mode 3 DT: {}", self.dot_target);
|
||||
// assert_eq!(self.total_dots, match self.first_frame && self.first_line {
|
||||
// true => self.dot_target,
|
||||
// false => 80 + self.dot_target
|
||||
|
@ -659,15 +660,14 @@ impl Ppu {
|
|||
if self.first_line && self.current_dot == 80 && self.dot_target == 0 {
|
||||
self.set_mode(interrupts, PPUMode::TransferringData);
|
||||
} else if self.dot_target != 0 && self.current_dot == self.dot_target {
|
||||
// println!("Mode 0 DT: {}", self.dot_target);
|
||||
self.set_scanline(interrupts, self.registers.ly + 1);
|
||||
|
||||
assert_eq!(
|
||||
self.total_dots,
|
||||
456 // match self.first_frame && self.first_line {
|
||||
// true => 456 - (80 - 64),
|
||||
// false => 456,
|
||||
// }
|
||||
456 /* match self.first_frame && self.first_line {
|
||||
* true => 456 - (80 - 64),
|
||||
* false => 456,
|
||||
* } */
|
||||
);
|
||||
self.total_dots = 0;
|
||||
self.first_line = false;
|
||||
|
@ -729,10 +729,8 @@ impl Ppu {
|
|||
Some(state) => state,
|
||||
None => {
|
||||
let scrolling_delay = self.registers.scx % 8;
|
||||
// println!("{}", scrolling_delay);
|
||||
self.dot_target += scrolling_delay as u16;
|
||||
if scrolling_delay != 0 {
|
||||
// println!("Scrolling delay of {:#X} with scx of {}", scrolling_delay, self.registers.scx);
|
||||
LineDrawingState::BackgroundScrolling(
|
||||
scrolling_delay as usize,
|
||||
self.registers.scx,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
pub mod gameboy;
|
||||
#[cfg(feature = "instr-dbg")]
|
||||
pub mod ringbuffer;
|
||||
|
||||
/// A helper for writing CPU tests in Rust, the emulator returned by this
|
||||
|
|
|
@ -68,3 +68,24 @@ fn test_ringbuffer() {
|
|||
&[16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31]
|
||||
);
|
||||
}
|
||||
|
||||
impl<T: std::fmt::Debug + Copy + Default + std::fmt::UpperHex, const SIZE: usize> std::fmt::Display
|
||||
for RingBuffer<T, SIZE>
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut offset = self.read_ptr;
|
||||
|
||||
f.write_str("[\n")?;
|
||||
|
||||
for idx in 0..self.size {
|
||||
f.write_fmt(format_args!(" {}: {:#X},\n", idx, self.buffer[offset]))?;
|
||||
|
||||
offset += 1;
|
||||
offset %= SIZE;
|
||||
}
|
||||
|
||||
f.write_str("]")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
1
meowgb-tests/expected_output/di_timing-GS.bin
Normal file
1
meowgb-tests/expected_output/di_timing-GS.bin
Normal file
|
@ -0,0 +1 @@
|
|||
"
|
1
meowgb-tests/expected_output/halt_ime0_nointr_timing.bin
Normal file
1
meowgb-tests/expected_output/halt_ime0_nointr_timing.bin
Normal file
|
@ -0,0 +1 @@
|
|||
"
|
1
meowgb-tests/expected_output/halt_ime1_timing.bin
Normal file
1
meowgb-tests/expected_output/halt_ime1_timing.bin
Normal file
|
@ -0,0 +1 @@
|
|||
"
|
1
meowgb-tests/expected_output/halt_ime1_timing2-GS.bin
Normal file
1
meowgb-tests/expected_output/halt_ime1_timing2-GS.bin
Normal file
|
@ -0,0 +1 @@
|
|||
"
|
1
meowgb-tests/expected_output/pop_timing.bin
Normal file
1
meowgb-tests/expected_output/pop_timing.bin
Normal file
|
@ -0,0 +1 @@
|
|||
"
|
1
meowgb-tests/expected_output/push_timing.bin
Normal file
1
meowgb-tests/expected_output/push_timing.bin
Normal file
|
@ -0,0 +1 @@
|
|||
"
|
1
meowgb-tests/expected_output/rst_timing.bin
Normal file
1
meowgb-tests/expected_output/rst_timing.bin
Normal file
|
@ -0,0 +1 @@
|
|||
"
|
|
@ -4,6 +4,9 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
debugger = []
|
||||
instr-dbg = ["meowgb-core/instr-dbg"]
|
||||
|
||||
[dependencies]
|
||||
meowgb-core = { path = "../meowgb-core" }
|
||||
|
|
|
@ -17,8 +17,24 @@ use meowgb_core::gameboy::{
|
|||
serial::SerialWriter,
|
||||
Gameboy,
|
||||
};
|
||||
use window::events::{EmulatorWindowEvent, GameboyEvent};
|
||||
use window::events::{EmulatorDebugEvent, EmulatorWindowEvent, GameboyEvent};
|
||||
|
||||
#[cfg(feature = "debugger")]
|
||||
#[derive(Debug, Parser)]
|
||||
/// DMG Emulator
|
||||
pub struct CliArgs {
|
||||
/// bootrom path
|
||||
#[clap(long)]
|
||||
pub bootrom: Option<PathBuf>,
|
||||
/// game path
|
||||
#[clap(long)]
|
||||
pub rom: Option<PathBuf>,
|
||||
/// start the emulator in debug mode
|
||||
#[clap(short, long)]
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "debugger"))]
|
||||
#[derive(Debug, Parser)]
|
||||
/// DMG Emulator
|
||||
pub struct CliArgs {
|
||||
|
@ -60,7 +76,13 @@ fn real_main() -> Result<(), MeowGBError> {
|
|||
None => None,
|
||||
};
|
||||
|
||||
let gameboy = Arc::new(RwLock::new(Gameboy::new(bootrom, std::io::stdout())));
|
||||
let mut gameboy = WrappedGameboy::new(Gameboy::new(bootrom, std::io::stdout()));
|
||||
#[cfg(feature = "debugger")]
|
||||
let dbg = args.debug;
|
||||
#[cfg(not(feature = "debugger"))]
|
||||
let dbg = false;
|
||||
gameboy.debugging = dbg;
|
||||
let gameboy = Arc::new(RwLock::new(gameboy));
|
||||
let gameboy_2 = gameboy.clone();
|
||||
|
||||
let jh = std::thread::Builder::new()
|
||||
|
@ -88,9 +110,21 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct WrappedGameboy<W: SerialWriter> {
|
||||
pub breakpoints: [[bool; 3]; 0x10000],
|
||||
pub debugging: bool,
|
||||
pub gameboy: Gameboy<W>,
|
||||
}
|
||||
|
||||
impl<W: SerialWriter> WrappedGameboy<W> {
|
||||
pub fn new(gameboy: Gameboy<W>) -> Self {
|
||||
Self { breakpoints: [[false; 3]; 0x10000], debugging: false, gameboy }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_gameboy(
|
||||
args: CliArgs,
|
||||
gameboy_arc: Arc<RwLock<Gameboy<impl SerialWriter>>>,
|
||||
gameboy_arc: Arc<RwLock<WrappedGameboy<impl SerialWriter>>>,
|
||||
rx: Receiver<EmulatorWindowEvent>,
|
||||
tx: Sender<GameboyEvent>,
|
||||
) -> Result<(), MeowGBError> {
|
||||
|
@ -103,50 +137,94 @@ pub fn run_gameboy(
|
|||
|
||||
let rom = std::fs::read(rom.as_path())?;
|
||||
|
||||
gameboy.load_cartridge(rom)
|
||||
gameboy.gameboy.load_cartridge(rom)
|
||||
}
|
||||
|
||||
drop(gameboy);
|
||||
|
||||
let mut goal = time::OffsetDateTime::now_utc() + time::Duration::milliseconds(1000 / 60);
|
||||
let mut frame_counter = 0;
|
||||
let mut debugging_tbf = None;
|
||||
|
||||
'outer: loop {
|
||||
let mut gameboy = gameboy_arc.write().unwrap();
|
||||
let mut step = false;
|
||||
|
||||
let mut gameboy = gameboy_arc.write().unwrap();
|
||||
while let Ok(event) = rx.try_recv() {
|
||||
match event {
|
||||
EmulatorWindowEvent::AToggle => gameboy.joypad.invert_a(),
|
||||
EmulatorWindowEvent::BToggle => gameboy.joypad.invert_b(),
|
||||
EmulatorWindowEvent::SelectToggle => gameboy.joypad.invert_select(),
|
||||
EmulatorWindowEvent::StartToggle => gameboy.joypad.invert_start(),
|
||||
EmulatorWindowEvent::UpToggle => gameboy.joypad.invert_up(),
|
||||
EmulatorWindowEvent::DownToggle => gameboy.joypad.invert_down(),
|
||||
EmulatorWindowEvent::LeftToggle => gameboy.joypad.invert_left(),
|
||||
EmulatorWindowEvent::RightToggle => gameboy.joypad.invert_right(),
|
||||
EmulatorWindowEvent::PauseToggle => unimplemented!(),
|
||||
EmulatorWindowEvent::AToggle => gameboy.gameboy.joypad.invert_a(),
|
||||
EmulatorWindowEvent::BToggle => gameboy.gameboy.joypad.invert_b(),
|
||||
EmulatorWindowEvent::SelectToggle => gameboy.gameboy.joypad.invert_select(),
|
||||
EmulatorWindowEvent::StartToggle => gameboy.gameboy.joypad.invert_start(),
|
||||
EmulatorWindowEvent::UpToggle => gameboy.gameboy.joypad.invert_up(),
|
||||
EmulatorWindowEvent::DownToggle => gameboy.gameboy.joypad.invert_down(),
|
||||
EmulatorWindowEvent::LeftToggle => gameboy.gameboy.joypad.invert_left(),
|
||||
EmulatorWindowEvent::RightToggle => gameboy.gameboy.joypad.invert_right(),
|
||||
EmulatorWindowEvent::Exit => break 'outer,
|
||||
EmulatorWindowEvent::Debug(EmulatorDebugEvent::ToggleBreakpoint(addr, breaks)) => {
|
||||
gameboy.breakpoints[addr as usize] = breaks;
|
||||
}
|
||||
EmulatorWindowEvent::Debug(EmulatorDebugEvent::Continue) => {
|
||||
gameboy.debugging = false;
|
||||
if let Some(debugging_tbf) = debugging_tbf.take() {
|
||||
let delta = time::OffsetDateTime::now_utc() - debugging_tbf;
|
||||
goal += delta;
|
||||
}
|
||||
}
|
||||
EmulatorWindowEvent::Debug(EmulatorDebugEvent::Step) => {
|
||||
step = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let redraw_needed = gameboy.tick_4();
|
||||
if !gameboy.debugging || step {
|
||||
let needs_redraw = gameboy.gameboy.tick_4();
|
||||
let bp_triggered = gameboy
|
||||
.gameboy
|
||||
.last_read
|
||||
.map(|(addr, _)| gameboy.breakpoints[addr as usize][0])
|
||||
.unwrap_or_default()
|
||||
|| gameboy
|
||||
.gameboy
|
||||
.last_write
|
||||
.map(|(addr, _)| gameboy.breakpoints[addr as usize][1])
|
||||
.unwrap_or_default()
|
||||
|| gameboy.breakpoints[gameboy.gameboy.registers.pc as usize][2];
|
||||
gameboy.debugging |= bp_triggered;
|
||||
|
||||
drop(gameboy);
|
||||
if bp_triggered || step {
|
||||
gameboy.debugging = bp_triggered;
|
||||
|
||||
if redraw_needed {
|
||||
let now = time::OffsetDateTime::now_utc();
|
||||
frame_counter += 1;
|
||||
tx.send(GameboyEvent::Framebuffer(gameboy_arc.read().unwrap().ppu.write_fb())).unwrap();
|
||||
let delta = goal - now;
|
||||
let delta_ms = delta.whole_milliseconds();
|
||||
if delta_ms > 0 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(delta_ms as u64));
|
||||
let now = time::OffsetDateTime::now_utc();
|
||||
|
||||
if let Some(debugging_tbf) = debugging_tbf {
|
||||
let delta = now - debugging_tbf;
|
||||
goal += delta;
|
||||
}
|
||||
|
||||
debugging_tbf = Some(now);
|
||||
}
|
||||
goal = goal + time::Duration::milliseconds(1000 / 60);
|
||||
|
||||
if frame_counter == 60 {
|
||||
log::debug!("Rendered 60 frames");
|
||||
frame_counter = 0;
|
||||
drop(gameboy);
|
||||
|
||||
if needs_redraw {
|
||||
let now = time::OffsetDateTime::now_utc();
|
||||
frame_counter += 1;
|
||||
tx.send(GameboyEvent::Framebuffer(
|
||||
gameboy_arc.read().unwrap().gameboy.ppu.write_fb(),
|
||||
))
|
||||
.unwrap();
|
||||
let delta = goal - now;
|
||||
let delta_ms = delta.whole_milliseconds();
|
||||
if delta_ms > 0 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(delta_ms as u64));
|
||||
}
|
||||
goal = goal + time::Duration::milliseconds(1000 / 60);
|
||||
|
||||
if frame_counter == 60 {
|
||||
log::debug!("Rendered 60 frames");
|
||||
frame_counter = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
pub mod events;
|
||||
#[cfg(feature = "debugger")]
|
||||
mod overlay;
|
||||
|
||||
use std::sync::{
|
||||
|
@ -6,7 +7,10 @@ use std::sync::{
|
|||
Arc, RwLock,
|
||||
};
|
||||
|
||||
use meowgb_core::gameboy::{serial::SerialWriter, Gameboy};
|
||||
use events::{EmulatorWindowEvent, GameboyEvent, Keymap};
|
||||
use meowgb_core::gameboy::serial::SerialWriter;
|
||||
#[cfg(feature = "debugger")]
|
||||
use overlay::Framework;
|
||||
use pixels::{Pixels, SurfaceTexture};
|
||||
use winit::{
|
||||
event::Event,
|
||||
|
@ -15,11 +19,7 @@ use winit::{
|
|||
};
|
||||
use winit_input_helper::WinitInputHelper;
|
||||
|
||||
use self::{
|
||||
events::{EmulatorWindowEvent, GameboyEvent, Keymap},
|
||||
overlay::Framework,
|
||||
};
|
||||
use crate::config::MeowGBConfig;
|
||||
use crate::{config::MeowGBConfig, WrappedGameboy};
|
||||
|
||||
macro_rules! define_keypress {
|
||||
($input:ident, $config:ident, $keymap:ident, $tx:ident, $key:ident, $event:ident) => {
|
||||
|
@ -42,10 +42,13 @@ macro_rules! define_keypress {
|
|||
pub fn run_window(
|
||||
rom_name: &str,
|
||||
config: MeowGBConfig,
|
||||
gameboy: Arc<RwLock<Gameboy<impl SerialWriter + 'static>>>,
|
||||
gameboy: Arc<RwLock<WrappedGameboy<impl SerialWriter + 'static>>>,
|
||||
rx: Receiver<GameboyEvent>,
|
||||
tx: Sender<EmulatorWindowEvent>,
|
||||
) {
|
||||
#[cfg(not(feature = "debugger"))]
|
||||
drop(gameboy);
|
||||
|
||||
let event_loop = EventLoop::new();
|
||||
let mut input = WinitInputHelper::new();
|
||||
|
||||
|
@ -53,26 +56,27 @@ pub fn run_window(
|
|||
WindowBuilder::new().with_title(format!("Meow - {}", rom_name)).build(&event_loop).unwrap()
|
||||
};
|
||||
|
||||
let (mut pixels, mut framework) = {
|
||||
let window_size = window.inner_size();
|
||||
let scale_factor = window.scale_factor() as f32;
|
||||
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
|
||||
let pixels = Pixels::new(
|
||||
meowgb_core::gameboy::ppu::FB_WIDTH,
|
||||
meowgb_core::gameboy::ppu::FB_HEIGHT,
|
||||
surface_texture,
|
||||
)
|
||||
.unwrap();
|
||||
let framework = Framework::new(
|
||||
&event_loop,
|
||||
window_size.width,
|
||||
window_size.height,
|
||||
scale_factor,
|
||||
&pixels,
|
||||
&gameboy.read().unwrap(),
|
||||
);
|
||||
(pixels, framework)
|
||||
};
|
||||
let window_size = window.inner_size();
|
||||
#[cfg(feature = "debugger")]
|
||||
let scale_factor = window.scale_factor() as f32;
|
||||
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
|
||||
let mut pixels = Pixels::new(
|
||||
meowgb_core::gameboy::ppu::FB_WIDTH,
|
||||
meowgb_core::gameboy::ppu::FB_HEIGHT,
|
||||
surface_texture,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(feature = "debugger")]
|
||||
let mut framework = Framework::new(
|
||||
&event_loop,
|
||||
window_size.width,
|
||||
window_size.height,
|
||||
scale_factor,
|
||||
&pixels,
|
||||
&gameboy.read().unwrap(),
|
||||
tx.clone(),
|
||||
);
|
||||
|
||||
let mut redraw_happened = true;
|
||||
let mut fb: Option<Vec<u8>> = None;
|
||||
|
@ -87,19 +91,34 @@ pub fn run_window(
|
|||
return;
|
||||
}
|
||||
|
||||
if input.key_pressed(config.bindings.pause) {
|
||||
tx.send(EmulatorWindowEvent::PauseToggle).unwrap();
|
||||
}
|
||||
|
||||
// if input.key_pressed(config.bindings.pause) {
|
||||
// tx.send(EmulatorWindowEvent::PauseToggle).unwrap();
|
||||
// }
|
||||
#[cfg(feature = "debugger")]
|
||||
if let Some(debug_menu) = config.bindings.debug_menu {
|
||||
if input.key_pressed(debug_menu) {
|
||||
framework.gui.window_open = !framework.gui.window_open;
|
||||
if !framework.gui.state.any_open() {
|
||||
if let Some(old_state) = framework.gui.state_restore.take() {
|
||||
framework.gui.state = old_state;
|
||||
}
|
||||
framework.gui.state.window_open = true;
|
||||
} else {
|
||||
framework.gui.state_restore = Some(framework.gui.state);
|
||||
framework.gui.state.close_all();
|
||||
}
|
||||
redraw_happened = true;
|
||||
}
|
||||
}
|
||||
if input.key_pressed(config.bindings.pause) {}
|
||||
|
||||
#[cfg(feature = "debugger")]
|
||||
if let Some(scale_factor) = input.scale_factor() {
|
||||
framework.scale_factor(scale_factor);
|
||||
redraw_happened = true;
|
||||
}
|
||||
#[cfg(not(feature = "debugger"))]
|
||||
{
|
||||
redraw_happened |= input.scale_factor().is_some();
|
||||
}
|
||||
|
||||
define_keypress!(input, config, keymap, tx, a, AToggle);
|
||||
|
@ -113,8 +132,9 @@ pub fn run_window(
|
|||
}
|
||||
|
||||
match event {
|
||||
#[cfg(feature = "debugger")]
|
||||
Event::WindowEvent { event, .. } => {
|
||||
framework.handle_event(&event);
|
||||
redraw_happened |= framework.handle_event(&event);
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
let frame = pixels.frame_mut();
|
||||
|
@ -130,12 +150,14 @@ pub fn run_window(
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debugger")]
|
||||
framework.prepare(&window, &gameboy.read().unwrap());
|
||||
|
||||
let render_result = pixels.render_with(|encoder, render_target, context| {
|
||||
// Render the world texture
|
||||
context.scaling_renderer.render(encoder, render_target);
|
||||
|
||||
#[cfg(feature = "debugger")]
|
||||
// Render egui
|
||||
framework.render(encoder, render_target, context);
|
||||
|
||||
|
@ -154,22 +176,23 @@ pub fn run_window(
|
|||
|
||||
if let Some(size) = input.window_resized() {
|
||||
pixels.resize_surface(size.width, size.height).unwrap();
|
||||
#[cfg(feature = "debugger")]
|
||||
framework.resize(size.width, size.height);
|
||||
window.request_redraw();
|
||||
redraw_happened = false;
|
||||
redraw_happened = true;
|
||||
}
|
||||
|
||||
while let Ok(event) = rx.try_recv() {
|
||||
match event {
|
||||
GameboyEvent::Framebuffer(buf) => {
|
||||
fb = Some(buf);
|
||||
|
||||
if redraw_happened {
|
||||
window.request_redraw();
|
||||
redraw_happened = false;
|
||||
}
|
||||
redraw_happened = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if redraw_happened {
|
||||
window.request_redraw();
|
||||
redraw_happened = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -12,10 +12,17 @@ pub enum EmulatorWindowEvent {
|
|||
DownToggle,
|
||||
LeftToggle,
|
||||
RightToggle,
|
||||
PauseToggle,
|
||||
Debug(EmulatorDebugEvent),
|
||||
Exit,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum EmulatorDebugEvent {
|
||||
Step,
|
||||
Continue,
|
||||
ToggleBreakpoint(u16, [bool; 3]),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum GameboyEvent {
|
||||
Framebuffer(Vec<u8>),
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
/// Provides an [egui] based overlay for debugigng the emulator whilst it is
|
||||
/// running
|
||||
use egui::{ClippedPrimitive, Context, TexturesDelta};
|
||||
use egui::{ClippedPrimitive, Context, Grid, TexturesDelta};
|
||||
use egui_wgpu::renderer::{Renderer, ScreenDescriptor};
|
||||
use meowgb_core::gameboy::{serial::SerialWriter, Gameboy};
|
||||
use meowgb_core::gameboy::serial::SerialWriter;
|
||||
use pixels::{wgpu, PixelsContext};
|
||||
use winit::{event_loop::EventLoopWindowTarget, window::Window};
|
||||
|
||||
use super::events::{EmulatorDebugEvent, EmulatorWindowEvent};
|
||||
use crate::WrappedGameboy;
|
||||
|
||||
pub(crate) struct Framework {
|
||||
egui_ctx: Context,
|
||||
egui_state: egui_winit::State,
|
||||
|
@ -16,13 +19,55 @@ pub(crate) struct Framework {
|
|||
pub gui: Gui,
|
||||
}
|
||||
|
||||
pub struct Gui {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct GuiWindowState {
|
||||
pub window_open: bool,
|
||||
pub register_window_open: bool,
|
||||
pub ppu_register_window_open: bool,
|
||||
pub debugger_window_open: bool,
|
||||
pub wram_window_open: bool,
|
||||
pub oam_window_open: bool,
|
||||
pub hram_window_open: bool,
|
||||
}
|
||||
|
||||
impl GuiWindowState {
|
||||
pub fn close_all(&mut self) {
|
||||
self.ppu_register_window_open = false;
|
||||
self.register_window_open = false;
|
||||
self.window_open = false;
|
||||
self.debugger_window_open = false;
|
||||
self.wram_window_open = false;
|
||||
self.oam_window_open = false;
|
||||
self.hram_window_open = false;
|
||||
}
|
||||
|
||||
pub fn any_open(&self) -> bool {
|
||||
self.window_open
|
||||
|| self.register_window_open
|
||||
|| self.ppu_register_window_open
|
||||
|| self.debugger_window_open
|
||||
|| self.wram_window_open
|
||||
|| self.oam_window_open
|
||||
|| self.hram_window_open
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Gui {
|
||||
pub state: GuiWindowState,
|
||||
pub state_restore: Option<GuiWindowState>,
|
||||
pub registers: meowgb_core::gameboy::cpu::Registers,
|
||||
pub ppu_registers: meowgb_core::gameboy::ppu::PpuRegisters,
|
||||
pub wram: [u8; 0x2000],
|
||||
pub hram: [u8; 0xAF],
|
||||
// pub vram: [u8; 0x2000],
|
||||
pub oam: [u8; 0xA0],
|
||||
pub bp_string: String,
|
||||
pub bp_read_checkbox: bool,
|
||||
pub bp_write_checkbox: bool,
|
||||
pub bp_execute_checkbox: bool,
|
||||
pub is_debugging: bool,
|
||||
pub breakpoints: [[bool; 3]; 0x10000],
|
||||
pub sender: std::sync::mpsc::Sender<EmulatorWindowEvent>,
|
||||
}
|
||||
|
||||
impl Framework {
|
||||
|
@ -32,7 +77,8 @@ impl Framework {
|
|||
height: u32,
|
||||
scale_factor: f32,
|
||||
pixels: &pixels::Pixels,
|
||||
gameboy: &Gameboy<impl SerialWriter>,
|
||||
gameboy: &WrappedGameboy<impl SerialWriter>,
|
||||
sender: std::sync::mpsc::Sender<EmulatorWindowEvent>,
|
||||
) -> Self {
|
||||
let max_texture_size = pixels.device().limits().max_texture_dimension_2d as usize;
|
||||
|
||||
|
@ -44,7 +90,7 @@ impl Framework {
|
|||
ScreenDescriptor { size_in_pixels: [width, height], pixels_per_point: scale_factor };
|
||||
let renderer = Renderer::new(pixels.device(), pixels.render_texture_format(), None, 1);
|
||||
let textures = TexturesDelta::default();
|
||||
let gui = Gui::new(gameboy);
|
||||
let gui = Gui::new(gameboy, sender);
|
||||
|
||||
Self {
|
||||
egui_ctx,
|
||||
|
@ -57,8 +103,8 @@ impl Framework {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_event(&mut self, event: &winit::event::WindowEvent) {
|
||||
let _ = self.egui_state.on_event(&self.egui_ctx, event);
|
||||
pub(crate) fn handle_event(&mut self, event: &winit::event::WindowEvent) -> bool {
|
||||
self.egui_state.on_event(&self.egui_ctx, event).repaint
|
||||
}
|
||||
|
||||
pub(crate) fn resize(&mut self, width: u32, height: u32) {
|
||||
|
@ -71,9 +117,13 @@ impl Framework {
|
|||
self.screen_descriptor.pixels_per_point = scale_factor as f32;
|
||||
}
|
||||
|
||||
pub(crate) fn prepare(&mut self, window: &Window, gameboy: &Gameboy<impl SerialWriter>) {
|
||||
self.gui.registers = gameboy.registers;
|
||||
self.gui.ppu_registers = gameboy.ppu.registers;
|
||||
pub(crate) fn prepare(&mut self, window: &Window, gameboy: &WrappedGameboy<impl SerialWriter>) {
|
||||
self.gui.registers = gameboy.gameboy.registers;
|
||||
self.gui.ppu_registers = gameboy.gameboy.ppu.registers;
|
||||
self.gui.is_debugging = gameboy.debugging;
|
||||
self.gui.oam = gameboy.gameboy.ppu.oam;
|
||||
self.gui.hram = gameboy.gameboy.memory.hram;
|
||||
self.gui.wram = gameboy.gameboy.memory.wram;
|
||||
|
||||
// Run the egui frame and create all paint jobs to prepare for rendering.
|
||||
let raw_input = self.egui_state.take_egui_input(window);
|
||||
|
@ -126,62 +176,194 @@ impl Framework {
|
|||
}
|
||||
|
||||
impl Gui {
|
||||
fn new(gameboy: &Gameboy<impl SerialWriter>) -> Self {
|
||||
fn new(
|
||||
gameboy: &WrappedGameboy<impl SerialWriter>,
|
||||
sender: std::sync::mpsc::Sender<EmulatorWindowEvent>,
|
||||
) -> Self {
|
||||
Self {
|
||||
window_open: false,
|
||||
register_window_open: false,
|
||||
ppu_register_window_open: false,
|
||||
registers: gameboy.registers,
|
||||
ppu_registers: gameboy.ppu.registers,
|
||||
state: GuiWindowState {
|
||||
window_open: gameboy.debugging,
|
||||
register_window_open: false,
|
||||
ppu_register_window_open: false,
|
||||
debugger_window_open: gameboy.debugging,
|
||||
wram_window_open: false,
|
||||
oam_window_open: false,
|
||||
hram_window_open: false,
|
||||
},
|
||||
state_restore: None,
|
||||
registers: gameboy.gameboy.registers,
|
||||
ppu_registers: gameboy.gameboy.ppu.registers,
|
||||
bp_string: String::with_capacity(16),
|
||||
breakpoints: [[false, false, false]; 0x10000],
|
||||
bp_read_checkbox: false,
|
||||
bp_write_checkbox: false,
|
||||
bp_execute_checkbox: false,
|
||||
sender,
|
||||
is_debugging: gameboy.debugging,
|
||||
wram: gameboy.gameboy.memory.wram,
|
||||
hram: gameboy.gameboy.memory.hram,
|
||||
oam: gameboy.gameboy.ppu.oam,
|
||||
}
|
||||
}
|
||||
|
||||
fn ui(&mut self, ctx: &Context) {
|
||||
egui::Window::new("MeowGB Debugger").open(&mut self.window_open).show(ctx, |ui| {
|
||||
egui::Window::new("MeowGB Debugger").open(&mut self.state.window_open).show(ctx, |ui| {
|
||||
if ui.button("Toggle Debugger Window").clicked() {
|
||||
self.state.debugger_window_open = !self.state.debugger_window_open;
|
||||
}
|
||||
|
||||
if ui.button("Toggle Register Window").clicked() {
|
||||
self.register_window_open = !self.register_window_open;
|
||||
self.state.register_window_open = !self.state.register_window_open;
|
||||
}
|
||||
|
||||
if ui.button("Toggle PPU Window").clicked() {
|
||||
self.ppu_register_window_open = !self.ppu_register_window_open;
|
||||
}
|
||||
|
||||
if ui.button("Toggle OAM Window").clicked() {
|
||||
self.ppu_register_window_open = !self.ppu_register_window_open;
|
||||
self.state.ppu_register_window_open = !self.state.ppu_register_window_open;
|
||||
}
|
||||
|
||||
if ui.button("Toggle BG Tiles Window").clicked() {
|
||||
self.ppu_register_window_open = !self.ppu_register_window_open;
|
||||
self.state.ppu_register_window_open = !self.state.ppu_register_window_open;
|
||||
}
|
||||
|
||||
if ui.button("Toggle WRAM Window").clicked() {
|
||||
self.state.wram_window_open = !self.state.wram_window_open;
|
||||
}
|
||||
|
||||
if ui.button("Toggle HRAM Window").clicked() {
|
||||
self.state.hram_window_open = !self.state.hram_window_open;
|
||||
}
|
||||
|
||||
if ui.button("Toggle OAM Window").clicked() {
|
||||
self.state.oam_window_open = !self.state.oam_window_open;
|
||||
}
|
||||
});
|
||||
|
||||
egui::Window::new("Register State").open(&mut self.register_window_open).show(ctx, |ui| {
|
||||
ui.label(format!("AF: {:04X}", self.registers.get_af()));
|
||||
ui.label(format!("BC: {:04X}", self.registers.get_bc()));
|
||||
ui.label(format!("DE: {:04X}", self.registers.get_de()));
|
||||
ui.label(format!("HL: {:04X}", self.registers.get_hl()));
|
||||
ui.label(format!("SP: {:04X}", self.registers.get_sp()));
|
||||
ui.label(format!("PC: {:04X}", self.registers.pc));
|
||||
egui::Window::new("Register State").open(&mut self.state.register_window_open).show(
|
||||
ctx,
|
||||
|ui| {
|
||||
ui.label(format!("AF: {:04X}", self.registers.get_af()));
|
||||
ui.label(format!("BC: {:04X}", self.registers.get_bc()));
|
||||
ui.label(format!("DE: {:04X}", self.registers.get_de()));
|
||||
ui.label(format!("HL: {:04X}", self.registers.get_hl()));
|
||||
ui.label(format!("SP: {:04X}", self.registers.get_sp()));
|
||||
ui.label(format!("PC: {:04X}", self.registers.pc));
|
||||
},
|
||||
);
|
||||
|
||||
egui::Window::new("Debugger").open(&mut self.state.debugger_window_open).show(ctx, |ui| {
|
||||
if ui.button("Step").clicked() {
|
||||
let _ = self.sender.send(EmulatorWindowEvent::Debug(EmulatorDebugEvent::Step));
|
||||
}
|
||||
if ui.button("Continue").clicked() {
|
||||
let _ = self.sender.send(EmulatorWindowEvent::Debug(EmulatorDebugEvent::Continue));
|
||||
}
|
||||
ui.label("Toggle Breakpoint");
|
||||
ui.text_edit_singleline(&mut self.bp_string);
|
||||
self.bp_string.retain(|x| x.is_ascii_hexdigit());
|
||||
if let Some((fourth_index, _)) = self.bp_string.char_indices().nth(4) {
|
||||
self.bp_string.truncate(fourth_index);
|
||||
}
|
||||
Grid::new("debugger_bp_select_grid").show(ui, |ui| {
|
||||
let address = u16::from_str_radix(self.bp_string.as_str(), 16).unwrap_or_default();
|
||||
ui.label(format!("({:#X}) ", address));
|
||||
let [read, write, execute] = &mut self.breakpoints[address as usize];
|
||||
let mut changed = ui.checkbox(read, "Read").clicked();
|
||||
changed |= ui.checkbox(write, "Write").clicked();
|
||||
changed |= ui.checkbox(execute, "Execute").clicked();
|
||||
if changed {
|
||||
let _ = self.sender.send(EmulatorWindowEvent::Debug(
|
||||
EmulatorDebugEvent::ToggleBreakpoint(
|
||||
address,
|
||||
self.breakpoints[address as usize],
|
||||
),
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
Grid::new("debugger_bp_list_grid").show(ui, |ui| {
|
||||
ui.heading("Enabled BPs");
|
||||
ui.end_row();
|
||||
for (idx, [read, write, execute]) in self.breakpoints.iter_mut().enumerate() {
|
||||
if *read || *write || *execute {
|
||||
ui.label(format!("{:#X}: ", idx));
|
||||
let mut changed = ui.checkbox(read, "Read").clicked();
|
||||
changed |= ui.checkbox(write, "Write").clicked();
|
||||
changed |= ui.checkbox(execute, "Execute").clicked();
|
||||
if changed {
|
||||
let _ = self.sender.send(EmulatorWindowEvent::Debug(
|
||||
EmulatorDebugEvent::ToggleBreakpoint(
|
||||
idx as u16,
|
||||
[*read, *write, *execute],
|
||||
),
|
||||
));
|
||||
}
|
||||
ui.end_row();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
egui::Window::new("PPU State").open(&mut self.ppu_register_window_open).show(ctx, |ui| {
|
||||
ui.label(format!("Mode: {:?}", self.ppu_registers.mode));
|
||||
ui.label(format!("LCDC: {:02X}", self.ppu_registers.lcdc));
|
||||
ui.label(format!(
|
||||
"Stat: {:02X}",
|
||||
(1 << 7)
|
||||
| self.ppu_registers.stat_flags.flag_bits()
|
||||
| ((match (self.ppu_registers.lcdc >> 7) == 1 {
|
||||
true => self.ppu_registers.ly == self.ppu_registers.lyc,
|
||||
false => self.ppu_registers.ly_lyc,
|
||||
} as u8) << 2) | self.ppu_registers.mode.mode_flag()
|
||||
));
|
||||
ui.label(format!("SCY: {:02X}", self.ppu_registers.scy));
|
||||
ui.label(format!("SCX: {:02X}", self.ppu_registers.scx));
|
||||
ui.label(format!("LY: {:02X}", self.ppu_registers.ly));
|
||||
ui.label(format!("LYC: {:02X}", self.ppu_registers.lyc));
|
||||
ui.label(format!("WY: {:02X}", self.ppu_registers.wy));
|
||||
ui.label(format!("WX: {:02X}", self.ppu_registers.wx));
|
||||
egui::Window::new("PPU State").open(&mut self.state.ppu_register_window_open).show(
|
||||
ctx,
|
||||
|ui| {
|
||||
ui.label(format!("Mode: {:?}", self.ppu_registers.mode));
|
||||
ui.label(format!("LCDC: {:02X}", self.ppu_registers.lcdc));
|
||||
ui.label(format!(
|
||||
"Stat: {:02X}",
|
||||
(1 << 7)
|
||||
| self.ppu_registers.stat_flags.flag_bits()
|
||||
| ((match (self.ppu_registers.lcdc >> 7) == 1 {
|
||||
true => self.ppu_registers.ly == self.ppu_registers.lyc,
|
||||
false => self.ppu_registers.ly_lyc,
|
||||
} as u8) << 2) | self.ppu_registers.mode.mode_flag()
|
||||
));
|
||||
ui.label(format!("SCY: {:02X}", self.ppu_registers.scy));
|
||||
ui.label(format!("SCX: {:02X}", self.ppu_registers.scx));
|
||||
ui.label(format!("LY: {:02X}", self.ppu_registers.ly));
|
||||
ui.label(format!("LYC: {:02X}", self.ppu_registers.lyc));
|
||||
ui.label(format!("WY: {:02X}", self.ppu_registers.wy));
|
||||
ui.label(format!("WX: {:02X}", self.ppu_registers.wx));
|
||||
},
|
||||
);
|
||||
|
||||
egui::Window::new("WRAM").vscroll(true).open(&mut self.state.wram_window_open).show(ctx, |ui| {
|
||||
egui::Grid::new("memory_ov_wram").show(ui, |ui| {
|
||||
ui.monospace(" 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
|
||||
for mem_row_idx in 0xC00..(0xE00) {
|
||||
let row_base = (mem_row_idx * 0x10) - 0xC000;
|
||||
ui.end_row();
|
||||
ui.monospace(format!("{:X}: {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}", mem_row_idx * 0x10, self.wram[row_base + 0], self.wram[row_base + 1], self.wram[row_base + 2], self.wram[row_base + 3], self.wram[row_base + 4], self.wram[row_base + 5], self.wram[row_base + 6], self.wram[row_base + 7], self.wram[row_base + 8], self.wram[row_base + 9], self.wram[row_base + 10], self.wram[row_base + 11], self.wram[row_base + 12], self.wram[row_base + 13], self.wram[row_base + 14], self.wram[row_base + 15]));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
egui::Window::new("HRAM").vscroll(true).open(&mut self.state.hram_window_open).show(ctx, |ui| {
|
||||
egui::Grid::new("memory_ov_hram").show(ui, |ui| {
|
||||
ui.label("ROW: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
|
||||
for mem_row_idx in 0x0..0xA {
|
||||
let row_base = mem_row_idx * 0x10;
|
||||
ui.end_row();
|
||||
ui.monospace(format!("{:X}: {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}", 0xFF80 + row_base, self.hram[row_base + 0], self.hram[row_base + 1], self.hram[row_base + 2], self.hram[row_base + 3], self.hram[row_base + 4], self.hram[row_base + 5], self.hram[row_base + 6], self.hram[row_base + 7], self.hram[row_base + 8], self.hram[row_base + 9], self.hram[row_base + 10], self.hram[row_base + 11], self.hram[row_base + 12], self.hram[row_base + 13], self.hram[row_base + 14], self.hram[row_base + 15]));
|
||||
}
|
||||
|
||||
let row_base = 0xA0;
|
||||
ui.end_row();
|
||||
ui.monospace(format!("FFF0: {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} ??", self.hram[row_base + 0], self.hram[row_base + 1], self.hram[row_base + 2], self.hram[row_base + 3], self.hram[row_base + 4], self.hram[row_base + 5], self.hram[row_base + 6], self.hram[row_base + 7], self.hram[row_base + 8], self.hram[row_base + 9], self.hram[row_base + 10], self.hram[row_base + 11], self.hram[row_base + 12], self.hram[row_base + 13], self.hram[row_base + 14]));
|
||||
});
|
||||
});
|
||||
|
||||
egui::Window::new("OAM").vscroll(true).open(&mut self.state.oam_window_open).show(ctx, |ui| {
|
||||
egui::Grid::new("memory_ov_oam").show(ui, |ui| {
|
||||
ui.label("ROW: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F");
|
||||
for mem_row_idx in 0xC00..(0xC0A) {
|
||||
let row_base = (mem_row_idx * 0x10) - 0xC000;
|
||||
ui.end_row();
|
||||
ui.label(format!("{:X}: {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X}", mem_row_idx * 0x10, self.hram[row_base + 0], self.hram[row_base + 1], self.hram[row_base + 2], self.hram[row_base + 3], self.hram[row_base + 4], self.hram[row_base + 5], self.hram[row_base + 6], self.hram[row_base + 7], self.hram[row_base + 8], self.hram[row_base + 9], self.hram[row_base + 10], self.hram[row_base + 11], self.hram[row_base + 12], self.hram[row_base + 13], self.hram[row_base + 14], self.hram[row_base + 15]));
|
||||
}
|
||||
|
||||
let row_base = 0xA0;
|
||||
ui.end_row();
|
||||
ui.label(format!("CF00: {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} {:02X} ??", self.hram[row_base + 0], self.hram[row_base + 1], self.hram[row_base + 2], self.hram[row_base + 3], self.hram[row_base + 4], self.hram[row_base + 5], self.hram[row_base + 6], self.hram[row_base + 7], self.hram[row_base + 8], self.hram[row_base + 9], self.hram[row_base + 10], self.hram[row_base + 11], self.hram[row_base + 12], self.hram[row_base + 13], self.hram[row_base + 14]));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,16 @@ else
|
|||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/di_timing-GS.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
||||
if res=$(./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/di_timing-GS.gb test -m 100000000 -s meowgb-tests/expected_output/di_timing-GS.bin 2>&1 > /dev/null) ; then
|
||||
TEST_SUCCESS=$((TEST_SUCCESS + 1))
|
||||
else
|
||||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/div_timing.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
@ -127,6 +137,36 @@ else
|
|||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/halt_ime0_nointr_timing.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
||||
if res=$(./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime0_nointr_timing.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime0_nointr_timing.bin 2>&1 > /dev/null) ; then
|
||||
TEST_SUCCESS=$((TEST_SUCCESS + 1))
|
||||
else
|
||||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/halt_ime1_timing.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
||||
if res=$(./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime1_timing.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime1_timing.bin 2>&1 > /dev/null) ; then
|
||||
TEST_SUCCESS=$((TEST_SUCCESS + 1))
|
||||
else
|
||||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/halt_ime1_timing2-GS.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
||||
if res=$(./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/halt_ime1_timing2-GS.gb test -m 100000000 -s meowgb-tests/expected_output/halt_ime1_timing2-GS.bin 2>&1 > /dev/null) ; then
|
||||
TEST_SUCCESS=$((TEST_SUCCESS + 1))
|
||||
else
|
||||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/intr_1_2_timing-GS.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
@ -187,6 +227,26 @@ else
|
|||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/pop_timing.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
||||
if res=$(./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/pop_timing.gb test -m 100000000 -s meowgb-tests/expected_output/pop_timing.bin 2>&1 > /dev/null) ; then
|
||||
TEST_SUCCESS=$((TEST_SUCCESS + 1))
|
||||
else
|
||||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/push_timing.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
||||
if res=$(./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/push_timing.gb test -m 100000000 -s meowgb-tests/expected_output/push_timing.bin 2>&1 > /dev/null) ; then
|
||||
TEST_SUCCESS=$((TEST_SUCCESS + 1))
|
||||
else
|
||||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/rapid_di_ei.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
@ -227,6 +287,16 @@ else
|
|||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/rst_timing.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
||||
if res=$(./target/release/meowgb-tests test-roms/mooneye-test-suite/roms/rst_timing.gb test -m 100000000 -s meowgb-tests/expected_output/rst_timing.bin 2>&1 > /dev/null) ; then
|
||||
TEST_SUCCESS=$((TEST_SUCCESS + 1))
|
||||
else
|
||||
echo "Failed: $res"
|
||||
fi
|
||||
|
||||
echo "Running test ROM ./test-roms/mooneye-test-suite/roms/stat_irq_blocking.gb"
|
||||
|
||||
TEST_TOTAL=$((TEST_TOTAL + 1))
|
||||
|
|
BIN
test-roms/mooneye-test-suite/roms/di_timing-GS.gb
Normal file
BIN
test-roms/mooneye-test-suite/roms/di_timing-GS.gb
Normal file
Binary file not shown.
BIN
test-roms/mooneye-test-suite/roms/halt_ime0_nointr_timing.gb
Normal file
BIN
test-roms/mooneye-test-suite/roms/halt_ime0_nointr_timing.gb
Normal file
Binary file not shown.
BIN
test-roms/mooneye-test-suite/roms/halt_ime1_timing.gb
Normal file
BIN
test-roms/mooneye-test-suite/roms/halt_ime1_timing.gb
Normal file
Binary file not shown.
BIN
test-roms/mooneye-test-suite/roms/halt_ime1_timing2-GS.gb
Normal file
BIN
test-roms/mooneye-test-suite/roms/halt_ime1_timing2-GS.gb
Normal file
Binary file not shown.
BIN
test-roms/mooneye-test-suite/roms/pop_timing.gb
Normal file
BIN
test-roms/mooneye-test-suite/roms/pop_timing.gb
Normal file
Binary file not shown.
BIN
test-roms/mooneye-test-suite/roms/push_timing.gb
Normal file
BIN
test-roms/mooneye-test-suite/roms/push_timing.gb
Normal file
Binary file not shown.
BIN
test-roms/mooneye-test-suite/roms/rst_timing.gb
Normal file
BIN
test-roms/mooneye-test-suite/roms/rst_timing.gb
Normal file
Binary file not shown.
7
tests.md
7
tests.md
|
@ -12,21 +12,28 @@
|
|||
* boot_hwio-dmgABCmgb.gb - [ROM](./test-roms/mooneye-test-suite/roms/boot_hwio-dmgABCmgb.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/boot_hwio-dmgABCmgb.bin)
|
||||
* boot_regs-dmgABC.gb - [ROM](./test-roms/mooneye-test-suite/roms/boot_regs-dmgABC.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/boot_regs-dmgABC.bin)
|
||||
* daa.gb - [ROM](./test-roms/mooneye-test-suite/roms/daa.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/daa.bin)
|
||||
* di_timing-GS.gb - [ROM](./test-roms/mooneye-test-suite/roms/di_timing-GS.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/di_timing-GS.bin)
|
||||
* div_timing.gb - [ROM](./test-roms/mooneye-test-suite/roms/div_timing.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/div_timing.bin)
|
||||
* div_write.gb - [ROM](./test-roms/mooneye-test-suite/roms/div_write.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/div_write.bin)
|
||||
* ei_sequence.gb - [ROM](./test-roms/mooneye-test-suite/roms/ei_sequence.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/ei_sequence.bin)
|
||||
* ei_timing.gb - [ROM](./test-roms/mooneye-test-suite/roms/ei_timing.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/ei_timing.bin)
|
||||
* halt_ime0_ei.gb - [ROM](./test-roms/mooneye-test-suite/roms/halt_ime0_ei.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/halt_ime0_ei.bin)
|
||||
* halt_ime0_nointr_timing.gb - [ROM](./test-roms/mooneye-test-suite/roms/halt_ime0_nointr_timing.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/halt_ime0_nointr_timing.bin)
|
||||
* halt_ime1_timing.gb - [ROM](./test-roms/mooneye-test-suite/roms/halt_ime1_timing.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/halt_ime1_timing.bin)
|
||||
* halt_ime1_timing2-GS.gb - [ROM](./test-roms/mooneye-test-suite/roms/halt_ime1_timing2-GS.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/halt_ime1_timing2-GS.bin)
|
||||
* intr_1_2_timing-GS.gb - [ROM](./test-roms/mooneye-test-suite/roms/intr_1_2_timing-GS.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/intr_1_2_timing-GS.bin)
|
||||
* intr_2_0_timing.gb - [ROM](./test-roms/mooneye-test-suite/roms/intr_2_0_timing.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/intr_2_0_timing.bin)
|
||||
* mem_oam.gb - [ROM](./test-roms/mooneye-test-suite/roms/mem_oam.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/mem_oam.bin)
|
||||
* oam_dma_restart.gb - [ROM](./test-roms/mooneye-test-suite/roms/oam_dma_restart.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/oam_dma_restart.bin)
|
||||
* oam_dma_start.gb - [ROM](./test-roms/mooneye-test-suite/roms/oam_dma_start.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/oam_dma_start.bin)
|
||||
* oam_dma_timing.gb - [ROM](./test-roms/mooneye-test-suite/roms/oam_dma_timing.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/oam_dma_timing.bin)
|
||||
* pop_timing.gb - [ROM](./test-roms/mooneye-test-suite/roms/pop_timing.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/pop_timing.bin)
|
||||
* push_timing.gb - [ROM](./test-roms/mooneye-test-suite/roms/push_timing.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/push_timing.bin)
|
||||
* rapid_di_ei.gb - [ROM](./test-roms/mooneye-test-suite/roms/rapid_di_ei.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/rapid_di_ei.bin)
|
||||
* rapid_toggle.gb - [ROM](./test-roms/mooneye-test-suite/roms/rapid_toggle.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/rapid_toggle.bin)
|
||||
* reg_f.gb - [ROM](./test-roms/mooneye-test-suite/roms/reg_f.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/reg_f.bin)
|
||||
* reg_read.gb - [ROM](./test-roms/mooneye-test-suite/roms/reg_read.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/reg_read.bin)
|
||||
* rst_timing.gb - [ROM](./test-roms/mooneye-test-suite/roms/rst_timing.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/rst_timing.bin)
|
||||
* stat_irq_blocking.gb - [ROM](./test-roms/mooneye-test-suite/roms/stat_irq_blocking.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/stat_irq_blocking.bin)
|
||||
* stat_lyc_onoff.gb - [ROM](./test-roms/mooneye-test-suite/roms/stat_lyc_onoff.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/stat_lyc_onoff.bin)
|
||||
* tim00.gb - [ROM](./test-roms/mooneye-test-suite/roms/tim00.gb) - [Expected Serial Output](./meowgb-tests/expected_output/MBC1/tim00.bin)
|
||||
|
|
Loading…
Reference in a new issue