diff --git a/src/main.rs b/src/main.rs index b7f3d4f..3b0bb0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,6 +78,7 @@ pub struct Memory { ime: bool, ime_scheduled: u8, io: [u8; 76], + user_mode: bool, } impl Memory { @@ -95,6 +96,7 @@ impl Memory { ime: false, ime_scheduled: 0x0, io: [0xFF; 76], + user_mode: false, } } @@ -171,7 +173,14 @@ impl Memory { 0xFEA0..0xFF00 => { // println!("empty space write: {:#X} to addr {:#X}", data, address); } - 0xFF00..0xFF4C => { + 0xFF04 => { + if self.user_mode { + self.io[0xFF04 - 0xFF00] = 0; + } else { + self.io[0xFF04 - 0xFF00] = data; + } + } + 0xFF00..0xFF04 | 0xFF05..0xFF4C => { // verbose_print!("writing to addr {:#X}\r", address); stdout().flush().unwrap(); diff --git a/src/processor/gpu.rs b/src/processor/gpu.rs index ba81f03..a527a41 100644 --- a/src/processor/gpu.rs +++ b/src/processor/gpu.rs @@ -14,6 +14,7 @@ enum DrawMode { Mode3, } +#[derive(Debug)] enum TilemapArea { T9800, T9C00, @@ -28,6 +29,7 @@ impl TilemapArea { } } +#[derive(Debug)] enum TiledataArea { D8000, D9000, @@ -42,12 +44,22 @@ impl TiledataArea { } } +#[derive(Debug)] enum ObjSize { S8x8, S8x16, } -#[allow(dead_code)] +impl ObjSize { + fn get_height(&self) -> u8 { + match self { + ObjSize::S8x8 => 8, + ObjSize::S8x16 => 16, + } + } +} + +#[derive(Debug)] struct LCDC { enable: bool, window_tilemap: TilemapArea, @@ -91,6 +103,20 @@ struct Palette { three: Colour, } +struct ObjectFlags { + behind_bg_and_window: bool, + y_flip: bool, + x_flip: bool, + palette: Palette, +} + +struct Object { + x: u8, + y: u8, + tile_index: u8, + flags: ObjectFlags, +} + const TILE_WINDOW_EDGE_LENGTH: usize = 16 * 8; const TILE_WINDOW_EDGE_LENGTH_SCALED: usize = TILE_WINDOW_EDGE_LENGTH * FACTOR; @@ -227,6 +253,7 @@ impl CPU { self.render_window(); self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0)); } + // println!("lcdc: {:#?}", lcdc); } fn exit_vblank(&mut self) { @@ -297,7 +324,65 @@ impl CPU { } } - fn render_scanline_obj(&mut self, _scanline: u8, _lcdc: &LCDC) {} + fn render_scanline_obj(&mut self, scanline: u8, lcdc: &LCDC) { + let objs = self.parse_oam(scanline, &lcdc.obj_size); + for object in objs { + println!("rendering object"); + self.render_object(scanline, object, &lcdc.obj_size); + } + } + + fn parse_oam(&mut self, scanline: u8, obj_size: &ObjSize) -> Vec { + let mut objs = vec![]; + for i in (0xFE00..0xFE9F).step_by(4) { + let y_pos = self.memory.get(i).wrapping_sub(16); + if y_pos <= scanline && (y_pos + obj_size.get_height()) > scanline { + // sprite is on line + let x_pos = self.memory.get(i + 1); + let tile_index = self.memory.get(i + 2); + let flags = self.memory.get(i + 3); + let palette_addr = if get_bit(flags, 4) { 0xFF49 } else { 0xFF48 }; + objs.push(Object { + x: x_pos, + y: y_pos, + tile_index, + flags: ObjectFlags { + behind_bg_and_window: get_bit(flags, 7), + y_flip: get_bit(flags, 6), + x_flip: get_bit(flags, 5), + palette: byte_to_palette(self.memory.get(palette_addr)), + }, + }); + if objs.len() >= 10 { + break; + } + } + } + objs + } + + fn render_object(&mut self, scanline: u8, object: Object, obj_size: &ObjSize) { + let mut object_row = scanline - object.y; + if object.flags.y_flip { + object_row = obj_size.get_height() - object_row; + } + let tile_row = object_row % 8; + let tile_addr = TiledataArea::D8000 + .get_addr(object.tile_index + if object_row >= 8 { 1 } else { 0 }) + + (tile_row as u16 * 2); + let lsbs = self.memory.get(tile_addr); + let msbs = self.memory.get(tile_addr + 1); + for px_x in 0..8 { + let x_addr = if object.flags.x_flip { px_x } else { 7 - px_x }; + let lsb = get_bit(lsbs, x_addr); + let msb = get_bit(msbs, x_addr); + let colour = bits_to_mapped_colour(lsb, msb, object.flags.palette); + let x_coord = (object.x as usize * 8) + (px_x as usize); + if x_coord < WIDTH { + self.gpu.buffer[(scanline as usize * WIDTH) + x_coord] = colour.to_rgb(); + } + } + } fn render_tiles( &mut self, diff --git a/src/processor/mod.rs b/src/processor/mod.rs index 6bff17f..eed661c 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -66,7 +66,9 @@ impl CPU { opcode, self.last_instruction_addr ); + self.memory.user_mode = true; let cycles = self.run_opcode(opcode); + self.memory.user_mode = false; self.increment_timers(cycles); let interrupt_cycles = self.handle_interrupts();