diff --git a/frontends/sdl/src/main.rs b/frontends/sdl/src/main.rs
index 9cdc4888d9140e5bfed4dd4966c4e8cff73e63bc..0e4239265e96e25ea2d3a777bffd9a2909f5710e 100644
--- a/frontends/sdl/src/main.rs
+++ b/frontends/sdl/src/main.rs
@@ -6,10 +6,11 @@ pub mod graphics;
 
 use audio::Audio;
 use boytacean::{
-    devices::printer::PrinterDevice,
+    devices::{printer::PrinterDevice, stdout::StdoutDevice},
     gb::{AudioProvider, GameBoy, GameBoyMode},
     pad::PadKey,
     ppu::{PaletteInfo, PpuMode, DISPLAY_HEIGHT, DISPLAY_WIDTH},
+    serial::{NullDevice, SerialDevice},
 };
 use chrono::Utc;
 use clap::Parser;
@@ -461,6 +462,9 @@ struct Args {
     #[arg(short, long, default_value_t = String::from("cgb"))]
     mode: String,
 
+    #[arg(short, long, default_value_t = String::from("printer"))]
+    device: String,
+
     #[arg(short, long, default_value_t = String::from("../../res/roms.prop/tetris_dx.gbc"))]
     rom_path: String,
 }
