fix(ppu): reimplement OAM scan
This commit is contained in:
parent
0672c7346a
commit
3c680dec2f
102
src/ppu.rs
102
src/ppu.rs
|
@ -39,7 +39,7 @@ pub struct Ppu {
|
||||||
pub vram: Box<[u8; VRAM_SIZE]>,
|
pub vram: Box<[u8; VRAM_SIZE]>,
|
||||||
pub stat: LCDStatus,
|
pub stat: LCDStatus,
|
||||||
pub oam: ObjectAttributeTable,
|
pub oam: ObjectAttributeTable,
|
||||||
clock: TimingClock,
|
scan_state: OamScanState,
|
||||||
fetcher: PixelFetcher,
|
fetcher: PixelFetcher,
|
||||||
fifo: FifoRenderer,
|
fifo: FifoRenderer,
|
||||||
obj_buffer: ObjectBuffer,
|
obj_buffer: ObjectBuffer,
|
||||||
|
@ -72,7 +72,7 @@ impl Ppu {
|
||||||
self.stat.set_mode(PpuMode::Drawing);
|
self.stat.set_mode(PpuMode::Drawing);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.scan_oam(self.cycles.into());
|
self.scan_oam();
|
||||||
}
|
}
|
||||||
PpuMode::Drawing => {
|
PpuMode::Drawing => {
|
||||||
if self.x_pos >= 160 {
|
if self.x_pos >= 160 {
|
||||||
|
@ -127,6 +127,7 @@ impl Ppu {
|
||||||
self.int.set_lcd_stat(true);
|
self.int.set_lcd_stat(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.scan_state.reset();
|
||||||
PpuMode::OamScan
|
PpuMode::OamScan
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -152,27 +153,22 @@ impl Ppu {
|
||||||
self.int.set_lcd_stat(true);
|
self.int.set_lcd_stat(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.scan_state.reset();
|
||||||
self.stat.set_mode(PpuMode::OamScan);
|
self.stat.set_mode(PpuMode::OamScan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The TimingClock is either Tick or Tock, and it changes
|
|
||||||
// every other cycle, which means that we can use it to ensure
|
|
||||||
// that things run every other cycle
|
|
||||||
self.clock_next();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_oam(&mut self, cycle: u32) {
|
fn scan_oam(&mut self) {
|
||||||
if self.clock == TimingClock::Tock {
|
if self.scan_state.mode() == OamScanMode::Scan {
|
||||||
// This is run 50% of the time, or 40 times
|
|
||||||
// which is the number of sprites in OAM
|
|
||||||
|
|
||||||
let sprite_height = self.control.obj_size().as_u8();
|
let sprite_height = self.control.obj_size().as_u8();
|
||||||
|
|
||||||
let attr = self.oam.attribute((cycle / 2) as usize);
|
let index = self.scan_state.count();
|
||||||
|
|
||||||
|
let attr = self.oam.attribute(index as usize);
|
||||||
let line_y = self.pos.line_y + 16;
|
let line_y = self.pos.line_y + 16;
|
||||||
|
|
||||||
if attr.x > 0
|
if attr.x > 0
|
||||||
|
@ -182,7 +178,11 @@ impl Ppu {
|
||||||
{
|
{
|
||||||
self.obj_buffer.add(attr);
|
self.obj_buffer.add(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.scan_state.increase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.scan_state.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, cycle: u32) {
|
fn draw(&mut self, cycle: u32) {
|
||||||
|
@ -370,19 +370,10 @@ impl Ppu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clock_next(&mut self) {
|
|
||||||
use TimingClock::*;
|
|
||||||
|
|
||||||
self.clock = match self.clock {
|
|
||||||
Tick => Tock,
|
|
||||||
Tock => Tick,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) {
|
fn reset(&mut self) {
|
||||||
// FIXME: Discover what actually is supposed to be reset here
|
// FIXME: Discover what actually is supposed to be reset here
|
||||||
|
|
||||||
self.clock = Default::default();
|
self.scan_state = Default::default();
|
||||||
self.cycles = Cycle::new(0);
|
self.cycles = Cycle::new(0);
|
||||||
|
|
||||||
self.x_pos = 0;
|
self.x_pos = 0;
|
||||||
|
@ -411,7 +402,7 @@ impl Default for Ppu {
|
||||||
pos: Default::default(),
|
pos: Default::default(),
|
||||||
stat: Default::default(),
|
stat: Default::default(),
|
||||||
oam: Default::default(),
|
oam: Default::default(),
|
||||||
clock: Default::default(),
|
scan_state: Default::default(),
|
||||||
fetcher: Default::default(),
|
fetcher: Default::default(),
|
||||||
fifo: Default::default(),
|
fifo: Default::default(),
|
||||||
obj_buffer: Default::default(),
|
obj_buffer: Default::default(),
|
||||||
|
@ -420,18 +411,6 @@ impl Default for Ppu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
enum TimingClock {
|
|
||||||
Tick = 0,
|
|
||||||
Tock = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TimingClock {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Tick
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct Interrupt {
|
pub struct Interrupt {
|
||||||
_vblank: bool,
|
_vblank: bool,
|
||||||
|
@ -490,7 +469,9 @@ impl ObjectAttributeTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attribute(&self, index: usize) -> ObjectAttribute {
|
pub fn attribute(&self, index: usize) -> ObjectAttribute {
|
||||||
let slice: &[u8; 4] = self.buf[index..(index + 4)]
|
let start = index * 4;
|
||||||
|
|
||||||
|
let slice: &[u8; 4] = self.buf[start..(start + 4)]
|
||||||
.try_into()
|
.try_into()
|
||||||
.expect("Could not interpret &[u8] as a &[u8; 4]");
|
.expect("Could not interpret &[u8] as a &[u8; 4]");
|
||||||
|
|
||||||
|
@ -918,3 +899,50 @@ impl TileBuilder {
|
||||||
self.high = Some(data);
|
self.high = Some(data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
struct OamScanState {
|
||||||
|
count: u8,
|
||||||
|
mode: OamScanMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OamScanState {
|
||||||
|
pub fn increase(&mut self) {
|
||||||
|
self.count += 1;
|
||||||
|
self.count %= 40;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.count = Default::default();
|
||||||
|
self.mode = Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(&self) -> u8 {
|
||||||
|
self.count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mode(&self) -> OamScanMode {
|
||||||
|
self.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) {
|
||||||
|
use OamScanMode::*;
|
||||||
|
|
||||||
|
self.mode = match self.mode {
|
||||||
|
Scan => Sleep,
|
||||||
|
Sleep => Scan,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
enum OamScanMode {
|
||||||
|
Scan,
|
||||||
|
Sleep,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OamScanMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Scan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue