diff --git a/src/peripherals/ppu.rs b/src/peripherals/ppu.rs index 0d5e026..5c70738 100644 --- a/src/peripherals/ppu.rs +++ b/src/peripherals/ppu.rs @@ -2,173 +2,259 @@ use log::{debug, error, info, trace, warn}; use std::{ - rc::Rc, - cell::RefCell, + rc::Rc, + cell::RefCell, }; use bitflags::bitflags; use crate::{ - peripherals::{Peripheral, Ram, Mapper}, - utils::DisplayBuffer, + peripherals::{Peripheral, Ram, Mapper}, + utils::DisplayBuffer, }; //-------------------------------------------------------------------------------------------------- bitflags! { - struct PpuCtrl: u8 { - const Nl = 0x1 << 0; - const Nh = 0x1 << 1; - const I = 0x1 << 2; - const S = 0x1 << 3; - const B = 0x1 << 4; - const H = 0x1 << 5; - const P = 0x1 << 6; - const V = 0x1 << 7; - } + struct PpuCtrl: u8 { + const NL = 0x1 << 0; + const NH = 0x1 << 1; + const I = 0x1 << 2; + const S = 0x1 << 3; + const B = 0x1 << 4; + const H = 0x1 << 5; + const P = 0x1 << 6; + const V = 0x1 << 7; + } } bitflags! { - struct PpuMask: u8 { - const Gr = 0x1 << 0; - const m = 0x1 << 1; - const M = 0x1 << 2; - const b = 0x1 << 3; - const s = 0x1 << 4; - const R = 0x1 << 5; - const G = 0x1 << 6; - const B = 0x1 << 7; - } + struct PpuMask: u8 { + const GR = 0x1 << 0; + const M_ = 0x1 << 1; + const M = 0x1 << 2; + const B_ = 0x1 << 3; + const S_ = 0x1 << 4; + const R = 0x1 << 5; + const G = 0x1 << 6; + const B = 0x1 << 7; + } } bitflags! { - struct PpuStatus: u8 { - const O = 0x1 << 5; - const S = 0x1 << 6; - const V = 0x1 << 7; - } + struct PpuStatus: u8 { + const O = 0x1 << 5; + const S = 0x1 << 6; + const V = 0x1 << 7; + } } #[allow(dead_code)] pub struct Ppu { - vbus: Rc>, - ppu_ctrl: PpuCtrl, - ppu_mask: PpuMask, - ppu_status: PpuStatus, - scroll: u8, - addr: u8, - data: u8, - oam_addr: u8, - oam_data: u8, - oam_dma: u8, - oam: Ram<0x100>, - blanking: bool, - line: u16, - dot: u16, - nmi: bool, + //peripherals + vbus: Rc>, - // outputs - screen: Rc>, - pattern_table: Rc>, + //public registers + ppu_ctrl: PpuCtrl, + ppu_mask: PpuMask, + ppu_status: PpuStatus, + + scroll: u8, + addr: u8, + data: u8, + + //OAM + oam_addr: u8, + oam_data: u8, + oam_dma: u8, + oam: Ram<0x100>, + + blanking: bool, + line: u16, + dot: u16, + nmi: bool, + odd: bool, + + // outputs + screen: Rc>, + pattern_table: Rc>, } impl Peripheral for Ppu { - fn read_addr(&self, _addr: u16) -> u8 { - unimplemented!(); - } + fn read_addr(&self, _addr: u16) -> u8 { + unimplemented!(); + } - fn write_addr(&mut self, _addr: u16, _data: u8) { - unimplemented!(); - } + fn write_addr(&mut self, _addr: u16, _data: u8) { + unimplemented!(); + } } impl Ppu { - pub fn new(mapper: Rc>, - screen: Rc>, - pattern_table: Rc>) - -> Self { + pub fn new(mapper: Rc>, + screen: Rc>, + pattern_table: Rc>) + -> Self { - Self { - vbus: mapper, - ppu_ctrl: PpuCtrl::from_bits(0).unwrap(), - ppu_mask: PpuMask::from_bits(0).unwrap(), - ppu_status: PpuStatus::from_bits(0).unwrap(), - scroll: 0, - addr: 0, - data: 0, - oam_addr: 0, - oam_data: 0, - oam_dma: 0, - oam: Ram::<0x100>::new(), - blanking: false, - line: 0, - dot: 0, - nmi: false, + Self { + vbus: mapper, + ppu_ctrl: PpuCtrl::from_bits(0).unwrap(), + ppu_mask: PpuMask::from_bits(0).unwrap(), + ppu_status: PpuStatus::from_bits(0).unwrap(), + scroll: 0, + addr: 0, + data: 0, + oam_addr: 0, + oam_data: 0, + oam_dma: 0, + oam: Ram::<0x100>::new(), + blanking: false, + line: 0, + dot: 0, + nmi: false, + odd: false, - screen, - pattern_table, - } - } + screen, + pattern_table, + } + } - pub fn poll_nmi(&mut self) -> bool { - let ret = self.nmi; - if self.nmi { - self.nmi = false; - } - ret - } + pub fn poll_nmi(&mut self) -> bool { + let ret = self.nmi; + if self.nmi { + self.nmi = false; + } + ret + } - pub fn tick(&mut self) { - use crate::utils::Pixel; + pub fn tick(&mut self) { + use crate::utils::Pixel; - self.screen.borrow_mut() - .set_pixel( - self.dot.into(), - self.line.into(), - Pixel::rgba(self.data.into(), 0, 0, 255)); +// self.screen.borrow_mut() +// .set_pixel( +// self.dot.into(), +// self.line.into(), +// Pixel::rgba(self.data.into(), 0, 0, 255)); +// + self.execute(); + } - self.execute(); - } + fn execute(&mut self) { - fn execute(&mut self) { + match self.line { + 0..=239 => self.visible_line(), + 241 => { + //start of vertical blank + self.post_render_line(); + if self.dot == 1 { + } + } + 261 => { + //end of vertical blank + self.pre_render_line(); + } + 240..=260 => (), //vertical blank + _ => panic!("{}: Invalid line number", self.line), + } - match self.line { - 0..=239 => self.render_frame(), - 241 => { - if self.dot == 1 { - self.ppu_status.set(PpuStatus::V, true); - } - } - 261 => { - if self.dot == 1 { - self.ppu_status.set(PpuStatus::V | PpuStatus::S | PpuStatus::O, false); - } - self.render_frame(); + //increment current dot and line + self.dot += 1; + if self.dot > 340 { + self.dot = 0; + self.line += 1; + if self.line > 261 { + self.line = 0; + } + } + } - //last line, got back to first line - self.line = 0; - } - 240..=260 => (), //vertical blank - _ => panic!("{}: Invalid line number", self.line), - } + fn visible_line(&mut self) { + match self.dot { + 0 => (), //nothing done on first cycle - self.dot = (self.dot + 1) % 256; - if self.dot == 0 { - self.line = (self.line + 1) % 240; - if self.line == 0 { - self.screen.borrow_mut().is_ready = true; - self.data = (self.data + 1) % 255; - } - } - } + 1..=256 | 321..=336 => { + //background + match self.dot % 8 { + 0 => (), //render inc hori v, BG msbit + 2 => (), //load NT + 4 => (), //load AT + 6 => (), //BG lsbit + _ => (), //idle clock cycle + } + //OAM + match self.dot { + 1..=64 => (), //secondary OAM clear + 65..=256 => (), //sprite evalutation for next scanline + _ => (), //idle clock cycle + } + }, + 257 => (), // hori(v) = hori(t) - fn render_frame(&mut self) { + //sprites + 258..=320 => { + //sprites + match self.dot % 8 { + 0 => (), //sprite msbit + 2 => (), //garbage NT + 4 => (), //garbageAT + 6 => (), //sprite lsbit + _ => (), //idle clock cycle + } + }, + + _ => (), //idle or unused clock cycles + } + } + + fn pre_render_line(&mut self) { + match self.dot { + 0 => (), + + //background + 1 => { + //clear vblank, sprite 0, overflow + self.ppu_status.set(PpuStatus::V | PpuStatus::S | PpuStatus::O, false); + }, + 2..=248 | 321..=336 => match self.dot % 8 { + 0 => (), //render inc hori v, BG msbit + 2 => (), //load NT + 4 => (), //load AT + 6 => (), //BG lsbit + _ => (), //idle clock cycle + } + 257 => (), // hori(v) = hori(t) + + 258..=320 => { + //sprites + match self.dot % 8 { + 0 => (), //sprite msbit + 2 => (), //garbage NT + 4 => (), //garbageAT + 6 => (), //sprite lsbit + _ => (), //idle clock cycle + } + //miscelaneous + match self.dot { + 280..=304 => (), //vert(v) = vert(t) + _ => (), //idle clock cycle + } + }, + + 339 => if self.odd { self.dot = 340 }, //skip on clock cycle every 2 frames + + _ => (), //idle or unused clock cycles + } + } + + fn post_render_line(&mut self) { + match self.dot { + 1 => { + self.ppu_status.set(PpuStatus::V, true); + self.screen.borrow_mut().is_ready = true; //screen can be displayed + }, + _ => (), //idle or unused clock cycles + } + } - match self.dot { - 0 => (), - 33..=40 => (), - _ => (), - } - } }