Skip to content

Commit

Permalink
Fixed incorrect SCY handling
Browse files Browse the repository at this point in the history
And also made a lot of code better, removing the ambiguosly
named variable "self.gpu.i", SCX is still wrong tho
  • Loading branch information
velllu committed Apr 1, 2024
1 parent edeaf6e commit d29aa3f
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 45 deletions.
7 changes: 6 additions & 1 deletion src/gpu/sprite_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,12 @@ impl GameBoy {

if x_condition && y_condition {
sprite_fifo = Some((
self.get_line_rotation(tile_number, y as u16 % 8, sprite.x_flip, sprite.y_flip),
self.get_line_from_tile_number_with_rotation(
tile_number,
y % 8,
sprite.x_flip,
sprite.y_flip,
),
sprite,
));
}
Expand Down
44 changes: 6 additions & 38 deletions src/gpu/states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ pub struct Gpu {
x: u8,
y: u8,

/// This is the offset to add to the tile map address
i: u16,

/// A dot is 1/4 of a CPU cycle
dots: u16,
}
Expand All @@ -55,7 +52,6 @@ impl Gpu {
rendered_sprites_on_line: 0,
x: 0,
y: 0,
i: 0,
dots: 0,
}
}
Expand Down Expand Up @@ -90,11 +86,6 @@ impl GameBoy {
}

fn pixel_transfer(&mut self) {
let mut tile_map_address: u16 = match self.bus[0xFF40].get_bit(3) {
false => 0x9800,
true => 0x9C00,
};

let sprite_height = match self.bus[LCDC].get_bit(2) {
false => SpriteHeight::Short,
true => SpriteHeight::Tall,
Expand All @@ -106,31 +97,20 @@ impl GameBoy {
self.gpu.dots += 12;
}

// Y Scrolling
// The gameboy tilemap is 32x32 tiles, both `SCX` and `SCY` use pixels, not tiles
// so we have to divide them by 8, skipping 32 tiles just means to set the
// "cursor" on the line below
for _ in 0..(self.bus[SCY] / 8) {
tile_map_address += 32;
}

// X Scrolling
// We add the number of tiles to skip to the adress
tile_map_address += self.bus[SCX] as u16 / 8;
// And we get the background fifo and the sprite fifo
let background_fifo = self.get_line_from_coordinates(
self.gpu.x.wrapping_add(self.bus[SCX]),
self.gpu.y.wrapping_add(self.bus[SCY]),
);

// Adding i
tile_map_address += self.gpu.i;

// 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, &sprite_height);

if let Some((sprite_fifo, sprite_data)) = &mut sprite {
if self.gpu.rendered_sprites_on_line < 10 {
self.apply_palette_to_sprite(sprite_fifo, &sprite_data.palette);
sprite_fifo.mix_with_background_tile(&background_fifo, &sprite_data.priority);

self.draw_line(&sprite_fifo, self.gpu.x as usize, self.gpu.y as usize);
self.draw_line(sprite_fifo, self.gpu.x as usize, self.gpu.y as usize);
self.gpu.rendered_sprites_on_line += 1;
} else {
self.draw_line(&background_fifo, self.gpu.x as usize, self.gpu.y as usize);
Expand All @@ -139,20 +119,9 @@ impl GameBoy {
self.draw_line(&background_fifo, self.gpu.x as usize, self.gpu.y as usize);
}

self.gpu.i += 1;
self.gpu.x += 8;

if self.gpu.x == (DISPLAY_SIZE_X as u8) {
// If we finished rendering all the 20 tiles, and we want to go to the next
// set of tile, we skip 12, because the tile map is 32x32, and the viewport
// is 20x18 (32 - 20), and if we haven't rendered the 20 tiles, we go back
// to the first one
if self.gpu.y % 8 == 7 {
self.gpu.i += 12;
} else {
self.gpu.i -= 20;
}

self.gpu.y += 1;
}

Expand Down Expand Up @@ -200,7 +169,6 @@ impl GameBoy {

if self.gpu.dots == 0 {
self.gpu.y = 0;
self.gpu.i = 0;
}

// After every line
Expand Down
34 changes: 28 additions & 6 deletions src/gpu/tile_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ impl Line {
}

impl GameBoy {
pub(crate) fn get_line(&self, tile_number: u8, y: u16) -> Line {
/// Returns the given line of a tile
/// # Parameters
/// - *tile_number*: The tile of the number, it will follow LCDC.4's tile data area
/// specification
/// - *line_y*: The line of the tile that we want to get, as a tile is 8x8, this
/// number can go from 0 to 7
pub(crate) fn get_line_from_tile_number(&self, tile_number: u8, line_y: u8) -> Line {
let mut tile = Line::new_blank();

// To calculate the address to fetch we calculate
Expand All @@ -65,21 +71,37 @@ impl GameBoy {

let tile_number: u16 = (tile_number as u16) << 4;

let low = self.bus[tile_data_address + tile_number + y * 2];
let high = self.bus[tile_data_address + tile_number + y * 2 + 1];
let low = self.bus[tile_data_address + tile_number + line_y as u16 * 2];
let high = self.bus[tile_data_address + tile_number + line_y as u16 * 2 + 1];

tile.draw_line(high, low);
tile
}

pub(crate) fn get_line_rotation(
/// Returns the line that should be at the given coordinates of the screen (excluding
/// horizontal and vertical scroll)
pub(crate) fn get_line_from_coordinates(&self, x: u8, y: u8) -> Line {
let tile_map_address: u16 = match self.bus[0xFF40].get_bit(3) {
false => 0x9800,
true => 0x9C00,
};

// TODO: This gets tricky when the X Scrolling is not a multiplier of 8
let tiled_y = y / 8; // The Y tile number
let tiled_x = x / 8; // The X tile number

let tile_number = self.bus[tile_map_address + (tiled_y as u16 * 0x20) + tiled_x as u16];
self.get_line_from_tile_number(tile_number, y % 8)
}

pub(crate) fn get_line_from_tile_number_with_rotation(
&self,
tile_number: u8,
y: u16,
y: u8,
x_flip: bool,
y_flip: bool,
) -> Line {
let mut line = self.get_line(
let mut line = self.get_line_from_tile_number(
tile_number,
match y_flip {
false => y,
Expand Down

0 comments on commit d29aa3f

Please sign in to comment.