Skip to content

Commit

Permalink
Implemented 8x16 sprite rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
velllu committed Mar 20, 2024
1 parent 07e6dc2 commit d38cb5b
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 12 deletions.
44 changes: 34 additions & 10 deletions src/gpu/sprite_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ use crate::{common::Bit, GameBoy};

use super::{tile_parser::Line, Color};

#[derive(Clone, Copy)]
pub(crate) enum SpriteHeight {
Short = 8,
Tall = 16,
}

#[derive(PartialEq, Eq)]
pub(crate) enum Priority {
AlwaysAbove,
Expand Down Expand Up @@ -49,7 +55,12 @@ impl GameBoy {
}
}

pub(crate) fn get_sprite_fifo(&self, x: u8, y: u8) -> Option<(Line, &SpriteData)> {
pub(crate) fn get_sprite_fifo(
&self,
x: u8,
y: u8,
sprite_height: &SpriteHeight,
) -> Option<(Line, &SpriteData)> {
let mut sprite_fifo: Option<(Line, &SpriteData)> = None;

for sprite in &self.gpu.sprites {
Expand All @@ -63,18 +74,31 @@ impl GameBoy {
// We check if there is any sprite that is on the same x axis as our "cursor"
let x_condition = sprite_x == x;

// and we check if we also are on the same y axis, however, a sprite is 8
// pixel long, so we check if we are anywhere between row 1 to 8
let y_condition = ((sprite_y)..(sprite_y + 8)).contains(&y);
// And we check if the sprite is contained in the range of the sprite's height
let y_condition = ((sprite_y)..(sprite_y + *sprite_height as u8)).contains(&y);

// This is the line of the sprite we are currently processing
let current_line = y % *sprite_height as u8;

// When the sprite is 8x8 we can just use the actual tile number, but when the
// sprite is 8x16, the higher part of it needs to be ANDed with `0xFE` and the
// lower part needs to be ORed with `0x01`, when y flipping is on, we do the
// opposite
let tile_number = match (sprite_height, sprite.y_flip) {
(&SpriteHeight::Short, _) => sprite.tile_number,

// High part
(&SpriteHeight::Tall, false) if current_line >= 8 => sprite.tile_number & 0xFE,
(&SpriteHeight::Tall, true) if current_line >= 8 => sprite.tile_number | 0x01,

// Low part
(&SpriteHeight::Tall, false) => sprite.tile_number | 0x01,
(&SpriteHeight::Tall, true) => sprite.tile_number & 0xFE,
};

if x_condition && y_condition {
sprite_fifo = Some((
self.get_line_rotation(
sprite.tile_number,
y as u16 % 8,
sprite.x_flip,
sprite.y_flip,
),
self.get_line_rotation(tile_number, y as u16 % 8, sprite.x_flip, sprite.y_flip),
sprite,
));
}
Expand Down
12 changes: 10 additions & 2 deletions src/gpu/states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ use crate::{
GameBoy,
};

use super::{sprite_parser::SpriteData, Color};
use super::{
sprite_parser::{SpriteData, SpriteHeight},
Color,
};

#[derive(PartialEq, Debug)]
pub enum GPUState {
Expand Down Expand Up @@ -92,6 +95,11 @@ impl GameBoy {
true => 0x9C00,
};

let sprite_height = match self.bus[LCDC].get_bit(2) {
false => SpriteHeight::Short,
true => SpriteHeight::Tall,
};

// There's a delay of 12 dots at the beginning of this mode due to the tile
// fetching below
if self.gpu.dots == 0 {
Expand All @@ -115,7 +123,7 @@ impl GameBoy {

// And we get both background/window fifo and the sprite fifo
let background_fifo = self.get_line(self.bus[tile_map_address], self.gpu.y as u16 % 8);
let mut sprite = self.get_sprite_fifo(self.gpu.x, self.gpu.y);
let mut sprite = self.get_sprite_fifo(self.gpu.x, self.gpu.y, &sprite_height);

if let Some((sprite_fifo, sprite_data)) = &mut sprite {
if self.gpu.rendered_sprites_on_line < 10 {
Expand Down

0 comments on commit d38cb5b

Please sign in to comment.