rlm@105: (ns com.aurellem.assembly rlm@105: (:use (com.aurellem gb-driver vbm title items)) rlm@105: (:import [com.aurellem.gb_driver SaveState])) rlm@105: rlm@106: (defn mid-game [] rlm@106: (read-state "mid-game")) rlm@105: rlm@105: (defn inject-assembly rlm@107: ([^SaveState state rlm@105: program-counter registers rlm@105: assembly-code] rlm@105: (let [scratch-memory (memory state)] rlm@105: ;; inject assembly code rlm@105: (dorun (map (fn [index val] rlm@105: (aset scratch-memory index val)) rlm@105: (range program-counter rlm@105: (+ program-counter (count assembly-code))) rlm@105: assembly-code)) rlm@106: (-> state rlm@106: (write-memory! scratch-memory) rlm@106: (write-registers! registers) rlm@112: (PC! program-counter))))) rlm@105: rlm@105: (defn inject-item-assembly rlm@105: ([^SaveState state assembly-code] rlm@105: (inject-assembly state (inc item-list-start) rlm@105: (registers state) rlm@105: assembly-code)) rlm@105: ([assembly-code] rlm@105: (inject-item-assembly @current-state assembly-code))) rlm@105: rlm@105: (defn info rlm@105: ([^SaveState state] rlm@107: (println (format "PC: 0x%04X" (PC state))) rlm@106: (println "Instruction:" rlm@106: (format "0x%02X" (aget (memory state) (PC state)))) rlm@105: state)) rlm@105: rlm@107: (defn print-interrupt rlm@107: [^SaveState state] rlm@107: (println (format "IE: %d" (IE state))) rlm@107: state) rlm@107: rlm@105: (defn run-assembly rlm@105: ([info-fn assembly n] rlm@105: (let [final-state rlm@105: (reduce (fn [state _] rlm@105: (tick (info-fn state))) rlm@107: (inject-item-assembly rlm@107: (mid-game) assembly) rlm@105: (range n))] rlm@105: final-state)) rlm@105: ([assembly n] rlm@105: (run-assembly info assembly n))) rlm@107: rlm@107: (def buttons-port 0xFF00) rlm@107: rlm@107: (defn A [state] rlm@107: (bit-shift-right (bit-and 0x0000FF00 (AF state)) 8)) rlm@107: rlm@110: (defn binary-str [num] rlm@110: (format "%08d" rlm@110: (Integer/parseInt rlm@110: (Integer/toBinaryString num) 10))) rlm@110: rlm@110: (defn view-register [state name reg-fn] rlm@110: (println (format "%s: %s" name rlm@110: (binary-str (reg-fn state)))) rlm@110: state) rlm@110: rlm@110: rlm@107: (defn view-memory [state mem] rlm@110: (println (format "mem 0x%04X = %s" mem rlm@110: (binary-str (aget (memory state) mem)))) rlm@107: state) rlm@107: rlm@113: (defn read-down-button [] rlm@110: (-> (tick (mid-game)) rlm@110: (IE! 0) ; disable interrupts rlm@110: (inject-item-assembly rlm@110: (concat rlm@110: ;; write 00010000 to 0xFF00 to select joypad rlm@110: [0x18 ;D31D ; jump over rlm@110: 0x01 ;D31E ; the next 8 bits rlm@113: ;D31F rlm@113: (Integer/parseInt "00100000" 2) ; data section rlm@111: rlm@111: 0xFA ;D320 ; load (D31F) into A rlm@111: 0x1F ;D321 --> rlm@111: 0xD3 ;D322 --> D31F rlm@107: rlm@111: 0xEA ;D323 ; load (A), which is rlm@111: 0x00 ;D324 --> ; 00010000, into FF00 rlm@111: 0xFF ;D325 --> FF00 rlm@111: rlm@111: 0x18 ;D326 ; this is the place where rlm@111: 0x01 ;D327 ; we will store whether rlm@111: 0x00 ;D328 ; "down" is pressed. rlm@107: rlm@111: 0xFA ;D329 ; (FF00) -> A rlm@111: 0x00 ;D32A rlm@111: 0xFF ;D32B rlm@110: rlm@111: 0xCB ;D32C ; Test whether "down" rlm@111: 0x5F ;D32D ; is pressed. rlm@111: rlm@111: 0x28 ;D32E ; if down is pressed, rlm@111: 0x03 ;D32F ; skip the next section rlm@111: ; of code. rlm@111: ;; down-is-not-pressed rlm@111: 0xC3 ;D330 rlm@111: 0x1D ;D331 ; return to beginning rlm@111: 0xD3 ;D332 rlm@111: rlm@111: ;; down-is-pressed rlm@111: 0xEA ;D334 ; write A to D328 if rlm@111: 0x28 ;D335 ; "down" was pressed rlm@111: 0xD3 ;D336 rlm@111: rlm@111: 0xC3 ;D330 rlm@111: 0x1D ;D331 ; return to beginning rlm@111: 0xD3 ;D332 rlm@110: ] rlm@110: rlm@111: [])))) rlm@111: rlm@113: rlm@113: ;; specs for main bootstrap program rlm@113: ;; starts in "mode-select" mode rlm@113: ;; Each button press takes place in a single frame. rlm@113: ;; mode-select-mode takes one of the main buttons rlm@113: ;; which selects one of up to eight modes rlm@113: ;; mode 1 activated by the "A" button rlm@113: ;; the next two button presses indicates the start rlm@113: ;; memory location which to which the bootstrap rlm@113: ;; program will write. rlm@113: ;; This is done by using each of the eight buttons to rlm@113: ;; spell out an 8 bit number. The order of buttons is rlm@113: ;; ["A" "B" "start" "select" "up" "right" "down" "left"], rlm@113: ;; [:a :start :l] --> 10100001 rlm@113: rlm@113: ;; the next button press determines how many bytes are to be rlm@113: ;; written, starting at the start position. rlm@113: rlm@113: ;; then, the actual bytes are entered and are written to the rlm@113: ;; start address in sequence. rlm@113: rlm@113: rlm@113: (defn count-frames [] rlm@113: (-> (tick (mid-game)) rlm@113: (IE! 0) ; disable interrupts rlm@113: (inject-item-assembly rlm@113: ;; write 00010000 to 0xFF00 to select joypad rlm@113: [0x18 ;D31D ; jump over rlm@113: 0x02 ;D31E ; the next 2 bytes rlm@113: 0x00 ;D31F ; frame-count rlm@113: 0x00 ;D320 ; v-blank-prev rlm@113: rlm@113: 0xFA ;D321 rlm@113: 0x41 ;D322 ; load (FF41) into A rlm@113: 0xFF ;D323 ; this contains mode flags rlm@113: rlm@113: rlm@113: rlm@113: ;; if we're in v-blank, the bit-1 is 0 rlm@113: ;; and bit-2 is 1 Otherwise, it is not v-blank. rlm@113: 0xCB ;D324 ; test bit-1 of A rlm@113: 0x4F ;D325 rlm@113: rlm@113: 0xC2 ;D326 ; if bit-1 is not 0 rlm@113: 0x43 ;D327 ; GOTO not-v-blank rlm@113: 0xD3 ;D328 rlm@113: rlm@113: 0xCB ;D329 ; test bit-0 of A rlm@113: 0x47 ;D32A rlm@113: rlm@113: 0xCA ;D32B ; if bit-0 is not 1 rlm@113: 0x43 ;D32C ; GOTO not-v-blank rlm@113: 0xD3 ;D32D rlm@113: rlm@114: ;;; in v-blank mode rlm@113: rlm@113: ;; if v-blank-prev was 0, rlm@113: ;; increment frame-count rlm@113: rlm@113: 0xFA ;D32E ; load v-blank-prev to A rlm@113: 0x20 ;D32F rlm@113: 0xD3 ;D330 rlm@113: rlm@113: 0xCB ;D331 rlm@113: 0x47 ;D332 ; test bit-0 of A rlm@113: rlm@113: 0x20 ;D333 ; skip next section rlm@113: 0x07 ;D334 ; if v-blank-prev was not zero rlm@113: rlm@113: ;; v-blank was 0, increment frame-count rlm@113: 0xFA ;D335 ; load frame-count into A rlm@113: 0x1F ;D336 rlm@113: 0xD3 ;D337 rlm@113: rlm@113: 0x3C ;D338 ; inc A rlm@113: rlm@113: 0xEA ;D339 ; load A into frame-count rlm@113: 0x1F ;D33A rlm@113: 0xD3 ;D33B rlm@113: rlm@114: ;; set v-blank-prev to 1 rlm@113: 0x3E ;D33C ; load 1 into A rlm@113: 0x01 ;D33D rlm@113: rlm@113: 0xEA ;D33E ; load A into v-blank-prev rlm@113: 0x20 ;D33F rlm@113: 0xD3 ;D340 rlm@113: rlm@113: 0x18 ;D341 ; skip not-in-v-blank section rlm@113: 0x05 ;D342 rlm@113: rlm@114: ;;; not in v-blank mode rlm@114: ;; set v-blank-prev to 0 rlm@113: 0x3E ;D343 ; load 0 into A rlm@113: 0x00 ;D344 rlm@113: rlm@113: 0xEA ;D345 ; load A into v-blank-prev rlm@113: 0x20 ;D346 rlm@113: 0xD3 ;D347 rlm@113: rlm@113: rlm@113: 0xC3 ;D348 ; return to beginning rlm@113: 0x1D ;D349 rlm@113: 0xD3 ;D34A rlm@113: ]))) rlm@113: rlm@114: (defn step-count-frames [] rlm@113: (-> (read-down-button) rlm@110: (info) rlm@110: (tick) ;; skip over data section rlm@110: (info) rlm@110: (view-register "Register A" A) rlm@110: (tick) ;; load-data into A rlm@110: (view-register "Register A" A) rlm@110: (info) rlm@110: (view-memory 0xFF00) rlm@110: (tick) ;; load A into 0xFF00 rlm@110: (view-memory 0xFF00) rlm@111: (info) rlm@111: (tick) rlm@111: (info) rlm@111: (tick) rlm@111: (info) rlm@111: (tick) rlm@111: (info) rlm@111: (tick) rlm@111: (info) rlm@111: (tick) rlm@111: (info) rlm@111: (tick) rlm@111: (print-inventory))) rlm@111: rlm@114: (defn test-read-down [] rlm@112: (= (view-memory (step (step (read-buttons) [:d])) 0xD328) rlm@112: (view-memory (step (step (read-buttons))) 0xD328))) rlm@111: rlm@107: (defn trace [state] rlm@107: (loop [program-counters [] rlm@107: opcodes []] rlm@107: (let [frame-boundary? rlm@107: (com.aurellem.gb.Gb/tick)] rlm@107: (println (count opcodes)) rlm@107: (if frame-boundary? rlm@107: [program-counters opcodes] rlm@107: (recur rlm@107: (conj program-counters rlm@107: (first (registers @current-state))) rlm@107: (conj opcodes rlm@107: (aget (memory @current-state) rlm@107: (PC @current-state)))))))) rlm@107: rlm@107: (defn good-trace [] rlm@107: (-> (mid-game) (tick) (IE! 0) rlm@107: (set-inv-mem [0x00 0x00 0X00 0x00]) rlm@107: (PC! item-list-start)(print-interrupt) rlm@112: (info) (tick) (info) (tick) (info))) rlm@114: rlm@114: rlm@114: rlm@114: rlm@114: rlm@114: rlm@114: rlm@114: rlm@114: