From fd22f62f217b0c3f0c22f050e954213f3c5e66b2 Mon Sep 17 00:00:00 2001 From: Alex Janka Date: Fri, 24 Feb 2023 10:11:05 +1100 Subject: [PATCH] timer is memory mapped --- src/processor/memory.rs | 35 ++++-- src/processor/memory/mmio/timer.rs | 169 +++++++++++++++++++++-------- src/processor/mod.rs | 4 +- 3 files changed, 149 insertions(+), 59 deletions(-) diff --git a/src/processor/memory.rs b/src/processor/memory.rs index 2da3666..98bad65 100644 --- a/src/processor/memory.rs +++ b/src/processor/memory.rs @@ -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)); diff --git a/src/processor/memory/mmio/timer.rs b/src/processor/memory/mmio/timer.rs index 4136de8..068b0c7 100644 --- a/src/processor/memory/mmio/timer.rs +++ b/src/processor/memory/mmio/timer.rs @@ -1,77 +1,154 @@ -use crate::{ - processor::Cpu, - util::{get_bit, set_bit}, -}; +use crate::util::{get_bit, set_or_clear_bit}; -pub struct Timer { - div_counter: usize, - tima_counter: usize, +enum TimerRate { + Sixteen, + SixtyFour, + TwoHundredFiftySix, + OneThousandTwentyFour, } -impl Timer { - pub fn init() -> Self { - Self { - div_counter: 0, - tima_counter: 0, +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, } // 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) } } diff --git a/src/processor/mod.rs b/src/processor/mod.rs index cdbf6a2..3846161 100644 --- a/src/processor/mod.rs +++ b/src/processor/mod.rs @@ -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(), }