vram dma transfer might work?
This commit is contained in:
parent
2a5d8b816c
commit
9ff6196ecf
|
@ -7,7 +7,7 @@ use self::{
|
|||
apu::ApuSaveState,
|
||||
gpu::{Colour, GpuSaveState},
|
||||
serial::SerialSaveState,
|
||||
Apu, Gpu, Joypad, OamDma, Serial, Timer,
|
||||
Apu, Gpu, Joypad, OamDma, Serial, Timer, VramDma,
|
||||
},
|
||||
rom::RomSaveState,
|
||||
};
|
||||
|
@ -94,7 +94,8 @@ where
|
|||
pub(super) ime: bool,
|
||||
pub(super) ime_scheduled: u8,
|
||||
pub(super) user_mode: bool,
|
||||
dma: OamDma,
|
||||
oam_dma: OamDma,
|
||||
vram_dma: Option<VramDma>,
|
||||
joypad: Joypad,
|
||||
gpu: Gpu<ColourFormat, R>,
|
||||
apu: Apu,
|
||||
|
@ -119,7 +120,8 @@ where
|
|||
pub(super) ime: bool,
|
||||
pub(super) ime_scheduled: u8,
|
||||
pub(super) user_mode: bool,
|
||||
dma: OamDma,
|
||||
oam_dma: OamDma,
|
||||
vram_dma: Option<VramDma>,
|
||||
joypad: Joypad,
|
||||
gpu: GpuSaveState<ColourFormat, R>,
|
||||
apu: ApuSaveState,
|
||||
|
@ -143,7 +145,8 @@ where
|
|||
ime: memory.ime,
|
||||
ime_scheduled: memory.ime_scheduled,
|
||||
user_mode: memory.user_mode,
|
||||
dma: memory.dma,
|
||||
oam_dma: memory.oam_dma,
|
||||
vram_dma: memory.vram_dma,
|
||||
joypad: memory.joypad,
|
||||
gpu: GpuSaveState::create(&memory.gpu),
|
||||
apu: ApuSaveState::create(&memory.apu),
|
||||
|
@ -213,7 +216,8 @@ where
|
|||
ime: false,
|
||||
ime_scheduled: 0x0,
|
||||
user_mode: false,
|
||||
dma: OamDma::default(),
|
||||
oam_dma: OamDma::default(),
|
||||
vram_dma: if cgb { Some(VramDma::default()) } else { None },
|
||||
joypad: Joypad::default(),
|
||||
gpu: Gpu::new(cgb, output.window, output.tile_window),
|
||||
apu: Apu::new(output.audio),
|
||||
|
@ -229,7 +233,7 @@ where
|
|||
T: Into<Address>,
|
||||
{
|
||||
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 {
|
||||
} else if let Address::Io(IoAddress::Video(v)) = address {
|
||||
if v.inner() == 0xFF46 {
|
||||
|
@ -276,7 +280,7 @@ where
|
|||
T: Into<Address>,
|
||||
{
|
||||
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 {
|
||||
} else if let Address::Io(IoAddress::Video(v)) = address {
|
||||
if v.inner() == 0xFF46 {
|
||||
|
@ -340,7 +344,7 @@ where
|
|||
0xFF43 => self.gpu.get_scx(),
|
||||
0xFF44 => self.gpu.get_ly(),
|
||||
0xFF45 => self.gpu.get_lyc(),
|
||||
0xFF46 => self.dma.get_register(),
|
||||
0xFF46 => self.oam_dma.get_register(),
|
||||
0xFF47 => self.gpu.get_bg_palette(),
|
||||
0xFF48 => self.gpu.get_obj_palette_0(),
|
||||
0xFF49 => self.gpu.get_obj_palette_1(),
|
||||
|
@ -349,11 +353,11 @@ where
|
|||
0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(),
|
||||
},
|
||||
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 {
|
||||
CgbIoAddress::PrepareSpeed => todo!(),
|
||||
CgbIoAddress::VramBank => self.gpu.vram.get_vram_bank(),
|
||||
CgbIoAddress::VramDma(_) => todo!(),
|
||||
CgbIoAddress::VramDma(address) => vram_dma.get_register(address),
|
||||
CgbIoAddress::Infrared => todo!(),
|
||||
CgbIoAddress::Palette(address) => self.gpu.get_cgb_palette(address),
|
||||
CgbIoAddress::ObjPriority => self.gpu.get_obj_priority(),
|
||||
|
@ -399,9 +403,7 @@ where
|
|||
0xFF43 => self.gpu.update_scx(data),
|
||||
0xFF44 => {}
|
||||
0xFF45 => self.gpu.update_lyc(data),
|
||||
0xFF46 => {
|
||||
self.dma.set_register(data);
|
||||
}
|
||||
0xFF46 => self.oam_dma.set_register(data),
|
||||
0xFF47 => self.gpu.update_bg_palette(data),
|
||||
0xFF48 => self.gpu.update_obj_palette_0(data),
|
||||
0xFF49 => self.gpu.update_obj_palette_1(data),
|
||||
|
@ -410,11 +412,11 @@ where
|
|||
0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(),
|
||||
},
|
||||
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 {
|
||||
CgbIoAddress::PrepareSpeed => todo!(),
|
||||
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::Palette(address) => self.gpu.set_cgb_palette(address, data),
|
||||
CgbIoAddress::ObjPriority => self.gpu.set_obj_priority(data),
|
||||
|
@ -422,8 +424,7 @@ where
|
|||
CgbIoAddress::Pcm12 => todo!(),
|
||||
CgbIoAddress::Pcm34 => todo!(),
|
||||
CgbIoAddress::Unused(v) => {
|
||||
println!("attempt to set 0x{v:0>4X} to 0x{data:0>2X}");
|
||||
todo!()
|
||||
println!("attempt to set 0x{v:0>4X} to 0x{data:0>2X}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -491,7 +492,8 @@ where
|
|||
ime: state.ime,
|
||||
ime_scheduled: state.ime_scheduled,
|
||||
user_mode: state.user_mode,
|
||||
dma: state.dma,
|
||||
oam_dma: state.oam_dma,
|
||||
vram_dma: state.vram_dma,
|
||||
joypad: state.joypad,
|
||||
gpu: Gpu::from_save_state(state.gpu, window, None),
|
||||
apu: Apu::from_save_state(state.apu, output),
|
||||
|
@ -513,7 +515,7 @@ where
|
|||
let steps = (machine_cycles as usize) * 4;
|
||||
self.cycle_count += steps;
|
||||
|
||||
self.memory.oam_tick(steps);
|
||||
self.memory.oam_dma_tick(steps);
|
||||
|
||||
self.memory.camera.lock().unwrap().tick(steps);
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
pub(crate) use self::types::DrawMode;
|
||||
use self::{
|
||||
cgb::CgbData,
|
||||
tile_window::TileWindow,
|
||||
types::{
|
||||
DrawMode, GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette,
|
||||
Stat, TiledataArea, TilemapArea, Vram,
|
||||
GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette, Stat,
|
||||
TiledataArea, TilemapArea, Vram,
|
||||
},
|
||||
};
|
||||
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 {
|
||||
let mut interrupts = GpuInterrupts::default();
|
||||
if self.lcdc.enable {
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)]
|
||||
pub(super) enum DrawMode {
|
||||
pub(crate) enum DrawMode {
|
||||
HBlank,
|
||||
VBlank,
|
||||
Mode2,
|
||||
|
|
|
@ -4,9 +4,11 @@ pub(crate) mod joypad;
|
|||
mod oam_dma;
|
||||
pub(crate) mod serial;
|
||||
mod timer;
|
||||
mod vram_dma;
|
||||
pub use apu::Apu;
|
||||
pub use gpu::Gpu;
|
||||
pub use joypad::Joypad;
|
||||
pub use oam_dma::OamDma;
|
||||
pub use serial::Serial;
|
||||
pub use timer::Timer;
|
||||
pub use vram_dma::VramDma;
|
||||
|
|
|
@ -41,13 +41,13 @@ where
|
|||
R: Renderer<ColourFormat>,
|
||||
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 {
|
||||
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;
|
||||
addr.set_high(self.dma.addr);
|
||||
addr.set_high(self.oam_dma.addr);
|
||||
addr.set_low(progress);
|
||||
let val = if self.dma.addr > 0xDF {
|
||||
let val = if self.oam_dma.addr > 0xDF {
|
||||
0xFF
|
||||
} else {
|
||||
self.get(addr)
|
||||
|
|
125
lib/src/processor/memory/mmio/vram_dma.rs
Normal file
125
lib/src/processor/memory/mmio/vram_dma.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -97,6 +97,20 @@ where
|
|||
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 {
|
||||
self.memory.ime_scheduled = self.memory.ime_scheduled.saturating_sub(1);
|
||||
if self.memory.ime_scheduled == 0 {
|
||||
|
|
Loading…
Reference in a new issue