diff --git a/examples/sdl/src/main.rs b/examples/sdl/src/main.rs
index f72bf2aa808ef50cc397c68fd013715008f6cd8a..2e236739c81b0488159c8132e66c0d5408420ba3 100644
--- a/examples/sdl/src/main.rs
+++ b/examples/sdl/src/main.rs
@@ -84,7 +84,8 @@ fn main() {
     let mut game_boy = GameBoy::new();
     game_boy.load_boot_default();
 
-    game_boy.load_rom_file("../../res/roms.prop/tetris.gb");
+    //game_boy.load_rom_file("../../res/roms.prop/tetris.gb");
+    game_boy.load_rom_file("../../res/roms.prop/dr_mario.gb");
     //game_boy.load_rom_file("../../res/roms.prop/alleyway.gb");
 
     //game_boy.load_rom_file("../../res/roms/firstwhite.gb");
diff --git a/src/cpu.rs b/src/cpu.rs
index 34d659d3669bb8a8de51ca49f4ff76267d6c3742..9d538684346b700d201f9e220673d215ea31d877 100644
--- a/src/cpu.rs
+++ b/src/cpu.rs
@@ -4,7 +4,8 @@ use crate::{
     inst::{EXTENDED, INSTRUCTIONS},
     mmu::Mmu,
     pad::Pad,
-    ppu::Ppu, timer::Timer,
+    ppu::Ppu,
+    timer::Timer,
 };
 
 pub const PREFIX: u8 = 0xcb;
@@ -129,6 +130,27 @@ impl Cpu {
 
                 return 16;
             }
+
+            // @todo aggregate the handling of these interrupts
+            if (self.mmu.ie & 0x04 == 0x04) && self.mmu.timer().int_tima() {
+                println!("Going to run Timer interrupt handler (0x50)");
+
+                self.disable_int();
+                self.push_word(pc);
+                self.pc = 0x50;
+
+                // acknowledges that the timer interrupt has been
+                // properly handled
+                self.mmu.timer().ack_tima();
+
+                // in case the CPU is currently halted waiting
+                // for an interrupt, releases it
+                if self.halted {
+                    self.halted = false;
+                }
+
+                return 16;
+            }
         }
 
         // in case the CPU is currently in the halted state
