From f7644d740cb6586574df064d58291c53f48c5b1c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com>
Date: Sat, 2 Jul 2022 16:16:27 +0100
Subject: [PATCH] feat: new instructions

---
 examples/sdl/src/main.rs |    2 +-
 src/inst.rs              | 2049 +++++++++++++++++++-------------------
 2 files changed, 1037 insertions(+), 1014 deletions(-)

diff --git a/examples/sdl/src/main.rs b/examples/sdl/src/main.rs
index 8692010c..0b39bd95 100644
--- a/examples/sdl/src/main.rs
+++ b/examples/sdl/src/main.rs
@@ -75,7 +75,7 @@ fn main() {
 
     let mut game_boy = GameBoy::new();
     game_boy.load_boot_default();
-    game_boy.load_rom("../../res/roms/tetris.gb");
+    game_boy.load_rom("../../res/roms/firstwhite.gb");
 
     let mut counter = 0;
 
diff --git a/src/inst.rs b/src/inst.rs
index 19bc49b0..4567f108 100644
--- a/src/inst.rs
+++ b/src/inst.rs
@@ -1,1013 +1,1036 @@
-use crate::cpu::Cpu;
-
-pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [
-    // 0x0 opcodes
-    (nop, 4, "NOP"),
-    (ld_bc_u16, 12, "LD BC, u16"),
-    (ld_mbc_a, 8, "LD [BC], A"),
-    (inc_bc, 8, "INC BC"),
-    (inc_b, 4, "INC B"),
-    (dec_b, 4, "DEC B"),
-    (ld_b_u8, 8, "LD B, u8"),
-    (rlca, 4, "RLCA"),
-    (ld_mu16_sp, 20, "LD [u16], SP"),
-    (add_hl_bc, 8, "ADD HL, BC"),
-    (noimpl, 4, "! UNIMP !"),
-    (dec_bc, 8, "DEC BC"),
-    (inc_c, 4, "INC C"),
-    (dec_c, 4, "DEC C"),
-    (ld_c_u8, 8, "LD C, u8"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x1 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (ld_de_u16, 12, "LD DE, u16"),
-    (noimpl, 4, "! UNIMP !"),
-    (inc_de, 8, "INC DE"),
-    (noimpl, 4, "! UNIMP !"),
-    (dec_d, 4, "DEC D"),
-    (ld_d_u8, 8, "LD D, u8"),
-    (rla, 4, "RLA"),
-    (jr_i8, 12, "JR i8"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_a_mde, 8, "LD A, [DE]"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (dec_e, 4, "DEC E"),
-    (ld_e_u8, 8, "LD E, u8"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x2 opcodes
-    (jr_nz_i8, 8, "JR NZ, i8"),
-    (ld_hl_u16, 12, "LD HL, u16"),
-    (ld_mhli_a, 8, "LD [HL+], A"),
-    (inc_hl, 8, "INC HL"),
-    (inc_h, 4, "INC H"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (jr_z_i8, 8, "JR Z, i8"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_a_mhli, 8, "LD A, [HL+] "),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_l_u8, 8, "LD L, u8"),
-    (cpl, 4, "CPL"),
-    // 0x3 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (ld_sp_u16, 12, "LD SP, u16"),
-    (ld_mhld_a, 8, "LD [HL-], A"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_mhl_u8, 12, "LD [HL], u8 "),
-    (scf, 4, "SCF"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (dec_a, 4, "DEC A"),
-    (ld_a_u8, 8, "LD A, u8"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x4 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_b_h, 4, "LD B, H"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_b_a, 4, "LD B, A"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_c_a, 4, "LD C, A"),
-    // 0x5 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_d_a, 4, "LD D, A"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x6 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_h_a, 4, "LD H, A"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x7 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_mhl_a, 8, "LD [HL], A"),
-    (ld_a_b, 4, "LD A, B"),
-    (ld_a_c, 4, "LD A, C"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_a_e, 4, "LD A, E"),
-    (ld_a_h, 4, "LD A, H"),
-    (ld_a_l, 4, "LD A, L"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x8 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (add_a_mhl, 8, "ADD A, [HL]"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x9 opcodes
-    (sub_a_b, 4, "SUB A, B"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0xa opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (and_a_c, 4, "AND A, C"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (xor_a_c, 4, "XOR A, C"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (xor_a_a, 4, "XOR A, A"),
-    // 0xb opcodes
-    (or_a_b, 4, "OR A, B"),
-    (or_a_c, 4, "OR A, C"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (cp_a_mhl, 8, "CP A, [HL]"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0xc opcodes
-    (ret_nz, 8, "RET NZ"),
-    (pop_bc, 12, "POP BC"),
-    (noimpl, 4, "! UNIMP !"),
-    (jp_u16, 16, "JP u16"),
-    (noimpl, 4, "! UNIMP !"),
-    (push_bc, 16, "PUSH BC"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ret, 16, "RET"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (call_u16, 24, "CALL u16"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0xd opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0xe opcodes
-    (ld_mff00u8_a, 12, "LD [FF00+u8], A"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_mff00c_a, 8, "LD [FF00+C], A"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (and_a_u8, 8, "AND A, u8"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ld_mu16_a, 16, "LD [u16], A"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (rst_18h, 16, "RST 18h"),
-    // 0xf opcodes
-    (ld_a_mff00u8, 12, "LD A, [FF00+u8]"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (di, 4, "DI"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (ei, 4, "EI"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (cp_a_u8, 8, "CP A, u8"),
-    (rst_38h, 16, "RST 38h"),
-];
-
-pub const BITWISE: [(fn(&mut Cpu), u8, &'static str); 176] = [
-    // 0x0 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x1 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (rl_c, 8, "RL C"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x2 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x3 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (swap_a, 8, "SWAP A"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x4 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x5 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x6 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x7 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (bit_7_h, 8, "BIT 7, H"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x8 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0x9 opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    // 0xa opcodes
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-    (noimpl, 4, "! UNIMP !"),
-];
-
-fn nop(_cpu: &mut Cpu) {}
-
-fn noimpl(_cpu: &mut Cpu) {
-    todo!("Instruction not implemented");
-}
-
-fn ld_bc_u16(cpu: &mut Cpu) {
-    let word = cpu.read_u16();
-    cpu.set_bc(word);
-}
-
-fn ld_mbc_a(cpu: &mut Cpu) {
-    cpu.mmu.write(cpu.bc(), cpu.a);
-}
-
-fn inc_bc(cpu: &mut Cpu) {
-    cpu.set_bc(cpu.bc().wrapping_add(1));
-}
-
-fn inc_b(cpu: &mut Cpu) {
-    let value = cpu.b.wrapping_add(1);
-
-    cpu.set_sub(false);
-    cpu.set_zero(value == 0);
-    cpu.set_half_carry((value & 0xf) == 0xf);
-
-    cpu.b = value;
-}
-
-fn dec_b(cpu: &mut Cpu) {
-    let b = cpu.b;
-    let value = b.wrapping_sub(1);
-
-    cpu.set_sub(true);
-    cpu.set_zero(value == 0);
-    cpu.set_half_carry((b & 0xf) == 0xf);
-
-    cpu.b = value;
-}
-
-fn ld_b_u8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-    cpu.b = byte;
-}
-
-fn rlca(cpu: &mut Cpu) {
-    let carry = cpu.a >> 7;
-
-    cpu.a = cpu.a << 1 | carry;
-
-    cpu.set_sub(false);
-    cpu.set_zero(false);
-    cpu.set_half_carry(false);
-    cpu.set_carry(carry == 1);
-}
-
-fn ld_mu16_sp(cpu: &mut Cpu) {
-    let word = cpu.read_u16();
-    cpu.mmu.write(word, cpu.sp as u8);
-    cpu.mmu.write(word + 1, (cpu.sp >> 8) as u8);
-}
-
-fn add_hl_bc(cpu: &mut Cpu) {
-    let value = add_u16_u16(cpu, cpu.hl(), cpu.bc());
-    cpu.set_hl(value);
-}
-
-fn dec_bc(cpu: &mut Cpu) {
-    cpu.set_bc(cpu.bc().wrapping_sub(1));
-}
-
-fn inc_c(cpu: &mut Cpu) {
-    let value = cpu.c.wrapping_add(1);
-
-    cpu.set_sub(false);
-    cpu.set_zero(value == 0);
-    cpu.set_half_carry((value & 0xf) == 0xf);
-
-    cpu.c = value;
-}
-
-fn dec_c(cpu: &mut Cpu) {
-    let c = cpu.c;
-    let value = c.wrapping_sub(1);
-
-    cpu.set_sub(true);
-    cpu.set_zero(value == 0);
-    cpu.set_half_carry((c & 0xf) == 0xf);
-
-    cpu.c = value;
-}
-
-fn ld_c_u8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-    cpu.c = byte;
-}
-
-fn ld_de_u16(cpu: &mut Cpu) {
-    let word = cpu.read_u16();
-    cpu.set_de(word);
-}
-
-fn inc_de(cpu: &mut Cpu) {
-    cpu.set_de(cpu.de().wrapping_add(1));
-}
-
-fn dec_d(cpu: &mut Cpu) {
-    let d = cpu.d;
-    let value = d.wrapping_sub(1);
-
-    cpu.set_sub(true);
-    cpu.set_zero(value == 0);
-    cpu.set_half_carry((d & 0xf) == 0xf);
-
-    cpu.d = value;
-}
-
-fn ld_d_u8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-    cpu.d = byte;
-}
-
-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 jr_i8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8() as i8;
-    cpu.pc = (cpu.pc as i16).wrapping_add(byte as i16) as u16;
-}
-
-fn ld_a_mde(cpu: &mut Cpu) {
-    let byte = cpu.mmu.read(cpu.de());
-    cpu.a = byte;
-}
-
-fn dec_e(cpu: &mut Cpu) {
-    let e = cpu.e;
-    let value = e.wrapping_sub(1);
-
-    cpu.set_sub(true);
-    cpu.set_zero(value == 0);
-    cpu.set_half_carry((e & 0xf) == 0xf);
-
-    cpu.e = value;
-}
-
-fn ld_e_u8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-    cpu.e = byte;
-}
-
-fn jr_nz_i8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8() as i8;
-
-    if cpu.get_zero() {
-        return;
-    }
-
-    cpu.pc = (cpu.pc as i16).wrapping_add(byte as i16) as u16;
-    cpu.ticks = cpu.ticks.wrapping_add(4);
-}
-
-fn ld_hl_u16(cpu: &mut Cpu) {
-    let word = cpu.read_u16();
-    cpu.set_hl(word);
-}
-
-fn ld_mhli_a(cpu: &mut Cpu) {
-    cpu.mmu.write(cpu.hl(), cpu.a);
-    cpu.set_hl(cpu.hl().wrapping_add(1));
-}
-
-fn inc_hl(cpu: &mut Cpu) {
-    cpu.set_hl(cpu.hl().wrapping_add(1));
-}
-
-fn inc_h(cpu: &mut Cpu) {
-    let value = cpu.h.wrapping_add(1);
-
-    cpu.set_sub(false);
-    cpu.set_zero(value == 0);
-    cpu.set_half_carry((value & 0xf) == 0xf);
-
-    cpu.h = value;
-}
-
-fn jr_z_i8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8() as i8;
-
-    if !cpu.get_zero() {
-        return;
-    }
-
-    cpu.pc = (cpu.pc as i16).wrapping_add(byte as i16) as u16;
-    cpu.ticks = cpu.ticks.wrapping_add(4);
-}
-
-fn ld_a_mhli(cpu: &mut Cpu) {
-    let byte = cpu.mmu.read(cpu.hl());
-    cpu.a = byte;
-    cpu.set_hl(cpu.hl().wrapping_add(1));
-}
-
-fn ld_l_u8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-    cpu.l = byte;
-}
-
-fn cpl(cpu: &mut Cpu) {
-    cpu.a = !cpu.a;
-
-    cpu.set_sub(true);
-    cpu.set_half_carry(true);
-}
-
-fn ld_sp_u16(cpu: &mut Cpu) {
-    cpu.sp = cpu.read_u16();
-}
-
-fn ld_mhld_a(cpu: &mut Cpu) {
-    cpu.mmu.write(cpu.hl(), cpu.a);
-    cpu.set_hl(cpu.hl().wrapping_sub(1));
-}
-
-fn ld_mhl_u8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-    cpu.mmu.write(cpu.hl(), byte);
-}
-
-fn scf(cpu: &mut Cpu) {
-    cpu.set_sub(false);
-    cpu.set_half_carry(false);
-    cpu.set_carry(true);
-}
-
-fn dec_a(cpu: &mut Cpu) {
-    let a = cpu.a;
-    let value = a.wrapping_sub(1);
-
-    cpu.set_sub(true);
-    cpu.set_zero(value == 0);
-    cpu.set_half_carry((a & 0xf) == 0xf);
-
-    cpu.a = value;
-}
-
-fn ld_a_u8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-    cpu.a = byte;
-}
-
-fn ld_b_h(cpu: &mut Cpu) {
-    cpu.b = cpu.h;
-}
-
-fn ld_b_a(cpu: &mut Cpu) {
-    cpu.b = cpu.a;
-}
-
-fn ld_c_a(cpu: &mut Cpu) {
-    cpu.c = cpu.a;
-}
-
-fn ld_d_a(cpu: &mut Cpu) {
-    cpu.d = cpu.a;
-}
-
-fn ld_h_a(cpu: &mut Cpu) {
-    cpu.h = cpu.a;
-}
-
-fn ld_mhl_a(cpu: &mut Cpu) {
-    cpu.mmu.write(cpu.hl(), cpu.a);
-}
-
-fn ld_a_b(cpu: &mut Cpu) {
-    cpu.a = cpu.b;
-}
-
-fn ld_a_c(cpu: &mut Cpu) {
-    cpu.a = cpu.c;
-}
-
-fn ld_a_e(cpu: &mut Cpu) {
-    cpu.a = cpu.e;
-}
-
-fn ld_a_h(cpu: &mut Cpu) {
-    cpu.a = cpu.h;
-}
-
-fn ld_a_l(cpu: &mut Cpu) {
-    cpu.a = cpu.l;
-}
-
-fn add_a_mhl(cpu: &mut Cpu) {
-    let byte = cpu.mmu.read(cpu.hl());
-    cpu.a = add_set_flags(cpu, cpu.a, byte);
-}
-
-fn sub_a_b(cpu: &mut Cpu) {
-    cpu.a = sub_set_flags(cpu, cpu.a, cpu.b);
-}
-
-fn and_a_c(cpu: &mut Cpu) {
-    cpu.a &= cpu.c;
-
-    cpu.set_sub(false);
-    cpu.set_zero(cpu.a == 0);
-    cpu.set_half_carry(true);
-    cpu.set_carry(false);
-}
-
-fn xor_a_c(cpu: &mut Cpu) {
-    cpu.a ^= cpu.c;
-
-    cpu.set_sub(false);
-    cpu.set_zero(cpu.a == 0);
-    cpu.set_half_carry(false);
-    cpu.set_carry(false);
-}
-
-fn xor_a_a(cpu: &mut Cpu) {
-    cpu.a ^= cpu.a;
-
-    cpu.set_sub(false);
-    cpu.set_zero(cpu.a == 0);
-    cpu.set_half_carry(false);
-    cpu.set_carry(false);
-}
-
-fn or_a_b(cpu: &mut Cpu) {
-    cpu.a |= cpu.b;
-
-    cpu.set_sub(false);
-    cpu.set_zero(cpu.a == 0);
-    cpu.set_half_carry(false);
-    cpu.set_carry(false);
-}
-
-fn or_a_c(cpu: &mut Cpu) {
-    cpu.a |= cpu.c;
-
-    cpu.set_sub(false);
-    cpu.set_zero(cpu.a == 0);
-    cpu.set_half_carry(false);
-    cpu.set_carry(false);
-}
-
-fn cp_a_mhl(cpu: &mut Cpu) {
-    let byte = cpu.mmu.read(cpu.hl());
-    sub_set_flags(cpu, cpu.a, byte);
-}
-
-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 jp_u16(cpu: &mut Cpu) {
-    let word = cpu.read_u16();
-    cpu.pc = word;
-}
-
-fn push_bc(cpu: &mut Cpu) {
-    cpu.push_word(cpu.bc());
-}
-
-fn ret(cpu: &mut Cpu) {
-    cpu.pc = cpu.pop_word();
-}
-
-fn call_u16(cpu: &mut Cpu) {
-    let word = cpu.read_u16();
-    cpu.push_word(cpu.pc);
-    cpu.pc = word;
-}
-
-fn ld_mff00u8_a(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-    cpu.mmu.write(0xff00 + byte as u16, cpu.a);
-}
-
-fn ld_mff00c_a(cpu: &mut Cpu) {
-    cpu.mmu.write(0xff00 + cpu.c as u16, cpu.a);
-}
-
-fn and_a_u8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-
-    cpu.a &= byte;
-
-    cpu.set_sub(false);
-    cpu.set_zero(cpu.a == 0);
-    cpu.set_half_carry(true);
-    cpu.set_carry(false);
-}
-
-fn ld_mu16_a(cpu: &mut Cpu) {
-    let word = cpu.read_u16();
-    cpu.mmu.write(word, cpu.a);
-}
-
-fn rst_18h(cpu: &mut Cpu) {
-    rst(cpu, 0x0018);
-}
-
-fn ld_a_mff00u8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-    cpu.a = cpu.mmu.read(0xff00 + byte as u16);
-}
-
-fn di(cpu: &mut Cpu) {
-    cpu.disable_int();
-}
-
-fn ei(cpu: &mut Cpu) {
-    cpu.enable_int();
-}
-
-fn cp_a_u8(cpu: &mut Cpu) {
-    let byte = cpu.read_u8();
-    sub_set_flags(cpu, cpu.a, byte);
-}
-
-fn rst_38h(cpu: &mut Cpu) {
-    rst(cpu, 0x0038);
-}
-
-fn rl_c(cpu: &mut Cpu) {
-    cpu.c = rl(cpu, cpu.c);
-}
-
-fn swap_a(cpu: &mut Cpu) {
-    cpu.a = swap(cpu, cpu.a)
-}
-
-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 {
-    (val & (1u8 << (bit as usize))) == 0
-}
-
-fn bit_h(cpu: &mut Cpu, bit: u8) {
-    cpu.set_sub(false);
-    cpu.set_zero(bit_zero(cpu.h, bit));
-    cpu.set_half_carry(true);
-}
-
-fn add_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
-    let first = first as u32;
-    let second = second as u32;
-
-    let value = first.wrapping_add(second);
-    let value_b = value as u8;
-
-    cpu.set_sub(false);
-    cpu.set_zero(value_b == 0);
-    cpu.set_half_carry((first ^ second ^ value) & 0x10 == 0x10);
-    cpu.set_carry(value & 0x100 == 0x100);
-
-    value_b
-}
-
-fn sub_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
-    let first = first as u32;
-    let second = second as u32;
-
-    let value = first.wrapping_sub(second);
-    let value_b = value as u8;
-
-    cpu.set_sub(true);
-    cpu.set_zero(value_b == 0);
-    cpu.set_half_carry((first ^ second ^ value) & 0x10 == 0x10);
-    cpu.set_carry(value & 0x100 == 0x100);
-
-    value_b
-}
-
-fn add_u16_u16(cpu: &mut Cpu, first: u16, second: u16) -> u16 {
-    let first = first as u32;
-    let second = second as u32;
-    let value = first.wrapping_add(second);
-
-    cpu.set_sub(false);
-    cpu.set_half_carry((first ^ second ^ value) & 0x1000 == 0x1000);
-    cpu.set_carry(value & 0x10000 == 0x10000);
-
-    value as u16
-}
-
-fn swap(cpu: &mut Cpu, value: u8) -> u8 {
-    cpu.set_sub(false);
-    cpu.set_zero(value == 0);
-    cpu.set_half_carry(false);
-    cpu.set_carry(false);
-
-    (value << 4) | (value >> 4)
-}
-
-/// Helper function for RST instructions, pushes the
-/// current PC to the stack and jumps to the provided
-/// address.
-fn rst(cpu: &mut Cpu, addr: u16) {
-    cpu.push_word(cpu.pc);
-    cpu.pc = addr;
-}
+use crate::cpu::Cpu;
+
+pub const INSTRUCTIONS: [(fn(&mut Cpu), u8, &'static str); 256] = [
+    // 0x0 opcodes
+    (nop, 4, "NOP"),
+    (ld_bc_u16, 12, "LD BC, u16"),
+    (ld_mbc_a, 8, "LD [BC], A"),
+    (inc_bc, 8, "INC BC"),
+    (inc_b, 4, "INC B"),
+    (dec_b, 4, "DEC B"),
+    (ld_b_u8, 8, "LD B, u8"),
+    (rlca, 4, "RLCA"),
+    (ld_mu16_sp, 20, "LD [u16], SP"),
+    (add_hl_bc, 8, "ADD HL, BC"),
+    (noimpl, 4, "! UNIMP !"),
+    (dec_bc, 8, "DEC BC"),
+    (inc_c, 4, "INC C"),
+    (dec_c, 4, "DEC C"),
+    (ld_c_u8, 8, "LD C, u8"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x1 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (ld_de_u16, 12, "LD DE, u16"),
+    (noimpl, 4, "! UNIMP !"),
+    (inc_de, 8, "INC DE"),
+    (noimpl, 4, "! UNIMP !"),
+    (dec_d, 4, "DEC D"),
+    (ld_d_u8, 8, "LD D, u8"),
+    (rla, 4, "RLA"),
+    (jr_i8, 12, "JR i8"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_a_mde, 8, "LD A, [DE]"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (dec_e, 4, "DEC E"),
+    (ld_e_u8, 8, "LD E, u8"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x2 opcodes
+    (jr_nz_i8, 8, "JR NZ, i8"),
+    (ld_hl_u16, 12, "LD HL, u16"),
+    (ld_mhli_a, 8, "LD [HL+], A"),
+    (inc_hl, 8, "INC HL"),
+    (inc_h, 4, "INC H"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (jr_z_i8, 8, "JR Z, i8"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_a_mhli, 8, "LD A, [HL+] "),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_l_u8, 8, "LD L, u8"),
+    (cpl, 4, "CPL"),
+    // 0x3 opcodes
+    (jr_nc_i8, 8, "JR NC, i8"),
+    (ld_sp_u16, 12, "LD SP, u16"),
+    (ld_mhld_a, 8, "LD [HL-], A"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_mhl_u8, 12, "LD [HL], u8 "),
+    (scf, 4, "SCF"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (dec_a, 4, "DEC A"),
+    (ld_a_u8, 8, "LD A, u8"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x4 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_b_h, 4, "LD B, H"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_b_a, 4, "LD B, A"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_c_a, 4, "LD C, A"),
+    // 0x5 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_d_a, 4, "LD D, A"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x6 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_h_a, 4, "LD H, A"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x7 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_mhl_a, 8, "LD [HL], A"),
+    (ld_a_b, 4, "LD A, B"),
+    (ld_a_c, 4, "LD A, C"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_a_e, 4, "LD A, E"),
+    (ld_a_h, 4, "LD A, H"),
+    (ld_a_l, 4, "LD A, L"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x8 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (add_a_mhl, 8, "ADD A, [HL]"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x9 opcodes
+    (sub_a_b, 4, "SUB A, B"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0xa opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (and_a_c, 4, "AND A, C"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (xor_a_c, 4, "XOR A, C"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (xor_a_a, 4, "XOR A, A"),
+    // 0xb opcodes
+    (or_a_b, 4, "OR A, B"),
+    (or_a_c, 4, "OR A, C"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (cp_a_mhl, 8, "CP A, [HL]"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0xc opcodes
+    (ret_nz, 8, "RET NZ"),
+    (pop_bc, 12, "POP BC"),
+    (noimpl, 4, "! UNIMP !"),
+    (jp_u16, 16, "JP u16"),
+    (call_nz_u16, 12, "CALL NZ, u16"),
+    (push_bc, 16, "PUSH BC"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ret, 16, "RET"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (call_u16, 24, "CALL u16"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0xd opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0xe opcodes
+    (ld_mff00u8_a, 12, "LD [FF00+u8], A"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_mff00c_a, 8, "LD [FF00+C], A"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (and_a_u8, 8, "AND A, u8"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ld_mu16_a, 16, "LD [u16], A"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (rst_18h, 16, "RST 18h"),
+    // 0xf opcodes
+    (ld_a_mff00u8, 12, "LD A, [FF00+u8]"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (di, 4, "DI"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (ei, 4, "EI"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (cp_a_u8, 8, "CP A, u8"),
+    (rst_38h, 16, "RST 38h"),
+];
+
+pub const BITWISE: [(fn(&mut Cpu), u8, &'static str); 176] = [
+    // 0x0 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x1 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (rl_c, 8, "RL C"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x2 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x3 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (swap_a, 8, "SWAP A"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x4 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x5 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x6 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x7 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (bit_7_h, 8, "BIT 7, H"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x8 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0x9 opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    // 0xa opcodes
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+    (noimpl, 4, "! UNIMP !"),
+];
+
+fn nop(_cpu: &mut Cpu) {}
+
+fn noimpl(_cpu: &mut Cpu) {
+    todo!("Instruction not implemented");
+}
+
+fn ld_bc_u16(cpu: &mut Cpu) {
+    let word = cpu.read_u16();
+    cpu.set_bc(word);
+}
+
+fn ld_mbc_a(cpu: &mut Cpu) {
+    cpu.mmu.write(cpu.bc(), cpu.a);
+}
+
+fn inc_bc(cpu: &mut Cpu) {
+    cpu.set_bc(cpu.bc().wrapping_add(1));
+}
+
+fn inc_b(cpu: &mut Cpu) {
+    let value = cpu.b.wrapping_add(1);
+
+    cpu.set_sub(false);
+    cpu.set_zero(value == 0);
+    cpu.set_half_carry((value & 0xf) == 0xf);
+
+    cpu.b = value;
+}
+
+fn dec_b(cpu: &mut Cpu) {
+    let b = cpu.b;
+    let value = b.wrapping_sub(1);
+
+    cpu.set_sub(true);
+    cpu.set_zero(value == 0);
+    cpu.set_half_carry((b & 0xf) == 0xf);
+
+    cpu.b = value;
+}
+
+fn ld_b_u8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+    cpu.b = byte;
+}
+
+fn rlca(cpu: &mut Cpu) {
+    let carry = cpu.a >> 7;
+
+    cpu.a = cpu.a << 1 | carry;
+
+    cpu.set_sub(false);
+    cpu.set_zero(false);
+    cpu.set_half_carry(false);
+    cpu.set_carry(carry == 1);
+}
+
+fn ld_mu16_sp(cpu: &mut Cpu) {
+    let word = cpu.read_u16();
+    cpu.mmu.write(word, cpu.sp as u8);
+    cpu.mmu.write(word + 1, (cpu.sp >> 8) as u8);
+}
+
+fn add_hl_bc(cpu: &mut Cpu) {
+    let value = add_u16_u16(cpu, cpu.hl(), cpu.bc());
+    cpu.set_hl(value);
+}
+
+fn dec_bc(cpu: &mut Cpu) {
+    cpu.set_bc(cpu.bc().wrapping_sub(1));
+}
+
+fn inc_c(cpu: &mut Cpu) {
+    let value = cpu.c.wrapping_add(1);
+
+    cpu.set_sub(false);
+    cpu.set_zero(value == 0);
+    cpu.set_half_carry((value & 0xf) == 0xf);
+
+    cpu.c = value;
+}
+
+fn dec_c(cpu: &mut Cpu) {
+    let c = cpu.c;
+    let value = c.wrapping_sub(1);
+
+    cpu.set_sub(true);
+    cpu.set_zero(value == 0);
+    cpu.set_half_carry((c & 0xf) == 0xf);
+
+    cpu.c = value;
+}
+
+fn ld_c_u8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+    cpu.c = byte;
+}
+
+fn ld_de_u16(cpu: &mut Cpu) {
+    let word = cpu.read_u16();
+    cpu.set_de(word);
+}
+
+fn inc_de(cpu: &mut Cpu) {
+    cpu.set_de(cpu.de().wrapping_add(1));
+}
+
+fn dec_d(cpu: &mut Cpu) {
+    let d = cpu.d;
+    let value = d.wrapping_sub(1);
+
+    cpu.set_sub(true);
+    cpu.set_zero(value == 0);
+    cpu.set_half_carry((d & 0xf) == 0xf);
+
+    cpu.d = value;
+}
+
+fn ld_d_u8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+    cpu.d = byte;
+}
+
+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 jr_i8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8() as i8;
+    cpu.pc = (cpu.pc as i16).wrapping_add(byte as i16) as u16;
+}
+
+fn ld_a_mde(cpu: &mut Cpu) {
+    let byte = cpu.mmu.read(cpu.de());
+    cpu.a = byte;
+}
+
+fn dec_e(cpu: &mut Cpu) {
+    let e = cpu.e;
+    let value = e.wrapping_sub(1);
+
+    cpu.set_sub(true);
+    cpu.set_zero(value == 0);
+    cpu.set_half_carry((e & 0xf) == 0xf);
+
+    cpu.e = value;
+}
+
+fn ld_e_u8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+    cpu.e = byte;
+}
+
+fn jr_nz_i8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8() as i8;
+
+    if cpu.get_zero() {
+        return;
+    }
+
+    cpu.pc = (cpu.pc as i16).wrapping_add(byte as i16) as u16;
+    cpu.ticks = cpu.ticks.wrapping_add(4);
+}
+
+fn ld_hl_u16(cpu: &mut Cpu) {
+    let word = cpu.read_u16();
+    cpu.set_hl(word);
+}
+
+fn ld_mhli_a(cpu: &mut Cpu) {
+    cpu.mmu.write(cpu.hl(), cpu.a);
+    cpu.set_hl(cpu.hl().wrapping_add(1));
+}
+
+fn inc_hl(cpu: &mut Cpu) {
+    cpu.set_hl(cpu.hl().wrapping_add(1));
+}
+
+fn inc_h(cpu: &mut Cpu) {
+    let value = cpu.h.wrapping_add(1);
+
+    cpu.set_sub(false);
+    cpu.set_zero(value == 0);
+    cpu.set_half_carry((value & 0xf) == 0xf);
+
+    cpu.h = value;
+}
+
+fn jr_z_i8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8() as i8;
+
+    if !cpu.get_zero() {
+        return;
+    }
+
+    cpu.pc = (cpu.pc as i16).wrapping_add(byte as i16) as u16;
+    cpu.ticks = cpu.ticks.wrapping_add(4);
+}
+
+fn ld_a_mhli(cpu: &mut Cpu) {
+    let byte = cpu.mmu.read(cpu.hl());
+    cpu.a = byte;
+    cpu.set_hl(cpu.hl().wrapping_add(1));
+}
+
+fn ld_l_u8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+    cpu.l = byte;
+}
+
+fn cpl(cpu: &mut Cpu) {
+    cpu.a = !cpu.a;
+
+    cpu.set_sub(true);
+    cpu.set_half_carry(true);
+}
+
+fn ld_sp_u16(cpu: &mut Cpu) {
+    cpu.sp = cpu.read_u16();
+}
+
+fn jr_nc_i8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+
+    if cpu.get_carry() {
+        return;
+    }
+
+    cpu.pc = (cpu.pc as i16).wrapping_add(byte as i16) as u16;
+    cpu.ticks = cpu.ticks.wrapping_add(4);
+}
+
+fn ld_mhld_a(cpu: &mut Cpu) {
+    cpu.mmu.write(cpu.hl(), cpu.a);
+    cpu.set_hl(cpu.hl().wrapping_sub(1));
+}
+
+fn ld_mhl_u8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+    cpu.mmu.write(cpu.hl(), byte);
+}
+
+fn scf(cpu: &mut Cpu) {
+    cpu.set_sub(false);
+    cpu.set_half_carry(false);
+    cpu.set_carry(true);
+}
+
+fn dec_a(cpu: &mut Cpu) {
+    let a = cpu.a;
+    let value = a.wrapping_sub(1);
+
+    cpu.set_sub(true);
+    cpu.set_zero(value == 0);
+    cpu.set_half_carry((a & 0xf) == 0xf);
+
+    cpu.a = value;
+}
+
+fn ld_a_u8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+    cpu.a = byte;
+}
+
+fn ld_b_h(cpu: &mut Cpu) {
+    cpu.b = cpu.h;
+}
+
+fn ld_b_a(cpu: &mut Cpu) {
+    cpu.b = cpu.a;
+}
+
+fn ld_c_a(cpu: &mut Cpu) {
+    cpu.c = cpu.a;
+}
+
+fn ld_d_a(cpu: &mut Cpu) {
+    cpu.d = cpu.a;
+}
+
+fn ld_h_a(cpu: &mut Cpu) {
+    cpu.h = cpu.a;
+}
+
+fn ld_mhl_a(cpu: &mut Cpu) {
+    cpu.mmu.write(cpu.hl(), cpu.a);
+}
+
+fn ld_a_b(cpu: &mut Cpu) {
+    cpu.a = cpu.b;
+}
+
+fn ld_a_c(cpu: &mut Cpu) {
+    cpu.a = cpu.c;
+}
+
+fn ld_a_e(cpu: &mut Cpu) {
+    cpu.a = cpu.e;
+}
+
+fn ld_a_h(cpu: &mut Cpu) {
+    cpu.a = cpu.h;
+}
+
+fn ld_a_l(cpu: &mut Cpu) {
+    cpu.a = cpu.l;
+}
+
+fn add_a_mhl(cpu: &mut Cpu) {
+    let byte = cpu.mmu.read(cpu.hl());
+    cpu.a = add_set_flags(cpu, cpu.a, byte);
+}
+
+fn sub_a_b(cpu: &mut Cpu) {
+    cpu.a = sub_set_flags(cpu, cpu.a, cpu.b);
+}
+
+fn and_a_c(cpu: &mut Cpu) {
+    cpu.a &= cpu.c;
+
+    cpu.set_sub(false);
+    cpu.set_zero(cpu.a == 0);
+    cpu.set_half_carry(true);
+    cpu.set_carry(false);
+}
+
+fn xor_a_c(cpu: &mut Cpu) {
+    cpu.a ^= cpu.c;
+
+    cpu.set_sub(false);
+    cpu.set_zero(cpu.a == 0);
+    cpu.set_half_carry(false);
+    cpu.set_carry(false);
+}
+
+fn xor_a_a(cpu: &mut Cpu) {
+    cpu.a ^= cpu.a;
+
+    cpu.set_sub(false);
+    cpu.set_zero(cpu.a == 0);
+    cpu.set_half_carry(false);
+    cpu.set_carry(false);
+}
+
+fn or_a_b(cpu: &mut Cpu) {
+    cpu.a |= cpu.b;
+
+    cpu.set_sub(false);
+    cpu.set_zero(cpu.a == 0);
+    cpu.set_half_carry(false);
+    cpu.set_carry(false);
+}
+
+fn or_a_c(cpu: &mut Cpu) {
+    cpu.a |= cpu.c;
+
+    cpu.set_sub(false);
+    cpu.set_zero(cpu.a == 0);
+    cpu.set_half_carry(false);
+    cpu.set_carry(false);
+}
+
+fn cp_a_mhl(cpu: &mut Cpu) {
+    let byte = cpu.mmu.read(cpu.hl());
+    sub_set_flags(cpu, cpu.a, byte);
+}
+
+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 jp_u16(cpu: &mut Cpu) {
+    let word = cpu.read_u16();
+    cpu.pc = word;
+}
+
+fn call_nz_u16(cpu: &mut Cpu) {
+    let word = cpu.read_u16();
+
+    if cpu.get_zero() {
+        return;
+    }
+
+    cpu.push_word(cpu.pc);
+    cpu.pc = word;
+    cpu.ticks = cpu.ticks.wrapping_add(12);
+}
+
+fn push_bc(cpu: &mut Cpu) {
+    cpu.push_word(cpu.bc());
+}
+
+fn ret(cpu: &mut Cpu) {
+    cpu.pc = cpu.pop_word();
+}
+
+fn call_u16(cpu: &mut Cpu) {
+    let word = cpu.read_u16();
+    cpu.push_word(cpu.pc);
+    cpu.pc = word;
+}
+
+fn ld_mff00u8_a(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+    cpu.mmu.write(0xff00 + byte as u16, cpu.a);
+}
+
+fn ld_mff00c_a(cpu: &mut Cpu) {
+    cpu.mmu.write(0xff00 + cpu.c as u16, cpu.a);
+}
+
+fn and_a_u8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+
+    cpu.a &= byte;
+
+    cpu.set_sub(false);
+    cpu.set_zero(cpu.a == 0);
+    cpu.set_half_carry(true);
+    cpu.set_carry(false);
+}
+
+fn ld_mu16_a(cpu: &mut Cpu) {
+    let word = cpu.read_u16();
+    cpu.mmu.write(word, cpu.a);
+}
+
+fn rst_18h(cpu: &mut Cpu) {
+    rst(cpu, 0x0018);
+}
+
+fn ld_a_mff00u8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+    cpu.a = cpu.mmu.read(0xff00 + byte as u16);
+}
+
+fn di(cpu: &mut Cpu) {
+    cpu.disable_int();
+}
+
+fn ei(cpu: &mut Cpu) {
+    cpu.enable_int();
+}
+
+fn cp_a_u8(cpu: &mut Cpu) {
+    let byte = cpu.read_u8();
+    sub_set_flags(cpu, cpu.a, byte);
+}
+
+fn rst_38h(cpu: &mut Cpu) {
+    rst(cpu, 0x0038);
+}
+
+fn rl_c(cpu: &mut Cpu) {
+    cpu.c = rl(cpu, cpu.c);
+}
+
+fn swap_a(cpu: &mut Cpu) {
+    cpu.a = swap(cpu, cpu.a)
+}
+
+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 {
+    (val & (1u8 << (bit as usize))) == 0
+}
+
+fn bit_h(cpu: &mut Cpu, bit: u8) {
+    cpu.set_sub(false);
+    cpu.set_zero(bit_zero(cpu.h, bit));
+    cpu.set_half_carry(true);
+}
+
+fn add_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
+    let first = first as u32;
+    let second = second as u32;
+
+    let value = first.wrapping_add(second);
+    let value_b = value as u8;
+
+    cpu.set_sub(false);
+    cpu.set_zero(value_b == 0);
+    cpu.set_half_carry((first ^ second ^ value) & 0x10 == 0x10);
+    cpu.set_carry(value & 0x100 == 0x100);
+
+    value_b
+}
+
+fn sub_set_flags(cpu: &mut Cpu, first: u8, second: u8) -> u8 {
+    let first = first as u32;
+    let second = second as u32;
+
+    let value = first.wrapping_sub(second);
+    let value_b = value as u8;
+
+    cpu.set_sub(true);
+    cpu.set_zero(value_b == 0);
+    cpu.set_half_carry((first ^ second ^ value) & 0x10 == 0x10);
+    cpu.set_carry(value & 0x100 == 0x100);
+
+    value_b
+}
+
+fn add_u16_u16(cpu: &mut Cpu, first: u16, second: u16) -> u16 {
+    let first = first as u32;
+    let second = second as u32;
+    let value = first.wrapping_add(second);
+
+    cpu.set_sub(false);
+    cpu.set_half_carry((first ^ second ^ value) & 0x1000 == 0x1000);
+    cpu.set_carry(value & 0x10000 == 0x10000);
+
+    value as u16
+}
+
+fn swap(cpu: &mut Cpu, value: u8) -> u8 {
+    cpu.set_sub(false);
+    cpu.set_zero(value == 0);
+    cpu.set_half_carry(false);
+    cpu.set_carry(false);
+
+    (value << 4) | (value >> 4)
+}
+
+/// Helper function for RST instructions, pushes the
+/// current PC to the stack and jumps to the provided
+/// address.
+fn rst(cpu: &mut Cpu, addr: u16) {
+    cpu.push_word(cpu.pc);
+    cpu.pc = addr;
+}
-- 
GitLab