From cbb5c0fea06d5e6634756fbd703cbf8e3b03dd8c Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 26 Nov 2021 10:42:14 +0200 Subject: [PATCH 01/26] WIP --- lib_gb/src/mmu/mod.rs | 3 +- lib_gb/src/mmu/scheduler.rs | 171 ++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 lib_gb/src/mmu/scheduler.rs diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index 886978cb..edd41348 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -7,4 +7,5 @@ pub mod io_ports; pub mod carts; pub mod access_bus; pub mod oam_dma_transfer; -pub mod io_components; \ No newline at end of file +pub mod io_components; +mod scheduler; \ No newline at end of file diff --git a/lib_gb/src/mmu/scheduler.rs b/lib_gb/src/mmu/scheduler.rs new file mode 100644 index 00000000..c6cbd1ad --- /dev/null +++ b/lib_gb/src/mmu/scheduler.rs @@ -0,0 +1,171 @@ +use std::collections::BinaryHeap; + +#[derive(Clone, Copy, Debug)] +pub enum ScheduledEventType{ + Ppu, + Timer +} + +#[derive(Debug)] +pub struct ScheduledEvent{ + event_type:ScheduledEventType, + cycles:u32 +} + +impl Ord for ScheduledEvent { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.cycles.cmp(&other.cycles) + } + + fn clamp(self, min: Self, max: Self) -> Self + where Self: Sized { + let cycles = self.cycles.clamp(min.cycles, max.cycles); + Self{ + cycles, + event_type:self.event_type + } + } + + fn max(self, other: Self) -> Self + where Self: Sized { + return if self.cycles >= other.cycles{ + self + } + else{ + other + }; + } + + fn min(self, other: Self) -> Self + where Self: Sized { + return if self.cycles >= other.cycles{ + self + } + else{ + other + }; + } +} + +impl PartialOrd for ScheduledEvent{ + fn ge(&self, other: &Self) -> bool { + self.cycles.ge(&other.cycles) + } + + fn gt(&self, other: &Self) -> bool { + self.cycles.gt(&other.cycles) + } + + fn le(&self, other: &Self) -> bool { + self.cycles.le(&other.cycles) + } + + fn lt(&self, other: &Self) -> bool { + self.cycles.lt(&other.cycles) + } + + fn partial_cmp(&self, other: &Self) -> Option { + self.cycles.partial_cmp(&other.cycles) + } +} + +impl PartialEq for ScheduledEvent{ + fn eq(&self, other: &Self) -> bool { + self.cycles.eq(&other.cycles) + } + + fn ne(&self, other: &Self) -> bool { + self.cycles.ne(&other.cycles) + } +} + +impl Eq for ScheduledEvent{} + +pub struct Scheduler{ + events:Vec, + m_cycles:u32 +} + +impl Default for Scheduler{ + fn default() -> Self { + Self{ + events: Vec::new(), + m_cycles:0 + } + } +} + +impl Scheduler{ + pub fn add_event(&mut self, mut event:ScheduledEvent){ + if self.events.is_empty(){ + self.events.push(event); + return; + } + + let mut index = 0; + for i in (0..self.events.len()).rev(){ + if self.events[i].cycles < event.cycles{ + index = i; + break; + } + // else{ + // self.events[i].cycles -= event.cycles; + // } + } + + for i in index+1..self.events.len(){ + self.events[i].cycles -= event.cycles; + } + + if index == 0{ + // event.cycles -= self.events[0].cycles; + self.events.push(event); + return; + } + + // event.cycles -= self.events[index - 1].cycles; + self.events.insert(index+1, event); + } + + pub fn cycle(&mut self, m_cycles:u32)->Vec{ + let mut events = Vec::new(); + for _ in 0..m_cycles{ + + self.m_cycles += 1; + + while m_cycles >= self.events[0].cycles{ + let mut event = self.events.remove(0); + + for e in &mut self.events{ + e.cycles -= event.cycles; + } + + event.cycles = self.m_cycles; + events.push(event); + } + } + + return events; + } +} + +mod tests{ + use super::{ScheduledEvent, ScheduledEventType, Scheduler}; + + #[test] + pub fn add_event_test(){ + let mut scheduler = Scheduler::default(); + + let event_type = ScheduledEventType::Ppu; + scheduler.add_event(ScheduledEvent{event_type, cycles: 10}); + scheduler.add_event(ScheduledEvent{event_type, cycles: 100}); + scheduler.add_event(ScheduledEvent{event_type, cycles: 50}); + scheduler.add_event(ScheduledEvent{event_type, cycles: 1000}); + + assert_eq!(scheduler.events, [ScheduledEvent{event_type, cycles: 10}, + ScheduledEvent{event_type, cycles: 40}, + ScheduledEvent{event_type, cycles: 50}, + ScheduledEvent{event_type, cycles: 900}] + ); + } +} \ No newline at end of file From 99c6c14a31ef2af6cca98abc0b4302d218032502 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 27 Nov 2021 17:04:25 +0200 Subject: [PATCH 02/26] Still wip --- lib_gb/src/apu/gb_apu.rs | 2 +- lib_gb/src/cpu/opcode_runner.rs | 8 +-- lib_gb/src/mmu/gb_mmu.rs | 2 +- lib_gb/src/mmu/io_components.rs | 73 +++++++++++++++++++++--- lib_gb/src/mmu/memory.rs | 2 +- lib_gb/src/mmu/mod.rs | 2 +- lib_gb/src/mmu/scheduler.rs | 90 +++++++++++++++++++----------- lib_gb/src/ppu/gb_ppu.rs | 34 +++++++++-- lib_gb/src/timer/gb_timer.rs | 14 ++++- lib_gb/tests/memory_stub.rs | 2 +- lib_gb/tests/rotate_shift_tests.rs | 2 +- 11 files changed, 172 insertions(+), 59 deletions(-) diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index 6233f458..aa61be3e 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -44,7 +44,7 @@ impl GbApu{ } } - pub fn cycle(&mut self, m_cycles_passed:u8){ + pub fn cycle(&mut self, m_cycles_passed:u32){ if self.enabled{ for _ in 0..m_cycles_passed{ diff --git a/lib_gb/src/cpu/opcode_runner.rs b/lib_gb/src/cpu/opcode_runner.rs index 5ff6fede..463e484c 100644 --- a/lib_gb/src/cpu/opcode_runner.rs +++ b/lib_gb/src/cpu/opcode_runner.rs @@ -171,7 +171,7 @@ impl GbCpu{ } - fn fetch_next_byte(&mut self, memory: &impl Memory)->u8{ + fn fetch_next_byte(&mut self, memory: &mut impl Memory)->u8{ let byte:u8 = memory.read(self.program_counter); self.program_counter+=1; return byte; @@ -180,7 +180,7 @@ impl GbCpu{ -fn run_u16_opcode(cpu: &mut GbCpu, memory: &impl Memory, opcode:u8, opcode_func:fn(&mut GbCpu, u16)->u8)->u8{ +fn run_u16_opcode(cpu: &mut GbCpu, memory: &mut impl Memory, opcode:u8, opcode_func:fn(&mut GbCpu, u16)->u8)->u8{ let u16_opcode = get_u16_opcode(cpu, memory, opcode); opcode_func(cpu, u16_opcode) } @@ -190,7 +190,7 @@ fn run_u16_memory_opcode(cpu: &mut GbCpu, memory: &mut T, opcode:u8, o opcode_func(cpu, memory, u16_opcode) } -fn run_u32_opcode(cpu: &mut GbCpu, memory: &impl Memory, opcode:u8, opcode_func:fn(&mut GbCpu, u32)->u8)->u8{ +fn run_u32_opcode(cpu: &mut GbCpu, memory: &mut impl Memory, opcode:u8, opcode_func:fn(&mut GbCpu, u32)->u8)->u8{ let mut u32_opcode:u32 = ((opcode as u32)<<8) | (cpu.fetch_next_byte(memory) as u32); u32_opcode <<= 8; u32_opcode |= cpu.fetch_next_byte(memory) as u32; @@ -206,6 +206,6 @@ fn run_u32_memory_opcode(cpu: &mut GbCpu, memory: &mut T, opcode:u8, o opcode_func(cpu, memory, u32_opcode) } -fn get_u16_opcode(cpu:&mut GbCpu, memory:&impl Memory, opcode:u8)->u16{ +fn get_u16_opcode(cpu:&mut GbCpu, memory:&mut impl Memory, opcode:u8)->u16{ (opcode as u16) << 8 | cpu.fetch_next_byte(memory) as u16 } \ No newline at end of file diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index 8c434add..c04548c8 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -24,7 +24,7 @@ pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice>{ //DMA only locks the used bus. there 2 possible used buses: extrnal (wram, rom, sram) and video (vram) impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ - fn read(&self, address:u16)->u8{ + fn read(&mut self, address:u16)->u8{ if let Some (bus) = &self.io_components.dma.enable{ return match address{ 0xFF00..=0xFF7F => self.io_components.read(address - 0xFF00), diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index fc18efc4..0d1a1add 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -4,7 +4,7 @@ use crate::{apu::{*,audio_device::AudioDevice, gb_apu::GbApu}, utils::memory_registers::* }; use crate::timer::gb_timer::GbTimer; -use super::{access_bus::AccessBus, memory::*, oam_dma_transfer::OamDmaTransfer, ram::Ram}; +use super::{access_bus::AccessBus, memory::*, oam_dma_transfer::OamDmaTransfer, ram::Ram, scheduler::ScheduledEvent}; use super::io_ports::*; pub const IO_PORTS_SIZE:usize = 0x80; @@ -16,9 +16,16 @@ pub struct IoComponents{ pub apu: GbApu, pub timer: GbTimer, pub ppu:GbPpu, - ports:[u8;IO_PORTS_SIZE], pub dma:OamDmaTransfer, pub finished_boot:bool, + + ports:[u8;IO_PORTS_SIZE], + apu_cycles:u32, + ppu_cycles:u32, + timer_cycles:u32, + + timer_event:Option, + ppu_event:Option, } io_port_index!(LCDC_REGISTER_INDEX, LCDC_REGISTER_ADDRESS); @@ -37,7 +44,15 @@ io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); io_port_index!(IF_REGISTER_INDEX, IF_REGISTER_ADDRESS); impl Memory for IoComponents{ - fn read(&self, address:u16)->u8 { + fn read(&mut self, address:u16)->u8 { + + match address{ + TAC_REGISTER_INDEX | DIV_REGISTER_INDEX | TIMA_REGISTER_INDEX=> self.cycle_timer(), + NR10_REGISTER_INDEX..=WAVE_RAM_END_INDEX => self.cycle_apu(), + LCDC_REGISTER_INDEX..=WX_REGISTER_INDEX => self.cycle_ppu(), + _=>{} + } + let mut value = self.ports[address as usize]; return match address { //Timer @@ -80,6 +95,12 @@ impl Memory for IoComponents{ } fn write(&mut self, address:u16, mut value:u8) { + match address{ + DIV_REGISTER_INDEX | TIMA_REGISTER_INDEX | TMA_REGISTER_INDEX | TAC_REGISTER_INDEX => self.cycle_timer(), + NR10_REGISTER_INDEX..=WAVE_RAM_END_INDEX => self.cycle_ppu(), + LCDC_REGISTER_INDEX..=WX_REGISTER_INDEX => self.cycle_ppu(), + _=>{} + } match address{ //timer DIV_REGISTER_INDEX=> { @@ -163,14 +184,52 @@ impl UnprotectedMemory for IoComponents{ impl IoComponents{ pub fn new(apu:GbApu, gfx_device:GFX)->Self{ - Self{apu, ports:[0;IO_PORTS_SIZE], timer:GbTimer::default(), ppu:GbPpu::new(gfx_device), dma:OamDmaTransfer::default(),finished_boot:false, ram:Ram::default()} + Self{apu, + ports:[0;IO_PORTS_SIZE], + timer:GbTimer::default(), + ppu:GbPpu::new(gfx_device), + dma:OamDmaTransfer::default(), + finished_boot:false, + ram:Ram::default(), + apu_cycles:0, + ppu_cycles:0, + timer_cycles:0, + ppu_event:None, + timer_event: None, + } } pub fn cycle(&mut self, cycles:u32){ + self.apu_cycles += cycles; + self.ppu_cycles += cycles; + self.timer_cycles += cycles; + + if let Some(event) = &self.ppu_event{ + if event.cycles >= self.ppu_cycles{ + self.cycle_ppu(); + } + } + if let Some(event) = &self.timer_event{ + if event.cycles >= self.timer_cycles{ + self.cycle_timer(); + } + } + } + + fn cycle_ppu(&mut self){ + let mut if_register = self.ports[IF_REGISTER_INDEX as usize]; + self.ppu_event = self.ppu.cycle(self.ppu_cycles, &mut if_register); + self.ports[IF_REGISTER_INDEX as usize] = if_register; + self.ppu_cycles = 0; + } + fn cycle_apu(&mut self){ + self.apu.cycle(self.apu_cycles); + self.apu_cycles = 0; + } + fn cycle_timer(&mut self){ let mut if_register = self.ports[IF_REGISTER_INDEX as usize]; - self.timer.cycle(&mut if_register, cycles as u8); - self.apu.cycle(cycles as u8); - self.ppu.cycle( cycles, &mut if_register); + self.timer_event = self.timer.cycle(self.timer_cycles, &mut if_register); self.ports[IF_REGISTER_INDEX as usize] = if_register; + self.timer_cycles = 0; } } \ No newline at end of file diff --git a/lib_gb/src/mmu/memory.rs b/lib_gb/src/mmu/memory.rs index 5eb4c11f..6cfec866 100644 --- a/lib_gb/src/mmu/memory.rs +++ b/lib_gb/src/mmu/memory.rs @@ -1,6 +1,6 @@ pub trait Memory{ - fn read(&self, address:u16)->u8; + fn read(&mut self, address:u16)->u8; fn write(&mut self, address:u16, value:u8); } diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index edd41348..a5417947 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -8,4 +8,4 @@ pub mod carts; pub mod access_bus; pub mod oam_dma_transfer; pub mod io_components; -mod scheduler; \ No newline at end of file +pub mod scheduler; \ No newline at end of file diff --git a/lib_gb/src/mmu/scheduler.rs b/lib_gb/src/mmu/scheduler.rs index c6cbd1ad..208742bc 100644 --- a/lib_gb/src/mmu/scheduler.rs +++ b/lib_gb/src/mmu/scheduler.rs @@ -1,5 +1,3 @@ -use std::collections::BinaryHeap; - #[derive(Clone, Copy, Debug)] pub enum ScheduledEventType{ Ppu, @@ -8,8 +6,8 @@ pub enum ScheduledEventType{ #[derive(Debug)] pub struct ScheduledEvent{ - event_type:ScheduledEventType, - cycles:u32 + pub event_type:ScheduledEventType, + pub cycles:u32 } impl Ord for ScheduledEvent { @@ -96,35 +94,16 @@ impl Default for Scheduler{ } impl Scheduler{ - pub fn add_event(&mut self, mut event:ScheduledEvent){ - if self.events.is_empty(){ - self.events.push(event); - return; - } - - let mut index = 0; - for i in (0..self.events.len()).rev(){ - if self.events[i].cycles < event.cycles{ + pub fn add_event(&mut self, event:ScheduledEvent){ + let mut index = self.events.len(); + for i in 0..self.events.len(){ + if self.events[i].cycles > event.cycles{ index = i; break; } - // else{ - // self.events[i].cycles -= event.cycles; - // } - } - - for i in index+1..self.events.len(){ - self.events[i].cycles -= event.cycles; - } - - if index == 0{ - // event.cycles -= self.events[0].cycles; - self.events.push(event); - return; } - // event.cycles -= self.events[index - 1].cycles; - self.events.insert(index+1, event); + self.events.insert(index, event); } pub fn cycle(&mut self, m_cycles:u32)->Vec{ @@ -133,14 +112,14 @@ impl Scheduler{ self.m_cycles += 1; - while m_cycles >= self.events[0].cycles{ - let mut event = self.events.remove(0); + while !self.events.is_empty() && self.m_cycles >= self.events[0].cycles{ + let event = self.events.remove(0); + self.m_cycles -= event.cycles; for e in &mut self.events{ e.cycles -= event.cycles; } - event.cycles = self.m_cycles; events.push(event); } } @@ -153,7 +132,7 @@ mod tests{ use super::{ScheduledEvent, ScheduledEventType, Scheduler}; #[test] - pub fn add_event_test(){ + pub fn add_event_test1(){ let mut scheduler = Scheduler::default(); let event_type = ScheduledEventType::Ppu; @@ -163,9 +142,52 @@ mod tests{ scheduler.add_event(ScheduledEvent{event_type, cycles: 1000}); assert_eq!(scheduler.events, [ScheduledEvent{event_type, cycles: 10}, - ScheduledEvent{event_type, cycles: 40}, ScheduledEvent{event_type, cycles: 50}, - ScheduledEvent{event_type, cycles: 900}] + ScheduledEvent{event_type, cycles: 100}, + ScheduledEvent{event_type, cycles: 1000}] + ); + } + + #[test] + pub fn add_event_test2(){ + let mut scheduler = Scheduler::default(); + + let event_type = ScheduledEventType::Ppu; + scheduler.add_event(ScheduledEvent{event_type, cycles: 2}); + scheduler.add_event(ScheduledEvent{event_type, cycles: 4}); + scheduler.add_event(ScheduledEvent{event_type, cycles: 1}); + scheduler.add_event(ScheduledEvent{event_type, cycles: 3}); + + assert_eq!(scheduler.events, [ScheduledEvent{event_type, cycles: 1}, + ScheduledEvent{event_type, cycles: 2}, + ScheduledEvent{event_type, cycles: 3}, + ScheduledEvent{event_type, cycles: 4}] + ); + + let events = scheduler.cycle(1); + + assert_eq!(scheduler.events, [ScheduledEvent{event_type, cycles: 1}, + ScheduledEvent{event_type, cycles: 2}, + ScheduledEvent{event_type, cycles: 3}] + ); + + assert_eq!(events, [ScheduledEvent{event_type, cycles: 1}]); + } + + #[test] + pub fn add_event_test3(){ + let mut scheduler = Scheduler::default(); + + let event_type = ScheduledEventType::Ppu; + scheduler.add_event(ScheduledEvent{event_type, cycles: 4}); + scheduler.add_event(ScheduledEvent{event_type, cycles: 3}); + scheduler.add_event(ScheduledEvent{event_type, cycles: 2}); + scheduler.add_event(ScheduledEvent{event_type, cycles: 1}); + + assert_eq!(scheduler.events, [ScheduledEvent{event_type, cycles: 1}, + ScheduledEvent{event_type, cycles: 2}, + ScheduledEvent{event_type, cycles: 3}, + ScheduledEvent{event_type, cycles: 4}] ); } } \ No newline at end of file diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 4a5b2dd6..917111b5 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -1,3 +1,4 @@ +use crate::mmu::scheduler::ScheduledEvent; use crate::utils::{vec2::Vec2, bit_masks::*}; use crate::mmu::vram::VRam; use crate::ppu::color::*; @@ -108,14 +109,16 @@ impl GbPpu{ self.state = PpuState::OamSearch; } - pub fn cycle(&mut self, m_cycles:u32, if_register:&mut u8){ + pub fn cycle(&mut self, m_cycles:u32, if_register:&mut u8)->Option{ if self.lcd_control & BIT_7_MASK == 0{ - return; + return None; } - self.cycle_fetcher(m_cycles, if_register); + let fethcer_cycles_to_next_event = self.cycle_fetcher(m_cycles, if_register); - self.update_stat_register(if_register); + let stat_cycles_to_next_event = self.update_stat_register(if_register); + + let cycles = std::cmp::min(fethcer_cycles_to_next_event, stat_cycles_to_next_event); for i in 0..self.push_lcd_buffer.len(){ self.screen_buffers[self.current_screen_buffer_index][self.screen_buffer_index] = u32::from(self.push_lcd_buffer[i]); @@ -126,6 +129,8 @@ impl GbPpu{ } self.push_lcd_buffer.clear(); + + return Some(ScheduledEvent{cycles, event_type:crate::mmu::scheduler::ScheduledEventType::Ppu}); } fn swap_buffer(&mut self){ @@ -134,7 +139,7 @@ impl GbPpu{ self.current_screen_buffer_index = (self.current_screen_buffer_index + 1) % BUFFERS_NUMBER; } - fn update_stat_register(&mut self, if_register: &mut u8) { + fn update_stat_register(&mut self, if_register: &mut u8) -> u32{ self.stat_register &= 0b1111_1100; self.stat_register |= self.state as u8; if self.ly_register == self.lyc_register{ @@ -156,9 +161,19 @@ impl GbPpu{ self.stat_triggered = false; } self.trigger_stat_interrupt = false; + + return if self.lyc_register < self.ly_register{ + (((self.ly_register - self.lyc_register) as u32 * HBLANK_T_CYCLES_LENGTH as u32) - self.t_cycles_passed as u32) >> 2 + } + else if self.lyc_register == self.ly_register{ + (HBLANK_T_CYCLES_LENGTH as u32 * 154 ) - self.t_cycles_passed as u32 + } + else{ + (((self.lyc_register - self.ly_register) as u32 * HBLANK_T_CYCLES_LENGTH as u32) - self.t_cycles_passed as u32) >> 2 + }; } - fn cycle_fetcher(&mut self, m_cycles:u32, if_register:&mut u8){ + fn cycle_fetcher(&mut self, m_cycles:u32, if_register:&mut u8)->u32{ let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {EXTENDED_SPRITE_HIGHT} else {NORMAL_SPRITE_HIGHT}; for _ in 0..m_cycles * 2{ @@ -261,6 +276,13 @@ impl GbPpu{ } } + + let t_cycles = match self.state{ + PpuState::Vblank => (SCREEN_HEIGHT - (self.ly_register as usize - SCREEN_HEIGHT)) as u32 * HBLANK_T_CYCLES_LENGTH as u32, + _ => ((SCREEN_HEIGHT - self.ly_register as usize) * HBLANK_T_CYCLES_LENGTH as usize) as u32 + }; + + return (t_cycles - self.t_cycles_passed as u32) >> 2; } fn try_push_to_lcd(&mut self){ diff --git a/lib_gb/src/timer/gb_timer.rs b/lib_gb/src/timer/gb_timer.rs index 444a2f1e..3a39ee04 100644 --- a/lib_gb/src/timer/gb_timer.rs +++ b/lib_gb/src/timer/gb_timer.rs @@ -1,4 +1,4 @@ -use crate::utils::bit_masks::*; +use crate::{mmu::scheduler::ScheduledEvent, utils::bit_masks::*}; pub struct GbTimer{ pub system_counter:u16, @@ -27,7 +27,7 @@ impl Default for GbTimer{ } impl GbTimer{ - pub fn cycle(&mut self, if_register:&mut u8, m_cycles:u8){ + pub fn cycle(&mut self, m_cycles:u32, if_register:&mut u8)->Option{ let (timer_interval, timer_enable) = self.get_timer_controller_data(); for _ in 0..m_cycles * 4{ @@ -61,6 +61,16 @@ impl GbTimer{ } self.last_and_result = current_and_result; } + + let v = match timer_interval{ + 0b00=>0x100 - self.system_counter & 0xFF, + 0b01=>0b1000 - self.system_counter & 0b111, + 0b10=>0b10_0000 - self.system_counter & 0b1_1111, + 0b11=>0b1000_0000 - self.system_counter & 0b111_1111, + _=>std::panic!("error ") + }; + + Some(ScheduledEvent{cycles:(v>>2) as u32 + 1, event_type:crate::mmu::scheduler::ScheduledEventType::Timer}) } fn get_timer_controller_data(&self)->(u8, bool){ diff --git a/lib_gb/tests/memory_stub.rs b/lib_gb/tests/memory_stub.rs index 31912207..9fd1f157 100644 --- a/lib_gb/tests/memory_stub.rs +++ b/lib_gb/tests/memory_stub.rs @@ -5,7 +5,7 @@ pub struct MemoryStub{ } impl Memory for MemoryStub{ - fn read(&self, address:u16)->u8{ + fn read(&mut self, address:u16)->u8{ self.data[address as usize] } diff --git a/lib_gb/tests/rotate_shift_tests.rs b/lib_gb/tests/rotate_shift_tests.rs index b8083f7f..0520a265 100644 --- a/lib_gb/tests/rotate_shift_tests.rs +++ b/lib_gb/tests/rotate_shift_tests.rs @@ -8,7 +8,7 @@ struct MemoryStub{ } impl Memory for MemoryStub{ - fn read(&self, address:u16)->u8{ + fn read(&mut self, address:u16)->u8{ self.data[address as usize] } From d962c7770600cf3b5d1498cef30ed7565a6c443f Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 27 Nov 2021 17:22:22 +0200 Subject: [PATCH 03/26] Fix the versions to 2.0.0 --- Cargo.lock | 4 ++-- gb/Cargo.toml | 2 +- lib_gb/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0660ac1c..f2b94e66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -414,7 +414,7 @@ dependencies = [ [[package]] name = "gb" -version = "1.0.0" +version = "2.0.0" dependencies = [ "cfg-if 1.0.0", "chrono", @@ -607,7 +607,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lib_gb" -version = "1.0.0" +version = "2.0.0" dependencies = [ "criterion", "log", diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 7eac1e63..b5c39eb7 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gb" -version = "1.0.0" +version = "2.0.0" authors = ["alloncm "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/lib_gb/Cargo.toml b/lib_gb/Cargo.toml index 29631e6f..51910fbc 100644 --- a/lib_gb/Cargo.toml +++ b/lib_gb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lib_gb" -version = "1.0.0" +version = "2.0.0" authors = ["alloncm "] edition = "2018" From 2f47af75c726be2c6e238a8a96630c3dba938dd7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 27 Nov 2021 21:55:13 +0200 Subject: [PATCH 04/26] It kinda works but without apu --- gb/src/audio/sdl_pull_audio_device.rs | 2 +- lib_gb/src/mmu/io_components.rs | 6 +++--- lib_gb/src/timer/gb_timer.rs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gb/src/audio/sdl_pull_audio_device.rs b/gb/src/audio/sdl_pull_audio_device.rs index efacca24..475617bb 100644 --- a/gb/src/audio/sdl_pull_audio_device.rs +++ b/gb/src/audio/sdl_pull_audio_device.rs @@ -58,7 +58,7 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ }; // Ignore device id - device.device_id = init_sdl_audio_device(&desired_audio_spec); + // device.device_id = init_sdl_audio_device(&desired_audio_spec); return device; } diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index 0d1a1add..5d4491e9 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -205,12 +205,12 @@ impl IoComponents{ self.timer_cycles += cycles; if let Some(event) = &self.ppu_event{ - if event.cycles >= self.ppu_cycles{ + if event.cycles <= self.ppu_cycles{ self.cycle_ppu(); } } if let Some(event) = &self.timer_event{ - if event.cycles >= self.timer_cycles{ + if event.cycles <= self.timer_cycles{ self.cycle_timer(); } } @@ -223,7 +223,7 @@ impl IoComponents{ self.ppu_cycles = 0; } fn cycle_apu(&mut self){ - self.apu.cycle(self.apu_cycles); + // self.apu.cycle(self.apu_cycles); self.apu_cycles = 0; } fn cycle_timer(&mut self){ diff --git a/lib_gb/src/timer/gb_timer.rs b/lib_gb/src/timer/gb_timer.rs index 3a39ee04..b74bc497 100644 --- a/lib_gb/src/timer/gb_timer.rs +++ b/lib_gb/src/timer/gb_timer.rs @@ -63,10 +63,10 @@ impl GbTimer{ } let v = match timer_interval{ - 0b00=>0x100 - self.system_counter & 0xFF, - 0b01=>0b1000 - self.system_counter & 0b111, - 0b10=>0b10_0000 - self.system_counter & 0b1_1111, - 0b11=>0b1000_0000 - self.system_counter & 0b111_1111, + 0b00=>0x100 - (self.system_counter & 0xFF), + 0b01=>0b1000 - (self.system_counter & 0b111), + 0b10=>0b10_0000 - (self.system_counter & 0b1_1111), + 0b11=>0b1000_0000 - (self.system_counter & 0b111_1111), _=>std::panic!("error ") }; From 5dda8232fd7922c82bf485223143d362e750e475 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 28 Nov 2021 00:31:11 +0200 Subject: [PATCH 05/26] Adding the apu. The apu is pretty heavy. After some twicks on my i7 machine the debug build runs at almost 60 fps --- gb/src/audio/sdl_pull_audio_device.rs | 2 +- lib_gb/src/apu/gb_apu.rs | 10 ++++------ lib_gb/src/apu/sound_terminal.rs | 14 ++++++++------ lib_gb/src/mmu/io_components.rs | 9 ++++++++- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/gb/src/audio/sdl_pull_audio_device.rs b/gb/src/audio/sdl_pull_audio_device.rs index 475617bb..efacca24 100644 --- a/gb/src/audio/sdl_pull_audio_device.rs +++ b/gb/src/audio/sdl_pull_audio_device.rs @@ -58,7 +58,7 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ }; // Ignore device id - // device.device_id = init_sdl_audio_device(&desired_audio_spec); + device.device_id = init_sdl_audio_device(&desired_audio_spec); return device; } diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index aa61be3e..9d29e6b6 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -23,7 +23,6 @@ pub struct GbApu{ audio_buffer:[StereoSample;BUFFER_SIZE], current_m_cycle:u32, device:Device, - last_enabled_state:bool } impl GbApu{ @@ -39,12 +38,11 @@ impl GbApu{ device:device, right_terminal: SoundTerminal::default(), left_terminal: SoundTerminal::default(), - enabled:false, - last_enabled_state: false + enabled:false, } } - pub fn cycle(&mut self, m_cycles_passed:u32){ + pub fn cycle(&mut self, m_cycles_passed:u32)->Option{ if self.enabled{ for _ in 0..m_cycles_passed{ @@ -75,9 +73,9 @@ impl GbApu{ self.push_buffer_if_full(); } - } + } - self.last_enabled_state = self.enabled; + return Some(crate::mmu::scheduler::ScheduledEvent{ event_type: crate::mmu::scheduler::ScheduledEventType::Ppu, cycles: BUFFER_SIZE as u32 - self.current_m_cycle}); } pub fn reset(&mut self){ diff --git a/lib_gb/src/apu/sound_terminal.rs b/lib_gb/src/apu/sound_terminal.rs index 1da007bf..1f393aa5 100644 --- a/lib_gb/src/apu/sound_terminal.rs +++ b/lib_gb/src/apu/sound_terminal.rs @@ -28,12 +28,14 @@ impl SoundTerminal{ #[inline] pub fn mix_terminal_samples(&self, samples:&[Sample;NUMBER_OF_CHANNELS])->Sample{ let mut mixed_sample:Sample = DEFAULT_SAPMPLE; - for i in 0..NUMBER_OF_CHANNELS{ - // This code should add the samples[i] only if channels[i] it true. - // After profiling this code is faster than if and since this is a hot spot in the code - // Im writing it like this. - mixed_sample += samples[i] & self.channel_masks[i] as Sample; - } + + // This code should add the samples[i] only if channels[i] it true. + // After profiling this code is faster than if and since this is a hot spot in the code + // Im writing it like this. + mixed_sample += samples[0] & self.channel_masks[0] as Sample; + mixed_sample += samples[1] & self.channel_masks[1] as Sample; + mixed_sample += samples[2] & self.channel_masks[2] as Sample; + mixed_sample += samples[3] & self.channel_masks[3] as Sample; mixed_sample >>= 2; // Divide by 4 in order to normal the sample diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index 5d4491e9..82f1e20e 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -26,6 +26,7 @@ pub struct IoComponents{ timer_event:Option, ppu_event:Option, + apu_event:Option, } io_port_index!(LCDC_REGISTER_INDEX, LCDC_REGISTER_ADDRESS); @@ -196,6 +197,7 @@ impl IoComponents{ timer_cycles:0, ppu_event:None, timer_event: None, + apu_event:Some(ScheduledEvent{cycles:1, event_type:super::scheduler::ScheduledEventType::Ppu}), } } @@ -214,6 +216,11 @@ impl IoComponents{ self.cycle_timer(); } } + if let Some(event) = &self.apu_event{ + if event.cycles <= self.apu_cycles{ + self.cycle_apu(); + } + } } fn cycle_ppu(&mut self){ @@ -223,7 +230,7 @@ impl IoComponents{ self.ppu_cycles = 0; } fn cycle_apu(&mut self){ - // self.apu.cycle(self.apu_cycles); + self.apu_event = self.apu.cycle(self.apu_cycles); self.apu_cycles = 0; } fn cycle_timer(&mut self){ From 53c1be51f1ecaf1c88435912b7220128e734e0dc Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 22 Jan 2022 01:13:09 +0200 Subject: [PATCH 06/26] change io_components to io_bus --- lib_gb/src/cpu/mod.rs | 22 +++++- lib_gb/src/machine/gameboy.rs | 2 +- lib_gb/src/machine/interrupts_handler.rs | 14 +--- lib_gb/src/mmu/gb_mmu.rs | 76 +++++++++---------- .../src/mmu/{io_components.rs => io_bus.rs} | 8 +- lib_gb/src/mmu/mod.rs | 2 +- 6 files changed, 67 insertions(+), 57 deletions(-) rename lib_gb/src/mmu/{io_components.rs => io_bus.rs} (97%) diff --git a/lib_gb/src/cpu/mod.rs b/lib_gb/src/cpu/mod.rs index 296b0e63..e970d9ab 100644 --- a/lib_gb/src/cpu/mod.rs +++ b/lib_gb/src/cpu/mod.rs @@ -2,4 +2,24 @@ pub mod gb_cpu; pub mod register; pub mod opcodes; pub mod flag; -pub mod opcode_runner; \ No newline at end of file +pub mod opcode_runner; + +use crate::mmu::memory::Memory; + +use self::gb_cpu::GbCpu; + +impl GbCpu { + pub fn prepare_for_interrupt(&mut self, memory: &mut impl Memory, address: u16)->u8{ + //reseting MIE register + self.mie = false; + //pushing PC + self::opcodes::opcodes_utils::push(self, memory, self.program_counter); + //jumping to the interupt address + self.program_counter = address; + //unhalting the CPU + self.halt = false; + + //cycles passed + return 5; + } +} \ No newline at end of file diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 5ed41351..7463037b 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -79,7 +79,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G let pc = self.cpu.program_counter; //debug - if self.mmu.io_components.finished_boot{ + if self.mmu.io_bus.finished_boot{ let a = *self.cpu.af.high(); let b = *self.cpu.bc.high(); let c = *self.cpu.bc.low(); diff --git a/lib_gb/src/machine/interrupts_handler.rs b/lib_gb/src/machine/interrupts_handler.rs index 89a53174..44f5d822 100644 --- a/lib_gb/src/machine/interrupts_handler.rs +++ b/lib_gb/src/machine/interrupts_handler.rs @@ -6,7 +6,6 @@ use crate::{cpu::gb_cpu::GbCpu, utils::{ STAT_REGISTER_ADDRESS } }}; -use crate::cpu::opcodes::opcodes_utils::push; use crate::mmu::memory::Memory; const V_BLANK_INTERRUPT_ADDERESS:u16 = 0x40; @@ -74,16 +73,7 @@ impl InterruptsHandler{ //reseting the interupt bit *interupt_flag &= !interupt_bit; memory.write(IF_REGISTER_ADDRESS, *interupt_flag); - //reseting MIE register - cpu.mie = false; - //pushing PC - push(cpu, memory, cpu.program_counter); - //jumping to the interupt address - cpu.program_counter = address; - //unhalting the CPU - cpu.halt = false; - - //cycles passed - return 5; + + return cpu.prepare_for_interrupt(memory, address); } } \ No newline at end of file diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index c04548c8..bf880ab8 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -1,4 +1,4 @@ -use super::{io_components::IoComponents, memory::*}; +use super::{io_bus::IoBus, memory::*}; use super::access_bus::AccessBus; use crate::ppu::gfx_device::GfxDevice; use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, utils::memory_registers::BOOT_REGISTER_ADDRESS}; @@ -14,7 +14,7 @@ const DMA_DEST:u16 = 0xFE00; const BAD_READ_VALUE:u8 = 0xFF; pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice>{ - pub io_components: IoComponents, + pub io_bus: IoBus, boot_rom:[u8;BOOT_ROM_SIZE], mbc: &'a mut Box, hram: [u8;HRAM_SIZE], @@ -25,9 +25,9 @@ pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice>{ //DMA only locks the used bus. there 2 possible used buses: extrnal (wram, rom, sram) and video (vram) impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ fn read(&mut self, address:u16)->u8{ - if let Some (bus) = &self.io_components.dma.enable{ + if let Some (bus) = &self.io_bus.dma.enable{ return match address{ - 0xFF00..=0xFF7F => self.io_components.read(address - 0xFF00), + 0xFF00..=0xFF7F => self.io_bus.read(address - 0xFF00), 0xFEA0..=0xFEFF | 0xFF80..=0xFFFE | 0xFFFF=>self.read_unprotected(address), 0x8000..=0x9FFF => if let AccessBus::External = bus {self.read_unprotected(address)} else{Self::bad_dma_read(address)}, 0..=0x7FFF | 0xA000..=0xFDFF => if let AccessBus::Video = bus {self.read_unprotected(address)} else{Self::bad_dma_read(address)}, @@ -37,7 +37,7 @@ impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ return match address{ 0x8000..=0x9FFF=>{ if self.is_vram_ready_for_io(){ - return self.io_components.ppu.vram.read_current_bank(address-0x8000); + return self.io_bus.ppu.vram.read_current_bank(address-0x8000); } else{ log::warn!("bad vram read"); @@ -46,22 +46,22 @@ impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ }, 0xFE00..=0xFE9F=>{ if self.is_oam_ready_for_io(){ - return self.io_components.ppu.oam[(address-0xFE00) as usize]; + return self.io_bus.ppu.oam[(address-0xFE00) as usize]; } else{ log::warn!("bad oam read"); return BAD_READ_VALUE; } }, - 0xFF00..=0xFF7F => self.io_components.read(address - 0xFF00), + 0xFF00..=0xFF7F => self.io_bus.read(address - 0xFF00), _=>self.read_unprotected(address) }; } fn write(&mut self, address:u16, value:u8){ - if let Some(bus) = &self.io_components.dma.enable{ + if let Some(bus) = &self.io_bus.dma.enable{ match address{ - 0xFF00..=0xFF7F => self.io_components.write(address- 0xFF00, value), + 0xFF00..=0xFF7F => self.io_bus.write(address- 0xFF00, value), 0xFF80..=0xFFFE | 0xFFFF=>self.write_unprotected(address, value), 0x8000..=0x9FFF => if let AccessBus::External = bus {self.write_unprotected(address, value)} else{Self::bad_dma_write(address)}, 0..=0x7FFF | 0xA000..=0xFDFF => if let AccessBus::Video = bus {self.write_unprotected(address, value)} else{Self::bad_dma_write(address)}, @@ -72,7 +72,7 @@ impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ match address{ 0x8000..=0x9FFF=>{ if self.is_vram_ready_for_io(){ - self.io_components.ppu.vram.write_current_bank(address-0x8000, value); + self.io_bus.ppu.vram.write_current_bank(address-0x8000, value); } else{ log::warn!("bad vram write") @@ -80,13 +80,13 @@ impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ }, 0xFE00..=0xFE9F=>{ if self.is_oam_ready_for_io(){ - self.io_components.ppu.oam[(address-0xFE00) as usize] = value; + self.io_bus.ppu.oam[(address-0xFE00) as usize] = value; } else{ log::warn!("bad oam write") } }, - 0xFF00..=0xFF7F=>self.io_components.write(address - 0xFF00, value), + 0xFF00..=0xFF7F=>self.io_bus.write(address - 0xFF00, value), _=>self.write_unprotected(address, value) } } @@ -97,7 +97,7 @@ impl<'a, D:AudioDevice, G:GfxDevice> UnprotectedMemory for GbMmu<'a, D, G>{ fn read_unprotected(&self, address:u16) ->u8 { return match address{ 0x0..=0xFF=>{ - if self.io_components.finished_boot{ + if self.io_bus.finished_boot{ return self.mbc.read_bank0(address); } @@ -105,14 +105,14 @@ impl<'a, D:AudioDevice, G:GfxDevice> UnprotectedMemory for GbMmu<'a, D, G>{ }, 0x100..=0x3FFF=>self.mbc.read_bank0(address), 0x4000..=0x7FFF=>self.mbc.read_current_bank(address-0x4000), - 0x8000..=0x9FFF=>self.io_components.ppu.vram.read_current_bank(address-0x8000), + 0x8000..=0x9FFF=>self.io_bus.ppu.vram.read_current_bank(address-0x8000), 0xA000..=0xBFFF=>self.mbc.read_external_ram(address-0xA000), - 0xC000..=0xCFFF =>self.io_components.ram.read_bank0(address - 0xC000), - 0xD000..=0xDFFF=>self.io_components.ram.read_current_bank(address-0xD000), - 0xE000..=0xFDFF=>self.io_components.ram.read_bank0(address - 0xE000), - 0xFE00..=0xFE9F=>self.io_components.ppu.oam[(address-0xFE00) as usize], + 0xC000..=0xCFFF =>self.io_bus.ram.read_bank0(address - 0xC000), + 0xD000..=0xDFFF=>self.io_bus.ram.read_current_bank(address-0xD000), + 0xE000..=0xFDFF=>self.io_bus.ram.read_bank0(address - 0xE000), + 0xFE00..=0xFE9F=>self.io_bus.ppu.oam[(address-0xFE00) as usize], 0xFEA0..=0xFEFF=>0x0, - 0xFF00..=0xFF7F=>self.io_components.read_unprotected(address - 0xFF00), + 0xFF00..=0xFF7F=>self.io_bus.read_unprotected(address - 0xFF00), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize], 0xFFFF=>self.interupt_enable_register }; @@ -121,14 +121,14 @@ impl<'a, D:AudioDevice, G:GfxDevice> UnprotectedMemory for GbMmu<'a, D, G>{ fn write_unprotected(&mut self, address:u16, value:u8) { match address{ 0x0..=0x7FFF=>self.mbc.write_rom(address, value), - 0x8000..=0x9FFF=>self.io_components.ppu.vram.write_current_bank(address-0x8000, value), + 0x8000..=0x9FFF=>self.io_bus.ppu.vram.write_current_bank(address-0x8000, value), 0xA000..=0xBFFF=>self.mbc.write_external_ram(address-0xA000,value), - 0xC000..=0xCFFF =>self.io_components.ram.write_bank0(address - 0xC000,value), - 0xE000..=0xFDFF=>self.io_components.ram.write_bank0(address - 0xE000,value), - 0xD000..=0xDFFF=>self.io_components.ram.write_current_bank(address-0xD000,value), - 0xFE00..=0xFE9F=>self.io_components.ppu.oam[(address-0xFE00) as usize] = value, + 0xC000..=0xCFFF =>self.io_bus.ram.write_bank0(address - 0xC000,value), + 0xE000..=0xFDFF=>self.io_bus.ram.write_bank0(address - 0xE000,value), + 0xD000..=0xDFFF=>self.io_bus.ram.write_current_bank(address-0xD000,value), + 0xFE00..=0xFE9F=>self.io_bus.ppu.oam[(address-0xFE00) as usize] = value, 0xFEA0..=0xFEFF=>{}, - 0xFF00..=0xFF7F=>self.io_components.write_unprotected(address - 0xFF00, value), + 0xFF00..=0xFF7F=>self.io_bus.write_unprotected(address - 0xFF00, value), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize] = value, 0xFFFF=>self.interupt_enable_register = value } @@ -138,7 +138,7 @@ impl<'a, D:AudioDevice, G:GfxDevice> UnprotectedMemory for GbMmu<'a, D, G>{ impl<'a, D:AudioDevice, G:GfxDevice> GbMmu<'a, D, G>{ pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu, gfx_device:G)->Self{ GbMmu{ - io_components:IoComponents::new(apu, gfx_device), + io_bus:IoBus::new(apu, gfx_device), mbc:mbc, hram:[0;HRAM_SIZE], interupt_enable_register:0, @@ -148,7 +148,7 @@ impl<'a, D:AudioDevice, G:GfxDevice> GbMmu<'a, D, G>{ pub fn new(mbc:&'a mut Box, apu:GbApu, gfx_device: G)->Self{ let mut mmu = GbMmu{ - io_components:IoComponents::new(apu, gfx_device), + io_bus:IoBus::new(apu, gfx_device), mbc:mbc, hram:[0;HRAM_SIZE], interupt_enable_register:0, @@ -163,31 +163,31 @@ impl<'a, D:AudioDevice, G:GfxDevice> GbMmu<'a, D, G>{ pub fn cycle(&mut self, cycles:u8){ self.handle_dma_trasnfer(cycles); - self.io_components.cycle(cycles as u32); + self.io_bus.cycle(cycles as u32); } fn handle_dma_trasnfer(&mut self, cycles: u8) { - if self.io_components.dma.enable.is_some(){ - let cycles_to_run = std::cmp::min(self.io_components.dma.dma_cycle_counter + cycles as u16, DMA_SIZE); - for i in self.io_components.dma.dma_cycle_counter..cycles_to_run as u16{ - self.write_unprotected(DMA_DEST + i, self.read_unprotected(self.io_components.dma.soure_address + i)); + if self.io_bus.dma.enable.is_some(){ + let cycles_to_run = std::cmp::min(self.io_bus.dma.dma_cycle_counter + cycles as u16, DMA_SIZE); + for i in self.io_bus.dma.dma_cycle_counter..cycles_to_run as u16{ + self.write_unprotected(DMA_DEST + i, self.read_unprotected(self.io_bus.dma.soure_address + i)); } - self.io_components.dma.dma_cycle_counter += cycles as u16; - if self.io_components.dma.dma_cycle_counter >= DMA_SIZE{ - self.io_components.dma.dma_cycle_counter = 0; - self.io_components.dma.enable = Option::None; + self.io_bus.dma.dma_cycle_counter += cycles as u16; + if self.io_bus.dma.dma_cycle_counter >= DMA_SIZE{ + self.io_bus.dma.dma_cycle_counter = 0; + self.io_bus.dma.enable = Option::None; } } } fn is_oam_ready_for_io(&self)->bool{ - let ppu_state = self.io_components.ppu.state as u8; + let ppu_state = self.io_bus.ppu.state as u8; return ppu_state != PpuState::OamSearch as u8 && ppu_state != PpuState::PixelTransfer as u8 } fn is_vram_ready_for_io(&self)->bool{ - return self.io_components.ppu.state as u8 != PpuState::PixelTransfer as u8; + return self.io_bus.ppu.state as u8 != PpuState::PixelTransfer as u8; } fn bad_dma_read(address:u16)->u8{ diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_bus.rs similarity index 97% rename from lib_gb/src/mmu/io_components.rs rename to lib_gb/src/mmu/io_bus.rs index 82f1e20e..10d72d49 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -11,7 +11,7 @@ pub const IO_PORTS_SIZE:usize = 0x80; const WAVE_RAM_START_INDEX:u16 = 0x30; const WAVE_RAM_END_INDEX:u16 = 0x3F; -pub struct IoComponents{ +pub struct IoBus{ pub ram: Ram, pub apu: GbApu, pub timer: GbTimer, @@ -44,7 +44,7 @@ io_port_index!(OBP0_REGISTER_INDEX, OBP0_REGISTER_ADDRESS); io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); io_port_index!(IF_REGISTER_INDEX, IF_REGISTER_ADDRESS); -impl Memory for IoComponents{ +impl Memory for IoBus{ fn read(&mut self, address:u16)->u8 { match address{ @@ -173,7 +173,7 @@ impl Memory for IoComponents{ } } -impl UnprotectedMemory for IoComponents{ +impl UnprotectedMemory for IoBus{ fn read_unprotected(&self, address:u16)->u8 { self.ports[address as usize] } @@ -183,7 +183,7 @@ impl UnprotectedMemory for IoComponents{ } } -impl IoComponents{ +impl IoBus{ pub fn new(apu:GbApu, gfx_device:GFX)->Self{ Self{apu, ports:[0;IO_PORTS_SIZE], diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index a5417947..25fedaa4 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -7,5 +7,5 @@ pub mod io_ports; pub mod carts; pub mod access_bus; pub mod oam_dma_transfer; -pub mod io_components; +pub mod io_bus; pub mod scheduler; \ No newline at end of file From 0994c10bedc051d249e34954c18221ba7c506fb7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Tue, 1 Feb 2022 18:38:22 +0200 Subject: [PATCH 07/26] Update the interrupt handler to not use Memory. * move the interrupt jumping to the cpu * fix the ppu event timing * add the registers to the io_bus --- lib_gb/src/cpu/gb_cpu.rs | 27 +++++++++ lib_gb/src/cpu/mod.rs | 22 +------- lib_gb/src/machine/gameboy.rs | 7 +-- lib_gb/src/machine/interrupts_handler.rs | 72 +++++++++++------------- lib_gb/src/mmu/gb_mmu.rs | 2 + lib_gb/src/mmu/io_bus.rs | 30 +++++----- lib_gb/src/ppu/gb_ppu.rs | 15 ++--- 7 files changed, 91 insertions(+), 84 deletions(-) diff --git a/lib_gb/src/cpu/gb_cpu.rs b/lib_gb/src/cpu/gb_cpu.rs index 3323d6e3..fd6df555 100644 --- a/lib_gb/src/cpu/gb_cpu.rs +++ b/lib_gb/src/cpu/gb_cpu.rs @@ -1,3 +1,6 @@ +use crate::machine::interrupts_handler::InterruptRequest; +use crate::mmu::memory::Memory; + use super::register::Reg; use super::flag::Flag; @@ -34,6 +37,30 @@ impl Default for GbCpu { } impl GbCpu { + pub fn execute_interrupt_request(&mut self, memory:&mut impl Memory, ir: InterruptRequest)->u8{ + match ir{ + InterruptRequest::Unhalt=>self.halt = false, + InterruptRequest::Interrupt(address)=>return self.prepare_for_interrupt(memory, address), + InterruptRequest::None=>{} + } + + return 0; + } + + fn prepare_for_interrupt(&mut self, memory: &mut impl Memory, address: u16)->u8{ + //reseting MIE register + self.mie = false; + //pushing PC + super::opcodes::opcodes_utils::push(self, memory, self.program_counter); + //jumping to the interupt address + self.program_counter = address; + //unhalting the CPU + self.halt = false; + + //cycles passed + return 5; + } + pub fn set_flag(&mut self, flag:Flag){ *self.af.low() |= flag as u8; } diff --git a/lib_gb/src/cpu/mod.rs b/lib_gb/src/cpu/mod.rs index e970d9ab..296b0e63 100644 --- a/lib_gb/src/cpu/mod.rs +++ b/lib_gb/src/cpu/mod.rs @@ -2,24 +2,4 @@ pub mod gb_cpu; pub mod register; pub mod opcodes; pub mod flag; -pub mod opcode_runner; - -use crate::mmu::memory::Memory; - -use self::gb_cpu::GbCpu; - -impl GbCpu { - pub fn prepare_for_interrupt(&mut self, memory: &mut impl Memory, address: u16)->u8{ - //reseting MIE register - self.mie = false; - //pushing PC - self::opcodes::opcodes_utils::push(self, memory, self.program_counter); - //jumping to the interupt address - self.program_counter = address; - //unhalting the CPU - self.halt = false; - - //cycles passed - return 5; - } -} \ No newline at end of file +pub mod opcode_runner; \ No newline at end of file diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 7463037b..3ad087c4 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -5,7 +5,6 @@ use crate::{ mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, ppu::gfx_device::GfxDevice }; -use super::interrupts_handler::InterruptsHandler; use std::boxed::Box; use log::debug; @@ -15,7 +14,6 @@ pub const CYCLES_PER_FRAME:u32 = 17556; pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice, GFX:GfxDevice> { cpu: GbCpu, mmu: GbMmu::<'a, AD, GFX>, - interrupts_handler:InterruptsHandler, joypad_provider: JP } @@ -25,7 +23,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G GameBoy{ cpu:GbCpu::default(), mmu:GbMmu::new_with_bootrom(mbc, boot_rom, GbApu::new(audio_device), gfx_device), - interrupts_handler: InterruptsHandler::default(), joypad_provider: joypad_provider } } @@ -43,7 +40,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G GameBoy{ cpu:cpu, mmu:GbMmu::new(mbc, GbApu::new(audio_device), gfx_device), - interrupts_handler: InterruptsHandler::default(), joypad_provider: joypad_provider, } } @@ -66,7 +62,8 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G self.mmu.cycle(cpu_cycles_passed); //interrupts - let interrupt_cycles = self.interrupts_handler.handle_interrupts(&mut self.cpu, &mut self.mmu); + let interrupt_request = self.mmu.io_bus.interrupt_handler.handle_interrupts(self.cpu.mie, self.mmu.io_bus.ppu.stat_register); + let interrupt_cycles = self.cpu.execute_interrupt_request(&mut self.mmu, interrupt_request); if interrupt_cycles != 0{ self.mmu.cycle(interrupt_cycles); } diff --git a/lib_gb/src/machine/interrupts_handler.rs b/lib_gb/src/machine/interrupts_handler.rs index 44f5d822..f382ff91 100644 --- a/lib_gb/src/machine/interrupts_handler.rs +++ b/lib_gb/src/machine/interrupts_handler.rs @@ -1,12 +1,4 @@ -use crate::{cpu::gb_cpu::GbCpu, utils::{ - bit_masks::*, - memory_registers::{ - IE_REGISTER_ADDRESS, - IF_REGISTER_ADDRESS, - STAT_REGISTER_ADDRESS - } -}}; -use crate::mmu::memory::Memory; +use crate::utils::bit_masks::*; const V_BLANK_INTERRUPT_ADDERESS:u16 = 0x40; const LCD_STAT_INTERRUPT_ADDERESS:u16 = 0x48; @@ -14,66 +6,70 @@ const TIMER_INTERRUPT_ADDERESS:u16 = 0x50; const SRIAL_INTERRUPT_ADDERESS:u16 = 0x58; const JOYPAD_INTERRUPT_ADDERESS:u16 = 0x60; +pub enum InterruptRequest{ + Interrupt(u16), + Unhalt, + None +} + pub struct InterruptsHandler{ - ei_triggered:bool + pub interrupt_flag:u8, + pub interrupt_enable_flag:u8, + ei_triggered:bool // use to delay the interrupt execution by one instruction in a special case } impl Default for InterruptsHandler{ fn default()->Self{ InterruptsHandler{ + interrupt_flag:0, + interrupt_enable_flag:0, ei_triggered:false } } } impl InterruptsHandler{ + pub fn handle_interrupts(&mut self, master_interrupt_enable:bool, stat_register:u8)->InterruptRequest{ + //there is a delay of one instruction cause there is this delay since EI opcode is called untill the interrupt could happen - pub fn handle_interrupts(&mut self, cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ - //this is delayed by one instruction cause there is this delay since EI opcode is called untill the interrupt could happen - - let mut interupt_flag = memory.read(IF_REGISTER_ADDRESS); - let interupt_enable = memory.read(IE_REGISTER_ADDRESS); - let stat_register = memory.read(STAT_REGISTER_ADDRESS); + let mut interrupt_request = InterruptRequest::None; - if cpu.mie && self.ei_triggered{ - if interupt_flag & BIT_0_MASK != 0 && interupt_enable & BIT_0_MASK != 0{ - return Self::prepare_for_interut(cpu, BIT_0_MASK, V_BLANK_INTERRUPT_ADDERESS, memory, &mut interupt_flag); + if master_interrupt_enable && self.ei_triggered{ + if self.interrupt_flag & BIT_0_MASK != 0 && self.interrupt_enable_flag & BIT_0_MASK != 0{ + interrupt_request = self.prepare_for_interrupt( BIT_0_MASK, V_BLANK_INTERRUPT_ADDERESS); } // Checking those STAT register bits for the STAT interrupts requests - if interupt_flag & BIT_1_MASK != 0 && interupt_enable & BIT_1_MASK != 0 && (stat_register & 0b111_1000) != 0{ - return Self::prepare_for_interut(cpu, BIT_1_MASK, LCD_STAT_INTERRUPT_ADDERESS, memory, &mut interupt_flag); + if self.interrupt_flag & BIT_1_MASK != 0 && self.interrupt_enable_flag & BIT_1_MASK != 0 && (stat_register & 0b111_1000) != 0{ + interrupt_request = self.prepare_for_interrupt(BIT_1_MASK, LCD_STAT_INTERRUPT_ADDERESS); } - if interupt_flag & BIT_2_MASK != 0 && interupt_enable & BIT_2_MASK != 0{ - return Self::prepare_for_interut(cpu, BIT_2_MASK, TIMER_INTERRUPT_ADDERESS, memory, &mut interupt_flag); + if self.interrupt_flag & BIT_2_MASK != 0 && self.interrupt_enable_flag & BIT_2_MASK != 0{ + interrupt_request = self.prepare_for_interrupt(BIT_2_MASK, TIMER_INTERRUPT_ADDERESS); } - if interupt_flag & BIT_3_MASK != 0 && interupt_enable & BIT_3_MASK != 0{ - return Self::prepare_for_interut(cpu, BIT_3_MASK, SRIAL_INTERRUPT_ADDERESS, memory, &mut interupt_flag); + if self.interrupt_flag & BIT_3_MASK != 0 && self.interrupt_enable_flag & BIT_3_MASK != 0{ + interrupt_request = self.prepare_for_interrupt(BIT_3_MASK, SRIAL_INTERRUPT_ADDERESS); } - if interupt_flag & BIT_4_MASK != 0 && interupt_enable & BIT_4_MASK != 0{ - return Self::prepare_for_interut(cpu, BIT_4_MASK, JOYPAD_INTERRUPT_ADDERESS, memory, &mut interupt_flag); + if self.interrupt_flag & BIT_4_MASK != 0 && self.interrupt_enable_flag & BIT_4_MASK != 0{ + interrupt_request = self.prepare_for_interrupt(BIT_4_MASK, JOYPAD_INTERRUPT_ADDERESS); } } - else if cpu.halt{ + else { for i in 0..5{ let mask = 1 << i; - if interupt_flag & mask != 0 && interupt_enable & mask != 0{ - cpu.halt = false; + if self.interrupt_flag & mask != 0 && self.interrupt_enable_flag & mask != 0{ + interrupt_request = InterruptRequest::Unhalt; } } } + self.ei_triggered = master_interrupt_enable; - self.ei_triggered = cpu.mie; - - //no cycles passed - return 0; + return interrupt_request; } - fn prepare_for_interut(cpu:&mut GbCpu, interupt_bit:u8, address:u16, memory:&mut impl Memory, interupt_flag:&mut u8)->u8{ + fn prepare_for_interrupt(&mut self, interupt_bit:u8, address:u16)->InterruptRequest{ //reseting the interupt bit - *interupt_flag &= !interupt_bit; - memory.write(IF_REGISTER_ADDRESS, *interupt_flag); + self.interrupt_flag &= !interupt_bit; - return cpu.prepare_for_interrupt(memory, address); + return InterruptRequest::Interrupt(address); } } \ No newline at end of file diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index bf880ab8..cd26966a 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -54,6 +54,7 @@ impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ } }, 0xFF00..=0xFF7F => self.io_bus.read(address - 0xFF00), + 0xFFFF => self.io_bus.interrupt_handler.interrupt_enable_flag, _=>self.read_unprotected(address) }; } @@ -87,6 +88,7 @@ impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ } }, 0xFF00..=0xFF7F=>self.io_bus.write(address - 0xFF00, value), + 0xFFFF => self.io_bus.interrupt_handler.interrupt_enable_flag = value, _=>self.write_unprotected(address, value) } } diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index 10d72d49..d8206f7f 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -1,9 +1,10 @@ -use crate::{apu::{*,audio_device::AudioDevice, gb_apu::GbApu}, +use crate::{ + apu::{*,audio_device::AudioDevice, gb_apu::GbApu}, ppu::{gb_ppu::GbPpu, ppu_register_updater::*, gfx_device::GfxDevice}, - timer::timer_register_updater::*, - utils::memory_registers::* + timer::{timer_register_updater::*, gb_timer::GbTimer}, + utils::memory_registers::*, + machine::interrupts_handler::InterruptsHandler }; -use crate::timer::gb_timer::GbTimer; use super::{access_bus::AccessBus, memory::*, oam_dma_transfer::OamDmaTransfer, ram::Ram, scheduler::ScheduledEvent}; use super::io_ports::*; @@ -17,6 +18,7 @@ pub struct IoBus{ pub timer: GbTimer, pub ppu:GbPpu, pub dma:OamDmaTransfer, + pub interrupt_handler:InterruptsHandler, pub finished_boot:bool, ports:[u8;IO_PORTS_SIZE], @@ -60,6 +62,8 @@ impl Memory for IoBus{ TAC_REGISTER_INDEX=> value & 0b111, DIV_REGISTER_INDEX=> get_div(&self.timer), TIMA_REGISTER_INDEX=> self.timer.tima_register, + //Interrupts handler + IF_REGISTER_INDEX => self.interrupt_handler.interrupt_flag, //APU NR10_REGISTER_INDEX=>value | 0b1000_0000, NR11_REGISTER_INDEX=> value | 0b0011_1111, @@ -114,6 +118,8 @@ impl Memory for IoBus{ set_tac(&mut self.timer, value); value &= 0b111; } + //Interrut handler + IF_REGISTER_INDEX => self.interrupt_handler.interrupt_flag = value, //APU NR10_REGISTER_INDEX=> set_nr10(&mut self.apu.sweep_tone_channel, value), NR11_REGISTER_INDEX=> set_nr11(&mut self.apu.sweep_tone_channel, value), @@ -185,12 +191,14 @@ impl UnprotectedMemory for IoBus{ impl IoBus{ pub fn new(apu:GbApu, gfx_device:GFX)->Self{ - Self{apu, + Self{ + apu, ports:[0;IO_PORTS_SIZE], timer:GbTimer::default(), - ppu:GbPpu::new(gfx_device), + ppu:GbPpu::new(gfx_device), dma:OamDmaTransfer::default(), - finished_boot:false, + interrupt_handler: InterruptsHandler::default(), + finished_boot:false, ram:Ram::default(), apu_cycles:0, ppu_cycles:0, @@ -224,9 +232,7 @@ impl IoBus{ } fn cycle_ppu(&mut self){ - let mut if_register = self.ports[IF_REGISTER_INDEX as usize]; - self.ppu_event = self.ppu.cycle(self.ppu_cycles, &mut if_register); - self.ports[IF_REGISTER_INDEX as usize] = if_register; + self.ppu_event = self.ppu.cycle(self.ppu_cycles, &mut self.interrupt_handler.interrupt_flag); self.ppu_cycles = 0; } fn cycle_apu(&mut self){ @@ -234,9 +240,7 @@ impl IoBus{ self.apu_cycles = 0; } fn cycle_timer(&mut self){ - let mut if_register = self.ports[IF_REGISTER_INDEX as usize]; - self.timer_event = self.timer.cycle(self.timer_cycles, &mut if_register); - self.ports[IF_REGISTER_INDEX as usize] = if_register; + self.timer_event = self.timer.cycle(self.timer_cycles, &mut self.interrupt_handler.interrupt_flag); self.timer_cycles = 0; } } \ No newline at end of file diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 917111b5..1b131709 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -114,7 +114,7 @@ impl GbPpu{ return None; } - let fethcer_cycles_to_next_event = self.cycle_fetcher(m_cycles, if_register); + let fethcer_cycles_to_next_event = self.cycle_fetcher(m_cycles, if_register) as u32; let stat_cycles_to_next_event = self.update_stat_register(if_register); @@ -130,7 +130,7 @@ impl GbPpu{ self.push_lcd_buffer.clear(); - return Some(ScheduledEvent{cycles, event_type:crate::mmu::scheduler::ScheduledEventType::Ppu}); + return Some(ScheduledEvent{cycles, event_type: crate::mmu::scheduler::ScheduledEventType::Ppu}); } fn swap_buffer(&mut self){ @@ -173,7 +173,7 @@ impl GbPpu{ }; } - fn cycle_fetcher(&mut self, m_cycles:u32, if_register:&mut u8)->u32{ + fn cycle_fetcher(&mut self, m_cycles:u32, if_register:&mut u8)->u16{ let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {EXTENDED_SPRITE_HIGHT} else {NORMAL_SPRITE_HIGHT}; for _ in 0..m_cycles * 2{ @@ -274,15 +274,16 @@ impl GbPpu{ self.t_cycles_passed += 2; } } - } let t_cycles = match self.state{ - PpuState::Vblank => (SCREEN_HEIGHT - (self.ly_register as usize - SCREEN_HEIGHT)) as u32 * HBLANK_T_CYCLES_LENGTH as u32, - _ => ((SCREEN_HEIGHT - self.ly_register as usize) * HBLANK_T_CYCLES_LENGTH as usize) as u32 + PpuState::Vblank => ((self.t_cycles_passed / HBLANK_T_CYCLES_LENGTH)+1) * HBLANK_T_CYCLES_LENGTH, + PpuState::Hblank => HBLANK_T_CYCLES_LENGTH, + PpuState::OamSearch => OAM_SEARCH_T_CYCLES_LENGTH, + PpuState::PixelTransfer => self.t_cycles_passed }; - return (t_cycles - self.t_cycles_passed as u32) >> 2; + return (t_cycles - self.t_cycles_passed) >> 2; } fn try_push_to_lcd(&mut self){ From 61a4c902eb3c8696fbb362844f2a0cbde9a6fabe Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 2 Feb 2022 01:09:58 +0200 Subject: [PATCH 08/26] Fix a bug where the ppu would stop cycle it happened cause the next even was to None when the ppu turned off but there was nothing to wake it up later when it turned on --- lib_gb/src/mmu/io_bus.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index d8206f7f..724d8601 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -210,9 +210,25 @@ impl IoBus{ } pub fn cycle(&mut self, cycles:u32){ - self.apu_cycles += cycles; - self.ppu_cycles += cycles; - self.timer_cycles += cycles; + if !self.apu_event.is_none(){ + self.apu_cycles += cycles; + } + else{ + self.apu_cycles = 0; + } + if !self.ppu_event.is_none(){ + self.ppu_cycles += cycles; + } + else{ + self.ppu_cycles = 0; + self.cycle_ppu(); + } + if !self.timer_event.is_none(){ + self.timer_cycles += cycles; + } + else{ + self.timer_cycles = 0; + } if let Some(event) = &self.ppu_event{ if event.cycles <= self.ppu_cycles{ From 20ba7941006ce7ddf737d24284ce23928db41e66 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 2 Feb 2022 01:13:31 +0200 Subject: [PATCH 09/26] Improve performance for the ppu * Made the fetching state machine to work better with batches of cycles * Improved the fifo data stracture - even though it is now uses a heap memory the algorithm is better and more effiecient (O(1) for all operations instead of O(n) for remove). Notice that this implementation is based upon that the Vec data stracture does not change it address in memory and its very much unsafe. --- lib_gb/src/ppu/gb_ppu.rs | 27 +++++---- lib_gb/src/utils/fixed_size_queue.rs | 81 +++++++++++++++++++------- lib_gb/tests/fixed_size_queue_tests.rs | 34 +++++++++++ 3 files changed, 111 insertions(+), 31 deletions(-) diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 1b131709..ac99052b 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -1,13 +1,8 @@ -use crate::mmu::scheduler::ScheduledEvent; +use crate::mmu::{scheduler::ScheduledEvent, vram::VRam}; use crate::utils::{vec2::Vec2, bit_masks::*}; -use crate::mmu::vram::VRam; -use crate::ppu::color::*; -use crate::ppu::colors::*; -use crate::ppu::gfx_device::GfxDevice; -use crate::ppu::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; +use crate::ppu::{gfx_device::GfxDevice, ppu_state::PpuState, sprite_attribute::SpriteAttribute, colors::*, color::*}; -use super::fifo::background_fetcher::BackgroundFetcher; -use super::fifo::{FIFO_SIZE, sprite_fetcher::*}; +use super::fifo::{FIFO_SIZE, sprite_fetcher::*, background_fetcher::BackgroundFetcher}; pub const SCREEN_HEIGHT: usize = 144; pub const SCREEN_WIDTH: usize = 160; @@ -175,8 +170,10 @@ impl GbPpu{ fn cycle_fetcher(&mut self, m_cycles:u32, if_register:&mut u8)->u16{ let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {EXTENDED_SPRITE_HIGHT} else {NORMAL_SPRITE_HIGHT}; + let t_cycles = m_cycles * 4; + let mut t_cycles_counter = 0; - for _ in 0..m_cycles * 2{ + while t_cycles_counter < t_cycles{ match self.state{ PpuState::OamSearch=>{ let oam_index = self.t_cycles_passed / 2; @@ -199,9 +196,13 @@ impl GbPpu{ self.state = PpuState::PixelTransfer; self.scanline_started = false; } + + t_cycles_counter += 2; } PpuState::Hblank=>{ - self.t_cycles_passed += 2; + let t_cycles_to_add = std::cmp::min((t_cycles - t_cycles_counter) as u16, HBLANK_T_CYCLES_LENGTH - self.t_cycles_passed); + self.t_cycles_passed += t_cycles_to_add; + t_cycles_counter += t_cycles_to_add as u32; if self.t_cycles_passed == HBLANK_T_CYCLES_LENGTH{ self.pixel_x_pos = 0; @@ -226,6 +227,10 @@ impl GbPpu{ } } PpuState::Vblank=>{ + let t_cycles_to_add = std::cmp::min((t_cycles - t_cycles_counter) as u16, VBLANK_T_CYCLES_LENGTH - self.t_cycles_passed); + self.t_cycles_passed += t_cycles_to_add; + t_cycles_counter += t_cycles_to_add as u32; + if self.t_cycles_passed == VBLANK_T_CYCLES_LENGTH{ self.state = PpuState::OamSearch; if self.oam_search_interrupt_request{ @@ -240,7 +245,6 @@ impl GbPpu{ self.ly_register = SCREEN_HEIGHT as u8 + (self.t_cycles_passed / HBLANK_T_CYCLES_LENGTH) as u8; } - self.t_cycles_passed += 2; } PpuState::PixelTransfer=>{ for _ in 0..2{ @@ -272,6 +276,7 @@ impl GbPpu{ } } self.t_cycles_passed += 2; + t_cycles_counter += 2; } } } diff --git a/lib_gb/src/utils/fixed_size_queue.rs b/lib_gb/src/utils/fixed_size_queue.rs index ef8a0760..17b2683f 100644 --- a/lib_gb/src/utils/fixed_size_queue.rs +++ b/lib_gb/src/utils/fixed_size_queue.rs @@ -1,21 +1,44 @@ -use super::create_default_array; - -pub struct FixedSizeQueue{ - data: [T;SIZE], +pub struct FixedSizeQueue{ + // According to the docs Vec should not be moved in memory if it not modified + // Im modifing it but not increasing its allocated size once its allocated so I hope this will work for me + // and I wont get weird memory issues + _data: Vec, + end_data_pointer: *mut T, + start_data_pointer: *mut T, + data_pointer: *mut T, + base_data_pointer: *mut T, length: usize, } -impl FixedSizeQueue{ +impl FixedSizeQueue{ pub fn new()->Self{ - Self{ - data:create_default_array(), + let data = Vec::with_capacity(SIZE); + let mut s = Self{ + _data:data, length:0, - } + base_data_pointer: std::ptr::null_mut(), + data_pointer: std::ptr::null_mut(), + end_data_pointer: std::ptr::null_mut(), + start_data_pointer: std::ptr::null_mut(), + }; + + s.base_data_pointer = s._data.as_mut_ptr(); + s.data_pointer = s._data.as_mut_ptr(); + s.start_data_pointer = s._data.as_mut_ptr(); + unsafe{s.end_data_pointer = s._data.as_mut_ptr().add(SIZE)}; + + return s; } pub fn push(&mut self, t:T){ if self.length < SIZE{ - self.data[self.length] = t; + unsafe{ + if self.data_pointer == self.end_data_pointer{ + self.data_pointer = self.start_data_pointer; + } + *self.data_pointer = t; + self.data_pointer = self.data_pointer.add(1); + } self.length += 1; } else{ @@ -25,12 +48,16 @@ impl FixedSizeQueue{ pub fn remove(&mut self)->T{ if self.length > 0{ - let t = self.data[0]; - for i in 1..self.length{ - self.data[i - 1] = self.data[i]; + unsafe{ + let t = *self.base_data_pointer; + self.base_data_pointer = self.base_data_pointer.add(1); + if self.base_data_pointer == self.end_data_pointer{ + self.base_data_pointer = self.start_data_pointer; + } + + self.length -= 1; + return t; } - self.length -= 1; - return t; } std::panic!("The fifo is empty"); @@ -38,6 +65,8 @@ impl FixedSizeQueue{ pub fn clear(&mut self){ self.length = 0; + self.data_pointer = self.start_data_pointer; + self.base_data_pointer = self.start_data_pointer; } pub fn len(&self)->usize{ @@ -45,22 +74,34 @@ impl FixedSizeQueue{ } } -impl std::ops::Index for FixedSizeQueue{ +impl std::ops::Index for FixedSizeQueue{ type Output = T; - fn index(&self, index: usize) -> &Self::Output { + fn index(&self, mut index: usize) -> &Self::Output { if index < self.length{ - return &self.data[index]; + unsafe{ + if self.base_data_pointer.add(index) >= self.end_data_pointer{ + index -= self.end_data_pointer.offset_from(self.base_data_pointer) as usize; + } + // casting a *mut T to a &T + return &*(self.base_data_pointer.add(index)); + } } std::panic!("Index is out of range"); } } -impl std::ops::IndexMut for FixedSizeQueue{ - fn index_mut(&mut self, index: usize) -> &mut Self::Output { +impl std::ops::IndexMut for FixedSizeQueue{ + fn index_mut(&mut self, mut index: usize) -> &mut Self::Output { if index < self.length{ - return &mut self.data[index]; + unsafe{ + if self.base_data_pointer.add(index) >= self.end_data_pointer{ + index -= self.end_data_pointer.offset_from(self.base_data_pointer) as usize; + } + // casting a *mut T to a &mut T + return &mut *(self.base_data_pointer.add(index)); + } } std::panic!("Index is out of range"); diff --git a/lib_gb/tests/fixed_size_queue_tests.rs b/lib_gb/tests/fixed_size_queue_tests.rs index 3e2e7391..d3e9e16e 100644 --- a/lib_gb/tests/fixed_size_queue_tests.rs +++ b/lib_gb/tests/fixed_size_queue_tests.rs @@ -18,6 +18,40 @@ fn test_fifo(){ assert_eq!(fifo[0], 21); } +#[test] +fn test_fifo_wrapping_around(){ + let mut fifo = FixedSizeQueue::::new(); + + check_push_and_remove(&mut fifo); + check_push_and_remove(&mut fifo); + check_push_and_remove(&mut fifo); + + fifo.push(10); + fifo.push(22); + fifo.remove(); + assert_eq!(fifo.len(), 1); + assert_eq!(fifo[0], 22); + + fifo[0] = 21; + assert_eq!(fifo[0], 21); +} + +fn check_push_and_remove(fifo: &mut FixedSizeQueue) { + fifo.push(10); + fifo.push(22); + fifo.push(33); + + assert_eq!(fifo.len(), 3); + assert_eq!(fifo[0], 10); + assert_eq!(fifo[1], 22); + assert_eq!(fifo[2], 33); + + assert_eq!(fifo.remove(), 10); + assert_eq!(fifo.remove(), 22); + assert_eq!(fifo.remove(), 33); + assert_eq!(fifo.len(), 0); +} + #[test] #[should_panic] fn panic_on_fifo_full(){ From 0cce2d9d071b6240b47ffc7d05f6ee5bdc94d80c Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 2 Feb 2022 14:22:09 +0200 Subject: [PATCH 10/26] Add another integartion test for dmg-acid2 --- lib_gb/tests/integration_tests.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/lib_gb/tests/integration_tests.rs b/lib_gb/tests/integration_tests.rs index 53520119..d65436ee 100644 --- a/lib_gb/tests/integration_tests.rs +++ b/lib_gb/tests/integration_tests.rs @@ -50,6 +50,12 @@ fn test_cpu_instrs_timing(){ run_integration_test_from_url(file_url, 100, 469033992149587554); } +#[test] +fn test_dmg_acid(){ + let file_url = "https://github.com/mattcurrie/dmg-acid2/releases/download/v1.0/dmg-acid2.gb"; + run_integration_test_from_url(file_url, 60, 1690571533691915665); +} + #[test] fn test_turtle_window_y_trigger(){ run_turtle_integration_test("window_y_trigger.gb", 15511617103807079362); @@ -115,7 +121,14 @@ fn run_integration_test(program:Vec, frames_to_execute:u32, expected_hash:u6 /// calc_hash("path_to_rom"); ///} ///``` -#[allow(dead_code)] + +#[test] +#[ignore] +fn generate_hash(){ + let path = "path to rom"; + calc_hash(path); +} + fn calc_hash(rom_path:&str){ static mut FRAMES_COUNTER:u32 = 0; static mut LAST_HASH:u64 = 0; From e8543eacd6c7b1db47ef3f7d0dbd57ba7ecbb1e2 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 2 Feb 2022 15:02:54 +0200 Subject: [PATCH 11/26] Move the interrupts handler to the mmu where it belongs --- lib_gb/src/cpu/gb_cpu.rs | 2 +- lib_gb/src/machine/gameboy.rs | 2 +- lib_gb/src/machine/mod.rs | 1 - lib_gb/src/mmu/gb_mmu.rs | 5 +++++ lib_gb/src/{machine => mmu}/interrupts_handler.rs | 0 lib_gb/src/mmu/io_bus.rs | 9 +++++---- lib_gb/src/mmu/mod.rs | 3 ++- 7 files changed, 14 insertions(+), 8 deletions(-) rename lib_gb/src/{machine => mmu}/interrupts_handler.rs (100%) diff --git a/lib_gb/src/cpu/gb_cpu.rs b/lib_gb/src/cpu/gb_cpu.rs index fd6df555..b04bf9a8 100644 --- a/lib_gb/src/cpu/gb_cpu.rs +++ b/lib_gb/src/cpu/gb_cpu.rs @@ -1,4 +1,4 @@ -use crate::machine::interrupts_handler::InterruptRequest; +use crate::mmu::interrupts_handler::InterruptRequest; use crate::mmu::memory::Memory; use super::register::Reg; diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 3ad087c4..d91ac983 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -62,7 +62,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G self.mmu.cycle(cpu_cycles_passed); //interrupts - let interrupt_request = self.mmu.io_bus.interrupt_handler.handle_interrupts(self.cpu.mie, self.mmu.io_bus.ppu.stat_register); + let interrupt_request = self.mmu.handle_interrupts(self.cpu.mie); let interrupt_cycles = self.cpu.execute_interrupt_request(&mut self.mmu, interrupt_request); if interrupt_cycles != 0{ self.mmu.cycle(interrupt_cycles); diff --git a/lib_gb/src/machine/mod.rs b/lib_gb/src/machine/mod.rs index 35abfc6c..a091ac4f 100644 --- a/lib_gb/src/machine/mod.rs +++ b/lib_gb/src/machine/mod.rs @@ -1,3 +1,2 @@ -pub mod interrupts_handler; pub mod gameboy; pub mod mbc_initializer; \ No newline at end of file diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index cd26966a..ca64b0cd 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -1,3 +1,4 @@ +use super::interrupts_handler::InterruptRequest; use super::{io_bus::IoBus, memory::*}; use super::access_bus::AccessBus; use crate::ppu::gfx_device::GfxDevice; @@ -168,6 +169,10 @@ impl<'a, D:AudioDevice, G:GfxDevice> GbMmu<'a, D, G>{ self.io_bus.cycle(cycles as u32); } + pub fn handle_interrupts(&mut self, master_interrupt_enable:bool)->InterruptRequest{ + return self.io_bus.interrupt_handler.handle_interrupts(master_interrupt_enable, self.io_bus.ppu.stat_register); + } + fn handle_dma_trasnfer(&mut self, cycles: u8) { if self.io_bus.dma.enable.is_some(){ let cycles_to_run = std::cmp::min(self.io_bus.dma.dma_cycle_counter + cycles as u16, DMA_SIZE); diff --git a/lib_gb/src/machine/interrupts_handler.rs b/lib_gb/src/mmu/interrupts_handler.rs similarity index 100% rename from lib_gb/src/machine/interrupts_handler.rs rename to lib_gb/src/mmu/interrupts_handler.rs diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index 724d8601..710def10 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -2,11 +2,12 @@ use crate::{ apu::{*,audio_device::AudioDevice, gb_apu::GbApu}, ppu::{gb_ppu::GbPpu, ppu_register_updater::*, gfx_device::GfxDevice}, timer::{timer_register_updater::*, gb_timer::GbTimer}, - utils::memory_registers::*, - machine::interrupts_handler::InterruptsHandler + utils::memory_registers::* +}; +use super::{ + interrupts_handler::*, access_bus::AccessBus, memory::*, + oam_dma_transfer::OamDmaTransfer, ram::Ram, scheduler::ScheduledEvent, io_ports::* }; -use super::{access_bus::AccessBus, memory::*, oam_dma_transfer::OamDmaTransfer, ram::Ram, scheduler::ScheduledEvent}; -use super::io_ports::*; pub const IO_PORTS_SIZE:usize = 0x80; const WAVE_RAM_START_INDEX:u16 = 0x30; diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index 25fedaa4..8541d560 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -8,4 +8,5 @@ pub mod carts; pub mod access_bus; pub mod oam_dma_transfer; pub mod io_bus; -pub mod scheduler; \ No newline at end of file +pub mod scheduler; +pub mod interrupts_handler; \ No newline at end of file From 53e0fcb3fd84d1b0100433c1e945989b57c1ea39 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 2 Feb 2022 16:43:35 +0200 Subject: [PATCH 12/26] typo in the write io method --- lib_gb/src/mmu/io_bus.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index 710def10..9b844063 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -103,7 +103,7 @@ impl Memory for IoBus{ fn write(&mut self, address:u16, mut value:u8) { match address{ DIV_REGISTER_INDEX | TIMA_REGISTER_INDEX | TMA_REGISTER_INDEX | TAC_REGISTER_INDEX => self.cycle_timer(), - NR10_REGISTER_INDEX..=WAVE_RAM_END_INDEX => self.cycle_ppu(), + NR10_REGISTER_INDEX..=WAVE_RAM_END_INDEX => self.cycle_apu(), LCDC_REGISTER_INDEX..=WX_REGISTER_INDEX => self.cycle_ppu(), _=>{} } From aba5b97df09741efeef3a9839cc59d59aa32f1bf Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 2 Feb 2022 19:15:51 +0200 Subject: [PATCH 13/26] Remove the scheduler and refactor the code a bit --- lib_gb/src/apu/gb_apu.rs | 4 +- lib_gb/src/mmu/io_bus.rs | 54 ++++------ lib_gb/src/mmu/mod.rs | 1 - lib_gb/src/mmu/scheduler.rs | 193 ----------------------------------- lib_gb/src/ppu/gb_ppu.rs | 6 +- lib_gb/src/timer/gb_timer.rs | 6 +- 6 files changed, 29 insertions(+), 235 deletions(-) delete mode 100644 lib_gb/src/mmu/scheduler.rs diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index 9d29e6b6..da65a36e 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -42,7 +42,7 @@ impl GbApu{ } } - pub fn cycle(&mut self, m_cycles_passed:u32)->Option{ + pub fn cycle(&mut self, m_cycles_passed:u32)->u32{ if self.enabled{ for _ in 0..m_cycles_passed{ @@ -75,7 +75,7 @@ impl GbApu{ } } - return Some(crate::mmu::scheduler::ScheduledEvent{ event_type: crate::mmu::scheduler::ScheduledEventType::Ppu, cycles: BUFFER_SIZE as u32 - self.current_m_cycle}); + return BUFFER_SIZE as u32 - self.current_m_cycle; } pub fn reset(&mut self){ diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index 9b844063..82c777a7 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -6,7 +6,7 @@ use crate::{ }; use super::{ interrupts_handler::*, access_bus::AccessBus, memory::*, - oam_dma_transfer::OamDmaTransfer, ram::Ram, scheduler::ScheduledEvent, io_ports::* + oam_dma_transfer::OamDmaTransfer, ram::Ram, io_ports::* }; pub const IO_PORTS_SIZE:usize = 0x80; @@ -23,13 +23,13 @@ pub struct IoBus{ pub finished_boot:bool, ports:[u8;IO_PORTS_SIZE], - apu_cycles:u32, + apu_cycles_counter:u32, ppu_cycles:u32, timer_cycles:u32, - timer_event:Option, - ppu_event:Option, - apu_event:Option, + timer_event_cycles:u32, + apu_event_cycles:u32, + ppu_event:Option, } io_port_index!(LCDC_REGISTER_INDEX, LCDC_REGISTER_ADDRESS); @@ -201,50 +201,38 @@ impl IoBus{ interrupt_handler: InterruptsHandler::default(), finished_boot:false, ram:Ram::default(), - apu_cycles:0, + apu_cycles_counter:0, ppu_cycles:0, timer_cycles:0, ppu_event:None, - timer_event: None, - apu_event:Some(ScheduledEvent{cycles:1, event_type:super::scheduler::ScheduledEventType::Ppu}), + timer_event_cycles: 0, + apu_event_cycles: 0, } } pub fn cycle(&mut self, cycles:u32){ - if !self.apu_event.is_none(){ - self.apu_cycles += cycles; - } - else{ - self.apu_cycles = 0; - } + self.apu_cycles_counter += cycles; + self.timer_cycles += cycles; + if !self.ppu_event.is_none(){ self.ppu_cycles += cycles; } else{ self.ppu_cycles = 0; + // When screen is off constantly try and check for activation by cycling with 0 self.cycle_ppu(); } - if !self.timer_event.is_none(){ - self.timer_cycles += cycles; - } - else{ - self.timer_cycles = 0; - } - if let Some(event) = &self.ppu_event{ - if event.cycles <= self.ppu_cycles{ + if let Some(cycles) = self.ppu_event{ + if cycles <= self.ppu_cycles{ self.cycle_ppu(); } } - if let Some(event) = &self.timer_event{ - if event.cycles <= self.timer_cycles{ - self.cycle_timer(); - } + if self.timer_event_cycles <= self.timer_cycles{ + self.cycle_timer(); } - if let Some(event) = &self.apu_event{ - if event.cycles <= self.apu_cycles{ - self.cycle_apu(); - } + if self.apu_event_cycles <= self.apu_cycles_counter{ + self.cycle_apu(); } } @@ -253,11 +241,11 @@ impl IoBus{ self.ppu_cycles = 0; } fn cycle_apu(&mut self){ - self.apu_event = self.apu.cycle(self.apu_cycles); - self.apu_cycles = 0; + self.apu_event_cycles = self.apu.cycle(self.apu_cycles_counter); + self.apu_cycles_counter = 0; } fn cycle_timer(&mut self){ - self.timer_event = self.timer.cycle(self.timer_cycles, &mut self.interrupt_handler.interrupt_flag); + self.timer_event_cycles = self.timer.cycle(self.timer_cycles, &mut self.interrupt_handler.interrupt_flag); self.timer_cycles = 0; } } \ No newline at end of file diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index 8541d560..f848af05 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -8,5 +8,4 @@ pub mod carts; pub mod access_bus; pub mod oam_dma_transfer; pub mod io_bus; -pub mod scheduler; pub mod interrupts_handler; \ No newline at end of file diff --git a/lib_gb/src/mmu/scheduler.rs b/lib_gb/src/mmu/scheduler.rs deleted file mode 100644 index 208742bc..00000000 --- a/lib_gb/src/mmu/scheduler.rs +++ /dev/null @@ -1,193 +0,0 @@ -#[derive(Clone, Copy, Debug)] -pub enum ScheduledEventType{ - Ppu, - Timer -} - -#[derive(Debug)] -pub struct ScheduledEvent{ - pub event_type:ScheduledEventType, - pub cycles:u32 -} - -impl Ord for ScheduledEvent { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.cycles.cmp(&other.cycles) - } - - fn clamp(self, min: Self, max: Self) -> Self - where Self: Sized { - let cycles = self.cycles.clamp(min.cycles, max.cycles); - Self{ - cycles, - event_type:self.event_type - } - } - - fn max(self, other: Self) -> Self - where Self: Sized { - return if self.cycles >= other.cycles{ - self - } - else{ - other - }; - } - - fn min(self, other: Self) -> Self - where Self: Sized { - return if self.cycles >= other.cycles{ - self - } - else{ - other - }; - } -} - -impl PartialOrd for ScheduledEvent{ - fn ge(&self, other: &Self) -> bool { - self.cycles.ge(&other.cycles) - } - - fn gt(&self, other: &Self) -> bool { - self.cycles.gt(&other.cycles) - } - - fn le(&self, other: &Self) -> bool { - self.cycles.le(&other.cycles) - } - - fn lt(&self, other: &Self) -> bool { - self.cycles.lt(&other.cycles) - } - - fn partial_cmp(&self, other: &Self) -> Option { - self.cycles.partial_cmp(&other.cycles) - } -} - -impl PartialEq for ScheduledEvent{ - fn eq(&self, other: &Self) -> bool { - self.cycles.eq(&other.cycles) - } - - fn ne(&self, other: &Self) -> bool { - self.cycles.ne(&other.cycles) - } -} - -impl Eq for ScheduledEvent{} - -pub struct Scheduler{ - events:Vec, - m_cycles:u32 -} - -impl Default for Scheduler{ - fn default() -> Self { - Self{ - events: Vec::new(), - m_cycles:0 - } - } -} - -impl Scheduler{ - pub fn add_event(&mut self, event:ScheduledEvent){ - let mut index = self.events.len(); - for i in 0..self.events.len(){ - if self.events[i].cycles > event.cycles{ - index = i; - break; - } - } - - self.events.insert(index, event); - } - - pub fn cycle(&mut self, m_cycles:u32)->Vec{ - let mut events = Vec::new(); - for _ in 0..m_cycles{ - - self.m_cycles += 1; - - while !self.events.is_empty() && self.m_cycles >= self.events[0].cycles{ - let event = self.events.remove(0); - - self.m_cycles -= event.cycles; - for e in &mut self.events{ - e.cycles -= event.cycles; - } - - events.push(event); - } - } - - return events; - } -} - -mod tests{ - use super::{ScheduledEvent, ScheduledEventType, Scheduler}; - - #[test] - pub fn add_event_test1(){ - let mut scheduler = Scheduler::default(); - - let event_type = ScheduledEventType::Ppu; - scheduler.add_event(ScheduledEvent{event_type, cycles: 10}); - scheduler.add_event(ScheduledEvent{event_type, cycles: 100}); - scheduler.add_event(ScheduledEvent{event_type, cycles: 50}); - scheduler.add_event(ScheduledEvent{event_type, cycles: 1000}); - - assert_eq!(scheduler.events, [ScheduledEvent{event_type, cycles: 10}, - ScheduledEvent{event_type, cycles: 50}, - ScheduledEvent{event_type, cycles: 100}, - ScheduledEvent{event_type, cycles: 1000}] - ); - } - - #[test] - pub fn add_event_test2(){ - let mut scheduler = Scheduler::default(); - - let event_type = ScheduledEventType::Ppu; - scheduler.add_event(ScheduledEvent{event_type, cycles: 2}); - scheduler.add_event(ScheduledEvent{event_type, cycles: 4}); - scheduler.add_event(ScheduledEvent{event_type, cycles: 1}); - scheduler.add_event(ScheduledEvent{event_type, cycles: 3}); - - assert_eq!(scheduler.events, [ScheduledEvent{event_type, cycles: 1}, - ScheduledEvent{event_type, cycles: 2}, - ScheduledEvent{event_type, cycles: 3}, - ScheduledEvent{event_type, cycles: 4}] - ); - - let events = scheduler.cycle(1); - - assert_eq!(scheduler.events, [ScheduledEvent{event_type, cycles: 1}, - ScheduledEvent{event_type, cycles: 2}, - ScheduledEvent{event_type, cycles: 3}] - ); - - assert_eq!(events, [ScheduledEvent{event_type, cycles: 1}]); - } - - #[test] - pub fn add_event_test3(){ - let mut scheduler = Scheduler::default(); - - let event_type = ScheduledEventType::Ppu; - scheduler.add_event(ScheduledEvent{event_type, cycles: 4}); - scheduler.add_event(ScheduledEvent{event_type, cycles: 3}); - scheduler.add_event(ScheduledEvent{event_type, cycles: 2}); - scheduler.add_event(ScheduledEvent{event_type, cycles: 1}); - - assert_eq!(scheduler.events, [ScheduledEvent{event_type, cycles: 1}, - ScheduledEvent{event_type, cycles: 2}, - ScheduledEvent{event_type, cycles: 3}, - ScheduledEvent{event_type, cycles: 4}] - ); - } -} \ No newline at end of file diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index ac99052b..410f2117 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -1,4 +1,4 @@ -use crate::mmu::{scheduler::ScheduledEvent, vram::VRam}; +use crate::mmu::vram::VRam; use crate::utils::{vec2::Vec2, bit_masks::*}; use crate::ppu::{gfx_device::GfxDevice, ppu_state::PpuState, sprite_attribute::SpriteAttribute, colors::*, color::*}; @@ -104,7 +104,7 @@ impl GbPpu{ self.state = PpuState::OamSearch; } - pub fn cycle(&mut self, m_cycles:u32, if_register:&mut u8)->Option{ + pub fn cycle(&mut self, m_cycles:u32, if_register:&mut u8)->Option{ if self.lcd_control & BIT_7_MASK == 0{ return None; } @@ -125,7 +125,7 @@ impl GbPpu{ self.push_lcd_buffer.clear(); - return Some(ScheduledEvent{cycles, event_type: crate::mmu::scheduler::ScheduledEventType::Ppu}); + return Some(cycles); } fn swap_buffer(&mut self){ diff --git a/lib_gb/src/timer/gb_timer.rs b/lib_gb/src/timer/gb_timer.rs index b74bc497..ccfef30a 100644 --- a/lib_gb/src/timer/gb_timer.rs +++ b/lib_gb/src/timer/gb_timer.rs @@ -1,4 +1,4 @@ -use crate::{mmu::scheduler::ScheduledEvent, utils::bit_masks::*}; +use crate::utils::bit_masks::*; pub struct GbTimer{ pub system_counter:u16, @@ -27,7 +27,7 @@ impl Default for GbTimer{ } impl GbTimer{ - pub fn cycle(&mut self, m_cycles:u32, if_register:&mut u8)->Option{ + pub fn cycle(&mut self, m_cycles:u32, if_register:&mut u8)->u32{ let (timer_interval, timer_enable) = self.get_timer_controller_data(); for _ in 0..m_cycles * 4{ @@ -70,7 +70,7 @@ impl GbTimer{ _=>std::panic!("error ") }; - Some(ScheduledEvent{cycles:(v>>2) as u32 + 1, event_type:crate::mmu::scheduler::ScheduledEventType::Timer}) + return (v>>2) as u32 + 1; } fn get_timer_controller_data(&self)->(u8, bool){ From 21183ea6602606d25360e91debdb33bd43ef0165 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 2 Feb 2022 20:20:43 +0200 Subject: [PATCH 14/26] Add comment for loop unrolling --- lib_gb/src/apu/sound_terminal.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib_gb/src/apu/sound_terminal.rs b/lib_gb/src/apu/sound_terminal.rs index 1f393aa5..61591345 100644 --- a/lib_gb/src/apu/sound_terminal.rs +++ b/lib_gb/src/apu/sound_terminal.rs @@ -32,6 +32,9 @@ impl SoundTerminal{ // This code should add the samples[i] only if channels[i] it true. // After profiling this code is faster than if and since this is a hot spot in the code // Im writing it like this. + // Also unrolling the for loop. + // for some reason this increase performance drastically. + mixed_sample += samples[0] & self.channel_masks[0] as Sample; mixed_sample += samples[1] & self.channel_masks[1] as Sample; mixed_sample += samples[2] & self.channel_masks[2] as Sample; From cd7a1fe45fa067068c04ef36585b0f8c894d9dce Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 3 Feb 2022 22:54:07 +0200 Subject: [PATCH 15/26] Remove the io_ports raw access all access to a port is though the relevant device also remove teh UnprotectedMemory trait - it was uncessary and only made things complex and slow --- lib_gb/src/apu/apu_registers_updater.rs | 42 +++--- lib_gb/src/apu/freq_sweep.rs | 6 +- lib_gb/src/apu/gb_apu.rs | 7 + lib_gb/src/apu/noise_sample_producer.rs | 12 +- lib_gb/src/apu/square_sample_producer.rs | 3 +- lib_gb/src/apu/volume_envelop.rs | 8 +- lib_gb/src/apu/wave_sample_producer.rs | 8 +- lib_gb/src/keypad/joypad.rs | 6 +- lib_gb/src/keypad/joypad_handler.rs | 45 ++++++ lib_gb/src/keypad/joypad_register_updater.rs | 25 ---- lib_gb/src/keypad/mod.rs | 2 +- lib_gb/src/machine/gameboy.rs | 19 +-- lib_gb/src/mmu/gb_mmu.rs | 32 +++-- lib_gb/src/mmu/io_bus.rs | 143 ++++++++----------- lib_gb/src/mmu/io_ports.rs | 20 ++- lib_gb/src/mmu/memory.rs | 5 - lib_gb/src/mmu/mod.rs | 1 - lib_gb/src/ppu/gb_ppu.rs | 6 + lib_gb/src/ppu/ppu_register_updater.rs | 14 +- lib_gb/src/utils/bit_masks.rs | 4 +- 20 files changed, 223 insertions(+), 185 deletions(-) create mode 100644 lib_gb/src/keypad/joypad_handler.rs delete mode 100644 lib_gb/src/keypad/joypad_register_updater.rs diff --git a/lib_gb/src/apu/apu_registers_updater.rs b/lib_gb/src/apu/apu_registers_updater.rs index a864b130..ee89e66d 100644 --- a/lib_gb/src/apu/apu_registers_updater.rs +++ b/lib_gb/src/apu/apu_registers_updater.rs @@ -1,4 +1,4 @@ -use crate::{mmu::io_ports::*, utils::bit_masks::*}; +use crate::utils::bit_masks::*; use super::{ audio_device::AudioDevice, channel::Channel, @@ -30,6 +30,7 @@ pub fn set_nr43(channel:&mut Channel, nr43:u8){ channel.sample_producer.bits_to_shift_divisor = (nr43 & 0b1111_0000) >> 4; channel.sample_producer.width_mode = (nr43 & BIT_3_MASK) != 0; channel.sample_producer.divisor_code = nr43 & 0b111; + channel.sample_producer.nr43_register = nr43; } pub fn set_nr44(channel:&mut Channel, fs:&FrameSequencer, nr44:u8){ @@ -46,6 +47,7 @@ pub fn set_nr44(channel:&mut Channel, fs:&FrameSequencer, n pub fn set_nr50(apu:&mut GbApu, nr50:u8){ apu.right_terminal.volume = nr50 & 0b111; apu.left_terminal.volume = (nr50 & 0b111_0000) >> 4; + apu.nr50_register = nr50; } @@ -56,9 +58,10 @@ pub fn set_nr51(apu:&mut GbApu, nr51:u8){ for i in 0..NUMBER_OF_CHANNELS{ apu.left_terminal.set_channel_state(i, nr51 & (0b1_0000 << i) != 0); } + apu.nr51_register = nr51; } -pub fn set_nr52(apu:&mut GbApu, ports:&mut [u8;IO_PORTS_SIZE], nr52:u8){ +pub fn set_nr52(apu:&mut GbApu, nr52:u8){ let prev_apu_state = apu.enabled; apu.enabled = nr52 & BIT_7_MASK != 0; @@ -66,21 +69,23 @@ pub fn set_nr52(apu:&mut GbApu, ports:&mut [u8;IO_PORTS_SIZE if !apu.enabled && prev_apu_state{ apu.reset(); } - - for i in NR10_REGISTER_INDEX..NR52_REGISTER_INDEX{ - ports[i as usize] = 0; - } } -pub fn get_nr52(apu:&GbApu, nr52:&mut u8){ - set_bit_u8(nr52, 3, apu.noise_channel.enabled && apu.noise_channel.length_enable && apu.noise_channel.sound_length != 0); - set_bit_u8(nr52, 2, apu.wave_channel.enabled && apu.wave_channel.length_enable && apu.wave_channel.sound_length != 0); - set_bit_u8(nr52, 1, apu.tone_channel.enabled && apu.tone_channel.length_enable && apu.tone_channel.sound_length != 0); - set_bit_u8(nr52, 0, apu.sweep_tone_channel.enabled && apu.sweep_tone_channel.length_enable && apu.sweep_tone_channel.sound_length != 0); +pub fn get_nr52(apu:&GbApu)->u8{ + let mut nr52:u8 = 0; + flip_bit_u8(&mut nr52, 3, apu.noise_channel.enabled && apu.noise_channel.length_enable && apu.noise_channel.sound_length != 0); + flip_bit_u8(&mut nr52, 2, apu.wave_channel.enabled && apu.wave_channel.length_enable && apu.wave_channel.sound_length != 0); + flip_bit_u8(&mut nr52, 1, apu.tone_channel.enabled && apu.tone_channel.length_enable && apu.tone_channel.sound_length != 0); + flip_bit_u8(&mut nr52, 0, apu.sweep_tone_channel.enabled && apu.sweep_tone_channel.length_enable && apu.sweep_tone_channel.sound_length != 0); + flip_bit_u8(&mut nr52, 7, apu.enabled); + return nr52; } pub fn set_nr30(channel:&mut Channel, value:u8){ - if (value & BIT_7_MASK) == 0{ + // Turning this bit on does not directly enable the channel its only enable dac + // but if the dac is off the channel is disabled + channel.sample_producer.nr30_dac_state = (value & BIT_7_MASK) == 0; + if channel.sample_producer.nr30_dac_state { channel.enabled = false; } } @@ -100,13 +105,12 @@ pub fn set_nr33(channel:&mut Channel, nr33:u8){ channel.frequency |= nr33 as u16; } -pub fn set_nr34(channel:&mut Channel, fs:&FrameSequencer, nr30:u8, nr34:u8){ +pub fn set_nr34(channel:&mut Channel, fs:&FrameSequencer, nr34:u8){ //clear the upper 8 bits channel.frequency &= 0xFF; channel.frequency |= ((nr34 & 0b111) as u16) << 8; - let dac_enabled = (nr30 & BIT_7_MASK) != 0; - update_channel_conrol_register(channel, dac_enabled, nr34, 256, fs); + update_channel_conrol_register(channel, channel.sample_producer.nr30_dac_state, nr34, 256, fs); if nr34 & BIT_7_MASK != 0{ channel.sample_producer.reset_counter(); @@ -118,13 +122,14 @@ pub fn set_nr10(channel:&mut Channel, value:u8){ sweep.sweep_decrease = (value & 0b1000) != 0; sweep.sweep_shift = value & 0b111; sweep.sweep_period = (value & 0b111_0000) >> 4; + sweep.register = value | 0b1000_0000; } -pub fn set_nr11(channel:&mut Channel, value:u8){ +pub fn set_nrx1(channel:&mut Channel, value:u8){ channel.sample_producer.wave_duty = (value & 0b1100_0000) >> 6; channel.sound_length = 64 - (value & 0b11_1111) as u16 } - pub fn set_nr12(channel:&mut Channel, value:u8){ + pub fn set_nrx2(channel:&mut Channel, value:u8){ update_volume_envelope(value, &mut channel.sample_producer.envelop); if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ @@ -132,7 +137,7 @@ pub fn set_nr11(channel:&mut Channel, value:u8){ } } - pub fn set_nr13(channel:&mut Channel, value:u8){ + pub fn set_nrx3(channel:&mut Channel, value:u8){ //discard lower bits channel.frequency &= 0xFF00; channel.frequency |= value as u16; @@ -243,6 +248,7 @@ fn update_volume_envelope(register:u8, envelop:&mut VolumeEnvlope){ envelop.volume = (register & 0b1111_0000) >> 4; envelop.number_of_envelope_sweep = register & 0b111; envelop.increase_envelope = (register & BIT_3_MASK) != 0; + envelop.register = register; } fn is_dac_enabled(volume:u8, envelop_increase:bool)->bool{ diff --git a/lib_gb/src/apu/freq_sweep.rs b/lib_gb/src/apu/freq_sweep.rs index e37bfc39..78137e1c 100644 --- a/lib_gb/src/apu/freq_sweep.rs +++ b/lib_gb/src/apu/freq_sweep.rs @@ -4,7 +4,9 @@ pub struct FreqSweep{ pub sweep_period:u8, //the original value from the register pub sweep_decrease:bool, pub sweep_shift:u8, - pub shadow_frequency:u16 + pub shadow_frequency:u16, + + pub register:u8, // The raw value of the NR10 register } impl FreqSweep{ @@ -15,6 +17,8 @@ impl FreqSweep{ self.sweep_decrease = false; self.enabled = false; self.sweep_period = 0; + + self.register = 0; } pub fn reload_sweep_time(&mut self){ diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index da65a36e..f37516be 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -20,6 +20,9 @@ pub struct GbApu{ pub left_terminal:SoundTerminal, pub enabled:bool, + pub nr50_register:u8, // The register orignal raw value + pub nr51_register:u8, // The register orignal raw value + audio_buffer:[StereoSample;BUFFER_SIZE], current_m_cycle:u32, device:Device, @@ -39,6 +42,8 @@ impl GbApu{ right_terminal: SoundTerminal::default(), left_terminal: SoundTerminal::default(), enabled:false, + nr50_register:0, + nr51_register:0, } } @@ -84,6 +89,8 @@ impl GbApu{ self.wave_channel.reset(); self.noise_channel.reset(); self.frame_sequencer.reset(); + self.nr50_register = 0; + self.nr51_register = 0; } fn push_buffer_if_full(&mut self){ diff --git a/lib_gb/src/apu/noise_sample_producer.rs b/lib_gb/src/apu/noise_sample_producer.rs index 14a24d12..534fcfa2 100644 --- a/lib_gb/src/apu/noise_sample_producer.rs +++ b/lib_gb/src/apu/noise_sample_producer.rs @@ -1,4 +1,4 @@ -use crate::utils::bit_masks::set_bit_u16; +use crate::utils::bit_masks::flip_bit_u16; use super::{sample_producer::SampleProducer, volume_envelop::VolumeEnvlope}; @@ -7,7 +7,9 @@ pub struct NoiseSampleProducer{ pub lfsr:u16, pub bits_to_shift_divisor:u8, pub width_mode:bool, - pub divisor_code:u8 + pub divisor_code:u8, + + pub nr43_register:u8, // The register original raw value } impl Default for NoiseSampleProducer{ @@ -17,7 +19,8 @@ impl Default for NoiseSampleProducer{ divisor_code:0, width_mode:false, bits_to_shift_divisor:0, - lfsr:0 + lfsr:0, + nr43_register:0 } } } @@ -30,7 +33,7 @@ impl SampleProducer for NoiseSampleProducer{ self.lfsr |= xor_result << 14; if self.width_mode{ - set_bit_u16(&mut self.lfsr, 6, false); + flip_bit_u16(&mut self.lfsr, 6, false); self.lfsr |= xor_result << 6; } @@ -45,6 +48,7 @@ impl SampleProducer for NoiseSampleProducer{ self.bits_to_shift_divisor = 0; self.divisor_code = 0; self.envelop.reset(); + self.nr43_register = 0; } fn get_updated_frequency_ticks(&self, _freq:u16)->u16 { diff --git a/lib_gb/src/apu/square_sample_producer.rs b/lib_gb/src/apu/square_sample_producer.rs index b41d1e43..22bdc5cc 100644 --- a/lib_gb/src/apu/square_sample_producer.rs +++ b/lib_gb/src/apu/square_sample_producer.rs @@ -20,7 +20,8 @@ impl SquareSampleProducer{ sweep_decrease:false, sweep_counter:0, shadow_frequency:0, - sweep_period:0 + sweep_period:0, + register: 0, }), envelop:VolumeEnvlope::default(), duty_sample_pointer:0 diff --git a/lib_gb/src/apu/volume_envelop.rs b/lib_gb/src/apu/volume_envelop.rs index e0f002fb..50ce2aff 100644 --- a/lib_gb/src/apu/volume_envelop.rs +++ b/lib_gb/src/apu/volume_envelop.rs @@ -4,7 +4,9 @@ pub struct VolumeEnvlope{ pub increase_envelope:bool, pub number_of_envelope_sweep:u8, - pub envelop_duration_counter:u8 + pub envelop_duration_counter:u8, + + pub register:u8, // The original register raw value } impl VolumeEnvlope{ @@ -12,6 +14,7 @@ impl VolumeEnvlope{ self.increase_envelope = false; self.number_of_envelope_sweep = 0; self.envelop_duration_counter = 0; + self.register = 0; } pub fn tick(&mut self){ @@ -42,7 +45,8 @@ impl Default for VolumeEnvlope{ volume:0, increase_envelope:false, number_of_envelope_sweep:0, - envelop_duration_counter:0 + envelop_duration_counter:0, + register:0 } } } \ No newline at end of file diff --git a/lib_gb/src/apu/wave_sample_producer.rs b/lib_gb/src/apu/wave_sample_producer.rs index 07a7cb7c..87e2dd02 100644 --- a/lib_gb/src/apu/wave_sample_producer.rs +++ b/lib_gb/src/apu/wave_sample_producer.rs @@ -4,6 +4,10 @@ pub struct WaveSampleProducer{ pub wave_samples:[u8;16], pub volume:u8, + pub nr30_dac_state:bool, // The raw value of the register, + // saving it casue the in the wave channel the on/off bit controls the dac and not the channel itself, + // and in order to turn on the channel we need to make sure that the dac is on + sample_counter:u8 } @@ -12,7 +16,8 @@ impl Default for WaveSampleProducer{ WaveSampleProducer{ wave_samples:[0;16], volume:0, - sample_counter:0 + sample_counter:0, + nr30_dac_state:false, } } } @@ -36,6 +41,7 @@ impl SampleProducer for WaveSampleProducer{ fn reset(&mut self) { self.volume = 0; self.sample_counter = 0; + self.nr30_dac_state = false; } fn get_updated_frequency_ticks(&self, freq:u16)->u16 { diff --git a/lib_gb/src/keypad/joypad.rs b/lib_gb/src/keypad/joypad.rs index 39485786..a7376c31 100644 --- a/lib_gb/src/keypad/joypad.rs +++ b/lib_gb/src/keypad/joypad.rs @@ -1,14 +1,14 @@ - pub const NUM_OF_KEYS: usize = 8; pub struct Joypad{ - pub buttons:[bool;NUM_OF_KEYS] + pub buttons:[bool;NUM_OF_KEYS], } impl Default for Joypad{ fn default()->Self{ + // Since the button pressed state is 0 initializing to unpressed state (1 == true) Joypad{ - buttons:[false;NUM_OF_KEYS] + buttons:[true;NUM_OF_KEYS], } } } \ No newline at end of file diff --git a/lib_gb/src/keypad/joypad_handler.rs b/lib_gb/src/keypad/joypad_handler.rs new file mode 100644 index 00000000..667ab1e3 --- /dev/null +++ b/lib_gb/src/keypad/joypad_handler.rs @@ -0,0 +1,45 @@ +use crate::utils::bit_masks::*; +use super::{joypad_provider::JoypadProvider, joypad::Joypad, button::Button}; + + +pub struct JoypadHandler{ + pub register:u8, + + joypad:Joypad, + joypad_provider:JP, +} + +impl JoypadHandler{ + pub fn new(provider: JP)->Self{ + Self{ + joypad_provider:provider, + register:0xFF, + joypad: Joypad::default() + } + } + + pub fn poll_joypad_state(&mut self){ + self.joypad_provider.provide(&mut self.joypad); + + let buttons = (self.register & BIT_5_MASK) == 0; + let directions = (self.register & BIT_4_MASK) == 0; + + if buttons{ + flip_bit_u8(&mut self.register, 0, !self.joypad.buttons[Button::A as usize]); + flip_bit_u8(&mut self.register, 1, !self.joypad.buttons[Button::B as usize]); + flip_bit_u8(&mut self.register, 2, !self.joypad.buttons[Button::Select as usize]); + flip_bit_u8(&mut self.register, 3, !self.joypad.buttons[Button::Start as usize]); + } + if directions{ + flip_bit_u8(&mut self.register, 0, !self.joypad.buttons[Button::Right as usize]); + flip_bit_u8(&mut self.register, 1, !self.joypad.buttons[Button::Left as usize]); + flip_bit_u8(&mut self.register, 2, !self.joypad.buttons[Button::Up as usize]); + flip_bit_u8(&mut self.register, 3, !self.joypad.buttons[Button::Down as usize]); + } + } + + pub fn set_register(&mut self, value:u8){ + self.register &= 0b1100_1111; // Reset bit 4 & 5 + self.register |= value & 0b0011_0000; // Seting the bits + } +} \ No newline at end of file diff --git a/lib_gb/src/keypad/joypad_register_updater.rs b/lib_gb/src/keypad/joypad_register_updater.rs deleted file mode 100644 index bd411b64..00000000 --- a/lib_gb/src/keypad/joypad_register_updater.rs +++ /dev/null @@ -1,25 +0,0 @@ -use crate::{mmu::memory::UnprotectedMemory, - utils::{bit_masks::{BIT_4_MASK, BIT_5_MASK, set_bit_u8}, memory_registers::JOYP_REGISTER_ADDRESS}}; -use super::{button::Button, joypad::Joypad}; - -pub fn update_joypad_registers(joypad:&Joypad, memory:&mut impl UnprotectedMemory){ - let mut state = memory.read_unprotected(JOYP_REGISTER_ADDRESS); - - let buttons = (state & BIT_5_MASK) == 0; - let directions = (state & BIT_4_MASK) == 0; - - if buttons{ - set_bit_u8(&mut state, 0, !joypad.buttons[Button::A as usize]); - set_bit_u8(&mut state, 1, !joypad.buttons[Button::B as usize]); - set_bit_u8(&mut state, 2, !joypad.buttons[Button::Select as usize]); - set_bit_u8(&mut state, 3, !joypad.buttons[Button::Start as usize]); - } - if directions{ - set_bit_u8(&mut state, 0, !joypad.buttons[Button::Right as usize]); - set_bit_u8(&mut state, 1, !joypad.buttons[Button::Left as usize]); - set_bit_u8(&mut state, 2, !joypad.buttons[Button::Up as usize]); - set_bit_u8(&mut state, 3, !joypad.buttons[Button::Down as usize]); - } - - memory.write_unprotected(JOYP_REGISTER_ADDRESS, state); -} \ No newline at end of file diff --git a/lib_gb/src/keypad/mod.rs b/lib_gb/src/keypad/mod.rs index 05d258c9..932652c7 100644 --- a/lib_gb/src/keypad/mod.rs +++ b/lib_gb/src/keypad/mod.rs @@ -1,4 +1,4 @@ pub mod joypad; pub mod joypad_provider; pub mod button; -pub mod joypad_register_updater; \ No newline at end of file +pub mod joypad_handler; \ No newline at end of file diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index d91ac983..7449d5db 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -1,9 +1,8 @@ use crate::{ apu::{audio_device::AudioDevice, gb_apu::GbApu}, - cpu::gb_cpu::GbCpu, - keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, + cpu::gb_cpu::GbCpu, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, - ppu::gfx_device::GfxDevice + ppu::gfx_device::GfxDevice, keypad::joypad_provider::JoypadProvider }; use std::boxed::Box; use log::debug; @@ -13,8 +12,7 @@ pub const CYCLES_PER_FRAME:u32 = 17556; pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice, GFX:GfxDevice> { cpu: GbCpu, - mmu: GbMmu::<'a, AD, GFX>, - joypad_provider: JP + mmu: GbMmu::<'a, AD, GFX, JP> } impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, GFX>{ @@ -22,8 +20,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G pub fn new_with_bootrom(mbc:&'a mut Box,joypad_provider:JP, audio_device:AD, gfx_device:GFX, boot_rom:[u8;BOOT_ROM_SIZE])->GameBoy{ GameBoy{ cpu:GbCpu::default(), - mmu:GbMmu::new_with_bootrom(mbc, boot_rom, GbApu::new(audio_device), gfx_device), - joypad_provider: joypad_provider + mmu:GbMmu::new_with_bootrom(mbc, boot_rom, GbApu::new(audio_device), gfx_device, joypad_provider), } } @@ -39,19 +36,15 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G GameBoy{ cpu:cpu, - mmu:GbMmu::new(mbc, GbApu::new(audio_device), gfx_device), - joypad_provider: joypad_provider, + mmu:GbMmu::new(mbc, GbApu::new(audio_device), gfx_device, joypad_provider) } } pub fn cycle_frame(&mut self){ - let mut joypad = Joypad::default(); - let mut cycles_counter = 0; while cycles_counter < CYCLES_PER_FRAME{ - self.joypad_provider.provide(&mut joypad); - joypad_register_updater::update_joypad_registers(&joypad, &mut self.mmu); + self.mmu.poll_joypad_state(); //CPU let mut cpu_cycles_passed = 1; diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index ca64b0cd..15dad6e3 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -1,6 +1,7 @@ use super::interrupts_handler::InterruptRequest; use super::{io_bus::IoBus, memory::*}; use super::access_bus::AccessBus; +use crate::keypad::joypad_provider::JoypadProvider; use crate::ppu::gfx_device::GfxDevice; use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, utils::memory_registers::BOOT_REGISTER_ADDRESS}; use super::carts::mbc::Mbc; @@ -14,8 +15,8 @@ const DMA_DEST:u16 = 0xFE00; const BAD_READ_VALUE:u8 = 0xFF; -pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice>{ - pub io_bus: IoBus, +pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider>{ + pub io_bus: IoBus, boot_rom:[u8;BOOT_ROM_SIZE], mbc: &'a mut Box, hram: [u8;HRAM_SIZE], @@ -24,7 +25,7 @@ pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice>{ //DMA only locks the used bus. there 2 possible used buses: extrnal (wram, rom, sram) and video (vram) -impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ +impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G, J>{ fn read(&mut self, address:u16)->u8{ if let Some (bus) = &self.io_bus.dma.enable{ return match address{ @@ -96,8 +97,8 @@ impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ } } -impl<'a, D:AudioDevice, G:GfxDevice> UnprotectedMemory for GbMmu<'a, D, G>{ - fn read_unprotected(&self, address:u16) ->u8 { +impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ + fn read_unprotected(&mut self, address:u16) ->u8 { return match address{ 0x0..=0xFF=>{ if self.io_bus.finished_boot{ @@ -115,7 +116,7 @@ impl<'a, D:AudioDevice, G:GfxDevice> UnprotectedMemory for GbMmu<'a, D, G>{ 0xE000..=0xFDFF=>self.io_bus.ram.read_bank0(address - 0xE000), 0xFE00..=0xFE9F=>self.io_bus.ppu.oam[(address-0xFE00) as usize], 0xFEA0..=0xFEFF=>0x0, - 0xFF00..=0xFF7F=>self.io_bus.read_unprotected(address - 0xFF00), + 0xFF00..=0xFF7F=>self.io_bus.read(address - 0xFF00), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize], 0xFFFF=>self.interupt_enable_register }; @@ -131,17 +132,17 @@ impl<'a, D:AudioDevice, G:GfxDevice> UnprotectedMemory for GbMmu<'a, D, G>{ 0xD000..=0xDFFF=>self.io_bus.ram.write_current_bank(address-0xD000,value), 0xFE00..=0xFE9F=>self.io_bus.ppu.oam[(address-0xFE00) as usize] = value, 0xFEA0..=0xFEFF=>{}, - 0xFF00..=0xFF7F=>self.io_bus.write_unprotected(address - 0xFF00, value), + 0xFF00..=0xFF7F=>self.io_bus.write(address - 0xFF00, value), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize] = value, 0xFFFF=>self.interupt_enable_register = value } } } -impl<'a, D:AudioDevice, G:GfxDevice> GbMmu<'a, D, G>{ - pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu, gfx_device:G)->Self{ +impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ + pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu, gfx_device:G, joypad_proider:J)->Self{ GbMmu{ - io_bus:IoBus::new(apu, gfx_device), + io_bus:IoBus::new(apu, gfx_device, joypad_proider), mbc:mbc, hram:[0;HRAM_SIZE], interupt_enable_register:0, @@ -149,9 +150,9 @@ impl<'a, D:AudioDevice, G:GfxDevice> GbMmu<'a, D, G>{ } } - pub fn new(mbc:&'a mut Box, apu:GbApu, gfx_device: G)->Self{ + pub fn new(mbc:&'a mut Box, apu:GbApu, gfx_device: G, joypad_proider:J)->Self{ let mut mmu = GbMmu{ - io_bus:IoBus::new(apu, gfx_device), + io_bus:IoBus::new(apu, gfx_device, joypad_proider), mbc:mbc, hram:[0;HRAM_SIZE], interupt_enable_register:0, @@ -173,11 +174,16 @@ impl<'a, D:AudioDevice, G:GfxDevice> GbMmu<'a, D, G>{ return self.io_bus.interrupt_handler.handle_interrupts(master_interrupt_enable, self.io_bus.ppu.stat_register); } + pub fn poll_joypad_state(&mut self){ + self.io_bus.joypad_handler.poll_joypad_state(); + } + fn handle_dma_trasnfer(&mut self, cycles: u8) { if self.io_bus.dma.enable.is_some(){ let cycles_to_run = std::cmp::min(self.io_bus.dma.dma_cycle_counter + cycles as u16, DMA_SIZE); for i in self.io_bus.dma.dma_cycle_counter..cycles_to_run as u16{ - self.write_unprotected(DMA_DEST + i, self.read_unprotected(self.io_bus.dma.soure_address + i)); + let source_value = self.read_unprotected(self.io_bus.dma.soure_address + i); + self.write_unprotected(DMA_DEST + i, source_value); } self.io_bus.dma.dma_cycle_counter += cycles as u16; diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index 82c777a7..f4b323ea 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -2,7 +2,7 @@ use crate::{ apu::{*,audio_device::AudioDevice, gb_apu::GbApu}, ppu::{gb_ppu::GbPpu, ppu_register_updater::*, gfx_device::GfxDevice}, timer::{timer_register_updater::*, gb_timer::GbTimer}, - utils::memory_registers::* + keypad::{joypad_provider::JoypadProvider, joypad_handler::JoypadHandler} }; use super::{ interrupts_handler::*, access_bus::AccessBus, memory::*, @@ -13,16 +13,16 @@ pub const IO_PORTS_SIZE:usize = 0x80; const WAVE_RAM_START_INDEX:u16 = 0x30; const WAVE_RAM_END_INDEX:u16 = 0x3F; -pub struct IoBus{ +pub struct IoBus{ pub ram: Ram, pub apu: GbApu, pub timer: GbTimer, pub ppu:GbPpu, pub dma:OamDmaTransfer, pub interrupt_handler:InterruptsHandler, + pub joypad_handler: JoypadHandler, pub finished_boot:bool, - ports:[u8;IO_PORTS_SIZE], apu_cycles_counter:u32, ppu_cycles:u32, timer_cycles:u32, @@ -32,22 +32,7 @@ pub struct IoBus{ ppu_event:Option, } -io_port_index!(LCDC_REGISTER_INDEX, LCDC_REGISTER_ADDRESS); -io_port_index!(STAT_REGISTER_INDEX, STAT_REGISTER_ADDRESS); -io_port_index!(SCY_REGISTER_INDEX, SCY_REGISTER_ADDRESS); -io_port_index!(SCX_REGISTER_INDEX, SCX_REGISTER_ADDRESS); -io_port_index!(LY_REGISTER_INDEX, LY_REGISTER_ADDRESS); -io_port_index!(LYC_REGISTER_INDEX, LYC_REGISTER_ADDRESS); -io_port_index!(DMA_REGISTER_INDEX, DMA_REGISTER_ADDRESS); -io_port_index!(WY_REGISTER_INDEX, WY_REGISTER_ADDRESS); -io_port_index!(WX_REGISTER_INDEX, WX_REGISTER_ADDRESS); -io_port_index!(BOOT_REGISTER_INDEX, BOOT_REGISTER_ADDRESS); -io_port_index!(BGP_REGISTER_INDEX, BGP_REGISTER_ADDRESS); -io_port_index!(OBP0_REGISTER_INDEX, OBP0_REGISTER_ADDRESS); -io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); -io_port_index!(IF_REGISTER_INDEX, IF_REGISTER_ADDRESS); - -impl Memory for IoBus{ +impl Memory for IoBus{ fn read(&mut self, address:u16)->u8 { match address{ @@ -57,50 +42,61 @@ impl Memory for IoBus{ _=>{} } - let mut value = self.ports[address as usize]; return match address { //Timer - TAC_REGISTER_INDEX=> value & 0b111, + TAC_REGISTER_INDEX=> self.timer.tac_tegister, DIV_REGISTER_INDEX=> get_div(&self.timer), TIMA_REGISTER_INDEX=> self.timer.tima_register, //Interrupts handler IF_REGISTER_INDEX => self.interrupt_handler.interrupt_flag, //APU - NR10_REGISTER_INDEX=>value | 0b1000_0000, - NR11_REGISTER_INDEX=> value | 0b0011_1111, + NR10_REGISTER_INDEX=> self.apu.sweep_tone_channel.sample_producer.sweep.as_mut().unwrap().register, + NR11_REGISTER_INDEX=> (self.apu.sweep_tone_channel.sample_producer.wave_duty << 6) | 0b0011_1111, + NR12_REGISTER_INDEX => self.apu.sweep_tone_channel.sample_producer.envelop.register, NR13_REGISTER_INDEX=> 0xFF, - NR14_REGISTER_INDEX=> value | 0b1011_1111, + NR14_REGISTER_INDEX=> ((self.apu.sweep_tone_channel.length_enable as u8) << 6 ) | 0b1011_1111, 0x15 => 0xFF, //Not used - NR21_REGISTER_INDEX=> value | 0b0011_1111, + NR21_REGISTER_INDEX=> (self.apu.tone_channel.sample_producer.wave_duty << 6) | 0b0011_1111, + NR22_REGISTER_INDEX=> self.apu.tone_channel.sample_producer.envelop.register, NR23_REGISTER_INDEX=> 0xFF, - NR24_REGISTER_INDEX=> value | 0b1011_1111, - NR30_REGISTER_INDEX=> value | 0b0111_1111, - NR31_REGISTER_INDEX=> value | 0xFF, - NR32_REGISTER_INDEX=> value | 0b1001_1111, - NR33_REGISTER_INDEX=> value | 0xFF, - NR34_REGISTER_INDEX=> value | 0b1011_1111, + NR24_REGISTER_INDEX=> ((self.apu.tone_channel.length_enable as u8) << 6 ) | 0b1011_1111, + NR30_REGISTER_INDEX=> ((self.apu.wave_channel.enabled as u8) << 7) | 0b0111_1111, + NR31_REGISTER_INDEX=> 0xFF, + NR32_REGISTER_INDEX=> (self.apu.wave_channel.sample_producer.volume << 5) | 0b1001_1111, + NR33_REGISTER_INDEX=> 0xFF, + NR34_REGISTER_INDEX=> ((self.apu.wave_channel.length_enable as u8) << 6 ) | 0b1011_1111, 0x1F => 0xFF, //Not used NR41_REGISTER_INDEX=> 0xFF, - NR44_REGISTER_INDEX=> value | 0b1011_1111, - NR52_REGISTER_INDEX=> { - get_nr52(&self.apu, &mut value); - value - } + NR42_REGISTER_INDEX=> self.apu.noise_channel.sample_producer.envelop.register, + NR43_REGISTER_INDEX=> self.apu.noise_channel.sample_producer.nr43_register, + NR44_REGISTER_INDEX=> ((self.apu.wave_channel.length_enable as u8) << 6 ) | 0b1011_1111, + NR50_REGISTER_INDEX=> self.apu.nr50_register, + NR51_REGISTER_INDEX=> self.apu.nr51_register, + NR52_REGISTER_INDEX=> get_nr52(&self.apu), 0x27..=0x2F => 0xFF, //Not used WAVE_RAM_START_INDEX..=WAVE_RAM_END_INDEX => get_wave_ram(&self.apu.wave_channel, address), //PPU + LCDC_REGISTER_INDEX=>self.ppu.lcd_control, STAT_REGISTER_INDEX=> get_stat(&self.ppu), - LY_REGISTER_INDEX=> get_ly(&self.ppu), + SCY_REGISTER_INDEX=> self.ppu.bg_pos.y, + SCX_REGISTER_INDEX=> self.ppu.bg_pos.x, + LY_REGISTER_INDEX=> self.ppu.ly_register, + LYC_REGISTER_INDEX=> self.ppu.lyc_register, + DMA_REGISTER_INDEX=> (self.dma.soure_address >> 8) as u8, + BGP_REGISTER_INDEX=> self.ppu.bg_palette_register, + OBP0_REGISTER_INDEX=> self.ppu.obj_pallete_0_register, + OBP1_REGISTER_INDEX=> self.ppu.obj_pallete_1_register, + WY_REGISTER_INDEX => self.ppu.window_pos.y, + WX_REGISTER_INDEX=> get_wx_register(&self.ppu), + //BOOT + BOOT_REGISTER_INDEX=> self.finished_boot as u8, //Joypad - JOYP_REGISTER_INDEX => { - let joypad_value = self.ports[JOYP_REGISTER_INDEX as usize]; - (joypad_value & 0xF) | (value & 0xF0) - } - _=>value + JOYP_REGISTER_INDEX => self.joypad_handler.register, + _=>0xFF }; } - fn write(&mut self, address:u16, mut value:u8) { + fn write(&mut self, address:u16, value:u8) { match address{ DIV_REGISTER_INDEX | TIMA_REGISTER_INDEX | TMA_REGISTER_INDEX | TAC_REGISTER_INDEX => self.cycle_timer(), NR10_REGISTER_INDEX..=WAVE_RAM_END_INDEX => self.cycle_apu(), @@ -109,49 +105,41 @@ impl Memory for IoBus{ } match address{ //timer - DIV_REGISTER_INDEX=> { - reset_div(&mut self.timer); - value = 0; - } + DIV_REGISTER_INDEX=> reset_div(&mut self.timer), TIMA_REGISTER_INDEX=> set_tima(&mut self.timer, value), TMA_REGISTER_INDEX=> set_tma(&mut self.timer, value), - TAC_REGISTER_INDEX=> { - set_tac(&mut self.timer, value); - value &= 0b111; - } + TAC_REGISTER_INDEX=> set_tac(&mut self.timer, value), //Interrut handler IF_REGISTER_INDEX => self.interrupt_handler.interrupt_flag = value, //APU NR10_REGISTER_INDEX=> set_nr10(&mut self.apu.sweep_tone_channel, value), - NR11_REGISTER_INDEX=> set_nr11(&mut self.apu.sweep_tone_channel, value), - NR12_REGISTER_INDEX=> set_nr12(&mut self.apu.sweep_tone_channel, value), - NR13_REGISTER_INDEX=> set_nr13(&mut self.apu.sweep_tone_channel, value), + NR11_REGISTER_INDEX=> set_nrx1(&mut self.apu.sweep_tone_channel, value), + NR12_REGISTER_INDEX=> set_nrx2(&mut self.apu.sweep_tone_channel, value), + NR13_REGISTER_INDEX=> set_nrx3(&mut self.apu.sweep_tone_channel, value), NR14_REGISTER_INDEX=> set_nr14(&mut self.apu.sweep_tone_channel, &self.apu.frame_sequencer, value), - NR21_REGISTER_INDEX=> set_nr11(&mut self.apu.tone_channel, value), - NR22_REGISTER_INDEX=> set_nr12(&mut self.apu.tone_channel, value), - NR23_REGISTER_INDEX=> set_nr13(&mut self.apu.tone_channel, value), + NR21_REGISTER_INDEX=> set_nrx1(&mut self.apu.tone_channel, value), + NR22_REGISTER_INDEX=> set_nrx2(&mut self.apu.tone_channel, value), + NR23_REGISTER_INDEX=> set_nrx3(&mut self.apu.tone_channel, value), NR24_REGISTER_INDEX=> set_nr24(&mut self.apu.tone_channel, &self.apu.frame_sequencer, value), NR30_REGISTER_INDEX=> set_nr30(&mut self.apu.wave_channel, value), NR31_REGISTER_INDEX=> set_nr31(&mut self.apu.wave_channel, value), NR32_REGISTER_INDEX=> set_nr32(&mut self.apu.wave_channel, value), NR33_REGISTER_INDEX=> set_nr33(&mut self.apu.wave_channel, value), - NR34_REGISTER_INDEX=> set_nr34(&mut self.apu.wave_channel, &self.apu.frame_sequencer, self.ports[NR30_REGISTER_INDEX as usize],value), + NR34_REGISTER_INDEX=> set_nr34(&mut self.apu.wave_channel, &self.apu.frame_sequencer, value), NR41_REGISTER_INDEX=> set_nr41(&mut self.apu.noise_channel, value), NR42_REGISTER_INDEX=> set_nr42(&mut self.apu.noise_channel, value), NR43_REGISTER_INDEX=> set_nr43(&mut self.apu.noise_channel, value), NR44_REGISTER_INDEX=> set_nr44(&mut self.apu.noise_channel, &self.apu.frame_sequencer, value), NR50_REGISTER_INDEX=> set_nr50(&mut self.apu, value), NR51_REGISTER_INDEX=> set_nr51(&mut self.apu, value), - NR52_REGISTER_INDEX=> set_nr52(&mut self.apu, &mut self.ports,value), + NR52_REGISTER_INDEX=> set_nr52(&mut self.apu, value), WAVE_RAM_START_INDEX..=WAVE_RAM_END_INDEX => set_wave_ram(&mut self.apu.wave_channel, address, value), //PPU LCDC_REGISTER_INDEX=> handle_lcdcontrol_register(value, &mut self.ppu), - STAT_REGISTER_INDEX=> { - update_stat_register(value, &mut self.ppu); - value = (value >> 2) << 2; - }, + STAT_REGISTER_INDEX=> update_stat_register(value, &mut self.ppu), SCY_REGISTER_INDEX=> set_scy(&mut self.ppu, value), SCX_REGISTER_INDEX=> set_scx(&mut self.ppu, value), + // LY is readonly LYC_REGISTER_INDEX=> set_lyc(&mut self.ppu, value), DMA_REGISTER_INDEX=>{ let address = (value as u16) << 8; @@ -162,43 +150,28 @@ impl Memory for IoBus{ 0xA0..=0xFF=>Some(AccessBus::External) } } - BGP_REGISTER_INDEX=> handle_bg_pallet_register(value,&mut self.ppu.bg_color_mapping), - OBP0_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping0), - OBP1_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping1), + BGP_REGISTER_INDEX=> handle_bg_pallet_register(value,&mut self.ppu.bg_color_mapping, &mut self.ppu.bg_palette_register), + OBP0_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping0, &mut self.ppu.obj_pallete_0_register), + OBP1_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping1, &mut self.ppu.obj_pallete_1_register), WY_REGISTER_INDEX=> handle_wy_register(value, &mut self.ppu), WX_REGISTER_INDEX=> handle_wx_register(value, &mut self.ppu), BOOT_REGISTER_INDEX=> self.finished_boot = value != 0, - JOYP_REGISTER_INDEX => { - let joypad_value = self.ports[JOYP_REGISTER_INDEX as usize]; - value = (joypad_value & 0xF) | (value & 0xF0); - } + JOYP_REGISTER_INDEX => self.joypad_handler.set_register(value), // TODO: handle gbc registers (expecailly ram and vram) _=>{} } - - self.ports[address as usize] = value; - } -} - -impl UnprotectedMemory for IoBus{ - fn read_unprotected(&self, address:u16)->u8 { - self.ports[address as usize] - } - - fn write_unprotected(&mut self, address:u16, value:u8) { - self.ports[address as usize] = value; } } -impl IoBus{ - pub fn new(apu:GbApu, gfx_device:GFX)->Self{ +impl IoBus{ + pub fn new(apu:GbApu, gfx_device:GFX, joypad_provider:JP)->Self{ Self{ apu, - ports:[0;IO_PORTS_SIZE], timer:GbTimer::default(), ppu:GbPpu::new(gfx_device), dma:OamDmaTransfer::default(), interrupt_handler: InterruptsHandler::default(), + joypad_handler: JoypadHandler::new(joypad_provider), finished_boot:false, ram:Ram::default(), apu_cycles_counter:0, diff --git a/lib_gb/src/mmu/io_ports.rs b/lib_gb/src/mmu/io_ports.rs index 58dc253f..5b384f4f 100644 --- a/lib_gb/src/mmu/io_ports.rs +++ b/lib_gb/src/mmu/io_ports.rs @@ -4,11 +4,6 @@ pub const IO_PORTS_SIZE:usize = 0x80; pub const IO_PORTS_MEMORY_OFFSET:u16 = 0xFF00; -macro_rules! io_port_index{ - ($name:ident, $reg_address:expr) => { - const $name:u16 = $reg_address - IO_PORTS_MEMORY_OFFSET; - }; -} macro_rules! pub_io_port_index{ ($name:ident, $reg_address:expr) => { pub const $name:u16 = $reg_address - IO_PORTS_MEMORY_OFFSET; @@ -20,6 +15,21 @@ pub_io_port_index!(TAC_REGISTER_INDEX, TAC_REGISTER_ADDRESS); pub_io_port_index!(TIMA_REGISTER_INDEX, TIMA_REGISTER_ADDRESS); pub_io_port_index!(TMA_REGISTER_INDEX, TMA_REGISTER_ADDRESS); +pub_io_port_index!(LCDC_REGISTER_INDEX, LCDC_REGISTER_ADDRESS); +pub_io_port_index!(STAT_REGISTER_INDEX, STAT_REGISTER_ADDRESS); +pub_io_port_index!(SCY_REGISTER_INDEX, SCY_REGISTER_ADDRESS); +pub_io_port_index!(SCX_REGISTER_INDEX, SCX_REGISTER_ADDRESS); +pub_io_port_index!(LY_REGISTER_INDEX, LY_REGISTER_ADDRESS); +pub_io_port_index!(LYC_REGISTER_INDEX, LYC_REGISTER_ADDRESS); +pub_io_port_index!(DMA_REGISTER_INDEX, DMA_REGISTER_ADDRESS); +pub_io_port_index!(WY_REGISTER_INDEX, WY_REGISTER_ADDRESS); +pub_io_port_index!(WX_REGISTER_INDEX, WX_REGISTER_ADDRESS); +pub_io_port_index!(BOOT_REGISTER_INDEX, BOOT_REGISTER_ADDRESS); +pub_io_port_index!(BGP_REGISTER_INDEX, BGP_REGISTER_ADDRESS); +pub_io_port_index!(OBP0_REGISTER_INDEX, OBP0_REGISTER_ADDRESS); +pub_io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); +pub_io_port_index!(IF_REGISTER_INDEX, IF_REGISTER_ADDRESS); + pub_io_port_index!(JOYP_REGISTER_INDEX, JOYP_REGISTER_ADDRESS); pub_io_port_index!(NR10_REGISTER_INDEX, NR10_REGISTER_ADDRESS); pub_io_port_index!(NR11_REGISTER_INDEX, NR11_REGISTER_ADDRESS); diff --git a/lib_gb/src/mmu/memory.rs b/lib_gb/src/mmu/memory.rs index 6cfec866..d86f05b6 100644 --- a/lib_gb/src/mmu/memory.rs +++ b/lib_gb/src/mmu/memory.rs @@ -2,9 +2,4 @@ pub trait Memory{ fn read(&mut self, address:u16)->u8; fn write(&mut self, address:u16, value:u8); -} - -pub trait UnprotectedMemory{ - fn read_unprotected(&self, address:u16)->u8; - fn write_unprotected(&mut self, address:u16, value:u8); } \ No newline at end of file diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index f848af05..d43dd08c 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -2,7 +2,6 @@ pub mod memory; pub mod gb_mmu; pub mod ram; pub mod vram; -#[macro_use] pub mod io_ports; pub mod carts; pub mod access_bus; diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 410f2117..0d07007d 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -25,8 +25,11 @@ pub struct GbPpu{ pub ly_register:u8, pub window_pos:Vec2, pub bg_pos:Vec2, + pub bg_palette_register:u8, pub bg_color_mapping: [Color; 4], + pub obj_pallete_0_register:u8, pub obj_color_mapping0: [Option;4], + pub obj_pallete_1_register:u8, pub obj_color_mapping1: [Option;4], //interrupts @@ -62,8 +65,11 @@ impl GbPpu{ window_pos: Vec2::{x:0,y:0}, screen_buffers:[[0;SCREEN_HEIGHT * SCREEN_WIDTH];BUFFERS_NUMBER], current_screen_buffer_index:0, + bg_palette_register:0, bg_color_mapping:[WHITE, LIGHT_GRAY, DARK_GRAY, BLACK], + obj_pallete_0_register:0, obj_color_mapping0: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], + obj_pallete_1_register:0, obj_color_mapping1: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], ly_register:0, state: PpuState::Hblank, diff --git a/lib_gb/src/ppu/ppu_register_updater.rs b/lib_gb/src/ppu/ppu_register_updater.rs index 2157ac41..3606a03d 100644 --- a/lib_gb/src/ppu/ppu_register_updater.rs +++ b/lib_gb/src/ppu/ppu_register_updater.rs @@ -20,7 +20,8 @@ pub fn update_stat_register(register:u8, ppu: &mut GbPpu){ ppu.oam_search_interrupt_request = register & BIT_5_MASK != 0; ppu.coincidence_interrupt_request = register & BIT_6_MASK != 0; - ppu.stat_register = register & 0b111_1000; + ppu.stat_register &= 0b1000_0111; + ppu.stat_register |= register & 0b111_1000; } pub fn set_scx(ppu: &mut GbPpu, value:u8){ @@ -31,18 +32,20 @@ pub fn set_scy(ppu:&mut GbPpu, value:u8){ ppu.bg_pos.y = value; } -pub fn handle_bg_pallet_register(register:u8, pallet:&mut [Color;4] ){ +pub fn handle_bg_pallet_register(register:u8, pallet:&mut [Color;4], palette_register:&mut u8){ pallet[0] = get_matching_color(register&0b00000011); pallet[1] = get_matching_color((register&0b00001100)>>2); pallet[2] = get_matching_color((register&0b00110000)>>4); pallet[3] = get_matching_color((register&0b11000000)>>6); + *palette_register = register; } -pub fn handle_obp_pallet_register(register:u8, pallet:&mut [Option;4] ){ +pub fn handle_obp_pallet_register(register:u8, pallet:&mut [Option;4], palette_register:&mut u8){ pallet[0] = None; pallet[1] = Some(get_matching_color((register&0b00001100)>>2)); pallet[2] = Some(get_matching_color((register&0b00110000)>>4)); pallet[3] = Some(get_matching_color((register&0b11000000)>>6)); + *palette_register = register; } fn get_matching_color(number:u8)->Color{ @@ -68,8 +71,9 @@ pub fn handle_wx_register(register:u8, ppu:&mut GbPpu){ } } -pub fn get_ly(ppu:&GbPpu)->u8{ - ppu.ly_register +pub fn get_wx_register(ppu:&GbPpu)->u8{ + // This function is not accurate as it wont return wx between 0-6 (will return them as 7) + return ppu.window_pos.x + WX_OFFSET; } pub fn get_stat(ppu:&GbPpu)->u8{ diff --git a/lib_gb/src/utils/bit_masks.rs b/lib_gb/src/utils/bit_masks.rs index 499bc630..9afab06f 100644 --- a/lib_gb/src/utils/bit_masks.rs +++ b/lib_gb/src/utils/bit_masks.rs @@ -9,7 +9,7 @@ pub const BIT_7_MASK:u8 = 1 << 7; pub const BIT_9_MASK:u16 = 1 << 9; -pub fn set_bit_u8(value:&mut u8, bit_number:u8, set:bool){ +pub fn flip_bit_u8(value:&mut u8, bit_number:u8, set:bool){ let mask = 1 << bit_number; if set{ *value |= mask; @@ -20,7 +20,7 @@ pub fn set_bit_u8(value:&mut u8, bit_number:u8, set:bool){ } } -pub fn set_bit_u16(value:&mut u16, bit_number:u8, set:bool){ +pub fn flip_bit_u16(value:&mut u16, bit_number:u8, set:bool){ let mask = 1 << bit_number; if set{ *value |= mask; From 37c8df58dc55635d62ba5564e143decf40d52211 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 4 Feb 2022 13:16:02 +0200 Subject: [PATCH 16/26] Self cr fixes * Add comment to the ppu_event in the io_bus * Renamed a bunch of variables and members * In fixed size queue, made some ptrs const and add a comment * Fix the joypad initialization --- lib_gb/src/apu/apu_registers_updater.rs | 4 ++-- lib_gb/src/apu/freq_sweep.rs | 4 ++-- lib_gb/src/apu/square_sample_producer.rs | 2 +- lib_gb/src/apu/volume_envelop.rs | 6 ++--- lib_gb/src/keypad/joypad.rs | 2 +- lib_gb/src/mmu/io_bus.rs | 11 ++++++---- lib_gb/src/timer/gb_timer.rs | 4 ++-- lib_gb/src/utils/fixed_size_queue.rs | 28 ++++++++++++------------ 8 files changed, 32 insertions(+), 29 deletions(-) diff --git a/lib_gb/src/apu/apu_registers_updater.rs b/lib_gb/src/apu/apu_registers_updater.rs index ee89e66d..feb39c66 100644 --- a/lib_gb/src/apu/apu_registers_updater.rs +++ b/lib_gb/src/apu/apu_registers_updater.rs @@ -122,7 +122,7 @@ pub fn set_nr10(channel:&mut Channel, value:u8){ sweep.sweep_decrease = (value & 0b1000) != 0; sweep.sweep_shift = value & 0b111; sweep.sweep_period = (value & 0b111_0000) >> 4; - sweep.register = value | 0b1000_0000; + sweep.nr10_register = value | 0b1000_0000; } pub fn set_nrx1(channel:&mut Channel, value:u8){ @@ -248,7 +248,7 @@ fn update_volume_envelope(register:u8, envelop:&mut VolumeEnvlope){ envelop.volume = (register & 0b1111_0000) >> 4; envelop.number_of_envelope_sweep = register & 0b111; envelop.increase_envelope = (register & BIT_3_MASK) != 0; - envelop.register = register; + envelop.nrx2_register = register; } fn is_dac_enabled(volume:u8, envelop_increase:bool)->bool{ diff --git a/lib_gb/src/apu/freq_sweep.rs b/lib_gb/src/apu/freq_sweep.rs index 78137e1c..eb89c565 100644 --- a/lib_gb/src/apu/freq_sweep.rs +++ b/lib_gb/src/apu/freq_sweep.rs @@ -6,7 +6,7 @@ pub struct FreqSweep{ pub sweep_shift:u8, pub shadow_frequency:u16, - pub register:u8, // The raw value of the NR10 register + pub nr10_register:u8, // The raw value of the NR10 register } impl FreqSweep{ @@ -18,7 +18,7 @@ impl FreqSweep{ self.enabled = false; self.sweep_period = 0; - self.register = 0; + self.nr10_register = 0; } pub fn reload_sweep_time(&mut self){ diff --git a/lib_gb/src/apu/square_sample_producer.rs b/lib_gb/src/apu/square_sample_producer.rs index 22bdc5cc..bd87ba5f 100644 --- a/lib_gb/src/apu/square_sample_producer.rs +++ b/lib_gb/src/apu/square_sample_producer.rs @@ -21,7 +21,7 @@ impl SquareSampleProducer{ sweep_counter:0, shadow_frequency:0, sweep_period:0, - register: 0, + nr10_register: 0, }), envelop:VolumeEnvlope::default(), duty_sample_pointer:0 diff --git a/lib_gb/src/apu/volume_envelop.rs b/lib_gb/src/apu/volume_envelop.rs index 50ce2aff..f723376b 100644 --- a/lib_gb/src/apu/volume_envelop.rs +++ b/lib_gb/src/apu/volume_envelop.rs @@ -6,7 +6,7 @@ pub struct VolumeEnvlope{ pub number_of_envelope_sweep:u8, pub envelop_duration_counter:u8, - pub register:u8, // The original register raw value + pub nrx2_register:u8, // The original register raw value } impl VolumeEnvlope{ @@ -14,7 +14,7 @@ impl VolumeEnvlope{ self.increase_envelope = false; self.number_of_envelope_sweep = 0; self.envelop_duration_counter = 0; - self.register = 0; + self.nrx2_register = 0; } pub fn tick(&mut self){ @@ -46,7 +46,7 @@ impl Default for VolumeEnvlope{ increase_envelope:false, number_of_envelope_sweep:0, envelop_duration_counter:0, - register:0 + nrx2_register:0 } } } \ No newline at end of file diff --git a/lib_gb/src/keypad/joypad.rs b/lib_gb/src/keypad/joypad.rs index a7376c31..61b80c6b 100644 --- a/lib_gb/src/keypad/joypad.rs +++ b/lib_gb/src/keypad/joypad.rs @@ -8,7 +8,7 @@ impl Default for Joypad{ fn default()->Self{ // Since the button pressed state is 0 initializing to unpressed state (1 == true) Joypad{ - buttons:[true;NUM_OF_KEYS], + buttons:[false;NUM_OF_KEYS], } } } \ No newline at end of file diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index f4b323ea..0c1ec465 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -29,6 +29,9 @@ pub struct IoBus{ timer_event_cycles:u32, apu_event_cycles:u32, + + // Since the PPU is the only that can be completely off and not operate at all Im using an option + // where None indicates the PPU is off ppu_event:Option, } @@ -50,14 +53,14 @@ impl Memory for IoBus self.interrupt_handler.interrupt_flag, //APU - NR10_REGISTER_INDEX=> self.apu.sweep_tone_channel.sample_producer.sweep.as_mut().unwrap().register, + NR10_REGISTER_INDEX=> self.apu.sweep_tone_channel.sample_producer.sweep.as_mut().unwrap().nr10_register, NR11_REGISTER_INDEX=> (self.apu.sweep_tone_channel.sample_producer.wave_duty << 6) | 0b0011_1111, - NR12_REGISTER_INDEX => self.apu.sweep_tone_channel.sample_producer.envelop.register, + NR12_REGISTER_INDEX => self.apu.sweep_tone_channel.sample_producer.envelop.nrx2_register, NR13_REGISTER_INDEX=> 0xFF, NR14_REGISTER_INDEX=> ((self.apu.sweep_tone_channel.length_enable as u8) << 6 ) | 0b1011_1111, 0x15 => 0xFF, //Not used NR21_REGISTER_INDEX=> (self.apu.tone_channel.sample_producer.wave_duty << 6) | 0b0011_1111, - NR22_REGISTER_INDEX=> self.apu.tone_channel.sample_producer.envelop.register, + NR22_REGISTER_INDEX=> self.apu.tone_channel.sample_producer.envelop.nrx2_register, NR23_REGISTER_INDEX=> 0xFF, NR24_REGISTER_INDEX=> ((self.apu.tone_channel.length_enable as u8) << 6 ) | 0b1011_1111, NR30_REGISTER_INDEX=> ((self.apu.wave_channel.enabled as u8) << 7) | 0b0111_1111, @@ -67,7 +70,7 @@ impl Memory for IoBus ((self.apu.wave_channel.length_enable as u8) << 6 ) | 0b1011_1111, 0x1F => 0xFF, //Not used NR41_REGISTER_INDEX=> 0xFF, - NR42_REGISTER_INDEX=> self.apu.noise_channel.sample_producer.envelop.register, + NR42_REGISTER_INDEX=> self.apu.noise_channel.sample_producer.envelop.nrx2_register, NR43_REGISTER_INDEX=> self.apu.noise_channel.sample_producer.nr43_register, NR44_REGISTER_INDEX=> ((self.apu.wave_channel.length_enable as u8) << 6 ) | 0b1011_1111, NR50_REGISTER_INDEX=> self.apu.nr50_register, diff --git a/lib_gb/src/timer/gb_timer.rs b/lib_gb/src/timer/gb_timer.rs index ccfef30a..9924d40c 100644 --- a/lib_gb/src/timer/gb_timer.rs +++ b/lib_gb/src/timer/gb_timer.rs @@ -62,7 +62,7 @@ impl GbTimer{ self.last_and_result = current_and_result; } - let v = match timer_interval{ + let t_cycles_to_next_timer_event = match timer_interval{ 0b00=>0x100 - (self.system_counter & 0xFF), 0b01=>0b1000 - (self.system_counter & 0b111), 0b10=>0b10_0000 - (self.system_counter & 0b1_1111), @@ -70,7 +70,7 @@ impl GbTimer{ _=>std::panic!("error ") }; - return (v>>2) as u32 + 1; + return (t_cycles_to_next_timer_event >> 2) as u32 + 1; } fn get_timer_controller_data(&self)->(u8, bool){ diff --git a/lib_gb/src/utils/fixed_size_queue.rs b/lib_gb/src/utils/fixed_size_queue.rs index 17b2683f..95be450b 100644 --- a/lib_gb/src/utils/fixed_size_queue.rs +++ b/lib_gb/src/utils/fixed_size_queue.rs @@ -2,9 +2,9 @@ pub struct FixedSizeQueue{ // According to the docs Vec should not be moved in memory if it not modified // Im modifing it but not increasing its allocated size once its allocated so I hope this will work for me // and I wont get weird memory issues - _data: Vec, - end_data_pointer: *mut T, - start_data_pointer: *mut T, + _data: Vec, // This field is not use directly only through pointers aquired in the new() function + end_data_pointer: *const T, + start_data_pointer: *const T, data_pointer: *mut T, base_data_pointer: *mut T, length: usize, @@ -14,7 +14,7 @@ impl FixedSizeQueue{ pub fn new()->Self{ let data = Vec::with_capacity(SIZE); let mut s = Self{ - _data:data, + _data: data, length:0, base_data_pointer: std::ptr::null_mut(), data_pointer: std::ptr::null_mut(), @@ -24,8 +24,8 @@ impl FixedSizeQueue{ s.base_data_pointer = s._data.as_mut_ptr(); s.data_pointer = s._data.as_mut_ptr(); - s.start_data_pointer = s._data.as_mut_ptr(); - unsafe{s.end_data_pointer = s._data.as_mut_ptr().add(SIZE)}; + s.start_data_pointer = s._data.as_ptr(); + unsafe{s.end_data_pointer = s._data.as_ptr().add(SIZE)}; return s; } @@ -33,8 +33,8 @@ impl FixedSizeQueue{ pub fn push(&mut self, t:T){ if self.length < SIZE{ unsafe{ - if self.data_pointer == self.end_data_pointer{ - self.data_pointer = self.start_data_pointer; + if self.data_pointer == self.end_data_pointer as *mut T{ + self.data_pointer = self.start_data_pointer as *mut T; } *self.data_pointer = t; self.data_pointer = self.data_pointer.add(1); @@ -51,8 +51,8 @@ impl FixedSizeQueue{ unsafe{ let t = *self.base_data_pointer; self.base_data_pointer = self.base_data_pointer.add(1); - if self.base_data_pointer == self.end_data_pointer{ - self.base_data_pointer = self.start_data_pointer; + if self.base_data_pointer == self.end_data_pointer as *mut T{ + self.base_data_pointer = self.start_data_pointer as *mut T; } self.length -= 1; @@ -65,8 +65,8 @@ impl FixedSizeQueue{ pub fn clear(&mut self){ self.length = 0; - self.data_pointer = self.start_data_pointer; - self.base_data_pointer = self.start_data_pointer; + self.data_pointer = self.start_data_pointer as *mut T; + self.base_data_pointer = self.start_data_pointer as *mut T; } pub fn len(&self)->usize{ @@ -80,7 +80,7 @@ impl std::ops::Index for FixedSizeQueue &Self::Output { if index < self.length{ unsafe{ - if self.base_data_pointer.add(index) >= self.end_data_pointer{ + if self.base_data_pointer.add(index) >= self.end_data_pointer as *mut T{ index -= self.end_data_pointer.offset_from(self.base_data_pointer) as usize; } // casting a *mut T to a &T @@ -96,7 +96,7 @@ impl std::ops::IndexMut for FixedSizeQueue &mut Self::Output { if index < self.length{ unsafe{ - if self.base_data_pointer.add(index) >= self.end_data_pointer{ + if self.base_data_pointer.add(index) >= self.end_data_pointer as *mut T{ index -= self.end_data_pointer.offset_from(self.base_data_pointer) as usize; } // casting a *mut T to a &mut T From d0fcae2442aaed20d1cc24b1e731770493c210a0 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 4 Feb 2022 14:43:58 +0200 Subject: [PATCH 17/26] Self cr fixes: * FIx some t_cycles m_cycles confusion * Add comments for confusing cases --- lib_gb/src/ppu/gb_ppu.rs | 16 ++++++++++------ lib_gb/src/timer/gb_timer.rs | 2 ++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 0d07007d..03988fa1 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -115,11 +115,11 @@ impl GbPpu{ return None; } - let fethcer_cycles_to_next_event = self.cycle_fetcher(m_cycles, if_register) as u32; + let fethcer_m_cycles_to_next_event = self.cycle_fetcher(m_cycles, if_register) as u32; - let stat_cycles_to_next_event = self.update_stat_register(if_register); + let stat_m_cycles_to_next_event = self.update_stat_register(if_register); - let cycles = std::cmp::min(fethcer_cycles_to_next_event, stat_cycles_to_next_event); + let cycles = std::cmp::min(fethcer_m_cycles_to_next_event, stat_m_cycles_to_next_event); for i in 0..self.push_lcd_buffer.len(){ self.screen_buffers[self.current_screen_buffer_index][self.screen_buffer_index] = u32::from(self.push_lcd_buffer[i]); @@ -163,15 +163,18 @@ impl GbPpu{ } self.trigger_stat_interrupt = false; - return if self.lyc_register < self.ly_register{ - (((self.ly_register - self.lyc_register) as u32 * HBLANK_T_CYCLES_LENGTH as u32) - self.t_cycles_passed as u32) >> 2 + let t_cycles_to_next_stat_change = if self.lyc_register < self.ly_register{ + ((self.ly_register - self.lyc_register) as u32 * HBLANK_T_CYCLES_LENGTH as u32) - self.t_cycles_passed as u32 } else if self.lyc_register == self.ly_register{ (HBLANK_T_CYCLES_LENGTH as u32 * 154 ) - self.t_cycles_passed as u32 } else{ - (((self.lyc_register - self.ly_register) as u32 * HBLANK_T_CYCLES_LENGTH as u32) - self.t_cycles_passed as u32) >> 2 + ((self.lyc_register - self.ly_register) as u32 * HBLANK_T_CYCLES_LENGTH as u32) - self.t_cycles_passed as u32 }; + + // Divide by 4 to transform the t_cycles to m_cycles + return t_cycles_to_next_stat_change >> 2; } fn cycle_fetcher(&mut self, m_cycles:u32, if_register:&mut u8)->u16{ @@ -294,6 +297,7 @@ impl GbPpu{ PpuState::PixelTransfer => self.t_cycles_passed }; + // Subtract by 4 in order to cast the t_cycles to m_cycles return (t_cycles - self.t_cycles_passed) >> 2; } diff --git a/lib_gb/src/timer/gb_timer.rs b/lib_gb/src/timer/gb_timer.rs index 9924d40c..2d7ad7b7 100644 --- a/lib_gb/src/timer/gb_timer.rs +++ b/lib_gb/src/timer/gb_timer.rs @@ -70,6 +70,8 @@ impl GbTimer{ _=>std::panic!("error ") }; + // Divide by 4 to cast m_cycles to t_cycles + // Adding +1 in order be in the next event and not cycle before return (t_cycles_to_next_timer_event >> 2) as u32 + 1; } From f19495fd9b81196c4c30b073f4f9f1bcf1cf03ff Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 4 Feb 2022 14:47:58 +0200 Subject: [PATCH 18/26] Remove redudndant shit from the joypad --- lib_gb/src/keypad/joypad.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib_gb/src/keypad/joypad.rs b/lib_gb/src/keypad/joypad.rs index 61b80c6b..fb447e51 100644 --- a/lib_gb/src/keypad/joypad.rs +++ b/lib_gb/src/keypad/joypad.rs @@ -1,14 +1,13 @@ pub const NUM_OF_KEYS: usize = 8; pub struct Joypad{ - pub buttons:[bool;NUM_OF_KEYS], + pub buttons:[bool;NUM_OF_KEYS] } impl Default for Joypad{ fn default()->Self{ - // Since the button pressed state is 0 initializing to unpressed state (1 == true) Joypad{ - buttons:[false;NUM_OF_KEYS], + buttons:[false;NUM_OF_KEYS] } } } \ No newline at end of file From 19fe17a839f51a783b1a28963fe5faff6e83e186 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 4 Feb 2022 17:24:34 +0200 Subject: [PATCH 19/26] Refactor dma into oam dma contoller --- lib_gb/src/mmu/dma_controller.rs | 59 +++++++++++++++++++++++++ lib_gb/src/mmu/external_memory_bus.rs | 38 ++++++++++++++++ lib_gb/src/mmu/gb_mmu.rs | 62 ++++++++------------------- lib_gb/src/mmu/io_bus.rs | 23 +++------- lib_gb/src/mmu/mod.rs | 4 +- 5 files changed, 122 insertions(+), 64 deletions(-) create mode 100644 lib_gb/src/mmu/dma_controller.rs create mode 100644 lib_gb/src/mmu/external_memory_bus.rs diff --git a/lib_gb/src/mmu/dma_controller.rs b/lib_gb/src/mmu/dma_controller.rs new file mode 100644 index 00000000..019f4b50 --- /dev/null +++ b/lib_gb/src/mmu/dma_controller.rs @@ -0,0 +1,59 @@ +use crate::ppu::{gb_ppu::GbPpu, gfx_device::GfxDevice}; +use super::{oam_dma_transfer::OamDmaTransfer, external_memory_bus::ExternalMemoryBus, access_bus::AccessBus}; + +const DMA_SIZE:u16 = 0xA0; +const VRAM_BASE_ADDRESS:u16 = 0x8000; + +pub struct OamDmaController{ + oam_dma_transfer:OamDmaTransfer +} + +impl OamDmaController{ + pub fn new()->Self{ + Self{ + oam_dma_transfer: OamDmaTransfer::default() + } + } + pub fn cycle(&mut self, m_cycles:u32, external_bus: &mut ExternalMemoryBus, ppu:&mut GbPpu)->Option{ + if let Some(bus) = self.oam_dma_transfer.enable{ + let cycles_to_run = std::cmp::min(self.oam_dma_transfer.dma_cycle_counter + m_cycles as u16, DMA_SIZE); + match bus{ + AccessBus::External=>{ + for i in self.oam_dma_transfer.dma_cycle_counter..cycles_to_run as u16{ + let source_value = external_bus.read(self.oam_dma_transfer.soure_address + i); + ppu.oam[i as usize] = source_value; + } + } + AccessBus::Video=>{ + let base_source_address = self.oam_dma_transfer.soure_address - VRAM_BASE_ADDRESS; + for i in self.oam_dma_transfer.dma_cycle_counter..cycles_to_run as u16{ + let source_value = ppu.vram.read_current_bank(base_source_address + i); + ppu.oam[i as usize] = source_value; + } + } + } + + self.oam_dma_transfer.dma_cycle_counter += m_cycles as u16; + if self.oam_dma_transfer.dma_cycle_counter >= DMA_SIZE{ + self.oam_dma_transfer.dma_cycle_counter = 0; + self.oam_dma_transfer.enable = Option::None; + } + } + + return self.oam_dma_transfer.enable; + } + + pub fn get_dma_register(&self)->u8{ + (self.oam_dma_transfer.soure_address >> 8) as u8 + } + + pub fn set_dma_register(&mut self, value:u8){ + let address = (value as u16) << 8; + self.oam_dma_transfer.soure_address = address; + self.oam_dma_transfer.enable = match value{ + 0..=0x7F=>Some(AccessBus::External), + 0x80..=0x9F=>Some(AccessBus::Video), + 0xA0..=0xFF=>Some(AccessBus::External) + } + } +} \ No newline at end of file diff --git a/lib_gb/src/mmu/external_memory_bus.rs b/lib_gb/src/mmu/external_memory_bus.rs new file mode 100644 index 00000000..06d1df28 --- /dev/null +++ b/lib_gb/src/mmu/external_memory_bus.rs @@ -0,0 +1,38 @@ +use super::{ram::Ram, carts::Mbc}; + +pub struct ExternalMemoryBus<'a>{ + ram: Ram, + mbc: &'a mut Box +} + +impl<'a> ExternalMemoryBus<'a> { + pub fn new(mbc:&'a mut Box)->Self{ + Self{ + mbc, + ram:Ram::default() + } + } + + pub fn read(&mut self, address:u16)->u8 { + return match address{ + 0x0000..=0x3FFF=>self.mbc.read_bank0(address), + 0x4000..=0x7FFF=>self.mbc.read_current_bank(address - 0x4000), + 0xA000..=0xBFFF=>self.mbc.read_external_ram(address - 0xA000), + 0xC000..=0xCFFF=>self.ram.read_bank0(address - 0xC000), + 0xD000..=0xDFFF=>self.ram.read_current_bank(address - 0xD000), + 0xE000..=0xFDFF=>self.ram.read_bank0(address - 0xE000), + _=>std::panic!("Error: attemp to read invalid external memory bus address: {:#X}", address) + } + } + + pub fn write(&mut self, address:u16, value:u8) { + match address{ + 0x0000..=0x7FFF=>self.mbc.write_rom(address, value), + 0xA000..=0xBFFF=>self.mbc.write_external_ram(address - 0xA000, value), + 0xC000..=0xCFFF =>self.ram.write_bank0(address - 0xC000, value), + 0xD000..=0xDFFF=>self.ram.write_current_bank(address-0xD000, value), + 0xE000..=0xFDFF=>self.ram.write_bank0(address - 0xE000, value), + _=>std::panic!("Error: attemp to write invalid external memory bus address: {:#X}", address) + } + } +} \ No newline at end of file diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index 15dad6e3..c9097307 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -1,3 +1,4 @@ +use super::external_memory_bus::ExternalMemoryBus; use super::interrupts_handler::InterruptRequest; use super::{io_bus::IoBus, memory::*}; use super::access_bus::AccessBus; @@ -10,15 +11,14 @@ use std::boxed::Box; pub const BOOT_ROM_SIZE:usize = 0x100; const HRAM_SIZE:usize = 0x7F; -const DMA_SIZE:u16 = 0xA0; -const DMA_DEST:u16 = 0xFE00; const BAD_READ_VALUE:u8 = 0xFF; pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider>{ pub io_bus: IoBus, boot_rom:[u8;BOOT_ROM_SIZE], - mbc: &'a mut Box, + external_memory_bus:ExternalMemoryBus<'a>, + oucupied_access_bus:Option, hram: [u8;HRAM_SIZE], interupt_enable_register:u8 } @@ -27,7 +27,7 @@ pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider>{ //DMA only locks the used bus. there 2 possible used buses: extrnal (wram, rom, sram) and video (vram) impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G, J>{ fn read(&mut self, address:u16)->u8{ - if let Some (bus) = &self.io_bus.dma.enable{ + if let Some (bus) = &self.oucupied_access_bus{ return match address{ 0xFF00..=0xFF7F => self.io_bus.read(address - 0xFF00), 0xFEA0..=0xFEFF | 0xFF80..=0xFFFE | 0xFFFF=>self.read_unprotected(address), @@ -62,7 +62,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> Memory for GbMmu<'a, D, G } fn write(&mut self, address:u16, value:u8){ - if let Some(bus) = &self.io_bus.dma.enable{ + if let Some(bus) = &self.oucupied_access_bus{ match address{ 0xFF00..=0xFF7F => self.io_bus.write(address- 0xFF00, value), 0xFF80..=0xFFFE | 0xFFFF=>self.write_unprotected(address, value), @@ -102,18 +102,14 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ return match address{ 0x0..=0xFF=>{ if self.io_bus.finished_boot{ - return self.mbc.read_bank0(address); + return self.external_memory_bus.read(address); } return self.boot_rom[address as usize]; }, - 0x100..=0x3FFF=>self.mbc.read_bank0(address), - 0x4000..=0x7FFF=>self.mbc.read_current_bank(address-0x4000), + 0x100..=0x7FFF=>self.external_memory_bus.read(address), 0x8000..=0x9FFF=>self.io_bus.ppu.vram.read_current_bank(address-0x8000), - 0xA000..=0xBFFF=>self.mbc.read_external_ram(address-0xA000), - 0xC000..=0xCFFF =>self.io_bus.ram.read_bank0(address - 0xC000), - 0xD000..=0xDFFF=>self.io_bus.ram.read_current_bank(address-0xD000), - 0xE000..=0xFDFF=>self.io_bus.ram.read_bank0(address - 0xE000), + 0xA000..=0xFDFF=>self.external_memory_bus.read(address), 0xFE00..=0xFE9F=>self.io_bus.ppu.oam[(address-0xFE00) as usize], 0xFEA0..=0xFEFF=>0x0, 0xFF00..=0xFF7F=>self.io_bus.read(address - 0xFF00), @@ -124,12 +120,9 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ fn write_unprotected(&mut self, address:u16, value:u8) { match address{ - 0x0..=0x7FFF=>self.mbc.write_rom(address, value), + 0x0..=0x7FFF=>self.external_memory_bus.write(address, value), 0x8000..=0x9FFF=>self.io_bus.ppu.vram.write_current_bank(address-0x8000, value), - 0xA000..=0xBFFF=>self.mbc.write_external_ram(address-0xA000,value), - 0xC000..=0xCFFF =>self.io_bus.ram.write_bank0(address - 0xC000,value), - 0xE000..=0xFDFF=>self.io_bus.ram.write_bank0(address - 0xE000,value), - 0xD000..=0xDFFF=>self.io_bus.ram.write_current_bank(address-0xD000,value), + 0xA000..=0xFDFF=>self.external_memory_bus.write(address, value), 0xFE00..=0xFE9F=>self.io_bus.ppu.oam[(address-0xFE00) as usize] = value, 0xFEA0..=0xFEFF=>{}, 0xFF00..=0xFF7F=>self.io_bus.write(address - 0xFF00, value), @@ -143,7 +136,8 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu, gfx_device:G, joypad_proider:J)->Self{ GbMmu{ io_bus:IoBus::new(apu, gfx_device, joypad_proider), - mbc:mbc, + external_memory_bus: ExternalMemoryBus::new(mbc), + oucupied_access_bus:None, hram:[0;HRAM_SIZE], interupt_enable_register:0, boot_rom:boot_rom, @@ -151,23 +145,17 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ } pub fn new(mbc:&'a mut Box, apu:GbApu, gfx_device: G, joypad_proider:J)->Self{ - let mut mmu = GbMmu{ - io_bus:IoBus::new(apu, gfx_device, joypad_proider), - mbc:mbc, - hram:[0;HRAM_SIZE], - interupt_enable_register:0, - boot_rom:[0;BOOT_ROM_SIZE], - }; + let mut mmu = GbMmu::new_with_bootrom(mbc, [0;BOOT_ROM_SIZE], apu, gfx_device, joypad_proider); //Setting the bootrom register to be set (the boot sequence has over) mmu.write(BOOT_REGISTER_ADDRESS, 1); - mmu + return mmu; } - pub fn cycle(&mut self, cycles:u8){ - self.handle_dma_trasnfer(cycles); - self.io_bus.cycle(cycles as u32); + pub fn cycle(&mut self, m_cycles:u8){ + self.io_bus.dma_controller.cycle(m_cycles as u32, &mut self.external_memory_bus, &mut self.io_bus.ppu); + self.io_bus.cycle(m_cycles as u32); } pub fn handle_interrupts(&mut self, master_interrupt_enable:bool)->InterruptRequest{ @@ -178,22 +166,6 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ self.io_bus.joypad_handler.poll_joypad_state(); } - fn handle_dma_trasnfer(&mut self, cycles: u8) { - if self.io_bus.dma.enable.is_some(){ - let cycles_to_run = std::cmp::min(self.io_bus.dma.dma_cycle_counter + cycles as u16, DMA_SIZE); - for i in self.io_bus.dma.dma_cycle_counter..cycles_to_run as u16{ - let source_value = self.read_unprotected(self.io_bus.dma.soure_address + i); - self.write_unprotected(DMA_DEST + i, source_value); - } - - self.io_bus.dma.dma_cycle_counter += cycles as u16; - if self.io_bus.dma.dma_cycle_counter >= DMA_SIZE{ - self.io_bus.dma.dma_cycle_counter = 0; - self.io_bus.dma.enable = Option::None; - } - } - } - fn is_oam_ready_for_io(&self)->bool{ let ppu_state = self.io_bus.ppu.state as u8; return ppu_state != PpuState::OamSearch as u8 && ppu_state != PpuState::PixelTransfer as u8 diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index 0c1ec465..8a4e9717 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -4,21 +4,17 @@ use crate::{ timer::{timer_register_updater::*, gb_timer::GbTimer}, keypad::{joypad_provider::JoypadProvider, joypad_handler::JoypadHandler} }; -use super::{ - interrupts_handler::*, access_bus::AccessBus, memory::*, - oam_dma_transfer::OamDmaTransfer, ram::Ram, io_ports::* -}; +use super::{interrupts_handler::*, memory::*,io_ports::*, dma_controller::OamDmaController}; pub const IO_PORTS_SIZE:usize = 0x80; const WAVE_RAM_START_INDEX:u16 = 0x30; const WAVE_RAM_END_INDEX:u16 = 0x3F; pub struct IoBus{ - pub ram: Ram, pub apu: GbApu, pub timer: GbTimer, pub ppu:GbPpu, - pub dma:OamDmaTransfer, + pub dma_controller:OamDmaController, pub interrupt_handler:InterruptsHandler, pub joypad_handler: JoypadHandler, pub finished_boot:bool, @@ -85,7 +81,7 @@ impl Memory for IoBus self.ppu.bg_pos.x, LY_REGISTER_INDEX=> self.ppu.ly_register, LYC_REGISTER_INDEX=> self.ppu.lyc_register, - DMA_REGISTER_INDEX=> (self.dma.soure_address >> 8) as u8, + DMA_REGISTER_INDEX=> self.dma_controller.get_dma_register(), BGP_REGISTER_INDEX=> self.ppu.bg_palette_register, OBP0_REGISTER_INDEX=> self.ppu.obj_pallete_0_register, OBP1_REGISTER_INDEX=> self.ppu.obj_pallete_1_register, @@ -144,15 +140,7 @@ impl Memory for IoBus set_scx(&mut self.ppu, value), // LY is readonly LYC_REGISTER_INDEX=> set_lyc(&mut self.ppu, value), - DMA_REGISTER_INDEX=>{ - let address = (value as u16) << 8; - self.dma.soure_address = address; - self.dma.enable = match value{ - 0..=0x7F=>Some(AccessBus::External), - 0x80..=0x9F=>Some(AccessBus::Video), - 0xA0..=0xFF=>Some(AccessBus::External) - } - } + DMA_REGISTER_INDEX=>self.dma_controller.set_dma_register(value), BGP_REGISTER_INDEX=> handle_bg_pallet_register(value,&mut self.ppu.bg_color_mapping, &mut self.ppu.bg_palette_register), OBP0_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping0, &mut self.ppu.obj_pallete_0_register), OBP1_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping1, &mut self.ppu.obj_pallete_1_register), @@ -172,11 +160,10 @@ impl IoBus{ apu, timer:GbTimer::default(), ppu:GbPpu::new(gfx_device), - dma:OamDmaTransfer::default(), + dma_controller: OamDmaController::new(), interrupt_handler: InterruptsHandler::default(), joypad_handler: JoypadHandler::new(joypad_provider), finished_boot:false, - ram:Ram::default(), apu_cycles_counter:0, ppu_cycles:0, timer_cycles:0, diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index d43dd08c..5d7592e5 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -7,4 +7,6 @@ pub mod carts; pub mod access_bus; pub mod oam_dma_transfer; pub mod io_bus; -pub mod interrupts_handler; \ No newline at end of file +pub mod interrupts_handler; +pub mod external_memory_bus; +pub mod dma_controller; \ No newline at end of file From 91a896a6e85b11d9bf999eb505d8381b09bf034e Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 4 Feb 2022 17:34:16 +0200 Subject: [PATCH 20/26] Rename the controller and delete oam_dma_transfer --- lib_gb/src/mmu/dma_controller.rs | 59 ---------------------------- lib_gb/src/mmu/io_bus.rs | 2 +- lib_gb/src/mmu/mod.rs | 3 +- lib_gb/src/mmu/oam_dma_controller.rs | 58 +++++++++++++++++++++++++++ lib_gb/src/mmu/oam_dma_transfer.rs | 13 ------ 5 files changed, 60 insertions(+), 75 deletions(-) delete mode 100644 lib_gb/src/mmu/dma_controller.rs create mode 100644 lib_gb/src/mmu/oam_dma_controller.rs delete mode 100644 lib_gb/src/mmu/oam_dma_transfer.rs diff --git a/lib_gb/src/mmu/dma_controller.rs b/lib_gb/src/mmu/dma_controller.rs deleted file mode 100644 index 019f4b50..00000000 --- a/lib_gb/src/mmu/dma_controller.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::ppu::{gb_ppu::GbPpu, gfx_device::GfxDevice}; -use super::{oam_dma_transfer::OamDmaTransfer, external_memory_bus::ExternalMemoryBus, access_bus::AccessBus}; - -const DMA_SIZE:u16 = 0xA0; -const VRAM_BASE_ADDRESS:u16 = 0x8000; - -pub struct OamDmaController{ - oam_dma_transfer:OamDmaTransfer -} - -impl OamDmaController{ - pub fn new()->Self{ - Self{ - oam_dma_transfer: OamDmaTransfer::default() - } - } - pub fn cycle(&mut self, m_cycles:u32, external_bus: &mut ExternalMemoryBus, ppu:&mut GbPpu)->Option{ - if let Some(bus) = self.oam_dma_transfer.enable{ - let cycles_to_run = std::cmp::min(self.oam_dma_transfer.dma_cycle_counter + m_cycles as u16, DMA_SIZE); - match bus{ - AccessBus::External=>{ - for i in self.oam_dma_transfer.dma_cycle_counter..cycles_to_run as u16{ - let source_value = external_bus.read(self.oam_dma_transfer.soure_address + i); - ppu.oam[i as usize] = source_value; - } - } - AccessBus::Video=>{ - let base_source_address = self.oam_dma_transfer.soure_address - VRAM_BASE_ADDRESS; - for i in self.oam_dma_transfer.dma_cycle_counter..cycles_to_run as u16{ - let source_value = ppu.vram.read_current_bank(base_source_address + i); - ppu.oam[i as usize] = source_value; - } - } - } - - self.oam_dma_transfer.dma_cycle_counter += m_cycles as u16; - if self.oam_dma_transfer.dma_cycle_counter >= DMA_SIZE{ - self.oam_dma_transfer.dma_cycle_counter = 0; - self.oam_dma_transfer.enable = Option::None; - } - } - - return self.oam_dma_transfer.enable; - } - - pub fn get_dma_register(&self)->u8{ - (self.oam_dma_transfer.soure_address >> 8) as u8 - } - - pub fn set_dma_register(&mut self, value:u8){ - let address = (value as u16) << 8; - self.oam_dma_transfer.soure_address = address; - self.oam_dma_transfer.enable = match value{ - 0..=0x7F=>Some(AccessBus::External), - 0x80..=0x9F=>Some(AccessBus::Video), - 0xA0..=0xFF=>Some(AccessBus::External) - } - } -} \ No newline at end of file diff --git a/lib_gb/src/mmu/io_bus.rs b/lib_gb/src/mmu/io_bus.rs index 8a4e9717..735a3b9c 100644 --- a/lib_gb/src/mmu/io_bus.rs +++ b/lib_gb/src/mmu/io_bus.rs @@ -4,7 +4,7 @@ use crate::{ timer::{timer_register_updater::*, gb_timer::GbTimer}, keypad::{joypad_provider::JoypadProvider, joypad_handler::JoypadHandler} }; -use super::{interrupts_handler::*, memory::*,io_ports::*, dma_controller::OamDmaController}; +use super::{interrupts_handler::*, memory::*,io_ports::*, oam_dma_controller::OamDmaController}; pub const IO_PORTS_SIZE:usize = 0x80; const WAVE_RAM_START_INDEX:u16 = 0x30; diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index 5d7592e5..d10960c1 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -5,8 +5,7 @@ pub mod vram; pub mod io_ports; pub mod carts; pub mod access_bus; -pub mod oam_dma_transfer; pub mod io_bus; pub mod interrupts_handler; pub mod external_memory_bus; -pub mod dma_controller; \ No newline at end of file +pub mod oam_dma_controller; \ No newline at end of file diff --git a/lib_gb/src/mmu/oam_dma_controller.rs b/lib_gb/src/mmu/oam_dma_controller.rs new file mode 100644 index 00000000..af237d2d --- /dev/null +++ b/lib_gb/src/mmu/oam_dma_controller.rs @@ -0,0 +1,58 @@ +use crate::ppu::{gb_ppu::GbPpu, gfx_device::GfxDevice}; +use super::{external_memory_bus::ExternalMemoryBus, access_bus::AccessBus}; + +const DMA_SIZE:u16 = 0xA0; +const VRAM_BASE_ADDRESS:u16 = 0x8000; + +pub struct OamDmaController{ + soure_address:u16, + enable:Option, + dma_cycle_counter:u16 +} + +impl OamDmaController{ + pub fn new()->Self{ + Self{dma_cycle_counter:0, enable:None, soure_address:0} + } + pub fn cycle(&mut self, m_cycles:u32, external_bus: &mut ExternalMemoryBus, ppu:&mut GbPpu)->Option{ + if let Some(bus) = self.enable{ + let cycles_to_run = std::cmp::min(self.dma_cycle_counter + m_cycles as u16, DMA_SIZE); + match bus{ + AccessBus::External=>{ + for i in self.dma_cycle_counter..cycles_to_run as u16{ + let source_value = external_bus.read(self.soure_address + i); + ppu.oam[i as usize] = source_value; + } + } + AccessBus::Video=>{ + let base_source_address = self.soure_address - VRAM_BASE_ADDRESS; + for i in self.dma_cycle_counter..cycles_to_run as u16{ + let source_value = ppu.vram.read_current_bank(base_source_address + i); + ppu.oam[i as usize] = source_value; + } + } + } + + self.dma_cycle_counter += m_cycles as u16; + if self.dma_cycle_counter >= DMA_SIZE{ + self.dma_cycle_counter = 0; + self.enable = Option::None; + } + } + + return self.enable; + } + + pub fn get_dma_register(&self)->u8{ + (self.soure_address >> 8) as u8 + } + + pub fn set_dma_register(&mut self, value:u8){ + let address = (value as u16) << 8; + self.soure_address = address; + self.enable = match value{ + 0..=0x7F | 0xA0..=0xFF=> Some(AccessBus::External), + 0x80..=0x9F=> Some(AccessBus::Video), + } + } +} \ No newline at end of file diff --git a/lib_gb/src/mmu/oam_dma_transfer.rs b/lib_gb/src/mmu/oam_dma_transfer.rs deleted file mode 100644 index 703d74d1..00000000 --- a/lib_gb/src/mmu/oam_dma_transfer.rs +++ /dev/null @@ -1,13 +0,0 @@ -use super::access_bus::AccessBus; - -pub struct OamDmaTransfer{ - pub soure_address:u16, - pub enable:Option, - pub dma_cycle_counter:u16 -} - -impl Default for OamDmaTransfer{ - fn default() -> Self { - OamDmaTransfer{dma_cycle_counter:0, enable:None, soure_address:0} - } -} \ No newline at end of file From e38e772ebcb44e6e119758db50524dc9cf01e677 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 4 Feb 2022 18:26:08 +0200 Subject: [PATCH 21/26] Fix not setting the access bus in the mmu --- lib_gb/src/mmu/gb_mmu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index c9097307..f7c127d9 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -154,7 +154,7 @@ impl<'a, D:AudioDevice, G:GfxDevice, J:JoypadProvider> GbMmu<'a, D, G, J>{ } pub fn cycle(&mut self, m_cycles:u8){ - self.io_bus.dma_controller.cycle(m_cycles as u32, &mut self.external_memory_bus, &mut self.io_bus.ppu); + self.oucupied_access_bus = self.io_bus.dma_controller.cycle(m_cycles as u32, &mut self.external_memory_bus, &mut self.io_bus.ppu); self.io_bus.cycle(m_cycles as u32); } From 38a9d7358ad3107667e214d4e29d15f968d2c0aa Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 4 Feb 2022 18:42:50 +0200 Subject: [PATCH 22/26] Add building section in the readme --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index c8646236..3dbc2911 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,18 @@ magenboy [path_to_rom] [other_optional_flags] * `--no-vsync` - Disable vsync * `--bootrom [path to bootrom file]` - Specify the path for a bootrom (If not specified the emualtor will look for `dmg_boot.bin` at the cwd) +### Building + +```shell +cargo build --release --features [optional_features] +``` +#### Optional features: +* static-sdl - will link statically to sdl2. +On by default (to turn off pass `--no-default-features`) +* sdl-resample - Use the audio resampler from sdl2 library and a manual one I wrote +* push-audio - Use a push methododlogy instead of pull for the delivery of the sound samples to sdl2 +* static-scale - Will use a fixed scale values for the renderer instead of addapting to the screen size + ## GameBoy ### Development Status From 1eb6d5cc81b080ed90cf31439515ecfe9ca993cf Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 22 Apr 2022 00:46:33 +0300 Subject: [PATCH 23/26] Add rom_menu feature This feature pops a terminal menu to choose a rom from the rom folder supplied The idea is to allow the player to have one command to boot the emulator and than he can choose new rom each time --- Cargo.lock | 168 ++++++++++++++++++++++++++++++++- README.md | 1 + gb/Cargo.toml | 1 + gb/src/joypad_terminal_menu.rs | 111 ++++++++++++++++++++++ gb/src/main.rs | 55 ++++++++++- 5 files changed, 326 insertions(+), 10 deletions(-) create mode 100644 gb/src/joypad_terminal_menu.rs diff --git a/Cargo.lock b/Cargo.lock index f2b94e66..6adf729f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,9 +30,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" @@ -262,6 +262,31 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crossterm" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2102ea4f781910f8a5b98dd061f4c2023f479ce7bb1236330099ceb5a93cf17" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio 0.8.2", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +dependencies = [ + "winapi", +] + [[package]] name = "csv" version = "1.1.6" @@ -419,6 +444,7 @@ dependencies = [ "cfg-if 1.0.0", "chrono", "crossbeam-channel", + "crossterm", "fern", "lib_gb", "log", @@ -434,7 +460,7 @@ checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.10.0+wasi-snapshot-preview1", ] [[package]] @@ -621,6 +647,16 @@ version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" +[[package]] +name = "lock_api" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.14" @@ -680,6 +716,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "mio" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "wasi 0.11.0+wasi-snapshot-preview1", + "winapi", +] + [[package]] name = "miow" version = "0.3.7" @@ -790,6 +840,29 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + [[package]] name = "percent-encoding" version = "2.1.0" @@ -1158,12 +1231,48 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio 0.8.2", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" +[[package]] +name = "smallvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" + [[package]] name = "socket2" version = "0.4.2" @@ -1246,7 +1355,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", - "wasi", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] @@ -1285,7 +1394,7 @@ dependencies = [ "bytes", "libc", "memchr", - "mio", + "mio 0.7.13", "num_cpus", "pin-project-lite", "winapi", @@ -1436,6 +1545,12 @@ version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "wasm-bindgen" version = "0.2.78" @@ -1554,6 +1669,49 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" + +[[package]] +name = "windows_i686_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" + +[[package]] +name = "windows_i686_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" + [[package]] name = "winreg" version = "0.7.0" diff --git a/README.md b/README.md index 3dbc2911..6f906f2f 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ magenboy [path_to_rom] [other_optional_flags] * `--full-screen` - Full screen mode * `--no-vsync` - Disable vsync * `--bootrom [path to bootrom file]` - Specify the path for a bootrom (If not specified the emualtor will look for `dmg_boot.bin` at the cwd) +* `--rom_menu [path to roms folder]` - Opens an interactive dialog uopn start to choose the rom from the folder ### Building diff --git a/gb/Cargo.toml b/gb/Cargo.toml index b5c39eb7..9d9e0972 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -18,6 +18,7 @@ sdl2 = "0.34" wav = "1.0" crossbeam-channel = "0.5" cfg-if = "1.0" +crossterm = "0.23" [features] default = ["static-sdl"] diff --git a/gb/src/joypad_terminal_menu.rs b/gb/src/joypad_terminal_menu.rs new file mode 100644 index 00000000..b52b347b --- /dev/null +++ b/gb/src/joypad_terminal_menu.rs @@ -0,0 +1,111 @@ +use std::{io::Write, time::Duration}; + +use crossterm::{QueueableCommand, style::{self, Stylize}, cursor, terminal::{self, ClearType}, event::{self, KeyCode}}; +use lib_gb::keypad::{joypad_provider::JoypadProvider, joypad::Joypad, self, button::Button}; + +pub struct TerminalRawModeJoypadProviderOption