diff --git a/crates/common/src/data.rs b/crates/common/src/data.rs
index 717c23818a7bdf9b872950d12eb01606875c6282..c7b03c6206e4cf8d008c0437671210e7b45d463a 100644
--- a/crates/common/src/data.rs
+++ b/crates/common/src/data.rs
@@ -11,18 +11,36 @@ pub fn write_u8<W: Write>(writer: &mut W, value: u8) -> Result<(), Error> {
     Ok(())
 }
 
+#[inline(always)]
+pub fn write_i8<W: Write>(writer: &mut W, value: i8) -> Result<(), Error> {
+    writer.write_all(&value.to_le_bytes())?;
+    Ok(())
+}
+
 #[inline(always)]
 pub fn write_u16<W: Write>(writer: &mut W, value: u16) -> Result<(), Error> {
     writer.write_all(&value.to_le_bytes())?;
     Ok(())
 }
 
+#[inline(always)]
+pub fn write_i16<W: Write>(writer: &mut W, value: i16) -> Result<(), Error> {
+    writer.write_all(&value.to_le_bytes())?;
+    Ok(())
+}
+
 #[inline(always)]
 pub fn write_u32<W: Write>(writer: &mut W, value: u32) -> Result<(), Error> {
     writer.write_all(&value.to_le_bytes())?;
     Ok(())
 }
 