@@ -191,7 +213,7 @@ impl Cpu {
     pub fn pad(&mut self) -> &mut Pad {
         self.mmu().pad()
     }
-    
+
     #[inline(always)]
     pub fn timer(&mut self) -> &mut Timer {
         self.mmu().timer()
diff --git a/src/data.rs b/src/data.rs
index c9330da6e7c417932c73e6ade7f54d794db89d5f..c77a3e8e5cc6a85a32c0a09832a9d8e89e14cd2e 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -9,8 +9,6 @@ pub enum BootRom {
     MgbBootix,
 }
 
-/// Static data corresponding to the DMG boot ROM
-/// allows freely using the emulator without external dependency.
 pub const DMG_BOOT: [u8; 256] = [
     49, 254, 255, 175, 33, 255, 159, 50, 203, 124, 32, 251, 33, 38, 255, 14, 17, 62, 128, 50, 226,
     12, 62, 243, 226, 50, 62, 119, 119, 62, 252, 224, 71, 17, 4, 1, 33, 16, 128, 26, 205, 149, 0,
diff --git a/src/gb.rs b/src/gb.rs
index f9a12c84f9cd375222890984bb1b1a20352cf5f3..acfd5d9825dea000a104bef7fed1ce42213fd263 100644
--- a/src/gb.rs
+++ b/src/gb.rs
@@ -4,7 +4,8 @@ use crate::{
     mmu::Mmu,
     pad::{Pad, PadKey},
     ppu::{Ppu, Tile, FRAME_BUFFER_SIZE},
-    util::read_file, timer::Timer,
+    timer::Timer,
+    util::read_file,
 };
 
 #[cfg(feature = "wasm")]
@@ -51,6 +52,7 @@ impl GameBoy {
     pub fn clock(&mut self) -> u8 {
         let cycles = self.cpu_clock();
         self.ppu_clock(cycles);
+        self.timer_clock(cycles);
         cycles
     }
 
@@ -62,6 +64,10 @@ impl GameBoy {
         self.ppu().clock(cycles)
     }
 
+    pub fn timer_clock(&mut self, cycles: u8) {
+        self.timer().clock(cycles)
+    }
+
     pub fn load_rom(&mut self, data: &[u8]) {
         self.cpu.mmu().write_rom(0x0000, data);
     }
diff --git a/src/mmu.rs b/src/mmu.rs
index ed98c3ec0efc4dec3e39c25c5d9c8d47efb0b727..e3dae1ddcfd9fe68afa9179e48b6ca229dcc045e 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -96,28 +96,30 @@ impl Mmu {
                 0x000 | 0x100 | 0x200 | 0x300 | 0x400 | 0x500 | 0x600 | 0x700 | 0x800 | 0x900
                 | 0xa00 | 0xb00 | 0xc00 | 0xd00 => self.ram[(addr & 0x1fff) as usize],
                 0xe00 => self.ppu.oam[(addr & 0x009f) as usize],
-                0xf00 => {
-                    if addr == 0xffff {
-                        self.ie
-                    } else if addr >= 0xff80 {
-                        self.ppu.hram[(addr & 0x007f) as usize]
-                    } else {
-                        match addr & 0x00f0 {
-                            0x00 => match addr & 0x00ff {
-                                0x00 => self.pad.read(addr),
-                                _ => {
-                                    println!("Reading from unknown IO control 0x{:04x}", addr);
-                                    0x00
-                                }
-                            },
-                            0x40 | 0x50 | 0x60 | 0x70 => self.ppu.read(addr),
+                0xf00 => match addr & 0x00ff {
+                    0x0f => {
+                        let value = if self.ppu.int_vblank() { 0x01 } else { 0x00 }
+                            | if self.timer.int_tima() { 0x04 } else { 0x00 };
+                        value
+                    }
+                    0x80..=0xfe => self.ppu.hram[(addr & 0x007f) as usize],
+                    0xff => self.ie,
+                    _ => match addr & 0x00f0 {
+                        0x00 => match addr & 0x00ff {
+                            0x00 => self.pad.read(addr),
+                            0x04..=0x07 => self.timer.read(addr),
                             _ => {
                                 println!("Reading from unknown IO control 0x{:04x}", addr);
                                 0x00
                             }
+                        },
+                        0x40 | 0x50 | 0x60 | 0x70 => self.ppu.read(addr),
+                        _ => {
+                            println!("Reading from unknown IO control 0x{:04x}", addr);
+                            0x00
                         }
-                    }
-                }
+                    },
+                },
                 addr => panic!("Reading from unknown location 0x{:04x}", addr),
             },
             addr => panic!("Reading from unknown location 0x{:04x}", addr),
@@ -168,15 +170,18 @@ impl Mmu {
                     self.ppu.oam[(addr & 0x009f) as usize] = value;
                     self.ppu.update_object(addr, value);
                 }
-                0xf00 => {
-                    if addr == 0xffff {
-                        self.ie = value;
-                    } else if addr >= 0xff80 {
-                        self.ppu.hram[(addr & 0x007f) as usize] = value;
-                    } else {
+                0xf00 => match addr & 0x00ff {
+                    0x0f => {
+                        self.ppu.set_int_vblank(value & 0x01 == 0x01);
+                        self.timer.set_int_tima(value & 0x04 == 0x04);
+                    }
+                    0x80..=0xfe => self.ppu.hram[(addr & 0x007f) as usize] = value,
+                    0xff => self.ie = value,
+                    _ => {
                         match addr & 0x00f0 {
                             0x00 => match addr & 0x00ff {
                                 0x00 => self.pad.write(addr, value),
+                                0x04..=0x07 => self.timer.write(addr, value),
                                 _ => println!("Writing to unknown IO control 0x{:04x}", addr),
                             },
                             0x40 | 0x60 | 0x70 => {
@@ -198,7 +203,7 @@ impl Mmu {
                             _ => println!("Writing to unknown IO control 0x{:04x}", addr),
                         }
                     }
-                }
+                },
                 addr => panic!("Writing in unknown location 0x{:04x}", addr),
             },
             addr => panic!("Writing in unknown location 0x{:04x}", addr),
diff --git a/src/ppu.rs b/src/ppu.rs
index 66a5c397a780fd1fdcadd01936fc4e7f29d27b40..c2073b2fde7864f4103b46f4840f55cf6669964f 100644
--- a/src/ppu.rs
+++ b/src/ppu.rs
@@ -498,6 +498,10 @@ impl Ppu {
         self.int_vblank
     }
 
+    pub fn set_int_vblank(&mut self, value: bool) {
+        self.int_vblank = value;
+    }
+
     pub fn ack_vblank(&mut self) {
         self.int_vblank = false;
     }
@@ -705,10 +709,10 @@ impl Ppu {
                     // the object is only considered visible if it's a priority
                     // or if the underlying pixel is transparent (zero value)
                     let is_visible = obj.priority || self.color_buffer[color_offset] == 0;
-                    if is_visible {
+                    let pixel = tile_row[if obj.xflip { 7 - x } else { x }];
+                    if is_visible && pixel != 0 {
                         // obtains the current pixel data from the tile row and
                         // re-maps it according to the object palette
-                        let pixel = tile_row[if obj.xflip { 7 - x } else { x }];
                         let color = palette[pixel as usize];
 
                         // sets the color pixel in the frame buffer
diff --git a/src/timer.rs b/src/timer.rs
index 0302f9be229362cfbb4156f9d09c8190a8c398e8..0169e9e8ceec2700463b66134338174179ef1c15 100644
--- a/src/timer.rs
+++ b/src/timer.rs
@@ -3,7 +3,11 @@ pub struct Timer {
     tima: u8,
     tma: u8,
     tac: u8,
-    ratio: u16,
+    div_clock: u16,
+    tima_clock: u16,
+    tima_enabled: bool,
+    tima_ratio: u16,
+    int_tima: bool,
 }
 
 impl Timer {
@@ -13,15 +17,73 @@ impl Timer {
             tima: 0,
             tma: 0,
             tac: 0x0,
-            ratio: 1024
+            div_clock: 0,
+            tima_clock: 0,
+            tima_enabled: false,
+            tima_ratio: 1024,
+            int_tima: false,
+        }
+    }
+
+    pub fn clock(&mut self, cycles: u8) {
+        self.div_clock += cycles as u16;
+        self.tima_clock += cycles as u16;
+
+        if self.div_clock >= 256 {
+            self.div = self.div.wrapping_add(1);
+            self.div_clock = self.div_clock - 256;
+        }
+
+        if self.tima_enabled && self.tima_clock >= self.tima_ratio {
+            if self.tima == 0xff {
+                self.int_tima = true;
+                self.tima = self.tma;
+            }
+
+            self.tima = self.tima.wrapping_add(1);
+            self.tima_clock = self.tima_clock - self.tima_ratio;
         }
     }
 
     pub fn read(&mut self, addr: u16) -> u8 {
-        0x00
+        match addr & 0x00ff {
+            0x04 => self.div,
+            0x05 => self.tima,
+            0x06 => self.tma,
+            0x07 => self.tac,
+            addr => panic!("Reding from unknown Timer location 0x{:04x}", addr),
+        }
     }
 
     pub fn write(&mut self, addr: u16, value: u8) {
+        match addr & 0x00ff {
+            0x04 => self.div = 0,
+            0x05 => self.tima = value,
+            0x06 => self.tma = value,
+            0x07 => {
+                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!("Invalid TAC value 0x{:02x}", value),
+                }
+                self.tima_enabled = value & 0x04 == 0x04;
+            }
+            addr => panic!("Writing to unknown Timer location 0x{:04x}", addr),
+        }
+    }
+
+    pub fn int_tima(&self) -> bool {
+        self.int_tima
     }
-}
 
+    pub fn set_int_tima(&mut self, value: bool) {
+        self.int_tima = value;
+    }
+
+    pub fn ack_tima(&mut self) {
+        self.int_tima = false;
+    }
+}