diff --git a/frontends/libretro/src/lib.rs b/frontends/libretro/src/lib.rs
index 7027c30b5987132ae4c66c8c89821256eea50a70..3893b0d27d15c49fd12a6fab91f92160fe17d413 100644
--- a/frontends/libretro/src/lib.rs
+++ b/frontends/libretro/src/lib.rs
@@ -439,7 +439,9 @@ pub unsafe extern "C" fn retro_cheat_set(_index: c_uint, enabled: bool, code: *c
     let emulator = EMULATOR.as_mut().unwrap();
     let code_c = CStr::from_ptr(code);
     let code_s = code_c.to_string_lossy().into_owned();
-    emulator.add_cheat_code(&code_s).unwrap();
+    if let Err(err) = emulator.add_cheat_code(&code_s) {
+        warnln!("Failed to add cheat code ({}): {}", code_s, err);
+    }
 }
 
 #[no_mangle]
diff --git a/src/cheats/shark.rs b/src/cheats/shark.rs
index e62deacdab5139552ab283bd44279ecb41120493..5ce6d2eaddb379d2429d5ca375af40c7d41569d5 100644
--- a/src/cheats/shark.rs
+++ b/src/cheats/shark.rs
@@ -37,31 +37,12 @@ impl GameShark {
         self.codes.clear();
     }
 
-    pub fn contains_addr(&self, addr: u16, ram_bank: u8) -> bool {
-        // runs the check to see if the code is registered
-        // for the provided (RAM) address
-        if !self.codes.contains_key(&addr) {
-            return false;
-        }
-
-        // in case the code is for internal RAM then we don't
-        // need to check the RAM bank
-        if addr <= 0xd000 {
-            return true;
-        }
-
-        // this access is for the external RAM so we need to check
-        // that the RAM bank matches
-        let code = self.get_addr(addr);
-        code.ram_bank == ram_bank
-    }
-
     pub fn get_addr(&self, addr: u16) -> &GameSharkCode {
         self.codes.get(&addr).unwrap()
     }
 
     pub fn add_code(&mut self, code: &str) -> Result<&GameSharkCode, String> {
-        let genie_code = match GameSharkCode::from_code(code, None) {
+        let genie_code = match GameSharkCode::from_code(code) {
             Ok(genie_code) => genie_code,
             Err(message) => return Err(message),
         };
@@ -70,17 +51,20 @@ impl GameShark {
         Ok(self.get_addr(addr))
     }
 
-    pub fn writes(&self) -> Vec<(u16, u8)> {
+    pub fn writes(&self) -> Vec<(u16, u16, u8)> {
         let mut writes = vec![];
         for code in self.codes.values() {
             // calculates the real RAM address using both
             // he base RAM address the RAM bank offset
-            let addr = if code.addr <= 0xd000 {
-                code.addr - 0xc000
+            let (base_addr, addr) = if code.addr <= 0xc000 {
+                (
+                    0xa000,
+                    code.addr - 0xa000 + (0x1000 * (code.ram_bank - 1) as u16),
+                )
             } else {
-                code.addr - 0xc000 + (0x1000 * (code.ram_bank - 1) as u16)
+                (0xc000, code.addr - 0xc000)
             };
-            writes.push((addr, code.new_data));
+            writes.push((base_addr, addr, code.new_data));
         }
         writes
     }
@@ -111,7 +95,7 @@ pub struct GameSharkCode {
 impl GameSharkCode {
     /// Creates a new GameShark code structure from the provided string
     /// in the ABCDGHEF format.
-    pub fn from_code(code: &str, _handle_additive: Option<bool>) -> Result<Self, String> {
+    pub fn from_code(code: &str) -> Result<Self, String> {
         let code_length = code.len();
 
         if code_length != 8 {
@@ -124,13 +108,21 @@ impl GameSharkCode {
         let code_u = code.to_uppercase();
 
         let ram_bank_slice = &code_u[0..=1];
-        let ram_bank = u8::from_str_radix(ram_bank_slice, 16).unwrap();
+        let ram_bank = u8::from_str_radix(ram_bank_slice, 16)
+            .map_err(|e| format!("Invalid RAM bank: {}", e))?
+            & 0x0f;
 
         let new_data_slice = &code_u[2..=3];
-        let new_data = u8::from_str_radix(new_data_slice, 16).unwrap();
+        let new_data = u8::from_str_radix(new_data_slice, 16)
+            .map_err(|e| format!("Invalid new data: {}", e))?;
 
         let addr_slice = format!("{}{}", &code_u[6..=7], &code_u[4..=5]);
-        let addr = u16::from_str_radix(&addr_slice, 16).unwrap();
+        let addr =
+            u16::from_str_radix(&addr_slice, 16).map_err(|e| format!("Invalid address: {}", e))?;
+
+        if addr < 0xa000 || addr > 0xdfff {
+            return Err(format!("Invalid cheat address: 0x{:04x}", addr));
+        }
 
         Ok(Self {
             code: code_u,
diff --git a/src/mmu.rs b/src/mmu.rs
index 8220ec3ebb7083b86f3cc2d70fb5b37ed41f079f..4e03d0457202399af1d5bb8515cb8ae251a4e0c4 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -172,8 +172,12 @@ impl Mmu {
     pub fn vblank(&mut self) {
         let writes = self.rom.vblank();
         if let Some(writes) = writes {
-            for (addr, value) in writes {
-                self.ram[addr as usize] = value;
+            for (base_addr, addr, value) in writes {
+                match base_addr {
+                    0xa000 => self.rom.ram_data_mut()[addr as usize] = value,
+                    0xc000 => self.ram[addr as usize] = value,
+                    _ => panic!("Invalid base address for write: 0x{:04x}", base_addr),
+                }
             }
         }
     }
diff --git a/src/rom.rs b/src/rom.rs
index bac315157ed17a0bcac60e038324902b1c420a8b..b9fa6643391303cfac69fe8c490e95ae0d423e54 100644
--- a/src/rom.rs
+++ b/src/rom.rs
@@ -389,7 +389,7 @@ impl Cartridge {
         self.rumble_cb = |_| {};
     }
 
-    pub fn vblank(&mut self) -> Option<Vec<(u16, u8)>> {
+    pub fn vblank(&mut self) -> Option<Vec<(u16, u16, u8)>> {
         if let Some(game_shark) = &mut self.game_shark {
             return Some(game_shark.writes());
         }
@@ -757,9 +757,17 @@ impl Cartridge {
         &self.rom_data
     }
 
+    pub fn rom_data_mut(&mut self) -> &mut Vec<u8> {
+        &mut self.rom_data
+    }
+
     pub fn ram_data(&self) -> &Vec<u8> {
         &self.ram_data
     }
+
+    pub fn ram_data_mut(&mut self) -> &mut Vec<u8> {
+        &mut self.ram_data
+    }
 }
 
 impl Default for Cartridge {