diff --git a/src/cpu.rs b/src/cpu.rs
index d663cdf8b54cacea4619b52ec9ed29487113e759..a28049fd816f545253928c6b0c045f4a67502f98 100644
--- a/src/cpu.rs
+++ b/src/cpu.rs
@@ -28,7 +28,7 @@ pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [
     (nop, 4, "NOP"),
     (nop, 4, "NOP"),
     (nop, 4, "NOP"),
-    (nop, 4, "NOP"),
+    (rla, 4, "RLA"),
     (nop, 4, "NOP"),
     (nop, 4, "NOP"),
     (ld_a_mde, 8, "LD A, [DE]"),
@@ -208,8 +208,8 @@ pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [
     (nop, 4, "NOP"),
     (nop, 4, "NOP"),
     // 0xc opcodes
-    (nop, 4, "NOP"),
-    (nop, 4, "NOP"),
+    (ret_nz, 8, "RET NZ"),
+    (pop_bc, 12, "POP BC"),
     (nop, 4, "NOP"),
     (nop, 4, "NOP"),
     (nop, 4, "NOP"),
@@ -297,7 +297,7 @@ pub const BITWISE: [(fn(&mut Cpu), u8, &'static str); 176] = [
     (nop, 4, "NOP"),
     // 0x1 opcodes
     (nop, 4, "NOP"),
-    (nop, 4, "NOP"),
+    (rl_c, 8, "RL C"),
     (nop, 4, "NOP"),
     (nop, 4, "NOP"),
     (nop, 4, "NOP"),
@@ -514,9 +514,10 @@ impl Cpu {
         let mut opcode = self.mmu.read(self.pc);
         self.pc = self.pc.wrapping_add(1);
 
+        let is_prefix = opcode == PREFIX;
         let instruction: &(fn(&mut Cpu), u8, &str);
 
-        if opcode == PREFIX {
+        if is_prefix {
             opcode = self.mmu.read(self.pc);
             self.pc = self.pc.wrapping_add(1);
             instruction = &BITWISE[opcode as usize];
@@ -526,7 +527,10 @@ impl Cpu {
 
         let (instruction_fn, instruction_size, instruction_str) = instruction;
 
-        println!("{}\t(0x{:02x})\t${:04x}", instruction_str, opcode, pc);
+        println!(
+            "{}\t(0x{:02x})\t${:04x} {}",
+            instruction_str, opcode, pc, is_prefix
+        );
 
         instruction_fn(self);
         self.ticks = self.ticks.wrapping_add(*instruction_size as u32);
@@ -614,7 +618,7 @@ impl Cpu {
     }
 
     #[inline(always)]
-    fn push_byte(&mut self, byte: u8){
+    fn push_byte(&mut self, byte: u8) {
         self.sp -= 1;
         self.mmu.write(self.sp, byte);
     }
@@ -625,6 +629,19 @@ impl Cpu {
         self.push_byte(word as u8);
     }
 
+    #[inline(always)]
+    fn pop_byte(&mut self) -> u8 {
+        let byte = self.mmu.read(self.sp);
+        self.sp += 1;
+        byte
+    }
+
+    #[inline(always)]
+    fn pop_word(&mut self) -> u16 {
+        let word = self.pop_byte() as u16 | ((self.pop_byte() as u16) << 8);
+        word
+    }
+
     #[inline(always)]
     fn get_zero(&self) -> bool {
         self.zero
@@ -748,6 +765,18 @@ fn ld_de_u16(cpu: &mut Cpu) {
     cpu.set_de(word);
 }
 
+fn rla(cpu: &mut Cpu) {
+    let carry = cpu.get_carry();
+
+    cpu.set_carry(cpu.a & 0x80 == 0x80);
+
+    cpu.a = cpu.a << 1 | carry as u8;
+
+    cpu.set_sub(false);
+    cpu.set_zero(false);
+    cpu.set_half_carry(false);
+}
+
 fn ld_a_mde(cpu: &mut Cpu) {
     let byte = cpu.mmu.read(cpu.de());
     cpu.a = byte;
@@ -796,6 +825,20 @@ fn xor_a_a(cpu: &mut Cpu) {
     cpu.set_carry(false);
 }
 
+fn ret_nz(cpu: &mut Cpu) {
+    if cpu.get_zero() {
+        return;
+    }
+
+    cpu.pc = cpu.pop_word();
+    cpu.ticks = cpu.ticks.wrapping_add(12);
+}
+
+fn pop_bc(cpu: &mut Cpu) {
+    let word = cpu.pop_word();
+    cpu.set_bc(word);
+}
+
 fn push_bc(cpu: &mut Cpu) {
     cpu.push_word(cpu.bc());
 }
@@ -815,10 +858,31 @@ fn ld_mff00c_a(cpu: &mut Cpu) {
     cpu.mmu.write(0xff0c + cpu.c as u16, cpu.a);
 }
 
+fn rl_c(cpu: &mut Cpu) {
+    cpu.c = rl(cpu, cpu.c);
+}
+
 fn bit_7_h(cpu: &mut Cpu) {
     bit_h(cpu, 7);
 }
 
+/// Helper function that rotates (shifts) the given
+/// byte (probably from a register) and updates the
+/// proper flag registers.
+fn rl(cpu: &mut Cpu, byte: u8) -> u8 {
+    let carry = cpu.get_carry();
+
+    cpu.set_carry(byte & 0x80 == 0x80);
+
+    let result = (byte << 1) | carry as u8;
+
+    cpu.set_sub(false);
+    cpu.set_zero(result == 0);
+    cpu.set_half_carry(false);
+
+    result
+}
+
 /// Helper function to test one bit in a u8.
 /// Returns true if bit is 0.
 fn bit_zero(val: u8, bit: u8) -> bool {
@@ -826,7 +890,6 @@ fn bit_zero(val: u8, bit: u8) -> bool {
 }
 
 fn bit_h(cpu: &mut Cpu, bit: u8) {
-    println!("{}", cpu.h);
     cpu.set_sub(false);
     cpu.set_zero(bit_zero(cpu.h, bit));
     cpu.set_half_carry(true);
diff --git a/src/gb.rs b/src/gb.rs
index 8b2a4a9ba336fa437f98231df8774f5939c2d0d3..0d1da401e7d5accbad188be162a40ba3fbd203ad 100644
--- a/src/gb.rs
+++ b/src/gb.rs
@@ -26,8 +26,7 @@ impl GameBoy {
 
     pub fn load_boot(&mut self, path: &str) {
         let data = read_file(path);
-        self.cpu.mmu().write_buffer(0x0000, &data);
-        println!("LOADED BOOT")
+        self.cpu.mmu().write_boot(0x0000, &data);
     }
 
     pub fn load_boot_default(&mut self) {
diff --git a/src/mmu.rs b/src/mmu.rs
index d2adbdd72e823e029e714c2d3b3e71cb487aa502..3cd912f14b2ccbdd0b46867a18c4bf9fc66fa749 100644
--- a/src/mmu.rs
+++ b/src/mmu.rs
@@ -1,29 +1,103 @@
 use crate::ppu::Ppu;
 
+pub const BIOS_SIZE: usize = 256;
+pub const ROM_SIZE: usize = 32768;
 pub const RAM_SIZE: usize = 8192;
+pub const ERAM_SIZE: usize = 8192;
+pub const HRAM_SIZE: usize = 128;
 
 pub struct Mmu {
     ppu: Ppu,
+    boot_active: bool,
+    boot: [u8; BIOS_SIZE],
+    rom: [u8; ROM_SIZE],
     ram: [u8; RAM_SIZE],
+    eram: [u8; RAM_SIZE],
+    hram: [u8; HRAM_SIZE],
 }
 
 impl Mmu {
     pub fn new(ppu: Ppu) -> Mmu {
         Mmu {
             ppu: ppu,
+            boot_active: true,
+            boot: [0u8; BIOS_SIZE],
+            rom: [0u8; ROM_SIZE],
             ram: [0u8; RAM_SIZE],
+            eram: [0u8; ERAM_SIZE],
+            hram: [0u8; HRAM_SIZE],
         }
     }
 
     pub fn read(&self, addr: u16) -> u8 {
-        self.ram[addr as usize]
+        match addr & 0xf000 {
+            // BIOS
+            0x0000 => {
+                //@todo we still need to control if we're reading from boot
+                // if(MMU._inboot)
+                // {
+                // if(addr < 0x0100)
+                // return MMU._boot[addr];
+                // else if(Z80._r.pc == 0x0100)
+                // MMU._inboot = 0;
+                //}
+                //return MMU._rom[addr];
+                if self.boot_active {
+                    if addr < 0x0100 {
+                        return self.boot[addr as usize];
+                    }
+                    //else if self @todo implementar isto
+                }
+                self.rom[addr as usize]
+            }
+            // ROM0
+            0x1000 | 0x2000 | 0x3000 => self.rom[addr as usize],
+            // ROM1 (unbanked) (16k)
+            0x4000 | 0x5000 | 0x6000 | 0x7000 => self.rom[addr as usize],
+            // Graphics: VRAM (8k)
+            0x8000 | 0x9000 => {
+                println!("READING FROM VRAM");
+                self.ppu.vram[(addr & 0x1fff) as usize]
+            }
+            // External RAM (8k)
+            0xa000 | 0xb000 => {
+                println!("READING FROM ERAM");
+                self.eram[(addr & 0x1fff) as usize]
+            }
+            // Working RAM (8k)
+            0xc000 | 0xd000 => self.ram[(addr & 0x1fff) as usize],
+            // Working RAM shadow
+            0xe000 => {
+                println!("READING FROM RAM Shadow");
+                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 => {
+                    println!("READING FROM GPU OAM - NOT IMPLEMENTED");
+                    0x00
+                }
+                0xf00 => {
+                    if addr >= 0xff80 {
+                        self.ram[(addr & 0x7f) as usize]
+                    } else {
+                        println!("WRITING TO IO control");
+                        0x00
+                    }
+                }
+                addr => panic!("Reading from unknown location 0x{:04x}", addr),
+            },
+            addr => panic!("Reading from unknown location 0x{:04x}", addr),
+        }
     }
 
     pub fn write(&mut self, addr: u16, value: u8) {
         match addr & 0xf000 {
-            // BIOS
+            // BOOT
             0x0000 => {
-                println!("WRITING to BIOS")
+                println!("WRITING to BOOT")
             }
             // ROM0
             0x1000 | 0x2000 | 0x3000 => {
@@ -64,6 +138,7 @@ impl Mmu {
                 0xf00 => {
                     if addr >= 0xff80 {
                         println!("WRITING TO Zero page");
+                        self.hram[(addr & 0x7f) as usize] = value;
                     } else {
                         println!("WRITING TO IO control");
                     }
@@ -74,7 +149,11 @@ impl Mmu {
         }
     }
 
-    pub fn write_buffer(&mut self, addr: u16, buffer: &[u8]) {
+    pub fn write_boot(&mut self, addr: u16, buffer: &[u8]) {
+        self.boot[addr as usize..addr as usize + buffer.len()].clone_from_slice(buffer);
+    }
+
+    pub fn write_ram(&mut self, addr: u16, buffer: &[u8]) {
         self.ram[addr as usize..addr as usize + buffer.len()].clone_from_slice(buffer);
     }
 }