oam dma lockout
This commit is contained in:
parent
828da3e01f
commit
e805f32380
|
@ -5,13 +5,12 @@ use self::{
|
|||
apu::ApuSaveState,
|
||||
gpu::{Colour, GpuSaveState},
|
||||
serial::SerialSaveState,
|
||||
Apu, Gpu, Joypad, Serial, Timer,
|
||||
Apu, Gpu, Joypad, OamDma, Serial, Timer,
|
||||
},
|
||||
rom::RomSaveState,
|
||||
};
|
||||
use crate::{
|
||||
connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget},
|
||||
processor::SplitRegister,
|
||||
Cpu,
|
||||
};
|
||||
|
||||
|
@ -35,7 +34,8 @@ where
|
|||
pub(super) interrupts: Interrupts,
|
||||
pub(super) ime: bool,
|
||||
pub(super) ime_scheduled: u8,
|
||||
dma_addr: u8,
|
||||
pub(super) user_mode: bool,
|
||||
dma: OamDma,
|
||||
joypad: Joypad,
|
||||
gpu: Gpu<ColourFormat, R>,
|
||||
apu: Apu,
|
||||
|
@ -59,7 +59,8 @@ where
|
|||
pub(super) interrupts: Interrupts,
|
||||
pub(super) ime: bool,
|
||||
pub(super) ime_scheduled: u8,
|
||||
dma_addr: u8,
|
||||
pub(super) user_mode: bool,
|
||||
dma: OamDma,
|
||||
joypad: Joypad,
|
||||
gpu: GpuSaveState<ColourFormat, R>,
|
||||
apu: ApuSaveState,
|
||||
|
@ -81,7 +82,8 @@ where
|
|||
interrupts: memory.interrupts,
|
||||
ime: memory.ime,
|
||||
ime_scheduled: memory.ime_scheduled,
|
||||
dma_addr: memory.dma_addr,
|
||||
user_mode: memory.user_mode,
|
||||
dma: memory.dma,
|
||||
joypad: memory.joypad,
|
||||
gpu: GpuSaveState::create(&memory.gpu),
|
||||
apu: ApuSaveState::create(&memory.apu),
|
||||
|
@ -114,7 +116,8 @@ where
|
|||
interrupts: Interrupts::default(),
|
||||
ime: false,
|
||||
ime_scheduled: 0x0,
|
||||
dma_addr: 0xFF,
|
||||
user_mode: false,
|
||||
dma: OamDma::default(),
|
||||
joypad: Joypad::default(),
|
||||
gpu: Gpu::new(window, tile_window),
|
||||
apu: Apu::new(output),
|
||||
|
@ -129,6 +132,17 @@ where
|
|||
T: Into<Address>,
|
||||
{
|
||||
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 {
|
||||
Address::Rom(address) => {
|
||||
// rom access
|
||||
|
@ -139,12 +153,12 @@ where
|
|||
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::WorkRam(address) => self.ram[address.get_local() as usize],
|
||||
Address::BankedWorkRam(address) => self.ram[(address.get_local() + 0x1000) 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::Io(address) => self.get_io(address),
|
||||
Address::Hram(address) => self.cpu_ram[address.get_local() as usize],
|
||||
|
@ -157,6 +171,17 @@ where
|
|||
T: Into<Address>,
|
||||
{
|
||||
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 {
|
||||
Address::Rom(address) => {
|
||||
self.rom.set(address, data);
|
||||
|
@ -165,14 +190,14 @@ where
|
|||
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::WorkRam(address) => self.ram[address.get_local() as usize] = data,
|
||||
Address::BankedWorkRam(address) => {
|
||||
self.ram[(address.get_local() + 0x1000) 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::Io(address) => {
|
||||
if address.inner() == 0xFF50 {
|
||||
|
@ -210,13 +235,13 @@ where
|
|||
0xFF43 => self.gpu.get_scx(),
|
||||
0xFF44 => self.gpu.get_ly(),
|
||||
0xFF45 => self.gpu.get_lyc(),
|
||||
0xFF46 => self.dma_addr,
|
||||
0xFF46 => self.dma.get_register(),
|
||||
0xFF47 => self.gpu.get_bg_palette(),
|
||||
0xFF48 => self.gpu.get_obj_palette_0(),
|
||||
0xFF49 => self.gpu.get_obj_palette_1(),
|
||||
0xFF4A => self.gpu.get_wy(),
|
||||
0xFF4B => self.gpu.get_wx(),
|
||||
_ => unreachable!(),
|
||||
0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(),
|
||||
},
|
||||
IoAddress::Unused(_) => 0xFF,
|
||||
}
|
||||
|
@ -245,25 +270,17 @@ where
|
|||
0xFF41 => self.gpu.update_lcd_status(data),
|
||||
0xFF42 => self.gpu.update_scy(data),
|
||||
0xFF43 => self.gpu.update_scx(data),
|
||||
0xFF44 => {}
|
||||
0xFF45 => self.gpu.update_lyc(data),
|
||||
0xFF46 => {
|
||||
if data > 0xDF {
|
||||
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);
|
||||
}
|
||||
self.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),
|
||||
0xFF4A => self.gpu.update_wy(data),
|
||||
0xFF4B => self.gpu.update_wx(data),
|
||||
_ => unreachable!(),
|
||||
0x0..0xFF40 | 0xFF4C..=0xFFFF => unreachable!(),
|
||||
},
|
||||
IoAddress::Unused(_) => {}
|
||||
}
|
||||
|
@ -327,7 +344,8 @@ where
|
|||
interrupts: state.interrupts,
|
||||
ime: state.ime,
|
||||
ime_scheduled: state.ime_scheduled,
|
||||
dma_addr: state.dma_addr,
|
||||
user_mode: state.user_mode,
|
||||
dma: state.dma,
|
||||
joypad: state.joypad,
|
||||
gpu: Gpu::from_save_state(state.gpu, window, None),
|
||||
apu: Apu::from_save_state(state.apu, output),
|
||||
|
@ -347,6 +365,8 @@ where
|
|||
pub fn increment_timers(&mut self, machine_cycles: u8) {
|
||||
let steps = (machine_cycles as usize) * 4;
|
||||
|
||||
self.memory.oam_tick(steps);
|
||||
|
||||
self.memory.camera.lock().unwrap().tick(steps);
|
||||
|
||||
let timer_return = self.memory.timers.tick(steps);
|
||||
|
|
|
@ -5,7 +5,7 @@ pub(crate) enum AddressError {
|
|||
OutOfBounds,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct BoundedAddress<const MIN: u16, const MAX: u16>(u16);
|
||||
|
||||
impl<const MIN: u16, const MAX: u16> Add<u16> for BoundedAddress<MIN, MAX> {
|
||||
|
|
|
@ -255,7 +255,7 @@ impl Apu {
|
|||
0xFF13 | 0xFF18 | 0xFF1B | 0xFF1D | 0xFF20 => 0xFF,
|
||||
// not registers
|
||||
0xFF15 | 0xFF1F => 0xFF,
|
||||
_ => unreachable!(),
|
||||
0x0..0xFF10 | 0xFF27..=0xFFFF => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,6 +266,7 @@ impl Apu {
|
|||
0xFF12 => self.channels.one.update_volume_and_envelope(data),
|
||||
0xFF13 => self.channels.one.update_wavelength_low(data),
|
||||
0xFF14 => self.channels.one.update_wavelength_high_and_control(data),
|
||||
0xFF15 => {}
|
||||
0xFF16 => self.channels.two.update_length_timer_and_duty_cycle(data),
|
||||
0xFF17 => self.channels.two.update_volume_and_envelope(data),
|
||||
0xFF18 => self.channels.two.update_wavelength_low(data),
|
||||
|
@ -275,6 +276,7 @@ impl Apu {
|
|||
0xFF1C => self.channels.three.update_volume(data),
|
||||
0xFF1D => self.channels.three.update_wavelength_low(data),
|
||||
0xFF1E => self.channels.three.update_wavelength_high_and_control(data),
|
||||
0xFF1F => {}
|
||||
0xFF20 => self.channels.four.update_length_timer(data),
|
||||
0xFF21 => self.channels.four.update_volume_and_envelope(data),
|
||||
0xFF22 => self.channels.four.update_frequency_and_randomness(data),
|
||||
|
@ -306,7 +308,7 @@ impl Apu {
|
|||
}
|
||||
self.apu_enable = (1 << 7) == (data & 0b10000000);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
0x0..0xFF10 | 0xFF27..=0xFFFF => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,10 @@ use self::{
|
|||
};
|
||||
use crate::{
|
||||
connect::Renderer,
|
||||
processor::SplitRegister,
|
||||
processor::{
|
||||
memory::addresses::{OamAddress, VramAddress},
|
||||
SplitRegister,
|
||||
},
|
||||
util::{clear_bit, get_bit},
|
||||
HEIGHT, WIDTH,
|
||||
};
|
||||
|
@ -247,6 +250,34 @@ where
|
|||
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) {
|
||||
self.stat.mode = DrawMode::HBlank;
|
||||
self.render_scanline(self.scanline);
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
pub(crate) mod apu;
|
||||
pub(crate) mod gpu;
|
||||
pub(crate) mod joypad;
|
||||
mod oam_dma;
|
||||
pub(crate) mod serial;
|
||||
mod timer;
|
||||
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;
|
||||
|
|
67
lib/src/processor/memory/mmio/oam_dma.rs
Normal file
67
lib/src/processor/memory/mmio/oam_dma.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -104,7 +104,10 @@ where
|
|||
}
|
||||
|
||||
self.last_instruction_addr = self.reg.pc;
|
||||
|
||||
self.memory.user_mode = true;
|
||||
let opcode = self.next_opcode();
|
||||
|
||||
if self.should_halt_bug {
|
||||
self.reg.pc = self.reg.pc.wrapping_sub(0x1);
|
||||
self.should_halt_bug = false;
|
||||
|
@ -116,7 +119,10 @@ where
|
|||
opcode,
|
||||
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();
|
||||
self.increment_timers(interrupt_cycles);
|
||||
|
@ -128,11 +134,6 @@ where
|
|||
opcode
|
||||
}
|
||||
|
||||
fn run_and_increment_timers(&mut self, opcode: u8) {
|
||||
let cycles = self.run_opcode(opcode);
|
||||
self.increment_timers(cycles);
|
||||
}
|
||||
|
||||
fn halt(&mut self) {
|
||||
if !self.memory.ime && self.memory.interrupts.is_interrupt_queued() {
|
||||
// halt bug
|
||||
|
|
Loading…
Reference in a new issue