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; pub use self::rom::Rom;
use crate::{ use crate::{
processor::SplitRegister, processor::SplitRegister,
util::{clear_bit, set_bit}, util::{clear_bit, set_bit, set_or_clear_bit},
verbose_println, Cpu, verbose_println, Cpu,
}; };
use gilrs::Gilrs; use gilrs::Gilrs;
@ -29,6 +29,7 @@ pub struct Memory {
gpu: Gpu, gpu: Gpu,
apu: Apu, apu: Apu,
serial: Serial, serial: Serial,
timers: Timer,
} }
impl Memory { impl Memory {
@ -59,6 +60,7 @@ impl Memory {
gpu: Gpu::new(window, enable_tile_window), gpu: Gpu::new(window, enable_tile_window),
apu: Apu::init_default(), apu: Apu::init_default(),
serial, serial,
timers: Timer::init(),
} }
} }
@ -119,6 +121,10 @@ impl Memory {
0xFF00 => self.joypad.as_register(), 0xFF00 => self.joypad.as_register(),
0xFF01 => self.serial.get_queued(), 0xFF01 => self.serial.get_queued(),
0xFF02 => self.serial.get_control(), 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), 0xFF10..0xFF40 => self.apu.get_register(address),
0xFF40 => self.gpu.get_lcdc(), 0xFF40 => self.gpu.get_lcdc(),
0xFF41 => self.gpu.get_lcd_status(), 0xFF41 => self.gpu.get_lcd_status(),
@ -148,8 +154,10 @@ impl Memory {
} }
0xFF01 => self.serial.update_queued(data), 0xFF01 => self.serial.update_queued(data),
0xFF02 => self.serial.update_control(data), 0xFF02 => self.serial.update_control(data),
0xFF04 => self.io[addr_l] = 0, 0xFF04 => self.timers.update_div(),
0xFF07 => self.masked_io(addr_l, data, 0b111), 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), 0xFF0F => self.masked_io(addr_l, data, 0b11111),
0xFF10..0xFF40 => self.apu.mmio_write(address, data), 0xFF10..0xFF40 => self.apu.mmio_write(address, data),
0xFF40 => self.gpu.update_lcdc(data), 0xFF40 => self.gpu.update_lcdc(data),
@ -179,7 +187,6 @@ impl Memory {
println!("BANNED write: {data:#X} to {address:#X}"); println!("BANNED write: {data:#X} to {address:#X}");
} }
0x0..0xFF00 | 0xFF4C..=u16::MAX => panic!("passed wrong address to set_io"), 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) 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) { pub(super) fn cpu_ram_init(&mut self) {
self.set(0xFF04, 0xAD); self.set(0xFF04, 0xAD);
self.set(0xFF10, 0x80); self.set(0xFF10, 0x80);
@ -224,7 +227,19 @@ impl Memory {
} }
impl Cpu { 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); self.memory.apu.tick(steps);
if self.memory.serial.tick(steps) { if self.memory.serial.tick(steps) {
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 3)); self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 3));

View file

@ -1,77 +1,154 @@
use crate::{ use crate::util::{get_bit, set_or_clear_bit};
processor::Cpu,
util::{get_bit, set_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 { pub struct Timer {
// 0xFF04
div: u8,
// 0xFF05
tima: u8,
// 0xFF06
tma: u8,
// 0xFF07
control: TimerControl,
div_counter: usize, div_counter: usize,
tima_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 // this will need to change when cgb mode is implemented
// as it uses bit 5 in double speed mode // as it uses bit 5 in double speed mode
const AUDIO_BIT: u8 = 4; const AUDIO_BIT: u8 = 4;
impl Cpu { impl Timer {
pub fn increment_timers(&mut self, machine_cycles: u8) { pub fn init() -> Self {
let clock_cycles = (machine_cycles as usize) * 4; Self {
div: 0,
tima: 0,
tma: 0,
control: TimerControl::default(),
div_counter: 0,
tima_counter: 0,
}
}
self.advance_mmio_clocks(clock_cycles); pub fn tick(&mut self, clock_cycles: usize) -> TimerReturn {
self.div_counter += clock_cycles;
self.timers.div_counter += clock_cycles; let mut div_diff = (self.div_counter / 256) as u8;
let mut div_diff = (self.timers.div_counter / 256) as u8; let mut last_div = self.div;
let mut last_div = self.memory.get(0xFF04); let mut returning = TimerReturn::default();
let mut new_div = None;
while div_diff > 0 { while div_diff > 0 {
let div = last_div.wrapping_add(1); let div = last_div.wrapping_add(1);
if (div & (1 << AUDIO_BIT)) < (last_div & (1 << AUDIO_BIT)) { if (div & (1 << AUDIO_BIT)) < (last_div & (1 << AUDIO_BIT)) {
// trigger DIV-APU // trigger DIV-APU
self.memory.div_apu_tick(); returning.num_apu_ticks += 1;
} }
new_div = Some(div); self.div = div;
last_div = div; last_div = div;
div_diff -= 1; div_diff -= 1;
} }
self.timers.div_counter %= 256; self.div_counter %= 256;
if let Some(div) = new_div {
self.memory.set(0xFF04, div)
};
let (timer_enabled, timer_rate) = self.timer_scale(); if self.control.enable {
if timer_enabled { self.tima_counter += clock_cycles;
self.timers.tima_counter += clock_cycles; let tima_diff = (self.tima_counter / self.control.rate.as_num()) as u8;
let tima_diff = (self.timers.tima_counter / timer_rate) as u8; self.tima_counter %= self.control.rate.as_num();
self.timers.tima_counter %= timer_rate; let (val, wrap) = self.tima.overflowing_add(tima_diff);
let (val, wrap) = self.memory.get(0xFF05).overflowing_add(tima_diff);
if wrap { if wrap {
self.memory.set(0xFF05, self.memory.get(0xFF06)); self.tima = self.tma;
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 2)); returning.timer_interrupt = true;
} else { } else {
self.memory.set(0xFF05, val); self.tima = val;
} }
} }
returning
} }
fn timer_scale(&self) -> (bool, usize) { pub fn update_div(&mut self) {
let timer_control = self.memory.get(0xFF07); self.div = 0;
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, pub fn get_div(&self) -> u8 {
(true, false) => 64, self.div
(false, true) => 16, }
(false, false) => 1024,
}; pub fn update_tima(&mut self, data: u8) {
(timer_enable, timer_rate) 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 std::time::Instant;
use self::memory::{mmio::Timer, Memory}; use self::memory::Memory;
use crate::{ use crate::{
util::{clear_bit, get_bit}, util::{clear_bit, get_bit},
verbose_println, verbose_println,
@ -30,7 +30,6 @@ pub struct Cpu {
pub last_instruction: u8, pub last_instruction: u8,
last_instruction_addr: u16, last_instruction_addr: u16,
halted: bool, halted: bool,
timers: Timer,
gamepad_handler: Gilrs, gamepad_handler: Gilrs,
cycle_start: Instant, cycle_start: Instant,
} }
@ -46,7 +45,6 @@ impl Cpu {
last_instruction: 0x0, last_instruction: 0x0,
last_instruction_addr: 0x0, last_instruction_addr: 0x0,
halted: false, halted: false,
timers: Timer::init(),
gamepad_handler, gamepad_handler,
cycle_start: Instant::now(), cycle_start: Instant::now(),
} }