# HG changeset patch # User Robert McIntyre # Date 1331779057 18000 # Node ID 3a60bb14a64ae926189b9bae43e4e3b038d936c9 # Parent 2f8089eacab9ce2e1a7fe6f66ddc2d21a31de9ad better functional assembly interface; removed frame numbers from SaveStates diff -r 2f8089eacab9 -r 3a60bb14a64a clojure/com/aurellem/assembly.clj --- a/clojure/com/aurellem/assembly.clj Tue Mar 13 14:40:01 2012 -0500 +++ b/clojure/com/aurellem/assembly.clj Wed Mar 14 21:37:37 2012 -0500 @@ -2,14 +2,13 @@ (:use (com.aurellem gb-driver vbm title items)) (:import [com.aurellem.gb_driver SaveState])) - - +(defn mid-game [] + (read-state "mid-game")) (defn inject-assembly [^SaveState state program-counter registers assembly-code] - (set-state! state) (let [scratch-memory (memory state)] ;; inject assembly code (dorun (map (fn [index val] @@ -17,12 +16,11 @@ (range program-counter (+ program-counter (count assembly-code))) assembly-code)) - (write-memory! scratch-memory) - (set-state! (update-state)) - (write-registers! registers) - (set-state! (update-state)) - (PC! program-counter) - (update-state))) + (-> state + (write-memory! scratch-memory) + (write-registers! registers) + (PC! program-counter)))) + (defn inject-item-assembly ([^SaveState state assembly-code] @@ -34,19 +32,17 @@ (defn info ([^SaveState state] - (set-state! state) - (println "PC: " (PC)) - (println "Instruction:" (format "0x%02X" (aget (memory state) (PC)))) + (println "PC: " (PC state)) + (println "Instruction:" + (format "0x%02X" (aget (memory state) (PC state)))) state)) -(defn irony [] (read-state 578544)) - (defn run-assembly ([info-fn assembly n] (let [final-state (reduce (fn [state _] (tick (info-fn state))) - (inject-item-assembly (tick (tick (tick (irony)))) assembly) + (inject-item-assembly (tick (tick (tick (mid-game)))) assembly) (range n))] (shutdown!) final-state)) diff -r 2f8089eacab9 -r 3a60bb14a64a clojure/com/aurellem/experiments/items.clj --- a/clojure/com/aurellem/experiments/items.clj Tue Mar 13 14:40:01 2012 -0500 +++ b/clojure/com/aurellem/experiments/items.clj Wed Mar 14 21:37:37 2012 -0500 @@ -1,3 +1,7 @@ +(ns com.aurellem.experiments.items + (:use (com.aurellem gb-driver vbm title items)) + (:import [com.aurellem.gb_driver SaveState])) + ;; try just buying five potions in sequence and see what changes ;; each time. @@ -10,28 +14,27 @@ ;; trying to find how items are represented in memory -(comment - (def empty-inventory @current-state) - - (def one-potion @current-state) - - (def two-potions @current-state) - - (def three-potions @current-state) - - (def four-potions @current-state) - - (def five-potions @current-state) +(def zero-potions (read-state "zero-potions")) + +(def one-potion (read-state "one-potion")) + +(def two-potions (read-state "two-potions")) + +(def three-potions (read-state "three-potions")) + +(def four-potions (read-state "four-potions")) + +(def five-potions (read-state "five-potions")) ;; result - (def canidates - (apply common-differences - (map (comp vec memory) - [empty-inventory one-potion two-potions three-potions - four-potions five-potions]))) +(defn canidates [] + (apply common-differences + (map (comp vec memory) + [zero-potions one-potion two-potions three-potions + four-potions five-potions]))) - [55875 (37 15 49 27 14 44)] + (comment [55875 (37 15 49 27 14 44)] [55876 (30 1 49 56 55 23)] [49158 (154 191 78 135 70 73)] [54087 (49 40 37 34 25 22)] @@ -53,10 +56,6 @@ -(def item-hack (read-state 7999)) - -(def item-hack2 (read-state 75882)) - (defn get-mem [] (subvec (vec (memory @current-state)) 54040 (+ 54046 100))) @@ -92,14 +91,19 @@ ;; now it's time to learn the item codes -(def item-hack-3 (read-state 77557)) +(def inventory-begin + (read-state "inventory-begin")) + (defn show-item "Run a saved pokemon with the first item replaced by the item named by n." [n] - (set-state! item-hack-3) + (set-state! inventory-begin) (let [mem (memory)] + (aset mem 54044 1) (aset mem 54045 n) + (aset mem 54046 1) + (aset mem 54047 255) (write-memory! mem)) (step) (->> [[] @current-state] @@ -372,3 +376,128 @@ ;; 253 TM53 ;; 254 TM54 ;; 255 end-of-list-sentinel + + + +(defn run-item-program + "This is my first assembly/item program! + it just increments BC by one. + + The code places a 3 'great balls' at the beginning of the + inventory, then directly sets the program counter to start + executing at the position of the 'great balls' in memory. + + Since a 'great ball' is represented in memory as 0x03, which + corresponts to the opcode which increments BC by one, that is + what happens. Then the program counter to the 0x03 quantity entry + and BC is incremented again. + + Obviously, the game crashes more or less immediately after the + program counter advances past the 'great balls' into the next items + in the inventory, thus I call shutdown! before anything bad happens." + [] + (set-inventory (read-state "mid-game") [[:great-ball 3]]) + (print-inventory) + (println "3 ticks") (tick) (tick) (tick) + (println "PC before:" (PC)) + (println "BC before:" (BC)) + (PC! (inc item-list-start)) + (println "PC after setting:" (PC)) + (println "data at PC:" (aget (memory) (PC))) + (println "one tick") + (tick) + (println "PC after one tick:" (PC)) + (println "BC after one tick:" (BC)) + (tick) + (println "PC after two ticks:" (PC)) + (println "BC after two ticks:" (BC)) + + (shutdown!)) + + + + +(defn test-opcodes-1 + [] + (let [final-state + (-> + (read-state "mid-game") + (set-inv-mem + [20 0x02 0x00 0x00 0x02 0x00 0x00 + 0x00 0x0 0xFF]) + (print-inventory) + ;;((fn [_] (println "3 ticks") _)) + (tick) (tick) (tick) + + ;;(println "PC before:" (PC)) + ;;(println "BC before:" (BC)) + ;;(println "AF:" (AF)) + (PC! (inc item-list-start)) + (BC! (+ 1 item-list-start)) + ;;(println "PC after setting:" (PC)) + ;;(println "data at PC:" (aget (memory) (PC))) + ;;(println "data at " (BC) "(BC):" (aget (memory) (BC))) + + ;;(println "one tick") + (tick) + ;;(println "PC after one tick:" (PC)) + ;;(println "BC after one tick:" (BC)) + ;;(println "data at PC:" (aget (memory) (PC))) + ;;(println "data at " (BC) "(BC):" (aget (memory) (BC))) + (tick) + (AF! 0xFFFF) + ;;(println "PC after two ticks:" (PC)) + ;;(println "BC after two ticks:" (BC)) + ;;(println "data at PC:" (aget (memory) (PC))) + ;;(println "data at " (BC) "(BC):" (aget (memory) (BC))) + (tick) + ;;(println "PC after three ticks:" (PC)) + ;;(println "BC after three ticks:" (BC)) + ;;(println "data at PC:" (aget (memory) (PC))) + ;;(println "data at " (BC) "(BC):" (aget (memory) (BC))) + (tick) + ;;(println "PC after four ticks:" (PC)) + ;;(println "BC after four ticks:" (BC)) + ;;(println "data at PC:" (aget (memory) (PC))) + ;;(println "data at " (BC) "(BC):" (aget (memory) (BC))) + (tick) + ;;(println "PC after five ticks:" (PC)) + ;;(println "BC after five ticks:" (BC)) + ;;(println "data at PC:" (aget (memory) (PC))) + ;;(println "data at " (BC) "(BC):" (aget (memory) (BC))) + (print-inventory) + )] + + (shutdown!) + final-state)) + + + +(defn test-opcodes-2 + [] + (set-inv-mem (read-state "mid-game") + [20 0x08 0x1D 0xD3 0x00 0x00 0x00 + 0x00 0x0 0xFF]) + (print-inventory) + (println "3 ticks") (tick) (tick) (tick) + (println "PC before:" (PC)) + (println "SP:" (SP)) + (PC! (inc item-list-start)) + (println "PC after setting:" (PC)) + (println "SP:" (Integer/toBinaryString (SP))) + (println "data at PC:" (aget (memory) (PC))) + (println "data at 0xD31D:" (Integer/toBinaryString (aget (memory) 0xD31D))) + (println "data at 0xD31E:" (Integer/toBinaryString (aget (memory) 0xD31E))) + (println "one tick") + (tick) + (println "PC after one tick:" (PC)) + (println "data at PC:" (aget (memory) (PC))) + (println "data at 0xD31D:" (Integer/toBinaryString (aget (memory) 0xD31D))) + (println "data at 0xD31E:" (Integer/toBinaryString (aget (memory) 0xD31E))) + (tick) (tick) (tick) + (println "PC aftter four tick:" (PC)) + (println "data at PC:" (aget (memory) (PC))) + (println "data at 0xD31D:" (aget (memory) 0xD31D)) + + (print-inventory) + (shutdown!)) diff -r 2f8089eacab9 -r 3a60bb14a64a clojure/com/aurellem/gb_driver.clj --- a/clojure/com/aurellem/gb_driver.clj Tue Mar 13 14:40:01 2012 -0500 +++ b/clojure/com/aurellem/gb_driver.clj Wed Mar 14 21:37:37 2012 -0500 @@ -5,31 +5,36 @@ (:import (java.nio IntBuffer ByteOrder))) ;; Savestates -(defrecord SaveState [frame data]) +(defrecord SaveState [data]) (def ^:dynamic *save-state-cache* (File. "/home/r/proj/pokemon-escape/save-states/")) -(defn frame->filename [frame] - (File. *save-state-cache* (format "%07d.sav" frame))) - -(defn write-state! [^SaveState save] - (let [buf (:data save) - bytes (byte-array (.limit buf)) - dest (frame->filename (:frame save))] - (.get buf bytes) - (FileUtils/writeByteArrayToFile dest bytes) - (.rewind buf) - dest)) +(def current-state (atom nil)) -(defn read-state [frame] - (let [save (frame->filename frame)] +(defn state-cache-file [name] + (File. *save-state-cache* (str name ".sav"))) + +(defn write-state! + ([^SaveState name] + (write-state! @current-state name)) + ([^SaveState save ^String name] + (let [buffer (:data save) + bytes (byte-array (.limit buffer)) + dest (state-cache-file name)] + (.get buffer bytes) + (FileUtils/writeByteArrayToFile dest bytes) + (.rewind buffer) + dest))) + +(defn read-state [name] + (let [save (state-cache-file name)] (if (.exists save) - (let [buf (Gb/saveBuffer) + (let [buffer (Gb/saveBuffer) bytes (FileUtils/readFileToByteArray save)] - (.put buf bytes) - (.flip buf) - (SaveState. frame buf))))) + (.put buffer bytes) + (.flip buffer) + (SaveState. buffer))))) ;;;;;;;;;;;;;;;; ;; Gameboy management @@ -54,13 +59,12 @@ ;;; The first state! (defn gen-root! [] (restart!) - (let [state (SaveState. 0 (Gb/saveState))] - (write-state! state) - state)) + (let [state (SaveState. (Gb/saveState))] + (write-state! state "root" ) state)) (defn root [] - (if (.exists (frame->filename 0)) - (read-state 0) + (if (.exists (state-cache-file "root")) + (read-state "root") (gen-root!))) ;;;; Press Buttons @@ -88,27 +92,24 @@ (defn button-mask [buttons] (reduce bit-or 0x0000 (map button-code buttons))) -(def current-state (atom nil)) - (defn set-state! [^SaveState state] (assert (:data state) "Not a valid state!") (if (not @on?) (restart!)) - (Gb/loadState (:data state)) - (reset! current-state state)) - -(defrecord Move [keys state]) + (if (not= state @current-state) + (do + (Gb/loadState (:data state)) + (reset! current-state state)))) (defn update-state [] (reset! current-state - (SaveState. (:frame @current-state) - (Gb/saveState)))) + (SaveState. (Gb/saveState)))) (defn step ([^SaveState state buttons] (set-state! state) (Gb/step (button-mask buttons)) (reset! current-state - (SaveState. (inc (:frame state))(Gb/saveState)))) + (SaveState. (Gb/saveState)))) ([^SaveState state] (step state [:listen])) ([] (step (if @current-state @current-state (root))))) @@ -120,18 +121,24 @@ (Gb/tick) (update-state))) -(defn move - [^Move move buttons] - (Move. (step (:state move) buttons) buttons)) - (defn play ([^SaveState state n] - (reduce (fn [s _] (step s)) state (range n))) + (try + (set-state! state) + (dorun (dotimes [_ n] + (Thread/sleep 1) + (Gb/step))) + + (finally + (update-state)))) ([n] (play @current-state n))) -(defn continue! [] - (play @current-state Integer/MAX_VALUE)) +(defn continue! + ([state] + (play state Integer/MAX_VALUE)) + ([] + (continue! @current-state))) (defn play-moves ([moves [prev state]] @@ -152,10 +159,16 @@ (set-state! state) (arr-fn store) store)))) (defn write-cpu-data [size store-fn] - (fn [new-data] - (let [store (int-array new-data)] - (assert (= size (count new-data))) - (store-fn store)))) + (fn store-data + ([state new-data] + (set-state! state) + (let [store (int-array new-data)] + (assert (= size (count new-data))) + (store-fn store) + (update-state))) + ([new-data] + (store-data @current-state new-data)))) + (def memory (cpu-data Gb/GB_MEMORY #(Gb/getMemory %))) diff -r 2f8089eacab9 -r 3a60bb14a64a clojure/com/aurellem/items.clj --- a/clojure/com/aurellem/items.clj Tue Mar 13 14:40:01 2012 -0500 +++ b/clojure/com/aurellem/items.clj Wed Mar 14 21:37:37 2012 -0500 @@ -214,7 +214,8 @@ (aset mem index val)) (range item-list-start (+ item-list-start (count inv-codes))) inv-codes)) - (write-memory! mem))) + (write-memory! mem) + (update-state))) (defn set-inventory [^SaveState state new-inventory] @@ -235,7 +236,11 @@ (concat items (inventory state)))) ([items] (give @current-state items))) - + +(defn clear-inventory + ([^SaveState state] + (set-inventory state [])) + ([] (clear-inventory @current-state))) (def gliched-tms [[:TM51 1] @@ -264,115 +269,3 @@ [:soulbadge 1] ]) -(defn run-item-program - "This is my first assembly/item program! - it just increments BC by one. - - The code places a 3 'great balls' at the beginning of the - inventory, then directly sets the program counter to start - executing at the position of the 'great balls' in memory. - - Since a 'great ball' is represented in memory as 0x03, which - corresponts to the opcode which increments BC by one, that is - what happens. Then the program counter to the 0x03 quantity entry - and BC is incremented again. - - Obviously, the game crashes more or less immediately after the - program counter advances past the 'great balls' into the next items - in the inventory, thus I call shutdown! before anything bad happens." - [] - (set-inventory (read-state 578544) [[:great-ball 3]]) - (print-inventory) - (println "3 ticks") (tick) (tick) (tick) - (println "PC before:" (PC)) - (println "BC before:" (BC)) - (PC! (inc item-list-start)) - (println "PC after setting:" (PC)) - (println "data at PC:" (aget (memory) (PC))) - (println "one tick") - (tick) - (println "PC after one tick:" (PC)) - (println "BC after one tick:" (BC)) - (tick) - (println "PC after two ticks:" (PC)) - (println "BC after two ticks:" (BC)) - - (shutdown!)) - - -(defn test-opcodes-1 - [] - (set-inv-mem (read-state 578544) - [20 0x02 0x00 0x00 0x02 0x00 0x00 - 0x00 0x0 0xFF]) - (print-inventory) - (println "3 ticks") (tick) (tick) (tick) - (println "PC before:" (PC)) - (println "BC before:" (BC)) - (println "AF:" (AF)) - (PC! (inc item-list-start)) - (BC! (+ 1 item-list-start)) - (println "PC after setting:" (PC)) - (println "data at PC:" (aget (memory) (PC))) - (println "data at " (BC) "(BC):" (aget (memory) (BC))) - - (println "one tick") - (tick) - (println "PC after one tick:" (PC)) - (println "BC after one tick:" (BC)) - (println "data at PC:" (aget (memory) (PC))) - (println "data at " (BC) "(BC):" (aget (memory) (BC))) - (tick) - (AF! 0xFFFF) - (println "PC after two ticks:" (PC)) - (println "BC after two ticks:" (BC)) - (println "data at PC:" (aget (memory) (PC))) - (println "data at " (BC) "(BC):" (aget (memory) (BC))) - (tick) - (println "PC after three ticks:" (PC)) - (println "BC after three ticks:" (BC)) - (println "data at PC:" (aget (memory) (PC))) - (println "data at " (BC) "(BC):" (aget (memory) (BC))) - (tick) - (println "PC after four ticks:" (PC)) - (println "BC after four ticks:" (BC)) - (println "data at PC:" (aget (memory) (PC))) - (println "data at " (BC) "(BC):" (aget (memory) (BC))) - (tick) - (println "PC after five ticks:" (PC)) - (println "BC after five ticks:" (BC)) - (println "data at PC:" (aget (memory) (PC))) - (println "data at " (BC) "(BC):" (aget (memory) (BC))) - (print-inventory) - (shutdown!)) - - - -(defn test-opcodes-2 - [] - (set-inv-mem (read-state 578544) - [20 0x08 0x1D 0xD3 0x00 0x00 0x00 - 0x00 0x0 0xFF]) - (print-inventory) - (println "3 ticks") (tick) (tick) (tick) - (println "PC before:" (PC)) - (println "SP:" (SP)) - (PC! (inc item-list-start)) - (println "PC after setting:" (PC)) - (println "SP:" (Integer/toBinaryString (SP))) - (println "data at PC:" (aget (memory) (PC))) - (println "data at 0xD31D:" (Integer/toBinaryString (aget (memory) 0xD31D))) - (println "data at 0xD31E:" (Integer/toBinaryString (aget (memory) 0xD31E))) - (println "one tick") - (tick) - (println "PC after one tick:" (PC)) - (println "data at PC:" (aget (memory) (PC))) - (println "data at 0xD31D:" (Integer/toBinaryString (aget (memory) 0xD31D))) - (println "data at 0xD31E:" (Integer/toBinaryString (aget (memory) 0xD31E))) - (tick) (tick) (tick) - (println "PC aftter four tick:" (PC)) - (println "data at PC:" (aget (memory) (PC))) - (println "data at 0xD31D:" (aget (memory) 0xD31D)) - - (print-inventory) - (shutdown!)) diff -r 2f8089eacab9 -r 3a60bb14a64a java/src/com/aurellem/gb/Gb.java --- a/java/src/com/aurellem/gb/Gb.java Tue Mar 13 14:40:01 2012 -0500 +++ b/java/src/com/aurellem/gb/Gb.java Wed Mar 14 21:37:37 2012 -0500 @@ -20,7 +20,6 @@ * @param rom - the name of the rom. */ public static native void startEmulator(String rom); - public static void loadVBA(){ System.loadLibrary("vba");