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:
parent
b60ad3114c
commit
9607b16e4f
|
@ -145,7 +145,7 @@ impl GameBoyAdvance {
|
||||||
pub fn frame(&mut self) {
|
pub fn frame(&mut self) {
|
||||||
self.key_poll();
|
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 {
|
while remaining_cycles > 0 {
|
||||||
let cycles = self.step();
|
let cycles = self.step();
|
||||||
|
|
|
@ -48,6 +48,8 @@ pub mod consts {
|
||||||
pub(super) const CYCLES_VDRAW: usize = 197120;
|
pub(super) const CYCLES_VDRAW: usize = 197120;
|
||||||
pub(super) const CYCLES_VBLANK: usize = 83776;
|
pub(super) const CYCLES_VBLANK: usize = 83776;
|
||||||
|
|
||||||
|
pub const CYCLES_FULL_REFRESH: usize = 280896;
|
||||||
|
|
||||||
pub const TILE_SIZE: u32 = 0x20;
|
pub const TILE_SIZE: u32 = 0x20;
|
||||||
}
|
}
|
||||||
pub use self::consts::*;
|
pub use self::consts::*;
|
||||||
|
@ -507,6 +509,7 @@ impl Gpu {
|
||||||
} else {
|
} else {
|
||||||
self.update_vcount(0, irqs);
|
self.update_vcount(0, irqs);
|
||||||
self.dispstat.set_vblank_flag(false);
|
self.dispstat.set_vblank_flag(false);
|
||||||
|
self.dispstat.set_hblank_flag(false);
|
||||||
self.render_scanline();
|
self.render_scanline();
|
||||||
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
self.cycles_left_for_current_state = CYCLES_HDRAW;
|
||||||
self.state = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -91,10 +91,10 @@ bitfield! {
|
||||||
pub get_vblank_flag, set_vblank_flag: 0;
|
pub get_vblank_flag, set_vblank_flag: 0;
|
||||||
pub get_hblank_flag, set_hblank_flag: 1;
|
pub get_hblank_flag, set_hblank_flag: 1;
|
||||||
pub get_vcount_flag, set_vcount_flag: 2;
|
pub get_vcount_flag, set_vcount_flag: 2;
|
||||||
pub vblank_irq_enable, _ : 3;
|
pub vblank_irq_enable, set_vblank_irq_enable : 3;
|
||||||
pub hblank_irq_enable, _ : 4;
|
pub hblank_irq_enable, set_hblank_irq_enable : 4;
|
||||||
pub vcount_irq_enable, _ : 5;
|
pub vcount_irq_enable, set_vcount_irq_enable : 5;
|
||||||
pub vcount_setting, _ : 15, 8;
|
pub vcount_setting, set_vcount_setting : 15, 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
bitfield! {
|
bitfield! {
|
||||||
|
|
Reference in a new issue