175 lines
3.8 KiB
Rust
175 lines
3.8 KiB
Rust
#[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<M: Mapper> {
|
|
vbus: Rc<RefCell<M>>,
|
|
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<RefCell<DisplayBuffer>>,
|
|
pattern_table: Rc<RefCell<DisplayBuffer>>,
|
|
}
|
|
|
|
impl<M: Mapper> Peripheral for Ppu<M> {
|
|
|
|
fn read_addr(&self, _addr: u16) -> u8 {
|
|
unimplemented!();
|
|
}
|
|
|
|
fn write_addr(&mut self, _addr: u16, _data: u8) {
|
|
unimplemented!();
|
|
}
|
|
}
|
|
|
|
impl<M: Mapper> Ppu<M> {
|
|
|
|
pub fn new(mapper: Rc<RefCell<M>>,
|
|
screen: Rc<RefCell<DisplayBuffer>>,
|
|
pattern_table: Rc<RefCell<DisplayBuffer>>)
|
|
-> 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 => (),
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|