//! Timer functions and structures. use std::io::Cursor; use boytacean_common::{ data::{read_u16, read_u8, write_u16, write_u8}, error::Error, }; use crate::{ consts::{DIV_ADDR, TAC_ADDR, TIMA_ADDR, TMA_ADDR}, mmu::BusComponent, panic_gb, state::{StateComponent, StateFormat}, warnln, }; pub struct Timer { div: u8, tima: u8, tma: u8, tac: u8, div_clock: u16, tima_clock: u16, tima_enabled: bool, tima_ratio: u16, int_tima: bool, } impl Timer { pub fn new() -> Self { Self { div: 0, tima: 0, tma: 0, tac: 0x0, div_clock: 0, tima_clock: 0, tima_enabled: false, tima_ratio: 1024, int_tima: false, } } pub fn reset(&mut self) { self.div = 0; self.tima = 0; self.tma = 0; self.tac = 0x0; self.div_clock = 0; self.tima_clock = 0; self.tima_enabled = false; self.tima_ratio = 1024; self.int_tima = false; } pub fn clock(&mut self, cycles: u16) { self.div_clock += cycles; while self.div_clock >= 256 { self.div = self.div.wrapping_add(1); self.div_clock -= 256; } if self.tima_enabled { self.tima_clock += cycles; while self.tima_clock >= self.tima_ratio { // in case TIMA value overflows must set the // interrupt and update the TIMA value to // the TMA one (reset operation) if self.tima == 0xff { self.int_tima = true; self.tima = self.tma; } // otherwise uses the normal add operation // and increments the TIMA value by one else { self.tima = self.tima.wrapping_add(1); } self.tima_clock -= self.tima_ratio; } } } pub fn read(&self, addr: u16) -> u8 { match addr { // 0xFF04 — DIV: Divider register DIV_ADDR => self.div, // 0xFF05 — TIMA: Timer counter TIMA_ADDR => self.tima, // 0xFF06 — TMA: Timer modulo TMA_ADDR => self.tma, // 0xFF07 — TAC: Timer control TAC_ADDR => self.tac | 0xf8, _ => { warnln!("Reding from unknown Timer location 0x{:04x}", addr); #[allow(unreachable_code)] 0xff } } } pub fn write(&mut self, addr: u16, value: u8) { match addr { // 0xFF04 — DIV: Divider register DIV_ADDR => self.div = 0, // 0xFF05 — TIMA: Timer counter TIMA_ADDR => self.tima = value, // 0xFF06 — TMA: Timer modulo TMA_ADDR => self.tma = value, // 0xFF07 — TAC: Timer control TAC_ADDR => { self.tac = value; match value & 0x03 { 0x00 => self.tima_ratio = 1024, 0x01 => self.tima_ratio = 16, 0x02 => self.tima_ratio = 64, 0x03 => self.tima_ratio = 256, value => panic_gb!("Invalid TAC value 0x{:02x}", value), } self.tima_enabled = (value & 0x04) == 0x04; } _ => warnln!("Writing to unknown Timer location 0x{:04x}", addr), } } #[inline(always)] pub fn int_tima(&self) -> bool { self.int_tima } #[inline(always)] pub fn set_int_tima(&mut self, value: bool) { self.int_tima = value; } #[inline(always)] pub fn ack_tima(&mut self) { self.set_int_tima(false); } #[inline(always)] pub fn div(&self) -> u8 { self.div } #[inline(always)] pub fn set_div(&mut self, value: u8) { self.div = value; } #[inline(always)] pub fn div_clock(&self) -> u16 { self.div_clock } #[inline(always)] pub fn set_div_clock(&mut self, value: u16) { self.div_clock = value; } } impl BusComponent for Timer { fn read(&self, addr: u16) -> u8 { self.read(addr) } fn write(&mut self, addr: u16, value: u8) { self.write(addr, value); } } impl StateComponent for Timer { fn state(&self, _format: Option<StateFormat>) -> Result<Vec<u8>, Error> { let mut cursor = Cursor::new(vec![]); write_u8(&mut cursor, self.div)?; write_u8(&mut cursor, self.tima)?; write_u8(&mut cursor, self.tma)?; write_u8(&mut cursor, self.tac)?; write_u16(&mut cursor, self.div_clock)?; write_u16(&mut cursor, self.tima_clock)?; write_u8(&mut cursor, self.tima_enabled as u8)?; write_u16(&mut cursor, self.tima_ratio)?; write_u8(&mut cursor, self.int_tima as u8)?; Ok(cursor.into_inner()) } fn set_state(&mut self, data: &[u8], _format: Option<StateFormat>) -> Result<(), Error> { let mut cursor = Cursor::new(data); self.div = read_u8(&mut cursor)?; self.tima = read_u8(&mut cursor)?; self.tma = read_u8(&mut cursor)?; self.tac = read_u8(&mut cursor)?; self.div_clock = read_u16(&mut cursor)?; self.tima_clock = read_u16(&mut cursor)?; self.tima_enabled = read_u8(&mut cursor)? != 0; self.tima_ratio = read_u16(&mut cursor)?; self.int_tima = read_u8(&mut cursor)? != 0; Ok(()) } } impl Default for Timer { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::Timer; use crate::state::StateComponent; #[test] fn test_state_and_set_state() { let timer = Timer { div: 0x12, tima: 0x34, tma: 0x56, tac: 0x78, div_clock: 0x9abc, tima_clock: 0xdef0, tima_enabled: true, tima_ratio: 0x1234, int_tima: true, }; let state = timer.state(None).unwrap(); assert_eq!(state.len(), 12); let mut new_timer = Timer::new(); new_timer.set_state(&state, None).unwrap(); assert_eq!(new_timer.div, 0x12); assert_eq!(new_timer.tima, 0x34); assert_eq!(new_timer.tma, 0x56); assert_eq!(new_timer.tac, 0x78); assert_eq!(new_timer.div_clock, 0x9abc); assert_eq!(new_timer.tima_clock, 0xdef0); assert!(new_timer.tima_enabled); assert_eq!(new_timer.tima_ratio, 0x1234); assert!(new_timer.int_tima); } }