Some cleanup; Delayed rendering

SDL will only used as soon as all pixels have been described
in the internal buffer.
This commit is contained in:
Kevin Hamacher 2016-06-01 23:47:07 +02:00
parent 458fec99dd
commit dc0656475f
3 changed files with 198 additions and 116 deletions

View File

@ -6,7 +6,7 @@ enum MemoryBankControllerType {
MBC1,
MBC2,
MBC3,
HuC1,
//HuC1,
}
#[derive(Debug)]
@ -18,7 +18,6 @@ enum RamSize {
}
pub struct Cartridge {
mbc_type: MemoryBankControllerType,
mbc: Box<super::mbc::mbc::MBC>,
}
@ -67,12 +66,11 @@ impl Cartridge {
MemoryBankControllerType::MBC1 => Box::new(super::mbc::mbc1::MBC1::new(rom, ram)),
MemoryBankControllerType::MBC2 => Box::new(super::mbc::mbc2::MBC2::new(rom, ram)),
MemoryBankControllerType::MBC3 => Box::new(super::mbc::mbc3::MBC3::new(rom, ram)),
_ => panic!("{:?} not implemented", mbc_type),
// _ => panic!("{:?} not implemented", mbc_type),
};
Cartridge {
mbc: mbc,
mbc_type: mbc_type,
}
}

View File

@ -565,12 +565,6 @@ impl CPU {
self.set_8bit_reg(b, value as u8);
}
fn dump_stack(&self) {
for i in self.sp .. 0xFFFE {
println!("[{:#04X}]: {:#02X}", i, self.interconnect.read_byte(i));
}
}
#[inline]
fn call(&mut self, dst: u16) {
let ip = self.ip;
@ -1652,7 +1646,6 @@ impl CPU {
_ => panic!("Unknown instruction: {:02x}", instruction)
};
}
// self.dump_stack();
self.interconnect.tick(cycles);
}
}

View File

@ -14,13 +14,16 @@ const TICKS_END_HBLANK: u16 = 204;
const TICKS_END_VBLANK: u16 = 456;
// Display size
const GB_PIXELS_X: u16 = 160;
const GB_PIXELS_Y: u16 = 144;
const SCALE: u16 = 4;
const GB_PIXELS_X: usize = 160;
const GB_PIXELS_Y: usize = 144;
const SCALE: usize = 4;
const WND_RES_X: usize = GB_PIXELS_X * SCALE;
const WND_RES_Y: usize = GB_PIXELS_Y * SCALE;
// Control flags
const CTRL_LCD_DISPLAY_ENABLE: u8 = 1 << 7;
const CTRL_TILE_MAP_SELECT: u8 = 1 << 6;
const CTRL_WND_TILE_MAP_SELECT: u8 = 1 << 6;
const CTRL_WND_DISPLAY_ENABLE: u8 = 1 << 5;
const CTRL_BG_WINDOW_TILE_DATA_SELECT: u8 = 1 << 4;
const CTRL_BG_TILE_MAP_SELECT: u8 = 1 << 3;
@ -35,22 +38,13 @@ const STAT_MODE_VBLANK_INT: u8 = 1 << 4;
const STAT_MODE_HBLANK_INT: u8 = 1 << 3;
// Sprite flags
/*
Bit7 OBJ-to-BG Priority (0=OBJ Above BG, 1=OBJ Behind BG color 1-3)
(Used for both BG and Window. BG color 0 is always behind OBJ)
Bit6 Y flip (0=Normal, 1=Vertically mirrored)
Bit5 X flip (0=Normal, 1=Horizontally mirrored)
Bit4 Palette number **Non CGB Mode Only** (0=OBP0, 1=OBP1)
Bit3 Tile VRAM-Bank **CGB Mode Only** (0=Bank 0, 1=Bank 1)
Bit2-0 Palette number **CGB Mode Only** (OBP0-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
const SPRITE_TILE_VRAM_BANK: u8 = 1 << 3; // CGB only
// const SPRITE_TILE_VRAM_BANK: u8 = 1 << 3; // CGB only
// Display color
const MONOCHROME_PALETTE: &'static [[f64; 3]; 4] = &[
[0.605, 0.734, 0.059],
[0.543, 0.672, 0.059],
@ -58,6 +52,29 @@ const MONOCHROME_PALETTE: &'static [[f64; 3]; 4] = &[
[0.059, 0.219, 0.059],
];
#[derive(Debug, Copy, Clone)]
enum PixelOrigin {
Empty,
Background,
Window,
Sprite
}
#[derive(Copy, Clone)]
struct Pixel {
origin: PixelOrigin,
color: sdl2::pixels::Color,
}
impl Default for Pixel {
fn default() -> Pixel {
Pixel {
origin: PixelOrigin::Empty,
color: sdl2::pixels::Color::RGB(255, 0, 0),
}
}
}
#[derive(Debug)]
enum DisplayMode {
ReadOAMMemory,
@ -85,19 +102,19 @@ impl Sprite {
}
fn is_foreground(&self) -> bool {
self.flags & SPRITE_OBJ_BG_PRIORITY == 0
(self.flags & SPRITE_OBJ_BG_PRIORITY) == 0
}
fn is_x_flipped(&self) -> bool {
self.flags & SPRITE_X_FLIP == SPRITE_X_FLIP
(self.flags & SPRITE_X_FLIP) == SPRITE_X_FLIP
}
fn is_y_flipped(&self) -> bool {
self.flags & SPRITE_Y_FLIP == SPRITE_Y_FLIP
(self.flags & SPRITE_Y_FLIP) == SPRITE_Y_FLIP
}
fn palette(&self) -> u8 {
if self.flags & SPRITE_PALETTE_NO == 0 {
if (self.flags & SPRITE_PALETTE_NO) == 0 {
0
} else {
1
@ -131,15 +148,18 @@ pub struct Display {
vblank_interrupt: bool,
stat_interrupt: bool,
pixels: [Pixel; GB_PIXELS_X * GB_PIXELS_Y],
}
impl Display {
pub fn new() -> Display {
let sdl_ctx = sdl2::init().unwrap();
let video_ctx = sdl_ctx.video().unwrap();
let wnd = video_ctx.window("RustBoy", (GB_PIXELS_X * SCALE) as u32, (GB_PIXELS_Y * SCALE) as u32).position_centered().build().expect("Failed to create window :<");
let wnd = video_ctx.window("RustBoy", WND_RES_X as u32, WND_RES_Y as u32).position_centered().build().expect("Failed to create window :<");
let renderer = wnd.renderer().build().expect("Could not build renderer");
let event_pump = sdl_ctx.event_pump().expect("Getting event pump failed");
let pixels = [Pixel::default(); (GB_PIXELS_X as usize) * (GB_PIXELS_Y as usize)];
Display {
control: 0,
@ -164,14 +184,50 @@ impl Display {
vblank_interrupt: false,
stat_interrupt: false,
pixels: pixels,
}
}
#[inline]
fn set_pixel(&mut self, x: u8, y: u8, color: sdl2::pixels::Color) {
self.renderer.set_draw_color(color);
self.renderer.fill_rect(sdl2::rect::Rect::new((x as i32) * SCALE as i32, (y as i32) * SCALE as i32, SCALE as u32, SCALE as u32))
.expect("Draw failed");
fn set_pixel(&mut self, x: u8, y: u8, color: sdl2::pixels::Color, origin: PixelOrigin) {
let p = &mut self.pixels[(x as usize) + (y as usize) * GB_PIXELS_X];
p.color = color;
p.origin = origin;
}
#[inline]
fn get_pixel_origin(&self, x: u8, y: u8) -> PixelOrigin {
self.pixels[(x as usize) + (y as usize) * GB_PIXELS_X].origin
}
fn render_screen(&mut self) {
for y in 0 .. GB_PIXELS_Y {
for x in 0 .. GB_PIXELS_X {
let f = &mut self.pixels[(x as usize) + (y as usize) * GB_PIXELS_X];
self.renderer.set_draw_color(f.color);
self.renderer.fill_rect(
sdl2::rect::Rect::new(
x as i32 * SCALE as i32,
y as i32 * SCALE as i32,
SCALE as u32,
SCALE as u32,
)
).expect("Rendering failed");
// Clear origin after rendering
f.origin = PixelOrigin::Empty;
}
}
self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 0, 255));
self.renderer.draw_rect(
sdl2::rect::Rect::new(
0,
0,
(GB_PIXELS_X * SCALE) as u32,
(GB_PIXELS_Y * SCALE) as u32,
)
).expect("Rendering failed");
}
#[inline]
@ -199,38 +255,27 @@ impl Display {
#[inline]
pub fn write_byte(&mut self, addr: u16, val: u8) {
match addr {
0x8000 ... 0x9FFF => {
// println!("VRAM: Write {:02X} to {:04X}", val, addr);
self.vram[(addr - 0x8000) as usize] = val;
}
0xFE00 ... 0xFE9F => {
// println!("OAM: Write {:02X} to {:04X}", val, addr);
self.oam[(addr - 0xFE00) as usize] = val;
}
0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize] = val,
0xFE00 ... 0xFE9F => self.oam[(addr - 0xFE00) as usize] = val,
0xFF40 => self.control = val,
0xFF41 => self.status = val,
0xFF42 => self.scrolly = val,
0xFF43 => self.scrollx = val,
0xFF44 => self.curline = 0,
0xFF45 => self.lyc = val,
0xFF47 => {
println!("BG PALETTE = {:02X}", val);
self.background_palette = val;
}
0xFF48 => {
println!("OBJ0 PALETTE = {:02X}", val);
self.object_palette_0 = val;
}
0xFF49 => {
println!("OBJ1 PALETTE = {:02X}", val);
self.object_palette_1 = val;
}
0xFF47 => self.background_palette = val,
0xFF48 => self.object_palette_0 = val,
0xFF49 => self.object_palette_1 = val,
0xFF4A => {
println!("WY set to {:02X}", val);
if self.windowy != val {
println!("WY set to {:02X}", val);
}
self.windowy = val;
}
0xFF4B => {
println!("WX set to {:02X}", val);
if self.windowx != val {
println!("WX set to {:02X}", val);
}
self.windowx = val;
}
_ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr),
@ -300,11 +345,6 @@ impl Display {
if self.status & STAT_MODE_VBLANK_INT > 0 {
self.stat_interrupt = true;
}
// render frame.
self.renderer.present();
self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255));
self.renderer.clear();
} else {
self.current_mode = DisplayMode::ReadOAMMemory; // Mode 2 again
if self.status & STAT_MODE_OAM_INT > 0 {
@ -325,6 +365,10 @@ impl Display {
}
if self.curline > 153 {
self.current_mode = DisplayMode::ReadOAMMemory; // Mode 2, scanline.
self.render_screen();
self.renderer.present();
self.renderer.set_draw_color(sdl2::pixels::Color::RGB(255, 255, 255));
self.renderer.clear();
self.curline = 0;
if self.status & STAT_MODE_OAM_INT > 0 {
self.stat_interrupt = true;
@ -346,9 +390,14 @@ impl Display {
}
}
fn render_sprites(&mut self, sprites: &Vec<Sprite>, foreground: bool) {
fn render_sprites(&mut self, sprites: &Vec<Sprite>) {
if self.control & CTRL_BG_SPRITE_ENABLE > 0 {
let mut num_rendered: u8 = 0;
for i in 0 .. 39 {
// Gameboy limitation
if num_rendered > 10 {
break;
}
let sprite = &sprites[i];
// Skip hidden sprites
@ -356,12 +405,6 @@ impl Display {
continue;
}
// Should we draw this sprite this iteration
if foreground && !sprite.is_foreground() {
continue;
} else if !foreground && sprite.is_foreground() {
continue;
}
// Calculate correct coords
let x: u8 = sprite.x.wrapping_sub(8);
@ -371,6 +414,7 @@ impl Display {
// 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,
@ -396,8 +440,17 @@ impl Display {
for x_o in 0 .. limit {
let b1: bool;
let b2: bool;
let mut factor = 0;
let scr_x = x.wrapping_add(x_o);
let scr_y = render_y;
let pixel_origin = self.get_pixel_origin(scr_x, scr_y);
// Do not draw if the sprite should be drawn in the background
if !sprite.is_foreground() {
match pixel_origin {
PixelOrigin::Background | PixelOrigin::Window => continue,
_ => {}
}
}
let x_o2: u8 = match sprite.is_x_flipped() {
true => x_o ^ 7,
false => x_o,
@ -411,11 +464,6 @@ impl Display {
b2 = (tile_line_2 & 1 << (7 - x_o2)) > 0;
}
// -> self.background_palette.
//0xFF47 => self.background_palette = val,
//0xFF48 => self.object_palette_0 = val,
//0xFF49 => self.object_palette_1 = val,
// Sprites may be transparent.
if !b1 && !b2 {
continue;
@ -440,24 +488,43 @@ impl Display {
let entry = MONOCHROME_PALETTE[lookup[c as usize] as usize];
// Draw stuff. We're currently only in monochrome mode
self.set_pixel(x.wrapping_add(x_o), render_y, sdl2::pixels::Color::RGB((entry[0]*255.0) as u8, (entry[1]*255.0) as u8, (entry[2]*255.0) as u8));
self.set_pixel(x.wrapping_add(x_o), render_y, sdl2::pixels::Color::RGB((entry[0]*255.0) as u8, (entry[1]*255.0) as u8, (entry[2]*255.0) as u8), PixelOrigin::Sprite);
}
}
}
}
}
#[inline]
fn get_bg_window_tile_addr(&self, tile_id: u8) -> usize {
if (self.control & CTRL_BG_WINDOW_TILE_DATA_SELECT) == CTRL_BG_WINDOW_TILE_DATA_SELECT {
let base_addr = 0x0000;
base_addr + (tile_id as usize) * 16
} else {
let base_addr = 0x0800;
let tile_id = (128u8 as i8).wrapping_add(tile_id as i8) as u8;
base_addr + (tile_id as usize) * 16
}
}
#[inline]
fn renderscan(&mut self) {
// Points to the background map offset to use.
let background_map: usize;
let window_map: usize;
// verify!
if self.control & CTRL_BG_TILE_MAP_SELECT > 0 {
if self.control & CTRL_BG_TILE_MAP_SELECT == CTRL_BG_TILE_MAP_SELECT {
background_map = 0x1C00;
} else {
background_map = 0x1800;
}
if self.control & CTRL_WND_TILE_MAP_SELECT == CTRL_WND_TILE_MAP_SELECT {
window_map = 0x1C00;
} else {
window_map = 0x1800;
}
// Those are pixel units, not tile units.
let map_offset_y: u8 = self.scrolly;
let map_offset_x: u8 = self.scrollx;
@ -489,55 +556,27 @@ impl Display {
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));
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)
).expect("Draw failed");
}
}
if (self.control & CTRL_BG_DISPLAY) == CTRL_BG_DISPLAY {
// Render pixels (20 tiles)
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 / 8;
let tile_offset_x: u8 = render_abs_x % 8;
let tile_index_x: u8 = render_abs_x >> 3;
let tile_offset_x: u8 = render_abs_x & 7;
let tile_index_y: u8 = render_abs_y / 8;
let tile_offset_y: u8 = render_abs_y % 8;
let tile_index_y: u8 = render_abs_y >> 3;
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;
// Obtain tile ID in this area
let mut tile_id = self.vram[vram_offset];
let tile_id = self.vram[vram_offset];
// Obtain tile information
let tile_base_addr: usize;
if self.control & CTRL_BG_WINDOW_TILE_DATA_SELECT > 0 {
tile_base_addr = 0x0000;
} else {
tile_base_addr = 0x0800;
// This set goes from -127 to 127.
// TODO: Fix this. (?)
let s_tid: i8 = tile_id as i8;
tile_id = ((128u8 as i8).wrapping_add(s_tid)) as u8;
// panic!("OH MY GOD, this wasn't tested yet");
}
let tile_base_addr: usize = tile_base_addr + (tile_id as usize) * 16;
let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id);
let tile_line_1 = self.vram[tile_base_addr + (tile_offset_y as usize) * 2];
let tile_line_2 = self.vram[tile_base_addr + (tile_offset_y as usize) * 2 + 1];
@ -545,6 +584,7 @@ impl Display {
let b1: bool = (tile_line_1 & 1 << (7 - tile_offset_x)) > 0;
let b2: bool = (tile_line_2 & 1 << (7 - tile_offset_x)) > 0;
// Lookup the color
let c = (b1 as u8) * 2 + b2 as u8;
let lookup: [u8; 4] = [
self.background_palette & 3,
@ -554,12 +594,63 @@ impl Display {
];
let entry = MONOCHROME_PALETTE[lookup[c as usize] as usize];
// Draw stuff. We're currently only in monochrome mode
self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB((entry[0]*255.0) as u8, (entry[1]*255.0) as u8, (entry[2]*255.0) as u8));
let origin = match c {
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
_ => PixelOrigin::Background,
};
self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB((entry[0]*255.0) as u8, (entry[1]*255.0) as u8, (entry[2]*255.0) as u8), origin);
}
if self.control & CTRL_BG_SPRITE_ENABLE > 0 {
self.render_sprites(&queue, true);
if (self.control & CTRL_BG_SPRITE_ENABLE) == CTRL_BG_SPRITE_ENABLE {
self.render_sprites(&queue);
}
}
if (self.control & CTRL_WND_DISPLAY_ENABLE) == CTRL_WND_DISPLAY_ENABLE {
// Draw 'window' over the background.
// Screen coordinates of the top left corner are WX-7, WY
if self.windowx < 167 && self.windowy < 144 {
//let rx = self.windowx.wrapping_add(7);
let rx = 7u8.wrapping_sub(self.windowx);
let ry = render_y.wrapping_add(self.windowy);
for r_x in 0 .. 160u8 {
let render_x = r_x.wrapping_add(rx);
// Absolute render coordinates
let tile_index_x: u8 = render_x >> 3;
let tile_offset_x: u8 = render_x & 7;
let tile_index_y: u8 = ry >> 3;
let tile_offset_y: u8 = ry & 7;
let vram_offset: usize = window_map + (tile_index_y as usize) * 32 + tile_index_x as usize;
// Obtain tile ID in this area
let tile_id = self.vram[vram_offset];
// Obtain tile information
let tile_base_addr: usize = self.get_bg_window_tile_addr(tile_id);
let tile_line_1 = self.vram[tile_base_addr + (tile_offset_y as usize) * 2];
let tile_line_2 = self.vram[tile_base_addr + (tile_offset_y as usize) * 2 + 1];
// 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;
let c = (b1 as u8) * 2 + b2 as u8;
let lookup: [u8; 4] = [
self.background_palette & 3,
(self.background_palette >> 2) & 3,
(self.background_palette >> 4) & 3,
(self.background_palette >> 6) & 3,
];
let entry = MONOCHROME_PALETTE[lookup[c as usize] as usize];
// Draw stuff. We're currently only in monochrome mode
let origin = match c {
0 => PixelOrigin::Empty, // Hack so that objects will be in front of it.
_ => PixelOrigin::Window,
};
self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB((entry[0]*255.0) as u8, (entry[1]*255.0) as u8, (entry[2]*255.0) as u8), origin);
}
}
}
}