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@118: (defn print-listing [state begin end] rlm@118: (dorun (map rlm@118: (fn [opcode line] rlm@118: (println (format "0x%04X: 0x%02X" line opcode))) rlm@118: (subvec (vec (memory state)) begin end) rlm@118: (range begin end))) rlm@118: state) rlm@118: 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@128: (defn B [state] rlm@128: (bit-shift-right (bit-and 0x0000FF00 (BC state)) 8)) rlm@128: rlm@128: (defn C [state] rlm@128: (bit-and 0xFF (BC state))) rlm@128: 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@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@118: (defn trace [state] rlm@124: (loop [program-counters [(first (registers @current-state)) ] rlm@124: opcodes [(aget (memory @current-state) (PC @current-state))]] rlm@118: (let [frame-boundary? rlm@118: (com.aurellem.gb.Gb/tick)] rlm@118: (if frame-boundary? rlm@118: [program-counters opcodes] rlm@118: (recur rlm@118: (conj program-counters rlm@118: (first (registers @current-state))) rlm@118: (conj opcodes rlm@118: (aget (memory @current-state) rlm@118: (PC @current-state)))))))) rlm@124: rlm@124: (defn print-trace [state n] rlm@124: (let [[program-counters opcodes] (trace state)] rlm@124: (dorun (map (fn [pc op] (println (format "%04X: 0x%02X" pc op))) rlm@124: (take n program-counters) rlm@124: (take n opcodes))))) rlm@124: rlm@118: (defn good-trace [] rlm@118: (-> (mid-game) (tick) (IE! 0) rlm@118: (set-inv-mem [0x00 0x00 0X00 0x00]) rlm@118: (PC! item-list-start)(print-interrupt) rlm@118: (info) (tick) (info) (tick) (info))) rlm@118: rlm@113: (defn read-down-button [] rlm@110: (-> (tick (mid-game)) rlm@110: (IE! 0) ; disable interrupts rlm@110: (inject-item-assembly rlm@118: ;; write 00010000 to 0xFF00 to select joypad rlm@118: [0x18 ;D31D ; jump over rlm@118: 0x01 ;D31E ; the next 8 bits rlm@118: ;D31F rlm@118: (Integer/parseInt "00100000" 2) ; data section rlm@118: rlm@118: 0xFA ;D320 ; load (D31F) into A rlm@118: 0x1F ;D321 --> rlm@118: 0xD3 ;D322 --> D31F rlm@107: rlm@118: 0xEA ;D323 ; load (A), which is rlm@118: 0x00 ;D324 --> ; 00010000, into FF00 rlm@118: 0xFF ;D325 --> FF00 rlm@118: rlm@118: 0x18 ;D326 ; this is the place where rlm@118: 0x01 ;D327 ; we will store whether rlm@118: 0x00 ;D328 ; "down" is pressed. rlm@107: rlm@118: 0xFA ;D329 ; (FF00) -> A rlm@118: 0x00 ;D32A rlm@118: 0xFF ;D32B rlm@110: rlm@118: 0xCB ;D32C ; Test whether "down" rlm@118: 0x5F ;D32D ; is pressed. rlm@111: rlm@118: 0x28 ;D32E ; if down is pressed, rlm@118: 0x03 ;D32F ; skip the next section rlm@118: ; of code. rlm@118: ;; down-is-not-pressed rlm@118: 0xC3 ;D330 rlm@118: 0x1D ;D331 ; return to beginning rlm@118: 0xD3 ;D332 rlm@118: rlm@118: ;; down-is-pressed rlm@118: 0xEA ;D334 ; write A to D328 if rlm@118: 0x28 ;D335 ; "down" was pressed rlm@118: 0xD3 ;D336 rlm@111: rlm@118: 0xC3 ;D330 rlm@118: 0x1D ;D331 ; return to beginning rlm@118: 0xD3 ;D332 rlm@118: ]))) rlm@111: rlm@118: (defn test-read-down [] rlm@118: (= (view-memory (step (step (read-down-button) [:d])) 0xD328) rlm@118: (view-memory (step (step (read-down-button))) 0xD328))) 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: [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: ;; 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@115: 0x44 ;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@115: 0x44 ;D32C ; GOTO not-v-blank rlm@113: 0xD3 ;D32D rlm@114: ;;; in v-blank mode 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@115: 0xC3 ;D341 ; return to beginning rlm@115: 0x1D ;D342 rlm@115: 0xD3 ;D343 rlm@113: rlm@114: ;;; not in v-blank mode rlm@114: ;; set v-blank-prev to 0 rlm@115: 0x3E ;D344 ; load 0 into A rlm@115: 0x00 ;D345 rlm@113: rlm@115: 0xEA ;D346 ; load A into v-blank-prev rlm@115: 0x20 ;D347 rlm@115: 0xD3 ;D348 rlm@115: rlm@115: 0xC3 ;D349 ; return to beginning rlm@115: 0x1D ;D34A rlm@115: 0xD3 ;D34B 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@115: (defn test-count-frames [] rlm@115: (= 255 (aget (memory ((apply comp (repeat 255 step)) rlm@115: (count-frames))) rlm@115: 0xD31F))) rlm@115: rlm@115: ;; specs for main bootstrap program rlm@115: ;; starts in "mode-select" mode rlm@115: ;; Each button press takes place in a single frame. rlm@115: ;; mode-select-mode takes one of the main buttons rlm@115: ;; which selects one of up to eight modes rlm@115: ;; mode 1 activated by the "A" button rlm@115: ;; the next two button presses indicates the start rlm@115: ;; memory location which to which the bootstrap rlm@115: ;; program will write. rlm@115: ;; This is done by using each of the eight buttons to rlm@115: ;; spell out an 8 bit number. The order of buttons is rlm@116: ;; [:d :u :l :r :start :select :b :a] rlm@116: ;; [:a :start :l] --> 00101001 rlm@114: rlm@115: ;; the next button press determines how many bytes are to be rlm@115: ;; written, starting at the start position. rlm@114: rlm@115: ;; then, the actual bytes are entered and are written to the rlm@115: ;; start address in sequence. rlm@114: rlm@118: (defn input-number-assembly [] rlm@118: [0x18 ;D31D ; jump over rlm@118: 0x02 ;D31E ; the next 2 bytes rlm@118: 0x00 ;D31F ; frame-count rlm@118: 0x00 ;D320 ; v-blank-prev rlm@118: rlm@118: 0xFA ;D321 rlm@118: 0x41 ;D322 ; load (FF41) into A rlm@118: 0xFF ;D323 ; this contains mode flags rlm@118: rlm@118: ;; if we're in v-blank, the bit-1 is 0 rlm@118: ;; and bit-2 is 1 Otherwise, it is not v-blank. rlm@118: 0xCB ;D324 ; test bit-1 of A rlm@118: 0x4F ;D325 rlm@114: rlm@118: 0xC2 ;D326 ; if bit-1 is not 0 rlm@118: 0x44 ;D327 ; GOTO not-v-blank rlm@118: 0xD3 ;D328 rlm@118: rlm@118: 0xCB ;D329 ; test bit-0 of A rlm@118: 0x47 ;D32A rlm@118: rlm@118: 0xCA ;D32B ; if bit-0 is not 1 rlm@118: 0x44 ;D32C ; GOTO not-v-blank rlm@118: 0xD3 ;D32D rlm@118: rlm@118: ;;; in v-blank mode rlm@118: rlm@118: ;; if v-blank-prev was 0, rlm@118: ;; increment frame-count rlm@118: rlm@118: 0xFA ;D32E ; load v-blank-prev to A rlm@118: 0x20 ;D32F rlm@118: 0xD3 ;D330 rlm@118: rlm@118: 0xCB ;D331 rlm@118: 0x47 ;D332 ; test bit-0 of A rlm@118: rlm@118: 0x20 ;D333 ; skip next section rlm@118: 0x07 ;D334 ; if v-blank-prev was not zero rlm@118: rlm@118: ;; v-blank was 0, increment frame-count rlm@118: 0xFA ;D335 ; load frame-count into A rlm@118: 0x1F ;D336 rlm@118: 0xD3 ;D337 rlm@118: rlm@118: 0x3C ;D338 ; inc A rlm@118: rlm@118: 0xEA ;D339 ; load A into frame-count rlm@118: 0x1F ;D33A rlm@118: 0xD3 ;D33B rlm@118: rlm@118: ;; set v-blank-prev to 1 rlm@118: 0x3E ;D33C ; load 1 into A rlm@118: 0x01 ;D33D rlm@118: rlm@118: 0xEA ;D33E ; load A into v-blank-prev rlm@118: 0x20 ;D33F rlm@118: 0xD3 ;D340 rlm@118: rlm@118: 0xC3 ;D341 ; GOTO input handling code rlm@118: 0x4E ;D342 rlm@118: 0xD3 ;D343 rlm@118: rlm@118: ;;; not in v-blank mode rlm@118: ;; set v-blank-prev to 0 rlm@118: 0x3E ;D344 ; load 0 into A rlm@118: 0x00 ;D345 rlm@118: rlm@118: 0xEA ;D346 ; load A into v-blank-prev rlm@118: 0x20 ;D347 rlm@118: 0xD3 ;D348 rlm@118: rlm@118: 0xC3 ;D349 ; return to beginning rlm@118: 0x1D ;D34A rlm@118: 0xD3 ;D34B rlm@118: rlm@118: 0x00 ;D34C ; these are here rlm@118: 0x00 ;D34D ; for glue rlm@118: rlm@118: rlm@118: ;;; calculate input number based on button presses rlm@118: 0x18 ;D34E ; skip next 3 bytes rlm@118: 0x03 ;D34F rlm@118: ;D350 rlm@118: (Integer/parseInt "00100000" 2) ; select directional pad rlm@118: ;D351 rlm@118: (Integer/parseInt "00010000" 2) ; select buttons rlm@118: 0x00 ;D352 ; input-number rlm@118: rlm@118: ;; select directional pad, store low bits in B rlm@118: rlm@118: 0xFA ;D353 ; load (D350) into A rlm@118: 0x50 ;D354 --> rlm@118: 0xD3 ;D355 --> D31F rlm@118: ocsenave@135: 0xEA ;D356 ; load A, which is rlm@118: 0x00 ;D357 --> ; 00010000, into FF00 rlm@118: 0xFF ;D358 --> FF00 rlm@118: rlm@118: 0x06 ;D359 rlm@118: ;D35A rlm@118: (Integer/parseInt "11110000" 2) ; "11110000" -> B rlm@118: 0xFA ;D35B ; (FF00) -> A rlm@118: 0x00 ;D35C rlm@118: 0xFF ;D35D rlm@118: rlm@118: 0xCB ;D35E ; swap nybbles on A rlm@118: 0x37 ;D35F rlm@118: 0xA0 ;D360 ; (AND A B) -> A rlm@118: 0x47 ;D361 ; A -> B rlm@118: rlm@118: ;; select buttons store bottom bits in C rlm@118: rlm@118: 0xFA ; ; load (D351) into A rlm@118: 0x51 ; --> rlm@118: 0xD3 ; --> D31F rlm@118: rlm@118: 0xEA ; ; load (A), which is rlm@118: 0x00 ; --> ; 00001000, into FF00 rlm@118: 0xFF ; --> FF00 rlm@118: rlm@118: 0x0E ; rlm@118: (Integer/parseInt "00001111" 2) ; "00001111" -> C rlm@118: rlm@118: 0xFA ; ; (FF00) -> A rlm@118: 0x00 ; rlm@118: 0xFF ; rlm@118: rlm@118: 0xA1 ; ; (AND A C) -> A rlm@118: 0x4F ; ; A -> C rlm@118: rlm@118: ;; combine the B and C registers into the input number rlm@118: 0x79 ; ; C -> A rlm@118: 0xB0 ; ; (OR A B) -> A rlm@118: 0x2F ; ; negate A rlm@118: rlm@118: 0xEA ; ; store A into input-number rlm@118: 0x52 ; rlm@118: 0xD3 ; rlm@118: rlm@118: 0xC3 ; ; return to beginning rlm@118: 0x1D ; rlm@118: 0xD3 ; rlm@118: ]) rlm@114: rlm@128: rlm@128: (defn print-pc [state] rlm@128: (println (format "PC: 0x%04X" (PC state))) rlm@128: state) rlm@128: rlm@128: (defn print-op [state] rlm@128: (println (format "OP: 0x%02X" (aget (memory state) (PC state)))) rlm@128: state) rlm@128: rlm@128: (defn d-tick rlm@128: ([state] rlm@128: (-> state print-pc print-op tick))) rlm@128: rlm@115: (defn input-number [] rlm@115: (-> (tick (mid-game)) rlm@115: (IE! 0) ; disable interrupts rlm@118: (inject-item-assembly (input-number-assembly)))) rlm@115: rlm@117: (defn test-input-number rlm@117: "Input freestyle buttons and observe the effects at the repl." rlm@117: [] rlm@117: (set-state! (input-number)) rlm@117: (dotimes [_ 90000] (step (view-memory @current-state 0xD352)))) rlm@115: rlm@128: rlm@128: rlm@128: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@136: (defn write-memory-assembly* ocsenave@136: "Currently, grabs input from the user each frame." ocsenave@136: [] rlm@128: [ ocsenave@135: ;; --------- FRAME METRONOME ocsenave@136: 0x00 ;; v-blank-prev D31D rlm@128: rlm@128: 0xFA ;; load modes into A rlm@128: 0x41 rlm@128: 0xFF ocsenave@135: ocsenave@135: 0x47 ;; A -> B ocsenave@135: 0xCB ;; rotate A rlm@128: 0x2F ocsenave@135: 0x2F ;; invert A rlm@128: rlm@128: 0xA0 ocsenave@135: 0x47 ;; now B_0 contains (VB==1) rlm@128: rlm@128: 0xFA ;; load v-blank-prev ocsenave@136: 0x1D rlm@128: 0xD3 rlm@128: ocsenave@135: 0x2F ;; complement v-blank-prev ocsenave@135: ocsenave@135: 0xA0 ;; A & B --> A ocsenave@135: 0x4F ;; now C_0 contains increment? ocsenave@135: rlm@128: rlm@128: 0x78 ;; B->A rlm@128: rlm@128: 0xEA ;; spit A --> vbprev ocsenave@136: 0x1D rlm@128: 0xD3 rlm@128: ocsenave@136: 0xCB ;test C_0 ocsenave@136: 0x41 ocsenave@136: 0x20 ; JUMP ahead to button input if nonzero ocsenave@136: 0x03 ocsenave@136: 0x18 ; JUMP back to beginning ocsenave@136: 0xE7 ocsenave@135: ocsenave@135: ;; -------- GET BUTTON INPUT ocsenave@136: 0x00 ;; var: which-input D336 ocsenave@135: ;; prepare to select bits ocsenave@135: ocsenave@135: 0x01 ;; load 0x0000 into BC ocsenave@135: 0x00 ocsenave@135: 0x00 ocsenave@135: ocsenave@135: 0x3E ;; load 0x20 into A ocsenave@135: 0x20 ocsenave@135: ocsenave@135: ocsenave@136: 0xEA ;; load A into [FF00] ;; D33D ocsenave@135: 0x00 ocsenave@135: 0xFF ocsenave@135: ocsenave@135: 0xFA ;; load A from [FF00] ocsenave@135: 0x00 ocsenave@135: 0xFF ocsenave@135: ocsenave@135: 0xE6 ;; bitmask 00001111 ocsenave@135: 0x0F ocsenave@135: ocsenave@135: 0xB0 ;; A or B --> A ocsenave@135: ocsenave@135: 0xCB ocsenave@135: 0x41 ;; test bit 0 of C ocsenave@136: 0x20 ;; JUMP forward if 1 ocsenave@135: 0x08 ocsenave@135: ocsenave@135: 0x47 ;; A -> B ocsenave@135: 0xCB ;; swap B nybbles ocsenave@135: 0x30 ocsenave@135: 0x0C ;; increment C ocsenave@135: 0x3E ;; load 0x10 into A ocsenave@135: 0x10 ocsenave@135: 0x18 ;; JUMP back to "load A into [FF00]" [20 steps?] ocsenave@135: 0xEB ocsenave@135: ocsenave@135: ;; now A contains the pressed keys ocsenave@136: 0xEA ;; copy keys to input-number [D336] ocsenave@135: 0x39 ocsenave@135: 0xD3 ocsenave@136: 0x18 ;;JUMP back to "beginning" [D31E] ocsenave@136: 0xC7 ocsenave@135: ocsenave@135: rlm@128: ] rlm@128: ) rlm@128: rlm@128: (defn write-mem-dyl [] rlm@128: (-> (tick (mid-game)) rlm@128: (IE! 0) rlm@128: (inject-item-assembly (write-memory-assembly*)))) rlm@131: rlm@131: ocsenave@135: (defn dylan [] ocsenave@135: (-> ocsenave@135: (write-mem-dyl) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@136: (tick) ;; first loop ocsenave@136: ocsenave@136: ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@136: (tick) ;; dpad bits ocsenave@136: ocsenave@135: (tick) ocsenave@135: (tick) ocsenave@136: (tick) ocsenave@136: (tick) ocsenave@136: (tick) ocsenave@136: (tick) ocsenave@136: (tick) ocsenave@136: (tick) ocsenave@136: (d-tick) ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: (view-register "A" A) ocsenave@135: (view-register "B" B) ocsenave@135: (view-register "C" C) ocsenave@135: ocsenave@135: )) ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: rlm@131: (defn d2 [] rlm@131: (-> rlm@131: (write-mem-dyl) rlm@131: (view-memory 0xD31F) rlm@131: step step step step step rlm@131: (view-memory 0xD31F))) rlm@131: rlm@131: rlm@131: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: ocsenave@135: rlm@131: rlm@131: rlm@128: rlm@128: rlm@118: (defn write-memory-assembly [] rlm@129: [ rlm@123: ;; Main Timing Loop rlm@123: ;; Constantly check for v-blank and Trigger main state machine on rlm@123: ;; every transtion from v-blank to non-v-blank. rlm@129: rlm@123: 0x18 ; D31D ; Variable declaration rlm@123: 0x02 ; D31E rlm@123: 0x00 ; D31F ; frame-count rlm@123: 0x00 ; D320 ; v-blank-prev rlm@129: rlm@129: 0xF0 ; D321 ; load v-blank mode flags into A rlm@129: 0x41 rlm@129: 0x00 rlm@123: rlm@123: rlm@123: ;; Branch dependent on v-blank. v-blank happens when the last two rlm@123: ;; bits in A are "01" rlm@123: 0xCB ; D324 rlm@123: 0x4F ; D325 rlm@123: rlm@123: 0xC2 ; D326 ; if bit-1 is not 0, then rlm@124: 0x3E ; D327 ; GOTO non-v-blank. rlm@123: 0xD3 ; D328 rlm@123: rlm@123: 0xCB ; D329 rlm@123: 0x47 ; D32A rlm@123: rlm@123: 0xCA ; D32B ; if bit-0 is not 1, then rlm@124: 0x3E ; D32C ; GOTO non-v-blank. rlm@123: 0xD3 ; D32D rlm@123: rlm@123: ;; V-Blank rlm@123: ;; Activate state-machine if this is a transition event. rlm@123: rlm@123: 0xFA ; D32E ; load v-bank-prev into A rlm@123: 0x20 ; D32F rlm@123: 0xD3 ; D330 rlm@123: rlm@124: 0xFE ; D331 ; compare A to 0. >--------\ rlm@124: 0x00 ; D332 \ rlm@124: ; | rlm@124: ;; set v-blank-prev to 1. | rlm@124: 0x3E ; D333 ; load 1 into A. | rlm@124: 0x01 ; D334 | rlm@124: ; | rlm@124: 0xEA ; D335 ; load A into v-blank-prev | rlm@124: 0x20 ; D336 | rlm@124: 0xD3 ; D337 | rlm@124: ; / rlm@124: ;; if v-blank-prev was 0, activate state-machine <------/ rlm@124: 0xCA ; D338 ; if v-blank-prev rlm@123: 0x46 ; D339 ; was 0, rlm@123: 0xD3 ; D33A ; GOTO state-machine rlm@123: rlm@123: 0xC3 ; D33B rlm@123: 0x1D ; D33C rlm@123: 0xD3 ; D33D ; GOTO beginning rlm@123: ;; END V-blank rlm@123: rlm@123: ;; Non-V-Blank rlm@123: ;; Set v-blank-prev to 0 rlm@123: 0x3E ; D33E ; load 0 into A rlm@123: 0x00 ; D33F rlm@123: rlm@123: 0xEA ; D340 ; load A into v-blank-prev rlm@123: 0x20 ; D341 rlm@123: 0xD3 ; D342 rlm@118: rlm@123: 0xC3 ; D343 rlm@123: 0x1D ; D344 rlm@123: 0xD3 ; D345 ; GOTO beginning rlm@123: ;; END Not-V-Blank rlm@115: rlm@124: rlm@123: ;; Main State Machine -- Input Section rlm@123: ;; This is called once every frame. rlm@123: ;; It collects input and uses it to drive the rlm@123: ;; state transitions. rlm@123: rlm@123: ;; Increment frame-count rlm@123: 0xFA ; D346 ; load frame-count into A rlm@123: 0x1F ; D347 rlm@123: 0xD3 ; D348 rlm@118: rlm@123: 0x3C ; D349 ; inc A rlm@115: rlm@123: 0xEA ; D34A rlm@123: 0x1F ; D34B ; load A into frame-count rlm@123: 0xD3 ; D34C rlm@118: rlm@123: 0x00 ; D34D ; glue :) rlm@118: rlm@118: 0x18 ;D34E ; skip next 3 bytes rlm@118: 0x03 ;D34F rlm@123: ;D350 rlm@118: (Integer/parseInt "00100000" 2) ; select directional pad rlm@123: ;D351 rlm@118: (Integer/parseInt "00010000" 2) ; select buttons rlm@118: 0x00 ;D352 ; input-number rlm@118: rlm@123: ;; select directional pad; store low bits in B rlm@118: rlm@118: 0xFA ;D353 ; load (D350) into A rlm@118: 0x50 ;D354 --> rlm@123: 0xD3 ;D355 --> D350 rlm@118: rlm@129: 0xE0 ;D356 ; load (A), which is rlm@118: 0x00 ;D357 --> ; 00010000, into FF00 rlm@129: 0x00 ;D358 --> FF00 ;; NO-OP rlm@118: rlm@118: 0x06 ;D359 rlm@123: ;D35A rlm@118: (Integer/parseInt "11110000" 2) ; "11110000" -> B rlm@129: 0xF0 ;D35B ; (FF00) -> A rlm@118: 0x00 ;D35C rlm@129: 0x00 ;D35D ;; NO-OP rlm@118: rlm@118: 0xCB ;D35E ; swap nybbles on A rlm@118: 0x37 ;D35F rlm@118: 0xA0 ;D360 ; (AND A B) -> A rlm@118: 0x47 ;D361 ; A -> B rlm@118: rlm@123: ;; select buttons; store bottom bits in C rlm@118: rlm@118: 0xFA ;D362 ; load (D351) into A rlm@118: 0x51 ;D363 --> rlm@123: 0xD3 ;D364 --> D351 rlm@118: rlm@129: 0xE0 ;D365 ; load (A), which is rlm@118: 0x00 ;D366 --> ; 00001000, into FF00 rlm@129: 0x00 ;D367 --> FF00 ;; NO-OP rlm@118: rlm@118: 0x0E ;D368 rlm@118: ;D369 rlm@118: (Integer/parseInt "00001111" 2) ; "00001111" -> C rlm@118: rlm@129: 0xF0 ;D36A ; (FF00) -> A rlm@118: 0x00 ;D36B rlm@129: 0x00 ;D36C rlm@118: rlm@118: 0xA1 ;D36D ; (AND A C) -> A rlm@118: 0x4F ;D36E ; A -> C rlm@118: rlm@118: ;; combine the B and C registers into the input number rlm@118: 0x79 ;D36F ; C -> A rlm@118: 0xB0 ;D370 ; (OR A B) -> A rlm@118: 0x2F ;D371 ; negate A rlm@118: rlm@118: 0xEA ;D372 ; store A into input-number rlm@118: 0x52 ;D373 rlm@118: 0xD3 ;D374 rlm@118: rlm@124: 0x00 ;D375 rlm@124: 0x00 ;D376 rlm@124: 0x00 ;D377 rlm@118: 0x00 ;D378 rlm@118: 0x00 ;D379 rlm@118: 0x00 ;D37A rlm@118: 0x00 ;D37B ; these are here because rlm@118: 0x00 ;D37C ; I messed up :( rlm@118: 0x00 ;D37D rlm@118: 0x00 ;D37E rlm@118: 0x00 ;D37F rlm@118: rlm@118: ;; beginning of main state machine rlm@118: 0x18 ;D380 ; Declaration of variables rlm@118: 0x05 ;D381 ; 5 variables: rlm@118: 0x00 ;D382 ; current-mode rlm@122: 0x00 ;D383 ; bytes-to-write rlm@126: 0x00 ;D384 ; bytes-written rlm@126: 0x00 ;D385 ; start-point-high rlm@126: 0x00 ;D386 ; start-point-low rlm@118: rlm@118: rlm@118: ;; banch on current mode rlm@118: 0xFA ;D387 ; load current-mode (0xD382) rlm@118: 0x82 ;D388 ; into A rlm@118: 0xD3 ;D389 rlm@118: 0x00 ;D38A rlm@118: rlm@120: rlm@120: ;; GOTO Mode 0 (input-mode) if current-mode is 0 rlm@118: 0xFE ;D38B rlm@118: 0x00 ;D38C ; compare A with 0x00 rlm@118: rlm@120: 0xCA ;D38D ; goto Mode 0 if A == 0 rlm@118: 0xA8 ;D38E rlm@118: 0xD3 ;D38F rlm@118: rlm@121: ;; GOTO Mode 1 (set-length) if current-mode is 1 rlm@120: 0xFE ;D390 rlm@120: 0x01 ;D391 ; compare A with 0x01 rlm@120: rlm@120: 0xCA ;D392 rlm@120: 0xB1 ;D393 rlm@120: 0xD3 ;D394 ; goto Mode 1 if A == 1 rlm@120: rlm@126: ;; GOTO Mode 2 (set-start-point-high) if current mode is 2 rlm@121: 0xFE ;D395 rlm@121: 0x02 ;D396 ; compare A with 0x02 rlm@121: rlm@121: 0xCA ;D397 rlm@121: 0xBF ;D398 rlm@121: 0xD3 ;D399 ; goto Mode 2 if A == 2 rlm@121: rlm@126: ;; GOTO Mode 3 (set-start-point-low) if current mode is 3 rlm@125: 0xFE ;D39A rlm@125: 0x03 ;D39B rlm@125: rlm@126: 0xCA ;D39C rlm@126: 0xCD ;D39D rlm@126: 0xD3 ;D39E ; goto Mode 3 if A == 3 rlm@126: rlm@127: ;; GOTO Mode 4 (write-memory) if current mode is 4 rlm@127: 0xFE ;D39F rlm@127: 0x04 ;D3A0 rlm@127: rlm@127: 0xCA ;D3A1 rlm@127: 0xDB ;D3A2 rlm@127: 0xD3 ;D3A3 rlm@127: rlm@118: 0x00 ;D3A4 rlm@118: ;; End of Mode checking, goto beginning rlm@118: 0xC3 ;D3A5 rlm@118: 0x1D ;D3A6 rlm@118: 0xD3 ;D3A7 rlm@120: rlm@120: rlm@120: ;; Mode 0 -- input-mode mode rlm@120: ;; means that we are waiting for a mode, so set the mode to rlm@120: ;; whatever is currently in input-number. If nothing is rlm@120: ;; entered, then the program stays in input-mode mode rlm@120: rlm@118: ;; set current-mode to input-number rlm@118: 0xFA ;D3A8 ; load input-number (0xD352) rlm@118: 0x52 ;D3A9 ; into A rlm@118: 0xD3 ;D3AA rlm@118: rlm@118: 0xEA ;D3AB ; load A into current-mode rlm@118: 0x82 ;D3AC ; (0xD382) rlm@118: 0xD3 ;D3AD rlm@118: rlm@118: 0xC3 ;D3AE ; go back to beginning rlm@118: 0x1D ;D3AF rlm@118: 0xD3 ;D3B0 rlm@120: ;; End Mode 0 rlm@118: rlm@120: rlm@121: ;; Mode 1 -- set-length mode rlm@121: ;; This is the header for writing things to memory. rlm@121: ;; User specifies the number of bytes to write. rlm@120: ;; Mode is auto advanced to Mode 2 after this mode rlm@120: ;; completes. rlm@120: rlm@121: ;; Set bytes left to write to input-number; rlm@120: ;; set current-mode to 0x02. rlm@120: 0xFA ;D3B1 ; load input-number (0xD352) rlm@120: 0x52 ;D3B2 ; into A rlm@120: 0xD3 ;D3B3 rlm@120: rlm@120: 0xEA ;D3B4 ; load A into bytes-left-to-write rlm@120: 0x83 ;D3B5 ; (0xD383) rlm@120: 0xD3 ;D3B6 rlm@120: rlm@121: 0x3E ;D3B7 ; load 0x02 into A. rlm@120: 0x02 ;D3B8 rlm@120: rlm@120: 0xEA ;D3B9 ; load A to current-mode rlm@120: 0x82 ;D3BA ; advancing from Mode 1 to rlm@120: 0xD3 ;D3BB ; Mode 2 rlm@120: rlm@120: 0xC3 ;D3BC ; go back to beginning rlm@120: 0x1D ;D3BD rlm@120: 0xD3 ;D3BE rlm@120: ;; End Mode 1 rlm@121: rlm@121: rlm@126: ;; Mode 2 -- set start-point-high mode rlm@126: ;; Middle part of the header for writing things to memory. rlm@121: ;; User specifies the start location in RAM to which rlm@121: ;; data will be written. rlm@121: ;; Mode is auto advanced to Mode 3 after this mode completes. rlm@121: rlm@126: ;; Set start-point-high to input-number; rlm@121: ;; set current mode to 0x03. rlm@121: 0xFA ;D3BF ; load input-number (0xD352) rlm@121: 0x52 ;D3C0 ; into A rlm@121: 0xD3 ;D3C1 rlm@121: rlm@126: 0xEA ;D3C2 ; load A into start-point-high rlm@126: 0x85 ;D3C3 ; (0xD385) rlm@121: 0xD3 ;D3C4 rlm@121: rlm@121: 0x3E ;D3C5 ; load 0x03 into A. rlm@121: 0x03 ;D3C6 rlm@121: rlm@121: 0xEA ;D3C7 ; load A to current-mode, rlm@121: 0x82 ;D3C8 ; advancing from Mode 2 to rlm@121: 0xD3 ;D3C9 ; Mode 3. rlm@120: rlm@121: 0xC3 ;D3CA ; go back to beginning rlm@121: 0x1D ;D3CB rlm@121: 0xD3 ;D3CC rlm@121: ;;End Mode 2 rlm@125: rlm@118: rlm@126: ;; Mode 3 -- set-start-point-low mode rlm@126: ;; Final part of header for writing things to memory. rlm@126: ;; User specifies the low bytes of 16 bit start-point. rlm@126: rlm@126: ;; Set start-point-low to input-number; rlm@126: ;; set current mode to 0x04 rlm@126: 0xFA ;D3CD ; load input-number into A rlm@126: 0x52 ;D3CE rlm@126: 0xD3 ;D3CF rlm@126: rlm@126: 0xEA ;D3D0 ; load A into start-point-low rlm@126: 0x86 ;D3D1 rlm@126: 0xD3 ;D3D2 rlm@126: rlm@126: 0x3E ;D3D3 ; load 0x04 into A. rlm@126: 0x04 ;D3D4 rlm@126: rlm@126: 0xEA ;D3D5 ; load A to current-mode, rlm@126: 0x82 ;D3D6 ; advancing from Mode 3 to rlm@126: 0xD3 ;D3D7 ; Mode 4. rlm@126: rlm@126: 0xC3 ;D3D8 ; go back to beginning rlm@126: 0x1D ;D3D9 rlm@126: 0xD3 ;D3DA rlm@126: rlm@126: ;; Mode 4 -- write bytes mode rlm@125: rlm@125: ;; This is where RAM manipulation happens. User supplies rlm@125: ;; bytes every frame, which are written sequentially to rlm@125: ;; start-point until bytes-to-write have been written. Once rlm@125: ;; bytes-to-write have been written, the mode is reset to 0. rlm@125: rlm@125: ;; compare bytes-written with bytes-to-write. rlm@125: ;; if they are the same, then reset mode to 0 rlm@125: rlm@126: 0xFA ;D3DB ; load bytes-to-write into A rlm@126: 0x83 ;D3DC rlm@126: 0xD3 ;D3DD rlm@127: rlm@126: 0x47 ;D3DE ; load A into B rlm@127: rlm@126: 0xFA ;D3DF ; load bytes-written into A rlm@126: 0x84 ;D3E0 rlm@126: 0xD3 ;D3E1 rlm@127: rlm@126: 0xB8 ;D3E2 ; compare A with B rlm@127: rlm@126: 0xCA ;D3E3 ; if they are equal, go to cleanup rlm@127: 0x07 ;D3E4 rlm@127: 0xD4 ;D3E5 rlm@125: rlm@125: ;; Write Memory Section rlm@125: ;; Write the input-number, interpreted as an 8-bit number, rlm@125: ;; into the current target register, determined by rlm@125: ;; (+ start-point bytes-written). rlm@125: ;; Then, increment bytes-written by 1. rlm@125: rlm@127: 0xFA ;D3E6 ; load start-point-high into A rlm@127: 0x85 ;D3E7 rlm@127: 0xD3 ;D3E8 rlm@127: rlm@127: 0x67 ;D3E9 ; load A into H rlm@127: rlm@127: 0xFA ;D3EA ; load start-point-low into A rlm@127: 0x86 ;D3EB rlm@127: 0xD3 ;D3EC rlm@127: rlm@127: 0x6F ;D3ED ; load A into L rlm@127: rlm@127: 0xFA ;D3EE ; load bytes-written into A rlm@127: 0x84 ;D3EF rlm@127: 0xD3 ;D3F0 rlm@127: rlm@127: 0x00 ;D3F1 ; These are here because rlm@127: 0x00 ;D3F2 ; I screwed up again. rlm@125: 0x00 ;D3F3 rlm@125: rlm@127: 0x85 ;D3F4 ; add L to A; store A in L. rlm@127: 0x6F ;D3F5 rlm@121: rlm@127: 0x30 ;D3F6 ; If the addition overflowed, rlm@127: 0x01 ;D3F7 rlm@127: 0x24 ;D3F8 ; increment H. rlm@118: rlm@127: ;; Now, HL points to the correct place in memory rlm@127: rlm@127: 0xFA ;D3F9 ; load input-number into A rlm@127: 0x52 ;D3FA rlm@127: 0xD3 ;D3FB rlm@118: rlm@127: 0x77 ;D3FC ; load A into (HL) rlm@118: rlm@127: 0xFA ;D3FD ; load bytes-written into A rlm@127: 0x84 ;D3FE rlm@127: 0xD3 ;D3FF rlm@118: rlm@127: 0x3C ;D400 ; increment A rlm@127: rlm@127: 0xEA ;D401 ; load A into bytes-written rlm@127: 0x84 ;D402 rlm@127: 0xD3 ;D403 rlm@127: rlm@127: 0xC3 ;D404 ; go back to beginning. rlm@127: 0x1D ;D405 rlm@127: 0xD3 ;D406 rlm@127: ;; End Write Memory Section rlm@127: rlm@127: ;; Mode 4 Cleanup Section rlm@127: ;; reset bytes-written to 0 rlm@127: ;; set mode to 0 rlm@127: 0x3E ;D407 ; load 0 into A rlm@127: 0x00 ;D408 rlm@127: rlm@127: 0xEA ;D409 ; load A into bytes-written rlm@127: 0x84 ;D40A rlm@127: 0xD3 ;D40B rlm@127: rlm@127: 0xEA ;D40C ; load A into current-mode rlm@127: 0x82 ;D40D rlm@127: 0xD3 ;D40E rlm@127: rlm@127: 0xC3 ;D40F ; go back to beginning rlm@127: 0x1D ;D410 rlm@127: 0xD3 ;D411 rlm@127: rlm@127: ;; End Mode 4 rlm@118: rlm@118: ]) rlm@118: rlm@118: rlm@123: rlm@118: (def frame-count 0xD31F) rlm@118: (def input 0xD352) rlm@127: (def current-mode 0xD382) rlm@127: (def bytes-to-write 0xD383) rlm@127: (def bytes-written 0xD384) rlm@127: (def start-point-high 0xD385) rlm@127: (def start-point-low 0xD386) rlm@126: rlm@127: rlm@118: rlm@118: (defn write-memory [] rlm@118: (-> (tick (mid-game)) rlm@118: (IE! 0) ; disable interrupts rlm@118: (inject-item-assembly (write-memory-assembly)))) rlm@119: rlm@119: (defn test-write-memory [] rlm@119: (set-state! (write-memory)) rlm@119: (dorun rlm@119: (dotimes [_ 5000] rlm@119: (view-memory (step @current-state) current-mode)))) rlm@122: rlm@122: (def bytes-to-write 0xD383) rlm@122: (def start-point 0xD384) rlm@122: rlm@125: (defn print-blank-assembly rlm@125: [start end] rlm@125: (dorun rlm@125: (map rlm@125: #(println (format "0x00 ;%04X " %)) rlm@125: (range start end)))) rlm@125: rlm@122: (defn test-mode-2 [] rlm@122: (-> rlm@122: (write-memory) rlm@123: (view-memory frame-count) rlm@122: (step) rlm@122: (step [:a]) rlm@122: (step [:b]) rlm@122: (step [:start]) rlm@122: (step []) rlm@123: (view-memory frame-count))) rlm@123: rlm@128: (defn test-mode-4 rlm@128: ([] (test-mode-4 (write-memory))) rlm@128: ([target-state] rlm@128: (-> rlm@128: target-state rlm@128: (#(do (println "memory from 0xC00F to 0xC01F:" rlm@128: (subvec (vec (memory %)) 0xC00F 0xC01F)) %)) rlm@128: (view-memory current-mode) rlm@128: (step []) rlm@128: (step []) rlm@128: (step []) rlm@128: (#(do (println "after three steps") %)) rlm@128: (view-memory current-mode) rlm@127: rlm@128: ;; Activate memory writing mode rlm@128: rlm@128: (#(do (println "step with [:a]") %)) rlm@128: (step [:a]) rlm@128: (view-memory current-mode) rlm@128: (view-memory bytes-to-write) rlm@128: (view-memory start-point-high) rlm@128: (view-memory start-point-low) rlm@127: rlm@128: ;; Specify four bytes to be written rlm@128: rlm@128: (#(do (println "step with [:select]")%)) rlm@128: (step [:select]) rlm@128: (view-memory current-mode) rlm@128: (view-memory bytes-to-write) rlm@128: (view-memory start-point-high) rlm@128: (view-memory start-point-low) rlm@127: rlm@128: ;; Specify target memory address as 0xC00F rlm@128: rlm@128: (#(do (println "step with [:u :d]")%)) rlm@128: (step [:u :d]) rlm@128: (view-memory current-mode) rlm@128: (view-memory bytes-to-write) rlm@128: (view-memory start-point-high) rlm@128: (view-memory start-point-low) rlm@127: rlm@128: (#(do (println "step with [:a :b :start :select]")%)) rlm@128: (step [:a :b :start :select]) rlm@128: (view-memory current-mode) rlm@128: (view-memory bytes-to-write) rlm@128: (view-memory start-point-high) rlm@128: (view-memory start-point-low) rlm@127: rlm@128: ;; Start reprogramming memory rlm@127: rlm@128: (#(do (println "step with [:a]")%)) rlm@128: (step [:a]) rlm@128: (view-memory current-mode) rlm@128: (view-memory bytes-written) rlm@127: rlm@128: (#(do (println "step with [:b]")%)) rlm@128: (step [:b]) rlm@128: (view-memory current-mode) rlm@128: (view-memory bytes-written) rlm@127: rlm@128: (#(do (println "step with [:a :b]")%)) rlm@128: (step [:a :b]) rlm@128: (view-memory current-mode) rlm@128: (view-memory bytes-written) rlm@127: rlm@128: (#(do (println "step with [:select]")%)) rlm@128: (step [:select]) rlm@128: (view-memory current-mode) rlm@128: (view-memory bytes-written) rlm@127: rlm@128: ;; Reprogramming done, program ready for more commands. rlm@127: rlm@128: (#(do (println "step with []")%)) rlm@128: (step []) rlm@128: (view-memory current-mode) rlm@128: (view-memory bytes-written) rlm@128: rlm@128: (#(do (println "memory from 0xC00F to 0xC01F:" rlm@128: (subvec (vec (memory %)) 0xC00F 0xC01F)) %))))) rlm@123: