oam dma lockout

This commit is contained in:
Alex Janka 2023-04-19 17:28:18 +10:00
parent 828da3e01f
commit e805f32380
7 changed files with 157 additions and 34 deletions

View file

@ -5,13 +5,12 @@ use self::{
apu::ApuSaveState, apu::ApuSaveState,
gpu::{Colour, GpuSaveState}, gpu::{Colour, GpuSaveState},
serial::SerialSaveState, serial::SerialSaveState,
Apu, Gpu, Joypad, Serial, Timer, Apu, Gpu, Joypad, OamDma, Serial, Timer,
}, },
rom::RomSaveState, rom::RomSaveState,
}; };
use crate::{ use crate::{
connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget}, connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget},
processor::SplitRegister,
Cpu, Cpu,
}; };
@ -35,7 +34,8 @@ where
pub(super) interrupts: Interrupts, pub(super) interrupts: Interrupts,
pub(super) ime: bool, pub(super) ime: bool,
pub(super) ime_scheduled: u8, pub(super) ime_scheduled: u8,
dma_addr: u8, pub(super) user_mode: bool,
dma: OamDma,
joypad: Joypad, joypad: Joypad,
gpu: Gpu<ColourFormat, R>, gpu: Gpu<ColourFormat, R>,
apu: Apu, apu: Apu,
@ -59,7 +59,8 @@ where
pub(super) interrupts: Interrupts, pub(super) interrupts: Interrupts,
pub(super) ime: bool, pub(super) ime: bool,
pub(super) ime_scheduled: u8, pub(super) ime_scheduled: u8,
dma_addr: u8, pub(super) user_mode: bool,
dma: OamDma,
joypad: Joypad, joypad: Joypad,
gpu: GpuSaveState<ColourFormat, R>, gpu: GpuSaveState<ColourFormat, R>,
apu: ApuSaveState, apu: ApuSaveState,
@ -81,7 +82,8 @@ where
interrupts: memory.interrupts, interrupts: memory.interrupts,
ime: memory.ime, ime: memory.ime,
ime_scheduled: memory.ime_scheduled, ime_scheduled: memory.ime_scheduled,
dma_addr: memory.dma_addr, user_mode: memory.user_mode,
dma: memory.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),
@ -114,7 +116,8 @@ where
interrupts: Interrupts::default(), interrupts: Interrupts::default(),
ime: false, ime: false,
ime_scheduled: 0x0, ime_scheduled: 0x0,
dma_addr: 0xFF, user_mode: false,
dma: OamDma::default(),
joypad: Joypad::default(), joypad: Joypad::default(),
gpu: Gpu::new(window, tile_window), gpu: Gpu::new(window, tile_window),
apu: Apu::new(output), apu: Apu::new(output),
@ -129,6 +132,17 @@ 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 let Address::Hram(_) = address {
} else if let Address::Io(IoAddress::Video(v)) = address {
if v.inner() == 0xFF46 {
} else {
return 0xFF;
}
} else {
return 0xFF;
}
}
match address { match address {
Address::Rom(address) => { Address::Rom(address) => {
// rom access // rom access
@ -139,12 +153,12 @@ where
self.rom.get(address) self.rom.get(address)
} }
} }
Address::Vram(address) => self.gpu.vram.get(address), Address::Vram(address) => self.gpu.get_vram(address),
Address::CartRam(address) => self.rom.get_ram(address), Address::CartRam(address) => self.rom.get_ram(address),
Address::WorkRam(address) => self.ram[address.get_local() as usize], Address::WorkRam(address) => self.ram[address.get_local() as usize],
Address::BankedWorkRam(address) => self.ram[(address.get_local() + 0x1000) as usize], Address::BankedWorkRam(address) => self.ram[(address.get_local() + 0x1000) as usize],
Address::MirroredRam(address) => self.ram[address.get_local() as usize], Address::MirroredRam(address) => self.ram[address.get_local() as usize],
Address::Oam(address) => self.gpu.oam.get(address), Address::Oam(address) => self.gpu.get_oam(address),
Address::Prohibited(_) => 0xFF, Address::Prohibited(_) => 0xFF,
Address::Io(address) => self.get_io(address), Address::Io(address) => self.get_io(address),
Address::Hram(address) => self.cpu_ram[address.get_local() as usize], Address::Hram(address) => self.cpu_ram[address.get_local() as usize],
@ -157,6 +171,17 @@ 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 let Address::Hram(_) = address {
} else if let Address::Io(IoAddress::Video(v)) = address {
if v.inner() == 0xFF46 {
} else {
return;
}
} else {
return;
}
}
match address { match address {
Address::Rom(address) => { Address::Rom(address) => {
self.rom.set(address, data); self.rom.set(address, data);
@ -165,14 +190,14 @@ where
self.gpu.window.set_rumble(self.rom.is_rumbling()) self.gpu.window.set_rumble(self.rom.is_rumbling())
} }
} }
Address::Vram(address) => self.gpu.vram.set(address, data), Address::Vram(address) => self.gpu.set_vram(address, data),
Address::CartRam(address) => self.rom.set_ram(address, data), Address::CartRam(address) => self.rom.set_ram(address, data),
Address::WorkRam(address) => self.ram[address.get_local() as usize] = data, Address::WorkRam(address) => self.ram[address.get_local() as usize] = data,
Address::BankedWorkRam(address) => { Address::BankedWorkRam(address) => {
self.ram[(address.get_local() + 0x1000) as usize] = data self.ram[(address.get_local() + 0x1000) as usize] = data
} }
Address::MirroredRam(address) => self.ram[address.get_local() as usize] = data, Address::MirroredRam(address) => self.ram[address.get_local() as usize] = data,
Address::Oam(address) => self.gpu.oam.set(address, data), Address::Oam(address) => self.gpu.set_oam(address, data),
Address::Prohibited(_) => {} Address::Prohibited(_) => {}
Address::Io(address) => { Address::Io(address) => {
if address.inner() == 0xFF50 { if address.inner() == 0xFF50 {
@ -210,13 +235,13 @@ 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_addr, 0xFF46 => self.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(),
0xFF4A => self.gpu.get_wy(), 0xFF4A => self.gpu.get_wy(),
0xFF4B => self.gpu.get_wx(), 0xFF4B => self.gpu.get_wx(),
_ => unreachable!(), 0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(),
}, },
IoAddress::Unused(_) => 0xFF, IoAddress::Unused(_) => 0xFF,
} }
@ -245,25 +270,17 @@ where
0xFF41 => self.gpu.update_lcd_status(data), 0xFF41 => self.gpu.update_lcd_status(data),
0xFF42 => self.gpu.update_scy(data), 0xFF42 => self.gpu.update_scy(data),
0xFF43 => self.gpu.update_scx(data), 0xFF43 => self.gpu.update_scx(data),
0xFF44 => {}
0xFF45 => self.gpu.update_lyc(data), 0xFF45 => self.gpu.update_lyc(data),
0xFF46 => { 0xFF46 => {
if data > 0xDF { self.dma.set_register(data);
panic!("dma transfer out of bounds: {data:#X}");
}
self.dma_addr = data;
let mut addr: u16 = 0x0;
addr.set_high(data);
for l in 0x0..0xA0 {
addr.set_low(l);
self.gpu.oam.data[l as usize] = self.get(addr);
}
} }
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),
0xFF4A => self.gpu.update_wy(data), 0xFF4A => self.gpu.update_wy(data),
0xFF4B => self.gpu.update_wx(data), 0xFF4B => self.gpu.update_wx(data),
_ => unreachable!(), 0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(),
}, },
IoAddress::Unused(_) => {} IoAddress::Unused(_) => {}
} }
@ -327,7 +344,8 @@ where
interrupts: state.interrupts, interrupts: state.interrupts,
ime: state.ime, ime: state.ime,
ime_scheduled: state.ime_scheduled, ime_scheduled: state.ime_scheduled,
dma_addr: state.dma_addr, user_mode: state.user_mode,
dma: state.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),
@ -347,6 +365,8 @@ where
pub fn increment_timers(&mut self, machine_cycles: u8) { pub fn increment_timers(&mut self, machine_cycles: u8) {
let steps = (machine_cycles as usize) * 4; let steps = (machine_cycles as usize) * 4;
self.memory.oam_tick(steps);
self.memory.camera.lock().unwrap().tick(steps); self.memory.camera.lock().unwrap().tick(steps);
let timer_return = self.memory.timers.tick(steps); let timer_return = self.memory.timers.tick(steps);

View file

@ -5,7 +5,7 @@ pub(crate) enum AddressError {
OutOfBounds, OutOfBounds,
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
pub(crate) struct BoundedAddress<const MIN: u16, const MAX: u16>(u16); pub(crate) struct BoundedAddress<const MIN: u16, const MAX: u16>(u16);
impl<const MIN: u16, const MAX: u16> Add<u16> for BoundedAddress<MIN, MAX> { impl<const MIN: u16, const MAX: u16> Add<u16> for BoundedAddress<MIN, MAX> {

View file

@ -255,7 +255,7 @@ impl Apu {
0xFF13 | 0xFF18 | 0xFF1B | 0xFF1D | 0xFF20 => 0xFF, 0xFF13 | 0xFF18 | 0xFF1B | 0xFF1D | 0xFF20 => 0xFF,
// not registers // not registers
0xFF15 | 0xFF1F => 0xFF, 0xFF15 | 0xFF1F => 0xFF,
_ => unreachable!(), 0x0..0xFF10 | 0xFF27..=0xFFFF => unreachable!(),
} }
} }
@ -266,6 +266,7 @@ impl Apu {
0xFF12 => self.channels.one.update_volume_and_envelope(data), 0xFF12 => self.channels.one.update_volume_and_envelope(data),
0xFF13 => self.channels.one.update_wavelength_low(data), 0xFF13 => self.channels.one.update_wavelength_low(data),
0xFF14 => self.channels.one.update_wavelength_high_and_control(data), 0xFF14 => self.channels.one.update_wavelength_high_and_control(data),
0xFF15 => {}
0xFF16 => self.channels.two.update_length_timer_and_duty_cycle(data), 0xFF16 => self.channels.two.update_length_timer_and_duty_cycle(data),
0xFF17 => self.channels.two.update_volume_and_envelope(data), 0xFF17 => self.channels.two.update_volume_and_envelope(data),
0xFF18 => self.channels.two.update_wavelength_low(data), 0xFF18 => self.channels.two.update_wavelength_low(data),
@ -275,6 +276,7 @@ impl Apu {
0xFF1C => self.channels.three.update_volume(data), 0xFF1C => self.channels.three.update_volume(data),
0xFF1D => self.channels.three.update_wavelength_low(data), 0xFF1D => self.channels.three.update_wavelength_low(data),
0xFF1E => self.channels.three.update_wavelength_high_and_control(data), 0xFF1E => self.channels.three.update_wavelength_high_and_control(data),
0xFF1F => {}
0xFF20 => self.channels.four.update_length_timer(data), 0xFF20 => self.channels.four.update_length_timer(data),
0xFF21 => self.channels.four.update_volume_and_envelope(data), 0xFF21 => self.channels.four.update_volume_and_envelope(data),
0xFF22 => self.channels.four.update_frequency_and_randomness(data), 0xFF22 => self.channels.four.update_frequency_and_randomness(data),
@ -306,7 +308,7 @@ impl Apu {
} }
self.apu_enable = (1 << 7) == (data & 0b10000000); self.apu_enable = (1 << 7) == (data & 0b10000000);
} }
_ => unreachable!(), 0x0..0xFF10 | 0xFF27..=0xFFFF => unreachable!(),
} }
} }

