rustyboy/src/display.rs
Kevin Hamacher 79d2c99c06 cleanup
2016-05-26 18:56:22 +02:00

271 lines
8.7 KiB
Rust

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;
// Control flags
const CTRL_LCD_DISPLAY_ENABLE: u8 = 1 << 7;
const CTRL_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;
const CTRL_BG_SPRITE_SIZE: u8 = 1 << 2;
const CTRL_BG_SPRITE_ENABLE: u8 = 1 << 1;
const CTRL_BG_DISPLAY: u8 = 1 << 0;
#[derive(Debug)]
enum DisplayMode {
Scanline,
Readmode,
HBlank,
VBlank,
}
impl Default for DisplayMode {
fn default() -> DisplayMode {
DisplayMode::Scanline
}
}
pub struct Display {
control: u8,
status: u8,
background_palette: u8,
object_palette_0: u8,
object_palette_1: u8,
scrollx: u8,
scrolly: u8,
windowx: u8,
windowy: u8,
curline: u8,
vram: Box<[u8]>,
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,
background_palette: 0,
object_palette_0: 0,
object_palette_1: 0,
scrollx: 0,
scrolly: 0,
windowx: 0,
windowy: 0,
curline: 0,
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,
0xFF40 => self.control = val,
0xFF41 => self.status = val,
0xFF42 => self.scrolly = val,
0xFF43 => self.scrollx = val,
0xFF44 => self.curline = 0,
0xFF47 => self.background_palette = val,
0xFF48 => self.object_palette_0 = val,
0xFF49 => self.object_palette_1 = val,
0xFF4A => self.windowy = val,
0xFF4B => self.windowx = val,
_ => panic!("Display: Write {:02X} to {:04X} unsupported", val, addr),
}
}
pub fn read_byte(&self, addr: u16) -> u8 {
match addr {
0x8000 ... 0x9FFF => self.vram[(addr - 0x8000) as usize],
0xFF40 => self.control,
0xFF41 => self.status,
0xFF42 => self.scrolly,
0xFF43 => self.scrollx,
0xFF44 => self.curline,
0xFF47 => self.background_palette,
0xFF48 => self.object_palette_0,
0xFF49 => self.object_palette_1,
0xFF4A => self.windowy,
0xFF4B => self.windowx,
_ => panic!("Display: Read from {:04X} unsupported", addr),
}
}
// Do we want to have a time delta here?
pub fn tick(&mut self, ticks: u16) {
self.current_ticks += ticks;
match self.current_mode {
DisplayMode::Scanline => {
if self.current_ticks > TICKS_END_SCANLINE {
self.current_ticks = 0;
self.current_mode = DisplayMode::Readmode;
}
},
DisplayMode::Readmode => {
if self.current_ticks > TICKS_END_READMODE {
self.current_ticks = 0;
self.current_mode = DisplayMode::HBlank;
self.renderscan();
}
},
DisplayMode::HBlank => {
if self.current_ticks > TICKS_END_HBLANK {
self.current_ticks = 0;
self.curline += 1;
if self.curline == 143 {
self.current_mode = DisplayMode::VBlank;
} else {
self.current_mode = DisplayMode::Scanline;
}
}
},
DisplayMode::VBlank => {
if self.current_ticks > TICKS_END_VBLANK {
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;
}
}
}
}
}
fn renderscan(&mut self) {
// Points to the background map offset to use.
let background_map: usize;
// verify!
if self.control & CTRL_BG_TILE_MAP_SELECT > 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 & CTRL_BG_SPRITE_ENABLE > 0 {
panic!("Sprites not supported");
}
// Render background
if self.control & CTRL_BG_DISPLAY > 0 {
print!("Drawing tiles: ");
// 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
let mut tile_id = self.vram[vram_offset];
// println!("vram offset: {:04X}", vram_offset);
if tile_id != 0 {
// println!("Drawing tile {:02X}", tile_id);
print!("{:02X} ", tile_id);
}
// 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.
let s_tid: i8 = tile_id as i8;
tile_id = (128 + 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_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. We're currently only in monochrome mode
self.set_pixel(render_x, render_y, sdl2::pixels::Color::RGB(factor, factor, factor));
}
}
println!("");
}
pub fn vblank_interrupt(&mut self) {
self.curline += 1;
if self.curline > 153 {
self.curline = 0;
}
}
pub fn controller_interrupt(&self) {
}
}