use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Waitstates {
regions: [[u32; 4]; 16],
pub waitcnt: u16,
pub prefetch_enabled: bool,
}
const N16: usize = 0;
const N32: usize = 1;
const S16: usize = 2;
const S32: usize = 3;
const WAIT_N_LUT: [u32; 4] = [4, 3, 2, 8];
const WAIT_S0_LUT: [u32; 2] = [2, 1];
const WAIT_S1_LUT: [u32; 2] = [4, 1];
const WAIT_S2_LUT: [u32; 2] = [8, 1];
impl Default for Waitstates {
fn default() -> Self {
Self::new()
}
}
impl Waitstates {
pub fn new() -> Self {
let mut ws = Self {
regions: [[1; 4]; 16],
waitcnt: 0,
prefetch_enabled: false,
};
ws.recalculate(0);
ws
}
pub fn recalculate(&mut self, waitcnt: u16) {
self.waitcnt = waitcnt & 0x5FFF; self.prefetch_enabled = waitcnt & (1 << 14) != 0;
let bios = [1, 1, 1, 1];
let ewram = [3, 6, 3, 6];
let iwram = [1, 1, 1, 1];
let io = [1, 1, 1, 1];
let pram = [1, 2, 1, 2];
let vram = [1, 2, 1, 2];
let oam = [1, 1, 1, 1];
self.regions[0x0] = bios;
self.regions[0x1] = bios; self.regions[0x2] = ewram;
self.regions[0x3] = iwram;
self.regions[0x4] = io;
self.regions[0x5] = pram;
self.regions[0x6] = vram;
self.regions[0x7] = oam;
let sram_n16 = WAIT_N_LUT[(waitcnt & 0x3) as usize] + 1;
let sram_n32 = 2 * sram_n16 + 1;
let sram = [sram_n16, sram_n32, sram_n16, sram_n32];
self.regions[0xE] = sram;
self.regions[0xF] = sram;
let ws0_n16 = WAIT_N_LUT[((waitcnt >> 2) & 0x3) as usize] + 1;
let ws0_s16 = WAIT_S0_LUT[((waitcnt >> 4) & 0x1) as usize] + 1;
let ws0_n32 = ws0_n16 + ws0_s16;
let ws0_s32 = 2 * ws0_s16;
let ws0 = [ws0_n16, ws0_n32, ws0_s16, ws0_s32];
self.regions[0x8] = ws0;
self.regions[0x9] = ws0;
let ws1_n16 = WAIT_N_LUT[((waitcnt >> 5) & 0x3) as usize] + 1;
let ws1_s16 = WAIT_S1_LUT[((waitcnt >> 7) & 0x1) as usize] + 1;
let ws1_n32 = ws1_n16 + ws1_s16;
let ws1_s32 = 2 * ws1_s16;
let ws1 = [ws1_n16, ws1_n32, ws1_s16, ws1_s32];
self.regions[0xA] = ws1;
self.regions[0xB] = ws1;
let ws2_n16 = WAIT_N_LUT[((waitcnt >> 8) & 0x3) as usize] + 1;
let ws2_s16 = WAIT_S2_LUT[((waitcnt >> 10) & 0x1) as usize] + 1;
let ws2_n32 = ws2_n16 + ws2_s16;
let ws2_s32 = 2 * ws2_s16;
let ws2 = [ws2_n16, ws2_n32, ws2_s16, ws2_s32];
self.regions[0xC] = ws2;
self.regions[0xD] = ws2;
}
#[inline]
pub fn n_cycles(&self, addr: u32, width: WidthClass) -> u32 {
let region = ((addr >> 24) & 0xF) as usize;
match width {
WidthClass::HalfwordOrByte => self.regions[region][N16],
WidthClass::Word => self.regions[region][N32],
}
}
#[inline]
pub fn s_cycles(&self, addr: u32, width: WidthClass) -> u32 {
let region = ((addr >> 24) & 0xF) as usize;
match width {
WidthClass::HalfwordOrByte => self.regions[region][S16],
WidthClass::Word => self.regions[region][S32],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WidthClass {
HalfwordOrByte,
Word,
}