Create render mecanism's squeleton
This commit is contained in:
parent
af35cfc712
commit
25338bc0ee
@ -16,8 +16,8 @@ use crate::{
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
bitflags! {
|
||||
struct PpuCtrl: u8 {
|
||||
const Nl = 0x1 << 0;
|
||||
const Nh = 0x1 << 1;
|
||||
const NL = 0x1 << 0;
|
||||
const NH = 0x1 << 1;
|
||||
const I = 0x1 << 2;
|
||||
const S = 0x1 << 3;
|
||||
const B = 0x1 << 4;
|
||||
@ -29,11 +29,11 @@ bitflags! {
|
||||
|
||||
bitflags! {
|
||||
struct PpuMask: u8 {
|
||||
const Gr = 0x1 << 0;
|
||||
const m = 0x1 << 1;
|
||||
const GR = 0x1 << 0;
|
||||
const M_ = 0x1 << 1;
|
||||
const M = 0x1 << 2;
|
||||
const b = 0x1 << 3;
|
||||
const s = 0x1 << 4;
|
||||
const B_ = 0x1 << 3;
|
||||
const S_ = 0x1 << 4;
|
||||
const R = 0x1 << 5;
|
||||
const G = 0x1 << 6;
|
||||
const B = 0x1 << 7;
|
||||
@ -50,21 +50,29 @@ bitflags! {
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Ppu<M: Mapper> {
|
||||
//peripherals
|
||||
vbus: Rc<RefCell<M>>,
|
||||
|
||||
//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<RefCell<DisplayBuffer>>,
|
||||
@ -105,6 +113,7 @@ impl<M: Mapper> Ppu<M> {
|
||||
line: 0,
|
||||
dot: 0,
|
||||
nmi: false,
|
||||
odd: false,
|
||||
|
||||
screen,
|
||||
pattern_table,
|
||||
@ -122,53 +131,130 @@ impl<M: Mapper> Ppu<M> {
|
||||
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();
|
||||
}
|
||||
|
||||
fn execute(&mut self) {
|
||||
|
||||
match self.line {
|
||||
0..=239 => self.render_frame(),
|
||||
0..=239 => self.visible_line(),
|
||||
241 => {
|
||||
//start of vertical blank
|
||||
self.post_render_line();
|
||||
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;
|
||||
//end of vertical blank
|
||||
self.pre_render_line();
|
||||
}
|
||||
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;
|
||||
//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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render_frame(&mut self) {
|
||||
fn visible_line(&mut self) {
|
||||
match self.dot {
|
||||
0 => (), //nothing done on first cycle
|
||||
|
||||
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)
|
||||
|
||||
//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 => (),
|
||||
33..=40 => (),
|
||||
_ => (),
|
||||
|
||||
//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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user