diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f7a5aed4f1cc537bcaa094886a3f522b7102f47..4b0a3186241ed2f065358cd2f15fe17d5bd7002b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,37 +19,48 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 *
 
-## [0.9.12] - 2023-06-20
+## [0.9.13] - 2023-08-01
+
+### Changed
+
+* Improved command line parsing with positional ROM path value
+* Better CI/CD for releases
+
+### Fixed
+
+* Small issue with command line arguments
+
+## [0.9.12] - 2023-08-01
 
 ### Added
 
 * New WASM build
 
-## [0.9.11] - 2023-06-20
+## [0.9.11] - 2023-08-01
 
 ### Fixed
 
 * Build of a new release
 
-## [0.9.10] - 2023-06-20
+## [0.9.10] - 2023-08-01
 
 ### Fixed
 
 * Issue with release life-cycle
 
-## [0.9.9] - 2023-06-20
+## [0.9.9] - 2023-08-01
 
 ### Fixed
 
 * Issue with release life-cycle
 
-## [0.9.8] - 2023-06-20
+## [0.9.8] - 2023-08-01
 
 ### Added
 
 * Better release life-cycle
 
-## [0.9.7] - 2023-06-20
+## [0.9.7] - 2023-08-01
 
 ### Added
 
diff --git a/Cargo.toml b/Cargo.toml
index c0bfcc629f5d29b176f3fdc656b135badde546e8..c1119a157a0d5c48bd281ef09d7e10d980f1585a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "boytacean"
 description = "A Game Boy emulator that is written in Rust."
-version = "0.9.12"
+version = "0.9.13"
 authors = ["João Magalhães <joamag@gmail.com>"]
 license = "Apache-2.0"
 repository = "https://github.com/joamag/boytacean"
