From 509a76924aa5dc18a6c305de6a31868577d7ebc6 Mon Sep 17 00:00:00 2001 From: Kevin Hamacher Date: Wed, 1 Jun 2016 19:11:23 +0200 Subject: [PATCH] sprite cleanup part 1 --- src/cpu.rs | 16 +-- src/display.rs | 261 +++++++++++++++++++++++++++---------------------- 2 files changed, 150 insertions(+), 127 deletions(-) diff --git a/src/cpu.rs b/src/cpu.rs index 51b3cce..d092f85 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,11 +1,5 @@ use super::interconnect; -//const REG_B: usize = 0; -//const REG_C: usize = 1; -//const REG_D: usize = 2; -//const REG_E: usize = 3; -//const REG_H: usize = 4; -//const REG_L: usize = 5; const REG_A: usize = 6; const REG_N_B: usize = 0; @@ -874,7 +868,7 @@ impl CPU { } pub fn run_instruction(&mut self) { - //self.debug = !self.interconnect.is_boot_rom(); + // self.debug = !self.interconnect.is_boot_rom(); /* if self.ip == 0x553 { self.debug = true; @@ -1081,22 +1075,22 @@ impl CPU { if self.flags & FLAG_N == 0 { // Lower nibble - if self.flags & FLAG_H == FLAG_H || (v & 0xF) > 9 { + if ((self.flags & FLAG_H) == FLAG_H) || (v & 0xF) > 9 { v += 0x06; } // Higher nibble - if self.flags & FLAG_C == FLAG_C || v > 0x9F { + if ((self.flags & FLAG_C) == FLAG_C) || v > 0x9F { v += 0x60; } } else { // Lower nibble - if self.flags & FLAG_H == FLAG_H { + if (self.flags & FLAG_H) == FLAG_H { v = v.wrapping_sub(6) & 0xFF; } // Higher nibble - if self.flags & FLAG_C == FLAG_C { + if (self.flags & FLAG_C) == FLAG_C { v = v.wrapping_sub(0x60); } } diff --git a/src/display.rs b/src/display.rs index 0fc6bd1..28818ba 100644 --- a/src/display.rs +++ b/src/display.rs @@ -45,7 +45,7 @@ const STAT_MODE_HBLANK_INT: u8 = 1 << 3; Bit2-0 Palette number **CGB Mode Only** (OBP0-7) */ -const SPRITE_OBC_BG_PRIORITY: u8 = 1 << 7; +const SPRITE_OBJ_BG_PRIORITY: u8 = 1 << 7; const SPRITE_Y_FLIP: u8 = 1 << 6; const SPRITE_X_FLIP: u8 = 1 << 5; const SPRITE_PALETTE_NO: u8 = 1 << 4; // NonCGB only @@ -73,6 +73,24 @@ struct Sprite { flags: u8, } +impl Sprite { + fn is_hidden(&self) -> bool { + self.x == 0 || self.y == 0 + } + + fn is_foreground(&self) -> bool { + self.flags & SPRITE_OBJ_BG_PRIORITY == 0 + } + + fn is_x_flipped(&self) -> bool { + self.flags & SPRITE_X_FLIP == SPRITE_X_FLIP + } + + fn is_y_flipped(&self) -> bool { + self.flags & SPRITE_Y_FLIP == SPRITE_Y_FLIP + } +} + pub struct Display { control: u8, status: u8, @@ -255,7 +273,7 @@ impl Display { // render frame. self.renderer.present(); - self.renderer.set_draw_color(sdl2::pixels::Color::RGB(0, 0, 0)); + self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255)); self.renderer.clear(); } else { self.current_mode = DisplayMode::ReadOAMMemory; // Mode 2 again @@ -298,6 +316,87 @@ impl Display { } } + fn render_sprites(&mut self, sprites: &Vec, foreground: bool) { + if self.control & CTRL_BG_SPRITE_ENABLE > 0 { + for i in 0 .. 39 { + let sprite = &sprites[i]; + + if sprite.is_hidden() { + continue; + } + + if foreground && !sprite.is_foreground() { + continue; + } else if !foreground && sprite.is_foreground() { + continue; + } + + let x: u8 = sprite.x.wrapping_sub(8); + let y: u8 = sprite.y.wrapping_sub(16); + + let render_y = self.curline; + + // Is this sprite on the current line? + if y.wrapping_add(8) >= render_y && y <= render_y { + let y_o: u8 = match sprite.is_y_flipped() { + true => y ^ 7, + false => y + }; + let tile_offset_y: usize = render_y as usize - y_o as usize; + let tile_base_addr: usize = sprite.tile as usize * 16; + + let tile_line_1 = self.vram[tile_base_addr + tile_offset_y * 2 + 1]; + let tile_line_2 = self.vram[tile_base_addr + tile_offset_y * 2 + 1]; + let tile_line_3 = self.vram[tile_base_addr + tile_offset_y * 2 + 2]; + let tile_line_4 = self.vram[tile_base_addr + tile_offset_y * 2 + 3]; + + // We need to draw this. + let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0; + let limit = match wide_mode { + true => 16, + false => 8 + }; + for x_o in 0 .. limit { + let b1: bool; + let b2: bool; + let mut factor = 0; + + let x_o2: u8 = match sprite.is_x_flipped() { + true => x_o ^ 7, + false => x_o, + }; + + if wide_mode && x_o > 7 { + b1 = (tile_line_3 & 1 << (14 - x_o2)) > 0; + b2 = (tile_line_4 & 1 << (14 - x_o2)) > 0; + } else { + b1 = (tile_line_1 & 1 << (7 - x_o2)) > 0; + b2 = (tile_line_2 & 1 << (7 - x_o2)) > 0; + } + + if b1 { + factor += 64; + } + if b2 { + factor += 128; + } + + // Transparent. + if !b1 && !b2 { + continue; + } + + factor = 255 - factor; + + // Draw stuff. We're currently only in monochrome mode + self.set_pixel(x.wrapping_add(x_o), render_y, sdl2::pixels::Color::RGB(factor, factor, factor)); + + } + } + } + } + } + #[inline] fn renderscan(&mut self) { // Points to the background map offset to use. @@ -314,27 +413,50 @@ impl Display { let map_offset_x: u8 = self.scrollx; let render_y: u8 = self.curline; - - if self.control & CTRL_BG_SPRITE_ENABLE > 0 { - // panic!("Sprites not supported"); + // Order sprites by priority + let mut queue: Vec = Vec::new(); + for i in 0 .. 39 { + queue.push(Sprite{ + y: self.oam[i * 4 + 0], + x: self.oam[i * 4 + 1], + tile: self.oam[i * 4 + 2], + flags: self.oam[i * 4 + 3], + }); } + // This is the non-CGB priority. + // Smaller x coord = higher. + use std::cmp; + queue.sort_by(|x, y| { + if x.x > y.x { + cmp::Ordering::Greater + } else if x.x < y.x { + cmp::Ordering::Less + } else { + cmp::Ordering::Equal + } + }); + queue.reverse(); + + + // Draw sprites behind the background + self.render_sprites(&queue, false); + // Render background if self.control & CTRL_BG_DISPLAY > 0 { + // Draw borders if enabled + if false { + for t_x in 0 .. 160/8 { + self.renderer.set_draw_color(sdl2::pixels::Color::RGB(0xFF, 10, 0xFF)); - // Draw borders first. - /* - for t_x in 0 .. 160/8 { - self.renderer.set_draw_color(sdl2::pixels::Color::RGB(0xFF, 10, 0xFF)); - - let RX: i32 = (t_x as i32)*8 + map_offset_x as i32; - let RY: i32 = ((render_y as u32) & 0xFFFFFFF8) as i32 - map_offset_y as i32; - let TS: u32 = 8u32 * (SCALE as u32); - self.renderer.draw_rect( - sdl2::rect::Rect::new(RX * SCALE as i32, RY * SCALE as i32, TS, TS) - ); + let RX: i32 = (t_x as i32)*8 + map_offset_x as i32; + let RY: i32 = ((render_y as u32) & 0xFFFFFFF8) as i32 - map_offset_y as i32; + let TS: u32 = 8u32 * (SCALE as u32); + self.renderer.draw_rect( + sdl2::rect::Rect::new(RX * SCALE as i32, RY * SCALE as i32, TS, TS) + ); + } } - */ // Render pixels (20 tiles) for render_x in 0 .. 160 { // Absolute render coordinates @@ -372,6 +494,9 @@ impl Display { // Get the correct bit let b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) > 0; let b2: bool = (tile_line_2 & 1 << (7 - tile_offset_x)) > 0; + if !b1 && !b2 { + continue; + } let mut factor = 0; if b1 { @@ -381,112 +506,16 @@ impl Display { factor += 128; } + factor = 255 - factor; + // Draw stuff. We're currently only in monochrome mode self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB(factor, factor, factor)); } if self.control & CTRL_BG_SPRITE_ENABLE > 0 { - // Let's draw sprites. - // TODO: Sprites with smaller X coordinate should - // should be in front - // TODO: Draw only up to 10 sprites per line - if self.control & CTRL_BG_SPRITE_SIZE > 0 { - println!("Wide sprites not tested!"); - } - - // Order sprites by priority - let mut queue: Vec = Vec::new(); - for i in 0 .. 39 { - queue.push(Sprite{ - x: self.oam[i * 4 + 0], - y: self.oam[i * 4 + 1], - tile: self.oam[i * 4 + 2], - flags: self.oam[i * 4 + 3], - }); - } - - // This is the non-CGB priority. - // Smaller x coord = higher. - use std::cmp; - queue.sort_by(|x, y| { - if x.x > y.x { - cmp::Ordering::Greater - } else if x.x < y.x { - cmp::Ordering::Less - } else { - cmp::Ordering::Equal - } - }); - queue.reverse(); - - for i in 0 .. 39 { - let mut y: u8 = queue[i].x; - let mut x: u8 = queue[i].y; - let t_num: u8 = queue[i].tile; - let flags: u8 = queue[i].flags; - - if x == 0 || y == 0 { - // This sprite is hidden - continue; - } - - x = x.wrapping_sub(8); - y = y.wrapping_sub(16); - - // Is this sprite on the current line? - if y.wrapping_add(8) >= render_y && y <= render_y { - let y_o: u8 = match flags & SPRITE_Y_FLIP == SPRITE_Y_FLIP { - true => y ^ 7, - false => y - }; - let tile_offset_y: usize = render_y as usize - y_o as usize; - let tile_base_addr: usize = 0 + t_num as usize * 16; - - let tile_line_1 = self.vram[tile_base_addr + tile_offset_y * 2]; - let tile_line_2 = self.vram[tile_base_addr + tile_offset_y * 2 + 1]; - let tile_line_3 = self.vram[tile_base_addr + tile_offset_y * 2 + 2]; - let tile_line_4 = self.vram[tile_base_addr + tile_offset_y * 2 + 3]; - - // We need to draw this. - let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0; - let limit = match wide_mode { - true => 16, - false => 8 - }; - for x_o in 0 .. limit { - let b1: bool; - let b2: bool; - let mut factor = 0; - - let x_o2: u8 = match flags & SPRITE_X_FLIP == SPRITE_X_FLIP { - true => x_o ^ 7, - false => x_o, - }; - - if wide_mode && x_o > 7 { - b1 = (tile_line_3 & 1 << (14 - x_o2)) > 0; - b2 = (tile_line_4 & 1 << (14 - x_o2)) > 0; - } else { - b1 = (tile_line_1 & 1 << (7 - x_o2)) > 0; - b2 = (tile_line_2 & 1 << (7 - x_o2)) > 0; - } - - if b1 { - factor += 64; - } - if b2 { - factor += 128; - } - - // Draw stuff. We're currently only in monochrome mode - self.set_pixel(x.wrapping_add(x_o), render_y, sdl2::pixels::Color::RGB(factor, factor, factor)); - - } - } - } + self.render_sprites(&queue, true); } - } }