core/gpu: Add a test for the gpu state machine.

This test also caught a bug where hblank flag from the last
vblank-hblank would linger on the next HDraw.


Former-commit-id: b8916badc4bd874d9493229299150f6b2f00f5ed
Former-commit-id: 8ada7ccf3e65e7272d9bb828101520f1589cb4c4
This commit is contained in:
Michel Heily 2020-05-20 22:35:53 +03:00
parent b60ad3114c
commit 9607b16e4f
3 changed files with 104 additions and 5 deletions

View file

@ -145,7 +145,7 @@ impl GameBoyAdvance {
pub fn frame(&mut self) {
self.key_poll();
let mut remaining_cycles = 280896 - self.overshoot_cycles;
let mut remaining_cycles = CYCLES_FULL_REFRESH - self.overshoot_cycles;
while remaining_cycles > 0 {
let cycles = self.step();

View file

@ -48,6 +48,8 @@ pub mod consts {
pub(super) const CYCLES_VDRAW: usize = 197120;
pub(super) const CYCLES_VBLANK: usize = 83776;
pub const CYCLES_FULL_REFRESH: usize = 280896;
pub const TILE_SIZE: u32 = 0x20;
}
pub use self::consts::*;
@ -507,6 +509,7 @@ impl Gpu {
} else {
self.update_vcount(0, irqs);
self.dispstat.set_vblank_flag(false);
self.dispstat.set_hblank_flag(false);
self.render_scanline();
self.cycles_left_for_current_state = CYCLES_HDRAW;
self.state = HDraw;
@ -541,3 +544,99 @@ impl Gpu {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct NopDmaNotifer;
impl DmaNotifer for NopDmaNotifer {
fn notify(&mut self, _timing: u16) {}
}
#[derive(Default)]
struct TestVideoInterface {
frame_counter: usize,
}
impl VideoInterface for TestVideoInterface {
fn render(&mut self, _buffer: &[u32]) {
self.frame_counter += 1;
}
}
#[test]
fn test_gpu_state_machine() {
let mut gpu = Gpu::new();
let video = Rc::new(RefCell::new(TestVideoInterface::default()));
let video_clone: VideoDeviceRcRefCell = video.clone();
let mut irqs = IrqBitmask(0);
let mut dma_notifier = NopDmaNotifer;
let mut cycles_to_next_event = CYCLES_FULL_REFRESH;
gpu.dispstat.set_vcount_setting(0);
gpu.dispstat.set_vcount_irq_enable(true);
let mut total_cycles = 0;
macro_rules! update {
($cycles:expr) => {
gpu.update(
$cycles,
&mut irqs,
&mut cycles_to_next_event,
&mut dma_notifier,
&video_clone,
);
total_cycles += $cycles;
};
}
for line in 0..160 {
println!("line = {}", line);
assert_eq!(video.borrow().frame_counter, 0);
assert_eq!(gpu.vcount, line);
assert_eq!(gpu.state, GpuState::HDraw);
assert_eq!(gpu.dispstat.get_hblank_flag(), false);
assert_eq!(gpu.dispstat.get_vblank_flag(), false);
update!(CYCLES_HDRAW);
assert_eq!(gpu.state, GpuState::HBlank);
assert_eq!(gpu.dispstat.get_hblank_flag(), true);
assert_eq!(gpu.dispstat.get_vblank_flag(), false);
update!(CYCLES_HBLANK);
assert_eq!(irqs.LCD_VCounterMatch(), false);
}
assert_eq!(video.borrow().frame_counter, 1);
for line in 0..68 {
println!("line = {}", 160 + line);
assert_eq!(gpu.dispstat.get_hblank_flag(), false);
assert_eq!(gpu.dispstat.get_vblank_flag(), true);
assert_eq!(gpu.state, GpuState::VBlankHDraw);
update!(CYCLES_HDRAW);
assert_eq!(gpu.dispstat.get_hblank_flag(), true);
assert_eq!(gpu.dispstat.get_vblank_flag(), true);
assert_eq!(gpu.state, GpuState::VBlankHBlank);
assert_eq!(irqs.LCD_VCounterMatch(), false);
update!(CYCLES_HBLANK);
}
assert_eq!(video.borrow().frame_counter, 1);
assert_eq!(total_cycles, CYCLES_FULL_REFRESH);
assert_eq!(irqs.LCD_VCounterMatch(), true);
assert_eq!(gpu.cycles_left_for_current_state, CYCLES_HDRAW);
assert_eq!(gpu.state, GpuState::HDraw);
assert_eq!(gpu.vcount, 0);
assert_eq!(gpu.dispstat.get_vcount_flag(), true);
assert_eq!(gpu.dispstat.get_hblank_flag(), false);
}
}

View file

@ -91,10 +91,10 @@ bitfield! {
pub get_vblank_flag, set_vblank_flag: 0;
pub get_hblank_flag, set_hblank_flag: 1;
pub get_vcount_flag, set_vcount_flag: 2;
pub vblank_irq_enable, _ : 3;
pub hblank_irq_enable, _ : 4;
pub vcount_irq_enable, _ : 5;
pub vcount_setting, _ : 15, 8;
pub vblank_irq_enable, set_vblank_irq_enable : 3;
pub hblank_irq_enable, set_hblank_irq_enable : 4;
pub vcount_irq_enable, set_vcount_irq_enable : 5;
pub vcount_setting, set_vcount_setting : 15, 8;
}
bitfield! {