refactor DMA ticking, cpuadc chars are now visible through debug window

This commit is contained in:
2023-10-07 23:33:35 -05:00
parent 7769964cf6
commit 28784f47d3
4 changed files with 161 additions and 91 deletions

View File

@@ -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);
}
}
}

View File

@@ -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
}
}

View File

@@ -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());

View File

@@ -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 {