vram dma transfer might work?

This commit is contained in:
Alex Janka 2023-04-21 10:28:54 +10:00
parent 2a5d8b816c
commit 9ff6196ecf
7 changed files with 174 additions and 26 deletions

View file

@ -7,7 +7,7 @@ use self::{
apu::ApuSaveState, apu::ApuSaveState,
gpu::{Colour, GpuSaveState}, gpu::{Colour, GpuSaveState},
serial::SerialSaveState, serial::SerialSaveState,
Apu, Gpu, Joypad, OamDma, Serial, Timer, Apu, Gpu, Joypad, OamDma, Serial, Timer, VramDma,
}, },
rom::RomSaveState, rom::RomSaveState,
}; };
@ -94,7 +94,8 @@ where
pub(super) ime: bool, pub(super) ime: bool,
pub(super) ime_scheduled: u8, pub(super) ime_scheduled: u8,
pub(super) user_mode: bool, pub(super) user_mode: bool,
dma: OamDma, oam_dma: OamDma,
vram_dma: Option<VramDma>,
joypad: Joypad, joypad: Joypad,
gpu: Gpu<ColourFormat, R>, gpu: Gpu<ColourFormat, R>,
apu: Apu, apu: Apu,
@ -119,7 +120,8 @@ where
pub(super) ime: bool, pub(super) ime: bool,
pub(super) ime_scheduled: u8, pub(super) ime_scheduled: u8,
pub(super) user_mode: bool, pub(super) user_mode: bool,
dma: OamDma, oam_dma: OamDma,
vram_dma: Option<VramDma>,
joypad: Joypad, joypad: Joypad,
gpu: GpuSaveState<ColourFormat, R>, gpu: GpuSaveState<ColourFormat, R>,
apu: ApuSaveState, apu: ApuSaveState,
@ -143,7 +145,8 @@ where
ime: memory.ime, ime: memory.ime,
ime_scheduled: memory.ime_scheduled, ime_scheduled: memory.ime_scheduled,
user_mode: memory.user_mode, user_mode: memory.user_mode,
dma: memory.dma, oam_dma: memory.oam_dma,
vram_dma: memory.vram_dma,
joypad: memory.joypad, joypad: memory.joypad,
gpu: GpuSaveState::create(&memory.gpu), gpu: GpuSaveState::create(&memory.gpu),
apu: ApuSaveState::create(&memory.apu), apu: ApuSaveState::create(&memory.apu),
@ -213,7 +216,8 @@ where
ime: false, ime: false,
ime_scheduled: 0x0, ime_scheduled: 0x0,
user_mode: false, user_mode: false,
dma: OamDma::default(), oam_dma: OamDma::default(),
vram_dma: if cgb { Some(VramDma::default()) } else { None },
joypad: Joypad::default(), joypad: Joypad::default(),
gpu: Gpu::new(cgb, output.window, output.tile_window), gpu: Gpu::new(cgb, output.window, output.tile_window),
apu: Apu::new(output.audio), apu: Apu::new(output.audio),
@ -229,7 +233,7 @@ where
T: Into<Address>, T: Into<Address>,
{ {
let address: Address = address.into(); let address: Address = address.into();
if self.dma.is_active() && self.user_mode { if self.oam_dma.is_active() && self.user_mode {
if let Address::Hram(_) = address { if let Address::Hram(_) = address {
} else if let Address::Io(IoAddress::Video(v)) = address { } else if let Address::Io(IoAddress::Video(v)) = address {
if v.inner() == 0xFF46 { if v.inner() == 0xFF46 {
@ -276,7 +280,7 @@ where
T: Into<Address>, T: Into<Address>,
{ {
let address: Address = address.into(); let address: Address = address.into();
if self.dma.is_active() && self.user_mode { if self.oam_dma.is_active() && self.user_mode {
if let Address::Hram(_) = address { if let Address::Hram(_) = address {
} else if let Address::Io(IoAddress::Video(v)) = address { } else if let Address::Io(IoAddress::Video(v)) = address {
if v.inner() == 0xFF46 { if v.inner() == 0xFF46 {
@ -340,7 +344,7 @@ where
0xFF43 => self.gpu.get_scx(), 0xFF43 => self.gpu.get_scx(),
0xFF44 => self.gpu.get_ly(), 0xFF44 => self.gpu.get_ly(),
0xFF45 => self.gpu.get_lyc(), 0xFF45 => self.gpu.get_lyc(),
0xFF46 => self.dma.get_register(), 0xFF46 => self.oam_dma.get_register(),
0xFF47 => self.gpu.get_bg_palette(), 0xFF47 => self.gpu.get_bg_palette(),
0xFF48 => self.gpu.get_obj_palette_0(), 0xFF48 => self.gpu.get_obj_palette_0(),
0xFF49 => self.gpu.get_obj_palette_1(), 0xFF49 => self.gpu.get_obj_palette_1(),
@ -349,11 +353,11 @@ where
0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(), 0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(),
}, },
IoAddress::Cgb(address) => { IoAddress::Cgb(address) => {
if let WramBanks::Cgb { banks: _, selected } = &self.ram.banks { if let WramBanks::Cgb { banks: _, selected } = &self.ram.banks && let Some(vram_dma) = &self.vram_dma {
match address { match address {
CgbIoAddress::PrepareSpeed => todo!(), CgbIoAddress::PrepareSpeed => todo!(),
CgbIoAddress::VramBank => self.gpu.vram.get_vram_bank(), CgbIoAddress::VramBank => self.gpu.vram.get_vram_bank(),
CgbIoAddress::VramDma(_) => todo!(), CgbIoAddress::VramDma(address) => vram_dma.get_register(address),
CgbIoAddress::Infrared => todo!(), CgbIoAddress::Infrared => todo!(),
CgbIoAddress::Palette(address) => self.gpu.get_cgb_palette(address), CgbIoAddress::Palette(address) => self.gpu.get_cgb_palette(address),
CgbIoAddress::ObjPriority => self.gpu.get_obj_priority(), CgbIoAddress::ObjPriority => self.gpu.get_obj_priority(),
@ -399,9 +403,7 @@ where
0xFF43 => self.gpu.update_scx(data), 0xFF43 => self.gpu.update_scx(data),
0xFF44 => {} 0xFF44 => {}
0xFF45 => self.gpu.update_lyc(data), 0xFF45 => self.gpu.update_lyc(data),
0xFF46 => { 0xFF46 => self.oam_dma.set_register(data),
self.dma.set_register(data);
}
0xFF47 => self.gpu.update_bg_palette(data), 0xFF47 => self.gpu.update_bg_palette(data),
0xFF48 => self.gpu.update_obj_palette_0(data), 0xFF48 => self.gpu.update_obj_palette_0(data),
0xFF49 => self.gpu.update_obj_palette_1(data), 0xFF49 => self.gpu.update_obj_palette_1(data),
@ -410,11 +412,11 @@ where
0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(), 0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(),
}, },
IoAddress::Cgb(address) => { IoAddress::Cgb(address) => {
if let WramBanks::Cgb { banks: _, selected } = &mut self.ram.banks { if let WramBanks::Cgb { banks: _, selected } = &mut self.ram.banks && let Some(vram_dma) = &mut self.vram_dma {
match address { match address {
CgbIoAddress::PrepareSpeed => todo!(), CgbIoAddress::PrepareSpeed => todo!(),
CgbIoAddress::VramBank => self.gpu.vram.set_vram_bank(data), CgbIoAddress::VramBank => self.gpu.vram.set_vram_bank(data),
CgbIoAddress::VramDma(_) => todo!(), CgbIoAddress::VramDma(address) => vram_dma.set_register(address, data),
CgbIoAddress::Infrared => todo!(), CgbIoAddress::Infrared => todo!(),
CgbIoAddress::Palette(address) => self.gpu.set_cgb_palette(address, data), CgbIoAddress::Palette(address) => self.gpu.set_cgb_palette(address, data),
CgbIoAddress::ObjPriority => self.gpu.set_obj_priority(data), CgbIoAddress::ObjPriority => self.gpu.set_obj_priority(data),
@ -422,8 +424,7 @@ where
CgbIoAddress::Pcm12 => todo!(), CgbIoAddress::Pcm12 => todo!(),
CgbIoAddress::Pcm34 => todo!(), CgbIoAddress::Pcm34 => todo!(),
CgbIoAddress::Unused(v) => { CgbIoAddress::Unused(v) => {
println!("attempt to set 0x{v:0>4X} to 0x{data:0>2X}"); println!("attempt to set 0x{v:0>4X} to 0x{data:0>2X}")
todo!()
} }
} }
} }
@ -491,7 +492,8 @@ where
ime: state.ime, ime: state.ime,
ime_scheduled: state.ime_scheduled, ime_scheduled: state.ime_scheduled,
user_mode: state.user_mode, user_mode: state.user_mode,
dma: state.dma, oam_dma: state.oam_dma,
vram_dma: state.vram_dma,
joypad: state.joypad, joypad: state.joypad,
gpu: Gpu::from_save_state(state.gpu, window, None), gpu: Gpu::from_save_state(state.gpu, window, None),
apu: Apu::from_save_state(state.apu, output), apu: Apu::from_save_state(state.apu, output),
@ -513,7 +515,7 @@ where
let steps = (machine_cycles as usize) * 4; let steps = (machine_cycles as usize) * 4;
self.cycle_count += steps; self.cycle_count += steps;
self.memory.oam_tick(steps); self.memory.oam_dma_tick(steps);
self.memory.camera.lock().unwrap().tick(steps); self.memory.camera.lock().unwrap().tick(steps);

View file

@ -1,11 +1,12 @@
use std::marker::PhantomData; use std::marker::PhantomData;
pub(crate) use self::types::DrawMode;
use self::{ use self::{
cgb::CgbData, cgb::CgbData,
tile_window::TileWindow, tile_window::TileWindow,
types::{ types::{
DrawMode, GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette, GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette, Stat,
Stat, TiledataArea, TilemapArea, Vram, TiledataArea, TilemapArea, Vram,
}, },
}; };
use crate::{ use crate::{
@ -197,6 +198,10 @@ where
} }
} }
pub(crate) fn get_mode(&self) -> DrawMode {
self.stat.mode
}
pub fn tick(&mut self, steps: usize) -> GpuInterrupts { pub fn tick(&mut self, steps: usize) -> GpuInterrupts {
let mut interrupts = GpuInterrupts::default(); let mut interrupts = GpuInterrupts::default();
if self.lcdc.enable { if self.lcdc.enable {

View file

@ -6,7 +6,7 @@ use crate::{
}; };
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
pub(super) enum DrawMode { pub(crate) enum DrawMode {
HBlank, HBlank,
VBlank, VBlank,
Mode2, Mode2,

View file

@ -4,9 +4,11 @@ pub(crate) mod joypad;
mod oam_dma; mod oam_dma;
pub(crate) mod serial; pub(crate) mod serial;
mod timer; mod timer;
mod vram_dma;
pub use apu::Apu; pub use apu::Apu;
pub use gpu::Gpu; pub use gpu::Gpu;
pub use joypad::Joypad; pub use joypad::Joypad;
pub use oam_dma::OamDma; pub use oam_dma::OamDma;
pub use serial::Serial; pub use serial::Serial;
pub use timer::Timer; pub use timer::Timer;
pub use vram_dma::VramDma;

View file

@ -41,13 +41,13 @@ where
R: Renderer<ColourFormat>, R: Renderer<ColourFormat>,
C: PocketCamera + Send + 'static, C: PocketCamera + Send + 'static,
{ {
pub(crate) fn oam_tick(&mut self, steps: usize) { pub(crate) fn oam_dma_tick(&mut self, steps: usize) {
for _ in 0..steps { for _ in 0..steps {
self.dma.progress = if let Some(mut progress) = self.dma.progress { self.oam_dma.progress = if let Some(mut progress) = self.oam_dma.progress {
let mut addr: u16 = 0x0; let mut addr: u16 = 0x0;
addr.set_high(self.dma.addr); addr.set_high(self.oam_dma.addr);
addr.set_low(progress); addr.set_low(progress);
let val = if self.dma.addr > 0xDF { let val = if self.oam_dma.addr > 0xDF {
0xFF 0xFF
} else { } else {
self.get(addr) self.get(addr)

View file

@ -0,0 +1,125 @@
use super::gpu::{Colour, DrawMode};
use crate::{
connect::{PocketCamera, Renderer},
processor::{
memory::{
addresses::{AddressMarker, VramDmaAddress},
Memory,
},
SplitRegister,
},
util::get_bit,
};
use serde::{Deserialize, Serialize};
#[derive(Default, Serialize, Deserialize, Clone, Copy)]
pub struct VramDma {
source: u16,
destination: u16,
mode: DmaMode,
}
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Debug)]
enum DmaMode {
Halt(u8),
Hblank(u8, u16),
Waiting,
}
impl DmaMode {
fn get_byte(&self) -> u8 {
match self {
DmaMode::Halt(_) => unreachable!(),
DmaMode::Hblank(v, _) => *v,
DmaMode::Waiting => 0xFF,
}
}
}
impl Default for DmaMode {
fn default() -> Self {
Self::Waiting
}
}
impl VramDma {
pub(crate) fn get_register(&self, address: VramDmaAddress) -> u8 {
match address.inner() {
0xFF51 => self.source.get_high(),
0xFF52 => self.source.get_low(),
0xFF53 => self.destination.get_high(),
0xFF54 => self.destination.get_low(),
0xFF55 => self.mode.get_byte(),
_ => unreachable!(),
}
}
pub(crate) fn set_register(&mut self, address: VramDmaAddress, data: u8) {
match address.inner() {
0xFF51 => self.source.set_high(data),
0xFF52 => self.source.set_low(data & 0xF0),
0xFF53 => self.destination.set_high((data & 0b11111) | (0b1 << 7)),
0xFF54 => self.destination.set_low(data & 0xF0),
0xFF55 => {
let num = data & !(0b1 << 7);
self.mode = if get_bit(data, 7) {
DmaMode::Hblank(num, 0)
} else if self.mode == DmaMode::Waiting {
DmaMode::Halt(num)
} else {
DmaMode::Waiting
};
println!("vram dma - entered mode {:?}", self.mode);
println!(
" for transfer from 0x{:0>4X} to 0x{:0>4X}",
self.source, self.destination
);
println!(" num bytes/lines: 0x{num:X}");
}
_ => unreachable!(),
}
}
}
impl<ColourFormat, R, C> Memory<ColourFormat, R, C>
where
ColourFormat: From<Colour> + Clone,
R: Renderer<ColourFormat>,
C: PocketCamera + Send + 'static,
{
pub(crate) fn vram_dma_tick(&mut self) -> usize {
let mut copy = None;
let returning = if let Some(vram_dma) = &mut self.vram_dma {
match vram_dma.mode {
DmaMode::Halt(l) => {
copy = Some((vram_dma.destination, vram_dma.source, 16 * ((l as u16) + 1)));
vram_dma.mode = DmaMode::Waiting;
((l as usize) + 1) * 8
}
DmaMode::Hblank(l, ref mut progress) => {
if self.gpu.get_mode() == DrawMode::HBlank {
let offset = *progress * 16;
copy = Some((vram_dma.destination + offset, vram_dma.source + offset, 16));
*progress += 1;
if *progress > (l as u16) {
vram_dma.mode = DmaMode::Waiting
}
8
} else {
0
}
}
DmaMode::Waiting => 0,
}
} else {
0
};
if let Some((source, dest, length)) = copy {
println!("copying 0x{length:X} bytes from {source:0>4X} to {dest:0>4X}");
for i in 0..length {
self.set(dest + i, self.get(source + i));
}
}
returning
}
}

View file

@ -97,6 +97,20 @@ where
return; return;
} }
// double this if in double speed mode
let mut vram_dma_cycles = self.memory.vram_dma_tick();
while vram_dma_cycles > 0 {
let cycles = if vram_dma_cycles > 0xFF {
0xFF
} else {
vram_dma_cycles as u8
};
vram_dma_cycles -= cycles as usize;
self.increment_timers(cycles);
let interrupt_cycles = self.handle_interrupts();
self.increment_timers(interrupt_cycles);
}
if self.memory.ime_scheduled > 0 { if self.memory.ime_scheduled > 0 {
self.memory.ime_scheduled = self.memory.ime_scheduled.saturating_sub(1); self.memory.ime_scheduled = self.memory.ime_scheduled.saturating_sub(1);
if self.memory.ime_scheduled == 0 { if self.memory.ime_scheduled == 0 {