diff --git a/src/cpu.rs b/src/cpu.rs index 39bb3c0166ab8b16101557d52d8c592ed92a52a4..983f7109f6ffd94226c9d93311b16b729ce468f0 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -136,10 +136,12 @@ impl Cpu { self.halted = false; } + // checks the IME (interrupt master flag) is enabled and then checks + // if there's any interrupt to be handled, in case there's one, tries + // to check which one should be handled and then handles it + // this code assumes that the're no more that one interrupt triggered + // per clock cycle, this is a limitation of the current implementation if self.ime && self.mmu.ie != 0x00 { - // @TODO aggregate all of this interrupts in the MMU, as there's - // a lot of redundant code involved in here which complicates the - // readability and maybe performance of this code if (self.mmu.ie & 0x01 == 0x01) && self.mmu.ppu().int_vblank() { debugln!("Going to run V-Blank interrupt handler (0x40)"); @@ -158,9 +160,7 @@ impl Cpu { } return 24; - } - // @TODO aggregate the handling of these interrupts - else if (self.mmu.ie & 0x02 == 0x02) && self.mmu.ppu().int_stat() { + } else if (self.mmu.ie & 0x02 == 0x02) && self.mmu.ppu().int_stat() { debugln!("Going to run LCD STAT interrupt handler (0x48)"); self.disable_int(); @@ -178,9 +178,7 @@ impl Cpu { } return 24; - } - // @TODO aggregate the handling of these interrupts - else if (self.mmu.ie & 0x04 == 0x04) && self.mmu.timer().int_tima() { + } else if (self.mmu.ie & 0x04 == 0x04) && self.mmu.timer().int_tima() { debugln!("Going to run Timer interrupt handler (0x50)"); self.disable_int(); @@ -198,9 +196,7 @@ impl Cpu { } return 24; - } - // @TODO aggregate the handling of these interrupts - else if (self.mmu.ie & 0x08 == 0x08) && self.mmu.serial().int_serial() { + } else if (self.mmu.ie & 0x08 == 0x08) && self.mmu.serial().int_serial() { debugln!("Going to run Serial interrupt handler (0x58)"); self.disable_int(); @@ -218,9 +214,7 @@ impl Cpu { } return 24; - } - // @TODO aggregate the handling of these interrupts - else if (self.mmu.ie & 0x10 == 0x10) && self.mmu.pad().int_pad() { + } else if (self.mmu.ie & 0x10 == 0x10) && self.mmu.pad().int_pad() { debugln!("Going to run JoyPad interrupt handler (0x60)"); self.disable_int(); diff --git a/src/dma.rs b/src/dma.rs index df29926ba940ac3e55da3cc75e816d74cc90e063..f477b8607e2d4d8b631138a8acf8670a810adafa 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -1,5 +1,6 @@ use crate::warnln; +#[derive(Clone, Copy, PartialEq, Eq)] pub enum DmaMode { General = 0x00, HBlank = 0x01, @@ -8,7 +9,7 @@ pub enum DmaMode { pub struct Dma { source: u16, destination: u16, - length: u8, + length: u16, mode: DmaMode, active: bool, } @@ -37,7 +38,7 @@ impl Dma { pub fn read(&mut self, addr: u16) -> u8 { match addr { // 0xFF55 — HDMA5: VRAM DMA length/mode/start (CGB only) - 0xff45 => self.length | ((self.active as u8) << 7), + 0xff45 => ((self.length >> 4) - 1) as u8 | ((self.active as u8) << 7), _ => { warnln!("Reading from unknown DMA location 0x{:04x}", addr); 0xff @@ -48,29 +49,66 @@ impl Dma { pub fn write(&mut self, addr: u16, value: u8) { match addr { // 0xFF51 — HDMA1: VRAM DMA source high (CGB only) - 0xff41 => self.source = (self.source & 0x00ff) | ((value as u16) << 8), + 0xff51 => self.source = (self.source & 0x00ff) | ((value as u16) << 8), // 0xFF52 — HDMA2: VRAM DMA source low (CGB only) - 0xff42 => self.source = (self.source & 0xff00) | (value as u16), + 0xff52 => self.source = (self.source & 0xff00) | ((value & 0xf0) as u16), // 0xFF53 — HDMA3: VRAM DMA destination high (CGB only) - 0xff43 => self.destination = (self.destination & 0x00ff) | ((value as u16) << 8), + 0xff53 => self.destination = (self.destination & 0x00ff) | ((value as u16) << 8), // 0xFF54 — HDMA4: VRAM DMA destination low (CGB only) - 0xff44 => self.destination = (self.destination & 0xff00) | (value as u16), + 0xff54 => self.destination = (self.destination & 0xff00) | ((value & 0xf0) as u16), // 0xFF55 — HDMA5: VRAM DMA length/mode/start (CGB only) - 0xff45 => { - self.length = value & 0x7f; + 0xff55 => { + self.length = (((value & 0x7f) + 0x1) as u16) << 4; self.mode = match (value & 80) >> 7 { 0 => DmaMode::General, 1 => DmaMode::HBlank, _ => DmaMode::General, }; - - // @TODO: Implement DMA transfer in a better way - //let data = self.mmu.read_many(self.source, self.length as usize); - //self.mmu.write_many(self.destination, &data); + self.active = true; } _ => warnln!("Writing to unknown DMA location 0x{:04x}", addr), } } + + pub fn source(&self) -> u16 { + self.source + } + + pub fn set_source(&mut self, value: u16) { + self.source = value; + } + + pub fn destination(&self) -> u16 { + self.destination + } + + pub fn set_destination(&mut self, value: u16) { + self.destination = value; + } + + pub fn length(&self) -> u16 { + self.length + } + + pub fn set_length(&mut self, value: u16) { + self.length = value; + } + + pub fn mode(&self) -> DmaMode { + self.mode + } + + pub fn set_mode(&mut self, value: DmaMode) { + self.mode = value; + } + + pub fn active(&self) -> bool { + self.active + } + + pub fn set_active(&mut self, value: bool) { + self.active = value; + } } impl Default for Dma { diff --git a/src/gb.rs b/src/gb.rs index 6621d361378c93c9dcd43a13c57a93ea3acf58c5..7339d0e489fa7b57bb5bda8170e783362bd9c556 100644 --- a/src/gb.rs +++ b/src/gb.rs @@ -424,7 +424,7 @@ impl GameBoy { } pub fn dma_clock(&mut self, cycles: u8) { - self.dma().clock(cycles) + self.mmu().clock_dma(cycles); } pub fn timer_clock(&mut self, cycles: u8) { diff --git a/src/mmu.rs b/src/mmu.rs index 2b08fb74fc95e4f42d39852a5e864d0f91c64618..2d966791a5bc50418c0a91f2bcee7ac206877b6e 100644 --- a/src/mmu.rs +++ b/src/mmu.rs @@ -205,6 +205,17 @@ impl Mmu { self.boot_active = value; } + pub fn clock_dma(&mut self, _cycles: u8) { + if !self.dma.active() { + return; + } + + // @TODO: Implement DMA transfer in a better way + let data = self.read_many(self.dma.source(), self.dma.length()); + self.write_many(self.dma.destination(), &data); + self.dma.set_active(false); + } + pub fn read(&mut self, addr: u16) -> u8 { match addr & 0xf000 { // BOOT (256 B) + ROM0 (4 KB/16 KB) @@ -299,7 +310,14 @@ impl Mmu { } }, 0x10..=0x26 | 0x30..=0x37 => self.apu.read(addr), - 0x40 | 0x50 | 0x60 | 0x70 => self.ppu.read(addr), + 0x40 | 0x60 | 0x70 => self.ppu.read(addr), + 0x50 => match addr & 0x00ff { + 0x51..=0x55 => self.dma.read(addr), + _ => { + debugln!("Reading from unknown IO control 0x{:04x}", addr); + 0x00 + } + }, _ => { debugln!("Reading from unknown IO control 0x{:04x}", addr); 0x00 @@ -422,12 +440,7 @@ impl Mmu { } } 0x50 => match addr & 0x00ff { - // 0xFF51-0xFF52 - VRAM DMA source (CGB only) - 0x51..=0x52 => (), - - // 0xFF53-0xFF54 - VRAM DMA destination (CGB only) - 0x53..=0x54 => (), - + 0x51..=0x55 => self.dma.write(addr, value), _ => debugln!("Writing to unknown IO control 0x{:04x}", addr), }, _ => debugln!("Writing to unknown IO control 0x{:04x}", addr),