@@ -469,24 +473,13 @@ fn main() {
     // parses the provided command line arguments and uses them to
     // obtain structured values
     let args = Args::parse();
-    let mode = GameBoyMode::from_str(&args.mode);
+    let mode: GameBoyMode = GameBoyMode::from_str(&args.mode);
 
     // creates a new Game Boy instance and loads both the boot ROM
     // and the initial game ROM to "start the engine"
     let mut game_boy = GameBoy::new(mode);
-    let mut printer = Box::<PrinterDevice>::default();
-    printer.set_callback(|image_buffer| {
-        let file_name = format!("printer-{}.png", Utc::now().format("%Y%m%d-%H%M%S"));
-        image::save_buffer(
-            Path::new(&file_name),
-            image_buffer,
-            160,
-            (image_buffer.len() / 4 / 160) as u32,
-            ColorType::Rgba8,
-        )
-        .unwrap();
-    });
-    game_boy.attach_serial(printer);
+    let device = build_device(&args.device);
+    game_boy.attach_serial(device);
     game_boy.load(true);
 
     // prints the current version of the emulator (informational message)
@@ -503,6 +496,29 @@ fn main() {
     emulator.run();
 }
 
+fn build_device(device: &str) -> Box<dyn SerialDevice> {
+    match device {
+        "null" => Box::<NullDevice>::default(),
+        "stdout" => Box::<StdoutDevice>::default(),
+        "printer" => {
+            let mut printer = Box::<PrinterDevice>::default();
+            printer.set_callback(|image_buffer| {
+                let file_name = format!("printer-{}.png", Utc::now().format("%Y%m%d-%H%M%S"));
+                image::save_buffer(
+                    Path::new(&file_name),
+                    image_buffer,
+                    160,
+                    (image_buffer.len() / 4 / 160) as u32,
+                    ColorType::Rgba8,
+                )
+                .unwrap();
+            });
+            printer
+        }
+        _ => panic!("Unsupported device: {}", device),
+    }
+}
+
 fn key_to_pad(keycode: Keycode) -> Option<PadKey> {
     match keycode {
         Keycode::Up => Some(PadKey::Up),
diff --git a/src/cpu.rs b/src/cpu.rs
index 2f765e4b60836098cbcbcaededc5ec56adee0b74..1fb00036410f7fe2bb16dc2ee2c9af1d75014577 100644
--- a/src/cpu.rs
+++ b/src/cpu.rs
@@ -340,16 +340,31 @@ impl Cpu {
         self.mmu().pad()
     }
 
+    #[inline(always)]
+    pub fn pad_i(&self) -> &Pad {
+        self.mmu_i().pad_i()
+    }
+
     #[inline(always)]
     pub fn timer(&mut self) -> &mut Timer {
         self.mmu().timer()
     }
 
+    #[inline(always)]
+    pub fn timer_i(&self) -> &Timer {
+        self.mmu_i().timer_i()
+    }
+
     #[inline(always)]
     pub fn serial(&mut self) -> &mut Serial {
         self.mmu().serial()
     }
 
+    #[inline(always)]
+    pub fn serial_i(&self) -> &Serial {
+        self.mmu_i().serial_i()
+    }
+
     #[inline(always)]
     pub fn halted(&self) -> bool {
         self.halted
diff --git a/src/devices/printer.rs b/src/devices/printer.rs
index 23c46b4e0c8b2d03d85826217f634289934d03f1..cf0161a8c90b319f39c6aa0fbe30e3210333b50d 100644
--- a/src/devices/printer.rs
+++ b/src/devices/printer.rs
@@ -312,6 +312,10 @@ impl SerialDevice for PrinterDevice {
     fn allow_slave(&self) -> bool {
         false
     }
+
+    fn description(&self) -> String {
+        format!("Printer [{}]", self.command)
+    }
 }
 
 impl Default for PrinterDevice {
diff --git a/src/devices/stdout.rs b/src/devices/stdout.rs
index 048a83f7a78e61df5245d8aa03cd04524d8c8ba0..cdaefb2c9096c5dbe4cc766929588114222f4e6b 100644
--- a/src/devices/stdout.rs
+++ b/src/devices/stdout.rs
@@ -1,4 +1,7 @@
-use std::io::{stdout, Write};
+use std::{
+    fmt::{self, Display, Formatter},
+    io::{stdout, Write},
+};
 
 use crate::serial::SerialDevice;
 
@@ -37,6 +40,10 @@ impl SerialDevice for StdoutDevice {
     fn allow_slave(&self) -> bool {
         false
     }
+
+    fn description(&self) -> String {
+        String::from("Stdout")
+    }
 }
 
 impl Default for StdoutDevice {
@@ -44,3 +51,9 @@ impl Default for StdoutDevice {
         Self::new(true)
     }
 }
+
+impl Display for StdoutDevice {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(f, "Stdout")
+    }
+}
diff --git a/src/gb.rs b/src/gb.rs
index 74b8dc271a50d8dffefb1464e8cb948652c2c06a..5088e6f1fc4e59fb72afef3483c7eeb9cb5adcd2 100644
--- a/src/gb.rs
+++ b/src/gb.rs
@@ -610,7 +610,7 @@ impl GameBoy {
 
     pub fn description(&self, column_length: usize) -> String {
         format!(
-            "{}  {}\n{}  {}\n{}  {}\n{}  {}",
+            "{}  {}\n{}  {}\n{}  {}\n{}  {}\n{}  {}",
             format!("{:width$}", "Version", width = column_length),
             VERSION,
             format!("{:width$}", "Mode", width = column_length),
@@ -619,6 +619,8 @@ impl GameBoy {
             self.ram_size(),
             format!("{:width$}", "VRAM Size", width = column_length),
             self.vram_size(),
+            format!("{:width$}", "Serial", width = column_length),
+            self.serial_i().device().description(),
         )
     }
 }
@@ -662,14 +664,26 @@ impl GameBoy {
         self.cpu.pad()
     }
 
+    pub fn pad_i(&self) -> &Pad {
+        self.cpu.pad_i()
+    }
+
     pub fn timer(&mut self) -> &mut Timer {
         self.cpu.timer()
     }
 
+    pub fn timer_i(&self) -> &Timer {
+        self.cpu.timer_i()
+    }
+
     pub fn serial(&mut self) -> &mut Serial {
         self.cpu.serial()
     }
 
+    pub fn serial_i(&self) -> &Serial {
+        self.cpu.serial_i()
+    }
+
     pub fn frame_buffer(&mut self) -> &[u8; FRAME_BUFFER_SIZE] {
         &(self.ppu().frame_buffer)
     }
diff --git a/src/mmu.rs b/src/mmu.rs
index 301d32988283477eda33813766feec825850199a..983dcbdd3c48242c169b0c35d8fc175e868be658 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -144,14 +144,26 @@ impl Mmu {
         &mut self.pad
     }
 
+    pub fn pad_i(&self) -> &Pad {
+        &self.pad
+    }
+
     pub fn timer(&mut self) -> &mut Timer {
         &mut self.timer
     }
 
+    pub fn timer_i(&self) -> &Timer {
+        &self.timer
+    }
+
     pub fn serial(&mut self) -> &mut Serial {
         &mut self.serial
     }
 
+    pub fn serial_i(&self) -> &Serial {
+        &self.serial
+    }
+
     pub fn boot_active(&self) -> bool {
         self.boot_active
     }
diff --git a/src/rom.rs b/src/rom.rs
index 8d18fab57e8852b83ab9a2c7db04313057ce604e..c9acccf5538fa5e1264da0fe17532f121218dd07 100644
--- a/src/rom.rs
+++ b/src/rom.rs
@@ -1,6 +1,5 @@
 use core::fmt;
 use std::{
-    any::Any,
     cmp::max,
     fmt::{Display, Formatter},
 };
diff --git a/src/serial.rs b/src/serial.rs
index dd6388a3c65daddda4800dabbb2ead2851184322..551a375b0500d966d61a789b3a3560ae3f7f3997 100644
--- a/src/serial.rs
+++ b/src/serial.rs
@@ -8,6 +8,9 @@ pub trait SerialDevice {
     /// simulating an external clock source. Or if instead the
     /// clock should always be generated by the running device.
     fn allow_slave(&self) -> bool;
+
+    /// Returns a short description of the serial device.
+    fn description(&self) -> String;
 }
 
 pub struct Serial {
@@ -202,6 +205,10 @@ impl SerialDevice for NullDevice {
     fn allow_slave(&self) -> bool {
         false
     }
+
+    fn description(&self) -> String {
+        String::from("Null")
+    }
 }
 
 impl Default for NullDevice {