From 48a1978eb0fae643ec2e06b11fef33f5f64edf7b Mon Sep 17 00:00:00 2001 From: Nathan Leiby Date: Thu, 19 Dec 2024 15:40:40 -0500 Subject: [PATCH] feat: hacky scrolling (horiz only) --- src/main.rs | 2 +- src/ppu.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/render.rs | 21 ++++++++++++------- 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7447eff..b3140c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -83,7 +83,7 @@ fn main() -> Result<(), Box> { move |ppu: &Ppu, gamepad1: &mut GamepadRegister, _gamepad2: &mut GamepadRegister| { // compute the screen's content from the PPU let mut frame = Frame::new(); - ppu.draw_background(&mut frame); + ppu.draw_scrollable_background(&mut frame); ppu.draw_sprites(&mut frame); // redraw the screen diff --git a/src/ppu.rs b/src/ppu.rs index 93c5a68..8e773c0 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -182,7 +182,60 @@ impl Ppu { let tile_n = self.vram[self.mirror_vram_addr((nt_start + offset) as u16) as usize]; let bgp_idx = self.palette_for_bg_tile((x, y), nt_start); let palette = self.lookup_palette(bgp_idx); - frame.draw_bg_tile(pattern_table, tile_n as usize, (x, y), palette); + frame.draw_bg_tile(pattern_table, tile_n as usize, (x, y), 0, palette); + } + } + } + + /// Draws the entire possible background of 2 frames, but it gets filtered within, which you can then filter by applying a windowed viewport + pub fn draw_scrollable_background(&self, frame: &mut Frame) { + let bank = self.get_background_pattern_bank(); + let pattern_table = + &self.chr_rom[bank * PATTERN_TABLE_SIZE..(bank + 1) * PATTERN_TABLE_SIZE]; + + // In horizontal scrolling, this is the nametable at the LEFT of the screen + let which_nametable = self + .registers + .control + .intersection(ControlRegister::NAMETABLE) + .bits() as usize; + assert!(which_nametable <= 3); + let nt_start = which_nametable * 0x400; + + // self.registers.control. + + // Determine which nametable is on the left + let nts = if nt_start == 0 { + [0, 0x400] + } else { + [0x400, 0x800] + }; + + let x_scroll: usize = self.registers.scroll.x_scroll as usize; + let y_scroll: usize = self.registers.scroll.y_scroll as usize; + // TODO + // if self.registers.control.contains(ControlRegister::X_SCROLL) { + // x_scroll += 256; + // } + + let rows = 30; + let cols = 32; + for tile_y in 0..rows { + for (nt_idx, &nt_start) in nts.iter().enumerate() { + for tile_x in 0..cols { + let offset = tile_y * cols + tile_x; + let tile_n = + self.vram[self.mirror_vram_addr((nt_start + offset) as u16) as usize]; + let bgp_idx = self.palette_for_bg_tile((tile_x, tile_y), nt_start); + let palette = self.lookup_palette(bgp_idx); + frame.draw_bg_tile( + pattern_table, + tile_n as usize, + (tile_x + cols * (nt_idx), tile_y), + x_scroll, + palette, + ); + } } } } @@ -477,8 +530,7 @@ impl Ppu { let sprite0 = self.parse_sprite_from_oam_data(&self.oam_data[0..4]); let (scanline_y, scanline_x) = self.get_tick_status(); - scanline_y >= sprite0.y as usize - && (scanline_x % CYCLES_PER_SCANLINE) >= sprite0.x as usize + scanline_y >= sprite0.y as usize && (scanline_x % CYCLES_PER_SCANLINE) >= sprite0.x as usize } } diff --git a/src/render.rs b/src/render.rs index 97a346d..52e0f13 100644 --- a/src/render.rs +++ b/src/render.rs @@ -48,6 +48,7 @@ impl Frame { pub fn new() -> Self { Self { + // TODO: Consider a 'double-sized' frame with a viewport() function data: vec![0; Frame::WIDTH * Frame::HEIGHT * 3], } } @@ -68,6 +69,8 @@ impl Frame { pattern_table: &[u8], tile_n: usize, pos: (usize, usize), + x_scroll: usize, + // y_scroll: usize, palette: [u8; 4], ) { let tile = get_tile(pattern_table, tile_n); @@ -75,11 +78,15 @@ impl Frame { for (row, row_data) in tile.iter().enumerate() { for (col, &palette_idx) in row_data.iter().enumerate() { let color = SYSTEM_PALETTE[palette[palette_idx as usize] as usize]; - self.set_pixel( - (tile_x * TILE_SIZE_PIXELS) + col, - (tile_y * TILE_SIZE_PIXELS) + row, - color, - ); + let left_x = (tile_x * TILE_SIZE_PIXELS) + col; + // only draw pixels within the screen width + if x_scroll <= left_x && left_x - x_scroll <= 256 { + self.set_pixel( + (tile_x * TILE_SIZE_PIXELS) + col - x_scroll, + (tile_y * TILE_SIZE_PIXELS) + row, + color, + ); + } } } } @@ -156,7 +163,7 @@ mod test { let mut f = Frame::new(); assert_eq!(f.data[0], 0); - f.draw_bg_tile(&pattern_table, tile_n, pos, palette); + f.draw_bg_tile(&pattern_table, tile_n, pos, 0, palette); assert_eq!(f.data[0], 128); assert_eq!(f.data[1], 128); assert_eq!(f.data[2], 128); @@ -175,7 +182,7 @@ mod test { let palette = [65, 65, 65, 3]; // 65 should crash if read, since it's OOB the palette with 64 colors let mut f = Frame::new(); - f.draw_bg_tile(&pattern_table, tile_n, pos, palette); + f.draw_bg_tile(&pattern_table, tile_n, pos, 0, palette); // verify we drew the entire tile in the one selected color for row in 0..8 {