Pass all CPU tests in GBC fast mode (timer)
This commit is contained in:
parent
a58e599c7a
commit
34ae61e649
34
src/cpu.rs
34
src/cpu.rs
@ -883,32 +883,52 @@ impl CPU {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_interrupts(&mut self) {
|
||||
fn check_interrupts(&mut self, execute: bool) -> bool {
|
||||
// read pending interrupts
|
||||
let pending = self.interconnect.read_byte(0xFF0F);
|
||||
let enabled = self.interconnect.read_byte(0xFFFF);
|
||||
let e_pending = pending & enabled;
|
||||
|
||||
if e_pending & interconnect::INTERRUPT_DISPLAY_VBLANK > 0 {
|
||||
self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK);
|
||||
if execute {
|
||||
self.handle_interrupt(0x40, interconnect::INTERRUPT_DISPLAY_VBLANK);
|
||||
}
|
||||
return true;
|
||||
} else if e_pending & interconnect::INTERRUPT_DISPLAY_STAT > 0 {
|
||||
self.handle_interrupt(0x48, interconnect::INTERRUPT_DISPLAY_STAT);
|
||||
if execute {
|
||||
self.handle_interrupt(0x48, interconnect::INTERRUPT_DISPLAY_STAT);
|
||||
}
|
||||
return true;
|
||||
} else if e_pending & interconnect::INTERRUPT_TIMER_OVERFLOW > 0 {
|
||||
self.handle_interrupt(0x50, interconnect::INTERRUPT_TIMER_OVERFLOW);
|
||||
if execute {
|
||||
self.handle_interrupt(0x50, interconnect::INTERRUPT_TIMER_OVERFLOW);
|
||||
}
|
||||
return true;
|
||||
} else if e_pending & interconnect::INTERRUPT_SERIAL > 0 {
|
||||
self.handle_interrupt(0x58, interconnect::INTERRUPT_SERIAL);
|
||||
if execute {
|
||||
self.handle_interrupt(0x58, interconnect::INTERRUPT_SERIAL);
|
||||
}
|
||||
return true;
|
||||
} else if e_pending & interconnect::INTERRUPT_INPUT > 0 {
|
||||
self.handle_interrupt(0x60, interconnect::INTERRUPT_INPUT);
|
||||
if execute {
|
||||
self.handle_interrupt(0x60, interconnect::INTERRUPT_INPUT);
|
||||
}
|
||||
return true;
|
||||
} else if e_pending > 0 {
|
||||
panic!("Unknown pending interrupt: {:02X}", e_pending);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn run_instruction(&mut self) -> u8 {
|
||||
// self.debug = !self.interconnect.is_boot_rom();
|
||||
// Check for interrupts.
|
||||
if self.ime {
|
||||
self.check_interrupts();
|
||||
self.check_interrupts(true);
|
||||
} else if self.halted {
|
||||
if self.check_interrupts(false) {
|
||||
self.halted = false;
|
||||
}
|
||||
}
|
||||
|
||||
let mut cycles: u8 = 1;
|
||||
|
||||
@ -452,7 +452,7 @@ impl Display {
|
||||
// We need to draw this.
|
||||
let wide_mode = self.control & CTRL_BG_SPRITE_SIZE > 0;
|
||||
if wide_mode {
|
||||
panic!("TODO");
|
||||
// panic!("TODO");
|
||||
}
|
||||
let limit = match wide_mode {
|
||||
true => 16,
|
||||
@ -472,15 +472,19 @@ impl Display {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let x_o2: u8 = match sprite.is_x_flipped() {
|
||||
true => x_o ^ 7,
|
||||
false => x_o,
|
||||
};
|
||||
|
||||
if wide_mode && x_o > 7 {
|
||||
b1 = (tile_line_3 & 1 << (14 - x_o2)) > 0;
|
||||
b2 = (tile_line_4 & 1 << (14 - x_o2)) > 0;
|
||||
let x_o2: u8 = match sprite.is_x_flipped() {
|
||||
true => (x_o - 8) ^ 7,
|
||||
false => (x_o - 8),
|
||||
};
|
||||
b1 = (tile_line_3 & 1 << (7 - x_o2)) > 0;
|
||||
b2 = (tile_line_4 & 1 << (7 - x_o2)) > 0;
|
||||
} else {
|
||||
let x_o2: u8 = match sprite.is_x_flipped() {
|
||||
true => x_o ^ 7,
|
||||
false => x_o,
|
||||
};
|
||||
b1 = (tile_line_1 & 1 << (7 - x_o2)) > 0;
|
||||
b2 = (tile_line_2 & 1 << (7 - x_o2)) > 0;
|
||||
}
|
||||
|
||||
@ -231,6 +231,7 @@ impl Interconnect {
|
||||
0xFF01 ... 0xFF02 => self.serial.read_byte(addr),
|
||||
0xFF04 ... 0xFF07 => self.timer.read_byte(addr),
|
||||
0xFF0F => {
|
||||
// println!("Reading IF: {:02X}", self.interrupt_request_flags);
|
||||
self.interrupt_request_flags
|
||||
},
|
||||
0xFF10 ... 0xFF26 => {
|
||||
|
||||
42
src/timer.rs
42
src/timer.rs
@ -7,26 +7,31 @@ pub struct Timer {
|
||||
timer_interrupt: bool,
|
||||
tick_counter: u16,
|
||||
div_tick_counter: u16,
|
||||
|
||||
gb_ticks: u16,
|
||||
}
|
||||
|
||||
const TIMER_SPEED: [u16; 4] = [64, 1, 4, 16];
|
||||
const TIMER_ENABLE: u8 = (1 << 2);
|
||||
|
||||
impl Timer {
|
||||
pub fn new() -> Timer {
|
||||
Timer::default()
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, ticks: u16) {
|
||||
// One tick 1/4.194304MHz on regular speed?
|
||||
if self.tac & 1 << 2 == 1 { // Is timer enabled?
|
||||
self.tick_counter += ticks;
|
||||
let req_ticks = match self.tac & 3 {
|
||||
0 => 10240,
|
||||
1 => 160,
|
||||
2 => 640,
|
||||
3 => 2560,
|
||||
_ => unreachable!()
|
||||
};
|
||||
fn timer_clock_tick(&mut self) {
|
||||
// The div reg will always tick
|
||||
self.div_tick_counter += 1;
|
||||
if self.div_tick_counter >= TIMER_SPEED[3] {
|
||||
self.div = self.div.wrapping_add(1);
|
||||
self.div_tick_counter -= TIMER_SPEED[3];
|
||||
}
|
||||
|
||||
if self.tick_counter > req_ticks {
|
||||
if (self.tac & TIMER_ENABLE) == TIMER_ENABLE { // Is timer enabled?
|
||||
self.tick_counter += 1;
|
||||
let req_ticks = TIMER_SPEED[(self.tac & 3) as usize];
|
||||
|
||||
if self.tick_counter >= req_ticks {
|
||||
if self.tima == 0xFF {
|
||||
self.timer_interrupt = true;
|
||||
self.tima = 0;
|
||||
@ -36,12 +41,15 @@ impl Timer {
|
||||
self.tick_counter -= req_ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The div reg will always tick
|
||||
self.div_tick_counter += ticks;
|
||||
if self.div_tick_counter > 2560 {
|
||||
self.div = self.div.wrapping_add(1);
|
||||
self.div_tick_counter -= 2560;
|
||||
pub fn tick(&mut self, ticks: u16) {
|
||||
// One tick 1/4.194304MHz on regular speed => 16 gb ticks
|
||||
self.gb_ticks += ticks;
|
||||
// If we're in GBC fast mode, we'd require 32 ticks instead of 16
|
||||
while self.gb_ticks >= 16 {
|
||||
self.timer_clock_tick();
|
||||
self.gb_ticks -= 16;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user