diff --git a/src/mmu.rs b/src/mmu.rs
index d1b980a53879817718ebefe05f0a52f4e009346d..cf8f41734490e82c21b007132474299bf305db25 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -4,7 +4,6 @@ use std::sync::Mutex;
 
 use crate::{
     apu::Apu,
-    debugln,
     dma::Dma,
     gb::{Components, GameBoyConfig, GameBoyMode, GameBoySpeed},
     pad::Pad,
@@ -13,6 +12,7 @@ use crate::{
     serial::Serial,
     timer::Timer,
     util::SharedThread,
+    warnln,
 };
 
 pub const BOOT_SIZE_DMG: usize = 256;
@@ -319,9 +319,9 @@ impl Mmu {
     }
 
     pub fn read(&mut self, addr: u16) -> u8 {
-        match addr & 0xf000 {
-            // BOOT (256 B) + ROM0 (4 KB/16 KB)
-            0x0000 => {
+        match addr {
+            // 0x0000-0x0FFF - BOOT (256 B) + ROM0 (4 KB/16 KB)
+            0x0000..=0x0fff => {
                 // in case the boot mode is active and the
                 // address is withing boot memory reads from it
                 if self.boot_active && addr <= 0x00fe {
@@ -342,209 +342,194 @@ impl Mmu {
                 self.rom.read(addr)
             }
 
-            // ROM 0 (12 KB/16 KB)
-            0x1000 | 0x2000 | 0x3000 => self.rom.read(addr),
-
-            // ROM 1 (Banked) (16 KB)
-            0x4000 | 0x5000 | 0x6000 | 0x7000 => self.rom.read(addr),
-
-            // Graphics: VRAM (8 KB)
-            0x8000 | 0x9000 => self.ppu.read(addr),
-
-            // External RAM (8 KB)
-            0xa000 | 0xb000 => self.rom.read(addr),
-
-            // Working RAM 0 (4 KB)
-            0xc000 => self.ram[(addr & 0x0fff) as usize],
-
-            // Working RAM 1 (Banked) (4KB)
-            0xd000 => self.ram[(self.ram_offset + (addr & 0x0fff)) as usize],
-
-            // Working RAM Shadow
-            0xe000 => self.ram[(addr & 0x1fff) as usize],
-
-            // Working RAM Shadow, I/O, Zero-page RAM
-            0xf000 => match addr & 0x0f00 {
-                0x000 | 0x100 | 0x200 | 0x300 | 0x400 | 0x500 | 0x600 | 0x700 | 0x800 | 0x900
-                | 0xa00 | 0xb00 | 0xc00 | 0xd00 => self.ram[(addr & 0x1fff) as usize],
-                0xe00 => self.ppu.read(addr),
-                0xf00 => match addr & 0x00ff {
-                    // 0xFF01-0xFF02 - Serial data transfer
-                    0x01..=0x02 => self.serial.read(addr),
-
-                    // 0xFF0F — IF: Interrupt flag
-                    0x0f =>
-                    {
-                        #[allow(clippy::bool_to_int_with_if)]
-                        (if self.ppu.int_vblank() { 0x01 } else { 0x00 }
-                            | if self.ppu.int_stat() { 0x02 } else { 0x00 }
-                            | if self.timer.int_tima() { 0x04 } else { 0x00 }
-                            | if self.serial.int_serial() { 0x08 } else { 0x00 }
-                            | if self.pad.int_pad() { 0x10 } else { 0x00 }
-                            | 0xe0)
-                    }
+            // 0x1000-0x3FFF - ROM 0 (12 KB/16 KB)
+            // 0x4000-0x7FFF - ROM 1 (Banked) (16 KB)
+            0x1000..=0x7fff => self.rom.read(addr),
+
+            // 0x8000-0x9FFF - Graphics: VRAM (8 KB)
+            0x8000..=0x9fff => self.ppu.read(addr),
+
+            // 0xA000-0xBFFF - External RAM (8 KB)
+            0xa000..=0xbfff => self.rom.read(addr),
+
+            // 0xC000-0xCFFF - Working RAM 0 (4 KB)
+            0xc000..=0xcfff => self.ram[(addr & 0x0fff) as usize],
+
+            // 0xD000..=0xDFFF - Working RAM 1 (Banked) (4KB)
+            0xd000..=0xdfff => self.ram[(self.ram_offset + (addr & 0x0fff)) as usize],
+
+            // 0xE000..=0xFDFF - Working RAM Shadow
+            0xe000..=0xfdff => self.ram[(addr & 0x1fff) as usize],
+
+            // 0xFE00-0xFE9F - Object attribute memory (OAM)
+            0xfe00..=0xfe9f => self.ppu.read(addr),
+
+            // 0xFEA0-0xFEFF - Not Usable
+            0xfea0..=0xfeff => 0xff,
+
+            // 0xFF00 - Joypad input
+            0xff00 => self.pad.read(addr),
+
+            // 0xFF01-0xFF02 - Serial data transfer
+            0xff01..=0xff02 => self.serial.read(addr),
+
+            // 0xFF04-0xFF07 - Timer and divider
+            0xff04..=0xff07 => self.timer.read(addr),
+
+            // 0xFF0F — IF: Interrupt flag
+            0xff0f =>
+            {
+                #[allow(clippy::bool_to_int_with_if)]
+                (if self.ppu.int_vblank() { 0x01 } else { 0x00 }
+                    | if self.ppu.int_stat() { 0x02 } else { 0x00 }
+                    | if self.timer.int_tima() { 0x04 } else { 0x00 }
+                    | if self.serial.int_serial() { 0x08 } else { 0x00 }
+                    | if self.pad.int_pad() { 0x10 } else { 0x00 }
+                    | 0xe0)
+            }
+
+            // 0xFF10-0xFF26 — Audio
+            // 0xFF10-0xFF26 — Wave pattern
+            0xff10..=0xff26 | 0xff30..=0xff3f => self.apu.read(addr),
+
+            // 0xFF40-0xFF45 - PPU registers
+            // 0xFF47-0xFF4B - PPU registers
+            0xff40..=0xff45 | 0xff47..=0xff4b => self.ppu.read(addr),
+
+            // 0xFF46 — DMA: OAM DMA source address & start
+            0xff46 => self.dma.read(addr),
+
+            // 0xFF4C - KEY0: Compatibility flag (CGB only)
+            0xff4c => self.key0,
+
+            // 0xFF4D - KEY1: Speed switching (CGB only)
+            0xff4d => (if self.switching { 0x01 } else { 0x00 }) | ((self.speed as u8) << 7) | 0x7e,
+
+            // 0xFF4F - VRAM Bank Select (CGB only)
+            0xff4f => self.ppu.read(addr),
+
+            // 0xFF50 - Boot active flag
+            0xff50 => u8::from(!self.boot_active),
+
+            // 0xFF51-0xFF55 - VRAM DMA (HDMA) (CGB only)
+            0xff51..=0xff55 => self.dma.read(addr),
+
+            // 0xFF68-0xFF6B - BG / OBJ Palettes (CGB only)
+            0xff68..=0xff6b => self.ppu.read(addr),
+
+            // 0xFF70 - SVBK: WRAM bank (CGB only)
+            0xff70 => (self.ram_bank & 0x07) | 0xf8,
 
-                    // 0xFF4C - KEY0: Compatibility flag (CGB only)
-                    0x4c => self.key0,
-
-                    // 0xFF4D - KEY1: Speed switching (CGB only)
-                    0x4d => (false as u8) | ((self.speed as u8) << 7),
-
-                    // 0xFF50 - Boot active flag
-                    0x50 => u8::from(!self.boot_active),
-
-                    // 0xFF70 - SVBK: WRAM bank (CGB only)
-                    0x70 => self.ram_bank & 0x07,
-
-                    // 0xFF80-0xFFFE - High RAM (HRAM)
-                    0x80..=0xfe => self.ppu.read(addr),
-
-                    // 0xFFFF — IE: Interrupt enable
-                    0xff => self.ie,
-
-                    // Other registers
-                    _ => match addr & 0x00f0 {
-                        0x00 => match addr & 0x00ff {
-                            0x00 => self.pad.read(addr),
-                            0x04..=0x07 => self.timer.read(addr),
-                            _ => {
-                                debugln!("Reading from unknown IO control 0x{:04x}", addr);
-                                0x00
-                            }
-                        },
-                        0x10 | 0x20 | 0x30 => self.apu.read(addr),
-                        0x40 | 0x60 | 0x70 => match addr & 0x00ff {
-                            // 0xFF46 — DMA: OAM DMA source address & start
-                            0x0046 => self.dma.read(addr),
-
-                            // VRAM related read
-                            _ => self.ppu.read(addr),
-                        },
-                        0x50 => match addr & 0x00ff {
-                            0x51..=0x55 => self.dma.read(addr),
-                            _ => {
-                                debugln!("Reading from unknown IO control 0x{:04x}", addr);
-                                0x00
-                            }
-                        },
-                        _ => {
-                            debugln!("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),
+            // 0xFF80-0xFFFE - High RAM (HRAM)
+            0xff80..=0xfffe => self.ppu.read(addr),
+
+            // 0xFFFF — IE: Interrupt enable
+            0xffff => self.ie,
+
+            addr => {
+                warnln!("Reading from unknown location 0x{:04x}", addr);
+                0xff
+            }
         }
     }
 
     pub fn write(&mut self, addr: u16, value: u8) {
-        match addr & 0xf000 {
-            // BOOT (256 B) + ROM0 (4 KB/16 KB)
-            0x0000 => self.rom.write(addr, value),
+        match addr {
+            // 0x0000-0x0FFF - BOOT (256 B) + ROM0 (4 KB/16 KB)
+            // 0x1000-0x3FFF - ROM 0 (12 KB/16 KB)
+            // 0x4000-0x7FFF - ROM 1 (Banked) (16 KB)
+            0x0000..=0x7fff => self.rom.write(addr, value),
+
+            // 0x8000-0x9FFF - Graphics: VRAM (8 KB)
+            0x8000..=0x9fff => self.ppu.write(addr, value),
+
+            // 0xA000-0xBFFF - External RAM (8 KB)
+            0xa000..=0xbfff => self.rom.write(addr, value),
 
-            // ROM 0 (12 KB/16 KB)
-            0x1000 | 0x2000 | 0x3000 => self.rom.write(addr, value),
+            // 0xC000-0xCFFF - Working RAM 0 (4 KB)
+            0xc000..=0xcfff => self.ram[(addr & 0x0fff) as usize] = value,
 
-            // ROM 1 (Banked) (16 KB)
-            0x4000 | 0x5000 | 0x6000 | 0x7000 => self.rom.write(addr, value),
+            // 0xD000..=0xDFFF - Working RAM 1 (Banked) (4KB)
+            0xd000..=0xdfff => self.ram[(self.ram_offset + (addr & 0x0fff)) as usize] = value,
 
-            // Graphics: VRAM (8 KB)
-            0x8000 | 0x9000 => self.ppu.write(addr, value),
+            // 0xE000..=0xFDFF - Working RAM Shadow
+            0xe000..=0xfdff => self.ram[(addr & 0x1fff) as usize] = value,
 
-            // External RAM (8 KB)
-            0xa000 | 0xb000 => self.rom.write(addr, value),
+            // 0xFE00-0xFE9F - Object attribute memory (OAM)
+            0xfe00..=0xfe9f => self.ppu.write(addr, value),
+
+            // 0xFEA0-0xFEFF - Not Usable
+            0xfea0..=0xfeff => {}
+
+            // 0xFF00 - Joypad input
+            0xff00 => self.pad.write(addr, value),
+
+            // 0xFF01-0xFF02 - Serial data transfer
+            0xff01..=0xff02 => self.serial.write(addr, value),
+
+            // 0xFF04-0xFF07 - Timer and divider
+            0xff04..=0xff07 => self.timer.write(addr, value),
+
+            // 0xFF0F — IF: Interrupt flag
+            0xff0f => {
+                self.ppu.set_int_vblank(value & 0x01 == 0x01);
+                self.ppu.set_int_stat(value & 0x02 == 0x02);
+                self.timer.set_int_tima(value & 0x04 == 0x04);
+                self.serial.set_int_serial(value & 0x08 == 0x08);
+                self.pad.set_int_pad(value & 0x10 == 0x10);
+            }
 
-            // Working RAM 0 (4 KB)
-            0xc000 => self.ram[(addr & 0x0fff) as usize] = value,
+            // 0xFF10-0xFF26 — Audio
+            // 0xFF10-0xFF26 — Wave pattern
+            0xff10..=0xff26 | 0xff30..=0xff3f => self.apu.write(addr, value),
 
-            // Working RAM 1 (Banked) (4KB)
-            0xd000 => self.ram[(self.ram_offset + (addr & 0x0fff)) as usize] = value,
+            // 0xFF40-0xFF45 - PPU registers
+            // 0xFF47-0xFF4B - PPU registers
+            0xff40..=0xff45 | 0xff47..=0xff4b => self.ppu.write(addr, value),
 
-            // Working RAM Shadow
-            0xe000 => self.ram[(addr & 0x1fff) as usize] = value,
+            // 0xFF46 — DMA: OAM DMA source address & start
+            0xff46 => self.dma.write(addr, value),
 
-            // Working RAM Shadow, I/O, Zero-page RAM
-            0xf000 => match addr & 0x0f00 {
-                0x000 | 0x100 | 0x200 | 0x300 | 0x400 | 0x500 | 0x600 | 0x700 | 0x800 | 0x900
-                | 0xa00 | 0xb00 | 0xc00 | 0xd00 => {
-                    self.ram[(addr & 0x1fff) as usize] = value;
+            // 0xFF4C - KEY0: Compatibility flag (CGB only)
+            0xff4c => {
+                self.key0 = value;
+                if value == 0x04 {
+                    self.ppu().set_dmg_compat(true);
                 }
-                0xe00 => self.ppu.write(addr, value),
-                0xf00 => match addr & 0x00ff {
-                    // 0xFF01-0xFF02 - Serial data transfer
-                    0x01..=0x02 => self.serial.write(addr, value),
-
-                    // 0xFF0F — IF: Interrupt flag
-                    0x0f => {
-                        self.ppu.set_int_vblank(value & 0x01 == 0x01);
-                        self.ppu.set_int_stat(value & 0x02 == 0x02);
-                        self.timer.set_int_tima(value & 0x04 == 0x04);
-                        self.serial.set_int_serial(value & 0x08 == 0x08);
-                        self.pad.set_int_pad(value & 0x10 == 0x10);
-                    }
+            }
 
-                    // 0xFF4C - KEY0: Compatibility flag (CGB only)
-                    0x4c => {
-                        self.key0 = value;
-                        if value == 0x04 {
-                            self.ppu().set_dmg_compat(true);
-                        }
-                    }
+            // 0xFF4D - KEY1: Speed switching (CGB only)
+            0xff4d => self.switching = value & 0x01 == 0x01,
 
-                    // 0xFF4D - KEY1: Speed switching (CGB only)
-                    0x4d => {
-                        self.switching = value & 0x01 == 0x01;
-                    }
+            // 0xFF4F - VRAM Bank Select (CGB only)
+            0xff4f => self.ppu.write(addr, value),
 
-                    // 0xFF50 - Boot active flag
-                    0x50 => self.boot_active = value == 0x00,
-
-                    // 0xFF70 - SVBK: WRAM bank (CGB only)
-                    0x70 => {
-                        let mut ram_bank = value & 0x07;
-                        if ram_bank == 0x0 {
-                            ram_bank = 0x1;
-                        }
-                        self.ram_bank = ram_bank;
-                        self.ram_offset = self.ram_bank as u16 * 0x1000;
-                    }
+            // 0xFF50 - Boot active flag
+            0xff50 => self.boot_active = value == 0x00,
+
+            // 0xFF51-0xFF55 - VRAM DMA (HDMA) (CGB only)
+            0xff51..=0xff55 => self.dma.write(addr, value),
+
+            // 0xFF68-0xFF6B - BG / OBJ Palettes (CGB only)
+            0xff68..=0xff6b => self.ppu.write(addr, value),
+
+            // 0xFF70 - SVBK: WRAM bank (CGB only)
+            0xff70 => {
+                let mut ram_bank = value & 0x07;
+                if ram_bank == 0x0 {
+                    ram_bank = 0x1;
+                }
+                self.ram_bank = ram_bank;
+                self.ram_offset = self.ram_bank as u16 * 0x1000;
+            }
+
+            // 0xFF80-0xFFFE - High RAM (HRAM)
+            0xff80..=0xfffe => self.ppu.write(addr, value),
+
+            // 0xFFFF — IE: Interrupt enable
+            0xffff => self.ie = value,
 
-                    // 0xFF80-0xFFFE - High RAM (HRAM)
-                    0x80..=0xfe => self.ppu.write(addr, value),
-
-                    // 0xFFFF — IE: Interrupt enable
-                    0xff => self.ie = value,
-
-                    // Other registers
-                    _ => match addr & 0x00f0 {
-                        0x00 => match addr & 0x00ff {
-                            0x00 => self.pad.write(addr, value),
-                            0x04..=0x07 => self.timer.write(addr, value),
-                            _ => debugln!("Writing to unknown IO control 0x{:04x}", addr),
-                        },
-                        0x10 | 0x20 | 0x30 => self.apu.write(addr, value),
-                        0x40 | 0x60 | 0x70 => match addr & 0x00ff {
-                            // 0xFF46 — DMA: OAM DMA source address & start
-                            0x0046 => self.dma.write(addr, value),
-
-                            // VRAM related write
-                            _ => self.ppu.write(addr, value),
-                        },
-                        #[allow(clippy::single_match)]
-                        0x50 => match addr & 0x00ff {
-                            0x51..=0x55 => self.dma.write(addr, value),
-                            _ => debugln!("Writing to unknown IO control 0x{:04x}", addr),
-                        },
-                        _ => debugln!("Writing to unknown IO control 0x{:04x}", addr),
-                    },
-                },
-                addr => panic!("Writing to unknown location 0x{:04x}", addr),
-            },
-
-            addr => panic!("Writing to unknown location 0x{:04x}", addr),
+            addr => warnln!("Writing to unknown location 0x{:04x}", addr),
         }
     }
 
diff --git a/src/ppu.rs b/src/ppu.rs
index af0fd2dd45cbb02635237cb97f2209037db94fd0..ac1a73be110eab8c3ee0da0de5c95d4f101c4c1f 100644
--- a/src/ppu.rs
+++ b/src/ppu.rs
@@ -834,15 +834,7 @@ impl Ppu {
             // 0xFF6B — OCPD/OBPD (CGB only)
             0xff6b => self.palettes_color[1][self.palette_address_obj as usize],
             // 0xFF6C — OPRI (CGB only)
-            0xff6c =>
-            {
-                #[allow(clippy::bool_to_int_with_if)]
-                if self.obj_priority {
-                    0x01
-                } else {
-                    0x00
-                }
-            }
+            0xff6c => (if self.obj_priority { 0x01 } else { 0x00 }) | 0xfe,
             _ => {
                 warnln!("Reading from unknown PPU location 0x{:04x}", addr);
                 0xff
diff --git a/src/rom.rs b/src/rom.rs
index ca7f65feb692f15545fd6b7f6e00d118e4cae721..572c86ac1ee59f3973ab4b82e6a6ca81312e5d92 100644
--- a/src/rom.rs
+++ b/src/rom.rs
@@ -425,13 +425,11 @@ impl Cartridge {
     }
 
     pub fn read(&mut self, addr: u16) -> u8 {
-        match addr & 0xf000 {
+        match addr {
             // 0x0000-0x7FFF: 16 KiB ROM bank 00 & 16 KiB ROM Bank 01–NN
-            0x0000 | 0x1000 | 0x2000 | 0x3000 | 0x4000 | 0x5000 | 0x6000 | 0x7000 => {
-                (self.handler.read_rom)(self, addr)
-            }
+            0x0000..=0x7fff => (self.handler.read_rom)(self, addr),
             // 0xA000-0xBFFF: 8 KiB External RAM
-            0xa000 | 0xb000 => (self.handler.read_ram)(self, addr),
+            0xa000..=0xbfff => (self.handler.read_ram)(self, addr),
             _ => {
                 debugln!("Reading from unknown Cartridge control 0x{:04x}", addr);
                 0x00
@@ -440,13 +438,11 @@ impl Cartridge {
     }
 
     pub fn write(&mut self, addr: u16, value: u8) {
-        match addr & 0xf000 {
+        match addr {
             // 0x0000-0x7FFF: 16 KiB ROM bank 00 & 16 KiB ROM Bank 01–NN
-            0x0000 | 0x1000 | 0x2000 | 0x3000 | 0x4000 | 0x5000 | 0x6000 | 0x7000 => {
-                (self.handler.write_rom)(self, addr, value)
-            }
+            0x0000..=0x7fff => (self.handler.write_rom)(self, addr, value),
             // 0xA000-0xBFFF: 8 KiB External RAM
-            0xa000 | 0xb000 => (self.handler.write_ram)(self, addr, value),
+            0xa000..=0xbfff => (self.handler.write_ram)(self, addr, value),
             _ => debugln!("Writing to unknown Cartridge address 0x{:04x}", addr),
         }
     }
@@ -984,9 +980,9 @@ pub static NO_MBC: Mbc = Mbc {
 pub static MBC1: Mbc = Mbc {
     name: "MBC1",
     read_rom: |rom: &Cartridge, addr: u16| -> u8 {
-        match addr & 0xf000 {
-            0x0000 | 0x1000 | 0x2000 | 0x3000 => rom.rom_data[addr as usize],
-            0x4000 | 0x5000 | 0x6000 | 0x7000 => *rom
+        match addr {
+            0x0000..=0x3fff => rom.rom_data[addr as usize],
+            0x4000..=0x7fff => *rom
                 .rom_data
                 .get(rom.rom_offset + (addr - 0x4000) as usize)
                 .unwrap_or(&0x0),
@@ -997,13 +993,13 @@ pub static MBC1: Mbc = Mbc {
         }
     },
     write_rom: |rom: &mut Cartridge, addr: u16, value: u8| {
-        match addr & 0xf000 {
+        match addr {
             // RAM enabled flag
-            0x0000 | 0x1000 => {
+            0x0000..=0x1fff => {
                 rom.ram_enabled = (value & 0x0f) == 0x0a;
             }
             // ROM bank selection 5 lower bits
-            0x2000 | 0x3000 => {
+            0x2000..=0x3fff => {
                 let mut rom_bank = value as u16 & 0x1f;
                 rom_bank &= rom.rom_bank_count * 2 - 1;
                 if rom_bank == 0 {
@@ -1012,7 +1008,7 @@ pub static MBC1: Mbc = Mbc {
                 rom.set_rom_bank(rom_bank);
             }
             // RAM bank selection and ROM bank selection upper bits
-            0x4000 | 0x5000 => {
+            0x4000..=0x5fff => {
                 let ram_bank = value & 0x03;
                 if ram_bank as u16 >= rom.ram_bank_count {
                     return;
@@ -1020,7 +1016,7 @@ pub static MBC1: Mbc = Mbc {
                 rom.set_ram_bank(ram_bank);
             }
             // ROM mode selection
-            0x6000 | 0x7000 => {
+            0x6000..=0x7fff => {
                 if value == 0x1 && rom.rom_bank_count > 32 {
                     unimplemented!("Advanced ROM banking mode for MBC1 is not implemented");
                 }
@@ -1046,9 +1042,9 @@ pub static MBC1: Mbc = Mbc {
 pub static MBC3: Mbc = Mbc {
     name: "MBC3",
     read_rom: |rom: &Cartridge, addr: u16| -> u8 {
-        match addr & 0xf000 {
-            0x0000 | 0x1000 | 0x2000 | 0x3000 => rom.rom_data[addr as usize],
-            0x4000 | 0x5000 | 0x6000 | 0x7000 => *rom
+        match addr {
+            0x0000..=0x3fff => rom.rom_data[addr as usize],
+            0x4000..=0x7fff => *rom
                 .rom_data
                 .get(rom.rom_offset + (addr - 0x4000) as usize)
                 .unwrap_or(&0x0),
@@ -1059,13 +1055,13 @@ pub static MBC3: Mbc = Mbc {
         }
     },
     write_rom: |rom: &mut Cartridge, addr: u16, value: u8| {
-        match addr & 0xf000 {
+        match addr {
             // RAM enabled flag
-            0x0000 | 0x1000 => {
+            0x0000..=0x1fff => {
                 rom.ram_enabled = (value & 0x0f) == 0x0a;
             }
             // ROM bank selection
-            0x2000 | 0x3000 => {
+            0x2000..=0x3fff => {
                 let mut rom_bank = value as u16 & 0x7f;
                 rom_bank &= rom.rom_bank_count * 2 - 1;
                 if rom_bank == 0 {
@@ -1074,7 +1070,7 @@ pub static MBC3: Mbc = Mbc {
                 rom.set_rom_bank(rom_bank);
             }
             // RAM bank selection
-            0x4000 | 0x5000 => {
+            0x4000..=0x5fff => {
                 let ram_bank = value & 0x03;
                 if ram_bank as u16 >= rom.ram_bank_count {
                     return;
@@ -1102,9 +1098,9 @@ pub static MBC3: Mbc = Mbc {
 pub static MBC5: Mbc = Mbc {
     name: "MBC5",
     read_rom: |rom: &Cartridge, addr: u16| -> u8 {
-        match addr & 0xf000 {
-            0x0000 | 0x1000 | 0x2000 | 0x3000 => rom.rom_data[addr as usize],
-            0x4000 | 0x5000 | 0x6000 | 0x7000 => *rom
+        match addr {
+            0x0000..=0x3fff => rom.rom_data[addr as usize],
+            0x4000..=0x7fff => *rom
                 .rom_data
                 .get(rom.rom_offset + (addr - 0x4000) as usize)
                 .unwrap_or(&0x0),
@@ -1115,9 +1111,9 @@ pub static MBC5: Mbc = Mbc {
         }
     },
     write_rom: |rom: &mut Cartridge, addr: u16, value: u8| {
-        match addr & 0xf000 {
+        match addr {
             // RAM enabled flag
-            0x0000 | 0x1000 => {
+            0x0000..=0x1fff => {
                 rom.ram_enabled = (value & 0x0f) == 0x0a;
             }
             // ROM bank selection 8 lower bits
@@ -1131,7 +1127,7 @@ pub static MBC5: Mbc = Mbc {
                 rom.set_rom_bank(rom_bank);
             }
             // RAM bank selection
-            0x4000 | 0x5000 => {
+            0x4000..=0x5fff => {
                 let mut ram_bank = value & 0x0f;
 
                 // handles the rumble flag for the cartridges
diff --git a/src/state.rs b/src/state.rs
index 065f0af5256cf56e2b4659b711d1ab1abf593464..8c4c917d0102fa113502b3b79db41577200450f6 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -88,6 +88,17 @@ pub trait State {
     fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error>;
 }
 
+pub trait StateBox {
+    /// Obtains a new instance of the state from the provided
+    /// `GameBoy` instance and returns it as a boxed value.
+    fn from_gb(gb: &mut GameBoy) -> Result<Box<Self>, Error>
+    where
+        Self: Sized;
+
+    /// Applies the state to the provided `GameBoy` instance.
+    fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error>;
+}
+
 #[cfg_attr(feature = "wasm", wasm_bindgen)]
 #[derive(Default)]
 pub struct BosState {
@@ -248,16 +259,16 @@ impl Serialize for BosState {
     }
 }
 
-impl State for BosState {
-    fn from_gb(gb: &mut GameBoy) -> Result<Self, Error> {
-        Ok(Self {
+impl StateBox for BosState {
+    fn from_gb(gb: &mut GameBoy) -> Result<Box<Self>, Error> {
+        Ok(Box::new(Self {
             magic: BOS_MAGIC_UINT,
             version: BOS_VERSION,
             block_count: 2,
             info: Some(BosInfo::from_gb(gb)?),
             image_buffer: Some(BosImageBuffer::from_gb(gb)?),
-            bess: BessState::from_gb(gb)?,
-        })
+            bess: *BessState::from_gb(gb)?,
+        }))
     }
 
     fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error> {
@@ -606,16 +617,16 @@ impl Serialize for BessState {
     }
 }
 
-impl State for BessState {
-    fn from_gb(gb: &mut GameBoy) -> Result<Self, Error> {
-        Ok(Self {
+impl StateBox for BessState {
+    fn from_gb(gb: &mut GameBoy) -> Result<Box<Self>, Error> {
+        Ok(Box::new(Self {
             footer: BessFooter::default(),
             name: BessName::from_gb(gb)?,
             info: BessInfo::from_gb(gb)?,
             core: BessCore::from_gb(gb)?,
             mbc: BessMbc::from_gb(gb)?,
             end: BessBlock::from_magic(String::from("END ")),
-        })
+        }))
     }
 
     fn to_gb(&self, gb: &mut GameBoy) -> Result<(), Error> {
@@ -1630,9 +1641,48 @@ impl StateManager {
 mod tests {
     use boytacean_encoding::zippy::{decode_zippy, encode_zippy};
 
-    use crate::gb::GameBoy;
+    use crate::{gb::GameBoy, state::State};
+
+    use super::{BessCore, SaveStateFormat, StateManager};
 
-    use super::{SaveStateFormat, StateManager};
+    #[test]
+    fn test_bess_core() {
+        let mut gb = GameBoy::default();
+        gb.load(true).unwrap();
+        gb.load_rom_file("res/roms/test/firstwhite.gb", None)
+            .unwrap();
+        let bess_core = BessCore::from_gb(&mut gb).unwrap();
+        assert_eq!(bess_core.model, "GDB ");
+        assert_eq!(bess_core.pc, 0x0000);
+        assert_eq!(bess_core.af, 0x0000);
+        assert_eq!(bess_core.bc, 0x0000);
+        assert_eq!(bess_core.de, 0x0000);
+        assert_eq!(bess_core.hl, 0x0000);
+        assert_eq!(bess_core.sp, 0x0000);
+        assert_eq!(bess_core.ime, false);
+        assert_eq!(bess_core.ie, 0x00);
+        assert_eq!(bess_core.execution_mode, 0);
+        assert_eq!(bess_core.io_registers.len(), 128);
+        assert_eq!(
+            bess_core.io_registers,
+            [
+                63, 0, 0, 255, 0, 0, 0, 248, 255, 255, 255, 255, 255, 255, 255, 224, 128, 63, 0,
+                255, 191, 255, 63, 0, 255, 191, 127, 255, 159, 255, 191, 255, 255, 0, 0, 191, 0, 0,
+                240, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+                0, 0, 0, 0, 0, 0, 134, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126, 255, 254, 0, 255, 255,
+                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+                255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 249, 255, 255, 255, 255,
+                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
+            ]
+        );
+        assert_eq!(bess_core.ram.size, 0x2000);
+        assert_eq!(bess_core.vram.size, 0x2000);
+        assert_eq!(bess_core.mbc_ram.size, 0x2000);
+        assert_eq!(bess_core.oam.size, 0x00a0);
+        assert_eq!(bess_core.hram.size, 0x007f);
+        assert_eq!(bess_core.background_palettes.size, 0x0000);
+        assert_eq!(bess_core.object_palettes.size, 0x0000);
+    }
 
     #[test]
     fn test_load_bos() {
@@ -1667,7 +1717,7 @@ mod tests {
         let encoded = encode_zippy(&data).unwrap();
         let decoded = decode_zippy(&encoded).unwrap();
         assert_eq!(data, decoded);
-        assert_eq!(encoded.len(), 804);
+        assert_eq!(encoded.len(), 811);
         assert_eq!(decoded.len(), 25154);
     }
 }