From 28784f47d35a5f718edd3e1b857fb02df4943df6 Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Sat, 7 Oct 2023 23:33:35 -0500 Subject: [PATCH] refactor DMA ticking, cpuadc chars are now visible through debug window --- snes-core/src/cpu/bus.rs | 42 ++---- snes-core/src/cpu/dma.rs | 164 +++++++++++++++++------- snes-core/src/cpu/instructions.rs | 20 ++- snes-core/src/cpu/internal_registers.rs | 26 ++-- 4 files changed, 161 insertions(+), 91 deletions(-) diff --git a/snes-core/src/cpu/bus.rs b/snes-core/src/cpu/bus.rs index db885bf..4396193 100644 --- a/snes-core/src/cpu/bus.rs +++ b/snes-core/src/cpu/bus.rs @@ -1,17 +1,15 @@ use crate::ppu::PPU; -use crate::cpu::dma; +use crate::cpu::dma::DMA; use crate::cpu::internal_registers::InternalRegisters; use crate::rom::ROM; use crate::rom::lo_rom::LoROM; -use super::dma::DMATransferProps; - pub struct Bus { wram: [u8; 0x10000], pub ppu: PPU, pub rom: Box, pub internal_registers: InternalRegisters, - pub is_dma_active: bool, + pub dma: DMA, } #[derive(PartialEq, Debug)] @@ -19,6 +17,7 @@ pub enum MemoryMap { WRAM, PPU, CPU, + DMA, Joypad, Cartridge, } @@ -30,7 +29,7 @@ impl Bus { ppu: PPU::new(), rom: Box::new(LoROM::new()), internal_registers: InternalRegisters::new(), - is_dma_active: false, + dma: DMA::new(), } } @@ -55,6 +54,7 @@ impl Bus { 0x2100..=0x21FF => MemoryMap::PPU, 0x4016..=0x4017 => MemoryMap::Joypad, 0x4200..=0x42FF => MemoryMap::CPU, + 0x4300..=0x5FFF => MemoryMap::DMA, _ => MemoryMap::Cartridge, }, _ => MemoryMap::Cartridge, @@ -72,6 +72,7 @@ impl Bus { address as u16, &self.ppu.registers, ), + MemoryMap::DMA => self.dma.read(address as u16), MemoryMap::Joypad => 0x00, // TODO: Placeholder MemoryMap::Cartridge => self.rom.read(address), } @@ -86,6 +87,7 @@ impl Bus { address as u16, &mut self.ppu.registers, ), + MemoryMap::DMA => self.dma.read(address as u16), MemoryMap::Joypad => 0x00, // TODO: Placeholder MemoryMap::Cartridge => self.rom.read(address), } @@ -96,34 +98,16 @@ impl Bus { match section { MemoryMap::WRAM => self.write_wram(address, value), MemoryMap::PPU => self.ppu.registers.write(address as u16, value), - MemoryMap::CPU => self.internal_registers.write(address as u16, value), + MemoryMap::CPU => self.internal_registers.write( + address as u16, + value, + &mut self.dma, + ), + MemoryMap::DMA => self.dma.write(address as u16, value), MemoryMap::Joypad => {}, // TODO: Placeholder MemoryMap::Cartridge => self.rom.write(address, value), } } - - pub fn tick_dma(&mut self, cpu_cycles: usize) { - if !self.is_dma_active { - return; - } - - let dma_select = self.internal_registers.read_dma(dma::MDMAEN); - let mut active_channels = [false; 8]; - for i in 0..8 { - active_channels[i] = (dma_select & (1 << i)) != 0; - } - - for (channel, is_active) in active_channels.iter().enumerate() { - if !is_active { - continue - } - let mut props = DMATransferProps::new_from_internal_registers( - &self.internal_registers, - channel as u8, - ); - props.tick(self); - } - } } diff --git a/snes-core/src/cpu/dma.rs b/snes-core/src/cpu/dma.rs index e5629dc..c3a0d04 100644 --- a/snes-core/src/cpu/dma.rs +++ b/snes-core/src/cpu/dma.rs @@ -1,6 +1,3 @@ -use crate::cpu::internal_registers::InternalRegisters; -use crate::cpu::bus::Bus; - pub const MDMAEN: u16 = 0x420B; // Select General Purpose DMA Channel(s) and Start Transfer (W) pub const DMAPX: u16 = 0x4300; // DMA/HDMA Parameters (R/W) @@ -63,16 +60,16 @@ pub struct DMATransferProps { } impl DMATransferProps { - fn read_register(internal_registers: &InternalRegisters, register: u16, channel_number: u8) -> u8 { - internal_registers.read_dma((register & 0xFF0F) | ((channel_number as u16) << 4)) + fn read_channel_register(registers: &[u8], register: u16, channel_number: u8) -> u8 { + registers[(((register & 0xFF0F) | ((channel_number as u16) << 4)) - 0x4300) as usize] } - fn write_register(internal_registers: &mut InternalRegisters, register: u16, channel_number: u8, val: u8) { - internal_registers.write((register & 0xFF0F) | ((channel_number as u16) << 4), val); + fn write_channel_register(registers: &mut [u8], register: u16, channel_number: u8, val: u8) { + registers[(((register & 0xFF0F) | ((channel_number as u16) << 4)) - 0x4300) as usize] = val; } - pub fn new_from_internal_registers( - internal_registers: &InternalRegisters, + pub fn new_from_registers( + registers: &[u8], channel_number: u8, ) -> Self { let channel = match channel_number { @@ -86,7 +83,7 @@ impl DMATransferProps { 7 => DMAChannel::Channel7, _ => unreachable!(), }; - let params = DMATransferProps::read_register(internal_registers, DMAPX, channel_number); + let params = DMATransferProps::read_channel_register(registers, DMAPX, channel_number); let direction = match params >> 7 == 1 { false => TransferDirection::AtoB, true => TransferDirection::BtoA, @@ -113,13 +110,13 @@ impl DMATransferProps { _ => unreachable!(), }; let a_bus_address = - (DMATransferProps::read_register(internal_registers, A1BX, channel_number) as u32) << 16 | - (DMATransferProps::read_register(internal_registers, A1TXH, channel_number) as u32) << 8 | - DMATransferProps::read_register(internal_registers, A1TXL, channel_number) as u32; - let b_bus_address = DMATransferProps::read_register(internal_registers, BBADX, channel_number); + (DMATransferProps::read_channel_register(registers, A1BX, channel_number) as u32) << 16 | + (DMATransferProps::read_channel_register(registers, A1TXH, channel_number) as u32) << 8 | + DMATransferProps::read_channel_register(registers, A1TXL, channel_number) as u32; + let b_bus_address = DMATransferProps::read_channel_register(registers, BBADX, channel_number); let number_of_bytes = - (DMATransferProps::read_register(internal_registers, DASXH, channel_number) as u16) << 8 | - DMATransferProps::read_register(internal_registers, DASXL, channel_number) as u16; + (DMATransferProps::read_channel_register(registers, DASXH, channel_number) as u16) << 8 | + DMATransferProps::read_channel_register(registers, DASXL, channel_number) as u16; Self { channel: channel, @@ -134,78 +131,157 @@ impl DMATransferProps { } } - fn save_channel_state(&self, internal_registers: &mut InternalRegisters) { + fn save_channel_state(&self, registers: &mut [u8]) { // Update B Bus address - DMATransferProps::write_register(internal_registers, BBADX, self.channel_number, self.b_bus_address); + DMATransferProps::write_channel_register(registers, BBADX, self.channel_number, self.b_bus_address); // Update A Bus address - DMATransferProps::write_register(internal_registers, A1BX, self.channel_number, (self.a_bus_address >> 16) as u8); - DMATransferProps::write_register(internal_registers, A1TXH, self.channel_number, (self.a_bus_address >> 8) as u8); - DMATransferProps::write_register(internal_registers, A1BX, self.channel_number, self.a_bus_address as u8); + DMATransferProps::write_channel_register(registers, A1BX, self.channel_number, (self.a_bus_address >> 16) as u8); + DMATransferProps::write_channel_register(registers, A1TXH, self.channel_number, (self.a_bus_address >> 8) as u8); + DMATransferProps::write_channel_register(registers, A1BX, self.channel_number, self.a_bus_address as u8); // Update Byte count - DMATransferProps::write_register(internal_registers, DASXH, self.channel_number, (self.number_of_bytes >> 8) as u8); - DMATransferProps::write_register(internal_registers, DASXL, self.channel_number, self.number_of_bytes as u8); + DMATransferProps::write_channel_register(registers, DASXH, self.channel_number, (self.number_of_bytes >> 8) as u8); + DMATransferProps::write_channel_register(registers, DASXL, self.channel_number, self.number_of_bytes as u8); } - pub fn tick(&mut self, bus: &mut Bus) { + pub fn tick(&mut self, registers: &mut [u8]) -> Vec<(u32, u32)> { let source_address = match self.direction { - TransferDirection::AtoB => self.a_bus_address, - TransferDirection::BtoA => self.b_bus_address as u32, - }; - let dest_address = match self.direction { TransferDirection::AtoB => self.a_bus_address, TransferDirection::BtoA => 0x002100 | (self.b_bus_address as u32), }; + let dest_address = match self.direction { + TransferDirection::AtoB => 0x002100 | (self.b_bus_address as u32), + TransferDirection::BtoA => self.a_bus_address, + }; + let mut pending_bus_writes = Vec::new(); match self.transfer_format { TransferFormat::OneByteOneRegister => { // Transfer 1 byte xx ;eg. for WRAM (port 2180h) - let source_byte = bus.read(source_address); - bus.write(dest_address, source_byte) + pending_bus_writes.push((source_address, dest_address)); }, TransferFormat::TwoBytesTwoRegisters => { // Transfer 2 bytes xx, xx+1 ;eg. for VRAM (port 2118h/19h) for index in 0..2 { - let source_byte = bus.read(source_address.wrapping_add(index)); - bus.write(dest_address.wrapping_add(index), source_byte); + pending_bus_writes.push(( + source_address.wrapping_add(index), + dest_address.wrapping_add(index), + )); } }, TransferFormat::TwoBytesOneRegister => { // Transfer 2 bytes xx, xx ;eg. for OAM or CGRAM for index in 0..2 { - let source_byte = bus.read(source_address.wrapping_add(index)); - bus.write(dest_address, source_byte); + pending_bus_writes.push(( + source_address.wrapping_add(index), + dest_address, + )); } }, TransferFormat::FourBytesTwoRegisters => { // Transfer 4 bytes xx, xx, xx+1, xx+1 ;eg. for BGnxOFS, M7x for index in 0..4 { - let source_byte = bus.read(source_address.wrapping_add(index)); - bus.write(dest_address.wrapping_add(index / 2), source_byte); + pending_bus_writes.push(( + source_address.wrapping_add(index), + dest_address.wrapping_add(index / 2), + )); } }, TransferFormat::FourBytesFourRegisters => { // Transfer 4 bytes xx, xx+1, xx+2, xx+3 ;eg. for BGnSC, Window, APU.. for index in 0..4 { - let source_byte = bus.read(source_address.wrapping_add(index)); - bus.write(dest_address.wrapping_add(index), source_byte); + pending_bus_writes.push(( + source_address.wrapping_add(index), + dest_address.wrapping_add(index), + )); } }, TransferFormat::FourBytesTwoRegistersSequential => { // Transfer 4 bytes xx, xx+1, xx, xx+1 ;whatever purpose, VRAM maybe for index in 0..4 { - let source_byte = bus.read(source_address.wrapping_add(index)); - bus.write(dest_address.wrapping_add(index & 1), source_byte); + pending_bus_writes.push(( + source_address.wrapping_add(index), + dest_address.wrapping_add(index & 1), + )); } }, }; - self.number_of_bytes -= 1; + if self.number_of_bytes > 0 { + self.number_of_bytes -= 1; + } match self.auto_update_a_address { - AutoUpdateA::Increment => self.a_bus_address += self.a_bus_address, - AutoUpdateA::Decrement => self.a_bus_address -= self.a_bus_address, + AutoUpdateA::Increment => self.a_bus_address = self.a_bus_address.wrapping_add(1), + AutoUpdateA::Decrement => self.a_bus_address = self.a_bus_address.wrapping_sub(1), AutoUpdateA::Neither => {}, } - self.save_channel_state(&mut bus.internal_registers); + self.save_channel_state(registers); + pending_bus_writes } } + + +pub struct DMA { + pub active_dma_transfers: Vec, + registers: [u8; 0x1D00], +} + +impl DMA { + pub fn new() -> Self { + Self { + active_dma_transfers: Vec::new(), + registers: [0; 0x1D00], + } + } + + fn _read(&self, address: u16) -> u8 { + self.registers[(address - 0x4300) as usize] + } + + fn _write(&mut self, address: u16, value: u8) { + self.registers[(address - 0x4300) as usize] = value; + } + + pub fn prepare_dma_transfer(&mut self, dma_select: u8) { + self.active_dma_transfers = Vec::new(); + let mut active_channels = [false; 8]; + for i in 0..8 { + active_channels[i] = (dma_select & (1 << i)) != 0; + } + + for (channel, is_active) in active_channels.iter().enumerate() { + if !is_active { + continue; + } + let props = DMATransferProps::new_from_registers( + &self.registers, + channel as u8, + ); + self.active_dma_transfers.push(props); + } + } + + pub fn read(&self, address: u16) -> u8 { + self._read(address) + } + + pub fn write(&mut self, address: u16, value: u8) { + self._write(address, value); + } + + pub fn is_active(&self) -> bool { + !self.active_dma_transfers.is_empty() + } + + pub fn tick(&mut self) -> Vec<(u32, u32)> { + if !self.is_active() { + return vec![]; + } + + let pending_bus_writes = self.active_dma_transfers[0].tick(&mut self.registers); + if self.active_dma_transfers[0].number_of_bytes == 0 { + self.active_dma_transfers.remove(0); + } + pending_bus_writes + } +} \ No newline at end of file diff --git a/snes-core/src/cpu/instructions.rs b/snes-core/src/cpu/instructions.rs index 33db4ce..faa2a15 100644 --- a/snes-core/src/cpu/instructions.rs +++ b/snes-core/src/cpu/instructions.rs @@ -1,5 +1,6 @@ use super::cpu::CPU; use crate::cpu::bus::Bus; +use crate::cpu::dma; use crate::utils::addressing::{AddressingMode, IndexRegister}; use crate::utils::alu; use crate::utils::num_trait::SnesNum; @@ -1133,7 +1134,22 @@ impl CPU { self.increment_cycles_exchange(); } - fn check_running_state(&mut self) -> bool { + fn check_running_state(&mut self, bus: &mut Bus) -> bool { + // Each byte in a DMA transfer takes 8 master cycles. + // And each CPU can take either 6, 8 or 12 master cycles depending + // on what's being read from memory. So this won't be accurate. + if bus.dma.is_active() { + let pending_bus_writes = bus.dma.tick(); + for (src, dst) in pending_bus_writes { + let byte = bus.read(src); + bus.write(dst, byte); + self.increment_cycles_while_stopped(); + } + if !bus.dma.is_active() { + bus.write(dma::MDMAEN as u32, 0x00) + } + return false; + } if self.is_stopped { self.increment_cycles_while_stopped(); return false; @@ -1147,7 +1163,7 @@ impl CPU { } pub fn tick(&mut self, bus: &mut Bus) { - if !self.check_running_state() { + if !self.check_running_state(bus) { return; } let opcode = bus.read(self.registers.get_pc_address()); diff --git a/snes-core/src/cpu/internal_registers.rs b/snes-core/src/cpu/internal_registers.rs index 1360883..d804228 100644 --- a/snes-core/src/cpu/internal_registers.rs +++ b/snes-core/src/cpu/internal_registers.rs @@ -1,4 +1,5 @@ use crate::ppu::registers::PPURegisters; +use crate::cpu::dma; pub const INTERNAL_REGISTERS_ADDRESS: u16 = 0x4200; @@ -6,33 +7,22 @@ pub const INTERNAL_REGISTERS_ADDRESS: u16 = 0x4200; pub const RDNMI: u16 = 0x4210; // V-Blank NMI Flag pub struct InternalRegisters { - registers: [u8; 32], + registers: [u8; 256], } impl InternalRegisters { pub fn new() -> Self { Self { - registers: [0; 32], + registers: [0; 256], } } - fn get_index(address: u16) -> usize { - (address - INTERNAL_REGISTERS_ADDRESS) as usize - } - fn _read(&self, address: u16) -> u8 { - let index = InternalRegisters::get_index(address); - if index >= self.registers.len() { - return 0xFF; - } - self.registers[index] + self.registers[(address - 0x4200) as usize] } fn _write(&mut self, address: u16, value: u8) { - let index = InternalRegisters::get_index(address); - if index < self.registers.len() { - self.registers[index] = value - } + self.registers[(address - 0x4200) as usize] = value } pub fn read_external(&self, address: u16, ppu_registers: &PPURegisters) -> u8 { @@ -53,8 +43,12 @@ impl InternalRegisters { self._read(address) } - pub fn write(&mut self, address: u16, value: u8) { + pub fn write(&mut self, address: u16, value: u8, dma: &mut dma::DMA) { self._write(address, value); + match address { + dma::MDMAEN => dma.prepare_dma_transfer(value), + _ => {}, + } } fn read_vblank_nmi(&self, ppu_registers: &PPURegisters) -> u8 {