Fix sprite color & wide sprites

This commit is contained in:
Kevin Hamacher 2020-02-18 15:40:03 +01:00
parent 261fa4d78a
commit 2bbcba2152
2 changed files with 65 additions and 50 deletions

View File

@ -1,6 +1,8 @@
extern crate libc; extern crate libc;
extern crate sdl2; extern crate sdl2;
use std::io::Write;
// Internal ram size // Internal ram size
const VRAM_SIZE: usize = 0x2000; const VRAM_SIZE: usize = 0x2000;
@ -71,7 +73,15 @@ impl CgbPalette {
let r = (v & 0b1_1111) as u8; let r = (v & 0b1_1111) as u8;
let g = ((v >> 5) & 0b1_1111) as u8; let g = ((v >> 5) & 0b1_1111) as u8;
let b = ((v >> 10) & 0b1_1111) as u8; let b = ((v >> 10) & 0b1_1111) as u8;
sdl2::pixels::Color::RGB(r << 3, g << 3, b << 3) // According to some code:
// Real colors:
let r = r as u16;
let g = g as u16;
let b = b as u16;
let rR = ((r * 13 + g * 2 + b) >> 1) as u8;
let rG = ((g * 3 + b) << 1) as u8;
let rB = ((r * 3 + g * 2 + b * 11) >> 1) as u8;
sdl2::pixels::Color::RGB(rR, rG, rB)
} }
} }
@ -198,7 +208,7 @@ pub struct Display {
vram_bank: u8, vram_bank: u8,
current_ticks: u16, current_ticks: u16,
current_mode: DisplayMode, current_mode: DisplayMode,
// TODO
renderer: sdl2::render::Canvas<sdl2::video::Window>, renderer: sdl2::render::Canvas<sdl2::video::Window>,
pub event_pump: sdl2::EventPump, pub event_pump: sdl2::EventPump,
@ -216,7 +226,9 @@ pub struct Display {
background_palette_index: u8, background_palette_index: u8,
background_palette_cgb: [CgbPalette; 0x40 / 8], background_palette_cgb: [CgbPalette; 0x40 / 8],
object_palette_autoinc: bool,
object_palette_index: u8, object_palette_index: u8,
object_palette_cgb: [CgbPalette; 0x40 / 8],
} }
impl Display { impl Display {
@ -263,7 +275,9 @@ impl Display {
background_palette_autoinc: false, background_palette_autoinc: false,
background_palette_index: 0, background_palette_index: 0,
background_palette_cgb: [CgbPalette([0u8; 2 * 4]); 0x40 / 8], background_palette_cgb: [CgbPalette([0u8; 2 * 4]); 0x40 / 8],
object_palette_autoinc: false,
object_palette_index: 0, object_palette_index: 0,
object_palette_cgb: [CgbPalette([0u8; 2 * 4]); 0x40 / 8],
} }
} }
@ -360,19 +374,29 @@ impl Display {
} }
0xFF69 => { 0xFF69 => {
let idx = self.background_palette_index as usize; let idx = self.background_palette_index as usize;
if idx < 64 {
self.background_palette_cgb[idx / 8].0[idx % 8] = val; self.background_palette_cgb[idx / 8].0[idx % 8] = val;
} else {
panic!("OOB palette w/ autoinc");
}
if self.background_palette_autoinc { if self.background_palette_autoinc {
self.background_palette_index += 1; self.background_palette_index += 1;
if self.background_palette_index >= 8 {
self.background_palette_index = 0;
}
} }
} }
0xFF6A => { 0xFF6A => {
self.object_palette_index = val; self.object_palette_index = val & 0b0111_1111;
self.object_palette_autoinc = (val >> 7) != 0;
} }
0xFF6B => { 0xFF6B => {
self.object_palette[self.object_palette_index as usize] = val; let idx = self.object_palette_index as usize;
if idx < 64 {
self.object_palette_cgb[idx / 8].0[idx % 8] = val;
} else {
panic!("OOB obj palette w/ autoinc");
}
if self.object_palette_autoinc {
self.object_palette_index += 1;
}
} }
0xFF4A => { 0xFF4A => {
if self.windowy != val { if self.windowy != val {
@ -557,20 +581,25 @@ impl Display {
// Calculate correct coords // Calculate correct coords
let x: u8 = sprite.x.wrapping_sub(8); let x: u8 = sprite.x.wrapping_sub(8);
// Flip sprite, TODO: What does this do in WIDE mode?
let y: u8 = sprite.y.wrapping_sub(16); let y: u8 = sprite.y.wrapping_sub(16);
let render_y = self.curline; let render_y = self.curline;
// Is this sprite on the current line? // Is this sprite on the current line?
if y.wrapping_add(8) > render_y && y <= render_y { let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0;
num_rendered += 1; let actual_h = match wide_mode {
// Flip sprite, TODO: Validate true => 16,
let y_o: u8 = match sprite.is_y_flipped() { false => 8,
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; // Should this be twice as wide in wide mode? if sprite.is_y_flipped() {
panic!("Sorry, no y flip support yet");
}
if y.wrapping_add(actual_h) > render_y && y <= render_y {
num_rendered += 1;
let tile_offset_y: usize = render_y as usize - y as usize;
let tile_base_addr: usize = sprite.tile as usize * 16;
let tile_addr = tile_base_addr + tile_offset_y * 2; let tile_addr = tile_base_addr + tile_offset_y * 2;
let vram = if sprite.vram_bank() == 0 { let vram = if sprite.vram_bank() == 0 {
@ -580,16 +609,8 @@ impl Display {
}; };
let tile_line_1 = vram[tile_addr + 0]; let tile_line_1 = vram[tile_addr + 0];
let tile_line_2 = vram[tile_addr + 1]; let tile_line_2 = vram[tile_addr + 1];
let tile_line_3 = vram[tile_addr + 2];
let tile_line_4 = vram[tile_addr + 3];
// We need to draw this. for x_o in 0..8 {
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 b1: bool;
let b2: bool; let b2: bool;
@ -604,21 +625,12 @@ impl Display {
} }
} }
if wide_mode && x_o > 7 { let x_maybe_flipped: u8 = match sprite.is_x_flipped() {
let x_o2: u8 = match sprite.is_x_flipped() {
true => (x_o - 8) ^ 7,
false => (x_o - 8),
};
b1 = (tile_line_3 & 1 << (7 - x_o2)) > 0;
b2 = (tile_line_4 & 1 << (7 - x_o2)) > 0;
} else {
let x_o2: u8 = match sprite.is_x_flipped() {
true => x_o ^ 7, true => x_o ^ 7,
false => x_o, false => x_o,
}; };
b1 = (tile_line_1 & 1 << (7 - x_o2)) > 0; b1 = (tile_line_1 & 1 << (7 - x_o)) > 0;
b2 = (tile_line_2 & 1 << (7 - x_o2)) > 0; b2 = (tile_line_2 & 1 << (7 - x_o)) > 0;
}
// Sprites may be transparent. // Sprites may be transparent.
if !b1 && !b2 { if !b1 && !b2 {
@ -654,8 +666,11 @@ impl Display {
); );
*/ */
let c = ((b1 as u8) * 2 + b2 as u8) as usize; let c = ((b1 as u8) * 2 + b2 as u8) as usize;
let c = self.background_palette_cgb[sprite.palette() as usize].get_color(c); if c == 0 {
self.set_pixel(x.wrapping_add(x_o), render_y, c, PixelOrigin::Sprite); continue;
}
let c = self.object_palette_cgb[sprite.palette() as usize].get_color(c);
self.set_pixel(x.wrapping_add(x_maybe_flipped), render_y, c, PixelOrigin::Sprite);
} }
} }
} }
@ -730,16 +745,15 @@ impl Display {
// Render background // Render background
if (self.control & CTRL_BG_DISPLAY) == CTRL_BG_DISPLAY { if (self.control & CTRL_BG_DISPLAY) == CTRL_BG_DISPLAY {
// Render pixels (20 tiles) // Render pixels (20 tiles)
let render_abs_y = map_offset_y.wrapping_add(render_y);
let tile_index_y: u8 = render_abs_y >> 3;
let tile_offset_y: u8 = render_abs_y & 7;
for render_x in 0..160 { for render_x in 0..160 {
// Absolute render coordinates // Absolute render coordinates
let render_abs_x = map_offset_x.wrapping_add(render_x); let render_abs_x = map_offset_x.wrapping_add(render_x);
let render_abs_y = map_offset_y.wrapping_add(render_y);
let tile_index_x: u8 = render_abs_x >> 3; let tile_index_x: u8 = render_abs_x >> 3;
let tile_index_y: u8 = render_abs_y >> 3;
let tile_offset_x: u8 = render_abs_x & 7; let tile_offset_x: u8 = render_abs_x & 7;
let tile_offset_y: u8 = render_abs_y & 7;
let vram_offset: usize = let vram_offset: usize =
background_map + ((tile_index_y as usize) * 32) + tile_index_x as usize; background_map + ((tile_index_y as usize) * 32) + tile_index_x as usize;

View File

@ -174,6 +174,7 @@ impl Interconnect {
.. ..
} => { } => {
self.cartridge.save(); self.cartridge.save();
self.display.dump_vram();
return TickResult::Shutdown; return TickResult::Shutdown;
} }
Event::KeyDown { Event::KeyDown {
@ -361,14 +362,14 @@ impl Interconnect {
self.sound self.sound
.sound_object .sound_object
.lock() .lock()
.unwrap() .expect("Sound-related crash")
.write_byte(addr, val); .write_byte(addr, val);
} }
0xFF30..=0xFF3F => self 0xFF30..=0xFF3F => self
.sound .sound
.sound_object .sound_object
.lock() .lock()
.unwrap() .expect("Sound related crash")
.write_byte(addr, val), .write_byte(addr, val),
// Exclude DMA transfer, we will do this below // Exclude DMA transfer, we will do this below
0xFF40..=0xFF45 | 0xFF47..=0xFF4B => { 0xFF40..=0xFF45 | 0xFF47..=0xFF4B => {