diff --git a/src/bus.rs b/src/bus.rs index 773ff52..285ffb0 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -40,7 +40,8 @@ pub struct Bus { impl Bus { pub fn new() -> Self { - let game_rom = match ROM::load_file("ignore/dmg-acid2.gb".to_string()) { + let game_rom = match ROM::load_file("ignore/tetris.gb".to_string()) { + // let game_rom = match ROM::load_file("roms/cpu_instrs.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/01-special.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/03-op sp,hl.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/04-op r,imm.gb".to_string()) { @@ -65,11 +66,7 @@ impl Bus { if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) { return self.game_rom.read(address); } else if IO_REGISTERS.in_range(address) { - return match address { - 0xFF44 => 0x90, - 0xFF4D => 0xFF, - _ => self.data[address as usize], - } + return self.data[address as usize]; } self.data[address as usize] } diff --git a/src/cpu.rs b/src/cpu.rs index 0594b8f..b4e760a 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -806,8 +806,6 @@ pub enum Opcode { PrefixCB(Box), IllegalInstruction, } -// Frequency un Hz -const FREQUENCY: f64 = 4194.304; // Store cycles in M #[derive(Debug, Copy, Clone)] diff --git a/src/emulator.rs b/src/emulator.rs index e062f52..4d4043b 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -20,8 +20,8 @@ impl Emulator { } pub fn draw(&mut self, frame: &mut [u8]) { - self.ppu.draw_background(&self.bus); - let ppu_frame = self.ppu.get_rgba_frame(&self.bus); + // self.ppu.draw_background(&mut self.bus); + let ppu_frame = self.ppu.get_rgba_frame(); for (i, pixel) in frame.chunks_exact_mut(4).enumerate() { pixel.copy_from_slice(&ppu_frame[i]); } @@ -30,6 +30,7 @@ impl Emulator { pub fn run(&mut self, cpu_cycles: Cycles) { self.cpu.reset_cycles(); while self.cpu.get_cycles().0 <= cpu_cycles.0 { + self.ppu.do_cycle(&mut self.bus); self.cpu.run(&mut self.bus); } } diff --git a/src/ppu.rs b/src/ppu.rs index c256017..ab01fae 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -6,7 +6,6 @@ use crate::utils::{ }; use crate::bus::{Bus, AddressRange, BANK_ZERO, VIDEO_RAM}; use crate::cpu::{Cycles}; -use rand::Rng; #[derive(Debug, Copy, Clone)] enum Pixel { @@ -46,8 +45,10 @@ pub enum LCDStatus { ModeFlag(LCDStatusModeFlag), } -pub const WIDTH: u32 = 160; -pub const HEIGHT: u32 = 144; +pub const LCD_WIDTH: u32 = 160; +pub const LCD_HEIGHT: u32 = 144; +pub const WIDTH: u32 = LCD_WIDTH; +pub const HEIGHT: u32 = LCD_HEIGHT; pub const FRAME_BUFFER_LENGTH: u32 = WIDTH * HEIGHT; const LCD_CONTROL_ADDRESS: u16 = 0xFF40; @@ -74,10 +75,60 @@ impl PPU { pub fn new() -> Self { Self { cycles: Cycles(0), - rgba_frame: [[0, 0, 0xFF, 0]; FRAME_BUFFER_LENGTH as usize], + rgba_frame: [[0xFF, 0xFF, 0xFF, 0]; FRAME_BUFFER_LENGTH as usize], } } + pub fn reset_cycles(&mut self) { + self.cycles.0 = 0; + } + + pub fn increment_cycles(&mut self, cycles: Cycles) { + self.cycles.0 += cycles.0; + } + + pub fn do_cycle(&mut self, bus: &mut Bus) { + // Mode 1 Vertical blank + if PPU::get_lcd_y(bus) >= 144 { + PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true); + } else { + if self.cycles.0 <= 80 { + // Mode 2 OAM scan + PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true); + } else if self.cycles.0 <= 80 + 172 { + // Mode 3 drawing pixel line. This could also last 289 cycles + if !PPU::get_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) { + self.draw_line(bus); + PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true); + } + } else if self.cycles.0 <= 80 + 172 + 204 { + // Mode 0 Horizontal blank. This could last 87 or 204 cycles depending on the mode 3 + PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true); + } + } + + self.increment_cycles(Cycles(1)); + + // Horizontal scan completed + if self.cycles.0 > 456 { + self.reset_cycles(); + PPU::set_lcd_y(bus, PPU::get_lcd_y(bus) + 1); + + // Frame completed + if PPU::get_lcd_y(bus) > 153 { + PPU::set_lcd_y(bus, 0); + } + } + } + + fn get_lcd_y(bus: &Bus) -> u8 { + bus.read(LCD_Y_ADDRESS) + } + + fn set_lcd_y(bus: &mut Bus, val: u8) { + bus.write(LCD_Y_ADDRESS, val); + } + fn get_scroll_x(bus: &Bus) -> u8 { bus.read(SCROLL_X_ADDRESS) } @@ -158,26 +209,46 @@ impl PPU { bus.write(LCD_STATUS_ADDRESS, byte); } - fn get_pixel(two_bit_pixel: u8) -> Pixel { - match two_bit_pixel { - 0x00 => Pixel::White, - 0x01 => Pixel::Light, - 0x10 => Pixel::Dark, - 0x11 => Pixel::Black, - _ => Pixel::Black, + pub fn draw_line(&mut self, bus: &Bus) { + let lcd_y = PPU::get_lcd_y(bus); + if lcd_y as u32 >= LCD_HEIGHT { + return; } - } + let mut lcd_x: u8 = 0; + while (lcd_x as u32) < LCD_WIDTH { + let y = lcd_y.wrapping_add(PPU::get_scroll_y(bus)); + let x = lcd_x.wrapping_add(PPU::get_scroll_x(bus)); + let index_x = x as u16 / 8; + let index_y = (y as u16 / 8) * 32; + let index = index_x + index_y; + let tile_line = (y).rem_euclid(8) * 2; + let tile_number = bus.read(0x9800 + index as u16) as u16; + let addr = 0x8000 + tile_line as u16 + (tile_number * 16); - fn get_rgba(pixel: Pixel) -> [u8; 4] { - match pixel { - Pixel::White => [255, 255, 255, 0], - Pixel::Light => [192, 192, 192, 0], - Pixel::Dark => [81, 81, 81, 0], - Pixel::Black => [0, 0, 0, 0], + let tile_byte_1 = bus.read(addr); + let tile_byte_2 = bus.read(addr + 1); + + let pixels = PPU::get_byte_pixels(tile_byte_1, tile_byte_2); + + for pixel in pixels { + let idx = lcd_x as usize + (lcd_y as usize * LCD_WIDTH as usize); + self.rgba_frame[idx] = PPU::get_rgba(pixel); + lcd_x += 1; + } } } - pub fn draw_background(&mut self, bus: &Bus) { + pub fn draw_background(&mut self, bus: &mut Bus) { + let mut lcd_y: u8 = 0; + PPU::set_lcd_y(bus, lcd_y); + while lcd_y < 144 { + self.draw_line(bus); + lcd_y += 1; + PPU::set_lcd_y(bus, lcd_y); + } + } + + /* pub fn draw_background_old(&mut self, bus: &Bus) { let mut idx = 0; // let mut tile_line: u16 = 0; let mut lcd_y: u8 = 0; @@ -207,6 +278,25 @@ impl PPU { lcd_y += 1; // tile_line += 2; } + } */ + + fn get_pixel(two_bit_pixel: u8) -> Pixel { + match two_bit_pixel { + 0x00 => Pixel::White, + 0x01 => Pixel::Light, + 0x10 => Pixel::Dark, + 0x11 => Pixel::Black, + _ => Pixel::Black, + } + } + + fn get_rgba(pixel: Pixel) -> [u8; 4] { + match pixel { + Pixel::White => [255, 255, 255, 0], + Pixel::Light => [192, 192, 192, 0], + Pixel::Dark => [81, 81, 81, 0], + Pixel::Black => [0, 0, 0, 0], + } } fn get_byte_pixels(byte1: u8, byte2: u8) -> [Pixel; 8] { @@ -222,7 +312,7 @@ impl PPU { pixels } - pub fn get_rgba_frame(&self, bus: &Bus) -> &[[u8; 4]; FRAME_BUFFER_LENGTH as usize] { + pub fn get_rgba_frame(&self) -> &[[u8; 4]; FRAME_BUFFER_LENGTH as usize] { &self.rgba_frame } } diff --git a/src/render.rs b/src/render.rs index 7fda92e..f644768 100644 --- a/src/render.rs +++ b/src/render.rs @@ -38,7 +38,6 @@ pub fn start_eventloop() { let mut emulator = Emulator::new(); - let mut count: usize = 0; event_loop.run(move |event, _, control_flow| { // Handle input events if input.update(&event) { @@ -66,7 +65,7 @@ pub fn start_eventloop() { emulator.run(Cycles(70224)); emulator.draw(pixels.get_frame()); - thread::sleep(time::Duration::from_millis(14)); + // thread::sleep(time::Duration::from_millis(14)); window.request_redraw(); }, Event::RedrawRequested(_) => {