diff --git a/src/gb.rs b/src/gb.rs
index d418355db8d252734246865a7964d4cec5f6a9e0..26675bbf1950c1a92d813f5e6d2a61055aea769e 100644
--- a/src/gb.rs
+++ b/src/gb.rs
@@ -12,6 +12,7 @@ use crate::{
     devices::{printer::PrinterDevice, stdout::StdoutDevice},
     dma::Dma,
     gen::{COMPILATION_DATE, COMPILATION_TIME, COMPILER, COMPILER_VERSION, VERSION},
+    genie::GameGenie,
     mmu::Mmu,
     pad::{Pad, PadKey},
     ppu::{Ppu, PpuMode, Tile, DISPLAY_HEIGHT, DISPLAY_WIDTH, FRAME_BUFFER_SIZE},
@@ -970,8 +971,10 @@ impl GameBoy {
 
     pub fn load_rom(&mut self, data: &[u8]) -> &Cartridge {
         let mut rom = Cartridge::from_data(data);
-        rom.add_genie_code("00A-17B-C49").unwrap(); // SML
-        rom.add_genie_code("008-60A-E6E").unwrap(); // SML
+        let mut game_genie = GameGenie::default();
+        game_genie.add_code("00A-17B-C49").unwrap(); // SML
+        game_genie.add_code("008-60A-E6E").unwrap(); // SML
+        rom.attach_genie(game_genie);
         self.mmu().set_rom(rom);
         self.mmu().rom()
     }
diff --git a/src/genie.rs b/src/genie.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c28178e08505663dcfe7c3f73f732dc2d8fd34c8
--- /dev/null
+++ b/src/genie.rs
@@ -0,0 +1,92 @@
+use std::{
+    collections::HashMap,
+    fmt::{Display, Formatter, self},
+};
+
+#[derive(Clone)]
+pub struct GameGenie {
+    /// Hash map that contains the complete set of Game Genie
+    /// codes that have been registered for the current ROM.
+    /// These codes are going to apply a series of patches to
+    /// the ROM effectively allowing the user to cheat.
+    codes: HashMap<u16, GameGenieCode>,
+}
+
+impl GameGenie {
+    pub fn new() -> Self {
+        Self {
+            codes: HashMap::new(),
+        }
+    }
+
+    pub fn contains_addr(&self, addr: u16) -> bool {
+        self.codes.contains_key(&addr)
+    }
+
+    pub fn get_addr(&self, addr: u16) -> &GameGenieCode {
+        self.codes.get(&addr).unwrap()
+    }
+
+    pub fn add_code(&mut self, code: &str) -> Result<&GameGenieCode, &str> {
+        if code.len() != 11 {
+            return Err("Invalid Game Genie code length");
+        }
+
+        let code_u = code.to_uppercase();
+
+        let new_data_slice = &code_u[0..=1];
+        let new_data = u8::from_str_radix(new_data_slice, 16).unwrap();
+
+        let old_data_slice = format!("{}{}", &code_u[8..=8], &code_u[10..=10]);
+        let old_data: u8 = u8::from_str_radix(old_data_slice.as_str(), 16)
+            .unwrap()
+            .rotate_right(2)
+            ^ 0xba;
+
+        let addr_slice = format!("{}{}{}", &code_u[6..=6], &code_u[2..=2], &code_u[4..=5]);
+        let addr = u16::from_str_radix(addr_slice.as_str(), 16).unwrap() ^ 0xf000;
+
+        let genie_code = GameGenieCode {
+            code: code_u,
+            addr,
+            new_data,
+            old_data,
+        };
+
+        self.codes.insert(addr, genie_code);
+        Ok(self.codes.get(&addr).unwrap())
+    }
+}
+
+impl Default for GameGenie {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[derive(Clone)]
+pub struct GameGenieCode {
+    pub code: String,
+    pub addr: u16,
+    pub new_data: u8,
+    pub old_data: u8,
+}
+
+impl GameGenieCode {
+    pub fn short_description(&self) -> String {
+        self.code.to_string()
+    }
+
+    pub fn description(&self) -> String {
+        format!(
+            "Code: {}, Address: 0x{:04x}, New Data: 0x{:04x}, Old Data: 0x{:04x}",
+            self.code, self.addr, self.new_data, self.old_data
+        )
+    }
+}
+
+impl Display for GameGenieCode {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(f, "{}", self.short_description())
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 0f8c9a49a05ec691e4c9fcacd0bc53de1e681123..f03d7b489cabbf079aa908f4e2d3db81a4945395 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,6 +7,7 @@ pub mod devices;
 pub mod dma;
 pub mod gb;
 pub mod gen;
+pub mod genie;
 pub mod inst;
 pub mod macros;
 pub mod mmu;
diff --git a/src/rom.rs b/src/rom.rs
index 8e42666ef8a3216eb2bd34fb91c051a3eba325c4..4a7724b70f05eadae4ef9d4c15f20016eaa168b1 100644
--- a/src/rom.rs
+++ b/src/rom.rs
@@ -1,11 +1,10 @@
 use core::fmt;
 use std::{
     cmp::max,
-    collections::HashMap,
     fmt::{Display, Formatter},
 };
 
-use crate::{debugln, gb::GameBoyMode, util::read_file, warnln};
+use crate::{debugln, gb::GameBoyMode, genie::GameGenie, util::read_file, warnln};
 
 #[cfg(feature = "wasm")]
 use wasm_bindgen::prelude::*;
@@ -255,6 +254,13 @@ pub struct Cartridge {
     /// RAM and ROM access on the current cartridge.
     mbc: &'static Mbc,
 
+    /// The current memory handler in charge of handling the
+    /// memory access for the current cartridge.
+    /// Typically this is the same as the MBC, but to allow
+    /// memory patching (ex: Game Genie) we may need another
+    /// level of indirection.
+    handler: &'static Mbc,
+
     /// The number of ROM banks (of 8KB) that are available
     /// to the current cartridge, this is a computed value
     /// to allow improved performance.
@@ -277,16 +283,15 @@ pub struct Cartridge {
     /// control of memory access to avoid corruption.
     ram_enabled: bool,
 
-    // The final offset of the last character of the title
-    // that is considered to be non zero (0x0) so that a
-    // proper safe conversion to UTF-8 string can be done.
+    /// The final offset of the last character of the title
+    /// that is considered to be non zero (0x0) so that a
+    /// proper safe conversion to UTF-8 string can be done.
     title_offset: usize,
 
-    /// Hash map that contains the complete set of Game Genie
-    /// codes that have been registered for the current ROM.
-    /// These codes are going to apply a series of patches to
-    /// the ROM effectively allowing the user to cheat.
-    genie_codes: HashMap<u16, GameGenieCode>,
+    /// Optional reference to the Game Genie instance that
+    /// would be used for the "cheating" by patching the
+    /// current ROM's cartridge data.
+    game_genie: Option<GameGenie>,
 }
 
 impl Cartridge {
@@ -295,13 +300,14 @@ impl Cartridge {
             rom_data: vec![],
             ram_data: vec![],
             mbc: &NO_MBC,
+            handler: &NO_MBC,
             rom_bank_count: 0,
             ram_bank_count: 0,
             rom_offset: 0x4000,
             ram_offset: 0x0000,
             ram_enabled: false,
             title_offset: 0x0143,
-            genie_codes: HashMap::new(),
+            game_genie: None,
         }
     }
 
@@ -317,24 +323,11 @@ impl Cartridge {
     }
 
     pub fn read(&self, addr: u16) -> u8 {
-        if self.genie_codes.contains_key(&addr) {
-            let genie_code = self.genie_codes.get(&addr).unwrap();
-
-            // checks if the current data at the address is the same as the
-            // one that is expected by the Game Genie code, if that's the case
-            // applies the patch, otherwise returns the original strategy is
-            // going to be used
-            if genie_code.old_data == (self.mbc.read_rom)(self, addr) {
-                debugln!("Applying Game Genie code: {}", game_genie_code);
-                return genie_code.new_data;
-            }
-        }
-
         match addr & 0xf000 {
             0x0000 | 0x1000 | 0x2000 | 0x3000 | 0x4000 | 0x5000 | 0x6000 | 0x7000 => {
-                (self.mbc.read_rom)(self, addr)
+                (self.handler.read_rom)(self, addr)
             }
-            0xa000 | 0xb000 => (self.mbc.read_ram)(self, addr),
+            0xa000 | 0xb000 => (self.handler.read_ram)(self, addr),
             _ => {
                 debugln!("Reading from unknown Cartridge control 0x{:04x}", addr);
                 0x00
@@ -345,9 +338,9 @@ impl Cartridge {
     pub fn write(&mut self, addr: u16, value: u8) {
         match addr & 0xf000 {
             0x0000 | 0x1000 | 0x2000 | 0x3000 | 0x4000 | 0x5000 | 0x6000 | 0x7000 => {
-                (self.mbc.write_rom)(self, addr, value)
+                (self.handler.write_rom)(self, addr, value)
             }
-            0xa000 | 0xb000 => (self.mbc.write_ram)(self, addr, value),
+            0xa000 | 0xb000 => (self.handler.write_ram)(self, addr, value),
             _ => debugln!("Writing to unknown Cartridge address 0x{:04x}", addr),
         }
     }
@@ -405,6 +398,7 @@ impl Cartridge {
 
     fn set_mbc(&mut self) {
         self.mbc = self.get_mbc();
+        self.handler = self.mbc;
     }
 
     fn set_computed(&mut self) {
@@ -437,36 +431,6 @@ impl Cartridge {
         let ram_banks = max(self.ram_size().ram_banks(), 1);
         self.ram_data = vec![0u8; ram_banks as usize * RAM_BANK_SIZE];
     }
-
-    pub fn add_genie_code(&mut self, code: &str) -> Result<&GameGenieCode, &str> {
-        if code.len() != 11 {
-            return Err("Invalid Game Genie code length");
-        }
-
-        let code_u = code.to_uppercase();
-
-        let new_data_slice = &code_u[0..=1];
-        let new_data = u8::from_str_radix(new_data_slice, 16).unwrap();
-
-        let old_data_slice = format!("{}{}", &code_u[8..=8], &code_u[10..=10]);
-        let old_data: u8 = u8::from_str_radix(old_data_slice.as_str(), 16)
-            .unwrap()
-            .rotate_right(2)
-            ^ 0xba;
-
-        let address_slice = format!("{}{}{}", &code_u[6..=6], &code_u[2..=2], &code_u[4..=5]);
-        let address = u16::from_str_radix(address_slice.as_str(), 16).unwrap() ^ 0xf000;
-
-        let genie_code = GameGenieCode {
-            code: code_u,
-            address,
-            new_data,
-            old_data,
-        };
-
-        self.genie_codes.insert(address, genie_code);
-        Ok(self.genie_codes.get(&address).unwrap())
-    }
 }
 
 #[cfg_attr(feature = "wasm", wasm_bindgen)]
@@ -600,6 +564,16 @@ impl Cartridge {
         self.ram_data = ram_data;
     }
 
+    pub fn attach_genie(&mut self, game_genie: GameGenie) {
+        self.game_genie = Some(game_genie);
+        self.handler = &GAME_GENIE;
+    }
+
+    pub fn detach_genie(&mut self) {
+        self.game_genie = None;
+        self.handler = self.mbc;
+    }
+
     pub fn description(&self, column_length: usize) -> String {
         let name_l = format!("{:width$}", "Name", width = column_length);
         let type_l = format!("{:width$}", "Type", width = column_length);
@@ -829,3 +803,26 @@ pub static MBC5: Mbc = Mbc {
         rom.ram_data[rom.ram_offset + (addr - 0xa000) as usize] = value;
     },
 };
+
+pub static GAME_GENIE: Mbc = Mbc {
+    name: "GameGenie",
+    read_rom: |rom: &Cartridge, addr: u16| -> u8 {
+        let game_genie = rom.game_genie.as_ref().unwrap();
+        if game_genie.contains_addr(addr) {
+            let genie_code = game_genie.get_addr(addr);
+
+            // checks if the current data at the address is the same as the
+            // one that is expected by the Game Genie code, if that's the case
+            // applies the patch, otherwise returns the original strategy is
+            // going to be used
+            if genie_code.old_data == (rom.mbc.read_rom)(rom, addr) {
+                debugln!("Applying Game Genie code: {}", game_genie_code);
+                return genie_code.new_data;
+            }
+        }
+        (rom.mbc.read_rom)(rom, addr)
+    },
+    write_rom: |rom: &mut Cartridge, addr: u16, value: u8| (rom.mbc.write_rom)(rom, addr, value),
+    read_ram: |rom: &Cartridge, addr: u16| -> u8 { (rom.mbc.read_ram)(rom, addr) },
+    write_ram: |rom: &mut Cartridge, addr: u16, value: u8| (rom.mbc.write_ram)(rom, addr, value),
+};