Compare commits

..

No commits in common. "4cfd951ede134d7d2d3303fe68cb5bb84b402179" and "9b3ab73bb11c8aece7479f05ca18a1101cffa596" have entirely different histories.

1 changed files with 183 additions and 160 deletions

View File

@ -83,22 +83,11 @@ impl Ppu {
if self.dot >= 80 { if self.dot >= 80 {
self.x_pos = 0; self.x_pos = 0;
self.scanline_start = true; self.scanline_start = true;
self.fetch.back.tile_high_reset = true; self.fetch.back.scanline_first = true;
self.to_discard = 0; self.to_discard = 0;
self.fifo.back.clear(); self.fifo.back.clear();
self.fifo.obj.clear(); self.fifo.obj.clear();
// Sort Sprites
self.obj_buffer.inner.sort_by(|left, right| {
left.zip(*right)
.map(|(left, right)| right.x.cmp(&left.x))
.unwrap_or(std::cmp::Ordering::Greater)
});
// if self.obj_buffer.len != 0 {
// dbg!(&self.obj_buffer);
// }
self.stat.set_mode(PpuMode::Drawing); self.stat.set_mode(PpuMode::Drawing);
} }
@ -237,54 +226,56 @@ impl Ppu {
fn draw(&mut self) { fn draw(&mut self) {
use FetcherState::*; use FetcherState::*;
let mut obj_attr = &mut None; let mut iter = self.obj_buffer.iter_mut();
let default = &mut None;
for maybe_attr in &mut self.obj_buffer.inner { let obj_attr = loop {
match maybe_attr { match iter.next() {
Some(attr) if self.ctrl.obj_enabled() => { Some(attr_opt) => {
if let Some(attr) = attr_opt {
if attr.x <= (self.x_pos + 8) { if attr.x <= (self.x_pos + 8) {
self.fetch.back.reset(); self.fetch.back.reset();
self.fetch.back.enabled = false; self.fetch.back.pause();
self.fifo.pause(); self.fifo.pause();
obj_attr = maybe_attr; break attr_opt;
break;
} }
} }
_ => break,
} }
None => break default,
} }
};
if let Some(attr) = obj_attr { if let Some(attr) = obj_attr {
match self.fetch.obj.state { match self.fetch.obj.state {
TileNumberA => self.fetch.obj.state = TileNumberB, TileNumber => {
TileNumberB => {
self.fetch.obj.tile.with_id(attr.tile_index); self.fetch.obj.tile.with_id(attr.tile_index);
self.fetch.obj.state = TileLowA; self.fetch.obj.next(SleepOne);
} }
TileLowA => self.fetch.obj.state = TileLowB, SleepOne => self.fetch.obj.next(TileLow),
TileLowB => { TileLow => {
let obj_size = self.ctrl.obj_size(); let obj_size = self.ctrl.obj_size();
let addr = PixelFetcher::get_obj_addr(attr, &self.pos, obj_size); let addr = PixelFetcher::get_obj_addr(attr, &self.pos, obj_size);
let byte = self.read_byte(addr); let byte = self.read_byte(addr);
self.fetch.obj.tile.with_low(byte); self.fetch.obj.tile.with_low_byte(byte);
self.fetch.obj.state = TileHighA; self.fetch.obj.next(SleepTwo);
} }
TileHighA => self.fetch.obj.state = TileHighB, SleepTwo => self.fetch.obj.next(TileHigh),
TileHighB => { TileHigh => {
let obj_size = self.ctrl.obj_size(); let obj_size = self.ctrl.obj_size();
let addr = PixelFetcher::get_obj_addr(attr, &self.pos, obj_size); let addr = PixelFetcher::get_obj_addr(attr, &self.pos, obj_size);
let byte = self.read_byte(addr + 1); let byte = self.read_byte(addr + 1);
self.fetch.obj.tile.with_high(byte); self.fetch.obj.tile.with_high_byte(byte);
self.fetch.obj.state = ToFifoA; self.fetch.obj.next(SleepThree);
} }
ToFifoA => { SleepThree => self.fetch.obj.next(ToFifoOne),
ToFifoOne => {
// Load into Fifo // Load into Fifo
let (high, low) = self let (high, low) = self
.fetch .fetch
@ -316,65 +307,62 @@ impl Ppu {
self.fifo.obj.push_back(fifo_info); self.fifo.obj.push_back(fifo_info);
} }
self.fetch.back.enabled = true; self.fetch.back.resume();
self.fifo.resume(); self.fifo.resume();
let _ = std::mem::take(obj_attr); let _ = std::mem::take(obj_attr);
self.fetch.obj.state = ToFifoB; self.fetch.obj.next(ToFifoTwo);
} }
ToFifoB => self.fetch.obj.reset(), ToFifoTwo => self.fetch.obj.reset(),
} }
} }
if self.fetch.back.enabled { if self.fetch.back.is_enabled() {
match self.fetch.back.state { match self.fetch.back.state {
TileNumberA => self.fetch.back.state = TileNumberB, SleepOne => self.fetch.back.next(TileNumber),
TileNumberB => { TileNumber => {
// Are we rendering the Window currently? let x_pos = self.fetch.x_pos;
self.fetch.back.draw_window = self.win_stat.enabled;
let addr = self.fetch.back.should_render_window(self.win_stat.enabled);
self.fetch
.back let addr = self.fetch.bg_tile_num_addr(&self.ctrl, &self.pos, x_pos);
.tile_id_addr(&self.ctrl, &self.pos, self.fetch.x_pos);
let id = self.read_byte(addr); let id = self.read_byte(addr);
self.fetch.back.tile.with_id(id); self.fetch.back.tile.with_id(id);
self.fetch.back.state = TileLowA; self.fetch.back.next(TileLow);
} }
TileLowA => self.fetch.back.state = TileLowB, SleepTwo => self.fetch.back.next(TileLow),
TileLowB => { TileLow => {
let id = self.fetch.back.tile.id.expect("Tile ID present"); let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos);
let addr = self.fetch.back.tile_addr(&self.ctrl, &self.pos, id); let low = self.read_byte(addr);
let byte = self.read_byte(addr); self.fetch.back.tile.with_low_byte(low);
self.fetch.back.tile.with_low(byte);
self.fetch.back.state = TileHighA; self.fetch.back.next(SleepThree);
} }
TileHighA => self.fetch.back.state = TileHighB, SleepThree => self.fetch.back.next(TileHigh),
TileHighB => { TileHigh => {
let id = self.fetch.back.tile.id.expect("Tile ID present"); let addr = self.fetch.bg_byte_addr(&self.ctrl, &self.pos);
let addr = self.fetch.back.tile_addr(&self.ctrl, &self.pos, id); let high = self.read_byte(addr + 1);
let byte = self.read_byte(addr + 1); self.fetch.back.tile.with_high_byte(high);
self.fetch.back.tile.with_high(byte);
if self.fetch.back.tile_high_reset { if self.fetch.back.scanline_first {
self.fetch.back.reset(); self.fetch.back.reset();
self.fetch.back.tile_high_reset = false;
self.fetch.back.scanline_first = false;
} else { } else {
self.fetch.back.state = ToFifoA; self.fetch.back.next(ToFifoOne);
} }
} }
ToFifoA => { ToFifoOne | ToFifoTwo => {
if let Ok(_) = self.fetch.send_to_fifo(&mut self.fifo) { if let Ok(()) = self.fetch.send_to_fifo(&mut self.fifo) {
self.fetch.x_pos += 1; self.fetch.x_pos += 1;
self.fetch.back.state = ToFifoB; self.fetch.back.next(SleepOne);
self.fetch.back.tile = Default::default();
} }
} }
ToFifoB => self.fetch.back.reset(),
} }
} }
@ -430,39 +418,43 @@ impl Ppu {
} }
fn clock_fifo(&mut self) -> Option<GrayShade> { fn clock_fifo(&mut self) -> Option<GrayShade> {
use ObjectPaletteKind::*;
use RenderPriority::*; use RenderPriority::*;
self.fifo let obj_palette_0 = &self.monochrome.obj_palette_0;
.back let obj_palette_1 = &self.monochrome.obj_palette_1;
.pop_front()
.map(|bg| match self.fifo.obj.pop_front() { match self.fifo.back.pop_front() {
Some(obj) => match obj.priority { Some(bg_pixel) => match self.fifo.obj.pop_front() {
_ if obj.shade_id == 0 => self.bg_pixel(bg), Some(obj_pixel) if self.ctrl.obj_enabled() => match obj_pixel.priority {
BackgroundAndWindow if bg.shade_id != 0 => self.bg_pixel(bg), Object | BackgroundAndWindow if obj_pixel.shade_id == 0 => {
_ => self.obj_pixel(obj), Some(self.bg_pixel(bg_pixel.shade_id))
}
BackgroundAndWindow if bg_pixel.shade_id != 0 => {
Some(self.bg_pixel(bg_pixel.shade_id))
}
Object | BackgroundAndWindow => {
let maybe_sprite = match obj_pixel.palette_kind {
Zero => obj_palette_0.shade(obj_pixel.shade_id),
One => obj_palette_1.shade(obj_pixel.shade_id),
};
let sprite = maybe_sprite
.expect("Sprite w/ a colour id of 0 has already been handled");
Some(sprite)
}
}, },
None => self.bg_pixel(bg), _ => Some(self.bg_pixel(bg_pixel.shade_id)),
}) },
} None => None,
fn obj_pixel(&self, obj: ObjPixelProperty) -> GrayShade {
use ObjectPaletteKind::*;
assert!(obj.shade_id != 0);
let p0 = &self.monochrome.obj_palette_0;
let p1 = &self.monochrome.obj_palette_1;
match obj.palette_kind {
Zero => p0.shade(obj.shade_id).expect("Object shade id is non-zero"),
One => p1.shade(obj.shade_id).expect("Object shade id is non-zero"),
} }
} }
fn bg_pixel(&self, bg: BgPixelProperty) -> GrayShade { fn bg_pixel(&self, shade_id: u8) -> GrayShade {
let bg_palette = &self.monochrome.bg_palette; let bg_palette = &self.monochrome.bg_palette;
if self.ctrl.bg_win_enabled() { if self.ctrl.bg_win_enabled() {
bg_palette.shade(bg.shade_id) bg_palette.shade(shade_id)
} else { } else {
bg_palette.shade(0) bg_palette.shade(0)
} }
@ -581,7 +573,7 @@ impl Default for ObjectAttributeTable {
} }
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
struct ObjectAttribute { struct ObjectAttribute {
y: u8, y: u8,
x: u8, x: u8,
@ -631,6 +623,11 @@ impl ObjectBuffer {
self.inner[self.len] = Some(attr); self.inner[self.len] = Some(attr);
self.len += 1; self.len += 1;
} }
#[inline]
fn iter_mut(&mut self) -> std::slice::IterMut<'_, Option<ObjectAttribute>> {
self.inner.iter_mut()
}
} }
impl Default for ObjectBuffer { impl Default for ObjectBuffer {
@ -656,6 +653,57 @@ impl PixelFetcher {
self.x_pos = 0; self.x_pos = 0;
} }
fn bg_tile_num_addr(&self, control: &LCDControl, pos: &ScreenPosition, x_pos: u8) -> u16 {
let line_y = pos.line_y;
let scroll_y = pos.scroll_y;
let scroll_x = pos.scroll_x;
let is_window = self.back.is_window_tile();
// Determine which tile map is being used
let tile_map = if is_window {
control.win_tile_map_addr()
} else {
control.bg_tile_map_addr()
};
let tile_map_addr = tile_map.into_address();
// Both Offsets are used to offset the tile map address we found above
// Offsets are ANDed wih 0x3FF so that we stay in bounds of tile map memory
let scx_offset = if is_window { 0 } else { scroll_x / 8 };
let y_offset = if is_window {
self.back.wl_count as u16 / 8
} else {
((line_y as u16 + scroll_y as u16) & 0xFF) / 8
};
let x_offset = (scx_offset + x_pos) & 0x1F;
let offset = (32 * y_offset) + (x_offset as u16);
tile_map_addr + (offset & 0x3FF)
}
fn bg_byte_addr(&mut self, control: &LCDControl, pos: &ScreenPosition) -> u16 {
let line_y = pos.line_y;
let scroll_y = pos.scroll_y;
let is_window = self.back.is_window_tile();
let id = self.back.tile.id.expect("Tile Number is present");
let tile_data_addr = match control.tile_data_addr() {
TileDataAddress::X8800 => 0x9000u16.wrapping_add((id as i8 as i16 * 16) as u16),
TileDataAddress::X8000 => 0x8000 + (id as u16 * 16),
};
let offset = if is_window {
self.back.wl_count as u16 % 8
} else {
(line_y as u16 + scroll_y as u16) % 8
};
tile_data_addr + (offset * 2)
}
fn send_to_fifo(&self, fifo: &mut PixelFifo) -> Result<(), ()> { fn send_to_fifo(&self, fifo: &mut PixelFifo) -> Result<(), ()> {
if !fifo.back.is_empty() { if !fifo.back.is_empty() {
return Err(()); return Err(());
@ -701,6 +749,7 @@ impl PixelFetcher {
} }
trait Fetcher { trait Fetcher {
fn next(&mut self, state: FetcherState);
fn reset(&mut self); fn reset(&mut self);
fn hblank_reset(&mut self); fn hblank_reset(&mut self);
} }
@ -710,71 +759,47 @@ struct BackgroundFetcher {
state: FetcherState, state: FetcherState,
tile: TileBuilder, tile: TileBuilder,
wl_count: u8, wl_count: u8,
draw_window: bool, is_window_tile: bool,
enabled: bool, enabled: bool,
tile_high_reset: bool, scanline_first: bool,
} }
impl BackgroundFetcher { impl BackgroundFetcher {
fn tile_id_addr(&self, control: &LCDControl, pos: &ScreenPosition, x_pos: u8) -> u16 { fn should_render_window(&mut self, value: bool) {
let line_y = pos.line_y; self.is_window_tile = value;
let scroll_y = pos.scroll_y;
let scroll_x = pos.scroll_x;
let is_window = self.draw_window;
// Determine which tile map is being used
let tile_map = if is_window {
control.win_tile_map_addr()
} else {
control.bg_tile_map_addr()
};
let tile_map_addr = tile_map.into_address();
// Both Offsets are used to offset the tile map address we found above
// Offsets are ANDed wih 0x3FF so that we stay in bounds of tile map memory
let scx_offset = if is_window { 0 } else { scroll_x / 8 };
let y_offset = if is_window {
self.wl_count as u16 / 8
} else {
((line_y as u16 + scroll_y as u16) & 0xFF) / 8
};
let x_offset = (scx_offset + x_pos) & 0x1F;
let offset = (32 * y_offset) + (x_offset as u16);
tile_map_addr + (offset & 0x3FF)
} }
fn tile_addr(&mut self, control: &LCDControl, pos: &ScreenPosition, id: u8) -> u16 { fn is_window_tile(&self) -> bool {
let line_y = pos.line_y; self.is_window_tile
let scroll_y = pos.scroll_y; }
let tile_data_addr = match control.tile_data_addr() { fn pause(&mut self) {
TileDataAddress::X8800 => 0x9000u16.wrapping_add((id as i8 as i16 * 16) as u16), self.enabled = false;
TileDataAddress::X8000 => 0x8000 + (id as u16 * 16), }
};
let offset = if self.draw_window { fn resume(&mut self) {
self.wl_count as u16 % 8 self.enabled = true;
} else { }
(line_y as u16 + scroll_y as u16) % 8
};
tile_data_addr + (offset * 2) fn is_enabled(&self) -> bool {
self.enabled
} }
} }
impl Fetcher for BackgroundFetcher { impl Fetcher for BackgroundFetcher {
fn next(&mut self, state: FetcherState) {
self.state = state
}
fn reset(&mut self) { fn reset(&mut self) {
self.state = Default::default(); self.state = FetcherState::SleepOne;
self.tile = Default::default(); self.tile = Default::default();
} }
fn hblank_reset(&mut self) { fn hblank_reset(&mut self) {
self.reset(); self.reset();
self.draw_window = false; self.is_window_tile = false;
self.enabled = true; self.enabled = true;
} }
@ -783,12 +808,12 @@ impl Fetcher for BackgroundFetcher {
impl Default for BackgroundFetcher { impl Default for BackgroundFetcher {
fn default() -> Self { fn default() -> Self {
Self { Self {
state: Default::default(), state: FetcherState::SleepOne,
tile: Default::default(), tile: Default::default(),
draw_window: Default::default(), is_window_tile: Default::default(),
wl_count: Default::default(), wl_count: Default::default(),
enabled: true, enabled: true,
tile_high_reset: true, scanline_first: true,
} }
} }
} }
@ -802,15 +827,19 @@ struct ObjectFetcher {
impl Default for ObjectFetcher { impl Default for ObjectFetcher {
fn default() -> Self { fn default() -> Self {
Self { Self {
state: Default::default(), state: FetcherState::TileNumber,
tile: Default::default(), tile: Default::default(),
} }
} }
} }
impl Fetcher for ObjectFetcher { impl Fetcher for ObjectFetcher {
fn next(&mut self, state: FetcherState) {
self.state = state
}
fn reset(&mut self) { fn reset(&mut self) {
self.state = Default::default(); self.state = FetcherState::TileNumber;
self.tile = Default::default(); self.tile = Default::default();
} }
@ -821,20 +850,14 @@ impl Fetcher for ObjectFetcher {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum FetcherState { enum FetcherState {
TileNumberA, TileNumber,
TileNumberB, SleepOne,
TileLowA, TileLow,
TileLowB, SleepTwo,
TileHighA, TileHigh,
TileHighB, SleepThree,
ToFifoA, ToFifoOne,
ToFifoB, ToFifoTwo,
}
impl Default for FetcherState {
fn default() -> Self {
Self::TileNumberA
}
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -894,11 +917,11 @@ impl TileBuilder {
self.id = Some(id); self.id = Some(id);
} }
fn with_low(&mut self, data: u8) { fn with_low_byte(&mut self, data: u8) {
self.low = Some(data); self.low = Some(data);
} }
fn with_high(&mut self, data: u8) { fn with_high_byte(&mut self, data: u8) {
self.high = Some(data); self.high = Some(data);
} }