diff --git a/README.md b/README.md
index 7cc71d37fc85ec4eb49aeffeafffb23cdfacb42a..7b8c7221724bfc06b16551753d6f4ccd8e7626c3 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,7 @@ A Game Boy emulator that is written in Rust 🦀.
 * Serial Data Transfer ([Link Cable](https://en.wikipedia.org/wiki/Game_Link_Cable)) support
 * Game Boy Printer emulation
 * Support for multiple MBCs: MBC1, MBC2, MBC3, and MBC5
+* Cheat support using [Game Genie](https://en.wikipedia.org/wiki/Game_Genie) codes
 * Variable CPU clock speed
 * Accurate PPU - passes [dmg-acid2](https://github.com/mattcurrie/dmg-acid2) and [cgb-acid2](https://github.com/mattcurrie/cgb-acid2) tests
 
diff --git a/frontends/libretro/Cargo.toml b/frontends/libretro/Cargo.toml
index 859dff1f1dfe4f0d31da9cb9109938b7d49136a2..9b993f4a2166f01c586ef808e7565ef1e43f80ba 100644
--- a/frontends/libretro/Cargo.toml
+++ b/frontends/libretro/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "boytacean-libretro"
-version = "0.9.12"
+version = "0.9.13"
 authors = ["João Magalhães <joamag@gmail.com>"]
 description = "A Lib Retro frontend for Boytacen"
 license = "Apache-2.0"
@@ -11,5 +11,10 @@ readme = "README.md"
 [lib]
 crate-type = ["cdylib"]
 
+[features]
+debug = ["boytacean/debug"]
+pedantic = ["boytacean/pedantic"]
+cpulog = ["boytacean/cpulog"]
+
 [dependencies.boytacean]
 path = "../.."
diff --git a/frontends/libretro/res/boytacean_libretro.info b/frontends/libretro/res/boytacean_libretro.info
index 3f40e722b1afc13221c1cd03f434910695efad3a..2966bc690d4219eda144abe4a9ef676ed1f34807 100644
--- a/frontends/libretro/res/boytacean_libretro.info
+++ b/frontends/libretro/res/boytacean_libretro.info
@@ -6,7 +6,7 @@ corename = "Boytacean"
 categories = "Emulator"
 license = "Apache-2."
 permissions = ""
-display_version = "0.9.12"
+display_version = "0.9.13"
 
 # Hardware Information
 manufacturer = "Nintendo"
@@ -18,7 +18,7 @@ supports_no_game = "false"
 database = "Nintendo - Game Boy|Nintendo - Game Boy Color"
 savestate = "false"
 savestate_features = "null"
-cheats = "false"
+cheats = "true"
 input_descriptors = "true"
 memory_descriptors = "false"
 libretro_saves = "false"
diff --git a/frontends/libretro/src/lib.rs b/frontends/libretro/src/lib.rs
index 07b32d9dd8fb81252e7ab58ebd30d3f974bc41de..408b95e477b597e24f87224db5557777e422c48e 100644
--- a/frontends/libretro/src/lib.rs
+++ b/frontends/libretro/src/lib.rs
@@ -4,6 +4,7 @@ pub mod consts;
 
 use std::{
     collections::HashMap,
+    ffi::CStr,
     fmt::{self, Display, Formatter},
     os::raw::{c_char, c_float, c_uint, c_void},
     slice::from_raw_parts,
@@ -173,7 +174,7 @@ pub extern "C" fn retro_reset() {
 pub unsafe extern "C" fn retro_get_system_info(info: *mut RetroSystemInfo) {
     debugln!("retro_get_system_info()");
     (*info).library_name = "Boytacean\0".as_ptr() as *const c_char;
-    (*info).library_version = "v0.9.12\0".as_ptr() as *const c_char;
+    (*info).library_version = "v0.9.13\0".as_ptr() as *const c_char;
     (*info).valid_extensions = "gb|gbc\0".as_ptr() as *const c_char;
     (*info).need_fullpath = false;
     (*info).block_extract = false;
@@ -359,11 +360,21 @@ pub extern "C" fn retro_unserialize() {
 #[no_mangle]
 pub extern "C" fn retro_cheat_reset() {
     debugln!("retro_cheat_reset()");
+    println!("retro_cheat_reset()");
+    let emulator = unsafe { EMULATOR.as_mut().unwrap() };
+    emulator.reset_cheats();
 }
 
 #[no_mangle]
-pub extern "C" fn retro_cheat_set() {
+pub extern "C" fn retro_cheat_set(_index: c_uint, enabled: bool, code: *const c_char) {
     debugln!("retro_cheat_set()");
+    if !enabled {
+        return;
+    }
+    let emulator = unsafe { EMULATOR.as_mut().unwrap() };
+    let code_c = unsafe { CStr::from_ptr(code) };
+    let code_s = code_c.to_string_lossy().into_owned();
+    emulator.add_cheat_code(&code_s).unwrap();
 }
 
 #[no_mangle]
diff --git a/frontends/sdl/Cargo.toml b/frontends/sdl/Cargo.toml
index 04a7dab9c06c61f4c01fdb5f8be1ecdf49dac7d6..2ffea6e3d67bd0884989c0c87dd8594d9293d307 100644
--- a/frontends/sdl/Cargo.toml
+++ b/frontends/sdl/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "boytacean-sdl"
-version = "0.9.12"
+version = "0.9.13"
 authors = ["João Magalhães <joamag@gmail.com>"]
 description = "An SDL frontend for Boytacen"
 license = "Apache-2.0"
diff --git a/frontends/web/package.json b/frontends/web/package.json
index 9110434efb1640de9f471299ce1de51115121cc7..8fe26e28dc6e9528861e29e1f0f504c8c51aa5d9 100644
--- a/frontends/web/package.json
+++ b/frontends/web/package.json
@@ -1,6 +1,6 @@
 {
     "name": "boytacean-web",
-    "version": "0.9.12",
+    "version": "0.9.13",
     "description": "The web version of Boytacean",
     "repository": {
         "type": "git",
diff --git a/frontends/web/react/components/index.ts b/frontends/web/react/components/index.ts
index 6ec9010a9a7d8099e4c715093e38cbf130dfcf2b..88127a04f34a00346d54f184d59f9ca8f477d40e 100644
--- a/frontends/web/react/components/index.ts
+++ b/frontends/web/react/components/index.ts
@@ -3,4 +3,5 @@ export * from "./debug/debug";
 export * from "./help/help";
 export * from "./registers-gb/registers-gb";
 export * from "./serial-section/serial-section";
+export * from "./test-section/test-section";
 export * from "./tiles-gb/tiles-gb";
diff --git a/frontends/web/react/components/test-section/test-section.css b/frontends/web/react/components/test-section/test-section.css
new file mode 100644
index 0000000000000000000000000000000000000000..c84791736aba1e86d93cc0ba3af0a0f88bcbad56
--- /dev/null
+++ b/frontends/web/react/components/test-section/test-section.css
@@ -0,0 +1,2 @@
+.test-section {
+}
diff --git a/frontends/web/react/components/test-section/test-section.tsx b/frontends/web/react/components/test-section/test-section.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..d086e27c1ac37c325d23220650e07508e69e292e
--- /dev/null
+++ b/frontends/web/react/components/test-section/test-section.tsx
@@ -0,0 +1,19 @@
+import React, { FC } from "react";
+import { TextInput } from "emukit";
+
+import "./test-section.css";
+
+type TestSectionProps = {
+    style?: string[];
+};
+
+export const TestSection: FC<TestSectionProps> = ({ style = [] }) => {
+    const classes = () => ["test-section", ...style].join(" ");
+    return (
+        <div className={classes()}>
+            <TextInput size="small" placeholder="XXX-XXX-XXX" />
+        </div>
+    );
+};
+
+export default TestSection;
diff --git a/frontends/web/ts/gb.ts b/frontends/web/ts/gb.ts
index 6857cce7dffd58a3bbf8c50b953752be2f8d315d..8126bdf681a23720285a368a2a08ee83c846e342 100644
--- a/frontends/web/ts/gb.ts
+++ b/frontends/web/ts/gb.ts
@@ -24,7 +24,8 @@ import {
     DebugGeneral,
     HelpFaqs,
     HelpKeyboard,
-    SerialSection
+    SerialSection,
+    TestSection
 } from "../react";
 
 import {
@@ -531,6 +532,10 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
                 name: "Serial",
                 icon: require("../res/serial.svg"),
                 node: SerialSection({ emulator: this })
+            },
+            {
+                name: "Test",
+                node: TestSection({})
             }
         ];
     }
diff --git a/src/gb.rs b/src/gb.rs
index dee5b7dfd7b090d316dfc7bf2fba8ccbf3b3aded..6627cdee6680bdc44e18888c2ebb09d7835b4fe9 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, GameGenieCode},
     mmu::Mmu,
     pad::{Pad, PadKey},
     ppu::{
@@ -404,6 +405,7 @@ impl GameBoy {
         self.serial().reset();
         self.mmu().reset();
         self.cpu.reset();
+        self.reset_cheats();
     }
 
     pub fn reload(&mut self) {
@@ -1027,6 +1029,34 @@ impl GameBoy {
     pub fn set_speed_callback(&mut self, callback: fn(speed: GameBoySpeed)) {
         self.mmu().set_speed_callback(callback);
     }
+
+    pub fn reset_cheats(&mut self) {
+        self.reset_game_genie();
+    }
+
+    pub fn add_cheat_code(&mut self, code: &str) -> Result<bool, String> {
+        match self.add_game_genie_code(code) {
+            Ok(_) => Ok(true),
+            Err(message) => Err(message),
+        }
+    }
+
+    pub fn reset_game_genie(&mut self) {
+        let rom = self.mmu().rom();
+        if rom.game_genie().is_some() {
+            rom.game_genie().clone().unwrap().reset();
+        }
+    }
+
+    pub fn add_game_genie_code(&mut self, code: &str) -> Result<&GameGenieCode, String> {
+        let rom = self.mmu().rom();
+        if rom.game_genie().is_none() {
+            let game_genie = GameGenie::default();
+            rom.attach_genie(game_genie);
+        }
+        let game_genie = rom.game_genie_mut().as_mut().unwrap();
+        game_genie.add_code(code)
+    }
 }
 
 #[cfg(feature = "wasm")]
diff --git a/src/genie.rs b/src/genie.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f9a4b89bcc207be3e0475243781ecff4dbb47a47
--- /dev/null
+++ b/src/genie.rs
@@ -0,0 +1,260 @@
+use std::{
+    collections::HashMap,
+    fmt::{self, Display, Formatter},
+};
+
+#[cfg(feature = "wasm")]
+use wasm_bindgen::prelude::*;
+
+#[derive(Clone)]
+#[cfg_attr(feature = "wasm", wasm_bindgen)]
+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 reset(&mut self) {
+        self.codes.clear();
+    }
+
+    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, String> {
+        let genie_code = match GameGenieCode::from_code(code, None) {
+            Ok(genie_code) => genie_code,
+            Err(message) => return Err(message),
+        };
+        let addr = genie_code.addr;
+        self.codes.insert(addr, genie_code);
+        Ok(self.get_addr(addr))
+    }
+}
+
+impl Default for GameGenie {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[derive(Clone)]
+pub struct GameGenieCode {
+    code: String,
+    addr: u16,
+    new_data: u8,
+    old_data: u8,
+
+    /// A boolean value indicating whether the provided cheat code
+    /// was additive or not. If the code is additive, the new data
+    /// will be added to the old data, otherwise the new data will
+    /// replace the old data.
+    additive: bool,
+
+    /// A boolean value indicating whether the provided cheat code
+    /// was condensed (7 characters) or extended (11 characters).
+    condensed: bool,
+}
+
+impl GameGenieCode {
+    /// Creates a new Game Genie code structure from the provided string
+    /// in the ABC-DEF-GHI or ABC-DEF format.
+    /// Note that the additive mode (ex: ABC+DEF+GHI) can be optionally
+    /// handled or ignored using the `handle_additive` parameter.
+    pub fn from_code(code: &str, handle_additive: Option<bool>) -> Result<Self, String> {
+        let code_length = code.len();
+
+        if code_length != 11 && code_length != 7 {
+            return Err(format!(
+                "Invalid Game Genie code length: {} digits",
+                code_length
+            ));
+        }
+        let code_u = code.to_uppercase();
+
+        let additive = if handle_additive.unwrap_or(false) {
+            code_u.chars().nth(3).unwrap() == '+'
+        } else {
+            false
+        };
+        let condensed = code_length == 7;
+
+        let new_data_slice = &code_u[0..=1];
+        let new_data = u8::from_str_radix(new_data_slice, 16).unwrap();
+
+        let old_data = if code_length == 11 {
+            let old_data_slice: String = format!("{}{}", &code_u[8..=8], &code_u[10..=10]);
+            u8::from_str_radix(old_data_slice.as_str(), 16)
+                .unwrap()
+                .rotate_right(2)
+                ^ 0xba
+        } else {
+            0x00
+        };
+
+        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;
+
+        Ok(Self {
+            code: code_u,
+            addr,
+            new_data,
+            old_data,
+            additive,
+            condensed,
+        })
+    }
+
+    /// Tests whether the provided value is valid for the current
+    /// Game Genie code. A value is valid if it matches the old
+    /// data or if the code is condensed.
+    pub fn is_valid(&self, value: u8) -> bool {
+        self.condensed || self.old_data == value
+    }
+
+    /// Patches the provided value with the new data according to
+    /// the Game Genie code. If the code is additive, the new data
+    /// is added to the current value otherwise the new data is
+    /// returned (simple and normal patching operation).
+    pub fn patch_data(&self, value: u8) -> u8 {
+        if self.additive() {
+            value.saturating_add(self.new_data())
+        } else {
+            self.new_data()
+        }
+    }
+
+    pub fn code(&self) -> &str {
+        &self.code
+    }
+
+    pub fn set_code(&mut self, code: String) {
+        self.code = code;
+    }
+
+    pub fn addr(&self) -> u16 {
+        self.addr
+    }
+
+    pub fn set_addr(&mut self, addr: u16) {
+        self.addr = addr;
+    }
+
+    pub fn new_data(&self) -> u8 {
+        self.new_data
+    }
+
+    pub fn set_new_data(&mut self, new_data: u8) {
+        self.new_data = new_data;
+    }
+
+    pub fn old_data(&self) -> u8 {
+        self.old_data
+    }
+
+    pub fn set_old_data(&mut self, old_data: u8) {
+        self.old_data = old_data;
+    }
+
+    pub fn additive(&self) -> bool {
+        self.additive
+    }
+
+    pub fn set_additive(&mut self, additive: bool) {
+        self.additive = additive;
+    }
+
+    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())
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::genie::GameGenieCode;
+
+    #[test]
+    fn test_from_code() {
+        let mut game_genie_code = GameGenieCode::from_code("00A-17B-C49", None).unwrap();
+        assert_eq!(game_genie_code.code, "00A-17B-C49");
+        assert_eq!(game_genie_code.addr, 0x4a17);
+        assert_eq!(game_genie_code.new_data, 0x00);
+        assert_eq!(game_genie_code.old_data, 0xc8);
+        assert_eq!(game_genie_code.additive, false);
+        assert_eq!(game_genie_code.condensed, false);
+        assert_eq!(game_genie_code.is_valid(0xc8), true);
+        assert_eq!(game_genie_code.is_valid(0xc9), false);
+        assert_eq!(game_genie_code.patch_data(0x12), 0x00);
+
+        game_genie_code = GameGenieCode::from_code("00A+17B+C49", None).unwrap();
+        assert_eq!(game_genie_code.code, "00A+17B+C49");
+        assert_eq!(game_genie_code.addr, 0x4a17);
+        assert_eq!(game_genie_code.new_data, 0x00);
+        assert_eq!(game_genie_code.old_data, 0xc8);
+        assert_eq!(game_genie_code.additive, false);
+        assert_eq!(game_genie_code.condensed, false);
+        assert_eq!(game_genie_code.is_valid(0xc8), true);
+        assert_eq!(game_genie_code.is_valid(0xc9), false);
+        assert_eq!(game_genie_code.patch_data(0x12), 0x00);
+
+        game_genie_code = GameGenieCode::from_code("00A+17B+C49", Some(true)).unwrap();
+        assert_eq!(game_genie_code.code, "00A+17B+C49");
+        assert_eq!(game_genie_code.addr, 0x4a17);
+        assert_eq!(game_genie_code.new_data, 0x00);
+        assert_eq!(game_genie_code.old_data, 0xc8);
+        assert_eq!(game_genie_code.additive, true);
+        assert_eq!(game_genie_code.condensed, false);
+        assert_eq!(game_genie_code.is_valid(0xc8), true);
+        assert_eq!(game_genie_code.is_valid(0xc9), false);
+        assert_eq!(game_genie_code.patch_data(0x12), 0x12);
+
+        game_genie_code = GameGenieCode::from_code("00A+17B", None).unwrap();
+        assert_eq!(game_genie_code.code, "00A+17B");
+        assert_eq!(game_genie_code.addr, 0x4a17);
+        assert_eq!(game_genie_code.new_data, 0x00);
+        assert_eq!(game_genie_code.old_data, 0x00);
+        assert_eq!(game_genie_code.additive, false);
+        assert_eq!(game_genie_code.condensed, true);
+        assert_eq!(game_genie_code.is_valid(0xc8), true);
+        assert_eq!(game_genie_code.is_valid(0xc9), true);
+        assert_eq!(game_genie_code.patch_data(0x12), 0x00);
+
+        game_genie_code = GameGenieCode::from_code("00A+17B", Some(true)).unwrap();
+        assert_eq!(game_genie_code.code, "00A+17B");
+        assert_eq!(game_genie_code.addr, 0x4a17);
+        assert_eq!(game_genie_code.new_data, 0x00);
+        assert_eq!(game_genie_code.old_data, 0x00);
+        assert_eq!(game_genie_code.additive, true);
+        assert_eq!(game_genie_code.condensed, true);
+        assert_eq!(game_genie_code.is_valid(0xc8), true);
+        assert_eq!(game_genie_code.is_valid(0xc9), true);
+        assert_eq!(game_genie_code.patch_data(0x12), 0x012);
+    }
+}
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/ppu.rs b/src/ppu.rs
index 0639b11fccbcb6de4b947c2d29e1a33efbf5a634..587d93fdd084bca442168fa02134b7e4fdf7ef79 100644
--- a/src/ppu.rs
+++ b/src/ppu.rs
@@ -222,7 +222,7 @@ impl Display for ObjectData {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         write!(
             f,
-            "Index => {}\nX => {}\nY => {}\nTile => {}",
+            "Index: {}, X: {}, Y: {}, Tile: {}",
             self.index, self.x, self.y, self.tile
         )
     }
@@ -260,7 +260,7 @@ impl Display for TileData {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         write!(
             f,
-            "Palette => {}\nVRAM Bank => {}\nX Flip => {}\nY Flip => {}",
+            "Palette: {}, VRAM Bank: {}, X Flip: {}, Y Flip: {}",
             self.palette, self.vram_bank, self.xflip, self.yflip
         )
     }
diff --git a/src/rom.rs b/src/rom.rs
index 031285e469cf01b762420a28fab28af592b60825..a781cf1d6bab4629e1ce5808869bc2f7104fb428 100644
--- a/src/rom.rs
+++ b/src/rom.rs
@@ -4,7 +4,7 @@ use std::{
     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::*;
@@ -227,6 +227,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.
@@ -249,9 +256,9 @@ 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,
 
     /// The current rumble state of the cartridge, this
@@ -261,6 +268,11 @@ pub struct Cartridge {
     /// Callback function to be called whenever there's a new
     /// rumble vibration triggered or when it's disabled.
     rumble_cb: fn(active: bool),
+
+    /// 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 {
@@ -269,6 +281,7 @@ 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,
@@ -277,6 +290,7 @@ impl Cartridge {
             title_offset: 0x0143,
             rumble_active: false,
             rumble_cb: |_| {},
+            game_genie: None,
         }
     }
 
@@ -294,9 +308,9 @@ impl Cartridge {
     pub fn read(&self, addr: u16) -> u8 {
         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
@@ -307,9 +321,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),
         }
     }
@@ -396,6 +410,7 @@ impl Cartridge {
 
     fn set_mbc(&mut self) {
         self.mbc = self.get_mbc();
+        self.handler = self.mbc;
     }
 
     fn set_computed(&mut self) {
@@ -424,6 +439,18 @@ impl Cartridge {
         self.title_offset = 0x0134 + offset;
     }
 
+    pub fn game_genie(&self) -> &Option<GameGenie> {
+        &self.game_genie
+    }
+
+    pub fn game_genie_mut(&mut self) -> &mut Option<GameGenie> {
+        &mut self.game_genie
+    }
+
+    pub fn set_game_genie(&mut self, game_genie: Option<GameGenie>) {
+        self.game_genie = game_genie;
+    }
+
     fn allocate_ram(&mut self) {
         let ram_banks = max(self.ram_size().ram_banks(), 1);
         self.ram_data = vec![0u8; ram_banks as usize * RAM_BANK_SIZE];
@@ -603,6 +630,16 @@ impl Cartridge {
         self.ram_data = vec![0u8; self.ram_data.len()];
     }
 
+    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);
@@ -856,6 +893,36 @@ pub static MBC5: Mbc = Mbc {
     },
 };
 
+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) {
+            // retrieves the Game Genie code that matches the current address
+            // keep in mind that this assumes that no more that one code is
+            // registered for the same memory address
+            let genie_code = game_genie.get_addr(addr);
+
+            // obtains the current byte that is stored at the address using
+            // the MBC, this value will probably be patched
+            let data = (rom.mbc.read_rom)(rom, 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.is_valid(data) {
+                debugln!("Applying Game Genie code: {}", genie_code);
+                return genie_code.patch_data(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),
+};
+
 #[cfg(test)]
 mod tests {
     use super::{Cartridge, RomType};