feat: improve debugger

This commit is contained in:
EliseZeroTwo 2024-01-12 12:19:45 -07:00
parent c37d5d5e84
commit a2156ec7f4
Signed by: elise
GPG key ID: FA8F56FFFE6E8B3E
31 changed files with 615 additions and 144 deletions

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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 {

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}
}
}

View file

@ -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,

View file

@ -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

View file

@ -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(())
}
}

View file

@ -0,0 +1 @@
 "

View file

@ -0,0 +1 @@
 "

View file

@ -0,0 +1 @@
 "

View file

@ -0,0 +1 @@
 "

View file

@ -0,0 +1 @@
 "

View file

@ -0,0 +1 @@
 "

View file

@ -0,0 +1 @@
 "

View file

@ -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" }

View file

@ -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;
}
}
}
}

View file

@ -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;
}
});
}

View file

@ -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>),

View file

@ -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]));
});
});
}
}

View file

@ -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))

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -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)