Jared DiCarlo / Mbed 2 deprecated manworm_wormboy

Dependencies:   mbed SDFileSystem2

Files at this revision

API Documentation at this revision

Comitter:
dicarloj
Date:
Sun Jan 13 19:00:10 2019 +0000
Parent:
16:1f728d08b3a7
Commit message:
a

Changed in this revision

cpu.cpp Show annotated file Show diff for this revision Revisions of this file
cpu.h Show annotated file Show diff for this revision Revisions of this file
graphics_display.cpp Show annotated file Show diff for this revision Revisions of this file
graphics_display.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
main.h Show annotated file Show diff for this revision Revisions of this file
mem.cpp Show annotated file Show diff for this revision Revisions of this file
mem.h Show annotated file Show diff for this revision Revisions of this file
platform.cpp Show annotated file Show diff for this revision Revisions of this file
platform.h Show annotated file Show diff for this revision Revisions of this file
types.h Show annotated file Show diff for this revision Revisions of this file
video.cpp Show annotated file Show diff for this revision Revisions of this file
video.h Show annotated file Show diff for this revision Revisions of this file
diff -r 1f728d08b3a7 -r c9afe1a7b423 cpu.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cpu.cpp	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,2921 @@
+// Implementation of OPCODES and cpu step function
+
+#define NDEBUG
+#include <assert.h>
+
+#include "cpu.h"
+#include "mem.h"
+
+// cpu registers, halted state, and timers
+CpuState globalState;
+
+typedef void OpcodeHandler(u8 opcode);
+
+// the GB has several invalid opcodes
+void invHandler(u8 opcode) {
+  printf("got invalid opcode 0x%x\n", opcode);
+  assert(false);
+}
+
+////////////////////////////////
+// CB Opcode Helper Functions //
+////////////////////////////////
+
+u8 rlcReg(u8 value, bool isA) {
+  u8 result = value;
+  clearAllFlags();
+  if((result & 0x80) != 0) {
+    setCarryFlag();
+    result <<= 1;
+    result |= 0x1;
+  } else {
+    result <<= 1;
+  }
+  if(!isA) {
+    if((u8)result == 0) {
+      setZeroFlag();
+    }
+  }
+  return result;
+}
+
+u8 rlReg(u8 value, bool isA) {
+  u8 carry = getCarryFlag() ? (u8)1 : (u8)0;
+  u8 result = value;
+  clearAllFlags();
+  if((result & 0x80) != 0) {
+    setCarryFlag();
+  }
+  result <<= 1;
+  result |= carry;
+  if(!isA) {
+    if((u8)result == 0) {
+      setZeroFlag();
+    }
+  }
+  return result;
+}
+
+u8 rrc(u8 value, bool isA) {
+  u8 result = value;
+  clearAllFlags();
+  if((result & 1) != 0) {
+    setCarryFlag();
+    result >>= 1;
+    result |= 0x80;
+  } else {
+    result >>= 1;
+  }
+  if(!isA) {
+    if((u8)result == 0) {
+      setZeroFlag();
+    }
+  }
+  return result;
+}
+
+u8 rr(u8 value, bool isA) {
+  u8 carry = getCarryFlag() ? (u8)0x80 : (u8)0x00;
+  u8 result = value;
+  clearAllFlags();
+  if((result & 1) != 0) {
+    setCarryFlag();
+  }
+  result >>= 1;
+  result |= carry;
+  if(!isA) {
+    if((u8)result == 0) {
+      setZeroFlag();
+    }
+  }
+  return result;
+}
+
+u8 srl(u8 value) {
+  u8 result = value;
+  clearAllFlags();
+  if(result & 1) {
+    setCarryFlag();
+  }
+  result >>= 1;
+  if(result == 0) {
+    setZeroFlag();
+  }
+  return result;
+}
+
+u8 sla(u8 value) {
+  clearAllFlags();
+  if(value & 0x80) {
+    setCarryFlag();
+  }
+  u8 result = value << 1;
+  if(result == 0) {
+    setZeroFlag();
+  }
+  return result;
+}
+
+u8 sra(u8 value) {
+  u8 result = value;
+  clearAllFlags();
+  if(result & 1) {
+    setCarryFlag();
+  }
+  if((result & 0x80)) {
+    result >>= 1;
+    result |= 0x80;
+  } else {
+    result >>= 1;
+  }
+  if(result == 0) {
+    setZeroFlag();
+  }
+  return result;
+}
+
+u8 swapRegister(u8 value) {
+  u8 low = value & 0xf;
+  u8 hi = (value >> 4) & 0xf;
+  u8 result = (low << 4) + hi;
+  clearAllFlags();
+  if((u8)result == 0) {
+    setZeroFlag();
+  }
+  return result;
+}
+
+
+////////////////////////////////
+// CB Opcodes                 //
+////////////////////////////////
+
+void SLA_A(u8 opcode) { // 0x27
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = sla(globalState.a);
+}
+
+void SLA_B(u8 opcode) { // 0x20
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.hi = sla(globalState.bc.hi);
+}
+
+void SLA_C(u8 opcode) { // 0x21
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.lo = sla(globalState.bc.lo);
+}
+
+void SLA_D(u8 opcode) { // 0x22
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.hi = sla(globalState.de.hi);
+}
+
+void SLA_E(u8 opcode) { // 0x23
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.lo = sla(globalState.de.lo);
+}
+
+void SLA_H(u8 opcode) { // 0x24
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.hi = sla(globalState.hl.hi);
+}
+
+void SLA_L(u8 opcode) { // 0x25
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.lo = sla(globalState.hl.lo);
+}
+
+void SLA_DHL(u8 opcode) { // 0x26
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeByte(sla(readByte(globalState.hl.v)), globalState.hl.v);
+}
+
+void SRA_A(u8 opcode) { // 0x2f
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = sra(globalState.a);
+}
+
+void SRA_B(u8 opcode) { // 0x28
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.hi = sra(globalState.bc.hi);
+}
+
+void SRA_C(u8 opcode) { // 0x29
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.lo = sra(globalState.bc.lo);
+}
+
+void SRA_D(u8 opcode) { // 0x2a
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.hi = sra(globalState.de.hi);
+}
+
+void SRA_E(u8 opcode) { // 0x2b
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.lo = sra(globalState.de.lo);
+}
+
+void SRA_H(u8 opcode) { // 0x2c
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.hi = sra(globalState.hl.hi);
+}
+
+void SRA_L(u8 opcode) { // 0x2d
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.lo = sra(globalState.hl.lo);
+}
+
+void SRA_DHL(u8 opcode) { // 0x2e
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeByte(sra(readByte(globalState.hl.v)), globalState.hl.v);
+}
+
+
+void SRL_A(u8 opcode) { // 0x3f
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = srl(globalState.a);
+}
+
+void SRL_B(u8 opcode) { // 0x38
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.hi = srl(globalState.bc.hi);
+}
+
+void SRL_C(u8 opcode) { // 0x39
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.lo = srl(globalState.bc.lo);
+}
+
+void SRL_D(u8 opcode) { // 0x3a
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.hi = srl(globalState.de.hi);
+}
+
+void SRL_E(u8 opcode) { // 0x3b
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.lo = srl(globalState.de.lo);
+}
+
+void SRL_H(u8 opcode) { // 0x3c
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.hi = srl(globalState.hl.hi);
+}
+
+void SRL_L(u8 opcode) { // 0x3d
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.lo = srl(globalState.hl.lo);
+}
+
+void SRL_DHL(u8 opcode) { // 0x3e
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeByte(srl(readByte(globalState.hl.v)), globalState.hl.v);
+}
+
+void RR_A(u8 opcode) { // 0x1f
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = rr(globalState.a, false);
+}
+
+void RR_B(u8 opcode) { // 0x18
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.hi = rr(globalState.bc.hi, false);
+}
+
+void RR_C(u8 opcode) { // 0x19
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.lo = rr(globalState.bc.lo, false);
+}
+
+void RR_D(u8 opcode) { // 0x1a
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.hi = rr(globalState.de.hi, false);
+}
+
+void RR_E(u8 opcode) { // 0x1b
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.lo = rr(globalState.de.lo, false);
+}
+
+void RR_H(u8 opcode) { // 0x1c
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.hi = rr(globalState.hl.hi, false);
+}
+
+void RR_L(u8 opcode) { // 0x1d
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.lo = rr(globalState.hl.lo, false);
+}
+
+void RR_DHL(u8 opcode) { // 0x1e
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeByte(rr(readByte(globalState.hl.v), false), globalState.hl.v);
+}
+
+void RL_A(u8 opcode) { // 0x17
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = rlReg(globalState.a, false);
+}
+
+void RL_B(u8 opcode) { // 0x10
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.hi = rlReg(globalState.bc.hi, false);
+}
+
+void RL_C(u8 opcode) { // 0x11
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.lo = rlReg(globalState.bc.lo, false);
+}
+
+void RL_D(u8 opcode) { // 0x12
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.hi = rlReg(globalState.de.hi, false);
+}
+
+void RL_E(u8 opcode) { // 0x13
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.lo = rlReg(globalState.de.lo, false);
+}
+
+void RL_H(u8 opcode) { // 0x14
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.hi = rlReg(globalState.hl.hi, false);
+}
+
+void RL_L(u8 opcode) { // 0x15
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.lo = rlReg(globalState.hl.lo, false);
+}
+
+void RL_DHL(u8 opcode) { // 0x16
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeByte(rlReg(readByte(globalState.hl.v), false), globalState.hl.v);
+}
+
+void SWAP_A(u8 opcode) { // 37
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = swapRegister(globalState.a);
+}
+
+void SWAP_B(u8 opcode) { // 30
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.hi = swapRegister(globalState.bc.hi);
+}
+
+void SWAP_C(u8 opcode) { // 31
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.lo = swapRegister(globalState.bc.lo);
+}
+
+void SWAP_D(u8 opcode) { // 32
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.hi = swapRegister(globalState.de.hi);
+}
+
+void SWAP_E(u8 opcode) { // 33
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.lo = swapRegister(globalState.de.lo);
+}
+
+void SWAP_H(u8 opcode) { // 34
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.hi = swapRegister(globalState.hl.hi);
+}
+
+void SWAP_L(u8 opcode) { // 35
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.lo = swapRegister(globalState.hl.lo);
+}
+
+void SWAP_DHL(u8 opcode) { // 36
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeByte(swapRegister(readByte(globalState.hl.v)), globalState.hl.v);
+}
+
+void RLC_A(u8 opcode) { // 07
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = rlcReg(globalState.a, false);
+}
+
+void RLC_B(u8 opcode) { // 00
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.hi = rlcReg(globalState.bc.hi, false);
+}
+
+void RLC_C(u8 opcode) { // 01
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.lo = rlcReg(globalState.bc.lo, false);
+}
+
+void RLC_D(u8 opcode) { // 02
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.hi = rlcReg(globalState.de.hi, false);
+}
+
+void RLC_E(u8 opcode) { // 03
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.lo = rlcReg(globalState.de.lo, false);
+}
+
+void RLC_H(u8 opcode) { // 04
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.hi = rlcReg(globalState.hl.hi, false);
+}
+
+void RLC_L(u8 opcode) { // 05
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.lo = rlcReg(globalState.hl.lo, false);
+}
+
+void RLC_DHL(u8 opcode) { // 06
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  u8 result = rlcReg(readByte(globalState.hl.v), false);
+  writeByte(result, globalState.hl.v);
+}
+
+
+void RRC_A(u8 opcode) { // 0f
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = rrc(globalState.a, false);
+}
+
+void RRC_B(u8 opcode) { // 08
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.hi = rrc(globalState.bc.hi, false);
+}
+
+void RRC_C(u8 opcode) { // 09
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.lo = rrc(globalState.bc.lo, false);
+}
+
+void RRC_D(u8 opcode) { // 0a
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.hi = rrc(globalState.de.hi, false);
+}
+
+void RRC_E(u8 opcode) { // 0b
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.lo = rrc(globalState.de.lo, false);
+}
+
+void RRC_H(u8 opcode) { // 0c
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.hi = rrc(globalState.hl.hi, false);
+}
+
+void RRC_L(u8 opcode) { // 0d
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.lo = rrc(globalState.hl.lo, false);
+}
+
+void RRC_DHL(u8 opcode) { // 0e
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  u8 result = rrc(readByte(globalState.hl.v), false);
+  writeByte(result, globalState.hl.v);
+}
+
+
+void bit_B_set(u8 opcode) {
+  u8 bitID = (opcode - (u8)0xC0) >> 3;
+  assert(bitID < 8);
+  globalState.bc.hi |= (1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_C_set(u8 opcode) {
+  u8 bitID = (opcode - (u8)0xC1) >> 3;
+  assert(bitID < 8);
+  globalState.bc.lo |= (1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_D_set(u8 opcode) {
+  u8 bitID = (opcode - (u8)0xC2) >> 3;
+  assert(bitID < 8);
+  globalState.de.hi |= (1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_E_set(u8 opcode) {
+  u8 bitID = (opcode - (u8)0xC3) >> 3;
+  assert(bitID < 8);
+  globalState.de.lo |= (1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_H_set(u8 opcode) {
+  u8 bitID = (opcode - (u8)0xC4) >> 3;
+  assert(bitID < 8);
+  globalState.hl.hi |= (1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_L_set(u8 opcode) {
+  u8 bitID = (opcode - (u8)0xC5) >> 3;
+  assert(bitID < 8);
+  globalState.hl.lo |= (1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_DHL_set(u8 opcode) {
+  u8 bitID = (opcode - (u8)0xC6) >> 3;
+  assert(bitID < 8);
+  u8 value = readByte(globalState.hl.v);
+  value |= (1 << bitID);
+  writeByte(value, globalState.hl.v);
+  globalState.pc += 1;
+  globalState.cycleCount += 16;
+}
+
+void bit_A_set(u8 opcode) {
+  u8 bitID = (opcode - (u8)0xC7) >> 3;
+  assert(bitID < 8);
+  globalState.a |= (1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_B_res(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x80) >> 3;
+  assert(bitID < 8);
+  globalState.bc.hi &= ~(1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_C_res(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x81) >> 3;
+  assert(bitID < 8);
+  globalState.bc.lo &= ~(1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_D_res(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x82) >> 3;
+  assert(bitID < 8);
+  globalState.de.hi &= ~(1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_E_res(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x83) >> 3;
+  assert(bitID < 8);
+  globalState.de.lo &= ~(1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_H_res(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x84) >> 3;
+  assert(bitID < 8);
+  globalState.hl.hi &= ~(1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_L_res(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x85) >> 3;
+  assert(bitID < 8);
+  globalState.hl.lo &= ~(1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_DHL_res(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x86) >> 3;
+  assert(bitID < 8);
+  u8 value = readByte(globalState.hl.v);
+  value &= ~(1 << bitID);
+  writeByte(value, globalState.hl.v);
+  globalState.pc += 1;
+  globalState.cycleCount += 16;
+}
+
+void bit_A_res(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x87) >> 3;
+  assert(bitID < 8);
+  globalState.a &= ~(1 << bitID);
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_A_test(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x47) >> 3;
+  assert(bitID < 8);
+  //printf("check bit %d of A\n", bitID);
+  u8 val = globalState.a;
+  if(((val >> bitID) & 1) == 0) {
+    setZeroFlag();
+  } else {
+    clearZeroFlag();
+  }
+  setHalfCarryFlag();
+  clearSubtractFlag();
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_B_test(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x40) >> 3;
+  //printf("B opcode 0x%x bitId %d\n", opcode, bitID);
+  assert(bitID < 8);
+  //printf("check bit %d of B\n", bitID);
+  u8 val = globalState.bc.hi;
+  if(((val >> bitID) & 1) == 0) {
+    setZeroFlag();
+  } else {
+    clearZeroFlag();
+  }
+  setHalfCarryFlag();
+  clearSubtractFlag();
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+
+void bit_C_test(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x41) >> 3;
+  assert(bitID < 8);
+  //printf("check bit %d of C\n", bitID);
+  u8 val = globalState.bc.lo;
+  if(((val >> bitID) & 1) == 0) {
+    setZeroFlag();
+  } else {
+    clearZeroFlag();
+  }
+  setHalfCarryFlag();
+  clearSubtractFlag();
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_D_test(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x42) >> 3;
+  assert(bitID < 8);
+  //printf("check bit %d of D\n", bitID);
+  u8 val = globalState.de.hi;
+  if(((val >> bitID) & 1) == 0) {
+    setZeroFlag();
+  } else {
+    clearZeroFlag();
+  }
+  setHalfCarryFlag();
+  clearSubtractFlag();
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_E_test(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x43) >> 3;
+  assert(bitID < 8);
+  //printf("check bit %d of E\n", bitID);
+  u8 val = globalState.de.lo;
+  if(((val >> bitID) & 1) == 0) {
+    setZeroFlag();
+  } else {
+    clearZeroFlag();
+  }
+  setHalfCarryFlag();
+  clearSubtractFlag();
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_H_test(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x44) >> 3;
+  assert(bitID < 8);
+  //printf("check bit %d of H\n", bitID);
+  u8 val = globalState.hl.hi;
+  if(((val >> bitID) & 1) == 0) {
+    setZeroFlag();
+  } else {
+    clearZeroFlag();
+  }
+  setHalfCarryFlag();
+  clearSubtractFlag();
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_L_test(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x45) >> 3;
+  assert(bitID < 8);
+  //printf("check bit %d of L\n", bitID);
+  u8 val = globalState.hl.lo;
+  if(((val >> bitID) & 1) == 0) {
+    setZeroFlag();
+  } else {
+    clearZeroFlag();
+  }
+  setHalfCarryFlag();
+  clearSubtractFlag();
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+void bit_DHL_test(u8 opcode) {
+  u8 bitID = (opcode - (u8)0x45) >> 3;
+  assert(bitID < 8);
+  //printf("check bit %d of L\n", bitID);
+  u8 val = readByte(globalState.hl.v);
+  if(((val >> bitID) & 1) == 0) {
+    setZeroFlag();
+  } else {
+    clearZeroFlag();
+  }
+  setHalfCarryFlag();
+  clearSubtractFlag();
+  globalState.pc += 1;
+  globalState.cycleCount += 16;
+}
+
+// CB-prefixed opcode handler table
+static OpcodeHandler* opcode_cbs[256] =
+        {RLC_B,          RLC_C,          RLC_D,          RLC_E,          RLC_H,          RLC_L,          RLC_DHL,        RLC_A,          // 0x0 - 0x7
+         RRC_B,          RRC_C,          RRC_D,          RRC_E,          RRC_H,          RRC_L,          RRC_DHL,        RRC_A,          // 0x8 - 0xf
+         RL_B,           RL_C,           RL_D,           RL_E,           RL_H,           RL_L,           RL_DHL,         RL_A,           // 0x10 - 0x17
+         RR_B,           RR_C,           RR_D,           RR_E,           RR_H,           RR_L,           RR_DHL,         RR_A,           // 0x18 - 0x1f
+         SLA_B,          SLA_C,          SLA_D,          SLA_E,          SLA_H,          SLA_L,          SLA_DHL,        SLA_A,          // 0x20 - 0x27
+         SRA_B,          SRA_C,          SRA_D,          SRA_E,          SRA_H,          SRA_L,          SRA_DHL,        SRA_A,          // 0x28 - 0x2f
+         SWAP_B,         SWAP_C,         SWAP_D,         SWAP_E,         SWAP_H,         SWAP_L,         SWAP_DHL,       SWAP_A,         // 0x30 - 0x37
+         SRL_B,          SRL_C,          SRL_D,          SRL_E,          SRL_H,          SRL_L,          SRL_DHL,        SRL_A,          // 0x38 - 0x3f
+         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x40 - 0x47
+         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x48 - 0x4f
+         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x50 - 0x57
+         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x58 - 0x5f
+         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x60 - 0x67
+         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x68 - 0x6f
+         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x70 - 0x77
+         bit_B_test,     bit_C_test,     bit_D_test,     bit_E_test,     bit_H_test,     bit_L_test,     bit_DHL_test,   bit_A_test,     // 0x78 - 0x7f
+         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0x80 - 0x8
+         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0x88 - 0x8f
+         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0x90 - 0x97
+         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0x98 - 0x9f
+         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0xa0 - 0xa7
+         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0xa8 - 0xaf
+         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0xb0 - 0xb7
+         bit_B_res,      bit_C_res,      bit_D_res,      bit_E_res,      bit_H_res,      bit_L_res,      bit_DHL_res,    bit_A_res,      // 0xb8 - 0xbf
+         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xc0 - 0xc7
+         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xc8 - 0xcf
+         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xd0 - 0xd7
+         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xd8 - 0xdf
+         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xe0 - 0xe7
+         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xe8 - 0xef
+         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     , // 0xf0 - 0xf7
+         bit_B_set,      bit_C_set,      bit_D_set,      bit_E_set,      bit_H_set,      bit_L_set,      bit_DHL_set,    bit_A_set     }; // 0xf8 - 0xff
+
+
+////////////////////////////////
+// Opcodes                    //
+////////////////////////////////
+void LD_B_n(u8 opocde) { // 06
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.bc.hi = imm;
+  globalState.cycleCount += 8;
+}
+
+void LD_C_n(u8 opocde) { // 0E
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.bc.lo = imm;
+  globalState.cycleCount += 8;
+}
+
+void LD_D_n(u8 opocde) { // 16
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.de.hi = imm;
+  globalState.cycleCount += 8;
+}
+
+void LD_E_n(u8 opocde) { // 1e
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.de.lo = imm;
+  globalState.cycleCount += 8;
+}
+
+void LD_H_n(u8 opocde) { // 26
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.hl.hi = imm;
+  globalState.cycleCount += 8;
+}
+
+void LD_L_n(u8 opocde) { // 2e
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.hl.lo = imm;
+  globalState.cycleCount += 8;
+}
+
+// REGISTER-REGISTER LOADS (REGISTER A)
+void LD_A_A(u8 opcode) { // 0x7f
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = globalState.a;
+}
+
+void LD_A_B(u8 opcode) { // 0x78
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = globalState.bc.hi;
+}
+
+void LD_A_C(u8 opcode) { // 0x79
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = globalState.bc.lo;
+}
+
+void LD_A_D(u8 opcode) { // 0x7a
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = globalState.de.hi;
+}
+
+void LD_A_E(u8 opcode) { // 0x7b
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = globalState.de.lo;
+}
+
+void LD_A_H(u8 opcode) { // 0x7c
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = globalState.hl.hi;
+}
+
+void LD_A_L(u8 opcode) { // 0x7d
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = globalState.hl.lo;
+}
+
+void LD_A_DHL(u8 opcode) { // 0x7e
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = readByte(globalState.hl.v);
+}
+
+
+
+// REGISTER-REGISTER LOADS (REGISTER B)
+void LD_B_B(u8 opcode) { // 0x40
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.hi = globalState.bc.hi;
+}
+
+void LD_B_C(u8 opcode) { // 0x41
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.hi = globalState.bc.lo;
+}
+
+void LD_B_D(u8 opcode) { // 0x42
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.hi = globalState.de.hi;
+}
+
+void LD_B_E(u8 opcode) { // 0x43
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.hi = globalState.de.lo;
+}
+
+void LD_B_H(u8 opcode) { // 0x44
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.hi = globalState.hl.hi;
+}
+
+void LD_B_L(u8 opcode) { // 0x45
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.hi = globalState.hl.lo;
+}
+
+void LD_B_DHL(u8 opcode) { // 0x46
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.hi = readByte(globalState.hl.v);
+}
+
+
+
+// REGISTER-REGISTER LOADS (REGISTER C)
+void LD_C_B(u8 opcode) { // 0x48
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.lo = globalState.bc.hi;
+}
+
+void LD_C_C(u8 opcode) { // 0x49
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.lo = globalState.bc.lo;
+}
+
+void LD_C_D(u8 opcode) { // 0x4a
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.lo = globalState.de.hi;
+}
+
+void LD_C_E(u8 opcode) { // 0x4b
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.lo = globalState.de.lo;
+}
+
+void LD_C_H(u8 opcode) { // 0x4c
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.lo = globalState.hl.hi;
+}
+
+void LD_C_L(u8 opcode) { // 0x4d
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.lo = globalState.hl.lo;
+}
+
+void LD_C_DHL(u8 opcode) { // 0x4e
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.lo = readByte(globalState.hl.v);
+}
+
+
+
+// REGISTER-REGISTER LOADS (REGISTER D)
+void LD_D_B(u8 opcode) { // 0x50
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.hi = globalState.bc.hi;
+}
+
+void LD_D_C(u8 opcode) { // 0x51
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.hi = globalState.bc.lo;
+}
+
+void LD_D_D(u8 opcode) { // 0x52
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.hi = globalState.de.hi;
+}
+
+void LD_D_E(u8 opcode) { // 0x53
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.hi = globalState.de.lo;
+}
+
+void LD_D_H(u8 opcode) { // 0x54
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.hi = globalState.hl.hi;
+}
+
+void LD_D_L(u8 opcode) { // 0x55
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.hi = globalState.hl.lo;
+}
+
+void LD_D_DHL(u8 opcode) { // 0x56
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.hi = readByte(globalState.hl.v);
+}
+
+
+// REGISTER-REGISTER LOADS (REGISTER E)
+void LD_E_B(u8 opcode) { // 0x58
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.lo = globalState.bc.hi;
+}
+
+void LD_E_C(u8 opcode) { // 0x59
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.lo = globalState.bc.lo;
+}
+
+void LD_E_D(u8 opcode) { // 0x5a
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.lo = globalState.de.hi;
+}
+
+void LD_E_E(u8 opcode) { // 0x5b
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.lo = globalState.de.lo;
+}
+
+void LD_E_H(u8 opcode) { // 0x5c
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.lo = globalState.hl.hi;
+}
+
+void LD_E_L(u8 opcode) { // 0x5d
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.lo = globalState.hl.lo;
+}
+
+void LD_E_DHL(u8 opcode) { // 0x5e
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.lo = readByte(globalState.hl.v);
+}
+
+// REGISTER-REGISTER LOADS (REGISTER H)
+void LD_H_B(u8 opcode) { // 0x60
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.hi = globalState.bc.hi;
+}
+
+void LD_H_C(u8 opcode) { // 0x61
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.hi = globalState.bc.lo;
+}
+
+void LD_H_D(u8 opcode) { // 0x62
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.hi = globalState.de.hi;
+}
+
+void LD_H_E(u8 opcode) { // 0x63
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.hi = globalState.de.lo;
+}
+
+void LD_H_H(u8 opcode) { // 0x64
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.hi = globalState.hl.hi;
+}
+
+void LD_H_L(u8 opcode) { // 0x65
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.hi = globalState.hl.lo;
+}
+
+void LD_H_DHL(u8 opcode) { // 0x66
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.hi = readByte(globalState.hl.v);
+}
+
+// REGISTER-REGISTER LOADS (REGISTER L)
+void LD_L_B(u8 opcode) { // 0x60
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.lo = globalState.bc.hi;
+}
+
+void LD_L_C(u8 opcode) { // 0x61
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.lo = globalState.bc.lo;
+}
+
+void LD_L_D(u8 opcode) { // 0x62
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.lo = globalState.de.hi;
+}
+
+void LD_L_E(u8 opcode) { // 0x63
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.lo = globalState.de.lo;
+}
+
+void LD_L_H(u8 opcode) { // 0x64
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.lo = globalState.hl.hi;
+}
+
+void LD_L_L(u8 opcode) { // 0x65
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.lo = globalState.hl.lo;
+}
+
+void LD_L_DHL(u8 opcode) { // 0x66
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.lo = readByte(globalState.hl.v);
+}
+
+// REGISTER-REGISTER LOADS (REGISTER (HL))
+void LD_DHL_B(u8 opcode) { // 0x70
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  writeByte(globalState.bc.hi, globalState.hl.v);
+}
+
+void LD_DHL_C(u8 opcode) { // 0x71
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  writeByte(globalState.bc.lo, globalState.hl.v);
+}
+
+void LD_DHL_D(u8 opcode) { // 0x72
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  writeByte(globalState.de.hi, globalState.hl.v);
+}
+
+void LD_DHL_E(u8 opcode) { // 0x73
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  writeByte(globalState.de.lo, globalState.hl.v);
+}
+
+void LD_DHL_H(u8 opcode) { // 0x74
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  writeByte(globalState.hl.hi, globalState.hl.v);
+}
+
+void LD_DHL_L(u8 opcode) { // 0x75
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  writeByte(globalState.hl.lo, globalState.hl.v);
+}
+
+// Random
+void LD_DHL_n(u8 opcode) { // 0x36
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.cycleCount += 12;
+  writeByte(imm, globalState.hl.v);
+}
+
+// Load Into Register A Specials
+void LD_A_DBC(u8 opcode) { // 0A
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = readByte(globalState.bc.v);
+}
+
+void LD_A_DDE(u8 opcode) { // 1A
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = readByte(globalState.de.v);
+}
+
+
+void LD_A_Dnn(u8 opcode) { // FA
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  globalState.a = readByte(readU16(globalState.pc));
+  globalState.pc += 2;
+}
+
+void LD_A_n(u8 opocde) { // 0x3e
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.a = imm;
+  globalState.cycleCount += 8;
+}
+
+// LOAD FROM REGISTER A
+void LD_B_A(u8 opcode) { // 0x47
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.hi = globalState.a;
+}
+
+void LD_C_A(u8 opcode) { // 0x4f
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.bc.lo = globalState.a;
+}
+
+void LD_D_A(u8 opcode) { // 0x57
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.hi = globalState.a;
+}
+
+void LD_E_A(u8 opcode) { // 0x5f
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.de.lo = globalState.a;
+}
+
+void LD_H_A(u8 opcode) { // 0x67
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.hi = globalState.a;
+}
+
+void LD_L_A(u8 opcode) { // 0x6f
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.hl.lo = globalState.a;
+}
+
+void LD_DBC_A(u8 opcode) { // 0x02
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  writeByte(globalState.a, globalState.bc.v);
+}
+
+void LD_DDE_A(u8 opcode) { // 0x12
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  writeByte(globalState.a, globalState.de.v);
+}
+
+void LD_DHL_A(u8 opcode) { // 0x77
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  writeByte(globalState.a, globalState.hl.v);
+}
+
+void LD_Dnn_A(u8 opcode) { // 0xea
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeByte(globalState.a, readU16(globalState.pc));
+  globalState.pc += 2;
+}
+
+// Load A with memory offset from 0xff00
+void LD_A_FF00_C(u8 opcode) { //0xf2
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = readByte((u16)0xff00 + globalState.bc.lo);
+}
+
+// Store A with memory offset from 0xff00
+void LD_FF00_C_A(u8 opcode) { // 0xe2
+  globalState.pc++;
+  writeByte(globalState.a, (u16)0xff00 + globalState.bc.lo);
+  globalState.cycleCount += 8;
+}
+
+// Load (HL) into A, decrement HL
+void LDD_A_DHL(u8 opcode) { // 0x3a
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.a = readByte(globalState.hl.v);
+  globalState.hl.v--;
+}
+
+// Load A into (HL), decrement HL
+void LDD_DHL_A(u8 opcode) { // 0x32
+  writeByte(globalState.a, globalState.hl.v);
+  globalState.hl.v--;
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+
+// Load (HL) into A, increment HL
+void LDI_A_DHL(u8 opcode){ // 0x2a
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+  globalState.a = readByte(globalState.hl.v);
+  globalState.hl.v++;
+}
+
+// Load A into (HL), increment HL
+void LDI_DHL_A(u8 opcode) { // 0x22
+  writeByte(globalState.a, globalState.hl.v);
+  globalState.hl.v++;
+  globalState.pc += 1;
+  globalState.cycleCount += 8;
+}
+// page 75
+
+// Store A into FF00 + n
+void LD_FF00_n_A(u8 opcode) { //0xe0
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  writeByte(globalState.a, (u16)0xff00 + imm);
+  globalState.cycleCount += 12;
+}
+
+// Load A with FF00 + n
+void LD_A_FF00_n(u8 opcode) { // 0xf0
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.a = readByte((u16)0xff00 + imm);
+  globalState.cycleCount += 12;
+}
+
+// 16-bit loads
+void LD_BC_nn(u8 opcode) { // 0x01
+  u16 imm = readU16(globalState.pc + (u16)1);
+  globalState.bc.v = imm;
+  globalState.pc += 3;
+  globalState.cycleCount += 12;
+}
+
+void LD_DE_nn(u8 opcode) { // 0x11
+  u16 imm = readU16(globalState.pc + (u16)1);
+  globalState.de.v = imm;
+  globalState.pc += 3;
+  globalState.cycleCount += 12;
+}
+
+void LD_HL_nn(u8 opcode) { // 0x21
+  u16 imm = readU16(globalState.pc + (u16)1);
+  globalState.hl.v = imm;
+  globalState.pc += 3;
+  globalState.cycleCount += 12;
+}
+
+void LD_SP_nn(u8 opcode) { // 0x31
+  u16 imm = readU16(globalState.pc + (u16)1);
+  globalState.sp = imm;
+  globalState.pc += 3;
+  globalState.cycleCount += 12;
+}
+
+void LD_SP_HL(u8 opcode) { // 0xf9
+  globalState.sp = globalState.hl.v;
+  globalState.pc++;
+  globalState.cycleCount += 8;
+}
+
+// load effective address relative to stack pointer
+void LD_HL_SP_n(u8 opcode) { //0xf8
+  globalState.pc++;
+  globalState.cycleCount += 12;
+  s8 imm = readByte(globalState.pc);
+  globalState.pc++;
+  clearAllFlags();
+  u16 result = globalState.sp + imm;
+  if(((globalState.sp ^ imm ^ result) & 0x100) == 0x100) {
+    setCarryFlag();
+  }
+  if(((globalState.sp ^ imm ^ result) & 0x10) == 0x10) {
+    setHalfCarryFlag();
+  }
+  globalState.hl.v = result;
+}
+
+// store stack pointer
+void LD_Dnn_SP(u8 opcode) { // 08
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  globalState.pc+=2;
+  globalState.cycleCount += 20;
+  writeU16(globalState.sp, addr);
+}
+
+// push U16 register onto stack
+void PUSH_AF(u8 opcode) { // 0xf5
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeU16(globalState.f + (((u16)globalState.a) << 8), globalState.sp - (u16)2);
+  globalState.sp -= 2;
+}
+
+void PUSH_BC(u8 opcode) { // 0xc5
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeU16(globalState.bc.v, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+}
+
+void PUSH_DE(u8 opcode) { // 0xd5
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeU16(globalState.de.v, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+}
+
+void PUSH_HL(u8 opcode) { // 0xe5
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  writeU16(globalState.hl.v, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+}
+
+// POP U16 register from stack
+void POP_AF(u8 opcode) { // 0xf1
+  globalState.pc++;
+  globalState.cycleCount += 12;
+  u16 v = readU16(globalState.sp);
+  globalState.sp += 2;
+  globalState.a = (u8)(v >> 8);
+  globalState.f = (u8)(v & 0xf0);
+}
+
+void POP_BC(u8 opcode) { // 0xc1
+  globalState.pc++;
+  globalState.cycleCount += 12;
+  u16 v = readU16(globalState.sp);
+  globalState.sp += 2;
+  globalState.bc.v = v;
+}
+
+void POP_DE(u8 opcode) { // 0xd1
+  globalState.pc++;
+  globalState.cycleCount += 12;
+  u16 v = readU16(globalState.sp);
+  globalState.sp += 2;
+  globalState.de.v = v;
+}
+
+void POP_HL(u8 opcode) { // 0xe1
+  globalState.pc++;
+  globalState.cycleCount += 12;
+  u16 v = readU16(globalState.sp);
+  globalState.sp += 2;
+  globalState.hl.v = v;
+}
+
+void addToA(u8 number) {
+  int result = globalState.a + number;
+  int carryBits = globalState.a ^ number ^ result;
+  globalState.a = (u8)result;
+  clearAllFlags();
+
+  if((u8)result == 0){
+    setZeroFlag();
+  }
+
+  if((carryBits & 0x100) != 0) {
+    setCarryFlag();
+  }
+  if((carryBits & 0x10) != 0) {
+    setHalfCarryFlag();
+  }
+}
+
+// ADD Opcodes
+void ADD_A(u8 opcode) { // 0x87
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  addToA(globalState.a);
+}
+
+void ADD_B(u8 opcode) { // 0x80
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  addToA(globalState.bc.hi);
+}
+
+void ADD_C(u8 opcode) { // 0x81
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  addToA(globalState.bc.lo);
+}
+
+void ADD_D(u8 opcode) { // 0x82
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  addToA(globalState.de.hi);
+}
+
+void ADD_E(u8 opcode) { // 0x83
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  addToA(globalState.de.lo);
+}
+
+void ADD_H(u8 opcode) { // 0x84
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  addToA(globalState.hl.hi);
+}
+
+void ADD_L(u8 opcode) { // 0x85
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  addToA(globalState.hl.lo);
+}
+
+void ADD_DHL(u8 opcode) { // 0x86
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  addToA(readByte(globalState.hl.v));
+}
+
+void ADD_n(u8 opcode) { // 0xC6
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  addToA(imm);
+}
+
+void adcToA(u8 number) {
+  int carry = getCarryFlag() ? 1 : 0;
+  int result = globalState.a + number + carry;
+  clearAllFlags();
+  if((u8)result == 0) {
+    setZeroFlag();
+  }
+  if(result > 0xff) {
+    setCarryFlag();
+  }
+  if(((globalState.a & 0xf) + (number & 0xf) + carry) > 0xf) {
+    setHalfCarryFlag();
+  }
+  globalState.a = (u8) result;
+}
+
+// ADC Opcodes
+void ADC_A(u8 opcode) { // 0x8f
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  adcToA(globalState.a);
+}
+
+void ADC_B(u8 opcode) { // 0x88
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  adcToA(globalState.bc.hi);
+}
+
+void ADC_C(u8 opcode) { // 0x89
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  adcToA(globalState.bc.lo);
+}
+
+void ADC_D(u8 opcode) { // 0x8a
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  adcToA(globalState.de.hi);
+}
+
+void ADC_E(u8 opcode) { // 0x8b
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  adcToA(globalState.de.lo);
+}
+
+void ADC_H(u8 opcode) { // 0x8c
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  adcToA(globalState.hl.hi);
+}
+
+void ADC_L(u8 opcode) { // 0x8d
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  adcToA(globalState.hl.lo);
+}
+
+void ADC_DHL(u8 opcode) { // 0x8e
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  adcToA(readByte(globalState.hl.v));
+}
+
+void ADC_n(u8 opcode) { // 0xCe
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  adcToA(imm);
+}
+
+void subToA(u8 number) {
+  int result = globalState.a - number;
+  int carryBits = globalState.a ^ number ^ result;
+  globalState.a = (u8)result;
+  clearAllFlags();
+  setSubtractFlag();
+  if((u8)result == 0) {
+    setZeroFlag();
+  }
+  if((carryBits & 0x100) != 0) {
+    setCarryFlag();
+  }
+  if((carryBits & 0x10) != 0) {
+    setHalfCarryFlag();
+  }
+}
+
+// SUB Opcodes
+void SUB_A(u8 opcode) { // 0x97
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  subToA(globalState.a);
+}
+
+void SUB_B(u8 opcode) { // 0x90
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  subToA(globalState.bc.hi);
+}
+
+void SUB_C(u8 opcode) { // 0x91
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  subToA(globalState.bc.lo);
+}
+
+void SUB_D(u8 opcode) { // 0x92
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  subToA(globalState.de.hi);
+}
+
+void SUB_E(u8 opcode) { // 0x93
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  subToA(globalState.de.lo);
+}
+
+void SUB_H(u8 opcode) { // 0x94
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  subToA(globalState.hl.hi);
+}
+
+void SUB_L(u8 opcode) { // 0x95
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  subToA(globalState.hl.lo);
+}
+
+void SUB_DHL(u8 opcode) { // 0x96
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  subToA(readByte(globalState.hl.v));
+}
+
+void SUB_n(u8 opcode) { // 0xD6
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  subToA(imm);
+}
+
+void sbcToA(u8 number) {
+  int carry = getCarryFlag() ? 1 : 0;
+  int result = globalState.a - number - carry;
+  clearAllFlags();
+  setSubtractFlag();
+  if((u8)result == 0) {
+    setZeroFlag();
+  }
+  if(result < 0) {
+    setCarryFlag();
+  }
+  if(((globalState.a & 0xf) - (number & 0xf) - carry) < 0) {
+    setHalfCarryFlag();
+  }
+  globalState.a = (u8)result;
+}
+
+// SBC Opcodes
+void SBC_A(u8 opcode) { // 0x9f
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  sbcToA(globalState.a);
+}
+
+void SBC_B(u8 opcode) { // 0x98
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  sbcToA(globalState.bc.hi);
+}
+
+void SBC_C(u8 opcode) { // 0x99
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  sbcToA(globalState.bc.lo);
+}
+
+void SBC_D(u8 opcode) { // 0x9a
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  sbcToA(globalState.de.hi);
+}
+
+void SBC_E(u8 opcode) { // 0x9b
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  sbcToA(globalState.de.lo);
+}
+
+void SBC_H(u8 opcode) { // 0x9c
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  sbcToA(globalState.hl.hi);
+}
+
+void SBC_L(u8 opcode) { // 0x9d
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  sbcToA(globalState.hl.lo);
+}
+
+void SBC_DHL(u8 opcode) { // 0x9e
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  sbcToA(readByte(globalState.hl.v));
+}
+
+void SBC_n(u8 opcode) { // 0xDe
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  sbcToA(imm);
+}
+
+void andtoA(u8 number) {
+  u8 result = globalState.a & number;
+  globalState.a = result;
+  clearAllFlags();
+  setHalfCarryFlag();
+  if((u8)result == 0) {
+    setZeroFlag();
+  }
+}
+
+// AND Opcodes
+void AND_A(u8 opcode) { // 0xa7
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  andtoA(globalState.a);
+}
+
+void AND_B(u8 opcode) { // 0xa0
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  andtoA(globalState.bc.hi);
+}
+
+void AND_C(u8 opcode) { // 0xa1
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  andtoA(globalState.bc.lo);
+}
+
+void AND_D(u8 opcode) { // 0xa2
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  andtoA(globalState.de.hi);
+}
+
+void AND_E(u8 opcode) { // 0xa3
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  andtoA(globalState.de.lo);
+}
+
+void AND_H(u8 opcode) { // 0xa4
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  andtoA(globalState.hl.hi);
+}
+
+void AND_L(u8 opcode) { // 0xa5
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  andtoA(globalState.hl.lo);
+}
+
+void AND_DHL(u8 opcode) { // 0xa6
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  andtoA(readByte(globalState.hl.v));
+}
+
+void AND_n(u8 opcode) { // 0xe6
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  andtoA(imm);
+}
+
+void xorToA(u8 number) {
+  u8 result = globalState.a ^ number;
+  globalState.a = result;
+  clearAllFlags();
+  if((u8)result == 0) {
+    setZeroFlag();
+  }
+}
+
+// XOR Opcodes
+void XOR_A(u8 opcode) { // 0xaf
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  xorToA(globalState.a);
+}
+
+void XOR_B(u8 opcode) { // 0xa8
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  xorToA(globalState.bc.hi);
+}
+
+void XOR_C(u8 opcode) { // 0xa9
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  xorToA(globalState.bc.lo);
+}
+
+void XOR_D(u8 opcode) { // 0xaa
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  xorToA(globalState.de.hi);
+}
+
+void XOR_E(u8 opcode) { // 0xab
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  xorToA(globalState.de.lo);
+}
+
+void XOR_H(u8 opcode) { // 0xac
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  xorToA(globalState.hl.hi);
+}
+
+void XOR_L(u8 opcode) { // 0xad
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  xorToA(globalState.hl.lo);
+}
+
+void XOR_DHL(u8 opcode) { // 0xae
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  xorToA(readByte(globalState.hl.v));
+}
+
+void XOR_n(u8 opcode) { // 0xee
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  xorToA(imm);
+}
+
+void orToA(u8 number) {
+  u8 result = globalState.a | number;
+  globalState.a = result;
+  clearAllFlags();
+  if((u8)result == 0) {
+    setZeroFlag();
+  }
+}
+
+// OR Opcodes
+void OR_A(u8 opcode) { // 0xb7
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  orToA(globalState.a);
+}
+
+void OR_B(u8 opcode) { // 0xb0
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  orToA(globalState.bc.hi);
+}
+
+void OR_C(u8 opcode) { // 0xb1
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  orToA(globalState.bc.lo);
+}
+
+void OR_D(u8 opcode) { // 0xb2
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  orToA(globalState.de.hi);
+}
+
+void OR_E(u8 opcode) { // 0xb3
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  orToA(globalState.de.lo);
+}
+
+void OR_H(u8 opcode) { // 0xb4
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  orToA(globalState.hl.hi);
+}
+
+void OR_L(u8 opcode) { // 0xb5
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  orToA(globalState.hl.lo);
+}
+
+void OR_DHL(u8 opcode) { // 0xb6
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  orToA(readByte(globalState.hl.v));
+}
+
+void OR_n(u8 opcode) { // 0xf6
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  orToA(imm);
+}
+
+void cpToA(u8 number) {
+  clearAllFlags();
+  setSubtractFlag();
+  if(globalState.a < number) {
+    setCarryFlag();
+  }
+  if(globalState.a == number) {
+    setZeroFlag();
+  }
+  if(((globalState.a - number) & 0xf) > (globalState.a & 0xf)) {
+    setHalfCarryFlag();
+  }
+}
+
+// CP Opcodes
+void CP_A(u8 opcode) { // 0xbf
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  cpToA(globalState.a);
+}
+
+void CP_B(u8 opcode) { // 0xb8
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  cpToA(globalState.bc.hi);
+}
+
+void CP_C(u8 opcode) { // 0xb9
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  cpToA(globalState.bc.lo);
+}
+
+void CP_D(u8 opcode) { // 0xba
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  cpToA(globalState.de.hi);
+}
+
+void CP_E(u8 opcode) { // 0xbb
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  cpToA(globalState.de.lo);
+}
+
+void CP_H(u8 opcode) { // 0xbc
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  cpToA(globalState.hl.hi);
+}
+
+void CP_L(u8 opcode) { // 0xbd
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  cpToA(globalState.hl.lo);
+}
+
+void CP_DHL(u8 opcode) { // 0xbe
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  cpToA(readByte(globalState.hl.v));
+}
+
+void CP_n(u8 opcode) { // 0xfe
+  u8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  cpToA(imm);
+}
+
+void DecodeCB(u8 opcode) {
+  globalState.pc += 1;
+  u8 newOpcode = readByte(globalState.pc);
+  opcode_cbs[newOpcode](newOpcode);
+}
+
+u8 increment(u8 in) {
+  u8 result = in + (u8)1;
+  if(getCarryFlag()) {
+    clearAllFlags();
+    setCarryFlag();
+  } else {
+    clearAllFlags();
+  }
+  if(result == 0) {
+    setZeroFlag();
+  }
+  if((result & 0x0f) == 0x00) {
+    setHalfCarryFlag();
+  }
+  return result;
+}
+
+void INC_A(u8 opcode) { // 0x3c
+  globalState.a = increment(globalState.a);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void INC_B(u8 opcode) { // 0x04
+  globalState.bc.hi = increment(globalState.bc.hi);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void INC_C(u8 opcode) { // 0x0c
+  globalState.bc.lo = increment(globalState.bc.lo);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void INC_D(u8 opcode) { // 0x14
+  globalState.de.hi = increment(globalState.de.hi);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void INC_E(u8 opcode) { // 0x1c
+  globalState.de.lo = increment(globalState.de.lo);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void INC_H(u8 opcode) { // 0x24
+  globalState.hl.hi = increment(globalState.hl.hi);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void INC_L(u8 opcode) { // 0x2c
+  globalState.hl.lo = increment(globalState.hl.lo);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void INC_DHL(u8 opcode) { // 0x34
+  // todo this one is hard.
+  u8 v = readByte(globalState.hl.v);
+  writeByte(increment(v), globalState.hl.v);
+  globalState.pc++;
+  globalState.cycleCount += 12;
+}
+
+u8 decrement(u8 in) {
+  u8 result = in - (u8)1;
+  if(getCarryFlag()) {
+    clearAllFlags();
+    setCarryFlag();
+  } else {
+    clearAllFlags();
+  }
+  setSubtractFlag();
+  if(result == 0) {
+    setZeroFlag();
+  }
+  if((result & 0xf) == 0xf) {
+    setHalfCarryFlag();
+  }
+  return result;
+}
+
+void DEC_A(u8 opcode) { // 0x3d
+  globalState.a = decrement(globalState.a);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void DEC_B(u8 opcode) { // 0x05
+  globalState.bc.hi = decrement(globalState.bc.hi);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void DEC_C(u8 opcode) { // 0x0d
+  globalState.bc.lo = decrement(globalState.bc.lo);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void DEC_D(u8 opcode) { // 0x15
+  globalState.de.hi = decrement(globalState.de.hi);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void DEC_E(u8 opcode) { // 0x1d
+  globalState.de.lo = decrement(globalState.de.lo);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void DEC_H(u8 opcode) { // 0x25
+  globalState.hl.hi = decrement(globalState.hl.hi);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void DEC_L(u8 opcode) { // 0x2d
+  globalState.hl.lo = decrement(globalState.hl.lo);
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+void DEC_DHL(u8 opcode) { // 0x35
+  // todo this one is hard.
+  u8 v = readByte(globalState.hl.v);
+  writeByte(decrement(v), globalState.hl.v);
+  globalState.pc++;
+  globalState.cycleCount += 12;
+}
+
+void addToHl(u16 number) {
+  int result = globalState.hl.v + number;
+  if(getZeroFlag()) {
+    clearAllFlags();
+    setZeroFlag();
+  } else {
+    clearAllFlags();
+  }
+  if(result & 0x10000) {
+    setCarryFlag();
+  }
+  if((globalState.hl.v ^ number ^ (result & 0xffff)) & 0x1000) {
+    setHalfCarryFlag();
+  }
+  globalState.hl.v = (u16)result;
+}
+
+void ADD_HL_BC(u8 opcode) { // 09
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  addToHl(globalState.bc.v);
+}
+
+void ADD_HL_DE(u8 opcode) { // 19
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  addToHl(globalState.de.v);
+}
+
+void ADD_HL_HL(u8 opcode) { // 29
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  addToHl(globalState.hl.v);
+}
+
+void ADD_HL_SP(u8 opcode) { // 39
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  addToHl(globalState.sp);
+}
+
+void ADD_SP_n(u8 opcode) { // E8
+  s8 number = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.cycleCount += 16;
+  int result = globalState.sp + number;
+  clearAllFlags();
+  if(((globalState.sp ^ number ^ (result & 0xffff)) & 0x100) == 0x100) {
+    setCarryFlag();
+  }
+  if(((globalState.sp ^ number ^ (result & 0xffff)) & 0x10) == 0x10) {
+    setHalfCarryFlag();
+  }
+  globalState.sp = (u16)result;
+}
+
+// 16 bit incs
+void INC_BC(u8 opcode) { // 03
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.v++;
+}
+
+void INC_DE(u8 opcode) { // 13
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.v++;
+}
+
+void INC_HL(u8 opcode) { // 23
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.v++;
+}
+
+void INC_SP(u8 opcode) { // 33
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.sp++;
+}
+
+// 16 bit decs
+void DEC_BC(u8 opcode) { // 0B
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.bc.v--;
+}
+
+void DEC_DE(u8 opcode) { // 1B
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.de.v--;
+}
+
+void DEC_HL(u8 opcode) { // 2B
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.hl.v--;
+}
+
+void DEC_SP(u8 opcode) { // 3B
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.sp--;
+}
+
+void DAA(u8 opcode) { // 0x27
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  u8 a = globalState.a;
+
+  if(!getSubtractFlag()) {
+    if(getCarryFlag() || a > 0x99) {
+      a += 0x60;
+      setCarryFlag();
+    }
+    if(getHalfCarryFlag() || (a & 0x0f) > 0x09) {
+      a += 0x6;
+    }
+  } else {
+    if(getCarryFlag()) {
+      a -= 0x60;
+    }
+    if(getHalfCarryFlag()) {
+      a -= 0x6;
+    }
+  }
+  clearZeroFlag();
+  clearHalfCarryFlag();
+  if(a == 0) {
+    setZeroFlag();
+  }
+  globalState.a = (u8)a;
+}
+
+void CPL(u8 opcode) { // 0x2f
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = ~globalState.a;
+  setHalfCarryFlag();
+  setSubtractFlag();
+}
+
+void CCF(u8 opcode) { // 0x3f
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  if(getCarryFlag()) {
+    clearCarryFlag();
+  } else {
+    setCarryFlag();
+  }
+  clearHalfCarryFlag();
+  clearSubtractFlag();
+}
+
+void SCF(u8 opcode) { // 0x37
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  clearHalfCarryFlag();
+  clearSubtractFlag();
+  setCarryFlag();
+}
+
+void NOP(u8 opcode) { // 00
+  globalState.pc++;
+  globalState.cycleCount += 4;
+}
+
+
+void HALT(u8 opcode) { // 76
+  globalState.cycleCount += 4;
+  globalState.pc++;
+  //if(globalState.ime) {
+    globalState.halt = true;
+  //}
+
+}
+
+void STOP(u8 opcode) { // 10 (00)
+  printf("stop not yet implemented\n");
+  assert(false);
+}
+
+void DI(u8 opcode) { // F3
+  // todo instruction after bs
+  globalState.ime = 0;
+  globalState.cycleCount += 4;
+  globalState.pc++;
+//  printf("di not yet implemented\n");
+//  assert(false);
+}
+
+void EI(u8 opcode) { // FB
+  globalState.ime = 1;
+  globalState.cycleCount += 4;
+  globalState.pc++;
+}
+
+void RLCA(u8 opcode) { // 07
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = rlcReg(globalState.a, true);
+}
+
+void RLA(u8 opcode) { // 17
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = rlReg(globalState.a, true);
+}
+
+void RRCA(u8 opcode) { // 0x0f
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = rrc(globalState.a, true);
+}
+
+void RRA(u8 opcode) { // 0x1f
+  globalState.pc++;
+  globalState.cycleCount += 4;
+  globalState.a = rr(globalState.a, true);
+}
+
+void JP_nn(u8 opcodes) { // 0xc3
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  globalState.pc += 2;
+  globalState.cycleCount += 12;
+  globalState.pc = addr;
+}
+
+void JP_NZ_nn(u8 opcodes) { // 0xc2
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  globalState.pc += 2;
+  if(!getZeroFlag()) {
+    globalState.pc = addr;
+  }
+  globalState.cycleCount += 12;
+}
+
+void JP_Z_nn(u8 opcodes) { // 0xca
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  globalState.pc += 2;
+  if(getZeroFlag()) {
+    globalState.pc = addr;
+  }
+  globalState.cycleCount += 12;
+}
+
+void JP_NC_nn(u8 opcodes) { // 0xd2
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  globalState.pc += 2;
+  if(!getCarryFlag()) {
+    globalState.pc = addr;
+  }
+  globalState.cycleCount += 12;
+}
+
+void JP_C_nn(u8 opcodes) { // 0xda
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  globalState.pc += 2;
+  if(getCarryFlag()) {
+    globalState.pc = addr;
+  }
+  globalState.cycleCount += 12;
+}
+
+void JP_HL(u8 opcodes) { // 0xe9
+  globalState.pc = globalState.hl.v;
+  globalState.cycleCount += 4;
+}
+
+void JR_n(u8 opcode) { // 18
+  s8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  globalState.pc += imm;
+  globalState.cycleCount += 8;
+}
+
+void JR_NZ(u8 opcode) { // 20
+  s8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  if(!getZeroFlag()){
+    globalState.pc += imm;
+  }
+  globalState.cycleCount += 8;
+}
+
+void JR_Z(u8 opcode) { // 28
+  s8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  if(getZeroFlag()){
+    globalState.pc += imm;
+  }
+  globalState.cycleCount += 8;
+}
+
+void JR_NC(u8 opcode) {  // 30
+  s8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  if(!getCarryFlag()){
+    globalState.pc += imm;
+  }
+  globalState.cycleCount += 8;
+}
+
+void JR_C(u8 opcode) { // 38
+  s8 imm = readByte(++globalState.pc);
+  globalState.pc++;
+  if(getCarryFlag()){
+    globalState.pc += imm;
+  }
+  globalState.cycleCount += 8;
+}
+
+
+void CALL_nn(u8 opcode) { // 0xCD
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  //printf("call address 0x%x\n", addr);
+  globalState.pc += 2;
+  writeU16(globalState.pc, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+  globalState.pc = addr;
+  globalState.cycleCount += 12;
+}
+
+void CALL_NZ(u8 opcode) { // 0xC4
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  globalState.pc += 2;
+  if(!getZeroFlag()) {
+    writeU16(globalState.pc, globalState.sp - (u16)2);
+    globalState.sp -= 2;
+    globalState.pc = addr;
+  }
+  globalState.cycleCount += 12;
+}
+
+void CALL_Z(u8 opcode) { // 0xCc
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  globalState.pc += 2;
+  if(getZeroFlag()) {
+    writeU16(globalState.pc, globalState.sp - (u16)2);
+    globalState.sp -= 2;
+    globalState.pc = addr;
+  }
+  globalState.cycleCount += 12;
+}
+
+
+void CALL_NC(u8 opcode) { // 0d4
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  globalState.pc += 2;
+  if(!getCarryFlag()) {
+    writeU16(globalState.pc, globalState.sp - (u16)2);
+    globalState.sp -= 2;
+    globalState.pc = addr;
+  }
+  globalState.cycleCount += 12;
+}
+
+void CALL_C(u8 opcode) { // 0xdc
+  globalState.pc++;
+  u16 addr = readU16(globalState.pc);
+  globalState.pc += 2;
+  if(getCarryFlag()) {
+    writeU16(globalState.pc, globalState.sp - (u16)2);
+    globalState.sp -= 2;
+    globalState.pc = addr;
+  }
+  globalState.cycleCount += 12;
+}
+
+void REST_00(u8 opcode) { // 0xc7
+  globalState.pc++;
+  writeU16(globalState.pc, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+  globalState.pc = 0x0000;
+  globalState.cycleCount += 32;
+}
+
+void REST_08(u8 opcode) { // 0xcf
+  globalState.pc++;
+  writeU16(globalState.pc, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+  globalState.pc = 0x0008;
+  globalState.cycleCount += 32;
+}
+
+void REST_10(u8 opcode) { // 0xd7
+  globalState.pc++;
+  writeU16(globalState.pc, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+  globalState.pc = 0x0010;
+  globalState.cycleCount += 32;
+}
+
+void REST_18(u8 opcode) { // 0xdf
+  globalState.pc++;
+  writeU16(globalState.pc, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+  globalState.pc = 0x0018;
+  globalState.cycleCount += 32;
+}
+
+void REST_20(u8 opcode) { // 0xe7
+  globalState.pc++;
+  writeU16(globalState.pc, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+  globalState.pc = 0x0020;
+  globalState.cycleCount += 32;
+}
+
+void REST_28(u8 opcode) { // 0xef
+  globalState.pc++;
+  writeU16(globalState.pc, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+  globalState.pc = 0x0028;
+  globalState.cycleCount += 32;
+}
+
+void REST_30(u8 opcode) { // 0xf7
+  globalState.pc++;
+  writeU16(globalState.pc, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+  globalState.pc = 0x0030;
+  globalState.cycleCount += 32;
+}
+
+void REST_38(u8 opcode) { // 0xff
+  globalState.pc++;
+  writeU16(globalState.pc, globalState.sp - (u16)2);
+  globalState.sp -= 2;
+  globalState.pc = 0x0038;
+  globalState.cycleCount += 32;
+}
+
+void RET(u8 opcode) { // 0xc9
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.pc = readU16(globalState.sp);
+  globalState.sp += 2;
+}
+
+void RET_NZ(u8 opcode) { // 0xc0
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  if(!getZeroFlag()) {
+    globalState.pc = readU16(globalState.sp);
+    globalState.sp += 2;
+  }
+}
+
+void RET_Z(u8 opcode) { // 0xc8
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  if(getZeroFlag()) {
+    globalState.pc = readU16(globalState.sp);
+    globalState.sp += 2;
+  }
+}
+
+void RET_NC(u8 opcode) { // 0xd0
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  if(!getCarryFlag()) {
+    globalState.pc = readU16(globalState.sp);
+    globalState.sp += 2;
+  }
+}
+
+void RET_C(u8 opcode) { // 0xd8
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  if(getCarryFlag()) {
+    globalState.pc = readU16(globalState.sp);
+    globalState.sp += 2;
+  }
+}
+
+void RETI(u8 opcode) { // 0xd9
+  globalState.pc++;
+  globalState.cycleCount += 8;
+  globalState.pc = readU16(globalState.sp);
+  globalState.sp += 2;
+  globalState.ime = 1;
+}
+
+// normal opcode handler table
+static OpcodeHandler* opcodes[256] =
+        {NOP,          LD_BC_nn,     LD_DBC_A,     INC_BC,       INC_B,        DEC_B,        LD_B_n,       RLCA,         // 0x0 - 0x7
+         LD_Dnn_SP,    ADD_HL_BC,    LD_A_DBC,     DEC_BC,       INC_C,        DEC_C,        LD_C_n,       RRCA,         // 0x8 - 0xf
+         STOP,         LD_DE_nn,     LD_DDE_A,     INC_DE,       INC_D,        DEC_D,        LD_D_n,       RLA,          // 0x10 - 0x17
+         JR_n,         ADD_HL_DE,    LD_A_DDE,     DEC_DE,       INC_E,        DEC_E,        LD_E_n,       RRA,          // 0x18 - 0x1f
+         JR_NZ,        LD_HL_nn,     LDI_DHL_A,    INC_HL,       INC_H,        DEC_H,        LD_H_n,       DAA,          // 0x20 - 0x27
+         JR_Z,         ADD_HL_HL,    LDI_A_DHL,    DEC_HL,       INC_L,        DEC_L,        LD_L_n,       CPL,          // 0x28 - 0x2f
+         JR_NC,        LD_SP_nn,     LDD_DHL_A,    INC_SP,       INC_DHL,      DEC_DHL,      LD_DHL_n,     SCF,          // 0x30 - 0x37
+         JR_C,         ADD_HL_SP,    LDD_A_DHL,    DEC_SP,       INC_A,        DEC_A,        LD_A_n,       CCF,          // 0x38 - 0x3f
+         LD_B_B,       LD_B_C,       LD_B_D,       LD_B_E,       LD_B_H,       LD_B_L,       LD_B_DHL,     LD_B_A,       // 0x40 - 0x47
+         LD_C_B,       LD_C_C,       LD_C_D,       LD_C_E,       LD_C_H,       LD_C_L,       LD_C_DHL,     LD_C_A,       // 0x48 - 0x4f
+         LD_D_B,       LD_D_C,       LD_D_D,       LD_D_E,       LD_D_H,       LD_D_L,       LD_D_DHL,     LD_D_A,       // 0x50 - 0x57
+         LD_E_B,       LD_E_C,       LD_E_D,       LD_E_E,       LD_E_H,       LD_E_L,       LD_E_DHL,     LD_E_A,       // 0x58 - 0x5f
+         LD_H_B,       LD_H_C,       LD_H_D,       LD_H_E,       LD_H_H,       LD_H_L,       LD_H_DHL,     LD_H_A,       // 0x60 - 0x67
+         LD_L_B,       LD_L_C,       LD_L_D,       LD_L_E,       LD_L_H,       LD_L_L,       LD_L_DHL,     LD_L_A,       // 0x68 - 0x6f
+         LD_DHL_B,     LD_DHL_C,     LD_DHL_D,     LD_DHL_E,     LD_DHL_H,     LD_DHL_L,     HALT,         LD_DHL_A,     // 0x70 - 0x77
+         LD_A_B,       LD_A_C,       LD_A_D,       LD_A_E,       LD_A_H,       LD_A_L,       LD_A_DHL,     LD_A_A      , // 0x78 - 0x7f
+         ADD_B,        ADD_C,        ADD_D,        ADD_E,        ADD_H,        ADD_L,        ADD_DHL,      ADD_A,        // 0x80 - 0x87
+         ADC_B,        ADC_C,        ADC_D,        ADC_E,        ADC_H,        ADC_L,        ADC_DHL,      ADC_A,        // 0x88 - 0x8f
+         SUB_B,        SUB_C,        SUB_D,        SUB_E,        SUB_H,        SUB_L,        SUB_DHL,      SUB_A,        // 0x90 - 0x97
+         SBC_B,        SBC_C,        SBC_D,        SBC_E,        SBC_H,        SBC_L,        SBC_DHL,      SBC_A,        // 0x98 - 0x9f
+         AND_B,        AND_C,        AND_D,        AND_E,        AND_H,        AND_L,        AND_DHL,      AND_A,        // 0xa0 - 0xa7
+         XOR_B,        XOR_C,        XOR_D,        XOR_E,        XOR_H,        XOR_L,        XOR_DHL,      XOR_A,        // 0xa8 - 0xaf
+         OR_B,         OR_C,         OR_D,         OR_E,         OR_H,         OR_L,         OR_DHL,       OR_A,         // 0xb0 - 0xb7
+         CP_B,         CP_C,         CP_D,         CP_E,         CP_H,         CP_L,         CP_DHL,       CP_A,         // 0xb8 - 0xbf
+         RET_NZ,       POP_BC,       JP_NZ_nn,     JP_nn,        CALL_NZ,      PUSH_BC,      ADD_n,        REST_00,      // 0xc0 - 0xc7
+         RET_Z,        RET,          JP_Z_nn,      DecodeCB,     CALL_Z,       CALL_nn,      ADC_n,        REST_08,      // 0xc8 - 0xcf
+         RET_NC,       POP_DE,       JP_NC_nn,     invHandler,   CALL_NC,      PUSH_DE,      SUB_n,        REST_10,      // 0xd0 - 0xd7
+         RET_C,        RETI,         JP_C_nn,      invHandler,   CALL_C,       invHandler,   SBC_n,        REST_18,      // 0xd8 - 0xdf
+         LD_FF00_n_A,  POP_HL,       LD_FF00_C_A,  invHandler,   invHandler,   PUSH_HL,      AND_n,        REST_20,      // 0xe0 - 0xe7
+         ADD_SP_n,     JP_HL,        LD_Dnn_A,     invHandler,   invHandler,   invHandler,   XOR_n,        REST_28,      // 0xe8 - 0xef
+         LD_A_FF00_n,  POP_AF,       LD_A_FF00_C,  DI,           invHandler,   PUSH_AF,      OR_n,         REST_30,      // 0xf0 - 0xf7
+         LD_HL_SP_n,   LD_SP_HL,     LD_A_Dnn,     EI,           invHandler,   invHandler,   CP_n,         REST_38};     // 0xf8 - 0xff
+
+
+// reset the globalState structure, including registers and timers
+void resetCpu() {
+  globalState.f = 0x1; // or 0x11? (
+  globalState.a = 0xb0; // maybe f, a swap??
+  globalState.bc.v = 0x0013;
+  globalState.de.v = 0x00d8;
+  globalState.hl.v = 0x014d;
+  globalState.sp = 0xfffe;
+  globalState.pc = 0x0;
+  globalState.halt = false;
+  globalState.cycleCount = 0;
+  globalState.divOffset = 0;
+  globalState.timSubcount = 0;
+}
+
+// set the globalState so the next call to step() will run an ISR
+void interrupt(u16 addr) {
+  globalState.ime = 0;                                // disable interrupts
+  writeU16(globalState.pc, globalState.sp - (u16)2);  // push pc to stack
+  globalState.sp -= 2;                                // push pc to stack
+  globalState.cycleCount += 12;                       // timing
+  globalState.pc = addr;                              // jump to ISR
+}
+
+// timer speeds: once this number of clock cycles has elapsed, the timer ticks.
+static u32 timReset[4] = {(1 << 10), (1 << 4), (1 << 6), (1 << 8)};
+
+// step the CPU 1 instruction
+// returns number of clock cycles
+u32 cpuStep() {
+  uint64_t oldCycleCount = globalState.cycleCount;
+
+  // update div register
+  globalMemState.ioRegs[IO_DIV] = (u8)((globalState.cycleCount - globalState.divOffset) >> 8);
+
+  // execute, if we aren't halted.
+  if(!globalState.halt) {
+    // fetch opcode
+    u8 opcode = readByte(globalState.pc);
+    // execute opcode
+    opcodes[opcode](opcode);
+  }
+
+    assert(false);
+  // interrupts
+  if(globalState.ime && globalMemState.upperRam[0x7f] && globalMemState.ioRegs[IO_IF]) {
+
+    // mask interrupts with the interrupt enable register at 0xffff.
+    u8 interrupts = globalMemState.upperRam[0x7f] & globalMemState.ioRegs[IO_IF];
+
+    if(interrupts & 0x01) {
+      globalMemState.ioRegs[IO_IF] &= ~1;
+      interrupt(VBLANK_INTERRUPT);
+      globalState.halt = false;
+    } else if(interrupts & 0x02) {
+      globalMemState.ioRegs[IO_IF] &= ~2;
+      interrupt(LCDC_INTERRUPT);
+      globalState.halt = false;
+    } else if(interrupts & 0x04) {
+      globalMemState.ioRegs[IO_IF] &= ~4;
+      interrupt(TIMER_INTERRUPT);
+      globalState.halt = false;
+    } else if(interrupts & 0x08) {
+      globalMemState.ioRegs[IO_IF] &= ~8;
+      interrupt(SERIAL_INTERRUPT);
+      globalState.halt = false;
+    } else if(interrupts & 0x10) {
+      globalMemState.ioRegs[IO_IF] &= ~0x10;
+      interrupt(HIGH_TO_LOW_P10_P13);
+      globalState.halt = false;
+    }
+  }
+
+  // even if we have IME off, and we're halted, we're supposed to check IF and IE register
+  // this won't fire an interrupt, but will get us out of halt
+  // this behavior isn't well documented, but was required to pass cpu_instr.gb
+  // (though none of the games seem to need it...)
+  if(globalState.halt) {
+    globalState.cycleCount += 800; // just to keep ticking the timer...
+    u8 interrupts = globalMemState.upperRam[0x7f] & globalMemState.ioRegs[IO_IF];
+    if(interrupts & 0x01) {
+      globalState.halt = false;
+    } else if(interrupts & 0x02) {
+      globalState.halt = false;
+    } else if(interrupts & 0x04) {
+      globalState.halt = false;
+    } else if(interrupts & 0x08) {
+      globalState.halt = false;
+    } else if(interrupts & 0x10) {
+      globalState.halt = false;
+    }
+  }
+
+
+  // cycle count
+  uint64_t cyclesThisIteration = globalState.cycleCount - oldCycleCount;
+
+  // update timer
+  u8 tac = globalMemState.ioRegs[IO_TAC];
+  bool ten = ((tac >> 2) & 1) != 0; // timer enable?
+  if(ten) {
+    u8 tclk = (tac & (u8)3);     // timer speed
+    globalState.timSubcount += cyclesThisIteration;
+    if(globalState.timSubcount >= timReset[tclk]) { // timer tick
+      globalState.timSubcount = 0;
+      u8 timv = globalMemState.ioRegs[IO_TIMA]; // check for overflow
+      if(timv == 255) {
+        globalMemState.ioRegs[IO_IF] |= 4; // set interrupt
+        globalMemState.ioRegs[IO_TIMA] = globalMemState.ioRegs[IO_TMA]; // reset
+      } else {
+        globalMemState.ioRegs[IO_TIMA] = timv + (u8)1; // increment.
+      }
+    }
+  }
+
+  return (u32)cyclesThisIteration;
+}
+
+bool getZeroFlag() {
+  return (globalState.f & 0x80) != 0;
+}
+
+bool getSubtractFlag() {
+  return (globalState.f & 0x40) != 0;
+}
+
+bool getHalfCarryFlag() {
+  return (globalState.f & 0x20) != 0;
+}
+
+bool getCarryFlag() {
+  return (globalState.f & 0x10) != 0;
+}
+
+void setZeroFlag() {
+  globalState.f = globalState.f | (u8)0x80;
+}
+
+void setSubtractFlag() {
+  globalState.f = globalState.f | (u8)0x40;
+}
+
+void setHalfCarryFlag() {
+  globalState.f = globalState.f | (u8)0x20;
+}
+
+void setCarryFlag() {
+  globalState.f = globalState.f | (u8)0x10;
+}
+
+void clearZeroFlag(){
+  globalState.f = globalState.f & ~((u8)0x80);
+}
+
+void clearSubtractFlag(){
+  globalState.f = globalState.f & ~((u8)0x40);
+}
+
+void clearHalfCarryFlag(){
+  globalState.f = globalState.f & ~((u8)0x20);
+}
+
+void clearCarryFlag(){
+  globalState.f = globalState.f & ~((u8)0x10);
+}
+
+void clearAllFlags() {
+  clearZeroFlag();
+  clearSubtractFlag();
+  clearHalfCarryFlag();
+  clearCarryFlag();
+}
\ No newline at end of file
diff -r 1f728d08b3a7 -r c9afe1a7b423 cpu.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cpu.h	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,49 @@
+#ifndef GBC_CPU_H
+#define GBC_CPU_H
+
+#include "types.h"
+
+// 16 bit register
+union reg {
+  u16 v;
+  struct {
+    u8 lo;
+    u8 hi;
+  };
+};
+
+struct CpuState {
+  // registers
+  reg bc, de, hl;
+  u8 f;
+  u8 a;
+  u16 sp, pc;
+
+  bool halt;
+  uint64_t cycleCount;
+  uint64_t divOffset;
+  u8 ime;
+  u32 timSubcount;
+};
+
+// external interface:
+extern CpuState globalState;
+void resetCpu(); // reinitialize the cpu
+u32 cpuStep();   // step 1 instruction, returns number of clock cycles elapsed
+
+
+bool getZeroFlag();
+bool getSubtractFlag();
+bool getHalfCarryFlag();
+bool getCarryFlag();
+void setZeroFlag();
+void clearZeroFlag();
+void setSubtractFlag();
+void clearSubtractFlag();
+void setHalfCarryFlag();
+void clearHalfCarryFlag();
+void setCarryFlag();
+void clearCarryFlag();
+void clearAllFlags();
+
+#endif //GBC_CPU_H
\ No newline at end of file
diff -r 1f728d08b3a7 -r c9afe1a7b423 graphics_display.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graphics_display.cpp	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,42 @@
+// SDL display code
+
+#include "types.h"
+//static SDL_Window* window;
+//static SDL_Renderer* renderer;
+//static SDL_Texture* tex;
+static u8* fb;
+
+#define SCALE 4 // pixel scaling
+
+void initGraphics(u8* frameBuffer) {
+//  SDL_Init(SDL_INIT_VIDEO);
+//  window = SDL_CreateWindow("WORM-BOY", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 160 * SCALE, 144 * SCALE, SDL_WINDOW_OPENGL);
+//  renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
+//  tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA8888, SDL_TEXTUREACCESS_STREAMING, 160 * SCALE, 144 * SCALE);
+  fb = frameBuffer;
+}
+
+void updateGraphics() {
+//  int pitch;
+//  u8* sdlFB;
+//  SDL_LockTexture(tex, nullptr, (void**)(&sdlFB), &pitch);
+//  for(u32 xIn = 0; xIn < 160; xIn++) {
+//    for(u32 yIn = 0; yIn < 144; yIn++) {
+//      u32 outCoord = (xIn * SCALE) + (yIn * SCALE * 160 * SCALE);
+//      u32 inCoord = xIn + yIn * 160;
+//      for(u32 xScale = 0; xScale < SCALE; xScale++) {
+//        for(u32 yScale = 0; yScale < SCALE; yScale++) {
+//          u32 outScaled = outCoord + xScale + (yScale * SCALE * 160);
+//          sdlFB[outScaled*4 + 0] = 255;
+//          sdlFB[outScaled*4 + 1] = fb[inCoord];
+//          sdlFB[outScaled*4 + 2] = fb[inCoord];
+//          sdlFB[outScaled*4 + 3] = fb[inCoord];
+//        }
+//      }
+//    }
+//  }
+//  SDL_UnlockTexture(tex);
+//  SDL_RenderCopy(renderer, tex, nullptr, nullptr);
+//  SDL_RenderPresent(renderer);
+}
+
diff -r 1f728d08b3a7 -r c9afe1a7b423 graphics_display.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graphics_display.h	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,9 @@
+#ifndef GBC_GRAPHICS_DISPLAY_H
+#define GBC_GRAPHICS_DISPLAY_H
+#include "types.h"
+
+void initGraphics(u8* frameBuffer); // initialize graphics
+void updateGraphics();              // draw whatever is in the fb.  format is array of u8's, 160x140, intensity.
+
+
+#endif //GBC_GRAPHICS_DISPLAY_H
diff -r 1f728d08b3a7 -r c9afe1a7b423 main.cpp
--- a/main.cpp	Thu Oct 04 12:37:25 2018 +0000
+++ b/main.cpp	Sun Jan 13 19:00:10 2019 +0000
@@ -2,6 +2,12 @@
 #include <math.h>
 #include "main.h"
 #include "SDFileSystem.h"
+#include "platform.h"
+#include "types.h"
+#include "mem.h"
+#include "cpu.h"
+#include "graphics_display.h"
+#include "video.h"
 
 #define TEXT_LEVEL 5
 
@@ -26,6 +32,8 @@
 //DigitalOut sout(D8); //sync PA_9
 //DigitalOut vout(D7); //video PA_8
 
+bool wantSwap = false;
+
 DigitalOut dac0(PA_4);
 DigitalOut dac1(PA_5);
 DigitalOut dac2(PA_6);
@@ -34,9 +42,9 @@
 DigitalOut user_led(PB_14);
 int led_state = 1;
 
-// trigger horizontal line draw
-Ticker t;
 
+//uint8_t im_line_va_1[H_RES*V_RES];
+uint8_t* im_line_va_1;
 uint8_t bl_line_s[H_RES]; //blank line sync buffer
 uint8_t bl_line_v[H_RES]; //blank line video buffer
 uint8_t vb_line_s[H_RES]; //vertical sync, sync buffer
@@ -45,12 +53,13 @@
 
 //buffers
 uint8_t current_frame[YL][XL / 2];
-uint8_t im_line_va_1[H_RES*V_RES];
+
+volatile u8 bufferSelect = 0;
 uint8_t im_line_va_2[H_RES*V_RES];
+//uint8_t* im_line_va_2;
 
 //double buffered drawing
-uint8_t* im_line_va;
-uint8_t* im_back;
+u8* im_line_vas[2];
 
 uint16_t l=0; //current line of scan
 uint32_t tics = 0; //timer
@@ -68,7 +77,7 @@
 {
     for(int i = 0; i < H_RES; i++)
         for(int j = 0; j < V_RES; j++)
-            im_line_va[i+j*H_RES] = 0;    
+            im_line_vas[bufferSelect][i+j*H_RES] = 0;    
 }
 
 // initialize video buffers
@@ -101,36 +110,49 @@
     vb_line_s[2] = DAC_SYNC;
 }
 
+volatile uint8_t drawing = 0;
 // video interrupt
-void isr()
-{
+
+//void isr() __attribute__ ((section ("rw")));
+void isr() {
     uint8_t nop = 0, img = 0; //use nops or use wait_us
     uint8_t* sptr; //pointer to sync buffer for line
     uint8_t* vptr; //pointer to video buffer for line
     uint8_t* pptr; //porch
     
+   // drawing = (l > 30 && l < (30 + YL));
+    
+    //if(l > 180) {
+//        drawing = 1;
+//    }
     if (l < V_PORCH_SIZE) {
+        
         vptr = bl_line_v; 
         sptr = im_line_s;
         nop = 1;
     }
-    else if (l < YL + V_PORCH_SIZE) {
-        vptr = im_line_va + (l - 30) * H_RES; 
+    else if ((l < YL + V_PORCH_SIZE)) {
+        //drawing = l - V_PORCH_SIZE + 1;
+        //drawing = 1;
+        vptr = im_line_vas[bufferSelect] + (l - 30) * H_RES; 
         sptr = im_line_s;
         nop = 1;
         img = 1;
     }
-    else if (l < 254) { 
+    else if (l < 255) { 
+        //drawing = 0;
         vptr = bl_line_v; 
         sptr = bl_line_s; 
         nop = 0;
     } 
     else {
+
         vptr = vb_line_v;
         sptr = vb_line_s;
         nop = 1;
     }
     
+    
     pptr = img ? bl_line_v : vptr;
     
     if (nop) {
@@ -162,7 +184,8 @@
     //move to next line
     l++;
     tics++;
-    if(l > 255) l = 0;
+    if(l > 255) {l = 0;}
+    
 }
 
 // draw letter
@@ -175,7 +198,7 @@
     {
         for(uint16_t yp = 0; yp < 8; yp++)
         {
-            im_line_va[H_RES*(yp+y0) + xp + x0] = CHECK_BIT(letter[yp],8-xp)?TEXT_LEVEL:0;
+            im_line_vas[bufferSelect][H_RES*(yp+y0) + xp + x0] = CHECK_BIT(letter[yp],8-xp)?TEXT_LEVEL:0;
         }
     }
 }
@@ -215,11 +238,37 @@
     char_row = char_row_backup;
 }
 
+extern "C" void TIM1_UP_TIM10_IRQHandler(void) {
+    
+    if (TIM1->SR & TIM_SR_UIF ) {
+        isr();
+        //printf("oof\n");
+    }
+    TIM1->SR = 0x00;
+}
+
+void brems_init() {
+    RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
+    NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn); //Enable TIM1 IRQ
+
+    TIM1->DIER |= TIM_DIER_UIE; //enable update interrupt
+    TIM1->CR1 = 0x00; //CMS = 10, interrupt only when counting up
+    TIM1->CR1 |= TIM_CR1_ARPE; //autoreload on, 
+    TIM1->RCR |= 0x00; //update event once per up/down count of tim1 
+    TIM1->EGR |= TIM_EGR_UG;
+    
+    TIM1->PSC = 0x00; //no prescaler, timer counts up in sync with the peripheral clock
+    TIM1->ARR = (int) (2 * (float) 9e7 / 15625);
+    TIM1->CCER |= (TIM_CCER_CC1NP); //rising edge aligned, non-inverting
+    TIM1->CR1 |= TIM_CR1_CEN;
+}
+
 int main()
 {
-    uint8_t *buf_swap_tmp;
-    im_line_va = im_line_va_1;
-    im_back = im_line_va_2;
+    //im_line_va_1 = (u8*)malloc(V_RES * H_RES);
+    //im_line_va_2 = im_line_va_1 + (V_RES*H_RES);
+    im_line_vas[0] = im_line_va_2;
+    im_line_vas[1] = im_line_va_1;
     
     //init serial
     pc.baud(115200);
@@ -228,7 +277,8 @@
     //init buffers
     init_buffers();
     //init timer
-    t.attach_us(&isr,64);
+    //t.attach_us(&isr,64);
+    brems_init();
     
     /*init SD card, wait for card insertion*/ 
     SDFileSystem sd(DI, DO, SCK, CS, "sd");
@@ -237,35 +287,72 @@
         wait(0.5);
     }
     
-    FILE *fp;
-    
-    fp = fopen("/sd/man.worm.starwars", "rb");
+    printf("screen %d x %d (buffer %d x %d, %d bytes)\n",
+           XL,YL,H_RES,V_RES,H_RES*V_RES);
+    printf("starting the WORM BOY\r\n");
+    FileLoadData file = loadFile("/sd/mario.gb");
+    printf("done with loadfile\r\n");
+    initMem(file);
+    printf("done with initMem\n");
+    initVideo(nullptr);
+    printf("done with initvideo\n");
+//    FILE *fp;
+//    
+//    fp = fopen("/sd/man.worm.mc", "rb");
             
     uint32_t old_tics = tics;
+    u32 icount = 0;
     for(;;)
     { 
-        if (feof(fp)) rewind(fp);
-        fread(current_frame, 1, VIDEO_FRAME_SIZE, fp);
-        for(int x = 0; x < XL; x += 2)
-        {
-            for(int y = 0; y < YL; y++)
-            {
-               im_back[H_RES*(y+Y0) + x + X0] = current_frame[YL - y][x >> 1] >> 4;
-               im_back[H_RES*(y+Y0) + x + 1 + X0] = current_frame[YL - y][x >> 1];
-            }
-        }
+    
+    icount++;
+//    if(icount > 0x40000) {
+//        icount = 0;
+//        bufferSelect = !bufferSelect;
+//    }
+    
+    if((globalState.cycleCount&0xfffff) > 0x80000 & globalState.cycleCount < 0x12fffff) {
+        keyboard.start = true;
+    } else {
+        keyboard.start = false;
+    }
+
+      u32 cpuCycles = cpuStep();
+      stepVideo(cpuCycles);
+
+//    wait_us(20);
+//    for(int x = 0; x < XL; x++) {
+//        for(int y = 0; y < YL; y++) {
+//            im_back[H_RES*(y+Y0) + x + X0] = 6 * (((x+y)>>1)&1);
+//        }
+//    }
+    
+//        if (feof(fp)) rewind(fp);
+//        fread(current_frame, 1, VIDEO_FRAME_SIZE, fp);
+//        for(int x = 0; x < XL; x += 2)
+//        {
+//            for(int y = 0; y < YL; y++)
+//            {
+//               im_back[H_RES*(y+Y0) + x + X0] = current_frame[YL - y][x >> 1] >> 4;
+//               im_back[H_RES*(y+Y0) + x + 1 + X0] = current_frame[YL - y][x >> 1];
+//            }
+//        }
         
-        buf_swap_tmp = im_line_va;
-        im_line_va = im_back;
-        im_back = buf_swap_tmp;
+//        buf_swap_tmp = im_line_va;
+//        im_line_va = im_back;
+//        im_back = buf_swap_tmp;
+        
+
         
-        led_state = !led_state;
-        user_led = led_state; 
-
-        char status_string[20];
-        sprintf(status_string, "%.1f", 15625.f / (float)(tics - old_tics));
-        old_tics = tics;
-        set_status_string(status_string);        
+//        if(globalState.cycleCount > 1000000) {
+//            printf("instr 100000\n");
+//        }
+//
+//        char status_string[20];
+//        sprintf(status_string, "%d", globalState.cycleCount);
+////        sprintf(status_string, "%.1f", 15625.f / (float)(tics - old_tics));
+////        old_tics = tics;
+//        set_status_string(status_string);        
     }
 }
 
diff -r 1f728d08b3a7 -r c9afe1a7b423 main.h
--- a/main.h	Thu Oct 04 12:37:25 2018 +0000
+++ b/main.h	Sun Jan 13 19:00:10 2019 +0000
@@ -5,10 +5,10 @@
 void new_line();
 void clear_all_text();
 void draw_gfx_line(float x0, float y0, float x1, float y1);
-
+extern volatile uint8_t drawing;
 // Buffer sizes
-#define V_RES 96
-#define H_RES 279
+#define V_RES 144
+#define H_RES (279 - 80 + 20 - 4)
 
 // Porches
 #define V_PORCH_SIZE 30
@@ -17,8 +17,8 @@
 // good new stuff
 #define X0 50  // start of image in X
 #define Y0 0  // start of image in Y
-#define XL 224 // 25 chars
-#define YL 96 // 20 chars
+#define XL (224 - 80 + 20 - 4) // 25 chars
+#define YL 144 // 20 chars
 
 //video
 #define VIDEO_FRAME_SIZE (XL * YL / 2)
@@ -29,7 +29,9 @@
 #define SCK PB_10
 #define CS PB_12
 
-extern uint8_t *im_line_va;
+extern uint8_t *im_line_vas[];
+extern volatile uint8_t bufferSelect;
+extern uint32_t tics;
 
 #define set_pixel(x, y, color) im_line_va[H_RES*((y)+Y0) + (x) + X0] = (color)
 
diff -r 1f728d08b3a7 -r c9afe1a7b423 mem.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mem.cpp	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,721 @@
+#define NDEBUG
+#include <assert.h>
+#include <stdlib.h>
+#include <mbed.h>
+
+#include "mem.h"
+#include "platform.h"
+
+#define DANGER_MODE
+
+MemState globalMemState;
+
+void CartInfo::print() {
+  printf("Gameboy Cartridge\n"
+         "\ttitle: %s\n"
+         "\tisColor: 0x%x\n"
+         "\tSGB: 0x%x\n"
+         "\tcartType: 0x%x\n"
+         "\tromSize: 0x%x\n"
+         "\tramSize: 0x%x\n"
+         "\tnotJapan: 0x%x\n",
+         title, isColor, SGB, (u8)cartType, (u8)romSize, (u8)ramSize, notJapan);
+}
+
+// number of banks for given cartridge types
+static const u8 romSizeIDToNBanks[7] = {2,4,8,16,32,64,128};
+static const u8 ramSizeIDToNBanks[5] = {0,1,1,4,4};
+static u8* fileData = nullptr;
+
+void saveGame() {
+  FileLoadData fld;
+  fld.size = 0x2000 * globalMemState.nRamBanks;
+  fld.data = globalMemState.mappedRamAllocation;
+  saveFile("savegame.gam", fld);
+}
+
+void loadGame() {
+  FileLoadData fld = loadFile("savegame.gam");
+  memcpy(globalMemState.mappedRamAllocation, fld.data, 0x2000 * globalMemState.nRamBanks);
+}
+
+
+
+
+void initMem(FileLoadData file) {
+  CartInfo* cartInfo = (CartInfo*)(file.data + CART_INFO_ADDR);
+  cartInfo->print();
+  fileData = file.data;
+  printf("ROM size: 0x%x bytes\n", file.size);
+
+
+  globalMemState.inBios = true;       // start in BIOS mode
+  globalMemState.rom0 = file.data;    // ROM-bank 0 is the bottom of the cartridge
+
+  // initialize everything to zero
+  globalMemState.mappedRom = nullptr;
+  globalMemState.nRamBanks = 0;
+  globalMemState.nRomBanks = 0;
+  globalMemState.vram = nullptr;
+  globalMemState.mappedRam = nullptr;
+  globalMemState.disabledMappedRam = nullptr;
+  globalMemState.mappedRamAllocation = nullptr;
+  globalMemState.internalRam = nullptr;
+  globalMemState.upperRam = nullptr;
+
+  switch(cartInfo->cartType) {
+    case ROM_ONLY:
+      globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default
+      break;
+
+    case ROM_MBC1:
+      globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default
+      globalMemState.mbcType = 1;
+      if((u8)cartInfo->romSize < 7) {
+        globalMemState.nRomBanks = romSizeIDToNBanks[(u8)cartInfo->romSize]; // has ROM banks
+      } else {
+        printf("unknown number of rom banks\n");
+        assert(false);
+      }
+      break;
+
+    case ROM_MBC1_RAM:
+      globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default
+      globalMemState.mbcType = 1;
+      if((u8)cartInfo->romSize < 7) {
+        globalMemState.nRomBanks = romSizeIDToNBanks[(u8)cartInfo->romSize]; // has ROM banks
+      } else {
+        printf("unknown number of rom banks\n");
+        assert(false);
+      }
+
+      if((u8)cartInfo->ramSize < 5) {
+        globalMemState.nRamBanks = ramSizeIDToNBanks[(u8)cartInfo->ramSize]; // has RAM banks
+      } else {
+        printf("unknown number of ram banks\n");
+        assert(false);
+      }
+      break;
+
+    case ROM_MBC3_RAM_BATT:
+    case ROM_MBC3_TIMER_RAM_BATT:
+      globalMemState.mappedRom = file.data + 0x4000; // maps in upper ROM by default
+      globalMemState.mbcType = 3;
+      if((u8)cartInfo->romSize < 7) {
+        globalMemState.nRomBanks = romSizeIDToNBanks[(u8)cartInfo->romSize]; // has ROM banks
+      } else {
+        printf("unknown number of rom banks\n");
+        assert(false);
+      }
+
+      if((u8)cartInfo->ramSize < 5) {
+        globalMemState.nRamBanks = ramSizeIDToNBanks[(u8)cartInfo->ramSize]; // has RAM banks
+      } else {
+        printf("unknown number of ram banks\n");
+      }
+      break;
+    default:
+      printf("unknown cart type 0x%x\n", (u8)cartInfo->cartType);
+      assert(false);
+      break;
+  }
+
+  printf("mbc%d\n", globalMemState.mbcType);
+  printf("rom-banks: %d\n", globalMemState.nRomBanks);
+  printf("ram-banks: %d\n", globalMemState.nRamBanks);
+
+  // allocate cartridge RAM (8 KB * # of banks)
+  if(globalMemState.nRamBanks) {
+    globalMemState.mappedRamAllocation = (u8*)malloc(0x2000 * globalMemState.nRamBanks);
+    memset((void*)globalMemState.mappedRamAllocation, 0, 0x2000 * globalMemState.nRamBanks);
+    globalMemState.disabledMappedRam = globalMemState.mappedRamAllocation;
+  } else {
+
+  }
+
+  // allocate memories:
+  // internal RAM
+  globalMemState.internalRam = (u8*)badalloc_check(0x2000, "internal_ram");
+  globalMemState.vram =        (u8*)badalloc_check(0x2000, "vram");
+  globalMemState.upperRam    = (u8*)badalloc_check(0x80,   "upper-regs");
+  globalMemState.ioRegs      = (u8*)badalloc_check(0x80,   "io-regs");
+  
+  printf("[initRam] ioRegs: 0x%x\n", globalMemState.ioRegs);
+
+  // clear RAMs
+  memset(globalMemState.internalRam, 0, 0x2000);
+  memset(globalMemState.vram, 0, 0x2000);
+  memset(globalMemState.upperRam, 0, 0x80);
+  memset(globalMemState.ioRegs, 0, 0x80);
+
+  // setup i/o regs and friends
+  u8* io = globalMemState.ioRegs;
+  io[IO_TIMA] = 0; // reset TIMER COUNT to 0
+  io[IO_TMA] = 0;  // TIMER RELOAD
+  io[IO_TAC] = 0;  // TIMER STOP
+  io[IO_NR10] = 0x80;
+  io[IO_NR11] = 0xbf;
+  io[IO_NR12] = 0xf3;
+  io[IO_NR14] = 0xbf;
+  io[IO_NR21] = 0x3f;
+  io[IO_NR22] = 0x00;
+  io[IO_NR24] = 0xbf;
+  io[IO_NR30] = 0x7f;
+  io[IO_NR31] = 0xff;
+  io[IO_NR32] = 0x9f;
+  io[IO_NR34] = 0xbf;
+  io[IO_NR41] = 0xff;
+  io[IO_NR42] = 0x00;
+  io[IO_NR43] = 0x00;
+  io[IO_NR44] = 0xbf;
+  io[IO_NR50] = 0x77;
+  io[IO_NR51] = 0xf3;
+  io[IO_NR52] = 0xf1;
+  io[IO_LCDC] = 0x91;
+  io[IO_SCROLLY] = 0x00;
+  io[IO_SCROLLX] = 0x00;
+  io[IO_LYC] = 0x00;
+  io[IO_BGP] = 0xfc;
+  io[IO_OBP0] = 0xff;
+  io[IO_OBP1] = 0xff;
+  io[IO_WINY] = 0x00;
+  io[IO_WINX] = 0x00;
+
+  // turn off interrupts
+  globalMemState.upperRam[0x7f] = 0;
+}
+
+
+// boot ROM
+static const u8 bios[256] = {0x31, 0xFE, 0xFF, // LD, SP, $fffe   0
+                       0xAF,             // XOR A           3
+                       0x21, 0xFF, 0x9F, // LD HL, $9fff    4
+                       0x32,             // LD (HL--), A    7
+                       0xCB, 0x7C,       // BIT 7, H        8
+                       0x20, 0xFB,       // JR NZ 7         a
+                       0x21, 0x26, 0xFF, // LD HL, $ff26    c
+                       0x0E, 0x11,       // LD c,$11        f
+                       0x3E, 0x80,       // LD a,$80        11
+                       0x32,             // LD (HL--), A    13
+                       0xE2,             // LD($FF00+C),A   14
+                       0x0C,             // INC C           15
+                       0x3E, 0xF3,       // LD A, $f3       16
+                       0xE2,             // LD (HL--), A    18
+                       0x32,             // LD($FF00+C),A   19
+                       0x3E, 0x77,       // LD A,$77        1a
+                       0x77, 0x3E, 0xFC, 0xE0,
+                       0x47, 0x11, 0x04, 0x01, 0x21, 0x10, 0x80, 0x1A, 0xCD, 0x95, 0x00, 0xCD, 0x96, 0x00, 0x13, 0x7B,
+                       0xFE, 0x34, 0x20, 0xF3, 0x11, 0xD8, 0x00, 0x06, 0x08, 0x1A, 0x13, 0x22, 0x23, 0x05, 0x20, 0xF9,
+                       0x3E, 0x19, 0xEA, 0x10, 0x99, 0x21, 0x2F, 0x99, 0x0E, 0x0C, 0x3D, 0x28, 0x08, 0x32, 0x0D, 0x20,
+                       0xF9, 0x2E, 0x0F, 0x18, 0xF3, 0x67, 0x3E, 0x64, 0x57, 0xE0, 0x42, 0x3E, 0x91, 0xE0, 0x40, 0x04,
+                       0x1E, 0x02, 0x0E, 0x0C, 0xF0, 0x44, 0xFE, 0x90, 0x20, 0xFA, 0x0D, 0x20, 0xF7, 0x1D, 0x20, 0xF2,
+                       0x0E, 0x13, 0x24, 0x7C, 0x1E, 0x83, 0xFE, 0x62, 0x28, 0x06, 0x1E, 0xC1, 0xFE, 0x64, 0x20, 0x06,
+                       0x7B, 0xE2, 0x0C, 0x3E, 0x87, 0xF2, 0xF0, 0x42, 0x90, 0xE0, 0x42, 0x15, 0x20, 0xD2, 0x05, 0x20,
+                       0x4F, 0x16, 0x20, 0x18, 0xCB, 0x4F, 0x06, 0x04, 0xC5, 0xCB, 0x11, 0x17, 0xC1, 0xCB, 0x11, 0x17,
+                       0x05, 0x20, 0xF5, 0x22, 0x23, 0x22, 0x23, 0xC9, 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B,
+                       0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E,
+                       0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99, 0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC,
+                       0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E, 0x3c, 0x42, 0xB9, 0xA5, 0xB9, 0xA5, 0x42, 0x4C,
+                       0x21, 0x04, 0x01, 0x11, 0xA8, 0x00, 0x1A, 0x13, 0xBE, 0x20, 0xFE, 0x23, 0x7D, 0xFE, 0x34, 0x20,
+                       0xF5, 0x06, 0x19, 0x78, 0x86, 0x23, 0x05, 0x20, 0xFB, 0x86, 0x20, 0xFE, 0x3E, 0x01, 0xE0, 0x50};
+
+// handler for MBC0 switch
+void mbc0Handler(u16 addr, u8 value) {
+
+  // it looks like tetris tries to select ROM 1 for banked ROM, so we need to allow this:
+  if(addr >= 0x2000 && addr < 0x3fff) {
+    if(value == 0 || value == 1) {
+      // nothing to do!
+    } else {
+      assert(false);
+    }
+  } else {
+    assert(false);
+  }
+
+}
+
+// handler for MBC1 switch (doesn't handle everything yet...)
+void mbc1Handler(u16 addr, u8 value) {
+  if(addr >= 0x2000 && addr < 0x3fff) {
+    // ROM bank switch
+    if(value >= globalMemState.nRomBanks) {
+      printf("\trequested rom bank %d when there are only %d banks!\n", value, globalMemState.nRomBanks);
+      assert(false);
+    }
+
+    if(value == 0) value = 1;
+    if(value == 0x21) value = 0x20;
+    if(value == 0x41) value = 0x40;
+    globalMemState.mappedRom = fileData + 0x4000 * value;
+  } else if(addr >= 0 && addr < 0x1fff) {
+    // enable RAM
+    if(value == 0) {
+      globalMemState.disabledMappedRam = globalMemState.mappedRam;
+      globalMemState.mappedRam = nullptr;
+    } else if(value == 0xa) {
+      globalMemState.mappedRam = globalMemState.disabledMappedRam;
+    } else {
+      assert(false);
+    }
+  } else {
+    assert(false);
+  }
+
+}
+
+// handler for MBC2 switch (doesn't handle anything yet...)
+void mbc2Handler(u16 addr, u8 value) {
+  assert(false);
+}
+
+// handler for MBC3 switch (doesn't handle anything yet...)
+void mbc3Handler(u16 addr, u8 value) {
+
+  if(addr >= 0x2000 && addr < 0x3fff) {
+    // ROM bank switch
+    if(value >= globalMemState.nRomBanks) {
+      printf("\trequested rom bank %d when there are only %d banks!\n", value, globalMemState.nRomBanks);
+      assert(false);
+    }
+
+    if(value == 0) value = 1;
+    globalMemState.mappedRom = fileData + 0x4000 * value;
+  } else if(addr >= 0 && addr < 0x1fff) {
+    // RAM enable/disable
+    if(value == 0) {
+      globalMemState.disabledMappedRam = globalMemState.mappedRam;
+      globalMemState.mappedRam = nullptr;
+    } else if(value == 0xa) {
+      globalMemState.mappedRam = globalMemState.disabledMappedRam;
+    } else {
+      //assert(false);
+    }
+  } else if(addr >= 0x4000 && addr < 0x5fff) {
+    // RAM bank switch
+    if(value < globalMemState.nRamBanks) {
+      globalMemState.mappedRam = globalMemState.mappedRamAllocation + 0x2000 * value;
+    } else {
+      //assert(false);
+    }
+  } else if(addr == 0x6000) {
+    // ?? RTC latch nonsense
+  } else {
+    assert(false);
+  }
+}
+
+// handler for all MBC switches
+void mbcHandler(u16 addr, u8 value) {
+  switch(globalMemState.mbcType) {
+    case 0:
+      mbc0Handler(addr, value);
+      break;
+    case 1:
+      mbc1Handler(addr, value);
+      break;
+    case 2:
+      mbc2Handler(addr, value);
+      break;
+    case 3:
+      mbc3Handler(addr, value);
+      break;
+    default:
+      assert(false);
+      break;
+  }
+}
+
+// read a u16 from game memory
+u16 readU16(u16 addr) {
+  return (u16)readByte(addr) + ((u16)(readByte(addr+(u16)1)) << 8);
+}
+
+// write a u16 to game memory
+void writeU16(u16 mem, u16 addr) {
+  writeByte((u8)(mem & 0xff), addr);
+  writeByte((u8)(mem >> 8),  addr + (u16)1);
+}
+
+// read byte from memory
+u8 readByte(u16 addr) {
+  switch(addr & 0xf000) {
+    case 0x0000: // either BIOS or ROM 0:
+      if(globalMemState.inBios) {
+        if(addr < 0x100) {
+          return bios[addr];
+        } else if(addr == 0x100) {
+          printf("EXIT BIOS ERROR\n");
+          assert(false);
+        } else {
+          return globalMemState.rom0[addr]; // todo <- change this for stm32
+        }
+      } else {
+        return globalMemState.rom0[addr];   // todo <- change this for stm32
+      }
+
+    case 0x1000: // ROM 0
+    case 0x2000: // ROM 0
+    case 0x3000: // ROM 0
+      return globalMemState.rom0[addr];    // todo <- change this for stm32
+
+    case 0x4000: // banked ROM
+    case 0x5000:
+    case 0x6000:
+    case 0x7000:
+      return globalMemState.mappedRom[addr & 0x3fff]; // todo <- change this for stm32
+
+    case 0x8000: // VRAM
+    case 0x9000:
+      return globalMemState.vram[addr & 0x1fff];
+
+    case 0xa000: // mapped RAM
+    case 0xb000:
+      if(!globalMemState.mappedRam) {
+#ifndef DANGER_MODE
+        assert(false);
+#endif
+        return 0xff;
+      }
+      return globalMemState.mappedRam[addr & 0x1fff];
+
+    case 0xc000: // internal RAM
+    case 0xd000:
+      return globalMemState.internalRam[addr & 0x1fff];
+
+    case 0xe000: // interal RAM copy
+      return globalMemState.internalRam[addr & 0x1fff];
+
+    case 0xf000: // either internal RAM copy or I/O or top-ram
+      switch(addr & 0x0f00) {
+        case 0x000:
+        case 0x100:
+        case 0x200:
+        case 0x300:
+        case 0x400:
+        case 0x500:
+        case 0x600:
+        case 0x700:
+        case 0x800:
+        case 0x900:
+        case 0xa00:
+        case 0xb00:
+        case 0xc00:
+        case 0xd00:
+        case 0xe00:
+          return globalMemState.internalRam[addr & 0x1fff];
+
+
+        case 0xf00:
+          if(addr >= 0xff80) {
+            return globalMemState.upperRam[addr & 0x7f];
+          } else {
+            u8 lowAddr = (u8)(addr & 0xff);
+            switch(lowAddr) {
+              case IO_LY:
+              case IO_SCROLLX:
+              case IO_SCROLLY:
+              case IO_NR10: // nyi
+              case IO_NR11: // nyi
+              case IO_NR12: // nyi
+              case IO_NR13: // nyi
+              case IO_NR14: // nyi
+              case IO_NR21: // nyi
+              case IO_NR22: // nyi
+              case IO_NR23: // nyi
+              case IO_NR24: // nyi
+              case IO_NR30: // nyi
+              case IO_NR31: // nyi
+              case IO_NR32: // nyi
+              case IO_NR33: // nyi
+              case IO_NR34: // nyi
+              case IO_NR41: // nyi
+              case IO_NR42: // nyi
+              case IO_NR43: // nyi
+              case IO_NR44: // nyi
+              case IO_NR50: // nyi
+              case IO_NR51: // nyi
+              case IO_NR52: // nyi
+              case IO_STAT: // nyi
+              case IO_WAVE_PATTERN: // nyi
+              case IO_LCDC: // nyi
+              case IO_BGP:
+              case IO_OBP0:
+              case IO_OBP1:
+              case IO_SERIAL_SB:
+              case IO_SERIAL_SC:
+              case IO_DIV:
+              case IO_TIMA:
+              case IO_TMA:
+              case IO_TAC:
+              case IO_WINY:
+              case IO_WINX:
+                return globalMemState.ioRegs[lowAddr];
+                break;
+
+              case IO_IF:
+                printf("read if: 0x%x\n", globalMemState.ioRegs[lowAddr]);
+                printf("timer value: 0x%x\n", globalMemState.ioRegs[IO_TIMA]);
+                return globalMemState.ioRegs[lowAddr];
+                break;
+
+
+              case IO_P1:
+              {
+                u8 regP1 = globalMemState.ioRegs[IO_P1];
+                //printf("ireg: 0x%x\n", regP1);
+                u8 joypad_data = 0;
+                if(regP1 & 0x10) {
+                  if(keyboard.a) joypad_data += 0x1;
+                  if(keyboard.b) joypad_data += 0x2;
+                  if(keyboard.select) joypad_data += 0x4;
+                  if(keyboard.start) joypad_data += 0x8;
+                }
+                if(regP1 & 0x20) {
+                  if(keyboard.r) joypad_data += 0x1;
+                  if(keyboard.l) joypad_data += 0x2;
+                  if(keyboard.u) joypad_data += 0x4;
+                  if(keyboard.d) joypad_data += 0x8;
+                }
+
+                regP1 = (regP1 & 0xf0);
+                joypad_data = ~joypad_data;
+                joypad_data = regP1 + (joypad_data & 0xf);
+                //globalMemState.ioRegs[IO_P1] = joypad_data;
+                //printf("jpd: 0x%x\n", joypad_data);
+                return joypad_data;
+              }
+
+                break;
+
+              case IO_GBCSPEED:
+                return 0xff;
+
+              case IO_LYC:
+              case IO_DMA:
+
+                printf("unhandled I/O read @ 0x%x\n", addr);
+#ifndef DANGER_MODE
+                assert(false);
+#endif
+                break;
+              default:
+                printf("unknown I/O read @ 0x%x\n", addr);
+#ifndef DANGER_MODE
+                assert(false);
+#endif
+                break;
+            }
+
+          }
+        default:
+#ifndef DANGER_MODE
+          assert(false);
+#endif
+          break;
+
+      }
+      break;
+
+    default:
+#ifndef DANGER_MODE
+      assert(false);
+#endif
+      break;
+  }
+  assert(false);
+  return 0xff;
+}
+
+
+void writeByte(u8 byte, u16 addr) {
+  switch(addr & 0xf000) {
+    case 0x0000: // ROM 0, but possibly the BIOS area
+      if(globalMemState.inBios) {
+        printf("ERROR: tried to write into ROM0 or BIOS (@ 0x%04x) during BIOS!\n", addr);
+#ifndef DANGER_MODE
+        assert(false);
+#endif
+      } else {
+        mbcHandler(addr, byte);
+      }
+      break;
+
+
+    case 0x1000: // ROM 0
+    case 0x2000: // ROM 0
+    case 0x3000: // ROM 0
+    case 0x4000: // ROM 1
+    case 0x5000: // ROM 1
+    case 0x6000: // ROM 1
+    case 0x7000: // ROM 1
+      mbcHandler(addr, byte);
+      break;
+
+    case 0x8000: // VRAM
+    case 0x9000:
+      globalMemState.vram[addr & 0x1fff] = byte;
+      break;
+
+    case 0xa000: // mapped RAM
+    case 0xb000:
+      if(!globalMemState.mappedRam) {
+        //printf("write to unmapped ram @ 0x%x value 0x%x\n", addr, byte);
+//#ifndef DANGER_MODE
+//        assert(false);
+//#endif
+        break;
+      }
+      globalMemState.mappedRam[addr & 0x1fff] = byte;
+      break;
+
+    case 0xc000: // internal RAM
+    case 0xd000:
+      globalMemState.internalRam[addr & 0x1fff] = byte;
+      break;
+
+    case 0xe000: // interal RAM copy
+      globalMemState.internalRam[addr & 0x1fff] = byte;
+      break;
+
+    case 0xf000: // either internal RAM copy or I/O or top-ram
+      switch(addr & 0x0f00) {
+        case 0x000:
+        case 0x100:
+        case 0x200:
+        case 0x300:
+        case 0x400:
+        case 0x500:
+        case 0x600:
+        case 0x700:
+        case 0x800:
+        case 0x900:
+        case 0xa00:
+        case 0xb00:
+        case 0xc00:
+        case 0xd00:
+        case 0xe00:
+          globalMemState.internalRam[addr & 0x1fff] = byte;
+          break;
+
+
+        case 0xf00:
+          if(addr >= 0xff80) {
+            globalMemState.upperRam[addr & 0x7f] = byte;
+            break;
+          } else {
+            u16 maskedAddress = addr & 0x7f;
+            globalMemState.ioRegs[maskedAddress] = byte;
+            u8 lowAddr = (u8)(addr & 0xff);
+            switch(lowAddr) {
+
+              case IO_NR10:
+              case IO_NR11:
+              case IO_NR12:
+              case IO_NR13:
+              case IO_NR14:
+              case IO_NR21:
+              case IO_NR22:
+              case IO_NR23:
+              case IO_NR24:
+              case IO_NR30:
+              case IO_NR31:
+              case IO_NR32:
+              case IO_NR33:
+              case IO_NR34:
+              case IO_NR41:
+              case IO_NR42:
+              case IO_NR43:
+              case IO_NR44:
+              case IO_NR50:
+              case IO_NR51:
+              case IO_NR52:
+              case IO_WAVE_PATTERN:
+              case 0x31:
+              case 0x32:
+              case 0x33:
+              case 0x34:
+              case 0x35:
+              case 0x36:
+              case 0x37:
+              case 0x38:
+              case 0x39:
+              case 0x3a:
+              case 0x3b:
+              case 0x3c:
+              case 0x3d:
+              case 0x3e:
+              case 0x3f:
+              case IO_BGP:
+              case IO_SCROLLX:
+              case IO_SCROLLY:
+              case IO_LCDC:
+              case IO_STAT:
+              case IO_OBP0:
+              case IO_OBP1:
+              case IO_P1:
+              case IO_IF:
+              case IO_TAC:
+              case IO_TIMA:
+              case IO_TMA:
+              case IO_SERIAL_SB:
+              case IO_SERIAL_SC:
+              case IO_WINY:
+              case IO_WINX:
+              case IO_LYC:
+                break;
+
+              case IO_EXIT_BIOS:
+                if(globalMemState.inBios) {
+                  printf("EXIT BIOS by write 0x%x to 0x%x", byte, addr);
+                  globalMemState.inBios = false;
+                  break;
+                } else {
+                  printf("tried to write to 0xff50 when not in bios?\n");
+                  break;
+                }
+                break;
+
+              case IO_DMA:
+              {
+                u16 dmaAddr = ((u16)byte) << 8;
+                for(u16 i = 0; i < 160; i++) {
+                  writeByte(readByte(dmaAddr + i), (u16)0xfe00 + i);
+                }
+                break;
+              }
+
+              case 0x7f:
+                printf("OOPS\n");
+                break;
+
+
+              case IO_LY:
+                printf("unhandled I/O write @ 0x%x\n", addr);
+#ifndef DANGER_MODE
+                assert(false);
+#endif
+                break;
+              default:
+                printf("unknown I/O write @ 0x%x\n", addr);
+#ifndef DANGER_MODE
+                assert(false);
+#endif
+                break;
+            }
+            break;
+          }
+        default:
+#ifndef DANGER_MODE
+          assert(false);
+#endif
+          break;
+      }
+      break;
+    default:
+#ifndef DANGER_MODE
+      assert(false);
+#endif
+      break;
+  }
+}
\ No newline at end of file
diff -r 1f728d08b3a7 -r c9afe1a7b423 mem.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mem.h	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,176 @@
+#ifndef GBC_MEM_H
+#define GBC_MEM_H
+
+#include "types.h"
+#include "stdio.h"
+#include "platform.h"
+
+// interrupt flags
+#define INTERRUPT_KEY 0x10;
+
+// interrupt vector locations
+#define VBLANK_INTERRUPT    0x0040
+#define LCDC_INTERRUPT      0x0048
+#define TIMER_INTERRUPT     0x0050
+#define SERIAL_INTERRUPT    0x0058
+#define HIGH_TO_LOW_P10_P13 0x0060
+
+// IO Regs (offset from 0xff00)
+#define IO_P1        0x0  // joystick               (needs keyboard handler)
+#define IO_SERIAL_SB 0x01 // serial transfer data   (don't support)
+#define IO_SERIAL_SC 0x02 // serial control         (don't support)
+#define IO_DIV       0x04 // div
+#define IO_TIMA      0x05 // timer value            (nyi)
+#define IO_TMA       0x06 // timer reload           (nyi)
+#define IO_TAC       0x07 // TIMER CONTROL (enable, speed) (nyi)
+#define IO_IF        0x0f
+
+#define IO_NR10      0x10
+#define IO_NR11      0x11
+#define IO_NR12      0x12
+#define IO_NR13      0x13
+#define IO_NR14      0x14
+
+#define IO_NR21      0x16
+#define IO_NR22      0x17
+#define IO_NR23      0x18
+#define IO_NR24      0x19
+
+#define IO_NR30      0x1a
+#define IO_NR31      0x1b
+#define IO_NR32      0x1c
+#define IO_NR33      0x1d
+#define IO_NR34      0x1e
+
+#define IO_NR41      0x20
+#define IO_NR42      0x21
+#define IO_NR43      0x22
+#define IO_NR44      0x23
+
+#define IO_NR50      0x24
+#define IO_NR51      0x25
+#define IO_NR52      0x26
+
+#define IO_WAVE_PATTERN 0x30 // this is 16 bytes
+
+#define IO_LCDC      0x40 // yes, revisit
+#define IO_STAT      0x41 // no
+#define IO_SCROLLY   0x42
+#define IO_SCROLLX   0x43
+#define IO_LY        0x44
+#define IO_LYC       0x45 // no
+
+#define IO_DMA       0x46 // yes
+#define IO_BGP       0x47 // ??
+#define IO_OBP0      0x48 // ??
+#define IO_OBP1      0x49 // ??
+#define IO_WINY      0x4a // no
+#define IO_WINX      0x4b // no
+
+#define IO_GBCSPEED  0x4d
+
+#define IO_EXIT_BIOS 0x50
+
+
+#define CART_INFO_ADDR      0x0100
+
+
+// cartridge types
+enum CartType {
+  ROM_ONLY = 0,
+  ROM_MBC1 = 1,
+  ROM_MBC1_RAM = 2,
+  ROM_MBC1_RAM_BATT = 3,
+  ROM_MBC2 = 5,
+  ROM_MBC2_BATT = 6,
+  ROM_RAM = 8,
+  ROM_RAM_BATT = 9,
+  ROM_MM01 = 0xb,
+  ROM_MM01_SRAM = 0xc,
+  ROM_MMM01_SRAM_BATT = 0xd,
+  ROM_MBC3_TIMER_BATT = 0xf,
+  ROM_MBC3_TIMER_RAM_BATT = 0x10, // pkmn crystal
+  ROM_MBC3 = 0x11,
+  ROM_MBC3_RAM = 0x12,
+  ROM_MBC3_RAM_BATT = 0x13, // pkmn red
+  ROM_MBC5 = 0x19,
+  ROM_MBC_RAM = 0x1a,
+  ROM_MBC5_RAM_BATT = 0x1b,
+  ROM_MBC5_RUMBLE = 0x1c,
+  ROM_MBC5_RUMBLE_SRAM = 0x1d,
+  ROM_MBC5_RUMBLE_SRAM_BATT = 0x1e,
+  POCKET_CAMERA = 0x1f,
+  BANDAI_TAMA5 = 0xfd,
+  HUDSON_HUC3 = 0xfe,
+  HUDSON_HUC1 = 0xff
+};
+
+// in banks, of 16 KByte each
+enum RomSize {
+  BANK_2 = 0,
+  BANK_4 = 1,
+  BANK_8 = 2,
+  BANK_16 = 3,
+  BANK_32 = 4,
+  BANK_64 = 5, // pkmn red: 1 MB
+  BANK_128 = 6, // pkmn crystal
+  BANK_72 = 0x52,
+  BANK_80 = 0x53,
+  BANK_96 = 0x54
+};
+
+enum RamSize {
+  NONE = 0,
+  SIZE_2KB = 1, // 1 bank
+  SIZE_8KB = 2, // 1 bank
+  SIZE_32KB = 3, // 4 banks, pkmn red, crystal
+  SIZE_128KB = 4, // 16 banks
+};
+
+struct CartInfo {
+  u8 beginExec[4];        // 0x0 -> 0x3
+  u8 nintendoGraphic[48]; // 0x3 ->
+  char title[15];
+  u8 isColor; // compare against CARTRIDGE_IS_COLOR
+  u8 license1;
+  u8 license2;
+  u8 SGB;
+  CartType cartType;
+  RomSize romSize;
+  RamSize ramSize;
+  u8 notJapan;
+  u8 license3;
+  u8 maskRomVersion;
+
+  void print();
+};
+
+struct MemState {
+  bool inBios;
+  u8 mbcType;
+  u8 nRomBanks;
+  u8 nRamBanks;
+  u8* rom0;
+  u8* mappedRom;
+  u8* vram;
+  u8* mappedRam;
+  u8* disabledMappedRam;
+  u8* internalRam;
+  u8* mappedRamAllocation;
+  u8* ioRegs;
+  u8* upperRam;
+  u8* spriteAttribute;
+};
+
+// external interface
+extern MemState globalMemState;
+void initMem(FileLoadData file); // intialize memory for a cartridge
+u8 readByte(u16 addr);           // read a byte from memory
+u16 readU16(u16 addr);           // read 16-bits from memory
+void writeByte(u8 byte, u16 addr); // write a byte to memory
+void writeU16(u16 mem, u16 addr); // read 16-bits from memory
+void saveGame();
+void loadGame();
+
+
+#endif //GBC_MEM_H
diff -r 1f728d08b3a7 -r c9afe1a7b423 platform.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/platform.cpp	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,78 @@
+// Linux specific code (excluding graphics, those are in graphics_display.cpp)
+#include "platform.h"
+
+#include <mbed.h>
+
+// global keyboard
+KeyState keyboard;
+
+void* badalloc_check(u32 size, const char* alloc_name) {
+    void* ptr = malloc(size);
+    if(ptr) {
+        printf("[badalloc] success %s\n", alloc_name);
+    } else {
+        printf("~!~!~!1!!!!!-----> [badalloc] fail %s\n", alloc_name);
+    }
+    return ptr;
+}
+
+// open a file
+FileLoadData loadFile(const char* name) {
+  FILE* fp = fopen(name, "rb");
+  FileLoadData loadData;
+
+  if(!fp) {
+    printf("loadFile(%s) failed!\r\n", name);
+    loadData.data = nullptr;
+    loadData.size = 0;
+    return loadData;
+  }
+  printf("loadfile fp is good\r\n");
+  
+  fseek(fp, 0, SEEK_END);
+  u32 fileSize = (u32)ftell(fp);
+  printf("loadFile 0x%x bytes\r\n", fileSize);
+  fseek(fp, 0, SEEK_SET);
+  u8* fileData = (u8*)badalloc_check(fileSize, name);
+  if(fileData) {
+    printf("allocation for loadFile succes\r\n");
+  } else {
+    printf("allocation for loadfile fail\r\n");
+  }
+  fread(fileData, 1, fileSize, fp);
+  fclose(fp);
+
+  printf("loadfile(%s) has loaded %d bytes (%.3f MB)\r\n", name, fileSize, (float)fileSize / (1 << 20));
+
+  loadData.size = fileSize;
+  loadData.data = fileData;
+  return loadData;
+}
+
+void saveFile(const char* name, FileLoadData info) {
+//  FILE* fp = fopen(name, "wb");
+//  printf("save 0x%x bytes 0x%llx\n", info.size, info.data);
+//  fwrite(info.data, info.size, 1, fp);
+//  fclose(fp);
+}
+
+// update keyboard.  Also checks to see if it's time to quit.
+void updateKeyboard(KeyState* keys) {
+//  SDL_Event e;
+//  SDL_PollEvent(&e);
+//  if(e.type == SDL_QUIT) {
+//    exit(0);
+//  }
+//  const u8* keyStats = SDL_GetKeyboardState(nullptr);
+//  keys->a = keyStats[SDL_SCANCODE_A];
+//  keys->b = keyStats[SDL_SCANCODE_B];
+//  keys->u = keyStats[SDL_SCANCODE_UP];
+//  keys->d = keyStats[SDL_SCANCODE_DOWN];
+//  keys->l = keyStats[SDL_SCANCODE_LEFT];
+//  keys->r = keyStats[SDL_SCANCODE_RIGHT];
+//  keys->save = keyStats[SDL_SCANCODE_S];
+//  keys->load = keyStats[SDL_SCANCODE_D];
+//  keys->start = keyStats[SDL_SCANCODE_P];
+//  keys->select = keyStats[SDL_SCANCODE_L];
+//  keys->turbo = keyStats[SDL_SCANCODE_SPACE];
+}
\ No newline at end of file
diff -r 1f728d08b3a7 -r c9afe1a7b423 platform.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/platform.h	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,16 @@
+#ifndef GBC_PLATFORM_H
+#define GBC_PLATFORM_H
+
+#include "types.h"
+
+struct FileLoadData {
+  u8* data;
+  u32 size;
+};
+
+void* badalloc_check(u32 size, const char* alloc_name);
+FileLoadData loadFile(const char* name);
+void saveFile(const char* name, FileLoadData info);
+void updateKeyboard(KeyState* keys);
+
+#endif //GBC_PLATFORM_H
\ No newline at end of file
diff -r 1f728d08b3a7 -r c9afe1a7b423 types.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/types.h	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,28 @@
+#ifndef GBC_TYPES_H
+#define GBC_TYPES_H
+
+#include <stdint.h>
+
+#define u8 uint8_t
+#define u16 uint16_t
+#define u32 uint32_t
+#define s8 int8_t
+#define s16 int16_t
+#define s32 int32_t
+#define nullptr NULL
+
+//using u8 = uint8_t;
+//using u16 = uint16_t;
+//using u32 = uint32_t;
+//
+//using s8 = int8_t;
+//using s16 = int16_t;
+//using s32 = int32_t;
+
+struct KeyState {
+  bool a,b,u,d,l,r,start,select,turbo, save, load;
+};
+
+extern KeyState keyboard;
+
+#endif //GBC_TYPES_H
diff -r 1f728d08b3a7 -r c9afe1a7b423 video.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/video.cpp	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,390 @@
+#include <stdlib.h>
+#include <mbed.h>
+#define NDEBUG
+#include <assert.h>
+#include "video.h"
+#include "mem.h"
+#include "graphics_display.h"
+#include "main.h"
+
+u32 frameCount = 0;
+
+// the implementation of sprites is poor.
+// pick a value for this that is between 0 and 256 and not equal to one of the colors
+#define TRANSPARENT_SPRITE 37
+
+#define BRIGHTEST_COLOR 6
+
+VideoState globalVideoState;
+
+// colors on screen (white, light gray, dark gray, black)
+// the first one must be "BRIGHTEST_COLOR" - other code depends on this!
+static const u8 colors[4] = {BRIGHTEST_COLOR, 4, 2, 0};
+
+
+
+
+void initVideo(u8* frameBuffer) {
+  globalVideoState.mode = 0;
+  globalVideoState.modeClock = 0;
+  globalVideoState.line = 0;
+  globalVideoState.frameBuffer = frameBuffer; // <- todo change for STM32
+
+//  for(u32 i = 0; i < 160*144; i++) {
+//    globalVideoState.frameBuffer[i] = 255;
+//  }
+
+}
+
+inline u32 xy2px(u8 x, u8 y) {
+    return H_RES*(y+Y0) + x + X0;
+}
+
+void dumpVideo() {
+  for(u16 i = 0x8000; i < 0x87ff; i++) {
+    if((i%8) == 1) {
+      printf("@ 0x%04x: ", i);
+    }
+    printf("0x%02x ", readByte(i));
+    if(!(i%8)) printf("\n");
+  }
+  printf("\n");
+  assert(false);
+}
+
+// read a pixel out of a tile and apply the given palette
+u8 readTile(u16 tileAddr, u8 x, u8 y, u8 palette) {
+  assert(x <= 8);
+  assert(y <= 8);
+  x = (7 - x);
+  u16 loAddr = tileAddr + (y*(u16)2);
+  u16 hiAddr = loAddr + (u16)1;
+  u8 lo = readByte(loAddr);
+  u8 hi = readByte(hiAddr);
+  u8 loV = (lo >> x) & (u8)1;
+  u8 hiV = (hi >> x) & (u8)1;
+  //u8 result = loV * 120 + hiV * 60;
+  u8 colorIdx = loV + 2 * hiV;
+  u8 colorID = (palette >> (2 * colorIdx)) & 3;
+  return colors[colorID];
+}
+
+u8 readTilePtr(u8* tileAddr, u8 x, u8 y, u8 palette) {
+  assert(x <= 8);
+  assert(y <= 8);
+  x = (7 - x);
+  u8* loAddr = tileAddr + (y*(u16)2);
+  u8* hiAddr = loAddr + (u16)1;
+  u8 lo = *(loAddr);
+  u8 hi = *(hiAddr);
+  u8 loV = (lo >> x) & (u8)1;
+  u8 hiV = (hi >> x) & (u8)1;
+  //u8 result = loV * 120 + hiV * 60;
+  u8 colorIdx = loV + 2 * hiV;
+  u8 colorID = (palette >> (2 * colorIdx)) & 3;
+  return colors[colorID];
+}
+
+// read a pixel out of a tile and apply the given palette
+// returns TRANSPARENT_SPRITE if the sprite should be transparent
+u8 readSpriteTile(u16 tileAddr, u8 x, u8 y, u8 palette) {
+  //tileAddr = 0x8180;
+  assert(x <= 8);
+  assert(y <= 8);
+  x = (7 - x);
+  u16 loAddr = tileAddr + (y*(u16)2);
+  u16 hiAddr = loAddr + (u16)1;
+  u8 lo = readByte(loAddr);
+  u8 hi = readByte(hiAddr);
+  u8 loV = (lo >> x) & (u8)1;
+  u8 hiV = (hi >> x) & (u8)1;
+  u8 colorIdx = loV + 2 * hiV;
+  if(colorIdx == 0) {
+    return TRANSPARENT_SPRITE;
+  }
+  u8 colorID = (palette >> (2 * colorIdx)) & 3;
+  return colors[colorID];
+}
+
+u8 readSpriteTileAddr(u8* tileAddr, u8 x, u8 y, u8 palette) {
+  //tileAddr = 0x8180;
+  assert(x <= 8);
+  assert(y <= 8);
+  x = (7 - x);
+  u8* loAddr = tileAddr + (y*(u16)2);
+  u8* hiAddr = loAddr + (u16)1;
+  u8 lo = *(loAddr);
+  u8 hi = *(hiAddr);
+  u8 loV = (lo >> x) & (u8)1;
+  u8 hiV = (hi >> x) & (u8)1;
+  u8 colorIdx = loV + 2 * hiV;
+  if(colorIdx == 0) {
+    return TRANSPARENT_SPRITE;
+  }
+  u8 colorID = (palette >> (2 * colorIdx)) & 3;
+  return colors[colorID];
+}
+
+// compute the address of the tile from the tile's index
+// this is confusing because depending on the tileData selected,
+// the tileIdx is either signed or unsigned
+u16 computeTileAddr(u8 tileIdx, bool tileData) {
+  if(tileData) {
+    return 0x8000 + 16 * tileIdx;
+  } else {
+    if(tileIdx <= 127) {
+      return 0x9000 + 16 * tileIdx;
+    } else {
+      return 0x8000 + 16 * (tileIdx);
+    }
+  }
+}
+
+u8* computeTileAddrPtr(u8 tileIdx, bool tileData) {
+  if(tileData) {
+    return globalMemState.vram + 16 * tileIdx;
+  } else {
+    if(tileIdx <= 127) {
+      return globalMemState.vram + 0x1000 + 16 * tileIdx;
+    } else {
+      return globalMemState.vram + 16 * (tileIdx);
+    }
+  }
+}
+
+// main function to render a line of the display.
+// this implementation is missing a number of things, including (but not limited to)
+//  -- proper position of the WINDOW
+//  -- 16x8 sprites
+//  -- sprite sorting
+//  -- 10 sprite limit
+void renderLine() {
+  if(frameCount % 3) return;
+  //return;
+  //if(drawing) return;
+  //printf("%x %x\n", im_line_va, im_back);
+  globalVideoState.frameBuffer = im_line_vas[bufferSelect];
+  u8 lcdc = globalMemState.ioRegs[IO_LCDC];    // lcd control register
+  bool lcdOn            = (lcdc >> 7) & (u8)1; // lcd display on?
+  bool windowTileMap    = (lcdc >> 6) & (u8)1; // select tilemap source for window
+  bool windowEnable     = (lcdc >> 5) & (u8)1; // draw window?
+  bool tileData         = (lcdc >> 4) & (u8)1; // select tile data source
+  bool bgTileMap        = (lcdc >> 3) & (u8)1; // select tilemap source for background
+  bool objSize          = (lcdc >> 2) & (u8)1; // pick sprite size (nyi)
+  bool objEnable        = (lcdc >> 1) & (u8)1; // enable sprite renderer
+  bool bgWinEnable      = (lcdc >> 0) & (u8)1; // enable background and window renderer?
+
+  u16 windowMapAddr = (u16)(windowTileMap ? 0x9c00 : 0x9800);
+  u16 bgTileMapAddr = (u16)(bgTileMap     ? 0x9c00 : 0x9800);
+
+  // background renderer
+  if(lcdOn && bgWinEnable) {
+    // render background onto framebuffer
+    u8 pal = globalMemState.ioRegs[IO_BGP]; // color palette
+    u16 tileMapRowAddr = (u16)(bgTileMapAddr + 32*((((u16)globalVideoState.line +
+            globalMemState.ioRegs[IO_SCROLLY]) & (u16)255) >> 3)); // address of the row of the tilemap
+    u8  tileMapColIdx  = globalMemState.ioRegs[IO_SCROLLX] >> 3;   // column index of the tilemap
+    u8  yPixOffset     = ((u8)globalVideoState.line + globalMemState.ioRegs[IO_SCROLLY]) & (u8)7; // y-pixel of tile
+    u8  xPixOffset     = globalMemState.ioRegs[IO_SCROLLX] & (u8)7;                               // x-pixel of tile
+    //u8* linePtr        = globalVideoState.frameBuffer + 160 * globalVideoState.line;              // frame buffer pointer
+    u8 tileIdx        = readByte(tileMapRowAddr + tileMapColIdx);                                 // tile index
+
+    // loop over pixels in the line
+    for(u8 px = 0; px < 160; px++) {
+      globalVideoState.frameBuffer = im_line_vas[bufferSelect];
+      globalVideoState.frameBuffer[xy2px(px,globalVideoState.line)] = 
+      readTilePtr(computeTileAddrPtr(tileIdx, tileData), xPixOffset, yPixOffset, pal);
+      //readTile(computeTileAddr(tileIdx, tileData), xPixOffset, yPixOffset, pal); // set the frame buffer
+
+      xPixOffset++; // increment tile pixel
+      //linePtr++;    // increment frame buffer pixel
+      if(xPixOffset == 8) { // if we have overflowed the tile
+        xPixOffset = 0;     // go to the beginning
+        tileMapColIdx = (tileMapColIdx + 1) & 31; // of the next tile (allow wraparound)
+        tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // and look up the tile index in the tile map
+      }
+    }
+  }
+
+//  // window renderer
+//  if(windowEnable) {
+//    u8 pal = globalMemState.ioRegs[IO_BGP];   // palette
+//    u8 wx = globalMemState.ioRegs[IO_WINX];   // location of the window (nyi)
+//    u8 wy = globalMemState.ioRegs[IO_WINY];   // location of the window (nyi)
+//    if(wx > 166 || wy > 143) {
+//      // if the window is out of this range, it is disabled too.
+//    } else {
+//      u16 tileMapRowAddr = windowMapAddr + 32*((((u16)globalVideoState.line)) >> 3);  // address of the row of the tilemap
+//      u8 tileMapColIdx = 0; // column index of the tilemap
+//      u8 yPixOffset = ((u8)globalVideoState.line) & (u8)7; // y-pixel of tile
+//      u8 xPixOffset = 0; // x-pixel of tile
+//      u8* linePtr        = globalVideoState.frameBuffer + 160 * globalVideoState.line;  // frame buffer pointer
+//      u8 tileIdx        = readByte(tileMapRowAddr + tileMapColIdx); // tile index
+//
+//      // loop over pixels in the line
+//      for(u8 px = 0; px < 160; px++) {
+//        *linePtr = readTile(computeTileAddr(tileIdx, tileData), xPixOffset, yPixOffset, pal); // set the frame buffer
+//
+//        xPixOffset++; // increment tile pixel
+//        linePtr++;    // increment frame buffer pixel
+//        if(xPixOffset == 8) { // if we have overflowed the tile
+//          xPixOffset = 0;     // go to the beginning
+//          tileMapColIdx = (tileMapColIdx + 1) & 31;  // of the next tile (allow wraparound, but it shouldn't happen?)
+//          tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // and look up the tile index in the tile map
+//        }
+//      }
+//    }
+//  }
+//
+//
+  // sprite renderer
+  if(objEnable) {
+    for(u16 spriteID = 0; spriteID < 40; spriteID++) {
+      u16 oamPtr = 0xfe00 + 4 * spriteID; // sprite information table
+      u8 spriteY = readByte(oamPtr);      // y-coordinate of sprite
+      u8 spriteX = readByte(oamPtr + 1);  // x-coordinate of sprite
+      u8 patternIdx = readByte(oamPtr + 2); // sprite pattern
+      u8 flags = readByte(oamPtr + 3);      // flag bits
+
+      bool pri   = (flags >> 7) & (u8)1;    // priority (transparency stuff)
+      bool yFlip = (flags >> 6) & (u8)1;    // flip around y?
+      bool xFlip = (flags >> 5) & (u8)1;    // flip around x?
+      bool palID = (flags >> 4) & (u8)1;    // palette ID (OBP0/OBP2)
+
+      u8 pal = palID ? globalMemState.ioRegs[IO_OBP1] : globalMemState.ioRegs[IO_OBP0];
+
+
+      if(spriteX | spriteY) {
+        // the sprite coordinates have an offset
+        u8 spriteStartY = spriteY - 16;
+        u8 spriteLastY = spriteStartY + 8; // todo 16 row sprites
+
+        // reject based on y if the sprite won't be visible in the current line
+        if(globalVideoState.line < spriteStartY || globalVideoState.line >= spriteLastY) {
+          continue;
+        }
+
+        // get y px relative to the sprite pattern
+        u8 tileY = globalVideoState.line - spriteStartY;
+        if(yFlip) {
+          tileY = 7 - tileY;
+        }
+
+        assert(tileY < 8);
+
+        // loop over the 8 pixels that the sprite is on:
+        for(u8 tileX = 0; tileX < 8; tileX++) {
+
+          u8 xPos = spriteX - 8 + tileX; // position on the screen
+          if(xPos >= 160) continue; // reject if we go off the end, don't wrap around
+
+          u32 fbIdx = xy2px(xPos, globalVideoState.line);
+          
+          globalVideoState.frameBuffer = im_line_vas[bufferSelect];
+          
+          
+          // current color at the screen
+          u8 old = globalVideoState.frameBuffer[fbIdx];
+
+          // get the pixel from the sprite pattern data
+          u8 tileLookupX = tileX;
+          if(xFlip) {
+            tileLookupX = 7 - tileX;
+          }
+          //u8 tileValue = readSpriteTile(0x8000 + patternIdx * 16, tileLookupX, tileY, pal);
+          u8 tileValue = readSpriteTileAddr(globalMemState.vram + patternIdx * 16, tileLookupX, tileY, pal);
+          //u8 tileValue = readSpriteTileAddr(globalMemState.vram + patternIdx*16, tileLookupX, tileY, pal);
+          //u8 tileValue = 4;
+          // don't draw transparent
+          if(tileValue == TRANSPARENT_SPRITE) continue; // (transparent sprites)
+
+          // not sure this is 100% right...
+          if(!pri) {
+            globalVideoState.frameBuffer[fbIdx] = tileValue;
+          } else {
+            if(old == BRIGHTEST_COLOR) {
+              globalVideoState.frameBuffer[fbIdx] = tileValue;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+static u32 oldTics = 0;
+float filteredFrameRate = 0.f;
+
+// step the video by a number of clock cycles
+void stepVideo(u32 cycles) {
+  globalVideoState.modeClock += cycles;
+  switch(globalVideoState.mode) {
+    case 2: // OAM read, scanning
+
+      if(globalVideoState.modeClock >= 80) {
+        globalVideoState.modeClock = 0;
+        globalVideoState.mode = 3; //  VRAM read, scanning
+      }
+      break;
+    case 3: //  VRAM read, scanning
+      if(globalVideoState.modeClock >= 172) {
+        globalVideoState.modeClock = 0;
+        globalVideoState.mode = 0; // hblank
+        renderLine(); // draw line into framebuffer
+      }
+      break;
+    case 0: // hblank
+      if(globalVideoState.modeClock >= 204) {
+        globalVideoState.modeClock = 0;
+        globalVideoState.line++;
+
+        if(globalVideoState.line == 143) {
+          
+          globalVideoState.mode = 1; // vblank
+          globalMemState.ioRegs[IO_IF] |= 0x1; // set interrupt for vblank
+          if(!keyboard.turbo) // if we are in "turbo" mode, don't update graphics
+            updateGraphics(); // display framebuffer on screen
+          //bufferSelect = !bufferSelect;
+          
+          
+          u32 dTics = tics - oldTics;
+          float frameTime = 64.0e-6 * (float)dTics;
+          filteredFrameRate = (0.94 * filteredFrameRate) + (0.06 * frameTime);
+          printf("%d %f %f\n", dTics, 1.f/frameTime, 1.f/filteredFrameRate);
+          frameCount++;
+          oldTics = tics;
+          
+        } else {
+          globalVideoState.mode = 2; // oam
+        }
+      }
+      break;
+    case 1: // vblank
+      if(globalVideoState.modeClock >= 456) {
+        globalVideoState.modeClock = 0;
+        globalVideoState.line++;
+
+        if(globalVideoState.line > 153) {
+          globalVideoState.mode = 2;
+          globalVideoState.line = 0;
+        }
+      }
+      break;
+    default:
+      assert(false);
+  }
+
+  globalMemState.ioRegs[IO_LY] = (u8)globalVideoState.line; // update current line
+
+
+  // this is likely somewhat wrong.
+  u8 stat = globalMemState.ioRegs[IO_STAT]; // update STAT register (this is likely the source of issue on bubble bobble)
+  stat &= ~7; // clear mode, coincidence
+  stat += globalVideoState.mode; // set current mode
+  if(globalMemState.ioRegs[IO_LYC] == globalMemState.ioRegs[IO_LY]) { // check coincidence
+    stat += 4;
+    if((stat >> 6) & 1) {
+      globalMemState.ioRegs[IO_IF] |= 2; // stat interrupt
+    }
+  }
+}
diff -r 1f728d08b3a7 -r c9afe1a7b423 video.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/video.h	Sun Jan 13 19:00:10 2019 +0000
@@ -0,0 +1,20 @@
+#ifndef GBC_VIDEO_H
+#define GBC_VIDEO_H
+
+#include "types.h"
+
+struct VideoState {
+  u32 mode;
+  u32 modeClock;
+  u32 line;
+  u8* frameBuffer;
+};
+
+extern VideoState globalVideoState;
+
+void initVideo(u8* frameBuffer);
+void stepVideo(u32 cycles);
+void renderLine();
+
+
+#endif //GBC_VIDEO_H