mirror of
https://github.com/FranLMSP/snes.git
synced 2026-01-01 07:21:35 -05:00
refactor DMA ticking, cpuadc chars are now visible through debug window
This commit is contained in:
@@ -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<dyn ROM>,
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<DMATransferProps>,
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user