use crate::cartridge::Cartridge;
use crate::interrupts::{IRQ1, IRQ2, InterruptController, TIQ};
use crate::io::IoPort;
use crate::psg::Psg;
use crate::timer::Timer;
use crate::vce::Vce;
use crate::vdc::Vdc;
use mos6502::memory::Bus;
const RAM_BANK: u8 = 0xF8;
const HARDWARE_BANK: u8 = 0xFF;
const ROM_BANK_MAX: u8 = 0x7F;
const RAM_SIZE: usize = 0x2000;
pub struct SystemBus {
mpr: [u8; 8],
ram: [u8; RAM_SIZE],
cartridge: Cartridge,
pub vdc: Vdc,
pub vce: Vce,
pub psg: Psg,
pub timer: Timer,
pub io: IoPort,
pub interrupts: InterruptController,
pub debug: BusDebug,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct BusDebug {
pub irq_vectors_fetched: u64,
pub vdc_status_reads: u64,
pub last_irq_vector: u16,
pub vce_writes: u64,
pub vce_offset_writes: [u64; 8],
pub vce_data_lo_nonzero: u64,
pub ram_watch_lo: usize,
pub ram_watch_hi: usize,
pub ram_watch_writes: u64,
}
impl SystemBus {
#[must_use]
pub fn new(cartridge: Cartridge) -> Self {
Self {
mpr: [0; 8],
ram: [0; RAM_SIZE],
cartridge,
vdc: Vdc::new(),
vce: Vce::new(),
psg: Psg::new(),
timer: Timer::new(),
io: IoPort::new(),
interrupts: InterruptController::new(),
debug: BusDebug::default(),
}
}
#[must_use]
pub const fn ram_byte(&self, offset: u16) -> u8 {
self.ram[(offset as usize) & (RAM_SIZE - 1)]
}
#[must_use]
pub fn peek(&self, logical: u16) -> u8 {
let phys = self.physical_address(logical);
let bank = (phys >> 13) as u8;
match bank {
HARDWARE_BANK => 0xFF,
RAM_BANK..=0xFB => self.ram[(phys as usize) & (RAM_SIZE - 1)],
0x00..=ROM_BANK_MAX => self.cartridge.read(phys),
_ => 0xFF,
}
}
#[must_use]
pub fn is_unmapped(&self, logical: u16) -> bool {
let bank = (self.physical_address(logical) >> 13) as u8;
!matches!(bank, HARDWARE_BANK | RAM_BANK..=0xFB | 0x00..=ROM_BANK_MAX)
}
#[must_use]
pub fn physical_address(&self, logical: u16) -> u32 {
let bank = self.mpr[(logical >> 13) as usize];
(u32::from(bank) << 13) | (u32::from(logical) & 0x1FFF)
}
fn irq_sources(&self) -> u8 {
let mut sources = 0;
if self.vdc.irq() {
sources |= IRQ1;
}
if self.timer.irq() {
sources |= TIQ;
}
sources
}
fn active_irq_vector(&self) -> u16 {
let active = self.interrupts.active(self.irq_sources());
if active & TIQ != 0 {
0xFFFA
} else if active & IRQ1 != 0 {
0xFFF8
} else {
0xFFF6
}
}
fn read_physical(&mut self, phys: u32) -> u8 {
let bank = (phys >> 13) as u8;
match bank {
HARDWARE_BANK => self.read_hardware((phys & 0x1FFF) as u16),
RAM_BANK..=0xFB => self.ram[(phys as usize) & (RAM_SIZE - 1)],
0x00..=ROM_BANK_MAX => self.cartridge.read(phys),
_ => 0xFF,
}
}
fn write_physical(&mut self, phys: u32, value: u8) {
let bank = (phys >> 13) as u8;
match bank {
HARDWARE_BANK => self.write_hardware((phys & 0x1FFF) as u16, value),
RAM_BANK..=0xFB => {
if bank == RAM_BANK {
let off = (phys as usize) & (RAM_SIZE - 1);
if (self.debug.ram_watch_lo..self.debug.ram_watch_hi).contains(&off) {
self.debug.ram_watch_writes += 1;
}
}
self.ram[(phys as usize) & (RAM_SIZE - 1)] = value;
}
0x00..=ROM_BANK_MAX => self.cartridge.write(phys, value),
_ => {}
}
}
fn read_hardware(&mut self, offset: u16) -> u8 {
match offset {
0x0000..=0x03FF => {
if offset & 0x03 == 0 {
self.debug.vdc_status_reads += 1;
}
self.vdc.read(offset & 0x03)
}
0x0400..=0x07FF => self.vce.read(offset & 0x07),
0x0800..=0x0BFF => self.psg.read(offset & 0x0F),
0x0C00..=0x0FFF => self.timer.read(offset & 0x01),
0x1000..=0x13FF => self.io.read(),
0x1400..=0x17FF => self.interrupts.read(offset & 0x03, self.irq_sources()),
_ => 0xFF,
}
}
fn write_hardware(&mut self, offset: u16, value: u8) {
match offset {
0x0000..=0x03FF => self.vdc.write(offset & 0x03, value),
0x0400..=0x07FF => {
self.debug.vce_writes += 1;
self.debug.vce_offset_writes[(offset & 0x07) as usize] += 1;
if offset & 0x07 == 0x04 && value != 0 {
self.debug.vce_data_lo_nonzero += 1;
}
self.vce.write(offset & 0x07, value);
}
0x0800..=0x0BFF => self.psg.write(offset & 0x0F, value),
0x0C00..=0x0FFF => self.timer.write(offset & 0x01, value),
0x1000..=0x13FF => self.io.write(value),
0x1400..=0x17FF
if self.interrupts.write(offset & 0x03, value) => {
self.timer.acknowledge();
}
_ => {}
}
}
}
impl Bus for SystemBus {
fn get_byte(&mut self, address: u16) -> u8 {
let phys = self.physical_address(address);
self.read_physical(phys)
}
fn set_byte(&mut self, address: u16, value: u8) {
let phys = self.physical_address(address);
self.write_physical(phys, value);
}
fn irq_pending(&mut self) -> bool {
self.interrupts.pending(self.irq_sources())
}
fn irq_vector(&mut self) -> u16 {
self.debug.irq_vectors_fetched += 1;
let vector = self.active_irq_vector();
self.debug.last_irq_vector = vector;
vector
}
fn set_mapping_register(&mut self, index: usize, value: u8) {
self.mpr[index] = value;
}
}
const _: u8 = IRQ2;