chore(ppu): reimplement background/sprite fetcher state machine

This commit is contained in:
Rekai Nyangadzayi Musuka 2021-04-27 04:57:00 -05:00
parent 9003617459
commit 7620633116
2 changed files with 98 additions and 100 deletions

View File

@ -6,7 +6,7 @@ use std::convert::TryInto;
use registers::{ use registers::{
BackgroundPalette, GrayShade, LCDControl, LCDStatus, ObjectFlags, ObjectPalette, BackgroundPalette, GrayShade, LCDControl, LCDStatus, ObjectFlags, ObjectPalette,
ObjectPaletteId, ObjectSize, PpuMode, RenderPriority, TileDataAddress, TwoBitsPerPixel, ObjectPaletteId, PpuMode, RenderPriority, TileDataAddress, TwoBitsPerPixel,
}; };
mod registers; mod registers;
@ -170,10 +170,7 @@ impl Ppu {
// This is run 50% of the time, or 40 times // This is run 50% of the time, or 40 times
// which is the number of sprites in OAM // which is the number of sprites in OAM
let sprite_height = match self.control.obj_size() { let sprite_height = self.control.obj_size().as_u8();
ObjectSize::Eight => 8,
ObjectSize::Sixteen => 16,
};
let attr = self.oam.attribute((cycle / 2) as usize); let attr = self.oam.attribute((cycle / 2) as usize);
let line_y = self.pos.line_y + 16; let line_y = self.pos.line_y + 16;
@ -215,52 +212,40 @@ impl Ppu {
if let Some(attr) = obj_attr { if let Some(attr) = obj_attr {
match self.fetcher.obj.state { match self.fetcher.obj.state {
TileNumber => { TileNumber => {
if self.clock == TimingClock::Tick {
self.fetcher.obj.tile.with_id(attr.tile_index); self.fetcher.obj.tile.with_id(attr.tile_index);
self.fetcher.obj.next(SleepZero);
self.fetcher.obj.next(TileDataLow);
}
} }
SleepZero => self.fetcher.obj.next(TileDataLow),
TileDataLow => { TileDataLow => {
if self.clock == TimingClock::Tick { let obj_size = self.control.obj_size().as_u8();
let obj_size = match self.control.obj_size() {
ObjectSize::Eight => 8,
ObjectSize::Sixteen => 16,
};
let addr = PixelFetcher::get_obj_low_addr(&attr, &self.pos, obj_size); let addr = PixelFetcher::get_obj_low_addr(&attr, &self.pos, obj_size);
let byte = self.read_byte(addr); let byte = self.read_byte(addr);
self.fetcher.obj.tile.with_low_byte(byte); self.fetcher.obj.tile.with_low_byte(byte);
self.fetcher.obj.next(TileDataHigh); self.fetcher.obj.next(SleepOne);
}
} }
SleepOne => self.fetcher.obj.next(TileDataHigh),
TileDataHigh => { TileDataHigh => {
if self.clock == TimingClock::Tick { let obj_size = self.control.obj_size().as_u8();
let obj_size = match self.control.obj_size() {
ObjectSize::Eight => 8,
ObjectSize::Sixteen => 16,
};
let addr = PixelFetcher::get_obj_low_addr(&attr, &self.pos, obj_size); let addr = PixelFetcher::get_obj_low_addr(&attr, &self.pos, obj_size);
let byte = self.read_byte(addr + 1); let byte = self.read_byte(addr + 1);
self.fetcher.obj.tile.with_high_byte(byte); self.fetcher.obj.tile.with_high_byte(byte);
self.fetcher.obj.next(SendToFifo); self.fetcher.obj.next(SleepTwo);
} }
} SleepTwo => self.fetcher.obj.next(SendToFifoOne),
SendToFifo => { SendToFifoOne => {
self.fetcher.obj.fifo_count += 1;
if self.fetcher.obj.fifo_count == 1 {
// Load into Fifo // Load into Fifo
let tile_bytes = self.fetcher.obj.tile.low.zip(self.fetcher.obj.tile.high); let maybe_low = self.fetcher.obj.tile.low;
let maybe_high = self.fetcher.obj.tile.high;
if let Some(bytes) = tile_bytes { let (low, high) = maybe_low
let low = bytes.0; .zip(maybe_high)
let high = bytes.1; .expect("Low & High Bytes in TileBuilder were unexpectedly missing.");
let pixel = TwoBitsPerPixel::from_bytes(high, low); let pixel = TwoBitsPerPixel::from_bytes(high, low);
@ -290,18 +275,14 @@ impl Ppu {
self.fetcher.bg.resume(); self.fetcher.bg.resume();
self.fifo.resume(); self.fifo.resume();
self.obj_buffer.remove(&attr); self.obj_buffer.remove(&attr);
self.fetcher.obj.next(SendToFifoTwo);
} }
} else if self.fetcher.obj.fifo_count == 2 { SendToFifoTwo => self.fetcher.obj.reset(),
self.fetcher.obj.reset();
} else {
panic!("Object FIFO Logic Error has occurred :angry:");
}
}
} }
} }
// By only running on odd cycles, we can ensure that we draw every two T cycles if self.fetcher.bg.is_enabled() {
if self.clock == TimingClock::Tick && self.fetcher.bg.is_enabled() {
match self.fetcher.bg.state { match self.fetcher.bg.state {
TileNumber => { TileNumber => {
// Increment Window line counter if scanline had any window pixels on it // Increment Window line counter if scanline had any window pixels on it
@ -320,25 +301,31 @@ impl Ppu {
self.fetcher.bg.tile.with_id(id); self.fetcher.bg.tile.with_id(id);
// Move on to the Next state in 2 T-cycles // Move on to the Next state in 2 T-cycles
self.fetcher.bg.next(TileDataLow); self.fetcher.bg.next(SleepZero);
} }
SleepZero => self.fetcher.bg.next(TileDataLow),
TileDataLow => { TileDataLow => {
let addr = self.fetcher.bg_byte_low_addr(control, pos, is_window); let addr = self.fetcher.bg_byte_low_addr(control, pos, is_window);
let low = self.read_byte(addr); let low = self.read_byte(addr);
self.fetcher.bg.tile.with_low_byte(low); self.fetcher.bg.tile.with_low_byte(low);
self.fetcher.bg.next(TileDataHigh); self.fetcher.bg.next(SleepOne);
} }
SleepOne => self.fetcher.bg.next(TileDataHigh),
TileDataHigh => { TileDataHigh => {
let addr = self.fetcher.bg_byte_low_addr(control, pos, is_window); let addr = self.fetcher.bg_byte_low_addr(control, pos, is_window);
let high = self.read_byte(addr + 1); let high = self.read_byte(addr + 1);
self.fetcher.bg.tile.with_high_byte(high); self.fetcher.bg.tile.with_high_byte(high);
self.fetcher.bg.next(SendToFifo); self.fetcher.bg.next(SleepTwo);
} }
SendToFifo => { SleepTwo => self.fetcher.bg.next(SendToFifoOne),
SendToFifoOne => {
self.fetcher.bg.next(SendToFifoTwo);
}
SendToFifoTwo => {
let palette = &self.monochrome.bg_palette; let palette = &self.monochrome.bg_palette;
self.fetcher.send_to_fifo(&mut self.fifo, palette); self.fetcher.send_to_fifo(&mut self.fifo, palette);
@ -558,10 +545,6 @@ impl ObjectBuffer {
pub fn iter(&self) -> std::slice::Iter<'_, Option<ObjectAttribute>> { pub fn iter(&self) -> std::slice::Iter<'_, Option<ObjectAttribute>> {
self.into_iter() self.into_iter()
} }
pub fn iter_mut(&mut self) -> &mut std::slice::IterMut<'_, Option<ObjectAttribute>> {
todo!("Figure out the lifetimes for ObjectBuffer::iter_mut()");
}
} }
impl<'a> IntoIterator for &'a ObjectBuffer { impl<'a> IntoIterator for &'a ObjectBuffer {
@ -704,11 +687,12 @@ impl PixelFetcher {
} }
fn send_to_fifo(&mut self, fifo: &mut FifoRenderer, palette: &BackgroundPalette) { fn send_to_fifo(&mut self, fifo: &mut FifoRenderer, palette: &BackgroundPalette) {
let tile_bytes = self.bg.tile.low.zip(self.bg.tile.high); let maybe_low = self.bg.tile.low;
let maybe_high = self.bg.tile.high;
if let Some(bytes) = tile_bytes { let (low, high) = maybe_low
let low = bytes.0; .zip(maybe_high)
let high = bytes.1; .expect("Low & High Bytes in TileBuilder were unexpectedly missing.");
let pixel = TwoBitsPerPixel::from_bytes(high, low); let pixel = TwoBitsPerPixel::from_bytes(high, low);
@ -723,7 +707,6 @@ impl PixelFetcher {
fifo.background.push_back(fifo_pixel); fifo.background.push_back(fifo_pixel);
} }
} }
}
self.x_pos += 1; self.x_pos += 1;
} }
@ -857,9 +840,13 @@ impl WindowLineCounter {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum FetcherState { pub enum FetcherState {
TileNumber, TileNumber,
SleepZero,
TileDataLow, TileDataLow,
SleepOne,
TileDataHigh, TileDataHigh,
SendToFifo, SleepTwo,
SendToFifoOne,
SendToFifoTwo,
} }
impl Default for FetcherState { impl Default for FetcherState {

View File

@ -179,6 +179,17 @@ pub enum ObjectSize {
Sixteen = 1, Sixteen = 1,
} }
impl ObjectSize {
pub fn as_u8(&self) -> u8 {
use ObjectSize::*;
match self {
Eight => 8,
Sixteen => 16,
}
}
}
impl From<u8> for ObjectSize { impl From<u8> for ObjectSize {
fn from(byte: u8) -> Self { fn from(byte: u8) -> Self {
match byte { match byte {