View file

@ -9,7 +9,10 @@ use self::{
}; };
use crate::{ use crate::{
connect::Renderer, connect::Renderer,
processor::SplitRegister, processor::{
memory::addresses::{OamAddress, VramAddress},
SplitRegister,
},
util::{clear_bit, get_bit}, util::{clear_bit, get_bit},
HEIGHT, WIDTH, HEIGHT, WIDTH,
}; };
@ -247,6 +250,34 @@ where
interrupts interrupts
} }
pub(crate) fn get_vram(&self, address: VramAddress) -> u8 {
if self.stat.mode != DrawMode::Mode3 {
self.vram.get(address)
} else {
0xFF
}
}
pub(crate) fn set_vram(&mut self, address: VramAddress, data: u8) {
if self.stat.mode != DrawMode::Mode3 {
self.vram.set(address, data)
}
}
pub(crate) fn get_oam(&self, address: OamAddress) -> u8 {
if self.stat.mode == DrawMode::VBlank || self.stat.mode == DrawMode::HBlank {
self.oam.get(address)
} else {
0xFF
}
}
pub(crate) fn set_oam(&mut self, address: OamAddress, data: u8) {
if self.stat.mode == DrawMode::VBlank || self.stat.mode == DrawMode::HBlank {
self.oam.set(address, data)
}
}
fn enter_hblank(&mut self) { fn enter_hblank(&mut self) {
self.stat.mode = DrawMode::HBlank; self.stat.mode = DrawMode::HBlank;
self.render_scanline(self.scanline); self.render_scanline(self.scanline);

View file

@ -1,10 +1,12 @@
pub(crate) mod apu; pub(crate) mod apu;
pub(crate) mod gpu; pub(crate) mod gpu;
pub(crate) mod joypad; pub(crate) mod joypad;
mod oam_dma;
pub(crate) mod serial; pub(crate) mod serial;
mod timer; mod timer;
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 serial::Serial; pub use serial::Serial;
pub use timer::Timer; pub use timer::Timer;

View file

@ -0,0 +1,67 @@
use super::gpu::Colour;
use crate::{
connect::{PocketCamera, Renderer},
processor::{memory::Memory, SplitRegister},
};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Copy)]
pub struct OamDma {
addr: u8,
progress: Option<u8>,
}
impl Default for OamDma {
fn default() -> Self {
Self {
addr: 0xFF,
progress: None,
}
}
}
impl OamDma {
pub(crate) fn get_register(&self) -> u8 {
self.addr
}
pub(crate) fn set_register(&mut self, data: u8) {
self.progress = Some(0);
self.addr = data;
}
pub(crate) fn is_active(&self) -> bool {
self.progress.is_some()
}
}
impl<ColourFormat, R, C> Memory<ColourFormat, R, C>
where
ColourFormat: From<Colour> + Clone,
R: Renderer<ColourFormat>,
C: PocketCamera + Send + 'static,
{
pub(crate) fn oam_tick(&mut self, steps: usize) {
for _ in 0..steps {
self.dma.progress = if let Some(mut progress) = self.dma.progress {
let mut addr: u16 = 0x0;
addr.set_high(self.dma.addr);
addr.set_low(progress);
let val = if self.dma.addr > 0xDF {
0xFF
} else {
self.get(addr)
};
self.gpu.oam.data[progress as usize] = val;
progress += 1;
if progress == 0xA0 {
None
} else {
Some(progress)
}
} else {
None
}
}
}
}

