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::ppu::{PPU, LCDStatus, LCDStatusModeFlag};
|
||||
use crate::cpu::{InterruptFlag};
|
||||
use crate::cpu::{Interrupt};
|
||||
|
||||
pub struct AddressRange {
|
||||
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 HIGH_RAM: AddressRange = AddressRange{begin: 0xFF80, end: 0xFFFE};
|
||||
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 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("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()) {
|
||||
// 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/05-op rp.gb".to_string()) {
|
||||
@ -75,7 +77,7 @@ impl Bus {
|
||||
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
|
||||
return 0xFF;
|
||||
}
|
||||
} else if IO_REGISTERS.in_range(address) {
|
||||
return self.data[address as usize];
|
||||
@ -104,7 +106,7 @@ impl Bus {
|
||||
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) {
|
||||
//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;
|
||||
// }
|
||||
} else {
|
||||
@ -118,14 +120,46 @@ impl Bus {
|
||||
self.write(address.wrapping_add(1), bytes[1]);
|
||||
}
|
||||
|
||||
pub fn set_flag(&mut self, flag: InterruptFlag, val: bool) {
|
||||
let byte = self.read(INTERRUPT_FLAG_ADDRESS);
|
||||
self.write(INTERRUPT_FLAG_ADDRESS, match flag {
|
||||
InterruptFlag::VBlank => set_bit(byte, val, BitIndex::I0),
|
||||
InterruptFlag::LCDSTAT => set_bit(byte, val, BitIndex::I1),
|
||||
InterruptFlag::Timer => set_bit(byte, val, BitIndex::I2),
|
||||
InterruptFlag::Serial => set_bit(byte, val, BitIndex::I3),
|
||||
InterruptFlag::Joypad => set_bit(byte, val, BitIndex::I4),
|
||||
pub fn set_interrupt_master(&mut self, flag: Interrupt, val: bool) {
|
||||
let byte = self.read(INTERRUPT_ENABLE_ADDRESS);
|
||||
self.write(INTERRUPT_ENABLE_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 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
62
src/cpu.rs
62
src/cpu.rs
@ -8,7 +8,7 @@ use crate::utils::{
|
||||
sub_half_carry,
|
||||
add_half_carry_16bit,
|
||||
};
|
||||
use crate::bus::Bus;
|
||||
use crate::bus::{Bus, INTERRUPT_ENABLE_ADDRESS, INTERRUPT_FLAG_ADDRESS};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
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
|
||||
}
|
||||
|
||||
pub enum InterruptFlag {
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum Interrupt {
|
||||
VBlank,
|
||||
LCDSTAT,
|
||||
Timer,
|
||||
@ -61,18 +62,6 @@ pub enum InterruptFlag {
|
||||
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 {
|
||||
a: u8,
|
||||
f: u8,
|
||||
@ -837,8 +826,11 @@ impl CPU {
|
||||
}
|
||||
|
||||
fn increment_cycles(&mut self, cycles: Cycles) {
|
||||
let Cycles(c) = cycles;
|
||||
self.cycles.0 += c;
|
||||
self.cycles.0 += cycles.0;
|
||||
}
|
||||
|
||||
fn decrement_cycles(&mut self, cycles: Cycles) {
|
||||
self.cycles.0 -= cycles.0;
|
||||
}
|
||||
|
||||
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) {
|
||||
let cycles_start = self.get_cycles();
|
||||
let program_counter = self.registers.get(Register::PC);
|
||||
@ -885,11 +905,11 @@ impl CPU {
|
||||
self.log(parameter_bytes);
|
||||
}
|
||||
self.increment_cycles(cycles);
|
||||
self.exec(opcode, bus);
|
||||
if !self.check_interrupt(bus) {
|
||||
self.exec(opcode, bus);
|
||||
}
|
||||
let cycles_end = self.get_cycles();
|
||||
|
||||
self.set_last_op_cycles(cycles_start, cycles_end);
|
||||
// self.increment_exec_calls_count();
|
||||
}
|
||||
|
||||
pub fn exec(&mut self, opcode: Opcode, bus: &mut Bus) {
|
||||
@ -1713,12 +1733,12 @@ impl CPU {
|
||||
// Enable interrupts
|
||||
Opcode::EI => {
|
||||
self.registers.increment(Register::PC, 1);
|
||||
bus.write(0xFFFF, 0xFF); // Disable all interrupts
|
||||
bus.write(INTERRUPT_ENABLE_ADDRESS, 0xFF); // Disable all interrupts
|
||||
},
|
||||
// Disable interrupts
|
||||
Opcode::DI => {
|
||||
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
|
||||
Opcode::RETI => {
|
||||
|
@ -30,8 +30,8 @@ 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);
|
||||
self.ppu.do_cycles(&mut self.bus, self.cpu.get_last_op_cycles());
|
||||
}
|
||||
}
|
||||
|
||||
|
48
src/ppu.rs
48
src/ppu.rs
@ -5,7 +5,7 @@ use crate::utils::{
|
||||
to_bit_index,
|
||||
};
|
||||
use crate::bus::{Bus, AddressRange, BANK_ZERO, VIDEO_RAM};
|
||||
use crate::cpu::{Cycles, InterruptFlag};
|
||||
use crate::cpu::{Cycles, Interrupt};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
enum Pixel {
|
||||
@ -87,25 +87,31 @@ impl PPU {
|
||||
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
|
||||
if PPU::get_lcd_y(bus) >= 144 {
|
||||
if PPU::get_lcd_y(bus) == 144 {
|
||||
bus.set_flag(InterruptFlag::VBlank, true);
|
||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
|
||||
}
|
||||
bus.set_interrupt(Interrupt::VBlank, true);
|
||||
PPU::set_lcd_status(bus, LCDStatus::ModeFlag(LCDStatusModeFlag::VBlank), true);
|
||||
} else {
|
||||
if self.cycles.0 == 0 {
|
||||
// Mode 2 OAM scan
|
||||
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_flag(InterruptFlag::LCDSTAT, true);
|
||||
bus.set_interrupt(Interrupt::LCDSTAT, true);
|
||||
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_flag(InterruptFlag::LCDSTAT, true);
|
||||
bus.set_interrupt(Interrupt::LCDSTAT, 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);
|
||||
PPU::set_lcd_status(bus, LCDStatus::LYCInterrupt, lyc_compare);
|
||||
if lyc_compare {
|
||||
bus.set_flag(InterruptFlag::LCDSTAT, true);
|
||||
bus.set_interrupt(Interrupt::LCDSTAT, true);
|
||||
}
|
||||
self.increment_cycles(Cycles(1));
|
||||
|
||||
@ -186,15 +192,15 @@ impl PPU {
|
||||
pub fn get_lcd_status(bus: &Bus, status: LCDStatus) -> bool {
|
||||
let byte = bus.read(LCD_STATUS_ADDRESS);
|
||||
match status {
|
||||
LCDStatus::LYCInterrupt => get_bit(byte, BitIndex::I6),
|
||||
LCDStatus::Mode2OAMInterrupt => get_bit(byte, BitIndex::I5),
|
||||
LCDStatus::LYCInterrupt => get_bit(byte, BitIndex::I6),
|
||||
LCDStatus::Mode2OAMInterrupt => get_bit(byte, BitIndex::I5),
|
||||
LCDStatus::Mode1VBlankInterrupt => get_bit(byte, BitIndex::I4),
|
||||
LCDStatus::Mode0HBlankInterrupt => get_bit(byte, BitIndex::I3),
|
||||
LCDStatus::LYCFlag => get_bit(byte, BitIndex::I2),
|
||||
LCDStatus::LYCFlag => get_bit(byte, BitIndex::I2),
|
||||
LCDStatus::ModeFlag(mode) => match mode {
|
||||
LCDStatusModeFlag::HBlank => (byte & 0b00000011) == 0,
|
||||
LCDStatusModeFlag::VBlank => (byte & 0b00000011) == 1,
|
||||
LCDStatusModeFlag::SearchingOAM => (byte & 0b00000011) == 2,
|
||||
LCDStatusModeFlag::HBlank => (byte & 0b00000011) == 0,
|
||||
LCDStatusModeFlag::VBlank => (byte & 0b00000011) == 1,
|
||||
LCDStatusModeFlag::SearchingOAM => (byte & 0b00000011) == 2,
|
||||
LCDStatusModeFlag::TransferringToLCD => (byte & 0b00000011) == 3,
|
||||
},
|
||||
}
|
||||
@ -203,15 +209,15 @@ impl PPU {
|
||||
fn set_lcd_status(bus: &mut Bus, status: LCDStatus, val: bool) {
|
||||
let mut byte = bus.read(LCD_STATUS_ADDRESS);
|
||||
byte = match status {
|
||||
LCDStatus::LYCInterrupt => set_bit(byte, val, BitIndex::I6),
|
||||
LCDStatus::Mode2OAMInterrupt => set_bit(byte, val, BitIndex::I5),
|
||||
LCDStatus::LYCInterrupt => set_bit(byte, val, BitIndex::I6),
|
||||
LCDStatus::Mode2OAMInterrupt => set_bit(byte, val, BitIndex::I5),
|
||||
LCDStatus::Mode1VBlankInterrupt => set_bit(byte, val, BitIndex::I4),
|
||||
LCDStatus::Mode0HBlankInterrupt => set_bit(byte, val, BitIndex::I3),
|
||||
LCDStatus::LYCFlag => set_bit(byte, val, BitIndex::I2),
|
||||
LCDStatus::LYCFlag => set_bit(byte, val, BitIndex::I2),
|
||||
LCDStatus::ModeFlag(mode) => match mode {
|
||||
LCDStatusModeFlag::HBlank => (byte & 0b11111100) | 0,
|
||||
LCDStatusModeFlag::VBlank => (byte & 0b11111100) | 1,
|
||||
LCDStatusModeFlag::SearchingOAM => (byte & 0b11111100) | 2,
|
||||
LCDStatusModeFlag::HBlank => (byte & 0b11111100) | 0,
|
||||
LCDStatusModeFlag::VBlank => (byte & 0b11111100) | 1,
|
||||
LCDStatusModeFlag::SearchingOAM => (byte & 0b11111100) | 2,
|
||||
LCDStatusModeFlag::TransferringToLCD => (byte & 0b11111100) | 3,
|
||||
},
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user