interrupts work properly! and halt bug almost does
This commit is contained in:
parent
f0f194f5d2
commit
b574e1b1bf
|
@ -226,9 +226,9 @@ impl Cpu {
|
||||||
self.memory.apu.div_apu_tick();
|
self.memory.apu.div_apu_tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
self.memory
|
if timer_return.timer_interrupt {
|
||||||
.interrupts
|
self.memory.interrupts.set_interrupt(Interrupt::Timer, true);
|
||||||
.set_interrupt(Interrupt::Timer, timer_return.timer_interrupt);
|
}
|
||||||
|
|
||||||
self.memory.apu.tick(steps);
|
self.memory.apu.tick(steps);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::util::get_bit;
|
use crate::util::get_bit;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Debug)]
|
||||||
struct InterruptRegister {
|
struct InterruptRegister {
|
||||||
vblank: bool,
|
vblank: bool,
|
||||||
lcd_stat: bool,
|
lcd_stat: bool,
|
||||||
|
@ -31,6 +31,7 @@ fn bool_to_shifted(input: bool, shift: u8) -> u8 {
|
||||||
(if input { 1 } else { 0 }) << shift
|
(if input { 1 } else { 0 }) << shift
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Interrupt {
|
pub enum Interrupt {
|
||||||
Vblank,
|
Vblank,
|
||||||
LcdStat,
|
LcdStat,
|
||||||
|
@ -57,7 +58,7 @@ impl Interrupts {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get_flag_register(&self) -> u8 {
|
pub(super) fn get_flag_register(&self) -> u8 {
|
||||||
self.flag_register.as_register()
|
0b11100000 | self.flag_register.as_register()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_flag_register(&mut self, data: u8) {
|
pub(super) fn set_flag_register(&mut self, data: u8) {
|
||||||
|
@ -94,4 +95,12 @@ impl Interrupts {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_interrupt_queued(&self) -> bool {
|
||||||
|
(self.enable_register.vblank && self.flag_register.vblank)
|
||||||
|
|| (self.enable_register.lcd_stat && self.flag_register.lcd_stat)
|
||||||
|
|| (self.enable_register.timer && self.flag_register.timer)
|
||||||
|
|| (self.enable_register.serial && self.flag_register.serial)
|
||||||
|
|| (self.enable_register.joypad && self.flag_register.joypad)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ pub struct Cpu {
|
||||||
pub last_instruction: u8,
|
pub last_instruction: u8,
|
||||||
last_instruction_addr: u16,
|
last_instruction_addr: u16,
|
||||||
halted: bool,
|
halted: bool,
|
||||||
|
should_halt_bug: bool,
|
||||||
gamepad_handler: Gilrs,
|
gamepad_handler: Gilrs,
|
||||||
cycle_start: Instant,
|
cycle_start: Instant,
|
||||||
}
|
}
|
||||||
|
@ -41,6 +42,7 @@ impl Cpu {
|
||||||
last_instruction: 0x0,
|
last_instruction: 0x0,
|
||||||
last_instruction_addr: 0x0,
|
last_instruction_addr: 0x0,
|
||||||
halted: false,
|
halted: false,
|
||||||
|
should_halt_bug: false,
|
||||||
gamepad_handler,
|
gamepad_handler,
|
||||||
cycle_start: Instant::now(),
|
cycle_start: Instant::now(),
|
||||||
}
|
}
|
||||||
|
@ -56,10 +58,6 @@ impl Cpu {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.last_instruction_addr = self.reg.pc;
|
|
||||||
let opcode = self.next_opcode();
|
|
||||||
self.last_instruction = opcode;
|
|
||||||
|
|
||||||
if self.memory.ime_scheduled > 0 {
|
if self.memory.ime_scheduled > 0 {
|
||||||
self.memory.ime_scheduled = self.memory.ime_scheduled.saturating_sub(1);
|
self.memory.ime_scheduled = self.memory.ime_scheduled.saturating_sub(1);
|
||||||
if self.memory.ime_scheduled == 0 {
|
if self.memory.ime_scheduled == 0 {
|
||||||
|
@ -67,13 +65,20 @@ impl Cpu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.last_instruction_addr = self.reg.pc;
|
||||||
|
let opcode = self.next_opcode();
|
||||||
|
if self.should_halt_bug {
|
||||||
|
self.reg.pc = self.reg.pc.wrapping_sub(0x1);
|
||||||
|
self.should_halt_bug = false;
|
||||||
|
}
|
||||||
|
self.last_instruction = opcode;
|
||||||
|
|
||||||
verbose_println!(
|
verbose_println!(
|
||||||
"exec {:#4X} from pc: {:#X}",
|
"exec {:#4X} from pc: {:#X}",
|
||||||
opcode,
|
opcode,
|
||||||
self.last_instruction_addr
|
self.last_instruction_addr
|
||||||
);
|
);
|
||||||
let cycles = self.run_opcode(opcode);
|
self.run_and_increment_timers(opcode);
|
||||||
self.increment_timers(cycles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_opcode(&mut self) -> u8 {
|
fn next_opcode(&mut self) -> u8 {
|
||||||
|
@ -82,8 +87,22 @@ impl Cpu {
|
||||||
opcode
|
opcode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_and_increment_timers(&mut self, opcode: u8) {
|
||||||
|
let cycles = self.run_opcode(opcode);
|
||||||
|
self.increment_timers(cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn halt(&mut self) {
|
||||||
|
if !self.memory.ime && self.memory.interrupts.is_interrupt_queued() {
|
||||||
|
// halt bug
|
||||||
|
self.should_halt_bug = true;
|
||||||
|
} else {
|
||||||
|
self.halted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_interrupts(&mut self) -> u8 {
|
fn handle_interrupts(&mut self) -> u8 {
|
||||||
if self.memory.ime || self.halted {
|
if self.memory.ime {
|
||||||
if let Some(interrupt) = self.memory.interrupts.get_next_interrupt() {
|
if let Some(interrupt) = self.memory.interrupts.get_next_interrupt() {
|
||||||
let interrupt_addr = match interrupt {
|
let interrupt_addr = match interrupt {
|
||||||
Interrupt::Vblank => 0x40,
|
Interrupt::Vblank => 0x40,
|
||||||
|
@ -98,6 +117,9 @@ impl Cpu {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if self.halted && self.memory.interrupts.is_interrupt_queued() {
|
||||||
|
self.halted = false;
|
||||||
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -554,7 +554,7 @@ impl Cpu {
|
||||||
2
|
2
|
||||||
}
|
}
|
||||||
0x76 => {
|
0x76 => {
|
||||||
self.halted = true;
|
self.halt();
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
0x77 => {
|
0x77 => {
|
||||||
|
|
Loading…
Reference in a new issue