object rendering + usermode ram

This commit is contained in:
Alex Janka 2023-02-06 19:17:52 +11:00
parent 94bb1a2bfb
commit f0b2369c9d
3 changed files with 99 additions and 3 deletions

View file

@ -78,6 +78,7 @@ pub struct Memory {
ime: bool, ime: bool,
ime_scheduled: u8, ime_scheduled: u8,
io: [u8; 76], io: [u8; 76],
user_mode: bool,
} }
impl Memory { impl Memory {
@ -95,6 +96,7 @@ impl Memory {
ime: false, ime: false,
ime_scheduled: 0x0, ime_scheduled: 0x0,
io: [0xFF; 76], io: [0xFF; 76],
user_mode: false,
} }
} }
@ -171,7 +173,14 @@ impl Memory {
0xFEA0..0xFF00 => { 0xFEA0..0xFF00 => {
// println!("empty space write: {:#X} to addr {:#X}", data, address); // 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); // verbose_print!("writing to addr {:#X}\r", address);
stdout().flush().unwrap(); stdout().flush().unwrap();

View file

@ -14,6 +14,7 @@ enum DrawMode {
Mode3, Mode3,
} }
#[derive(Debug)]
enum TilemapArea { enum TilemapArea {
T9800, T9800,
T9C00, T9C00,
@ -28,6 +29,7 @@ impl TilemapArea {
} }
} }
#[derive(Debug)]
enum TiledataArea { enum TiledataArea {
D8000, D8000,
D9000, D9000,
@ -42,12 +44,22 @@ impl TiledataArea {
} }
} }
#[derive(Debug)]
enum ObjSize { enum ObjSize {
S8x8, S8x8,
S8x16, S8x16,
} }
#[allow(dead_code)] impl ObjSize {
fn get_height(&self) -> u8 {
match self {
ObjSize::S8x8 => 8,
ObjSize::S8x16 => 16,
}
}
}
#[derive(Debug)]
struct LCDC { struct LCDC {
enable: bool, enable: bool,
window_tilemap: TilemapArea, window_tilemap: TilemapArea,
@ -91,6 +103,20 @@ struct Palette {
three: Colour, 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: usize = 16 * 8;
const TILE_WINDOW_EDGE_LENGTH_SCALED: usize = TILE_WINDOW_EDGE_LENGTH * FACTOR; const TILE_WINDOW_EDGE_LENGTH_SCALED: usize = TILE_WINDOW_EDGE_LENGTH * FACTOR;
@ -227,6 +253,7 @@ impl CPU {
self.render_window(); self.render_window();
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0)); self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0));
} }
// println!("lcdc: {:#?}", lcdc);
} }
fn exit_vblank(&mut self) { 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<Object> {
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( fn render_tiles(
&mut self, &mut self,

View file

@ -66,7 +66,9 @@ impl CPU {
opcode, opcode,
self.last_instruction_addr self.last_instruction_addr
); );
self.memory.user_mode = true;
let cycles = self.run_opcode(opcode); let cycles = self.run_opcode(opcode);
self.memory.user_mode = false;
self.increment_timers(cycles); self.increment_timers(cycles);
let interrupt_cycles = self.handle_interrupts(); let interrupt_cycles = self.handle_interrupts();