+#[inline(always)]
+pub fn write_i32<W: Write>(writer: &mut W, value: i32) -> Result<(), Error> {
+    writer.write_all(&value.to_le_bytes())?;
+    Ok(())
+}
+
 #[inline(always)]
 pub fn read_u8<R: Read>(reader: &mut R) -> Result<u8, Error> {
     let mut buffer = [0x00; size_of::<u8>()];
@@ -30,6 +48,13 @@ pub fn read_u8<R: Read>(reader: &mut R) -> Result<u8, Error> {
     Ok(u8::from_le_bytes(buffer))
 }
 
+#[inline(always)]
+pub fn read_i8<R: Read>(reader: &mut R) -> Result<i8, Error> {
+    let mut buffer = [0x00; size_of::<i8>()];
+    reader.read_exact(&mut buffer)?;
+    Ok(i8::from_le_bytes(buffer))
+}
+
 #[inline(always)]
 pub fn read_u16<R: Read>(reader: &mut R) -> Result<u16, Error> {
     let mut buffer = [0x00; size_of::<u16>()];
@@ -37,6 +62,13 @@ pub fn read_u16<R: Read>(reader: &mut R) -> Result<u16, Error> {
     Ok(u16::from_le_bytes(buffer))
 }
 
+#[inline(always)]
+pub fn read_i16<R: Read>(reader: &mut R) -> Result<i16, Error> {
+    let mut buffer = [0x00; size_of::<i16>()];
+    reader.read_exact(&mut buffer)?;
+    Ok(i16::from_le_bytes(buffer))
+}
+
 #[inline(always)]
 pub fn read_u32<R: Read>(reader: &mut R) -> Result<u32, Error> {
     let mut buffer = [0x00; size_of::<u32>()];
@@ -44,6 +76,13 @@ pub fn read_u32<R: Read>(reader: &mut R) -> Result<u32, Error> {
     Ok(u32::from_le_bytes(buffer))
 }
 
+#[inline(always)]
+pub fn read_i32<R: Read>(reader: &mut R) -> Result<i32, Error> {
+    let mut buffer = [0x00; size_of::<i32>()];
+    reader.read_exact(&mut buffer)?;
+    Ok(i32::from_le_bytes(buffer))
+}
+
 #[inline(always)]
 pub fn read_bytes<R: Read>(reader: &mut R, count: usize) -> Result<Vec<u8>, Error> {
     let mut buffer = vec![0; count];
diff --git a/src/apu.rs b/src/apu.rs
index abd68c30dbba7c3f9f0eb321b1a43c96029beae1..eb125ffcc5dd8357d94725997b6e046d7c9194d4 100644
--- a/src/apu.rs
+++ b/src/apu.rs
@@ -1,6 +1,14 @@
 //! APU (Audio Processing Unit) functions and structures.
 
-use std::collections::VecDeque;
+use std::{
+    collections::VecDeque,
+    io::{Cursor, Read, Write},
+};
+
+use boytacean_common::{
+    data::{read_i16, read_i32, read_u16, read_u8, write_i16, write_i32, write_u16, write_u8},
+    error::Error,
+};
 
 use crate::{
     consts::{
@@ -10,6 +18,7 @@ use crate::{
     },
     gb::GameBoy,
     mmu::BusComponent,
+    state::StateComponent,
     warnln,
 };
 
@@ -1158,6 +1167,182 @@ impl BusComponent for Apu {
     }
 }
 
+impl StateComponent for Apu {
+    fn state(&self) -> Result<Vec<u8>, Error> {
+        let mut cursor = Cursor::new(vec![]);
+
+        write_i16(&mut cursor, self.ch1_timer)?;
+        write_u8(&mut cursor, self.ch1_sequence)?;
+        write_u8(&mut cursor, self.ch1_envelope_sequence)?;
+        write_u8(&mut cursor, self.ch1_envelope_enabled as u8)?;
+        write_u8(&mut cursor, self.ch1_sweep_sequence)?;
+        write_u8(&mut cursor, self.ch1_output)?;
+        write_u8(&mut cursor, self.ch1_dac as u8)?;
+        write_u8(&mut cursor, self.ch1_sweep_slope)?;
+        write_u8(&mut cursor, self.ch1_sweep_increase as u8)?;
+        write_u8(&mut cursor, self.ch1_sweep_pace)?;
+        write_u8(&mut cursor, self.ch1_length_timer)?;
+        write_u8(&mut cursor, self.ch1_wave_duty)?;
+        write_u8(&mut cursor, self.ch1_pace)?;
+        write_u8(&mut cursor, self.ch1_direction)?;
+        write_u8(&mut cursor, self.ch1_volume)?;
+        write_u16(&mut cursor, self.ch1_wave_length)?;
+        write_u8(&mut cursor, self.ch1_length_enabled as u8)?;
+        write_u8(&mut cursor, self.ch1_enabled as u8)?;
+
+        write_i16(&mut cursor, self.ch2_timer)?;
+        write_u8(&mut cursor, self.ch2_sequence)?;
+        write_u8(&mut cursor, self.ch2_envelope_sequence)?;
+        write_u8(&mut cursor, self.ch2_envelope_enabled as u8)?;
+        write_u8(&mut cursor, self.ch2_output)?;
+        write_u8(&mut cursor, self.ch2_dac as u8)?;
+        write_u8(&mut cursor, self.ch2_length_timer)?;
+        write_u8(&mut cursor, self.ch2_wave_duty)?;
+        write_u8(&mut cursor, self.ch2_pace)?;
+        write_u8(&mut cursor, self.ch2_direction)?;
+        write_u8(&mut cursor, self.ch2_volume)?;
+        write_u16(&mut cursor, self.ch2_wave_length)?;
+        write_u8(&mut cursor, self.ch2_length_enabled as u8)?;
+        write_u8(&mut cursor, self.ch2_enabled as u8)?;
+
+        write_i16(&mut cursor, self.ch3_timer)?;
+        write_u8(&mut cursor, self.ch3_position)?;
+        write_u8(&mut cursor, self.ch3_output)?;
+        write_u8(&mut cursor, self.ch3_dac as u8)?;
+        write_u16(&mut cursor, self.ch3_length_timer)?;
+        write_u8(&mut cursor, self.ch3_output_level)?;
+        write_u16(&mut cursor, self.ch3_wave_length)?;
+        write_u8(&mut cursor, self.ch3_length_enabled as u8)?;
+        write_u8(&mut cursor, self.ch3_enabled as u8)?;
+
+        write_i32(&mut cursor, self.ch4_timer)?;
+        write_u8(&mut cursor, self.ch4_envelope_sequence)?;
+        write_u8(&mut cursor, self.ch4_envelope_enabled as u8)?;
+        write_u8(&mut cursor, self.ch4_output)?;
+        write_u8(&mut cursor, self.ch4_dac as u8)?;
+        write_u8(&mut cursor, self.ch4_length_timer)?;
+        write_u8(&mut cursor, self.ch4_pace)?;
+        write_u8(&mut cursor, self.ch4_direction)?;
+        write_u8(&mut cursor, self.ch4_volume)?;
+        write_u8(&mut cursor, self.ch4_divisor)?;
+        write_u8(&mut cursor, self.ch4_width_mode as u8)?;
+        write_u8(&mut cursor, self.ch4_clock_shift)?;
+        write_u16(&mut cursor, self.ch4_lfsr)?;
+        write_u8(&mut cursor, self.ch4_length_enabled as u8)?;
+        write_u8(&mut cursor, self.ch4_enabled as u8)?;
+
+        write_u8(&mut cursor, self.master)?;
+        write_u8(&mut cursor, self.glob_panning)?;
+
+        write_u8(&mut cursor, self.right_enabled as u8)?;
+        write_u8(&mut cursor, self.left_enabled as u8)?;
+        write_u8(&mut cursor, self.sound_enabled as u8)?;
+
+        write_u8(&mut cursor, self.ch1_out_enabled as u8)?;
+        write_u8(&mut cursor, self.ch2_out_enabled as u8)?;
+        write_u8(&mut cursor, self.ch3_out_enabled as u8)?;
+        write_u8(&mut cursor, self.ch4_out_enabled as u8)?;
+
+        cursor.write(&self.wave_ram)?;
+
+        write_u16(&mut cursor, self.sampling_rate)?;
+        write_u8(&mut cursor, self.channels)?;
+
+        write_u16(&mut cursor, self.sequencer)?;
+        write_u8(&mut cursor, self.sequencer_step)?;
+        write_i16(&mut cursor, self.output_timer)?;
+
+        Ok(cursor.into_inner())
+    }
+
+    fn set_state(&mut self, data: &[u8]) -> Result<(), Error> {
+        let mut cursor = Cursor::new(data);
+
+        self.ch1_timer = read_i16(&mut cursor)?;
+        self.ch1_sequence = read_u8(&mut cursor)?;
+        self.ch1_envelope_sequence = read_u8(&mut cursor)?;
+        self.ch1_envelope_enabled = read_u8(&mut cursor)? != 0;
+        self.ch1_sweep_sequence = read_u8(&mut cursor)?;
+        self.ch1_output = read_u8(&mut cursor)?;
+        self.ch1_dac = read_u8(&mut cursor)? != 0;
+        self.ch1_sweep_slope = read_u8(&mut cursor)?;
+        self.ch1_sweep_increase = read_u8(&mut cursor)? != 0;
+        self.ch1_sweep_pace = read_u8(&mut cursor)?;
+        self.ch1_length_timer = read_u8(&mut cursor)?;
+        self.ch1_wave_duty = read_u8(&mut cursor)?;
+        self.ch1_pace = read_u8(&mut cursor)?;
+        self.ch1_direction = read_u8(&mut cursor)?;
+        self.ch1_volume = read_u8(&mut cursor)?;
+        self.ch1_wave_length = read_u16(&mut cursor)?;
+        self.ch1_length_enabled = read_u8(&mut cursor)? != 0;
+        self.ch1_enabled = read_u8(&mut cursor)? != 0;
+
+        self.ch2_timer = read_i16(&mut cursor)?;
+        self.ch2_sequence = read_u8(&mut cursor)?;
+        self.ch2_envelope_sequence = read_u8(&mut cursor)?;
+        self.ch2_envelope_enabled = read_u8(&mut cursor)? != 0;
+        self.ch2_output = read_u8(&mut cursor)?;
+        self.ch2_dac = read_u8(&mut cursor)? != 0;
+        self.ch2_length_timer = read_u8(&mut cursor)?;
+        self.ch2_wave_duty = read_u8(&mut cursor)?;
+        self.ch2_pace = read_u8(&mut cursor)?;
+        self.ch2_direction = read_u8(&mut cursor)?;
+        self.ch2_volume = read_u8(&mut cursor)?;
+        self.ch2_wave_length = read_u16(&mut cursor)?;
+        self.ch2_length_enabled = read_u8(&mut cursor)? != 0;
+        self.ch2_enabled = read_u8(&mut cursor)? != 0;
+
+        self.ch3_timer = read_i16(&mut cursor)?;
+        self.ch3_position = read_u8(&mut cursor)?;
+        self.ch3_output = read_u8(&mut cursor)?;
+        self.ch3_dac = read_u8(&mut cursor)? != 0;
+        self.ch3_length_timer = read_u16(&mut cursor)?;
+        self.ch3_output_level = read_u8(&mut cursor)?;
+        self.ch3_wave_length = read_u16(&mut cursor)?;
+        self.ch3_length_enabled = read_u8(&mut cursor)? != 0;
+        self.ch3_enabled = read_u8(&mut cursor)? != 0;
+
+        self.ch4_timer = read_i32(&mut cursor)?;
+        self.ch4_envelope_sequence = read_u8(&mut cursor)?;
+        self.ch4_envelope_enabled = read_u8(&mut cursor)? != 0;
+        self.ch4_output = read_u8(&mut cursor)?;
+        self.ch4_dac = read_u8(&mut cursor)? != 0;
+        self.ch4_length_timer = read_u8(&mut cursor)?;
+        self.ch4_pace = read_u8(&mut cursor)?;
+        self.ch4_direction = read_u8(&mut cursor)?;
+        self.ch4_volume = read_u8(&mut cursor)?;
+        self.ch4_divisor = read_u8(&mut cursor)?;
+        self.ch4_width_mode = read_u8(&mut cursor)? != 0;
+        self.ch4_clock_shift = read_u8(&mut cursor)?;
+        self.ch4_lfsr = read_u16(&mut cursor)?;
+        self.ch4_length_enabled = read_u8(&mut cursor)? != 0;
+        self.ch4_enabled = read_u8(&mut cursor)? != 0;
+
+        self.master = read_u8(&mut cursor)?;
+        self.glob_panning = read_u8(&mut cursor)?;
+
+        self.right_enabled = read_u8(&mut cursor)? != 0;
+        self.left_enabled = read_u8(&mut cursor)? != 0;
+        self.sound_enabled = read_u8(&mut cursor)? != 0;
+
+        self.ch1_out_enabled = read_u8(&mut cursor)? != 0;
+        self.ch2_out_enabled = read_u8(&mut cursor)? != 0;
+        self.ch3_out_enabled = read_u8(&mut cursor)? != 0;
+        self.ch4_out_enabled = read_u8(&mut cursor)? != 0;
+
+        cursor.read_exact(&mut self.wave_ram)?;
+
+        self.sampling_rate = read_u16(&mut cursor)?;
+        self.channels = read_u8(&mut cursor)?;
+
+        self.sequencer = read_u16(&mut cursor)?;
+        self.sequencer_step = read_u8(&mut cursor)?;
+        self.output_timer = read_i16(&mut cursor)?;
+
+        Ok(())
+    }
+}
+
 impl Default for Apu {
     fn default() -> Self {
         Self::new(44100, 2, 1.0, GameBoy::CPU_FREQ)
diff --git a/src/state.rs b/src/state.rs
index 50472ed7dc7447c0f8dc3462c86f0e966ae322df..9f87eec8373378b6e4a4db81c099d3928f89a37e 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -514,6 +514,7 @@ impl StateBox for BosState {
                 None
             },
             device_states: vec![
+                BosDeviceState::from_gb(gb, GameBoyDevice::Apu)?,
                 BosDeviceState::from_gb(gb, GameBoyDevice::Dma)?,
                 BosDeviceState::from_gb(gb, GameBoyDevice::Timer)?,
             ],
@@ -791,6 +792,7 @@ impl BosDeviceState {
 
     fn from_gb(gb: &mut GameBoy, device: GameBoyDevice) -> Result<Self, Error> {
         match device {
+            GameBoyDevice::Apu => Ok(Self::new(device, gb.apu_i().state()?)),
             GameBoyDevice::Dma => Ok(Self::new(device, gb.dma_i().state()?)),
             GameBoyDevice::Timer => Ok(Self::new(device, gb.timer_i().state()?)),
             _ => Err(Error::NotImplemented),
@@ -799,6 +801,7 @@ impl BosDeviceState {
 
     fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error> {
         match self.device {
+            GameBoyDevice::Apu => gb.apu().set_state(&self.state)?,
             GameBoyDevice::Dma => gb.dma().set_state(&self.state)?,
             GameBoyDevice::Timer => gb.timer().set_state(&self.state)?,
             _ => return Err(Error::NotImplemented),