rlm@60: (ns com.aurellem.gb-driver rlm@87: (:import com.aurellem.gb.Gb) rlm@87: (:import java.io.File) rlm@87: (:import org.apache.commons.io.FileUtils) rlm@87: (:import (java.nio IntBuffer ByteOrder))) rlm@48: rlm@87: ;; Savestates rlm@87: (defrecord SaveState [frame data]) rlm@76: rlm@76: (def ^:dynamic *save-state-cache* rlm@76: (File. "/home/r/proj/pokemon-escape/save-states/")) rlm@76: rlm@87: (defn frame->filename [frame] rlm@87: (File. *save-state-cache* (format "%07d.sav" frame))) rlm@87: rlm@88: (defn write-state! [^SaveState save] rlm@87: (let [buf (:data save) rlm@87: bytes (byte-array (.limit buf)) rlm@87: dest (frame->filename (:frame save))] rlm@87: (.get buf bytes) rlm@87: (FileUtils/writeByteArrayToFile dest bytes) rlm@87: (.rewind buf) rlm@88: dest)) rlm@87: rlm@88: (defn read-state [frame] rlm@87: (let [save (frame->filename frame)] rlm@87: (if (.exists save) rlm@87: (let [buf (Gb/saveBuffer) rlm@87: bytes (FileUtils/readFileToByteArray save)] rlm@87: (.put buf bytes) rlm@87: (.flip buf) rlm@87: (SaveState. frame buf))))) rlm@87: ;;;;;;;;;;;;;;;; rlm@87: rlm@87: ;; Gameboy management rlm@87: (Gb/loadVBA) rlm@87: rlm@65: (def yellow-rom-image rlm@65: (File. "/home/r/proj/pokemon-escape/roms/yellow.gbc")) rlm@65: rlm@67: (def yellow-save-file rlm@67: (File. "/home/r/proj/pokemon-escape/roms/yellow.sav")) rlm@67: rlm@87: (def on? (atom nil)) rlm@73: rlm@87: (defn shutdown! [] (Gb/shutdown) (reset! on? false)) rlm@87: rlm@87: (defn restart! [] rlm@87: (shutdown!) rlm@67: (.delete yellow-save-file) rlm@87: (Gb/startEmulator (.getCanonicalPath yellow-rom-image)) rlm@87: (reset! on? true)) rlm@60: rlm@87: ;;; The first state! rlm@87: (defn gen-root! [] rlm@87: (restart!) rlm@88: (let [state (SaveState. 0 (Gb/saveState))] rlm@88: (write-state! state) rlm@88: state)) rlm@71: rlm@87: (defn root [] rlm@87: (if (.exists (frame->filename 0)) rlm@88: (read-state 0) rlm@87: (gen-root!))) rlm@87: rlm@87: ;;;; Press Buttons rlm@87: rlm@87: (def button-code rlm@87: {;; main buttons rlm@87: :a 0x0001 rlm@87: :b 0x0002 rlm@87: rlm@87: ;; directional pad rlm@87: :r 0x0010 rlm@87: :l 0x0020 rlm@87: :u 0x0040 rlm@87: :d 0x0080 rlm@87: rlm@87: ;; meta buttons rlm@87: :select 0x0004 rlm@87: :start 0x0008 rlm@87: rlm@87: ;; pseudo-buttons rlm@87: :restart 0x0800 ; hard reset rlm@87: :listen -1 ; listen for user input rlm@87: }) rlm@87: rlm@87: (defn button-mask [buttons] rlm@87: (reduce bit-or 0x0000 (map button-code buttons))) rlm@87: rlm@87: (def current-state (atom nil)) rlm@87: rlm@87: (defn set-state! [^SaveState state] rlm@87: (assert (:data state) "Not a valid state!") rlm@87: (if (not @on?) (restart!)) rlm@87: (if (not= @current-state state) rlm@87: (Gb/loadState (:data state))) rlm@87: (reset! current-state state)) rlm@87: rlm@87: (defrecord Move [keys state]) rlm@87: rlm@99: (defn update-state [] rlm@99: (reset! current-state rlm@99: (SaveState. (:frame @current-state) rlm@99: (Gb/saveState)))) rlm@99: rlm@87: (defn step rlm@87: ([^SaveState state buttons] rlm@87: (set-state! state) rlm@87: (Gb/step (button-mask buttons)) rlm@87: (reset! current-state rlm@87: (SaveState. (inc (:frame state))(Gb/saveState)))) rlm@87: ([^SaveState state] rlm@87: (step state [:listen])) rlm@87: ([] (step (if @current-state @current-state (root))))) rlm@87: rlm@100: (defn tick rlm@100: ([] (tick @current-state)) rlm@100: ([state] rlm@100: (set-state! state) rlm@100: (Gb/tick) rlm@100: (update-state))) rlm@100: rlm@87: (defn move rlm@87: [^Move move buttons] rlm@87: (Move. (step (:state move) buttons) buttons)) rlm@87: rlm@87: (defn play rlm@87: ([^SaveState state n] rlm@87: (reduce (fn [s _] (step s)) state (range n))) rlm@87: ([n] rlm@87: (play @current-state n))) rlm@87: rlm@87: (defn play-moves rlm@87: ([moves [prev state]] rlm@87: (set-state! state) rlm@87: (dorun (map (fn [move] (step @current-state move)) moves)) rlm@87: [(concat prev moves) @current-state])) rlm@87: rlm@87: ;;;;;;;;;;; rlm@87: rlm@87: rlm@87: ;;;;;;;;;;;;;;; CPU data rlm@87: rlm@64: (defn cpu-data [size arr-fn] rlm@64: (let [store (int-array size)] rlm@94: (fn get-data rlm@94: ([] (get-data @current-state)) rlm@94: ([state] rlm@94: (set-state! state) (arr-fn store) store)))) rlm@60: rlm@100: (defn write-cpu-data [size store-fn] rlm@100: (fn [new-data] rlm@100: (let [store (int-array new-data)] rlm@100: (assert (= size (count new-data))) rlm@100: (store-fn store)))) rlm@100: rlm@93: (def memory rlm@94: (cpu-data Gb/GB_MEMORY #(Gb/getMemory %))) rlm@93: rlm@64: (def ram rlm@93: (cpu-data Gb/RAM_SIZE #(Gb/getRAM %))) rlm@61: rlm@64: (def rom rlm@93: (cpu-data Gb/ROM_SIZE #(Gb/getROM %))) rlm@61: rlm@64: (def working-ram rlm@64: (cpu-data Gb/WRAM_SIZE #(Gb/getWRAM %))) rlm@62: rlm@64: (def video-ram rlm@64: (cpu-data Gb/VRAM_SIZE #(Gb/getVRAM %))) rlm@64: rlm@64: (def registers rlm@64: (cpu-data Gb/NUM_REGISTERS #(Gb/getRegisters %))) rlm@64: rlm@100: (def write-memory! rlm@100: (write-cpu-data Gb/GB_MEMORY #(Gb/writeMemory %))) rlm@96: rlm@100: (def write-registers! rlm@100: (write-cpu-data Gb/NUM_REGISTERS #(Gb/writeRegisters %))) rlm@100: rlm@101: ;;;;; Registers ;;;;;;;;;;;;;;;;;;;;;;;;;;; rlm@71: rlm@100: (defmacro gen-get-set-register [name index] rlm@100: (let [name-bang (symbol (str name "!"))] rlm@100: `(do rlm@100: (defn ~name rlm@100: ~(str "Retrieve the " name " register from state, or " rlm@100: "from @current-state if state is absent.") rlm@100: ([state#] rlm@100: (nth (registers state#) ~index)) rlm@100: ([] rlm@100: (~name @current-state))) rlm@100: (defn ~name-bang rlm@100: ~(str "Set the " name " register for state, or " rlm@100: "for @current-state if state is absent.") rlm@100: ([state# new-register#] rlm@100: (set-state! state#) rlm@100: (let [registers# (registers state#)] rlm@100: (aset registers# ~index new-register#) rlm@100: (Gb/writeRegisters registers#) rlm@100: (update-state))) rlm@100: ([new-register#] rlm@100: (~name-bang @current-state new-register#)))))) rlm@71: rlm@101: ;; 16 bit registers rlm@100: (gen-get-set-register PC 0) rlm@100: (gen-get-set-register SP 1) rlm@100: (gen-get-set-register AF 2) rlm@100: (gen-get-set-register BC 3) rlm@100: (gen-get-set-register DE 4) rlm@100: (gen-get-set-register HL 5) rlm@100: (gen-get-set-register IFF 6) rlm@100: rlm@101: ;; 8 bit registers rlm@101: (gen-get-set-register DIV 7) rlm@101: (gen-get-set-register TIMA 8) rlm@101: (gen-get-set-register TMA 9) rlm@101: (gen-get-set-register IF 11) rlm@101: (gen-get-set-register LCDC 12) rlm@101: (gen-get-set-register STAT 13) rlm@101: (gen-get-set-register SCY 14) rlm@101: (gen-get-set-register SCX 15) rlm@101: (gen-get-set-register LY 16) rlm@101: (gen-get-set-register DMA 18) rlm@101: (gen-get-set-register WY 19) rlm@101: (gen-get-set-register WX 20) rlm@101: (gen-get-set-register VBK 21) rlm@101: (gen-get-set-register HDMA1 22) rlm@101: (gen-get-set-register HDMA2 23) rlm@101: (gen-get-set-register HDMA3 24) rlm@101: (gen-get-set-register HDMA4 25) rlm@101: (gen-get-set-register HDMA5 26) rlm@101: (gen-get-set-register SVBK 27) rlm@101: (gen-get-set-register IE 28) rlm@101: rlm@87: ;;;;;;;;;;;;;;; rlm@71: rlm@87: (defmacro defn-memo rlm@87: [& forms] rlm@87: (let [fun-name (first forms)] rlm@87: `(do rlm@87: (defn ~@forms) rlm@87: (alter-var-root (var ~fun-name) memoize)))) rlm@95: