From ea8130527205a293fc7f3255617cd48bc20ee293 Mon Sep 17 00:00:00 2001 From: Kevin Hamacher Date: Thu, 26 May 2016 17:45:31 +0200 Subject: [PATCH] Started implementing graphics --- Cargo.toml | 2 + src/display.rs | 143 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 128 insertions(+), 17 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e6e8ab..97e19fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,3 +4,5 @@ version = "0.1.0" authors = ["Kevin Hamacher "] [dependencies] +sdl2 = "0.19.0" +libc = "*" diff --git a/src/display.rs b/src/display.rs index 949e571..a793bd5 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,10 +1,20 @@ +extern crate sdl2; +extern crate libc; + +// Internal ram size const VRAM_SIZE: usize = 0x2000; +// Timing values const TICKS_END_SCANLINE: u16 = 80; const TICKS_END_READMODE: u16 = 172; 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; + #[derive(Debug)] enum DisplayMode { Scanline, @@ -19,7 +29,6 @@ impl Default for DisplayMode { } } -#[derive(Debug, Default)] pub struct Display { control: u8, status: u8, @@ -37,10 +46,22 @@ pub struct Display { current_ticks: u16, current_mode: DisplayMode, // TODO + + + // For creating a window: +// sdl_context: sdl2::Sdl, +// video_context: sdl2::VideoSubsystem, +// window: sdl2::video::Window, + renderer: sdl2::render::Renderer<'static>, } 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 mut renderer = wnd.renderer().build().expect("Could not build renderer"); + Display { control: 0, status: 0, @@ -56,9 +77,18 @@ impl Display { current_ticks: 0, current_mode: DisplayMode::default(), vram: vec![0; VRAM_SIZE].into_boxed_slice(), +// sdl_context: sdl_ctx, +// video_context: video_ctx, +// window: wnd, + renderer: renderer } } + 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)); + } + pub fn write_byte(&mut self, addr: u16, val: u8) { match addr { 0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize] = val, @@ -107,6 +137,7 @@ impl Display { if self.current_ticks > TICKS_END_READMODE { self.current_ticks = 0; self.current_mode = DisplayMode::HBlank; + self.renderscan(); } }, DisplayMode::HBlank => { @@ -125,6 +156,9 @@ impl Display { self.current_ticks = 0; self.curline += 1; if self.curline > 153 { + self.renderer.present(); + self.renderer.set_draw_color(sdl2::pixels::Color::RGB(0, 0, 0)); + self.renderer.clear(); self.current_mode = DisplayMode::Scanline; self.curline = 0; } @@ -134,24 +168,99 @@ impl Display { } fn renderscan(&mut self) { - // TODO: Allow switching of tile map - let tilemap: u16 = 0x1800; - let map_offset_y: u8 = self.curline.wrapping_add(self.scrolly); - let map_offset_x: u8 = self.scrollx; - - let tile_index_y: u8 = map_offset_y / 8; - - // Render line - for render_x in 0 .. 159 { - let tile_index_x: u8 = render_x / 8; - let tile_offset_x: u8 = render_x % 8; - - // TODO: Draw bit - // let tile_base_addr = tilemap + tile_id*128 - // pixel(render_x, map_offset_y) := *tile_base_addr + 2* *(tile_base_addr+1) - //[tile_index_x][tile_index_y] + // Points to the background map offset to use. + let background_map: usize; + // verify! + if self.control & (1 << 3) > 0 { + background_map = 0x1C00; + } else { + background_map = 0x1800; } + // Those are pixel units, not tile units. + let map_offset_y: u8 = self.scrolly; + let map_offset_x: u8 = self.scrollx; + + let render_y: u8 = self.curline; + + if self.control & (1 << 1) > 0 { + panic!("Switch OBJ not supported"); + } + + // Render background + if self.control & (1 << 0) > 0 { + println!("Render background"); + // 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_y: u8 = render_abs_y / 8; + let tile_offset_y: u8 = render_abs_y % 8; + + let vram_offset: usize = background_map + (tile_index_y as usize) * 32 + tile_index_x as usize; + // Obtain tile ID in this area + // println!("Render absolute [{:03X}:{:03X}] -> {:03X}", render_abs_x, render_abs_y, vram_offset); + let mut tile_id = self.vram[vram_offset]; + /* + if self.control & (1 << 4) > 0 { + // This tile dataset is from -127 to 127, not from 0 to 255 + if (tile_id as i8) < 0 { + tile_id = (-(tile_id as i8)) as u8; + } else { + tile_id += 127; + } + } + */ + println!("vram offset: {:04X}", vram_offset); + if tile_id != 0 { + println!("Drawing tile {:02X}", tile_id); + } + + // Obtain tile information + let tile_base_addr: usize; + if self.control & (1 << 4) > 0 { + tile_base_addr = 0x0000; + } else { + tile_base_addr = 0x0800; + // This set goes from -127 to 127. + let s_tid: i8 = tile_id as i8; + if s_tid < 0 { + tile_id = (127 + s_tid) as u8; + } else { + tile_id = tile_id + 127; + } + panic!("OH MY GOD, this wasn't tested yet"); + } + + let tile_base_addr: usize = tile_base_addr + (tile_id as usize) * 16; + 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 mut factor = 0; + if b1 { + factor += 64; + } + if b2 { + factor += 128; + } + + // Draw stuff + self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB(factor, factor, factor)); + // self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB(255, 255, 255)); + // let tile_base_addr = tilemap + tile_id*128 + // pixel(render_x, map_offset_y) := *tile_base_addr + 2* *(tile_base_addr+1) + //[tile_index_x][tile_index_y] + } + } } pub fn vblank_interrupt(&mut self) {