This repository has been archived on 2026-08-27. You can view files and clone it, but cannot push or open issues or pull requests.
gopher64-netplay/src/device/vi.rs
Logan McNaughton 2c677ddb0c make func
2023-10-06 21:39:54 -06:00

188 lines
6.6 KiB
Rust

use crate::device;
use crate::ui;
pub const VI_STATUS_REG: u32 = 0;
//pub const VI_ORIGIN_REG: u32 = 1;
//pub const VI_WIDTH_REG: u32 = 2;
pub const VI_V_INTR_REG: u32 = 3;
pub const VI_CURRENT_REG: u32 = 4;
//pub const VI_BURST_REG: u32 = 5;
pub const VI_V_SYNC_REG: u32 = 6;
pub const VI_H_SYNC_REG: u32 = 7;
//pub const VI_LEAP_REG: u32 = 8;
//pub const VI_H_START_REG: u32 = 9;
//pub const VI_V_START_REG: u32 = 10;
//pub 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 holdover: std::time::Duration,
pub count_per_scanline: u64,
pub last_vi_time: std::time::Instant,
pub vi_period: std::time::Duration,
}
//static mut FRAME_COUNTER: u64 = 0;
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;
device.vi.vi_period = std::time::Duration::from_secs_f64(1.0 / expected_refresh_rate);
}
pub fn set_vertical_interrupt(device: &mut device::Device) {
if device::events::get_event(device, device::events::EventType::VI) == None
&& device.vi.regs[VI_V_INTR_REG as usize] < device.vi.regs[VI_V_SYNC_REG as usize]
{
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 != None {
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)
}
return 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_INTR_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);
ui::video::set_register(reg as u32, device.vi.regs[reg as usize])
}
}
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);
ui::video::set_register(reg as u32, device.vi.regs[reg as usize])
}
}
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);
ui::video::set_register(reg as u32, device.vi.regs[reg as usize])
}
}
_ => {
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;
}
*/
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: &mut device::Device) {
let elapsed = device.vi.last_vi_time.elapsed();
if elapsed <= device.vi.vi_period {
let sleep_time = device.vi.vi_period - elapsed;
let remaining_holdover_space = device.vi.vi_period - device.vi.holdover; // holdover can't exceed the vi period
if sleep_time <= remaining_holdover_space {
device.vi.holdover += sleep_time; // donate all time to the holdover
} else {
device.vi.holdover += remaining_holdover_space; // max out holdover
std::thread::sleep(sleep_time - remaining_holdover_space); // sleep the rest of the time
}
} else {
let over_time = elapsed - device.vi.vi_period; // this is how much we overshot the vi period
if over_time <= device.vi.holdover {
device.vi.holdover -= over_time; // consume some holdover
} else {
device.vi.holdover -= device.vi.holdover; // consume all holdover
}
}
device.vi.last_vi_time = std::time::Instant::now();
}