vram dma transfer might work?
This commit is contained in:
parent
2a5d8b816c
commit
9ff6196ecf
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
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;
|
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 {
|
||||||
|
|
Loading…
Reference in a new issue