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@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@118: (loop [program-counters [] rlm@118: opcodes []] rlm@118: (let [frame-boundary? rlm@118: (com.aurellem.gb.Gb/tick)] rlm@118: (println (count opcodes)) 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@118: 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@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@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: rlm@118: 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@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@118: (defn write-memory-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@115: 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@115: 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@115: 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: rlm@118: 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 ;D362 ; load (D351) into A rlm@118: 0x51 ;D363 --> rlm@118: 0xD3 ;D364 --> D31F rlm@118: rlm@118: 0xEA ;D365 ; load (A), which is rlm@118: 0x00 ;D366 --> ; 00001000, into FF00 rlm@118: 0xFF ;D367 --> FF00 rlm@118: rlm@118: 0x0E ;D368 rlm@118: ;D369 rlm@118: (Integer/parseInt "00001111" 2) ; "00001111" -> C rlm@118: rlm@118: 0xFA ;D36A ; (FF00) -> A rlm@118: 0x00 ;D36B rlm@118: 0xFF ;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@118: 0xC3 ;D375 ; GOTO state machine rlm@118: ;;0x1D rlm@118: 0x80 ;D376 rlm@118: 0xD3 ;D377 rlm@118: 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@118: 0x00 ;D383 ; bytes-left-to-write rlm@121: 0x00 ;D384 ; start-point rlm@118: 0x00 ;D385 ; unused rlm@118: 0x00 ;D386 ; unused 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@121: ;; GOTO Mode 2 (set-start-point) 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@118: 0x00 ;D39A rlm@118: 0x00 ;D39B rlm@118: 0x00 ;D39C rlm@118: 0x00 ;D39D rlm@118: 0x00 ;D39E rlm@118: 0x00 ;D39F rlm@118: 0x00 ;D3A0 rlm@118: 0x00 ;D3A1 rlm@118: 0x00 ;D3A2 rlm@118: 0x00 ;D3A3 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@121: ;; Mode 2 -- set start-point mode rlm@121: ;; Final 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@121: ;; Set start-point 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@121: 0xEA ;D3C2 ; load A into start-point rlm@121: 0x84 ;D3C3 ; (0xD384) 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@121: rlm@118: 0x00 ;D3CD rlm@118: 0x00 ;D3CE rlm@118: 0x00 ;D3CF rlm@118: 0x00 ;D3D0 rlm@118: 0x00 ;D3D1 rlm@118: 0x00 ;D3D2 rlm@118: 0x00 ;D3D3 rlm@118: 0x00 ;D3D4 rlm@118: 0x00 ;D3D5 rlm@118: 0x00 ;D3D6 rlm@118: rlm@121: ;; Mode 3 -- write bytes mode rlm@121: ;; This is where RAM manipulation happens. rlm@121: ;; User supplies bytes every frame, which are written rlm@121: ;; sequentially to rlm@121: rlm@118: rlm@118: rlm@118: rlm@118: 0xC3 ; ; Complete Loop rlm@118: 0x1D ; rlm@118: 0xD3 ; rlm@118: rlm@118: rlm@118: rlm@118: ]) rlm@118: rlm@118: rlm@118: (def frame-count 0xD31F) rlm@118: (def input 0xD352) rlm@118: (def current-mode 0xD382) 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@119: