 (defn- VxVy [V x y]
   [(nth V x) (nth V y)])
 
-(defn- update-memory
+
+(defn- write-pc
+  [state pc]
+  (assoc-in state [:cpu :pc] pc))
+
+(defn- write-sp
+  [state sp]
+  (assoc-in state [:cpu :sp] sp))
+
+(defn- write-i
+  [state i]
+  (assoc-in state [:cpu :i] i))
+
+(defn- write-dt
+  [state dt]
+  (assoc-in state [:cpu :dt] dt))
+
+(defn- write-st
+  [state st]
+  (assoc-in state [:cpu :st] st))
+
+(defn- write-draw-flag
+  [state draw-flag]
+  (assoc-in state [:cpu :draw-flag] draw-flag))
+
+(defn- write-stack
+  ([{{:keys [stack]} :cpu :as state} idx val]
+   (write-stack state (assoc stack idx val)))
+  ([state stack]
+   (assoc-in state [:cpu :stack] stack)))
+
+(defn- write-memory
   ([{{:keys [memory]} :cpu :as state} arr start]
-   (update-memory state (assoc-in-range memory arr start)))
+   (write-memory state (assoc-in-range memory arr start)))
   ([state memory]
    (assoc-in state [:cpu :memory] memory)))
 
-(defn- update-pc
-  [state pc]
-  (assoc-in state [:cpu :pc] pc))
-
-(defn- update-sp
-  [state sp]
-  (assoc-in state [:cpu :sp] sp))
-
-(defn- update-i
-  [state i]
-  (assoc-in state [:cpu :i] i))
-
-(defn- update-dt
-  [state dt]
-  (assoc-in state [:cpu :dt] dt))
-
-(defn- update-st
-  [state st]
-  (assoc-in state [:cpu :st] st))
-
-(defn- update-draw-flag
-  [state draw-flag]
-  (assoc-in state [:cpu :draw-flag] draw-flag))
-
-(defn- update-stack
-  ([{{:keys [stack]} :cpu :as state} idx val]
-   (update-stack state (assoc stack idx val)))
-  ([state stack]
-   (assoc-in state [:cpu :stack] stack)))
-
-(defn- update-v
+(defn- write-v
   ([{{:keys [v]} :cpu :as state} x val]
-   (update-v state (assoc v x val)))
+   (write-v state (assoc v x val)))
   ([{{:keys [v]} :cpu :as state} val]
-   (assoc-in state [:cpu :v]
-             (if (sequential? val)
-               (assoc-in-range v val) val))))
-
+   (assoc-in state [:cpu :v] (assoc-in-range v val))))
 
 (def ^{:private true}
   default-state

  (-> state
      (assoc :screen (screen/make-screen))
      (assoc-in [:cpu :draw-flag] 1)
      (update-draw-flag 1)
      (update-pc 2)))

(defn opcode-00EE
  "Return from a subroutine."
  [{{:keys [sp stack]} :cpu :as state}]
  (-> state
      (update-sp (dec sp))
      (update-pc (+ 2 (nth stack (dec sp))))))

(defn opcode-1NNN
  "Jump to address NNN."
  [state nnn]
  (-> state
      (update-pc nnn)))

(defn opcode-2NNN
  "Call subroutine at nnn."
  [{{:keys [pc sp stack]} :cpu :as state} nnn]
  (-> state
      (update-stack sp pc)
      (update-sp (inc pc))
      (update-pc nnn)))

(defn opcode-3XNN
  "Skip next instruction if VX = NN."
  [{{:keys [v]} :cpu :as state} x nn]
  (let [Vx (nth v x)]
    (-> state
        (update-pc (if (= Vx nn) 4 2)))))

(defn opcode-4XNN
  "Skip next instruction if VX != NN."
  [{{:keys [v]} :cpu :as state} x nn]
  (let [Vx (nth v x)]
    (-> state
        (update-pc (if-not (= Vx nn) 4 2)))))

(defn opcode-5XY0
  "Skip next instruction if Vx = Vy."
  [{{:keys [v]} :cpu :as state} X Y]
  (let [[Vx Vy] (VxVy v X Y)]
    (-> state
        (update-pc (if (= Vx Vy) 4 2)))))

(defn opcode-6XNN
  "Set Vx to NN."
  [state x nn]
  (-> state
      (update-v x nn)
      (update-pc 2)))

(defn opcode-7XNN
  "Set Vx = Vx + NN."
  [{{:keys [v]} :cpu :as state} x nn]
  (let [Vx (bit-and (+ (nth v x) nn) 0xff)]
    (-> state
        (update-v x Vx)
        (update-pc 2))))

(defn opcode-8XY0
  "Set Vx = Vy."
  [{{:keys [v]} :cpu :as state} x y]
  (let [Vy (nth v y)]
    (-> state
        (update-v  x Vy)
        (update-pc 2))))

(defn opcode-8XY1
  "Set Vx = Vx OR Vy."
  [{{:keys [v]} :cpu :as state} x y]
  (let [[Vx Vy] (VxVy v x y)]
    (-> state
        (update-v  x (bit-or Vx Vy))
        (update-pc 2))))

(defn opcode-8XY2
  "Set Vx = Vx AND Vy."
  [{{:keys [v]} :cpu :as state} x y]
  (let [[Vx Vy] (VxVy v x y)]
    (-> state
        (update-v  x (bit-and Vx Vy))
        (update-pc 2))))

(defn opcode-8XY3
  "Set Vx = Vx XOR Vy."
  [{{:keys [v]} :cpu :as state} x y]
  (let [[Vx Vy] (VxVy v x y)]
    (-> state
        (update-v  x (bit-xor Vx Vy))
        (update-pc 2))))

(defn opcode-8XY4
  "Set Vx = Vx + Vy, set VF = carry."

  (let [[Vx Vy] (VxVy v x y)
        sub (- Vx Vy)]
    (-> state
        (write-v  x (if (< sub 0) (+ sub 256) sub))
        (write-v 15 (if (< sub 0) 1 0))
        (write-pc 2))))

(defn opcode-8XY6
  "Set Vx = Vx SHR 1."
  [{{:keys [v]} :cpu :as state} x]
  (let [Vx (nth v x)]
    (-> state
        (write-v 15 (bit-and Vx 0x01))
        (write-v  x (bit-shift-right Vx 1))
        (write-pc 2))))

(defn opcode-8XY7
  "Set Vx = Vy - Vx, set VF = NOT borrow."
  [{{:keys [v]} :cpu :as state} x y]
  (let [[Vx Vy] (VxVy v x y)]
    (-> state
        (write-v 15 (if (> Vx Vy) 0 1))
        (write-v  x (- Vy Vx))
        (write-pc 2))))

(defn opcode-8XYE
  "Set Vx = Vx SHL 1."
  [{{:keys [v]} :cpu :as state} x]
  (let [Vx (nth v x)]
    (-> state
        (write-v 15 (bit-and Vx 0x80))
        (write-v  x (bit-shift-left Vx 1))
        (write-pc 2))))

(defn opcode-9XY0
  "Skip next instruction if Vx != Vy."
  [{{:keys [v]} :cpu :as state} x y]
  (let [[Vx Vy] (VxVy v x y)]
    (-> state
        (write-pc (if-not (= Vx Vy) 4 2)))))

(defn opcode-ANNN
  "Set I = NNN."
  [state nnn]
  (-> state
      (write-i nnn)
      (write-pc 2)))

(defn opcode-BNNN
  "Jump to location nnn + V0"
  [{{:keys [v]} :cpu :as state} nnn]
  (let [V0 (nth v 0)]
    (-> state
        (write-pc (+ V0 nnn)))))

(defn opcode-CXNN
  "Set Vx = random byte AND NN"
  [{{:keys [v]} :cpu :as state} x nn]
  (-> state
      (write-v x (bit-and (rand-int 256) nn))
      (write-pc 2)))

;; FIXME:
(defn opcode-DXYN
