#[allow(unused_imports)] use log::{debug, error, info, trace, warn}; use std::{ rc::Rc, cell::RefCell, }; use bitflags::bitflags; use crate::{ 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; } } 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; } } bitflags! { 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, // outputs screen: Rc>, pattern_table: Rc>, } impl Peripheral for Ppu { fn read_addr(&self, _addr: u16) -> 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 { 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, screen, pattern_table, } } 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; self.screen.borrow_mut() .set_pixel( self.dot.into(), self.line.into(), Pixel::rgba(self.data.into(), 0, 0, 255)); self.execute(); } fn execute(&mut self) { 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(); //last line, got back to first line self.line = 0; } 240..=260 => (), //vertical blank _ => panic!("{}: Invalid line number", self.line), } 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; } } } fn render_frame(&mut self) { match self.dot { 0 => (), 33..=40 => (), _ => (), } } }