Skip to content

Commit

Permalink
Made DIV timer more accurate
Browse files Browse the repository at this point in the history
  • Loading branch information
velllu committed Sep 27, 2024
1 parent c0ae2d8 commit d97ee4c
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 25 deletions.
4 changes: 2 additions & 2 deletions examples/debugger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ fn pretty_print_gameboy(gameboy: &GameBoy) -> Result<(), io::Error> {
writeln!(lock, "{}", "Flags".bold().red())?;
writeln!(
lock,
" Zero: {}, Carry: {}, Subtraction: {}, Half Carry: {}, IME: {}",
" Zero: {}, Subtraction: {}, Half Carry: {}, Carry: {}, IME: {}",
bool_to_symbol(gameboy.flags.zero),
bool_to_symbol(gameboy.flags.carry),
bool_to_symbol(gameboy.flags.subtraction),
bool_to_symbol(gameboy.flags.half_carry),
bool_to_symbol(gameboy.flags.carry),
bool_to_symbol(gameboy.cpu.ime),
)?;

Expand Down
30 changes: 22 additions & 8 deletions src/bus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ use std::{error::Error, fmt::Display, fs::File, io::Read};
use mbc1::Mbc1;
use mbc_no::NoMbc;

use crate::{common::merge_two_u8s_into_u16, consts::bus::*, registers::Registers};
use crate::{
common::merge_two_u8s_into_u16,
consts::{bus::*, cpu::DIV},
registers::Registers,
};

mod mbc1;
mod mbc_no;
Expand Down Expand Up @@ -46,6 +50,10 @@ pub struct Bus {

/// Gets true when the user writes to OAM DMA register
pub needs_to_dispatch_oam_dma: bool,

/// Gets true when the emulator writes to DIV, this means that we must reset the div
/// register internal cycle counter
pub(crate) needs_to_reset_div_register: bool,
}

impl Bus {
Expand Down Expand Up @@ -74,6 +82,7 @@ impl Bus {
io: new_io(),
unusable_ram: [0u8; UNUSABLE_RAM_SIZE],
needs_to_dispatch_oam_dma: false,
needs_to_reset_div_register: false,
})
}

Expand All @@ -88,6 +97,7 @@ impl Bus {
io: new_io(),
unusable_ram: [0u8; UNUSABLE_RAM_SIZE],
needs_to_dispatch_oam_dma: false,
needs_to_reset_div_register: false,
}
}
}
Expand All @@ -112,20 +122,24 @@ impl Bus {

pub fn write(&mut self, address: u16, value: u8) {
match address {
DMA => {
self.needs_to_dispatch_oam_dma = true;
self.io[0x46] = value;
}

DIV => {
self.needs_to_reset_div_register = true;
self.io[0x04] = 0;
}

0x0000..=0x7FFF => self.mbc.signal_rom_write(address, value),
0x8000..=0x9FFF => self.video_ram[(address - 0x8000) as usize] = value,
0xA000..=0xBFFF => self.mbc.set_external_ram(address - 0xA000, value),
0xC000..=0xDFFF => self.work_ram[(address - 0xC000) as usize] = value,
0xE000..=0xFDFF => self.work_ram[(address - 0xE000) as usize] = value,
0xFE00..=0xFE9F => self.eom[(address - 0xFE00) as usize] = value,
0xFEA0..=0xFEFF => self.unusable_ram[(address - 0xFEA0) as usize] = value,
0xFF00..=0xFF7F => {
if address == DMA {
self.needs_to_dispatch_oam_dma = true;
}

self.io[(address - IO_START as u16) as usize] = value;
}
0xFF00..=0xFF7F => self.io[(address - IO_START as u16) as usize] = value,
0xFF80..=0xFFFE => self.high_ram[(address - 0xFF80) as usize] = value,
0xFFFF => self.ie = value,
};
Expand Down
40 changes: 25 additions & 15 deletions src/cpu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,31 +23,41 @@ pub struct Cpu {
/// Wheter or not the CPU is halted
pub halt: bool,

/// This is increased after each cycle, it's used for the timers
pub(crate) div_register: u16,
/// A cycle counter for keeping track of when to increment the DIV register
pub(crate) div_cycle_counter: u8,

/// This is increased based on the TAC (timer control) register
pub(crate) tima_register: u16,
/// A cycle counter for keeping track of when to increment the TIMA register
pub(crate) tima_cycle_counter: u16,
}

impl Cpu {
pub(crate) fn new() -> Self {
Self {
ime: false,
halt: false,
div_register: 0,
tima_register: 0,
div_cycle_counter: 1,
tima_cycle_counter: 0,
}
}

/// Increase the div register
/// Increment the div register every 64 cycles
pub(crate) fn update_div_register(&mut self, bus: &mut Bus, cycles_num: u8) {
self.div_register = self.div_register.wrapping_add(cycles_num as u16);
// When we write to the div register we also reset the cycle counter
if bus.needs_to_reset_div_register {
// I don't know why this must be reset to 1, but it works!
self.div_cycle_counter = 1;
bus.needs_to_reset_div_register = false;
}

self.div_cycle_counter += cycles_num;

// While the div register is 16 bit, we only actually give the bus the higher 8
// bits, this means that it will increment after 256 cycles
let higher_byte = (self.div_register >> 8) as u8;
bus.write(DIV, higher_byte);
if self.div_cycle_counter >= 64 {
self.div_cycle_counter -= 64;

// Increment the DIV register, by using IO directly, otherwise writing to DIV
// will trigger a DIV reset
bus.io[0x04] = bus.read(DIV).wrapping_add(1);
}
}

/// Increse the tima register based on the contents of the timer control register
Expand All @@ -61,7 +71,7 @@ impl Cpu {
return;
}

self.tima_register += cycles_num as u16;
self.tima_cycle_counter += cycles_num as u16;

// This is the threshold at which we increment the timer counter
let increment_every = match (timer_control.get_bit(1), timer_control.get_bit(0)) {
Expand All @@ -71,8 +81,8 @@ impl Cpu {
(true, true) => 64,
};

if self.tima_register > increment_every {
self.tima_register -= increment_every;
if self.tima_cycle_counter > increment_every {
self.tima_cycle_counter -= increment_every;

// When the timer counter overflows we have to run a timer interrupt
let (result, has_overflown) = timer_counter.overflowing_add(1);
Expand Down

0 comments on commit d97ee4c

Please sign in to comment.