Create render mecanism's squeleton

This commit is contained in:
Steins7 2024-03-10 22:48:57 +01:00
parent af35cfc712
commit 25338bc0ee

View File

@ -2,173 +2,259 @@
use log::{debug, error, info, trace, warn}; use log::{debug, error, info, trace, warn};
use std::{ use std::{
rc::Rc, rc::Rc,
cell::RefCell, cell::RefCell,
}; };
use bitflags::bitflags; use bitflags::bitflags;
use crate::{ use crate::{
peripherals::{Peripheral, Ram, Mapper}, peripherals::{Peripheral, Ram, Mapper},
utils::DisplayBuffer, utils::DisplayBuffer,
}; };
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
bitflags! { bitflags! {
struct PpuCtrl: u8 { struct PpuCtrl: u8 {
const Nl = 0x1 << 0; const NL = 0x1 << 0;
const Nh = 0x1 << 1; const NH = 0x1 << 1;
const I = 0x1 << 2; const I = 0x1 << 2;
const S = 0x1 << 3; const S = 0x1 << 3;
const B = 0x1 << 4; const B = 0x1 << 4;
const H = 0x1 << 5; const H = 0x1 << 5;
const P = 0x1 << 6; const P = 0x1 << 6;
const V = 0x1 << 7; const V = 0x1 << 7;
} }
} }
bitflags! { bitflags! {
struct PpuMask: u8 { struct PpuMask: u8 {
const Gr = 0x1 << 0; const GR = 0x1 << 0;
const m = 0x1 << 1; const M_ = 0x1 << 1;
const M = 0x1 << 2; const M = 0x1 << 2;
const b = 0x1 << 3; const B_ = 0x1 << 3;
const s = 0x1 << 4; const S_ = 0x1 << 4;
const R = 0x1 << 5; const R = 0x1 << 5;
const G = 0x1 << 6; const G = 0x1 << 6;
const B = 0x1 << 7; const B = 0x1 << 7;
} }
} }
bitflags! { bitflags! {
struct PpuStatus: u8 { struct PpuStatus: u8 {
const O = 0x1 << 5; const O = 0x1 << 5;
const S = 0x1 << 6; const S = 0x1 << 6;
const V = 0x1 << 7; const V = 0x1 << 7;
} }
} }
#[allow(dead_code)] #[allow(dead_code)]
pub struct Ppu<M: Mapper> { pub struct Ppu<M: Mapper> {
vbus: Rc<RefCell<M>>, //peripherals
ppu_ctrl: PpuCtrl, vbus: Rc<RefCell<M>>,
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 //public registers
screen: Rc<RefCell<DisplayBuffer>>, ppu_ctrl: PpuCtrl,
pattern_table: Rc<RefCell<DisplayBuffer>>, 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<RefCell<DisplayBuffer>>,
pattern_table: Rc<RefCell<DisplayBuffer>>,
} }
impl<M: Mapper> Peripheral for Ppu<M> { impl<M: Mapper> Peripheral for Ppu<M> {
fn read_addr(&self, _addr: u16) -> u8 { fn read_addr(&self, _addr: u16) -> u8 {
unimplemented!(); unimplemented!();
} }
fn write_addr(&mut self, _addr: u16, _data: u8) { fn write_addr(&mut self, _addr: u16, _data: u8) {
unimplemented!(); unimplemented!();
} }
} }
impl<M: Mapper> Ppu<M> { impl<M: Mapper> Ppu<M> {
pub fn new(mapper: Rc<RefCell<M>>, pub fn new(mapper: Rc<RefCell<M>>,
screen: Rc<RefCell<DisplayBuffer>>, screen: Rc<RefCell<DisplayBuffer>>,
pattern_table: Rc<RefCell<DisplayBuffer>>) pattern_table: Rc<RefCell<DisplayBuffer>>)
-> Self { -> Self {
Self { Self {
vbus: mapper, vbus: mapper,
ppu_ctrl: PpuCtrl::from_bits(0).unwrap(), ppu_ctrl: PpuCtrl::from_bits(0).unwrap(),
ppu_mask: PpuMask::from_bits(0).unwrap(), ppu_mask: PpuMask::from_bits(0).unwrap(),
ppu_status: PpuStatus::from_bits(0).unwrap(), ppu_status: PpuStatus::from_bits(0).unwrap(),
scroll: 0, scroll: 0,
addr: 0, addr: 0,
data: 0, data: 0,
oam_addr: 0, oam_addr: 0,
oam_data: 0, oam_data: 0,
oam_dma: 0, oam_dma: 0,
oam: Ram::<0x100>::new(), oam: Ram::<0x100>::new(),
blanking: false, blanking: false,
line: 0, line: 0,
dot: 0, dot: 0,
nmi: false, nmi: false,
odd: false,
screen, screen,
pattern_table, pattern_table,
} }
} }
pub fn poll_nmi(&mut self) -> bool { pub fn poll_nmi(&mut self) -> bool {
let ret = self.nmi; let ret = self.nmi;
if self.nmi { if self.nmi {
self.nmi = false; self.nmi = false;
} }
ret ret
} }
pub fn tick(&mut self) { pub fn tick(&mut self) {
use crate::utils::Pixel; use crate::utils::Pixel;
self.screen.borrow_mut() // self.screen.borrow_mut()
.set_pixel( // .set_pixel(
self.dot.into(), // self.dot.into(),
self.line.into(), // self.line.into(),
Pixel::rgba(self.data.into(), 0, 0, 255)); // 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 { //increment current dot and line
0..=239 => self.render_frame(), self.dot += 1;
241 => { if self.dot > 340 {
if self.dot == 1 { self.dot = 0;
self.ppu_status.set(PpuStatus::V, true); self.line += 1;
} if self.line > 261 {
} self.line = 0;
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 fn visible_line(&mut self) {
self.line = 0; match self.dot {
} 0 => (), //nothing done on first cycle
240..=260 => (), //vertical blank
_ => panic!("{}: Invalid line number", self.line),
}
self.dot = (self.dot + 1) % 256; 1..=256 | 321..=336 => {
if self.dot == 0 { //background
self.line = (self.line + 1) % 240; match self.dot % 8 {
if self.line == 0 { 0 => (), //render inc hori v, BG msbit
self.screen.borrow_mut().is_ready = true; 2 => (), //load NT
self.data = (self.data + 1) % 255; 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 => (),
_ => (),
}
}
} }