timer is memory mapped

This commit is contained in:
Alex Janka 2023-02-24 10:11:05 +11:00
parent 5c44401c68
commit fd22f62f21
3 changed files with 149 additions and 59 deletions

View file

@ -1,8 +1,8 @@
use self::mmio::{Apu, Gpu, Joypad, Serial};
use self::mmio::{Apu, Gpu, Joypad, Serial, Timer};
pub use self::rom::Rom;
use crate::{
processor::SplitRegister,
util::{clear_bit, set_bit},
util::{clear_bit, set_bit, set_or_clear_bit},
verbose_println, Cpu,
};
use gilrs::Gilrs;
@ -29,6 +29,7 @@ pub struct Memory {
gpu: Gpu,
apu: Apu,
serial: Serial,
timers: Timer,
}
impl Memory {
@ -59,6 +60,7 @@ impl Memory {
gpu: Gpu::new(window, enable_tile_window),
apu: Apu::init_default(),
serial,
timers: Timer::init(),
}
}
@ -119,6 +121,10 @@ impl Memory {
0xFF00 => self.joypad.as_register(),
0xFF01 => self.serial.get_queued(),
0xFF02 => self.serial.get_control(),
0xFF04 => self.timers.get_div(),
0xFF05 => self.timers.get_tima(),
0xFF06 => self.timers.get_tma(),
0xFF07 => self.timers.get_timer_control(),
0xFF10..0xFF40 => self.apu.get_register(address),
0xFF40 => self.gpu.get_lcdc(),
0xFF41 => self.gpu.get_lcd_status(),
@ -148,8 +154,10 @@ impl Memory {
}
0xFF01 => self.serial.update_queued(data),
0xFF02 => self.serial.update_control(data),
0xFF04 => self.io[addr_l] = 0,
0xFF07 => self.masked_io(addr_l, data, 0b111),
0xFF04 => self.timers.update_div(),
0xFF05 => self.timers.update_tima(data),
0xFF06 => self.timers.update_tma(data),
0xFF07 => self.timers.update_timer_control(data),
0xFF0F => self.masked_io(addr_l, data, 0b11111),
0xFF10..0xFF40 => self.apu.mmio_write(address, data),
0xFF40 => self.gpu.update_lcdc(data),
@ -179,7 +187,6 @@ impl Memory {
println!("BANNED write: {data:#X} to {address:#X}");
}
0x0..0xFF00 | 0xFF4C..=u16::MAX => panic!("passed wrong address to set_io"),
_ => self.io[addr_l] = data,
}
}
}
@ -192,10 +199,6 @@ impl Memory {
self.joypad.update_pressed_keys(keys, gamepads)
}
pub fn div_apu_tick(&mut self) {
self.apu.div_apu_tick();
}
pub(super) fn cpu_ram_init(&mut self) {
self.set(0xFF04, 0xAD);
self.set(0xFF10, 0x80);
@ -224,7 +227,19 @@ impl Memory {
}
impl Cpu {
pub fn advance_mmio_clocks(&mut self, steps: usize) {
pub fn increment_timers(&mut self, machine_cycles: u8) {
let steps = (machine_cycles as usize) * 4;
let timer_return = self.memory.timers.tick(steps);
for _ in 0..timer_return.num_apu_ticks {
self.memory.apu.div_apu_tick();
}
self.memory.set(
0xFF0F,
set_or_clear_bit(self.memory.get(0xFF0F), 2, timer_return.timer_interrupt),
);
self.memory.apu.tick(steps);
if self.memory.serial.tick(steps) {
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 3));

View file

@ -1,77 +1,154 @@
use crate::{
processor::Cpu,
util::{get_bit, set_bit},
};
use crate::util::{get_bit, set_or_clear_bit};
enum TimerRate {
Sixteen,
SixtyFour,
TwoHundredFiftySix,
OneThousandTwentyFour,
}
impl TimerRate {
fn from_bits(one: bool, two: bool) -> Self {
match (one, two) {
(true, true) => Self::TwoHundredFiftySix,
(true, false) => Self::SixtyFour,
(false, true) => Self::Sixteen,
(false, false) => Self::OneThousandTwentyFour,
}
}
fn as_bits(&self) -> u8 {
match self {
TimerRate::Sixteen => 0b01,
TimerRate::SixtyFour => 0b10,
TimerRate::TwoHundredFiftySix => 0b11,
TimerRate::OneThousandTwentyFour => 0b00,
}
}
fn as_num(&self) -> usize {
match self {
TimerRate::Sixteen => 16,
TimerRate::SixtyFour => 64,
TimerRate::TwoHundredFiftySix => 256,
TimerRate::OneThousandTwentyFour => 1024,
}
}
}
struct TimerControl {
enable: bool,
rate: TimerRate,
}
impl Default for TimerControl {
fn default() -> Self {
Self {
enable: false,
rate: TimerRate::TwoHundredFiftySix,
}
}
}
#[derive(Default)]
pub struct TimerReturn {
pub num_apu_ticks: usize,
pub timer_interrupt: bool,
}
pub struct Timer {
// 0xFF04
div: u8,
// 0xFF05
tima: u8,
// 0xFF06
tma: u8,
// 0xFF07
control: TimerControl,
div_counter: usize,
tima_counter: usize,
}
impl Timer {
pub fn init() -> Self {
Self {
div_counter: 0,
tima_counter: 0,
}
}
}
// this will need to change when cgb mode is implemented
// as it uses bit 5 in double speed mode
const AUDIO_BIT: u8 = 4;
impl Cpu {
pub fn increment_timers(&mut self, machine_cycles: u8) {
let clock_cycles = (machine_cycles as usize) * 4;
impl Timer {
pub fn init() -> Self {
Self {
div: 0,
tima: 0,
tma: 0,
control: TimerControl::default(),
div_counter: 0,
tima_counter: 0,
}
}
self.advance_mmio_clocks(clock_cycles);
self.timers.div_counter += clock_cycles;
let mut div_diff = (self.timers.div_counter / 256) as u8;
let mut last_div = self.memory.get(0xFF04);
let mut new_div = None;
pub fn tick(&mut self, clock_cycles: usize) -> TimerReturn {
self.div_counter += clock_cycles;
let mut div_diff = (self.div_counter / 256) as u8;
let mut last_div = self.div;
let mut returning = TimerReturn::default();
while div_diff > 0 {
let div = last_div.wrapping_add(1);
if (div & (1 << AUDIO_BIT)) < (last_div & (1 << AUDIO_BIT)) {
// trigger DIV-APU
self.memory.div_apu_tick();
returning.num_apu_ticks += 1;
}
new_div = Some(div);
self.div = div;
last_div = div;
div_diff -= 1;
}
self.timers.div_counter %= 256;
if let Some(div) = new_div {
self.memory.set(0xFF04, div)
};
self.div_counter %= 256;
let (timer_enabled, timer_rate) = self.timer_scale();
if timer_enabled {
self.timers.tima_counter += clock_cycles;
let tima_diff = (self.timers.tima_counter / timer_rate) as u8;
self.timers.tima_counter %= timer_rate;
let (val, wrap) = self.memory.get(0xFF05).overflowing_add(tima_diff);
if self.control.enable {
self.tima_counter += clock_cycles;
let tima_diff = (self.tima_counter / self.control.rate.as_num()) as u8;
self.tima_counter %= self.control.rate.as_num();
let (val, wrap) = self.tima.overflowing_add(tima_diff);
if wrap {
self.memory.set(0xFF05, self.memory.get(0xFF06));
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 2));
self.tima = self.tma;
returning.timer_interrupt = true;
} else {
self.memory.set(0xFF05, val);
self.tima = val;
}
}
returning
}
fn timer_scale(&self) -> (bool, usize) {
let timer_control = self.memory.get(0xFF07);
let timer_enable = get_bit(timer_control, 2);
let timer_rate = match (get_bit(timer_control, 1), get_bit(timer_control, 0)) {
(true, true) => 256,
(true, false) => 64,
(false, true) => 16,
(false, false) => 1024,
};
(timer_enable, timer_rate)
pub fn update_div(&mut self) {
self.div = 0;
}
pub fn get_div(&self) -> u8 {
self.div
}
pub fn update_tima(&mut self, data: u8) {
self.tima = data;
}
pub fn get_tima(&self) -> u8 {
self.tima
}
pub fn update_tma(&mut self, data: u8) {
self.tma = data;
}
pub fn get_tma(&self) -> u8 {
self.tma
}
pub fn update_timer_control(&mut self, data: u8) {
self.control.enable = get_bit(data, 2);
self.control.rate = TimerRate::from_bits(get_bit(data, 1), get_bit(data, 0));
}
pub fn get_timer_control(&self) -> u8 {
set_or_clear_bit(!0b111 | self.control.rate.as_bits(), 2, self.control.enable)
}
}

View file

@ -1,6 +1,6 @@
use std::time::Instant;
use self::memory::{mmio::Timer, Memory};
use self::memory::Memory;
use crate::{
util::{clear_bit, get_bit},
verbose_println,
@ -30,7 +30,6 @@ pub struct Cpu {
pub last_instruction: u8,
last_instruction_addr: u16,
halted: bool,
timers: Timer,
gamepad_handler: Gilrs,
cycle_start: Instant,
}
@ -46,7 +45,6 @@ impl Cpu {
last_instruction: 0x0,
last_instruction_addr: 0x0,
halted: false,
timers: Timer::init(),
gamepad_handler,
cycle_start: Instant::now(),
}