use crate::gba::bus::WidthClass;
pub trait Bus {
fn read32(&mut self, addr: u32) -> u32;
fn read16(&mut self, addr: u32) -> u16;
fn read8(&mut self, addr: u32) -> u8;
fn write32(&mut self, addr: u32, value: u32);
fn write16(&mut self, addr: u32, value: u16);
fn write8(&mut self, addr: u32, value: u8);
fn n_cycles(&self, addr: u32, width: WidthClass) -> u32;
fn s_cycles(&self, addr: u32, width: WidthClass) -> u32;
fn gamepak_prefetch_enabled(&self) -> bool {
false
}
fn immediate_gamepak_dma_prefetch_penalty(&self, _code_width: WidthClass) -> bool {
false
}
fn embedded_bios_hle_enabled(&self) -> bool {
false
}
fn embedded_bios_hle_entry_penalty(&self, _addr: u32, _width: WidthClass) -> u32 {
0
}
fn fetch32(&mut self, addr: u32) -> u32 {
self.read32(addr)
}
fn fetch16(&mut self, addr: u32) -> u16 {
self.read16(addr)
}
fn prefetch_abort_pending(&mut self) -> bool {
false
}
fn data_abort_pending(&mut self) -> bool {
false
}
}
#[derive(Debug, Clone)]
pub struct RamBus {
bytes: Vec<u8>,
}
impl RamBus {
pub fn new(size: usize) -> Self {
assert!(size > 0, "RamBus size must be greater than zero");
Self {
bytes: vec![0; size],
}
}
pub fn write_bytes(&mut self, addr: u32, data: &[u8]) {
let len = self.bytes.len();
let base = (addr as usize) % len;
for (i, &b) in data.iter().enumerate() {
self.bytes[(base + i) % len] = b;
}
}
pub fn write_word(&mut self, addr: u32, word: u32) {
self.write_bytes(addr, &word.to_le_bytes());
}
pub fn write_halfword(&mut self, addr: u32, hw: u16) {
self.write_bytes(addr, &hw.to_le_bytes());
}
fn idx(&self, addr: u32) -> usize {
(addr as usize) % self.bytes.len()
}
}
impl Bus for RamBus {
fn read32(&mut self, addr: u32) -> u32 {
let a = addr & !0x3;
let i = self.idx(a);
let b = &self.bytes;
u32::from_le_bytes([
b[i],
b[(i + 1) % b.len()],
b[(i + 2) % b.len()],
b[(i + 3) % b.len()],
])
}
fn read16(&mut self, addr: u32) -> u16 {
let a = addr & !0x1;
let i = self.idx(a);
let b = &self.bytes;
u16::from_le_bytes([b[i], b[(i + 1) % b.len()]])
}
fn read8(&mut self, addr: u32) -> u8 {
let i = self.idx(addr);
self.bytes[i]
}
fn write32(&mut self, addr: u32, value: u32) {
let a = addr & !0x3;
self.write_bytes(a, &value.to_le_bytes());
}
fn write16(&mut self, addr: u32, value: u16) {
let a = addr & !0x1;
self.write_bytes(a, &value.to_le_bytes());
}
fn write8(&mut self, addr: u32, value: u8) {
let i = self.idx(addr);
self.bytes[i] = value;
}
fn n_cycles(&self, _addr: u32, _width: WidthClass) -> u32 {
1
}
fn s_cycles(&self, _addr: u32, _width: WidthClass) -> u32 {
1
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ram_bus_round_trips() {
let mut bus = RamBus::new(0x100);
bus.write32(0x10, 0xDEAD_BEEF);
assert_eq!(bus.read32(0x10), 0xDEAD_BEEF);
assert_eq!(bus.read16(0x10), 0xBEEF);
assert_eq!(bus.read16(0x12), 0xDEAD);
assert_eq!(bus.read8(0x10), 0xEF);
assert_eq!(bus.read8(0x13), 0xDE);
bus.write16(0x20, 0x1234);
assert_eq!(bus.read16(0x20), 0x1234);
assert_eq!(bus.read8(0x20), 0x34);
assert_eq!(bus.read8(0x21), 0x12);
bus.write8(0x30, 0xAB);
assert_eq!(bus.read8(0x30), 0xAB);
}
#[test]
fn read32_aligns_address() {
let mut bus = RamBus::new(0x100);
bus.write32(0x10, 0xCAFEBABE);
assert_eq!(bus.read32(0x11), 0xCAFEBABE);
assert_eq!(bus.read32(0x12), 0xCAFEBABE);
assert_eq!(bus.read32(0x13), 0xCAFEBABE);
}
#[test]
#[should_panic(expected = "RamBus size must be greater than zero")]
fn ram_bus_rejects_zero_size() {
let _ = RamBus::new(0);
}
#[test]
fn ram_bus_n_cycles_returns_1() {
let bus = RamBus::new(0x100);
assert_eq!(bus.n_cycles(0x0000_0000, WidthClass::HalfwordOrByte), 1);
assert_eq!(bus.n_cycles(0x0800_0000, WidthClass::Word), 1);
}
#[test]
fn ram_bus_s_cycles_returns_1() {
let bus = RamBus::new(0x100);
assert_eq!(bus.s_cycles(0x0000_0000, WidthClass::HalfwordOrByte), 1);
assert_eq!(bus.s_cycles(0x0800_0000, WidthClass::Word), 1);
}
}