From 2bbb0a04a1574d7629bfb03488edd08d036a0d16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Sat, 10 Aug 2024 17:55:09 +0100
Subject: [PATCH] chore: initial state management for timer

---
 crates/common/src/data.rs | 24 ++++++++++++
 crates/common/src/lib.rs  |  1 +
 src/timer.rs              | 78 ++++++++++++++++++++++++++++++++++++++-
 3 files changed, 102 insertions(+), 1 deletion(-)
 create mode 100644 crates/common/src/data.rs

diff --git a/crates/common/src/data.rs b/crates/common/src/data.rs
new file mode 100644
index 00000000..1f3973d0
--- /dev/null
+++ b/crates/common/src/data.rs
@@ -0,0 +1,24 @@
+use std::io::{Cursor, Read};
+
+use crate::error::Error;
+
+#[inline(always)]
+pub fn read_u8(data: &mut Cursor<&[u8]>) -> Result<u8, Error> {
+    let mut buffer = [0x00; size_of::<u8>()];
+    data.read_exact(&mut buffer)?;
+    Ok(u8::from_le_bytes(buffer))
+}
+
+#[inline(always)]
+pub fn read_u16(data: &mut Cursor<&[u8]>) -> Result<u16, Error> {
+    let mut buffer = [0x00; size_of::<u16>()];
+    data.read_exact(&mut buffer)?;
+    Ok(u16::from_le_bytes(buffer))
+}
+
+#[inline(always)]
+pub fn read_u32(data: &mut Cursor<&[u8]>) -> Result<u32, Error> {
+    let mut buffer = [0x00; size_of::<u32>()];
+    data.read_exact(&mut buffer)?;
+    Ok(u32::from_le_bytes(buffer))
+}
diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs
index 797c52e5..80b89fc5 100644
--- a/crates/common/src/lib.rs
+++ b/crates/common/src/lib.rs
@@ -1,4 +1,5 @@
 pub mod bench;
+pub mod data;
 pub mod error;
 pub mod util;
 
diff --git a/src/timer.rs b/src/timer.rs
index 2fe4038f..e902ea85 100644
--- a/src/timer.rs
+++ b/src/timer.rs
@@ -1,9 +1,18 @@
 //! Timer functions and structures.
 
+use std::io::{Cursor, Write};
+
+use boytacean_common::{
+    data::{read_u16, read_u8},
+    error::Error,
+};
+
 use crate::{
     consts::{DIV_ADDR, TAC_ADDR, TIMA_ADDR, TMA_ADDR},
     mmu::BusComponent,
-    panic_gb, warnln,
+    panic_gb,
+    state::StateComponent,
+    warnln,
 };
 
 pub struct Timer {
@@ -161,8 +170,75 @@ impl BusComponent for Timer {
     }
 }
 
+impl StateComponent for Timer {
+    fn state(&self) -> Result<Vec<u8>, Error> {
+        let mut cursor = Cursor::new(vec![]);
+        cursor.write(&self.div.to_le_bytes())?;
+        cursor.write(&self.tima.to_le_bytes())?;
+        cursor.write(&self.tma.to_le_bytes())?;
+        cursor.write(&self.tac.to_le_bytes())?;
+        cursor.write(&self.div_clock.to_le_bytes())?;
+        cursor.write(&self.tima_clock.to_le_bytes())?;
+        cursor.write(&(self.tima_enabled as u8).to_le_bytes())?;
+        cursor.write(&self.tima_ratio.to_le_bytes())?;
+        cursor.write(&(self.int_tima as u8).to_le_bytes())?;
+        Ok(cursor.into_inner())
+    }
+
+    fn set_state(&mut self, data: &[u8]) -> Result<(), boytacean_common::error::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)? == 1;
+        self.tima_ratio = read_u16(&mut cursor)?;
+        self.int_tima = read_u8(&mut cursor)? == 1;
+        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 mut timer = Timer::new();
+        timer.div = 0x12;
+        timer.tima = 0x34;
+        timer.tma = 0x56;
+        timer.tac = 0x78;
+        timer.div_clock = 0x9abc;
+        timer.tima_clock = 0xdef0;
+        timer.tima_enabled = true;
+        timer.tima_ratio = 0x1234;
+        timer.int_tima = true;
+
+        let state = timer.state().unwrap();
+        assert_eq!(state.len(), 12);
+
+        let mut new_timer = Timer::new();
+        new_timer.set_state(&state).unwrap();
+
+        assert_eq!(timer.div, new_timer.div);
+        assert_eq!(timer.tima, new_timer.tima);
+        assert_eq!(timer.tma, new_timer.tma);
+        assert_eq!(timer.tac, new_timer.tac);
+        assert_eq!(timer.div_clock, new_timer.div_clock);
+        assert_eq!(timer.tima_clock, new_timer.tima_clock);
+        assert_eq!(timer.tima_enabled, new_timer.tima_enabled);
+        assert_eq!(timer.tima_ratio, new_timer.tima_ratio);
+        assert_eq!(timer.int_tima, new_timer.int_tima);
+    }
+}
-- 
GitLab