Experimental interrupts

This commit is contained in:
Franco Colmenarez 2021-10-29 15:40:47 -05:00
parent f90900a4c1
commit c77bc9db70
4 changed files with 114 additions and 54 deletions

View File

@ -6,7 +6,7 @@ use crate::utils::{
}; };
use crate::rom::ROM; use crate::rom::ROM;
use crate::ppu::{PPU, LCDStatus, LCDStatusModeFlag}; use crate::ppu::{PPU, LCDStatus, LCDStatusModeFlag};
use crate::cpu::{InterruptFlag}; use crate::cpu::{Interrupt};
pub struct AddressRange { pub struct AddressRange {
begin: u16, begin: u16,
@ -39,6 +39,7 @@ pub const NOT_USABLE: AddressRange = AddressRange{begin: 0xFEA0,
pub const IO_REGISTERS: AddressRange = AddressRange{begin: 0xFF00, end: 0xFF7F}; pub const IO_REGISTERS: AddressRange = AddressRange{begin: 0xFF00, end: 0xFF7F};
pub const HIGH_RAM: AddressRange = AddressRange{begin: 0xFF80, end: 0xFFFE}; pub const HIGH_RAM: AddressRange = AddressRange{begin: 0xFF80, end: 0xFFFE};
pub const INTERRUPT_ENABLE_REGISTER: AddressRange = AddressRange{begin: 0xFFFF, end: 0xFFFF}; pub const INTERRUPT_ENABLE_REGISTER: AddressRange = AddressRange{begin: 0xFFFF, end: 0xFFFF};
pub const INTERRUPT_ENABLE_ADDRESS: u16 = 0xFFFF;
pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F; pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F;
pub struct Bus { pub struct Bus {
@ -51,6 +52,7 @@ impl Bus {
let game_rom = match ROM::load_file("ignore/tetris.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.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/01-special.gb".to_string()) {
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/02-interrupts.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/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()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/04-op r,imm.gb".to_string()) {
// let game_rom = match ROM::load_file("roms/cpu_instrs_individual/05-op rp.gb".to_string()) { // let game_rom = match ROM::load_file("roms/cpu_instrs_individual/05-op rp.gb".to_string()) {
@ -75,7 +77,7 @@ impl Bus {
return self.game_rom.read(address); return self.game_rom.read(address);
} else if VIDEO_RAM.in_range(address) { } else if VIDEO_RAM.in_range(address) {
if PPU::get_lcd_status(self, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) { if PPU::get_lcd_status(self, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
return 0xFF return 0xFF;
} }
} else if IO_REGISTERS.in_range(address) { } else if IO_REGISTERS.in_range(address) {
return self.data[address as usize]; return self.data[address as usize];
@ -104,7 +106,7 @@ impl Bus {
self.data[address as usize] = data; self.data[address as usize] = data;
self.data[(WORK_RAM_1.begin() + (address - ECHO_RAM.begin())) as usize] = data; // Copy to the working RAM 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) { } else if VIDEO_RAM.in_range(address) {
//if !PPU::get_lcd_status(self, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) { // if !PPU::get_lcd_status(self, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD)) {
self.data[address as usize] = data; self.data[address as usize] = data;
// } // }
} else { } else {
@ -118,14 +120,46 @@ impl Bus {
self.write(address.wrapping_add(1), bytes[1]); self.write(address.wrapping_add(1), bytes[1]);
} }
pub fn set_flag(&mut self, flag: InterruptFlag, val: bool) { pub fn set_interrupt_master(&mut self, flag: Interrupt, val: bool) {
let byte = self.read(INTERRUPT_FLAG_ADDRESS); let byte = self.read(INTERRUPT_ENABLE_ADDRESS);
self.write(INTERRUPT_FLAG_ADDRESS, match flag { self.write(INTERRUPT_ENABLE_ADDRESS, match flag {
InterruptFlag::VBlank => set_bit(byte, val, BitIndex::I0), Interrupt::VBlank => set_bit(byte, val, BitIndex::I0),
InterruptFlag::LCDSTAT => set_bit(byte, val, BitIndex::I1), Interrupt::LCDSTAT => set_bit(byte, val, BitIndex::I1),
InterruptFlag::Timer => set_bit(byte, val, BitIndex::I2), Interrupt::Timer => set_bit(byte, val, BitIndex::I2),
InterruptFlag::Serial => set_bit(byte, val, BitIndex::I3), Interrupt::Serial => set_bit(byte, val, BitIndex::I3),
InterruptFlag::Joypad => set_bit(byte, val, BitIndex::I4), Interrupt::Joypad => set_bit(byte, val, BitIndex::I4),
}); });
} }
pub fn set_interrupt(&mut self, flag: Interrupt, val: bool) {
let byte = self.read(INTERRUPT_FLAG_ADDRESS);
self.write(INTERRUPT_FLAG_ADDRESS, match flag {
Interrupt::VBlank => set_bit(byte, val, BitIndex::I0),
Interrupt::LCDSTAT => set_bit(byte, val, BitIndex::I1),
Interrupt::Timer => set_bit(byte, val, BitIndex::I2),
Interrupt::Serial => set_bit(byte, val, BitIndex::I3),
Interrupt::Joypad => set_bit(byte, val, BitIndex::I4),
});
}
pub fn get_interrupt(&mut self, flag: Interrupt) -> bool {
let byte = self.read(INTERRUPT_ENABLE_ADDRESS) & self.read(INTERRUPT_FLAG_ADDRESS);
match flag {
Interrupt::VBlank => get_bit(byte, BitIndex::I0),
Interrupt::LCDSTAT => get_bit(byte, BitIndex::I1),
Interrupt::Timer => get_bit(byte, BitIndex::I2),
Interrupt::Serial => get_bit(byte, BitIndex::I3),
Interrupt::Joypad => get_bit(byte, BitIndex::I4),
}
}
pub fn get_interrupt_vector(flag: Interrupt) -> u16 {
match flag {
Interrupt::VBlank => 0x40,
Interrupt::LCDSTAT => 0x48,
Interrupt::Timer => 0x50,
Interrupt::Serial => 0x58,
Interrupt::Joypad => 0x60,
}
}
} }

View File

@ -8,7 +8,7 @@ use crate::utils::{
sub_half_carry, sub_half_carry,
add_half_carry_16bit, add_half_carry_16bit,
}; };
use crate::bus::Bus; use crate::bus::{Bus, INTERRUPT_ENABLE_ADDRESS, INTERRUPT_FLAG_ADDRESS};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum Register { pub enum Register {
@ -53,7 +53,8 @@ pub enum FlagRegister {
Carry, // Set if a carry was ocurrend from the last math operation or if register A is the smaller value when executing the CP instruction Carry, // Set if a carry was ocurrend from the last math operation or if register A is the smaller value when executing the CP instruction
} }
pub enum InterruptFlag { #[derive(Debug, Copy, Clone)]
pub enum Interrupt {
VBlank, VBlank,
LCDSTAT, LCDSTAT,
Timer, Timer,
@ -61,18 +62,6 @@ pub enum InterruptFlag {
Joypad, Joypad,
} }
impl InterruptFlag {
pub fn get_bit_index(interrupt: InterruptFlag) -> BitIndex {
match interrupt {
InterruptFlag::VBlank => BitIndex::I0,
InterruptFlag::LCDSTAT => BitIndex::I1,
InterruptFlag::Timer => BitIndex::I2,
InterruptFlag::Serial => BitIndex::I3,
InterruptFlag::Joypad => BitIndex::I4,
}
}
}
pub struct Registers { pub struct Registers {
a: u8, a: u8,
f: u8, f: u8,
@ -837,8 +826,11 @@ impl CPU {
} }
fn increment_cycles(&mut self, cycles: Cycles) { fn increment_cycles(&mut self, cycles: Cycles) {
let Cycles(c) = cycles; self.cycles.0 += cycles.0;
self.cycles.0 += c; }
fn decrement_cycles(&mut self, cycles: Cycles) {
self.cycles.0 -= cycles.0;
} }
pub fn reset_cycles(&mut self) { pub fn reset_cycles(&mut self) {
@ -876,6 +868,34 @@ impl CPU {
); );
} }
pub fn handle_interrupt(&mut self, bus: &mut Bus, interrupt: Interrupt) {
bus.set_interrupt_master(interrupt, false);
bus.set_interrupt(interrupt, false);
let vector = Bus::get_interrupt_vector(interrupt);
self.exec(Opcode::CALL(OpcodeParameter::U16(vector)), bus);
self.decrement_cycles(Cycles(1));
}
pub fn check_interrupt(&mut self, bus: &mut Bus) -> bool {
if bus.get_interrupt(Interrupt::VBlank) {
self.handle_interrupt(bus, Interrupt::VBlank);
return true;
} else if bus.get_interrupt(Interrupt::LCDSTAT) {
self.handle_interrupt(bus, Interrupt::LCDSTAT);
return true;
} else if bus.get_interrupt(Interrupt::Timer) {
self.handle_interrupt(bus, Interrupt::Timer);
return true;
} else if bus.get_interrupt(Interrupt::Serial) {
self.handle_interrupt(bus, Interrupt::Serial);
return true;
} else if bus.get_interrupt(Interrupt::Joypad) {
self.handle_interrupt(bus, Interrupt::Joypad);
return true;
}
return false;
}
pub fn run(&mut self, bus: &mut Bus) { pub fn run(&mut self, bus: &mut Bus) {
let cycles_start = self.get_cycles(); let cycles_start = self.get_cycles();
let program_counter = self.registers.get(Register::PC); let program_counter = self.registers.get(Register::PC);
@ -885,11 +905,11 @@ impl CPU {
self.log(parameter_bytes); self.log(parameter_bytes);
} }
self.increment_cycles(cycles); self.increment_cycles(cycles);
if !self.check_interrupt(bus) {
self.exec(opcode, bus); self.exec(opcode, bus);
}
let cycles_end = self.get_cycles(); let cycles_end = self.get_cycles();
self.set_last_op_cycles(cycles_start, cycles_end); self.set_last_op_cycles(cycles_start, cycles_end);
// self.increment_exec_calls_count();
} }
pub fn exec(&mut self, opcode: Opcode, bus: &mut Bus) { pub fn exec(&mut self, opcode: Opcode, bus: &mut Bus) {
@ -1713,12 +1733,12 @@ impl CPU {
// Enable interrupts // Enable interrupts
Opcode::EI => { Opcode::EI => {
self.registers.increment(Register::PC, 1); self.registers.increment(Register::PC, 1);
bus.write(0xFFFF, 0xFF); // Disable all interrupts bus.write(INTERRUPT_ENABLE_ADDRESS, 0xFF); // Disable all interrupts
}, },
// Disable interrupts // Disable interrupts
Opcode::DI => { Opcode::DI => {
self.registers.increment(Register::PC, 1); self.registers.increment(Register::PC, 1);
bus.write(0xFFFF, 0x00); // Disable all interrupts bus.write(INTERRUPT_ENABLE_ADDRESS, 0x00); // Disable all interrupts
}, },
// Same as enabling interrupts and then executing RET // Same as enabling interrupts and then executing RET
Opcode::RETI => { Opcode::RETI => {

View File

@ -30,8 +30,8 @@ impl Emulator {
pub fn run(&mut self, cpu_cycles: Cycles) { pub fn run(&mut self, cpu_cycles: Cycles) {
self.cpu.reset_cycles(); self.cpu.reset_cycles();
while self.cpu.get_cycles().0 <= cpu_cycles.0 { while self.cpu.get_cycles().0 <= cpu_cycles.0 {
self.ppu.do_cycle(&mut self.bus);
self.cpu.run(&mut self.bus); self.cpu.run(&mut self.bus);
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles());
} }
} }

View File

@ -5,7 +5,7 @@ use crate::utils::{
to_bit_index, to_bit_index,
}; };
use crate::bus::{Bus, AddressRange, BANK_ZERO, VIDEO_RAM}; use crate::bus::{Bus, AddressRange, BANK_ZERO, VIDEO_RAM};
use crate::cpu::{Cycles, InterruptFlag}; use crate::cpu::{Cycles, Interrupt};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
enum Pixel { enum Pixel {
@ -87,25 +87,31 @@ impl PPU {
self.cycles.0 += cycles.0; self.cycles.0 += cycles.0;
} }
pub fn do_cycle(&mut self, bus: &mut Bus) { pub fn do_cycles(&mut self, bus: &mut Bus, cycles: Cycles) {
let mut count = 0;
while count < cycles.0 {
self.cycle(bus);
count += 1;
}
}
pub fn cycle(&mut self, bus: &mut Bus) {
// Mode 1 Vertical blank // Mode 1 Vertical blank
if PPU::get_lcd_y(bus) >= 144 { if PPU::get_lcd_y(bus) >= 144 {
if PPU::get_lcd_y(bus) == 144 { bus.set_interrupt(Interrupt::VBlank, true);
bus.set_flag(InterruptFlag::VBlank, true);
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
}
} else { } else {
if self.cycles.0 == 0 { if self.cycles.0 == 0 {
// Mode 2 OAM scan // Mode 2 OAM scan
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::SearchingOAM), true);
} else if self.cycles.0 == 80 + 1 { } else if self.cycles.0 == 80 + 1 {
// Mode 3 drawing pixel line. This could also last 289 cycles // Mode 3 drawing pixel line. This could also last 289 cycles
bus.set_flag(InterruptFlag::LCDSTAT, true); bus.set_interrupt(Interrupt::LCDSTAT, true);
self.draw_line(bus); self.draw_line(bus);
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::TransferringToLCD), true);
} else if self.cycles.0 == 80 + 172 + 1 { } else if self.cycles.0 == 80 + 172 + 1 {
// Mode 0 Horizontal blank. This could last 87 or 204 cycles depending on the mode 3 // Mode 0 Horizontal blank. This could last 87 or 204 cycles depending on the mode 3
bus.set_flag(InterruptFlag::LCDSTAT, true); bus.set_interrupt(Interrupt::LCDSTAT, true);
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true); PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::HBlank), true);
} }
} }
@ -113,7 +119,7 @@ impl PPU {
let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS); let lyc_compare = PPU::get_lcd_y(bus) == bus.read(LCD_Y_COMPARE_ADDRESS);
PPU::set_lcd_status(bus, LCDStatus::LYCInterrupt, lyc_compare); PPU::set_lcd_status(bus, LCDStatus::LYCInterrupt, lyc_compare);
if lyc_compare { if lyc_compare {
bus.set_flag(InterruptFlag::LCDSTAT, true); bus.set_interrupt(Interrupt::LCDSTAT, true);
} }
self.increment_cycles(Cycles(1)); self.increment_cycles(Cycles(1));