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 sdl2;
use std::io::Write;
// Internal ram size
const VRAM_SIZE: usize = 0x2000;
@ -71,7 +73,15 @@ impl CgbPalette {
let r = (v & 0b1_1111) as u8;
let g = ((v >> 5) & 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,
current_ticks: u16,
current_mode: DisplayMode,
// TODO
renderer: sdl2::render::Canvas<sdl2::video::Window>,
pub event_pump: sdl2::EventPump,
@ -216,7 +226,9 @@ pub struct Display {
background_palette_index: u8,
background_palette_cgb: [CgbPalette; 0x40 / 8],
object_palette_autoinc: bool,
object_palette_index: u8,
object_palette_cgb: [CgbPalette; 0x40 / 8],
}
impl Display {
@ -263,7 +275,9 @@ impl Display {
background_palette_autoinc: false,
background_palette_index: 0,
background_palette_cgb: [CgbPalette([0u8; 2 * 4]); 0x40 / 8],
object_palette_autoinc: false,
object_palette_index: 0,
object_palette_cgb: [CgbPalette([0u8; 2 * 4]); 0x40 / 8],
}
}
@ -360,19 +374,29 @@ impl Display {
}
0xFF69 => {
let idx = self.background_palette_index as usize;
if idx < 64 {
self.background_palette_cgb[idx / 8].0[idx % 8] = val;
} else {
panic!("OOB palette w/ autoinc");
}
if self.background_palette_autoinc {
self.background_palette_index += 1;
if self.background_palette_index >= 8 {
self.background_palette_index = 0;
}
}
}
0xFF6A => {
self.object_palette_index = val;
self.object_palette_index = val & 0b0111_1111;
self.object_palette_autoinc = (val >> 7) != 0;
}
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 => {
if self.windowy != val {
@ -557,20 +581,25 @@ impl Display {
// Calculate correct coords
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 render_y = self.curline;
// Is this sprite on the current line?
if y.wrapping_add(8) > render_y && y <= render_y {
num_rendered += 1;
// Flip sprite, TODO: Validate
let y_o: u8 = match sprite.is_y_flipped() {
true => y ^ 7,
false => y,
let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0;
let actual_h = match wide_mode {
true => 16,
false => 8,
};
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 vram = if sprite.vram_bank() == 0 {
@ -580,16 +609,8 @@ impl Display {
};
let tile_line_1 = vram[tile_addr + 0];
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.
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 {
for x_o in 0..8 {
let b1: bool;
let b2: bool;
@ -604,21 +625,12 @@ impl Display {
}
}
if wide_mode && x_o > 7 {
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() {
let x_maybe_flipped: u8 = match sprite.is_x_flipped() {
true => x_o ^ 7,
false => x_o,
};
b1 = (tile_line_1 & 1 << (7 - x_o2)) > 0;
b2 = (tile_line_2 & 1 << (7 - x_o2)) > 0;
}
b1 = (tile_line_1 & 1 << (7 - x_o)) > 0;
b2 = (tile_line_2 & 1 << (7 - x_o)) > 0;
// Sprites may be transparent.
if !b1 && !b2 {
@ -654,8 +666,11 @@ impl Display {
);
*/
let c = ((b1 as u8) * 2 + b2 as u8) as usize;
let c = self.background_palette_cgb[sprite.palette() as usize].get_color(c);
self.set_pixel(x.wrapping_add(x_o), render_y, c, PixelOrigin::Sprite);
if c == 0 {
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
if (self.control & CTRL_BG_DISPLAY) == CTRL_BG_DISPLAY {
// 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 {
// Absolute render coordinates
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_y: u8 = render_abs_y >> 3;
let tile_offset_x: u8 = render_abs_x & 7;
let tile_offset_y: u8 = render_abs_y & 7;
let vram_offset: 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.display.dump_vram();
return TickResult::Shutdown;
}
Event::KeyDown {
@ -361,14 +362,14 @@ impl Interconnect {
self.sound
.sound_object
.lock()
.unwrap()
.expect("Sound-related crash")
.write_byte(addr, val);
}
0xFF30..=0xFF3F => self
.sound
.sound_object
.lock()
.unwrap()
.expect("Sound related crash")
.write_byte(addr, val),
// Exclude DMA transfer, we will do this below
0xFF40..=0xFF45 | 0xFF47..=0xFF4B => {