use crate::device; use crate::ui; use governor::clock::Clock; const VI_STATUS_REG: u32 = 0; //const VI_ORIGIN_REG: u32 = 1; pub const VI_WIDTH_REG: u32 = 2; //const VI_V_INTR_REG: u32 = 3; const VI_CURRENT_REG: u32 = 4; //const VI_BURST_REG: u32 = 5; const VI_V_SYNC_REG: u32 = 6; const VI_H_SYNC_REG: u32 = 7; //const VI_LEAP_REG: u32 = 8; pub const VI_H_START_REG: u32 = 9; pub const VI_V_START_REG: u32 = 10; //const VI_V_BURST_REG: u32 = 11; pub const VI_X_SCALE_REG: u32 = 12; pub const VI_Y_SCALE_REG: u32 = 13; pub const VI_REGS_COUNT: u32 = 14; pub struct Vi { pub regs: [u32; VI_REGS_COUNT as usize], pub clock: u64, pub delay: u64, pub field: u32, pub limiter: Option, pub count_per_scanline: u64, pub vi_counter: u64, } //static mut FRAME_COUNTER: u64 = 0; const LIMIT_BUFFER: u64 = 3; pub fn set_expected_refresh_rate(device: &mut device::Device) { let expected_refresh_rate = device.vi.clock as f64 / (device.vi.regs[VI_V_SYNC_REG as usize] + 1) as f64 / ((device.vi.regs[VI_H_SYNC_REG as usize] & 0xFFF) + 1) as f64 * 2.0; device.vi.delay = (device.cpu.clock_rate as f64 / expected_refresh_rate) as u64; device.vi.count_per_scanline = device.vi.delay / (device.vi.regs[VI_V_SYNC_REG as usize] + 1) as u64; let quota = governor::Quota::with_period(std::time::Duration::from_secs_f64( (1.0 / expected_refresh_rate) * LIMIT_BUFFER as f64, )) .unwrap(); device.vi.limiter = Some(governor::RateLimiter::direct(quota)) } pub fn set_vertical_interrupt(device: &mut device::Device) { if device::events::get_event(device, device::events::EventType::VI).is_none() { device::events::create_event( device, device::events::EventType::VI, device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize] + device.vi.delay, vertical_interrupt_event, ) } } pub fn set_current_line(device: &mut device::Device) { let delay = device.vi.delay; let next_vi = device::events::get_event(device, device::events::EventType::VI); if next_vi.is_some() { device.vi.regs[VI_CURRENT_REG as usize] = ((delay - (next_vi.unwrap().count - device.cpu.cop0.regs[device::cop0::COP0_COUNT_REG as usize])) / device.vi.count_per_scanline) as u32; // wrap around VI_CURRENT_REG if needed if device.vi.regs[VI_CURRENT_REG as usize] >= device.vi.regs[VI_V_SYNC_REG as usize] { device.vi.regs[VI_CURRENT_REG as usize] -= device.vi.regs[VI_V_SYNC_REG as usize] } } /* update current field */ device.vi.regs[VI_CURRENT_REG as usize] = (device.vi.regs[VI_CURRENT_REG as usize] & !1) | device.vi.field } pub fn read_regs( device: &mut device::Device, address: u64, _access_size: device::memory::AccessSize, ) -> u32 { let reg = (address & 0xFFFF) >> 2; if reg as u32 == VI_CURRENT_REG { set_current_line(device) } device.vi.regs[reg as usize] } pub fn write_regs(device: &mut device::Device, address: u64, value: u32, mask: u32) { let reg = (address & 0xFFFF) >> 2; match reg as u32 { VI_CURRENT_REG => device::mi::clear_rcp_interrupt(device, device::mi::MI_INTR_VI), VI_V_SYNC_REG => { if device.vi.regs[reg as usize] != value & mask { device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask); set_vertical_interrupt(device); set_expected_refresh_rate(device); } } VI_H_SYNC_REG => { if device.vi.regs[reg as usize] != value & mask { device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask); set_expected_refresh_rate(device); } } _ => { device::memory::masked_write_32(&mut device.vi.regs[reg as usize], value, mask); } } ui::video::set_register(reg as u32, device.vi.regs[reg as usize]) } pub fn vertical_interrupt_event(device: &mut device::Device) { device.cpu.running = ui::video::update_screen(); /* unsafe { FRAME_COUNTER += 1; } */ device.vi.vi_counter += 1; if device.vi.vi_counter % LIMIT_BUFFER == 0 { speed_limiter(device); } /* toggle vi field if in interlaced mode */ device.vi.field ^= (device.vi.regs[VI_STATUS_REG as usize] >> 6) & 0x1; device::mi::set_rcp_interrupt(device, device::mi::MI_INTR_VI); device::events::create_event( device, device::events::EventType::VI, device.cpu.next_event_count + device.vi.delay, vertical_interrupt_event, ) } pub fn init(device: &mut device::Device) { if device.cart.pal { device.vi.clock = 49656530 } else { device.vi.clock = 48681812 } /* std::thread::spawn(move || { let mut last_count = 0; loop { unsafe { println!("{:?}", FRAME_COUNTER - last_count); last_count = FRAME_COUNTER; } std::thread::sleep(std::time::Duration::from_secs(1)); } }); */ } pub fn speed_limiter(device: &device::Device) { let result = device.vi.limiter.as_ref().unwrap().check(); if result.is_err() { let outcome = result.unwrap_err(); let dur = outcome.wait_time_from(governor::clock::DefaultClock::default().now()); spin_sleep::sleep(dur); device.vi.limiter.as_ref().unwrap().check().unwrap(); } }