mirror of
https://github.com/FranLMSP/rmg-001.git
synced 2024-11-23 10:12:11 +00:00
Experimental interrupts
This commit is contained in:
parent
f90900a4c1
commit
c77bc9db70
56
src/bus.rs
56
src/bus.rs
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
60
src/cpu.rs
60
src/cpu.rs
@ -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 => {
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
src/ppu.rs
22
src/ppu.rs
@ -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));
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user