View file

@ -104,7 +104,10 @@ where
} }
self.last_instruction_addr = self.reg.pc; self.last_instruction_addr = self.reg.pc;
self.memory.user_mode = true;
let opcode = self.next_opcode(); let opcode = self.next_opcode();
if self.should_halt_bug { if self.should_halt_bug {
self.reg.pc = self.reg.pc.wrapping_sub(0x1); self.reg.pc = self.reg.pc.wrapping_sub(0x1);
self.should_halt_bug = false; self.should_halt_bug = false;
@ -116,7 +119,10 @@ where
opcode, opcode,
self.last_instruction_addr self.last_instruction_addr
); );
self.run_and_increment_timers(opcode);
let cycles = self.run_opcode(opcode);
self.memory.user_mode = false;
self.increment_timers(cycles);
let interrupt_cycles = self.handle_interrupts(); let interrupt_cycles = self.handle_interrupts();
self.increment_timers(interrupt_cycles); self.increment_timers(interrupt_cycles);
@ -128,11 +134,6 @@ where
opcode opcode
} }
fn run_and_increment_timers(&mut self, opcode: u8) {
let cycles = self.run_opcode(opcode);
self.increment_timers(cycles);
}
fn halt(&mut self) { fn halt(&mut self) {
if !self.memory.ime && self.memory.interrupts.is_interrupt_queued() { if !self.memory.ime && self.memory.interrupts.is_interrupt_queued() {
// halt bug // halt bug