diff --git a/src/bus.rs b/src/bus.rs index 8030785..3c2cd51 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -5,7 +5,7 @@ use crate::utils::{ join_bytes }; use crate::rom::ROM; -use crate::ppu::{PPU, LCDStatus, LCDStatusModeFlag}; +use crate::ppu::{PPU, LCDStatus, LCDStatusModeFlag, LCD_CONTROL_ADDRESS, LCD_Y_ADDRESS}; use crate::cpu::{Interrupt}; use crate::timer::{TIMER_DIVIDER_REGISTER_ADDRESS}; @@ -50,7 +50,7 @@ pub struct Bus { impl Bus { pub fn new() -> Self { - let game_rom = match ROM::load_file("ignore/tetris.gb".to_string()) { + let game_rom = match ROM::load_file("ignore/mario-land.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/02-interrupts.gb".to_string()) { @@ -74,14 +74,11 @@ impl Bus { } pub fn read(&self, address: u16) -> u8 { + if address == 0xFF00 { + return 0xFF; + } if BANK_ZERO.in_range(address) || BANK_SWITCHABLE.in_range(address) { return self.game_rom.read(address); - } else if VIDEO_RAM.in_range(address) { - if PPU::get_lcd_status(self, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) { - return 0xFF; - } - } else if IO_REGISTERS.in_range(address) { - return self.data[address as usize]; } self.data[address as usize] } @@ -106,10 +103,11 @@ impl Bus { } else if ECHO_RAM.in_range(address) { self.data[address as usize] = data; self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM - } else if VIDEO_RAM.in_range(address) { - self.data[address as usize] = data; } else if address == TIMER_DIVIDER_REGISTER_ADDRESS { self.data[address as usize] = 0x00; + } 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 { self.data[address as usize] = data; } @@ -121,12 +119,12 @@ impl Bus { self.write(address.wrapping_add(1), bytes[1]); } - pub fn set_interrupt_master(&mut self, interrupt: Interrupt, val: bool) { + pub fn set_interrupt_enable(&mut self, interrupt: Interrupt, val: bool) { let byte = self.read(INTERRUPT_ENABLE_ADDRESS); self.write(INTERRUPT_ENABLE_ADDRESS, interrupt.set(byte, val)); } - pub fn set_interrupt(&mut self, interrupt: Interrupt, val: bool) { + pub fn set_interrupt_flag(&mut self, interrupt: Interrupt, val: bool) { let byte = self.read(INTERRUPT_FLAG_ADDRESS); self.write(INTERRUPT_FLAG_ADDRESS, interrupt.set(byte, val)); } diff --git a/src/cpu.rs b/src/cpu.rs index 63bace6..71843e6 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -841,6 +841,8 @@ pub struct CPU { cycles: Cycles, last_op_cycles: Cycles, exec_calls_count: usize, + is_halted: bool, + ime: bool, // Interrupt Master Enable } impl CPU { @@ -850,6 +852,8 @@ impl CPU { cycles: Cycles(0), last_op_cycles: Cycles(0), exec_calls_count: 0, + is_halted: false, + ime: true, } } @@ -905,8 +909,8 @@ impl CPU { } pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) { - bus.set_interrupt_master(interrupt, false); - bus.set_interrupt(interrupt, false); + bus.set_interrupt_enable(interrupt, false); + bus.set_interrupt_flag(interrupt, false); let vector = interrupt.get_vector(); self.exec(Opcode::CALL(OpcodeParameter::U16(vector)), bus); self.increment_cycles(Cycles(5)); @@ -914,6 +918,12 @@ impl CPU { } pub fn check_interrupts(&mut self, bus: &mut Bus) -> Option { + if bus.read(INTERRUPT_ENABLE_ADDRESS) & bus.read(INTERRUPT_FLAG_ADDRESS) != 0 { + self.is_halted = false; + } + if !self.ime { + return None; + } if bus.get_interrupt(Interrupt::VBlank) { return Some(Interrupt::VBlank); } else if bus.get_interrupt(Interrupt::LCDSTAT) { @@ -932,7 +942,7 @@ impl CPU { let cycles_start = self.get_cycles(); if let Some(interrupt) = self.check_interrupts(bus) { self.handle_interrupt(bus, interrupt); - } else { + } else if !self.is_halted { let program_counter = self.registers.get(Register::PC); let parameter_bytes = OpcodeParameterBytes::from_address(program_counter, bus); let (opcode, cycles) = parameter_bytes.parse_opcode(); @@ -941,6 +951,8 @@ impl CPU { } self.increment_cycles(cycles); self.exec(opcode, bus); + } else if self.is_halted { + self.increment_cycles(Cycles(1)); } let cycles_end = self.get_cycles(); self.set_last_op_cycles(cycles_start, cycles_end); @@ -1767,21 +1779,24 @@ impl CPU { // Enable interrupts Opcode::EI => { self.registers.increment(Register::PC, 1); - bus.write(INTERRUPT_ENABLE_ADDRESS, 0xFF); // Disable all interrupts + self.ime = true; + // bus.write(INTERRUPT_MASTER_ENABLE_ADDRESS, 0xFF); // Enable all interrupts }, // Disable interrupts Opcode::DI => { self.registers.increment(Register::PC, 1); - bus.write(INTERRUPT_ENABLE_ADDRESS, 0x00); // Disable all interrupts + self.ime = false; + // bus.write(INTERRUPT_MASTER_ENABLE_ADDRESS, 0x00); // Disable all interrupts }, // Same as enabling interrupts and then executing RET Opcode::RETI => { self.exec(Opcode::EI, bus); self.exec(Opcode::RET(OpcodeParameter::NoParam), bus); }, - // WIP + // Don't execute instructions until an interrupt is requested Opcode::HALT => { self.registers.increment(Register::PC, 1); + self.is_halted = true; }, Opcode::STOP => { self.registers.increment(Register::PC, 2); @@ -1919,16 +1934,16 @@ mod tests { let mut bus = Bus::new(); let addr = 0xFF00; cpu.registers.set(Register::A, 0xF1); - cpu.exec(Opcode::LD(OpcodeParameter::FF00plusU8_Register(4, Register::A)), &mut bus); - assert_eq!(bus.read(addr + 4), 0xF1); + cpu.exec(Opcode::LD(OpcodeParameter::FF00plusU8_Register(0x42, Register::A)), &mut bus); + assert_eq!(bus.read(addr + 0x42), 0xF1); assert_eq!(cpu.registers.get(Register::PC), 0x102); let mut cpu = CPU::new(); let mut bus = Bus::new(); let addr = 0xFF00; cpu.registers.set(Register::A, 0x00); - bus.write(addr + 4, 0xF1); - cpu.exec(Opcode::LD(OpcodeParameter::Register_FF00plusU8(Register::A, 4)), &mut bus); + bus.write(addr + 0x42, 0xF1); + cpu.exec(Opcode::LD(OpcodeParameter::Register_FF00plusU8(Register::A, 0x42)), &mut bus); assert_eq!(cpu.registers.get(Register::A), 0xF1); assert_eq!(cpu.registers.get(Register::PC), 0x102); diff --git a/src/emulator.rs b/src/emulator.rs index fd021c3..35b71d3 100644 --- a/src/emulator.rs +++ b/src/emulator.rs @@ -9,6 +9,7 @@ pub struct Emulator { cpu: CPU, ppu: PPU, bus: Bus, + timer: Timer, } impl Emulator { @@ -17,6 +18,7 @@ impl Emulator { cpu: CPU::new(), ppu: PPU::new(), bus: Bus::new(), + timer: Timer::new(), } } @@ -32,7 +34,7 @@ impl Emulator { while self.cpu.get_cycles().0 <= cpu_cycles.0 { self.cpu.run(&mut self.bus); self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles()); - Timer::do_cycles(&mut self.bus, self.cpu.get_last_op_cycles()); + self.timer.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles()); } } diff --git a/src/ppu.rs b/src/ppu.rs index b2a9450..ca02378 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -19,7 +19,7 @@ enum Pixel { struct ColorPalette(u8, u8, u8, u8); pub enum LCDControl { - DisplayEnable, + LCDEnable, WindowTileMapAddress, WindowEnable, BackgroundWindowTileAddress, @@ -51,20 +51,20 @@ 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; -const LCD_STATUS_ADDRESS: u16 = 0xFF41; +pub const LCD_CONTROL_ADDRESS: u16 = 0xFF40; +pub const LCD_STATUS_ADDRESS: u16 = 0xFF41; -const SCROLL_Y_ADDRESS: u16 = 0xFF42; -const SCROLL_X_ADDRESS: u16 = 0xFF43; -const LCD_Y_ADDRESS: u16 = 0xFF44; -const LCD_Y_COMPARE_ADDRESS: u16 = 0xFF45; -const DMA_ADDRESS: u16 = 0xFF46; -const BACKGROUND_PALETTE_ADDRESS: u16 = 0xFF47; -const OBJECT_PALETTE_0_ADDRESS: u16 = 0xFF48; -const OBJECT_PALETTE_1_ADDRESS: u16 = 0xFF49; -const WINDOW_X_ADDRESS: u16 = 0xFF4A; -const WINDOW_Y_ADDRESS: u16 = 0xFF4B; -const TILE_MAP_ADDRESS: u16 = 0x9800; +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, @@ -96,30 +96,32 @@ impl PPU { } pub fn cycle(&mut self, bus: &mut Bus) { - // Mode 1 Vertical blank - if PPU::get_lcd_y(bus) >= 144 { - if PPU::get_lcd_y(bus) == 144 { - bus.set_interrupt(Interrupt::VBlank, true); - PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true); - } - } else { + self.increment_cycles(Cycles(1)); + if !PPU::get_lcd_control(bus, LCDControl::LCDEnable) { + return; + } + + if PPU::get_lcd_y(bus) < 144 { if self.cycles.0 == 0 { // Mode 2 OAM scan + PPU::request_interrupt(bus, Interrupt::LCDSTAT); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true); } else if self.cycles.0 == 80 + 1 { // Mode 3 drawing pixel line. This could also last 289 cycles - bus.set_interrupt(Interrupt::LCDSTAT, true); + // PPU::request_interrupt(bus, Interrupt::LCDSTAT); self.draw_line(bus); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true); } else if self.cycles.0 == 80 + 172 + 1 { // Mode 0 Horizontal blank. This could last 87 or 204 cycles depending on the mode 3 - bus.set_interrupt(Interrupt::LCDSTAT, true); + PPU::request_interrupt(bus, Interrupt::LCDSTAT); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true); } + } else if PPU::get_lcd_y(bus) == 144 && self.cycles.0 == 0 { + // Mode 1 Vertical blank + PPU::request_interrupt(bus, Interrupt::VBlank); + PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true); } - self.increment_cycles(Cycles(1)); - // Horizontal scan completed if self.cycles.0 > 456 { self.reset_cycles(); @@ -129,7 +131,7 @@ impl PPU { let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS); if lyc_compare { PPU::set_lcd_status(bus, LCDStatus::LYCInterrupt, lyc_compare); - bus.set_interrupt(Interrupt::LCDSTAT, true); + PPU::request_interrupt(bus, Interrupt::LCDSTAT); } // Frame completed @@ -139,6 +141,14 @@ impl PPU { } } + fn request_interrupt(bus: &mut Bus, interrupt: Interrupt) { + if PPU::get_lcd_control(bus, LCDControl::LCDEnable) { + bus.set_interrupt_flag(interrupt, true); + } else { + println!("lcd off"); + } + } + fn get_lcd_y(bus: &Bus) -> u8 { bus.read(LCD_Y_ADDRESS) } @@ -166,7 +176,7 @@ impl PPU { pub fn get_lcd_control(bus: &Bus, control: LCDControl) -> bool { let byte = bus.read(LCD_CONTROL_ADDRESS); match control { - LCDControl::DisplayEnable => get_bit(byte, BitIndex::I7), + LCDControl::LCDEnable => get_bit(byte, BitIndex::I7), LCDControl::WindowTileMapAddress => get_bit(byte, BitIndex::I6), LCDControl::WindowEnable => get_bit(byte, BitIndex::I5), LCDControl::BackgroundWindowTileAddress => get_bit(byte, BitIndex::I4), @@ -180,7 +190,7 @@ impl PPU { fn set_lcd_control(bus: &mut Bus, control: LCDControl, val: bool) { let mut byte = bus.read(LCD_CONTROL_ADDRESS); byte = match control { - LCDControl::DisplayEnable => set_bit(byte, val, BitIndex::I7), + LCDControl::LCDEnable => set_bit(byte, val, BitIndex::I7), LCDControl::WindowTileMapAddress => set_bit(byte, val, BitIndex::I6), LCDControl::WindowEnable => set_bit(byte, val, BitIndex::I5), LCDControl::BackgroundWindowTileAddress => set_bit(byte, val, BitIndex::I4), diff --git a/src/timer.rs b/src/timer.rs index f07e9c4..7ca3ed5 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -10,45 +10,66 @@ pub const TIMER_COUNTER_ADDRESS: u16 = 0xFF05; pub const TIMER_MODULO_ADDRESS: u16 = 0xFF06; pub const TIMER_CONTROL_ADDRESS: u16 = 0xFF07; -pub struct Timer; +pub struct Timer { + cycles: Cycles, +} impl Timer { + + pub fn new() -> Self { + Self { + cycles: Cycles(0), + } + } + + fn increment_cycles(&mut self, cycles: Cycles) { + self.cycles.0 += cycles.0; + } + + fn reset_cycles(&mut self) { + self.cycles.0 = 0; + } - pub fn do_cycles(bus: &mut Bus, cycles: Cycles) { + pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles) { let mut count = 0; while count < cycles.to_t() { - Timer::cycle(bus); + self.cycle(bus); count += 1; } } - fn cycle(bus: &mut Bus) { + fn cycle(&mut self, bus: &mut Bus) { let div = bus.read(TIMER_DIVIDER_REGISTER_ADDRESS); bus.write(TIMER_DIVIDER_REGISTER_ADDRESS, div.wrapping_add(1)); if Timer::is_timer_enabled(bus) { let tima = bus.read(TIMER_COUNTER_ADDRESS); - let tima_increment = Timer::get_tima_increment(bus); - if tima.checked_add(tima_increment) == None { - bus.write(TIMER_COUNTER_ADDRESS, bus.read(TIMER_MODULO_ADDRESS)); - bus.set_interrupt(Interrupt::Timer, true); - } else { - bus.write(TIMER_COUNTER_ADDRESS, tima.wrapping_add(tima_increment)); + let tima_rate = Timer::get_tima_rate(bus); + if self.cycles.0 >= tima_rate { + if tima.checked_add(1) == None { + bus.write(TIMER_COUNTER_ADDRESS, bus.read(TIMER_MODULO_ADDRESS)); + bus.set_interrupt_flag(Interrupt::Timer, true); + } else { + bus.write(TIMER_COUNTER_ADDRESS, tima.wrapping_add(1)); + } + self.reset_cycles(); } } + + self.increment_cycles(Cycles(1)); } fn is_timer_enabled(bus: &Bus) -> bool { get_bit(bus.read(TIMER_CONTROL_ADDRESS), BitIndex::I2) } - fn get_tima_increment(bus: &Bus) -> u8 { + fn get_tima_rate(bus: &Bus) -> usize { let clock_select = bus.read(TIMER_CONTROL_ADDRESS) & 0b0000_0011; match clock_select { - 0b00 => (4096 as u16 / 1026 as u16 / 4 as u16).to_be_bytes()[1], - 0b01 => (4096 as u16 / 16 as u16 / 4 as u16).to_be_bytes()[1], - 0b10 => (4096 as u16 / 64 as u16 / 4 as u16).to_be_bytes()[1], - 0b11 => (4096 as u16 / 256 as u16 / 4 as u16).to_be_bytes()[1], + 0b00 => 16, + 0b01 => 64, + 0b10 => 256, + 0b11 => 1024, _ => 1, } }