From 7e2c9d040afd98313f29aad6e756907db14fd64c Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Fri, 6 Nov 2020 02:38:41 -0800 Subject: [PATCH] [perf] core: gpu: Refactor to use unpacked mmio configuration during rendering * Convert gpu bitfield!() registers to unpacked form, and defer pack/unpack to bus read/write operations Former-commit-id: 26e7d7d62d6418ce7bcdb8e414cabe5ddb56333d Former-commit-id: 716ddd9fe2b7b95b7613fc549a7bee406272478b --- Cargo.lock | 12 + core/Cargo.toml | 1 + core/src/gpu/mod.rs | 410 +++++++++------------------------- core/src/gpu/mosaic.rs | 28 +-- core/src/gpu/regs.rs | 297 +++++++++++++++--------- core/src/gpu/render/bitmap.rs | 22 +- core/src/gpu/render/text.rs | 27 +-- core/src/gpu/sfx.rs | 108 +++------ core/src/iodev.rs | 47 ++-- core/src/lib.rs | 3 + core/src/sched.rs | 12 + 11 files changed, 423 insertions(+), 544 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6202d4..ecd65b9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1231,6 +1231,7 @@ dependencies = [ "rustyline 6.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.116 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "smart-default 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "zip 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1481,6 +1482,16 @@ dependencies = [ "opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "smart-default" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "spin_sleep" version = "0.3.7" @@ -2134,6 +2145,7 @@ dependencies = [ "checksum serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)" = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" "checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" "checksum sha2 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +"checksum smart-default 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" "checksum spin_sleep 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "891836ef5f8a5b9678938d34d75391a3794267806482105ffcd363271980c10c" "checksum standback 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "33a71ea1ea5f8747d1af1979bfb7e65c3a025a70609f04ceb78425bc5adad8e6" "checksum static_assertions 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" diff --git a/core/Cargo.toml b/core/Cargo.toml index ebaebba..f094821 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -41,6 +41,7 @@ fuzzy-matcher = { version = "0.3.4", optional = true } bit_reverse = "0.1.8" yaml-rust = "0.4" lazy_static = "1.4.0" +smart-default = "0.6.0" [target.'cfg(target_arch="wasm32")'.dependencies] instant = { version = "0.1.2", features = ["wasm-bindgen"] } diff --git a/core/src/gpu/mod.rs b/core/src/gpu/mod.rs index f56699c..d3bd610 100644 --- a/core/src/gpu/mod.rs +++ b/core/src/gpu/mod.rs @@ -1,6 +1,5 @@ #[cfg(not(feature = "no_video_interface"))] use std::cell::RefCell; -use std::fmt; #[cfg(not(feature = "no_video_interface"))] use std::rc::Rc; @@ -14,7 +13,6 @@ pub use super::sysbus::consts::*; #[cfg(not(feature = "no_video_interface"))] use super::VideoInterface; -use crate::bitfield::Bit; use crate::num::FromPrimitive; mod render; @@ -66,88 +64,6 @@ pub enum PixelFormat { BPP8 = 1, } -#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)] -#[serde(deny_unknown_fields)] -pub enum GpuState { - HDraw = 0, - HBlank, - VBlankHDraw, - VBlankHBlank, -} - -impl Default for GpuState { - fn default() -> GpuState { - GpuState::HDraw - } -} - -impl GpuState { - pub fn is_vblank(&self) -> bool { - match self { - VBlankHBlank | VBlankHDraw => true, - _ => false, - } - } -} - -use GpuState::*; - -#[repr(transparent)] -#[derive(Serialize, Deserialize, Clone)] -pub struct Scanline { - inner: Vec, -} - -impl Default for Scanline { - fn default() -> Scanline { - Scanline { - inner: vec![Rgb15::TRANSPARENT; DISPLAY_WIDTH], - } - } -} - -impl fmt::Debug for Scanline { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "...") - } -} - -impl std::ops::Index for Scanline { - type Output = Rgb15; - fn index(&self, index: usize) -> &Self::Output { - &self.inner[index] - } -} - -impl std::ops::IndexMut for Scanline { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.inner[index] - } -} - -#[derive(Serialize, Deserialize, Debug, Default, Clone)] -pub struct Background { - pub bgcnt: BgControl, - pub bgvofs: u16, - pub bghofs: u16, - - line: Scanline, - - // for mosaic - mosaic_first_row: Scanline, -} - -impl Background { - #[inline] - pub fn get_priority(&self) -> u16 { - self.bgcnt.priority() - } - #[inline] - pub fn pixel_at(&self, x: usize) -> Rgb15 { - self.line[x] - } -} - #[derive(Debug, Default, Copy, Clone)] pub struct AffineMatrix { pub pa: i32, @@ -192,7 +108,6 @@ type VideoDeviceRcRefCell = Rc>; #[derive(Serialize, Deserialize, Clone, DebugStub)] pub struct Gpu { - pub state: GpuState, interrupt_flags: SharedInterruptFlags, /// When deserializing this struct using serde, make sure to call connect_scheduler @@ -208,30 +123,25 @@ pub struct Gpu { pub dispcnt: DisplayControl, pub dispstat: DisplayStatus, - pub backgrounds: [Background; 4], + pub bgcnt: [BgControl; 4], + pub bg_vofs: [u16; 4], + pub bg_hofs: [u16; 4], pub bg_aff: [BgAffine; 2], - pub win0: Window, pub win1: Window, pub winout_flags: WindowFlags, pub winobj_flags: WindowFlags, - pub mosaic: RegMosaic, pub bldcnt: BlendControl, pub bldalpha: BlendAlpha, pub bldy: u16, - pub palette_ram: Box<[u8]>, pub vram: Box<[u8]>, pub oam: Box<[u8]>, - pub(super) vram_obj_tiles_start: u32, - - #[debug_stub = "Sprite Buffer"] - pub obj_buffer: Vec, - - #[debug_stub = "Frame Buffer"] - pub(super) frame_buffer: Vec, + pub(super) obj_buffer: Box<[ObjBufferEntry]>, + pub(super) frame_buffer: Box<[u32]>, + pub(super) bg_line: [Box<[Rgb15]>; 4], } impl InterruptConnect for Gpu { @@ -249,56 +159,59 @@ impl SchedulerConnect for Gpu { impl Gpu { pub fn new(mut scheduler: SharedScheduler, interrupt_flags: SharedInterruptFlags) -> Gpu { scheduler.push_gpu_event(GpuEvent::HDraw, CYCLES_HDRAW); + + fn alloc_scanline_buffer() -> Box<[Rgb15]> { + vec![Rgb15::TRANSPARENT; DISPLAY_WIDTH].into_boxed_slice() + } + Gpu { interrupt_flags, scheduler, - dispcnt: DisplayControl(0x80), - dispstat: DisplayStatus(0), - backgrounds: [ - Background::default(), - Background::default(), - Background::default(), - Background::default(), - ], + dispcnt: DisplayControl::from(0x80), + dispstat: Default::default(), + bgcnt: Default::default(), + bg_vofs: [0; 4], + bg_hofs: [0; 4], bg_aff: [BgAffine::default(); 2], win0: Window::default(), win1: Window::default(), winout_flags: WindowFlags::from(0), winobj_flags: WindowFlags::from(0), mosaic: RegMosaic(0), - bldcnt: BlendControl(0), - bldalpha: BlendAlpha(0), + bldcnt: BlendControl::default(), + bldalpha: BlendAlpha::default(), bldy: 0, - vram_obj_tiles_start: VRAM_OBJ_TILES_START_TEXT, - - state: HDraw, vcount: 0, cycles_left_for_current_state: CYCLES_HDRAW, - palette_ram: vec![0; PALETTE_RAM_SIZE].into_boxed_slice(), vram: vec![0; VIDEO_RAM_SIZE].into_boxed_slice(), oam: vec![0; OAM_SIZE].into_boxed_slice(), - - obj_buffer: vec![Default::default(); DISPLAY_WIDTH * DISPLAY_HEIGHT], - - frame_buffer: vec![0; DISPLAY_WIDTH * DISPLAY_HEIGHT], + obj_buffer: vec![Default::default(); DISPLAY_WIDTH * DISPLAY_HEIGHT].into_boxed_slice(), + frame_buffer: vec![0; DISPLAY_WIDTH * DISPLAY_HEIGHT].into_boxed_slice(), + bg_line: [ + alloc_scanline_buffer(), + alloc_scanline_buffer(), + alloc_scanline_buffer(), + alloc_scanline_buffer(), + ], + vram_obj_tiles_start: VRAM_OBJ_TILES_START_TEXT, } } + #[inline] pub fn write_dispcnt(&mut self, value: u16) { - let new_dispcnt = DisplayControl(value); - let old_mode = self.dispcnt.mode(); - let new_mode = new_dispcnt.mode(); + let old_mode = self.dispcnt.mode; + self.dispcnt.write(value); + let new_mode = self.dispcnt.mode; if old_mode != new_mode { debug!("[GPU] Display mode changed! {} -> {}", old_mode, new_mode); - self.vram_obj_tiles_start = if new_dispcnt.mode() >= 3 { + self.vram_obj_tiles_start = if new_mode >= 3 { VRAM_OBJ_TILES_START_BITMAP } else { VRAM_OBJ_TILES_START_TEXT }; } - self.dispcnt = new_dispcnt; } pub fn skip_bios(&mut self) { @@ -368,7 +281,7 @@ impl Gpu { } pub fn render_scanline(&mut self) { - if self.dispcnt.force_blank() { + if self.dispcnt.force_blank { for x in self.frame_buffer[self.vcount * DISPLAY_WIDTH..] .iter_mut() .take(DISPLAY_WIDTH) @@ -378,35 +291,35 @@ impl Gpu { return; } - if self.dispcnt.enable_obj() { + if self.dispcnt.enable_obj { self.render_objs(); } - match self.dispcnt.mode() { + match self.dispcnt.mode { 0 => { for bg in 0..=3 { - if self.dispcnt.enable_bg(bg) { + if self.dispcnt.enable_bg[bg] { self.render_reg_bg(bg); } } self.finalize_scanline(0, 3); } 1 => { - if self.dispcnt.enable_bg(2) { + if self.dispcnt.enable_bg[2] { self.render_aff_bg(2); } - if self.dispcnt.enable_bg(1) { + if self.dispcnt.enable_bg[1] { self.render_reg_bg(1); } - if self.dispcnt.enable_bg(0) { + if self.dispcnt.enable_bg[0] { self.render_reg_bg(0); } self.finalize_scanline(0, 2); } 2 => { - if self.dispcnt.enable_bg(3) { + if self.dispcnt.enable_bg[3] { self.render_aff_bg(3); } - if self.dispcnt.enable_bg(2) { + if self.dispcnt.enable_bg[2] { self.render_aff_bg(2); } self.finalize_scanline(2, 3); @@ -423,7 +336,7 @@ impl Gpu { self.render_mode5(2); self.finalize_scanline(2, 2); } - _ => panic!("{:?} not supported", self.dispcnt.mode()), + _ => panic!("{:?} not supported", self.dispcnt.mode), } // self.mosaic_sfx(); } @@ -442,19 +355,18 @@ impl Gpu { #[inline] fn update_vcount(&mut self, value: usize) { self.vcount = value; - let vcount_setting = self.dispstat.vcount_setting(); - self.dispstat - .set_vcount_flag(vcount_setting == self.vcount as u16); + let vcount_setting = self.dispstat.vcount_setting; + self.dispstat.vcount_flag = vcount_setting == self.vcount; - if self.dispstat.vcount_irq_enable() && self.dispstat.get_vcount_flag() { + if self.dispstat.vcount_irq_enable && self.dispstat.vcount_flag { interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_VCounterMatch); } } #[inline] - fn handle_hdraw(&mut self, dma_notifier: &mut D) -> (GpuEvent, usize) { - self.dispstat.set_hblank_flag(true); - if self.dispstat.hblank_irq_enable() { + fn handle_hdraw_end(&mut self, dma_notifier: &mut D) -> (GpuEvent, usize) { + self.dispstat.hblank_flag = true; + if self.dispstat.hblank_irq_enable { interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank); }; dma_notifier.notify(TIMING_HBLANK); @@ -463,7 +375,7 @@ impl Gpu { (GpuEvent::HBlank, CYCLES_HBLANK) } - fn handle_hblank( + fn handle_hblank_end( &mut self, dma_notifier: &mut D, #[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell, @@ -471,7 +383,7 @@ impl Gpu { self.update_vcount(self.vcount + 1); if self.vcount < DISPLAY_HEIGHT { - self.dispstat.set_hblank_flag(false); + self.dispstat.hblank_flag = false; self.render_scanline(); // update BG2/3 reference points on the end of a scanline for i in 0..2 { @@ -487,9 +399,9 @@ impl Gpu { self.bg_aff[i].internal_y = self.bg_aff[i].y; } - self.dispstat.set_vblank_flag(true); - self.dispstat.set_hblank_flag(false); - if self.dispstat.vblank_irq_enable() { + self.dispstat.vblank_flag = true; + self.dispstat.hblank_flag = false; + if self.dispstat.vblank_irq_enable { interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_VBlank); }; @@ -504,149 +416,28 @@ impl Gpu { } } - fn handle_vblank_hdraw(&mut self) -> (GpuEvent, usize) { - self.dispstat.set_hblank_flag(true); - if self.dispstat.hblank_irq_enable() { + fn handle_vblank_hdraw_end(&mut self) -> (GpuEvent, usize) { + self.dispstat.hblank_flag = true; + if self.dispstat.hblank_irq_enable { interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank); }; (GpuEvent::VBlankHBlank, CYCLES_HBLANK) } - fn handle_vblank_hblank(&mut self) -> (GpuEvent, usize) { + fn handle_vblank_hblank_end(&mut self) -> (GpuEvent, usize) { if self.vcount < DISPLAY_HEIGHT + VBLANK_LINES - 1 { self.update_vcount(self.vcount + 1); - self.dispstat.set_hblank_flag(false); + self.dispstat.hblank_flag = false; (GpuEvent::VBlankHDraw, CYCLES_HDRAW) } else { self.update_vcount(0); - self.dispstat.set_vblank_flag(false); - self.dispstat.set_hblank_flag(false); + self.dispstat.vblank_flag = false; + self.dispstat.hblank_flag = false; self.render_scanline(); (GpuEvent::HDraw, CYCLES_HDRAW) } } - pub fn on_state_completed( - &mut self, - completed: GpuState, - dma_notifier: &mut D, - #[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell, - ) where - D: DmaNotifer, - { - macro_rules! update_vcount { - ($value:expr) => { - self.vcount = $value; - let vcount_setting = self.dispstat.vcount_setting(); - self.dispstat - .set_vcount_flag(vcount_setting == self.vcount as u16); - - if self.dispstat.vcount_irq_enable() && self.dispstat.get_vcount_flag() { - interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_VCounterMatch); - } - }; - } - - match completed { - HDraw => { - // Transition to HBlank - self.state = HBlank; - self.cycles_left_for_current_state = CYCLES_HBLANK; - - self.handle_hdraw(dma_notifier); - } - HBlank => { - update_vcount!(self.vcount + 1); - - if self.vcount < DISPLAY_HEIGHT { - self.state = HDraw; - self.dispstat.set_hblank_flag(false); - self.render_scanline(); - // update BG2/3 reference points on the end of a scanline - for i in 0..2 { - self.bg_aff[i].internal_x += self.bg_aff[i].pb as i16 as i32; - self.bg_aff[i].internal_y += self.bg_aff[i].pd as i16 as i32; - } - self.cycles_left_for_current_state = CYCLES_HDRAW; - } else { - // latch BG2/3 reference points on vblank - for i in 0..2 { - self.bg_aff[i].internal_x = self.bg_aff[i].x; - self.bg_aff[i].internal_y = self.bg_aff[i].y; - } - - self.dispstat.set_vblank_flag(true); - self.dispstat.set_hblank_flag(false); - if self.dispstat.vblank_irq_enable() { - interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_VBlank); - }; - - dma_notifier.notify(TIMING_VBLANK); - - #[cfg(not(feature = "no_video_interface"))] - video_device.borrow_mut().render(&self.frame_buffer); - - self.obj_buffer_reset(); - self.cycles_left_for_current_state = CYCLES_HDRAW; - self.state = VBlankHDraw; - } - } - VBlankHDraw => { - self.cycles_left_for_current_state = CYCLES_HBLANK; - self.state = VBlankHBlank; - - self.dispstat.set_hblank_flag(true); - if self.dispstat.hblank_irq_enable() { - interrupt::signal_irq(&self.interrupt_flags, Interrupt::LCD_HBlank); - }; - } - VBlankHBlank => { - if self.vcount < DISPLAY_HEIGHT + VBLANK_LINES - 1 { - update_vcount!(self.vcount + 1); - self.dispstat.set_hblank_flag(false); - self.cycles_left_for_current_state = CYCLES_HDRAW; - self.state = VBlankHDraw; - } else { - update_vcount!(0); - self.dispstat.set_vblank_flag(false); - self.dispstat.set_hblank_flag(false); - self.render_scanline(); - self.cycles_left_for_current_state = CYCLES_HDRAW; - self.state = HDraw; - } - } - }; - } - - pub fn update( - &mut self, - mut cycles: usize, - cycles_to_next_event: &mut usize, - dma_notifier: &mut D, - #[cfg(not(feature = "no_video_interface"))] video_device: &VideoDeviceRcRefCell, - ) where - D: DmaNotifer, - { - loop { - if self.cycles_left_for_current_state <= cycles { - cycles -= self.cycles_left_for_current_state; - self.on_state_completed( - self.state, - dma_notifier, - #[cfg(not(feature = "no_video_interface"))] - video_device, - ); - } else { - self.cycles_left_for_current_state -= cycles; - break; - } - } - - if self.cycles_left_for_current_state < *cycles_to_next_event { - *cycles_to_next_event = self.cycles_left_for_current_state; - } - } - pub fn on_event( &mut self, event: GpuEvent, @@ -657,14 +448,14 @@ impl Gpu { D: DmaNotifer, { let (next_event, cycles) = match event { - GpuEvent::HDraw => self.handle_hdraw(dma_notifier), - GpuEvent::HBlank => self.handle_hblank( + GpuEvent::HDraw => self.handle_hdraw_end(dma_notifier), + GpuEvent::HBlank => self.handle_hblank_end( dma_notifier, #[cfg(not(feature = "no_video_interface"))] video_device, ), - GpuEvent::VBlankHDraw => self.handle_vblank_hdraw(), - GpuEvent::VBlankHBlank => self.handle_vblank_hblank(), + GpuEvent::VBlankHDraw => self.handle_vblank_hdraw_end(), + GpuEvent::VBlankHBlank => self.handle_vblank_hblank_end(), }; self.scheduler .push(EventType::Gpu(next_event), cycles - extra_cycles); @@ -765,32 +556,32 @@ mod tests { #[test] fn test_gpu_state_machine() { - let mut gpu = Gpu::new( - Scheduler::new_shared(), - Rc::new(Cell::new(Default::default())), - ); + let mut sched = Scheduler::new_shared(); + let mut gpu = Gpu::new(sched.clone(), Rc::new(Cell::new(Default::default()))); #[cfg(not(feature = "no_video_interface"))] let video = Rc::new(RefCell::new(TestVideoInterface::default())); #[cfg(not(feature = "no_video_interface"))] let video_clone: VideoDeviceRcRefCell = video.clone(); let mut dma_notifier = NopDmaNotifer; - let mut cycles_to_next_event = CYCLES_FULL_REFRESH; - gpu.dispstat.set_vcount_setting(0); - gpu.dispstat.set_vcount_irq_enable(true); - - let mut total_cycles = 0; + gpu.dispstat.vcount_setting = 0; + gpu.dispstat.vcount_irq_enable = true; macro_rules! update { ($cycles:expr) => { - gpu.update( - $cycles, - &mut cycles_to_next_event, - &mut dma_notifier, - #[cfg(not(feature = "no_video_interface"))] - &video_clone, - ); - total_cycles += $cycles; + sched.update($cycles); + let (event, cycles_late) = sched.pop_pending_event().unwrap(); + assert_eq!(cycles_late, 0); + match event { + EventType::Gpu(event) => gpu.on_event( + event, + cycles_late, + &mut dma_notifier, + #[cfg(not(feature = "no_video_interface"))] + &video_clone, + ), + _ => panic!("Found unexpected event in queue!"), + } }; } @@ -799,15 +590,16 @@ mod tests { #[cfg(not(feature = "no_video_interface"))] assert_eq!(video.borrow().frame_counter, 0); assert_eq!(gpu.vcount, line); - assert_eq!(gpu.state, GpuState::HDraw); - assert_eq!(gpu.dispstat.get_hblank_flag(), false); - assert_eq!(gpu.dispstat.get_vblank_flag(), false); + assert_eq!(sched.peek_next(), Some(EventType::Gpu(GpuEvent::HDraw))); + assert_eq!(gpu.dispstat.hblank_flag, false); + assert_eq!(gpu.dispstat.vblank_flag, false); update!(CYCLES_HDRAW); - assert_eq!(gpu.state, GpuState::HBlank); - assert_eq!(gpu.dispstat.get_hblank_flag(), true); - assert_eq!(gpu.dispstat.get_vblank_flag(), false); + println!("{:?}", sched.num_pending_events()); + assert_eq!(sched.peek_next(), Some(EventType::Gpu(GpuEvent::HBlank))); + assert_eq!(gpu.dispstat.hblank_flag, true); + assert_eq!(gpu.dispstat.vblank_flag, false); update!(CYCLES_HBLANK); @@ -819,15 +611,21 @@ mod tests { for line in 0..68 { println!("line = {}", 160 + line); - assert_eq!(gpu.dispstat.get_hblank_flag(), false); - assert_eq!(gpu.dispstat.get_vblank_flag(), true); - assert_eq!(gpu.state, GpuState::VBlankHDraw); + assert_eq!(gpu.dispstat.hblank_flag, false); + assert_eq!(gpu.dispstat.vblank_flag, true); + assert_eq!( + sched.peek_next(), + Some(EventType::Gpu(GpuEvent::VBlankHDraw)) + ); update!(CYCLES_HDRAW); - assert_eq!(gpu.dispstat.get_hblank_flag(), true); - assert_eq!(gpu.dispstat.get_vblank_flag(), true); - assert_eq!(gpu.state, GpuState::VBlankHBlank); + assert_eq!(gpu.dispstat.hblank_flag, true); + assert_eq!(gpu.dispstat.vblank_flag, true); + assert_eq!( + sched.peek_next(), + Some(EventType::Gpu(GpuEvent::VBlankHBlank)) + ); assert_eq!(gpu.interrupt_flags.get().LCD_VCounterMatch(), false); update!(CYCLES_HBLANK); @@ -835,13 +633,13 @@ mod tests { #[cfg(not(feature = "no_video_interface"))] assert_eq!(video.borrow().frame_counter, 1); - assert_eq!(total_cycles, CYCLES_FULL_REFRESH); + assert_eq!(sched.timestamp(), CYCLES_FULL_REFRESH); assert_eq!(gpu.interrupt_flags.get().LCD_VCounterMatch(), true); assert_eq!(gpu.cycles_left_for_current_state, CYCLES_HDRAW); - assert_eq!(gpu.state, GpuState::HDraw); + assert_eq!(sched.peek_next(), Some(EventType::Gpu(GpuEvent::HDraw))); assert_eq!(gpu.vcount, 0); - assert_eq!(gpu.dispstat.get_vcount_flag(), true); - assert_eq!(gpu.dispstat.get_hblank_flag(), false); + assert_eq!(gpu.dispstat.vcount_flag, true); + assert_eq!(gpu.dispstat.hblank_flag, false); } } diff --git a/core/src/gpu/mosaic.rs b/core/src/gpu/mosaic.rs index ef61978..d5b1d92 100644 --- a/core/src/gpu/mosaic.rs +++ b/core/src/gpu/mosaic.rs @@ -9,21 +9,21 @@ impl RegMosaic { impl Gpu { fn mosaic_bg(&mut self) { - let hsize = (self.mosaic.bg_hsize() + 1) as usize; - let vsize = (self.mosaic.bg_vsize() + 1) as usize; + // let hsize = (self.mosaic.bg_hsize() + 1) as usize; + // let vsize = (self.mosaic.bg_vsize() + 1) as usize; - for bg in 0..4 { - if self.dispcnt.enable_bg(bg) && self.backgrounds[bg].bgcnt.mosaic() { - let y = self.vcount as usize; - if y % vsize == 0 { - self.backgrounds[bg].mosaic_first_row = self.backgrounds[bg].line.clone(); - } - for x in 0..DISPLAY_WIDTH { - let color = self.backgrounds[bg].mosaic_first_row[(x / hsize) * hsize]; - self.backgrounds[bg].line[x] = color; - } - } - } + // for bg in 0..4 { + // if self.dispcnt.enable_bg[bg] && self.backgrounds[bg].mosaic { + // let y = self.vcount as usize; + // if y % vsize == 0 { + // self.backgrounds[bg].mosaic_first_row = self.backgrounds[bg].line.clone(); + // } + // for x in 0..DISPLAY_WIDTH { + // let color = self.backgrounds[bg].mosaic_first_row[(x / hsize) * hsize]; + // self.backgrounds[bg].line[x] = color; + // } + // } + // } } pub fn mosaic_sfx(&mut self) { diff --git a/core/src/gpu/regs.rs b/core/src/gpu/regs.rs index a1acd5f..25f4033 100644 --- a/core/src/gpu/regs.rs +++ b/core/src/gpu/regs.rs @@ -1,27 +1,51 @@ use super::layer::RenderLayer; -use super::sfx::BldMode; use super::*; -use num::ToPrimitive; use serde::{Deserialize, Serialize}; pub const SCREEN_BLOCK_SIZE: u32 = 0x800; +pub trait GpuMemoryMappedIO { + fn read(&self) -> u16; + fn write(&mut self, value: u16); +} + #[derive(Debug, PartialEq)] pub enum ObjMapping { TwoDimension, OneDimension, } +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +pub struct DisplayControl { + pub mode: u16, + pub display_frame_select: u16, + pub hblank_interval_free: bool, + pub obj_character_vram_mapping: bool, + pub force_blank: bool, + pub enable_bg: [bool; 4], + pub enable_obj: bool, + pub enable_window0: bool, + pub enable_window1: bool, + pub enable_obj_window: bool, +} + +impl From for DisplayControl { + fn from(value: u16) -> DisplayControl { + let mut dispcnt = DisplayControl::default(); + dispcnt.write(value); + dispcnt + } +} + impl DisplayControl { - pub fn enable_bg(&self, bg: usize) -> bool { - self.0.bit(8 + bg) - } + #[inline] pub fn is_using_windows(&self) -> bool { - self.enable_window0() || self.enable_window1() || self.enable_obj_window() + self.enable_window0 || self.enable_window1 || self.enable_obj_window } + #[inline] pub fn obj_mapping(&self) -> ObjMapping { - if self.obj_character_vram_mapping() { + if self.obj_character_vram_mapping { ObjMapping::OneDimension } else { ObjMapping::TwoDimension @@ -29,17 +53,122 @@ impl DisplayControl { } } +impl GpuMemoryMappedIO for DisplayControl { + #[inline] + fn write(&mut self, value: u16) { + self.mode = value & 0b111; + self.display_frame_select = (value >> 4) & 1; + self.hblank_interval_free = (value >> 5) & 1 != 0; + self.obj_character_vram_mapping = (value >> 6) & 1 != 0; + self.force_blank = (value >> 7) & 1 != 0; + self.enable_bg[0] = (value >> 8) & 1 != 0; + self.enable_bg[1] = (value >> 9) & 1 != 0; + self.enable_bg[2] = (value >> 10) & 1 != 0; + self.enable_bg[3] = (value >> 11) & 1 != 0; + self.enable_obj = (value >> 12) & 1 != 0; + self.enable_window0 = (value >> 13) & 1 != 0; + self.enable_window1 = (value >> 14) & 1 != 0; + self.enable_obj_window = (value >> 15) & 1 != 0; + } + #[inline] + fn read(&self) -> u16 { + self.mode + | self.display_frame_select << 4 + | u16::from(self.hblank_interval_free) << 5 + | u16::from(self.obj_character_vram_mapping) << 6 + | u16::from(self.force_blank) << 7 + | u16::from(self.enable_bg[0]) << 8 + | u16::from(self.enable_bg[1]) << 9 + | u16::from(self.enable_bg[2]) << 10 + | u16::from(self.enable_bg[3]) << 11 + | u16::from(self.enable_obj) << 12 + | u16::from(self.enable_window0) << 13 + | u16::from(self.enable_window1) << 14 + | u16::from(self.enable_obj_window) << 15 + } +} + +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +pub struct DisplayStatus { + pub vblank_flag: bool, + pub hblank_flag: bool, + pub vcount_flag: bool, + pub vblank_irq_enable: bool, + pub hblank_irq_enable: bool, + pub vcount_irq_enable: bool, + pub vcount_setting: usize, +} + +impl GpuMemoryMappedIO for DisplayStatus { + #[inline] + fn write(&mut self, value: u16) { + // self.vblank_flag = (value >> 0) & 1 != 0; + // self.hblank_flag = (value >> 1) & 1 != 0; + // self.vcount_flag = (value >> 2) & 1 != 0; + self.vblank_irq_enable = (value >> 3) & 1 != 0; + self.hblank_irq_enable = (value >> 4) & 1 != 0; + self.vcount_irq_enable = (value >> 5) & 1 != 0; + self.vcount_setting = usize::from((value >> 8) & 0xff); + } + #[inline] + fn read(&self) -> u16 { + u16::from(self.vblank_flag) << 0 + | u16::from(self.hblank_flag) << 1 + | u16::from(self.vcount_flag) << 2 + | u16::from(self.vblank_irq_enable) << 3 + | u16::from(self.hblank_irq_enable) << 4 + | u16::from(self.vcount_irq_enable) << 5 + | (self.vcount_setting as u16) << 8 + } +} + +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +pub struct BgControl { + pub priority: u16, + pub character_base_block: u16, + pub screen_base_block: u16, + pub mosaic: bool, + pub palette256: bool, + pub affine_wraparound: bool, + pub size: u8, +} + +impl GpuMemoryMappedIO for BgControl { + #[inline] + fn write(&mut self, value: u16) { + self.priority = (value >> 0) & 0b11; + self.character_base_block = (value >> 2) & 0b11; + self.mosaic = (value >> 6) & 1 != 0; + self.palette256 = (value >> 7) & 1 != 0; + self.screen_base_block = (value >> 8) & 0b11111; + self.affine_wraparound = (value >> 13) & 1 != 0; + self.size = ((value >> 14) & 0b11) as u8; + } + + #[inline] + fn read(&self) -> u16 { + self.priority + | self.character_base_block << 2 + | u16::from(self.mosaic) << 6 + | u16::from(self.palette256) << 7 + | self.screen_base_block << 8 + | u16::from(self.affine_wraparound) << 13 + | u16::from(self.size) << 14 + } +} + impl BgControl { + #[inline] pub fn char_block(&self) -> u32 { - (self.character_base_block() as u32) * 0x4000 + (self.character_base_block as u32) * 0x4000 } - + #[inline] pub fn screen_block(&self) -> u32 { - (self.screen_base_block() as u32) * SCREEN_BLOCK_SIZE + (self.screen_base_block as u32) * SCREEN_BLOCK_SIZE } - + #[inline] pub fn size_regular(&self) -> (u32, u32) { - match self.bg_size() { + match self.size { 0b00 => (256, 256), 0b01 => (512, 256), 0b10 => (256, 512), @@ -47,14 +176,9 @@ impl BgControl { _ => unreachable!(), } } - - pub fn size_affine(&self) -> (i32, i32) { - let x = 128 << self.bg_size(); - (x, x) - } - + #[inline] pub fn tile_format(&self) -> (u32, PixelFormat) { - if self.palette256() { + if self.palette256 { (2 * TILE_SIZE, PixelFormat::BPP8) } else { (TILE_SIZE, PixelFormat::BPP4) @@ -62,55 +186,6 @@ impl BgControl { } } -// struct definitions below because the bitfield! macro messes up syntax highlighting in vscode. -bitfield! { - #[derive(Serialize, Deserialize, Clone)] - pub struct DisplayControl(u16); - impl Debug; - u16; - pub mode, set_mode: 2, 0; - pub display_frame, set_display_frame: 4, 4; - pub hblank_interval_free, _: 5; - pub obj_character_vram_mapping, _: 6; - pub force_blank, _: 7; - pub enable_bg0, _ : 8; - pub enable_bg1, _ : 9; - pub enable_bg2, _ : 10; - pub enable_bg3, _ : 11; - pub enable_obj, _ : 12; - pub enable_window0, _ : 13; - pub enable_window1, _ : 14; - pub enable_obj_window, _ : 15; -} - -bitfield! { - #[derive(Serialize, Deserialize, Clone)] - pub struct DisplayStatus(u16); - impl Debug; - u16; - pub get_vblank_flag, set_vblank_flag: 0; - pub get_hblank_flag, set_hblank_flag: 1; - pub get_vcount_flag, set_vcount_flag: 2; - pub vblank_irq_enable, set_vblank_irq_enable : 3; - pub hblank_irq_enable, set_hblank_irq_enable : 4; - pub vcount_irq_enable, set_vcount_irq_enable : 5; - pub vcount_setting, set_vcount_setting : 15, 8; -} - -bitfield! { - #[derive(Serialize, Deserialize, Default, Copy, Clone)] - pub struct BgControl(u16); - impl Debug; - u16; - pub priority, _: 1, 0; - pub character_base_block, _: 3, 2; - pub mosaic, _ : 6; - pub palette256, _ : 7; - pub screen_base_block, _: 12, 8; - pub affine_wraparound, _: 13; - pub bg_size, _ : 15, 14; -} - bitfield! { #[derive(Serialize, Deserialize, Default, Copy, Clone)] pub struct RegMosaic(u16); @@ -124,7 +199,7 @@ bitfield! { bitflags! { #[derive(Serialize, Deserialize, Default)] - pub struct BlendFlags: u32 { + pub struct BlendFlags: u16 { const BG0 = 0b00000001; const BG1 = 0b00000010; const BG2 = 0b00000100; @@ -134,12 +209,6 @@ bitflags! { } } -impl From for BlendFlags { - fn from(v: u16) -> BlendFlags { - BlendFlags::from_bits(v as u32).unwrap() - } -} - impl BlendFlags { const BG_LAYER_FLAG: [BlendFlags; 4] = [ BlendFlags::BG0, @@ -147,44 +216,73 @@ impl BlendFlags { BlendFlags::BG2, BlendFlags::BG3, ]; - #[inline] pub fn from_bg(bg: usize) -> BlendFlags { Self::BG_LAYER_FLAG[bg] } - #[inline] pub fn obj_enabled(&self) -> bool { self.contains(BlendFlags::OBJ) } - + #[inline] pub fn contains_render_layer(&self, layer: &RenderLayer) -> bool { - let layer_flags = BlendFlags::from_bits(layer.kind.to_u32().unwrap()).unwrap(); + let layer_flags = BlendFlags::from_bits_truncate(layer.kind as u16); self.contains(layer_flags) } } -bitfield! { - #[derive(Serialize, Deserialize, Default, Copy, Clone)] - pub struct BlendControl(u16); - impl Debug; - pub into BlendFlags, top, _: 5, 0; - pub into BldMode, mode, set_mode: 7, 6; - pub into BlendFlags, bottom, _: 13, 8; +#[derive(SmartDefault, Debug, Serialize, Deserialize, Primitive, PartialEq, Clone, Copy)] +pub enum BlendMode { + #[default] + BldNone = 0b00, + BldAlpha = 0b01, + BldWhite = 0b10, + BldBlack = 0b11, } -bitfield! { - #[derive(Serialize, Deserialize, Default, Copy, Clone)] - pub struct BlendAlpha(u16); - impl Debug; - u16; - pub eva, _: 5, 0; - pub evb, _: 12, 8; +#[derive(Debug, Serialize, Deserialize, Default, Copy, Clone)] +pub struct BlendControl { + pub target1: BlendFlags, + pub target2: BlendFlags, + pub mode: BlendMode, +} + +impl GpuMemoryMappedIO for BlendControl { + #[inline] + fn write(&mut self, value: u16) { + self.target1 = BlendFlags::from_bits_truncate((value >> 0) & 0x3f); + self.target2 = BlendFlags::from_bits_truncate((value >> 8) & 0x3f); + self.mode = BlendMode::from_u16((value >> 6) & 0b11).unwrap_or_else(|| unreachable!()); + } + + #[inline] + fn read(&self) -> u16 { + (self.target1.bits() << 0) | (self.mode as u16) << 6 | (self.target2.bits() << 8) + } +} + +#[derive(Debug, Serialize, Deserialize, Default, Copy, Clone)] +pub struct BlendAlpha { + pub eva: u16, + pub evb: u16, +} + +impl GpuMemoryMappedIO for BlendAlpha { + #[inline] + fn write(&mut self, value: u16) { + self.eva = value & 0x1f; + self.evb = (value >> 8) & 0x1f; + } + + #[inline] + fn read(&self) -> u16 { + self.eva | self.evb << 8 + } } bitflags! { #[derive(Serialize, Deserialize, Default)] - pub struct WindowFlags: u32 { + pub struct WindowFlags: u16 { const BG0 = 0b00000001; const BG1 = 0b00000010; const BG2 = 0b00000100; @@ -196,7 +294,7 @@ bitflags! { impl From for WindowFlags { fn from(v: u16) -> WindowFlags { - WindowFlags::from_bits(v as u32).unwrap() + WindowFlags::from_bits_truncate(v) } } @@ -218,12 +316,3 @@ const BG_WIN_FLAG: [WindowFlags; 4] = [ WindowFlags::BG2, WindowFlags::BG3, ]; - -bitfield! { - #[derive(Serialize, Deserialize, Default, Copy, Clone)] - pub struct WindowReg(u16); - impl Debug; - u16; - pub into WindowFlags, lower, _: 5, 0; - pub into WindowFlags, upper, _: 13, 8; -} diff --git a/core/src/gpu/render/bitmap.rs b/core/src/gpu/render/bitmap.rs index ba9185c..8b953b9 100644 --- a/core/src/gpu/render/bitmap.rs +++ b/core/src/gpu/render/bitmap.rs @@ -16,7 +16,7 @@ impl Gpu { let pc = self.bg_aff[bg - 2].pc as i32; let ref_point = self.get_ref_point(bg); - let wraparound = self.backgrounds[bg].bgcnt.affine_wraparound(); + let wraparound = self.bgcnt[bg].affine_wraparound; for x in 0..DISPLAY_WIDTH { let mut t = utils::transform_bg_point(ref_point, x as i32, pa, pc); @@ -25,19 +25,19 @@ impl Gpu { t.0 = t.0.rem_euclid(SCREEN_VIEWPORT.w); t.1 = t.1.rem_euclid(SCREEN_VIEWPORT.h); } else { - self.backgrounds[bg].line[x] = Rgb15::TRANSPARENT; + self.bg_line[bg][x] = Rgb15::TRANSPARENT; continue; } } let pixel_index = index2d!(u32, t.0, t.1, DISPLAY_WIDTH); let pixel_ofs = 2 * pixel_index; let color = Rgb15(self.vram.read_16(pixel_ofs)); - self.backgrounds[bg].line[x] = color; + self.bg_line[bg][x] = color; } } pub(in super::super) fn render_mode4(&mut self, bg: usize) { - let page_ofs: u32 = match self.dispcnt.display_frame() { + let page_ofs: u32 = match self.dispcnt.display_frame_select { 0 => 0x0600_0000 - VRAM_ADDR, 1 => 0x0600_a000 - VRAM_ADDR, _ => unreachable!(), @@ -49,7 +49,7 @@ impl Gpu { let pc = self.bg_aff[bg - 2].pc as i32; let ref_point = self.get_ref_point(bg); - let wraparound = self.backgrounds[bg].bgcnt.affine_wraparound(); + let wraparound = self.bgcnt[bg].affine_wraparound; for x in 0..DISPLAY_WIDTH { let mut t = utils::transform_bg_point(ref_point, x as i32, pa, pc); @@ -58,7 +58,7 @@ impl Gpu { t.0 = t.0.rem_euclid(SCREEN_VIEWPORT.w); t.1 = t.1.rem_euclid(SCREEN_VIEWPORT.h); } else { - self.backgrounds[bg].line[x] = Rgb15::TRANSPARENT; + self.bg_line[bg][x] = Rgb15::TRANSPARENT; continue; } } @@ -66,12 +66,12 @@ impl Gpu { let bitmap_ofs = page_ofs + (bitmap_index as u32); let index = self.vram.read_8(bitmap_ofs) as u32; let color = self.get_palette_color(index, 0, 0); - self.backgrounds[bg].line[x] = color; + self.bg_line[bg][x] = color; } } pub(in super::super) fn render_mode5(&mut self, bg: usize) { - let page_ofs: u32 = match self.dispcnt.display_frame() { + let page_ofs: u32 = match self.dispcnt.display_frame_select { 0 => 0x0600_0000 - VRAM_ADDR, 1 => 0x0600_a000 - VRAM_ADDR, _ => unreachable!(), @@ -83,7 +83,7 @@ impl Gpu { let pc = self.bg_aff[bg - 2].pc as i32; let ref_point = self.get_ref_point(bg); - let wraparound = self.backgrounds[bg].bgcnt.affine_wraparound(); + let wraparound = self.bgcnt[bg].affine_wraparound; for x in 0..DISPLAY_WIDTH { let mut t = utils::transform_bg_point(ref_point, x as i32, pa, pc); @@ -92,13 +92,13 @@ impl Gpu { t.0 = t.0.rem_euclid(MODE5_VIEWPORT.w); t.1 = t.1.rem_euclid(MODE5_VIEWPORT.h); } else { - self.backgrounds[bg].line[x] = Rgb15::TRANSPARENT; + self.bg_line[bg][x] = Rgb15::TRANSPARENT; continue; } } let pixel_ofs = page_ofs + 2 * index2d!(u32, t.0, t.1, MODE5_VIEWPORT.w); let color = Rgb15(self.vram.read_16(pixel_ofs)); - self.backgrounds[bg].line[x] = color; + self.bg_line[bg][x] = color; } } } diff --git a/core/src/gpu/render/text.rs b/core/src/gpu/render/text.rs index 1c9078c..a7e62a6 100644 --- a/core/src/gpu/render/text.rs +++ b/core/src/gpu/render/text.rs @@ -9,15 +9,12 @@ use crate::Bus; impl Gpu { pub(in super::super) fn render_reg_bg(&mut self, bg: usize) { - let (h_ofs, v_ofs) = ( - self.backgrounds[bg].bghofs as u32, - self.backgrounds[bg].bgvofs as u32, - ); - let tileset_base = self.backgrounds[bg].bgcnt.char_block(); - let tilemap_base = self.backgrounds[bg].bgcnt.screen_block(); - let (tile_size, pixel_format) = self.backgrounds[bg].bgcnt.tile_format(); + let (h_ofs, v_ofs) = (self.bg_hofs[bg] as u32, self.bg_vofs[bg] as u32); + let tileset_base = self.bgcnt[bg].char_block(); + let tilemap_base = self.bgcnt[bg].screen_block(); + let (tile_size, pixel_format) = self.bgcnt[bg].tile_format(); - let (bg_width, bg_height) = self.backgrounds[bg].bgcnt.size_regular(); + let (bg_width, bg_height) = self.bgcnt[bg].size_regular(); let screen_y = self.vcount as u32; let mut screen_x = 0; @@ -70,7 +67,7 @@ impl Gpu { PixelFormat::BPP8 => 0u32, }; let color = self.get_palette_color(index as u32, palette_bank, 0); - self.backgrounds[bg].line[screen_x as usize] = color; + self.bg_line[bg][screen_x as usize] = color; screen_x += 1; if (DISPLAY_WIDTH as u32) == screen_x { return; @@ -96,17 +93,17 @@ impl Gpu { pub(in super::super) fn render_aff_bg(&mut self, bg: usize) { assert!(bg == 2 || bg == 3); - let texture_size = 128 << self.backgrounds[bg].bgcnt.bg_size(); + let texture_size = 128 << self.bgcnt[bg].size; let viewport = ViewPort::new(texture_size, texture_size); let ref_point = self.get_ref_point(bg); let pa = self.bg_aff[bg - 2].pa as i16 as i32; let pc = self.bg_aff[bg - 2].pc as i16 as i32; - let screen_block = self.backgrounds[bg].bgcnt.screen_block(); - let char_block = self.backgrounds[bg].bgcnt.char_block(); + let screen_block = self.bgcnt[bg].screen_block(); + let char_block = self.bgcnt[bg].char_block(); - let wraparound = self.backgrounds[bg].bgcnt.affine_wraparound(); + let wraparound = self.bgcnt[bg].affine_wraparound; for screen_x in 0..(DISPLAY_WIDTH as i32) { let mut t = utils::transform_bg_point(ref_point, screen_x, pa, pc); @@ -116,7 +113,7 @@ impl Gpu { t.0 = t.0.rem_euclid(texture_size); t.1 = t.1.rem_euclid(texture_size); } else { - self.backgrounds[bg].line[screen_x as usize] = Rgb15::TRANSPARENT; + self.bg_line[bg][screen_x as usize] = Rgb15::TRANSPARENT; continue; } } @@ -131,7 +128,7 @@ impl Gpu { PixelFormat::BPP8, ) as u32; let color = self.get_palette_color(pixel_index, 0, 0); - self.backgrounds[bg].line[screen_x as usize] = color; + self.bg_line[bg][screen_x as usize] = color; } } } diff --git a/core/src/gpu/sfx.rs b/core/src/gpu/sfx.rs index e9fd89e..692961a 100644 --- a/core/src/gpu/sfx.rs +++ b/core/src/gpu/sfx.rs @@ -1,27 +1,12 @@ use std::cmp; use arrayvec::ArrayVec; -use num::FromPrimitive; use super::regs::*; use super::layer::*; use super::*; -#[derive(Debug, Primitive, PartialEq, Clone, Copy)] -pub enum BldMode { - BldNone = 0b00, - BldAlpha = 0b01, - BldWhite = 0b10, - BldBlack = 0b11, -} - -impl From for BldMode { - fn from(v: u16) -> BldMode { - BldMode::from_u16(v).unwrap() - } -} - impl Rgb15 { fn blend_with(self, other: Rgb15, my_weight: u16, other_weight: u16) -> Rgb15 { let r = cmp::min(31, (self.r() * my_weight + other.r() * other_weight) >> 4); @@ -31,33 +16,26 @@ impl Rgb15 { } } -impl From for BlendFlags { - fn from(wf: WindowFlags) -> BlendFlags { - BlendFlags::from_bits(wf.bits()).unwrap() - } +/// Filters a background indexes array by whether they're active +fn filter_window_backgrounds( + backgrounds: &[usize], + window_flags: WindowFlags, +) -> ArrayVec<[usize; 4]> { + backgrounds + .iter() + .copied() + .filter(|bg| window_flags.bg_enabled(*bg)) + .collect() } impl Gpu { - /// Filters a background indexes array by whether they're active - fn active_backgrounds_for_window( - &self, - backgrounds: &[usize], - window_flags: WindowFlags, - ) -> ArrayVec<[usize; 4]> { - backgrounds - .iter() - .copied() - .filter(|bg| window_flags.bg_enabled(*bg)) - .collect() - } - #[allow(unused)] fn layer_to_pixel(&mut self, x: usize, y: usize, layer: &RenderLayer) -> Rgb15 { match layer.kind { - RenderLayerKind::Background0 => self.backgrounds[0].line[x], - RenderLayerKind::Background1 => self.backgrounds[1].line[x], - RenderLayerKind::Background2 => self.backgrounds[2].line[x], - RenderLayerKind::Background3 => self.backgrounds[3].line[x], + RenderLayerKind::Background0 => self.bg_line[0][x], + RenderLayerKind::Background1 => self.bg_line[1][x], + RenderLayerKind::Background2 => self.bg_line[2][x], + RenderLayerKind::Background3 => self.bg_line[3][x], RenderLayerKind::Objects => self.obj_buffer_get(x, y).color, RenderLayerKind::Backdrop => Rgb15(self.palette_ram.read_16(0)), } @@ -70,9 +48,9 @@ impl Gpu { // filter out disabled backgrounds and sort by priority // the backgrounds are sorted once for the entire scanline let mut sorted_backgrounds: ArrayVec<[usize; 4]> = (bg_start..=bg_end) - .filter(|bg| self.dispcnt.enable_bg(*bg)) + .filter(|bg| self.dispcnt.enable_bg[*bg]) .collect(); - sorted_backgrounds.sort_by_key(|bg| (self.backgrounds[*bg].bgcnt.priority(), *bg)); + sorted_backgrounds.sort_by_key(|bg| (self.bgcnt[*bg].priority, *bg)); let y = self.vcount; @@ -84,10 +62,9 @@ impl Gpu { } else { let mut occupied = [false; DISPLAY_WIDTH]; let mut occupied_count = 0; - if self.dispcnt.enable_window0() && self.win0.contains_y(y) { + if self.dispcnt.enable_window0 && self.win0.contains_y(y) { let win = WindowInfo::new(WindowType::Win0, self.win0.flags); - let backgrounds = - self.active_backgrounds_for_window(&sorted_backgrounds, win.flags); + let backgrounds = filter_window_backgrounds(&sorted_backgrounds, win.flags); for x in self.win0.left()..self.win0.right() { self.finalize_pixel(x, y, &win, &backgrounds, backdrop_color); occupied[x] = true; @@ -97,10 +74,9 @@ impl Gpu { if occupied_count == DISPLAY_WIDTH { return; } - if self.dispcnt.enable_window1() && self.win1.contains_y(y) { + if self.dispcnt.enable_window1 && self.win1.contains_y(y) { let win = WindowInfo::new(WindowType::Win1, self.win1.flags); - let backgrounds = - self.active_backgrounds_for_window(&sorted_backgrounds, win.flags); + let backgrounds = filter_window_backgrounds(&sorted_backgrounds, win.flags); for x in self.win1.left()..self.win1.right() { if occupied[x] { continue; @@ -114,12 +90,11 @@ impl Gpu { return; } let win_out = WindowInfo::new(WindowType::WinOut, self.winout_flags); - let win_out_backgrounds = - self.active_backgrounds_for_window(&sorted_backgrounds, win_out.flags); - if self.dispcnt.enable_obj_window() { + let win_out_backgrounds = filter_window_backgrounds(&sorted_backgrounds, win_out.flags); + if self.dispcnt.enable_obj_window { let win_obj = WindowInfo::new(WindowType::WinObj, self.winobj_flags); let win_obj_backgrounds = - self.active_backgrounds_for_window(&sorted_backgrounds, win_obj.flags); + filter_window_backgrounds(&sorted_backgrounds, win_obj.flags); for x in 0..DISPLAY_WIDTH { if occupied[x] { continue; @@ -128,13 +103,9 @@ impl Gpu { if obj_entry.window { // WinObj self.finalize_pixel(x, y, &win_obj, &win_obj_backgrounds, backdrop_color); - // occupied[x] = true; - // occupied_count += 1; } else { // WinOut self.finalize_pixel(x, y, &win_out, &win_out_backgrounds, backdrop_color); - // occupied[x] = true; - // occupied_count += 1; } } } else { @@ -143,8 +114,6 @@ impl Gpu { continue; } self.finalize_pixel(x, y, &win_out, &win_out_backgrounds, backdrop_color); - // occupied[x] = true; - // occupied_count += 1; } } } @@ -170,25 +139,22 @@ impl Gpu { // lets start by taking the first 2 backgrounds that have an opaque pixel at x let mut it = backgrounds .iter() - .filter(|i| !self.backgrounds[**i].line[x].is_transparent()) + .filter(|i| !self.bg_line[**i][x].is_transparent()) .take(2); let mut top_layer = it.next().map_or(backdrop_layer, |bg| { - let background = &self.backgrounds[*bg]; - RenderLayer::background(*bg, background.pixel_at(x), background.get_priority()) + RenderLayer::background(*bg, self.bg_line[*bg][x], self.bgcnt[*bg].priority) }); let mut bot_layer = it.next().map_or(backdrop_layer, |bg| { - let background = &self.backgrounds[*bg]; - RenderLayer::background(*bg, background.pixel_at(x), background.get_priority()) + RenderLayer::background(*bg, self.bg_line[*bg][x], self.bgcnt[*bg].priority) }); drop(it); // Now that backgrounds are taken care of, we need to check if there is an object pixel that takes priority of one of the layers let obj_entry = self.obj_buffer_get(x, y); - if win.flags.obj_enabled() && self.dispcnt.enable_obj() && !obj_entry.color.is_transparent() - { + if win.flags.obj_enabled() && self.dispcnt.enable_obj && !obj_entry.color.is_transparent() { let obj_layer = RenderLayer::objects(obj_entry.color, obj_entry.priority); if obj_layer.priority <= top_layer.priority { bot_layer = top_layer; @@ -201,10 +167,10 @@ impl Gpu { let obj_entry = self.obj_buffer_get(x, y); let obj_alpha_blend = top_layer.is_object() && obj_entry.alpha; - let top_flags = self.bldcnt.top(); - let bot_flags = self.bldcnt.bottom(); + let top_flags = self.bldcnt.target1; + let bot_flags = self.bldcnt.target2; - let sfx_enabled = (self.bldcnt.mode() != BldMode::BldNone || obj_alpha_blend) + let sfx_enabled = (self.bldcnt.mode != BlendMode::BldNone || obj_alpha_blend) && top_flags.contains_render_layer(&top_layer); // sfx must at least have a first target configured if win.flags.sfx_enabled() && sfx_enabled { @@ -216,8 +182,8 @@ impl Gpu { } else { let (top_layer, bot_layer) = (top_layer, bot_layer); - match self.bldcnt.mode() { - BldMode::BldAlpha => { + match self.bldcnt.mode { + BlendMode::BldAlpha => { output[x] = if bot_flags.contains_render_layer(&bot_layer) { self.do_alpha(top_layer.pixel, bot_layer.pixel).to_rgb24() } else { @@ -225,11 +191,11 @@ impl Gpu { top_layer.pixel.to_rgb24() } } - BldMode::BldWhite => output[x] = self.do_brighten(top_layer.pixel).to_rgb24(), + BlendMode::BldWhite => output[x] = self.do_brighten(top_layer.pixel).to_rgb24(), - BldMode::BldBlack => output[x] = self.do_darken(top_layer.pixel).to_rgb24(), + BlendMode::BldBlack => output[x] = self.do_darken(top_layer.pixel).to_rgb24(), - BldMode::BldNone => output[x] = top_layer.pixel.to_rgb24(), + BlendMode::BldNone => output[x] = top_layer.pixel.to_rgb24(), } } } else { @@ -240,8 +206,8 @@ impl Gpu { #[inline] fn do_alpha(&self, upper: Rgb15, lower: Rgb15) -> Rgb15 { - let eva = self.bldalpha.eva(); - let evb = self.bldalpha.evb(); + let eva = self.bldalpha.eva; + let evb = self.bldalpha.evb; upper.blend_with(lower, eva, evb) } diff --git a/core/src/iodev.rs b/core/src/iodev.rs index d150cde..cc9d811 100644 --- a/core/src/iodev.rs +++ b/core/src/iodev.rs @@ -2,6 +2,7 @@ use std::cmp; use super::bus::*; use super::dma::DmaController; +use super::gpu::regs::GpuMemoryMappedIO; use super::gpu::regs::WindowFlags; use super::gpu::*; use super::interrupt::{InterruptConnect, InterruptController, SharedInterruptFlags}; @@ -99,13 +100,13 @@ impl Bus for IoDevices { // } match io_addr { - REG_DISPCNT => io.gpu.dispcnt.0, - REG_DISPSTAT => io.gpu.dispstat.0, + REG_DISPCNT => io.gpu.dispcnt.read(), + REG_DISPSTAT => io.gpu.dispstat.read(), REG_VCOUNT => io.gpu.vcount as u16, - REG_BG0CNT => io.gpu.backgrounds[0].bgcnt.0, - REG_BG1CNT => io.gpu.backgrounds[1].bgcnt.0, - REG_BG2CNT => io.gpu.backgrounds[2].bgcnt.0, - REG_BG3CNT => io.gpu.backgrounds[3].bgcnt.0, + REG_BG0CNT => io.gpu.bgcnt[0].read(), + REG_BG1CNT => io.gpu.bgcnt[1].read(), + REG_BG2CNT => io.gpu.bgcnt[2].read(), + REG_BG3CNT => io.gpu.bgcnt[3].read(), REG_WIN0H => ((io.gpu.win0.left as u16) << 8 | (io.gpu.win0.right as u16)), REG_WIN1H => ((io.gpu.win1.left as u16) << 8 | (io.gpu.win1.right as u16)), REG_WIN0V => ((io.gpu.win0.top as u16) << 8 | (io.gpu.win0.bottom as u16)), @@ -116,8 +117,8 @@ impl Bus for IoDevices { REG_WINOUT => { ((io.gpu.winobj_flags.bits() as u16) << 8) | (io.gpu.winout_flags.bits() as u16) } - REG_BLDCNT => io.gpu.bldcnt.0, - REG_BLDALPHA => io.gpu.bldalpha.0, + REG_BLDCNT => io.gpu.bldcnt.read(), + REG_BLDALPHA => io.gpu.bldalpha.read(), REG_IME => io.intc.interrupt_master_enable as u16, REG_IE => io.intc.interrupt_enable.0 as u16, @@ -187,19 +188,19 @@ impl Bus for IoDevices { match io_addr { REG_DISPCNT => io.gpu.write_dispcnt(value), - REG_DISPSTAT => io.gpu.dispstat.0 = value | (io.gpu.dispstat.0 & 7), - REG_BG0CNT => io.gpu.backgrounds[0].bgcnt.0 = value, - REG_BG1CNT => io.gpu.backgrounds[1].bgcnt.0 = value, - REG_BG2CNT => io.gpu.backgrounds[2].bgcnt.0 = value, - REG_BG3CNT => io.gpu.backgrounds[3].bgcnt.0 = value, - REG_BG0HOFS => io.gpu.backgrounds[0].bghofs = value & 0x1ff, - REG_BG0VOFS => io.gpu.backgrounds[0].bgvofs = value & 0x1ff, - REG_BG1HOFS => io.gpu.backgrounds[1].bghofs = value & 0x1ff, - REG_BG1VOFS => io.gpu.backgrounds[1].bgvofs = value & 0x1ff, - REG_BG2HOFS => io.gpu.backgrounds[2].bghofs = value & 0x1ff, - REG_BG2VOFS => io.gpu.backgrounds[2].bgvofs = value & 0x1ff, - REG_BG3HOFS => io.gpu.backgrounds[3].bghofs = value & 0x1ff, - REG_BG3VOFS => io.gpu.backgrounds[3].bgvofs = value & 0x1ff, + REG_DISPSTAT => io.gpu.dispstat.write(value), + REG_BG0CNT => io.gpu.bgcnt[0].write(value), + REG_BG1CNT => io.gpu.bgcnt[1].write(value), + REG_BG2CNT => io.gpu.bgcnt[2].write(value), + REG_BG3CNT => io.gpu.bgcnt[3].write(value), + REG_BG0HOFS => io.gpu.bg_hofs[0] = value & 0x1ff, + REG_BG0VOFS => io.gpu.bg_vofs[0] = value & 0x1ff, + REG_BG1HOFS => io.gpu.bg_hofs[1] = value & 0x1ff, + REG_BG1VOFS => io.gpu.bg_vofs[1] = value & 0x1ff, + REG_BG2HOFS => io.gpu.bg_hofs[2] = value & 0x1ff, + REG_BG2VOFS => io.gpu.bg_vofs[2] = value & 0x1ff, + REG_BG3HOFS => io.gpu.bg_hofs[3] = value & 0x1ff, + REG_BG3VOFS => io.gpu.bg_vofs[3] = value & 0x1ff, REG_BG2X_L | REG_BG3X_L => write_reference_point!(low bg x internal_x), REG_BG2Y_L | REG_BG3Y_L => write_reference_point!(low bg y internal_y), REG_BG2X_H | REG_BG3X_H => write_reference_point!(high bg x internal_x), @@ -247,8 +248,8 @@ impl Bus for IoDevices { io.gpu.winobj_flags = WindowFlags::from(value >> 8); } REG_MOSAIC => io.gpu.mosaic.0 = value, - REG_BLDCNT => io.gpu.bldcnt.0 = value, - REG_BLDALPHA => io.gpu.bldalpha.0 = value, + REG_BLDCNT => io.gpu.bldcnt.write(value), + REG_BLDALPHA => io.gpu.bldalpha.write(value), REG_BLDY => io.gpu.bldy = cmp::min(value & 0b11111, 16), REG_IME => io.intc.interrupt_master_enable = value != 0, diff --git a/core/src/lib.rs b/core/src/lib.rs index cb6c00e..f34c4d3 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -26,6 +26,9 @@ extern crate log; #[macro_use] extern crate hex_literal; +#[macro_use] +extern crate smart_default; + extern crate cfg_if; use zip; diff --git a/core/src/sched.rs b/core/src/sched.rs index 598bf07..0221ab4 100644 --- a/core/src/sched.rs +++ b/core/src/sched.rs @@ -128,6 +128,18 @@ impl Scheduler { SharedScheduler::new(self) } + #[inline] + #[allow(unused)] + pub fn num_pending_events(&self) -> usize { + self.events.len() + } + + #[inline] + #[allow(unused)] + pub fn peek_next(&self) -> Option { + self.events.peek().map(|e| e.typ) + } + /// Schedule an event to be executed in `cycles` cycles from now pub fn push(&mut self, typ: EventType, cycles: usize) { let event = Event::new(typ, self.timestamp + cycles);