diff --git a/src/bus.rs b/src/bus.rs index 73ce70a..cbb9b33 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -5,7 +5,14 @@ use crate::utils::{ join_bytes }; use crate::rom::ROM; -use crate::ppu::{PPU, LCDStatus, LCDStatusModeFlag, LCD_STATUS_ADDRESS, LCD_CONTROL_ADDRESS, LCD_Y_ADDRESS}; +use crate::ppu::{ + PPU, + LCDStatus, + LCDStatusModeFlag, + LCD_STATUS_ADDRESS, + LCD_CONTROL_ADDRESS, + LCD_Y_ADDRESS +}; use crate::cpu::{Interrupt}; use crate::timer::{TIMER_DIVIDER_REGISTER_ADDRESS}; use crate::joypad::{Joypad, JOYPAD_ADDRESS}; @@ -69,7 +76,26 @@ impl Bus { _ => panic!("Could not read ROM"), }; let mut data = [0x00; 0x10000]; - data[JOYPAD_ADDRESS as usize] = 0b11001111; + data[0xFF01] = 0x00; + data[0xFF02] = 0x7E; + data[0xFF04] = 0x18; + data[0xFF05] = 0x00; + data[0xFF06] = 0x00; + data[0xFF07] = 0xF8; + data[0xFF0F] = 0xE1; + + data[0xFF40] = 0x91; + data[0xFF41] = 0x81; + data[0xFF42] = 0x00; + data[0xFF43] = 0x00; + data[0xFF44] = 0x91; + data[0xFF45] = 0x00; + data[0xFF46] = 0xFF; + data[0xFF47] = 0xFC; + + data[0xFF4A] = 0x00; + data[0xFF4B] = 0x00; + Self { data, game_rom, @@ -80,9 +106,6 @@ impl Bus { if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) { return self.game_rom.read(address); } - if address == JOYPAD_ADDRESS { - println!("Joypad read {:08b}", self.data[address as usize]); - } self.data[address as usize] } @@ -108,15 +131,10 @@ impl Bus { self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM } else if address == TIMER_DIVIDER_REGISTER_ADDRESS { self.data[address as usize] = 0x00; - } else if address == LCD_STATUS_ADDRESS { - // Prevent user from modifying LCD Status mode - let byte = self.data[address as usize]; - self.data[address as usize] = (data & 0b1111_1100) | (byte & 0b0000_0011); } else if address == LCD_CONTROL_ADDRESS && get_bit(data, BitIndex::I7) { self.data[address as usize] = data; self.data[LCD_Y_ADDRESS as usize] = 0x00; } else if address == JOYPAD_ADDRESS { - println!("Joypad write: {:08b}", data); let byte = self.data[JOYPAD_ADDRESS as usize]; self.data[JOYPAD_ADDRESS as usize] = (data & 0b00110000) | 0b11000000 | (byte & 0b00001111); } else { diff --git a/src/cpu.rs b/src/cpu.rs index 3bb2621..3d2d96b 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -923,6 +923,7 @@ impl CPU { if bus.read(INTERRUPT_ENABLE_ADDRESS) & bus.read(INTERRUPT_FLAG_ADDRESS) != 0 { self.is_halted = false; } + if bus.get_interrupt(Interrupt::VBlank) { return Some(Interrupt::VBlank); } else if bus.get_interrupt(Interrupt::LCDSTAT) { @@ -1778,21 +1779,27 @@ impl CPU { }, // Enable interrupts Opcode::EI => { + println!("EI"); self.registers.increment(Register::PC, 1); self.ime = true; }, // Disable interrupts Opcode::DI => { + println!("DI"); self.registers.increment(Register::PC, 1); self.ime = false; }, // Same as enabling interrupts and then executing RET Opcode::RETI => { + println!("RETI"); + let prev_pc = self.registers.get(Register::PC); self.exec(Opcode::EI, bus); self.exec(Opcode::RET(OpcodeParameter::NoParam), bus); + self.registers.set(Register::PC, prev_pc.wrapping_add(1)); }, // Don't execute instructions until an interrupt is requested Opcode::HALT => { + println!("HALT"); self.registers.increment(Register::PC, 1); self.is_halted = true; }, diff --git a/src/emulator.rs b/src/emulator.rs index 33fda55..f566db1 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -96,7 +96,7 @@ impl Emulator { self.joypad.release(Button::Select); } if change { - self.bus.force_write(JOYPAD_ADDRESS, self.joypad.get(&self.bus)); + self.joypad.update(&mut self.bus); self.bus.set_interrupt_flag(Interrupt::Joypad, true); } } diff --git a/src/joypad.rs b/src/joypad.rs index 30b7e67..a733997 100644 --- a/src/joypad.rs +++ b/src/joypad.rs @@ -68,15 +68,12 @@ impl Joypad { }; } - pub fn get(&self, bus: &Bus) -> u8 { + pub fn update(&self, bus: &mut Bus) { let byte = bus.read(JOYPAD_ADDRESS); let direction = !get_bit(byte, BitIndex::I4); let action = !get_bit(byte, BitIndex::I5); - let action = true; - let direction = true; - - 0b11000000 | + let data = 0b11000000 | (byte & 0b00110000) | ( (!((direction && self.down) || (action && self.start)) as u8) << 3 @@ -86,6 +83,8 @@ impl Joypad { (!((direction && self.left) || (action && self.b)) as u8) << 1 ) | ( (!((direction && self.right) || (action && self.a)) as u8) - ) + ); + println!("New joypad write: {:08b}", data); + bus.force_write(JOYPAD_ADDRESS, data); } } diff --git a/src/ppu.rs b/src/ppu.rs index c1ddf3a..c9c86ba 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -7,6 +7,27 @@ use crate::utils::{ use crate::bus::{Bus, AddressRange, BANK_ZERO, VIDEO_RAM}; use crate::cpu::{Cycles, Interrupt}; +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; + +pub const LCD_CONTROL_ADDRESS: u16 = 0xFF40; +pub const LCD_STATUS_ADDRESS: u16 = 0xFF41; + +pub const SCROLL_Y_ADDRESS: u16 = 0xFF42; +pub const SCROLL_X_ADDRESS: u16 = 0xFF43; +pub const LCD_Y_ADDRESS: u16 = 0xFF44; +pub const LCD_Y_COMPARE_ADDRESS: u16 = 0xFF45; +pub const DMA_ADDRESS: u16 = 0xFF46; +pub const BACKGROUND_PALETTE_ADDRESS: u16 = 0xFF47; +pub const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48; +pub const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49; +pub const WINDOW_X_ADDRESS: u16 = 0xFF4A; +pub const WINDOW_Y_ADDRESS: u16 = 0xFF4B; +pub const TILE_MAP_ADDRESS: u16 = 0x9800; + #[derive(Debug, Copy, Clone)] enum Pixel { White, @@ -31,7 +52,7 @@ pub enum LCDControl { } impl LCDControl { - fn get_bit_index(&self) -> BitIndex { + fn index(&self) -> BitIndex { match self { LCDControl::LCDEnable => BitIndex::I7, LCDControl::WindowTileMapAddress => BitIndex::I6, @@ -45,11 +66,11 @@ impl LCDControl { } pub fn get(&self, byte: u8) -> bool { - get_bit(byte, self.get_bit_index()) + get_bit(byte, self.index()) } pub fn set(&self, byte: u8, val: bool) -> u8 { - set_bit(byte, val, self.get_bit_index()) + set_bit(byte, val, self.index()) } } @@ -69,27 +90,6 @@ pub enum LCDStatus { ModeFlag(LCDStatusModeFlag), } -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; - -pub const LCD_CONTROL_ADDRESS: u16 = 0xFF40; -pub const LCD_STATUS_ADDRESS: u16 = 0xFF41; - -pub const SCROLL_Y_ADDRESS: u16 = 0xFF42; -pub const SCROLL_X_ADDRESS: u16 = 0xFF43; -pub const LCD_Y_ADDRESS: u16 = 0xFF44; -pub const LCD_Y_COMPARE_ADDRESS: u16 = 0xFF45; -pub const DMA_ADDRESS: u16 = 0xFF46; -pub const BACKGROUND_PALETTE_ADDRESS: u16 = 0xFF47; -pub const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48; -pub const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49; -pub const WINDOW_X_ADDRESS: u16 = 0xFF4A; -pub const WINDOW_Y_ADDRESS: u16 = 0xFF4B; -pub const TILE_MAP_ADDRESS: u16 = 0x9800; - pub struct PPU { cycles: Cycles, rgba_frame: [[u8; 4]; FRAME_BUFFER_LENGTH as usize], @@ -125,8 +125,8 @@ impl PPU { } pub fn cycle(&mut self, bus: &mut Bus) { - self.increment_cycles(Cycles(1)); if !PPU::get_lcd_control(bus, LCDControl::LCDEnable) { + self.increment_cycles(Cycles(1)); return; } @@ -156,11 +156,13 @@ impl PPU { } } + 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); + PPU::set_lcd_y(bus, PPU::get_lcd_y(bus).wrapping_add(1)); let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS); PPU::set_lcd_status(bus, LCDStatus::LYCFlag, lyc_compare); @@ -249,15 +251,14 @@ impl PPU { bus.write(LCD_STATUS_ADDRESS, byte); } - fn get_tile_bytes(x: u8, y: u8, tile_number: TileNumber, default_method: bool, bus: &Bus) -> (u8, u8) { + fn get_tile_bytes(x: u8, y: u8, tile_number_type: TileNumber, default_method: bool, bus: &Bus) -> (u8, u8) { 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 = match tile_number { + let tile_number = match tile_number_type { TileNumber::Base(base) => bus.read(base + index as u16), TileNumber::Absolute(num) => bus.read(0x8000 + num as u16), - } as u16; let addr = if default_method { 0x8000 + tile_line as u16 + (tile_number * 16) @@ -276,7 +277,7 @@ impl PPU { let window_x = (PPU::get_window_x(bus) as i8 - 7) as u8; let window_y = PPU::get_window_y(bus); - if window_x != lcd_x || window_y != lcd_y { + if !PPU::get_lcd_control(bus, LCDControl::WindowEnable) || window_x != lcd_x || window_y != lcd_y { return None; } @@ -319,10 +320,8 @@ impl PPU { for pixel in bg_pixels { let idx = lcd_x as usize + (lcd_y as usize * LCD_WIDTH as usize); self.rgba_frame[idx] = PPU::get_rgba(pixel); - if PPU::get_lcd_control(bus, LCDControl::WindowEnable) { - if let Some(window_pixel) = PPU::get_window_pixel(lcd_x, bus) { - self.rgba_frame[idx] = PPU::get_rgba(window_pixel); - } + if let Some(window_pixel) = PPU::get_window_pixel(lcd_x, bus) { + self.rgba_frame[idx] = PPU::get_rgba(window_pixel); } lcd_x += 1;