diff --git a/src/snes/apu/apubus.rs b/src/snes/apu/apubus.rs index 4020ff4..9953bc9 100644 --- a/src/snes/apu/apubus.rs +++ b/src/snes/apu/apubus.rs @@ -1,9 +1,14 @@ +use std::cell::RefCell; +use std::rc::Rc; + use anyhow::Result; use crate::snes::bus::{Bus, BusMember}; use crate::snes::cpu_spc700::cpu::{SpcAddress, SPC_ADDRESS_MASK}; use crate::tickable::{Tickable, Ticks}; +use super::ApuPorts; + const APU_RAM_SIZE: usize = 64 * 1024; const APU_ROM_SIZE: usize = 64; @@ -11,13 +16,15 @@ const APU_ROM_SIZE: usize = 64; pub struct Apubus { ram: [u8; APU_RAM_SIZE], rom: [u8; APU_ROM_SIZE], + ports: Rc>, } impl Apubus { - pub fn new(rom: &[u8]) -> Self { + pub fn new(rom: &[u8], ports: Rc>) -> Self { Self { ram: [0; APU_RAM_SIZE], rom: rom.try_into().unwrap(), + ports, } } } @@ -25,6 +32,12 @@ impl Apubus { impl Bus for Apubus { fn read(&self, addr: SpcAddress) -> u8 { match addr { + // Ports + 0x00F4..=0x00F7 => { + let ports = self.ports.borrow(); + ports.apu[addr as usize - 0x00F4] + } + // ROM (IPL) // TODO mask setting! 0xFFC0..=0xFFFF => self.rom[addr as usize - 0xFFC0], @@ -33,6 +46,32 @@ impl Bus for Apubus { } fn write(&mut self, addr: SpcAddress, val: u8) { + match addr { + 0x00F1 => { + println!("APU control: {:02X}", val); + + if val & (1 << 4) != 0 { + let mut ports = self.ports.borrow_mut(); + println!("Clear input 0, 1"); + ports.apu[0] = 0; + ports.apu[1] = 0; + } + if val & (1 << 5) != 0 { + let mut ports = self.ports.borrow_mut(); + println!("Clear input 2, 3"); + ports.apu[2] = 0; + ports.apu[3] = 0; + } + } + // Ports + 0x00F4..=0x00F7 => { + let mut ports = self.ports.borrow_mut(); + println!("APU port {:04X} to {:02X}", addr, val); + ports.cpu[addr as usize - 0x00F4] = val; + } + _ => (), + } + // Writes ALWAYS go through to RAM self.ram[addr as usize] = val; } diff --git a/src/snes/apu/mod.rs b/src/snes/apu/mod.rs index 5027007..7814c56 100644 --- a/src/snes/apu/mod.rs +++ b/src/snes/apu/mod.rs @@ -1,14 +1,27 @@ pub mod apubus; +use std::cell::RefCell; +use std::rc::Rc; + use anyhow::Result; use colored::*; use crate::snes::bus::{Address, BusMember}; use crate::snes::cpu_spc700::cpu::{CpuSpc700, SpcAddress}; +use crate::snes::cpu_spc700::regs::Register; use crate::tickable::{Tickable, Ticks}; use apubus::Apubus; +/// Type for the CPU <-> APU communication ports +pub struct ApuPorts { + /// APU -> CPU + pub cpu: [u8; 4], + + /// CPU -> APU + pub apu: [u8; 4], +} + /// The SNES Audio Processing Unit pub struct Apu { /// SPC700 CPU core @@ -22,11 +35,14 @@ pub struct Apu { /// Print instructions verbose: bool, + + /// Main CPU communication ports + ports: Rc>, } impl Apu { /// One SPC cycle = 25 master cycles - const SPC_MASTER_FACTOR: Ticks = 25; + const SPC_MASTER_FACTOR: Ticks = 1; const IPL_ENTRYPOINT: SpcAddress = 0xFFC0; const IPL_SIZE: usize = 64; @@ -40,13 +56,26 @@ impl Apu { ]; pub fn new(verbose: bool) -> Self { + let ports = Rc::new(RefCell::new(ApuPorts { + cpu: [0; 4], + apu: [0; 4], + })); Self { - cpu: CpuSpc700::::new(Apubus::new(&Self::IPL_BIN), Self::IPL_ENTRYPOINT), + cpu: CpuSpc700::::new( + Apubus::new(&Self::IPL_BIN, Rc::clone(&ports)), + Self::IPL_ENTRYPOINT, + ), spc_cycles_taken: 0, spc_master_credit: 0, verbose, + ports, } } + + /// Get a (reference counted) copy of the communication ports + pub fn get_ports(&self) -> Rc> { + Rc::clone(&self.ports) + } } impl Tickable for Apu { @@ -57,8 +86,8 @@ impl Tickable for Apu { self.spc_master_credit += ticks; while self.spc_master_credit >= Self::SPC_MASTER_FACTOR { - if self.spc_cycles_taken == 0 { - if self.verbose { + if true || self.spc_cycles_taken == 0 { + if self.verbose && self.cpu.regs.read(Register::PC) < Self::IPL_ENTRYPOINT { println!("{}", self.cpu.dump_state().red()); } self.spc_cycles_taken += self.cpu.step()?; diff --git a/src/snes/bus/mainbus.rs b/src/snes/bus/mainbus.rs index b0383f8..9d77109 100644 --- a/src/snes/bus/mainbus.rs +++ b/src/snes/bus/mainbus.rs @@ -1,10 +1,11 @@ use std::cell::{Cell, RefCell}; +use std::rc::Rc; use anyhow::Result; use dbg_hex::dbg_hex; use crate::frontend::Renderer; -use crate::snes::apu::Apu; +use crate::snes::apu::{Apu, ApuPorts}; use crate::snes::bus::{Address, Bus, BusMember, ADDRESS_MASK}; use crate::snes::cartridge::Cartridge; use crate::snes::joypad::{Joypad, JOYPAD_COUNT}; @@ -37,8 +38,12 @@ where /// Controllers joypads: [Joypad; JOYPAD_COUNT], + /// Audio Processing Unit pub apu: Apu, + /// APU communication ports + apu_ports: Rc>, + /// Picture Processing Unit pub ppu: PPU, @@ -66,8 +71,6 @@ where /// NMITIMEN register nmitimen: u8, - apumock: RefCell<[u8; 4]>, - /// Multiplication/division unit registers wrmpya: u8, wrdiv: u16, @@ -230,6 +233,9 @@ where joypads: [Joypad; JOYPAD_COUNT], apu_verbose: bool, ) -> Self { + let apu = Apu::new(apu_verbose); + let apu_ports = apu.get_ports(); + Self { cartridge, wram: vec![0; WRAM_SIZE], @@ -239,7 +245,8 @@ where joypads, ppu: PPU::::new(renderer), - apu: Apu::new(apu_verbose), + apu, + apu_ports, memsel: 0, wmadd: Cell::new(0), @@ -249,8 +256,6 @@ where intreq_int: false, nmitimen: 0, - apumock: RefCell::new([0xAA, 0, 0, 0]), - wrmpya: 0, wrdiv: 0, rddiv: 0, @@ -439,14 +444,9 @@ where // APU comms 0x2140..=0x217F => { let ch = (addr - 0x2140) % 4; - let mut apumock = self.apumock.borrow_mut(); - let value = apumock[ch]; - apumock[ch] = match ch { - 0 => 0xAA, - 1 => 0xBB, - _ => 0, - }; - Some(value) + + let ports = self.apu_ports.borrow(); + Some(ports.cpu[ch]) } // WMDATA - WRAM Data Read/Write (R/W) 0x2180 => { @@ -586,8 +586,9 @@ where // APU comms 0x2140..=0x217F => { let ch = (addr - 0x2140) % 4; - let mut apumock = self.apumock.borrow_mut(); - apumock[ch] = val; + let mut ports = self.apu_ports.borrow_mut(); + println!("CPU APU port {} to {:02X}", ch, val); + ports.apu[ch] = val; Some(()) } // WMDATA - WRAM Data Read/Write @@ -812,6 +813,7 @@ mod tests { BusTrace::All, NullRenderer::new(0, 0).unwrap(), joypads, + false, ) } diff --git a/src/test/mod.rs b/src/test/mod.rs index 88fa408..152d53f 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -18,7 +18,7 @@ fn test_display(rom: &[u8], pass_hash: &[u8], time_limit: u128, stable: bool) { let (display, dispstatus) = TestRenderer::new_test(SCREEN_WIDTH, SCREEN_HEIGHT); let (joypads, _) = Joypad::new_channel_all(); let cart = Cartridge::load_nohdr(rom, false); - let bus = Mainbus::::new(cart, BusTrace::None, display, joypads); + let bus = Mainbus::::new(cart, BusTrace::None, display, joypads, false); let reset = bus.read16(0xFFFC); let mut cpu = Cpu65816::>::new(bus, reset);