rlm@239: (ns com.aurellem.gb.dylan-assembly rlm@239: "A much more compact version of write-memory-assembly" rlm@239: {:author "Dylan Holmes"} rlm@377: (:use (com.aurellem.gb gb-driver assembly util vbm)) rlm@239: (:import [com.aurellem.gb.gb_driver SaveState])) rlm@239: rlm@376: ;; Specs for main bootstrap program rlm@376: rlm@376: ;; Number-Input rlm@376: ;; Number input works using all eight buttons to rlm@376: ;; spell out an 8 bit number. The order of buttons is rlm@376: ;; [:d :u :l :r :start :select :b :a] --> 11111111 rlm@376: ;; [ :l :start :a] --> 00101001 rlm@376: rlm@376: ;;; MODE-SELECT rlm@376: ;; The bootstrap program starts in MODE-SELECT mode. rlm@376: ;; MODE-SELECT transitions to one of three modes depending rlm@376: ;; on which buttons are pressed: rlm@376: ;; 0 (no-buttons) : MODE-SELECT rlm@376: ;; 8 [:start] : WRITE-BYTES rlm@376: ;; 0xFF (all-buttons) : JUMP rlm@376: rlm@376: rlm@376: ;;; WRITE-BYTES rlm@376: rlm@376: ;; WRITE-BYTES mode writes sequences of arbitray values to rlm@376: ;; arbitray memory locations. It expects you to enter a rlm@376: ;; header of three bytes describing what to write: rlm@376: rlm@376: ;; Byte 0 : Number of Bytes to Write rlm@376: ;; Byte 1 : Start Address High Byte rlm@376: ;; Byte 1 : Start Address Low Byte rlm@376: rlm@376: ;; Then, you enter the number of bytes specified in Byte 0 rlm@376: ;; they are written to the start address in rlm@376: ;; sequence. After the last byte is written control rlm@376: ;; returns to MODE-SELECT mode. rlm@376: rlm@376: ;; Example: to write the sequence [1 2 3 4] starting at rlm@376: ;; address 0xC01F enter rlm@376: ;; Byte 0 : 4 (will write four bytes) rlm@376: ;; Byte 1 : 0xC0 (high byte of 0xC01F) rlm@376: ;; Byte 2 : 0x1F (low byte of 0xC01F) rlm@376: ;; Byte 3 : 1 (write 1 to 0xC01F) rlm@376: ;; Byte 4 : 2 (write 2 to 0xC020) rlm@376: ;; Byte 5 : 3 (write 3 to 0xC021) rlm@376: ;; Byte 6 : 4 (write 4 to 0xC022) rlm@376: rlm@376: ;;; JUMP rlm@376: ;; JUMP mode jumps program control to any arbitray rlm@376: ;; location. It expects you to enter two bytes which rlm@376: ;; correspond to the high and low bytes of the memory rlm@376: ;; address to which you want to jump. rlm@376: ;; Byte 0 : Jump Address High Byte rlm@376: ;; Byte 1 : Jump Address Low Byte rlm@376: rlm@376: ;; Example: to jump to address 0x1234 enter rlm@376: ;; Byte 0 : 0x12 (high byte of 0x1234) rlm@376: ;; Byte 1 : 0x34 (low byte of 0x1234) rlm@376: rlm@376: rlm@239: (defn write-memory-assembly-compact rlm@239: "Currently, grabs input from the user each frame." rlm@239: [] rlm@239: [ rlm@239: ;; --------- FRAME METRONOME rlm@239: 0x18 ;; jump ahead to cleanup. first time only. rlm@239: 0x40 ;; v-blank-prev [D31E] rlm@239: rlm@239: 0xFA ;; load modes into A [D31F] rlm@239: 0x41 rlm@239: 0xFF rlm@239: rlm@239: 0x47 ;; A -> B rlm@239: 0xCB ;; rotate A rlm@239: 0x2F rlm@239: 0x2F ;; invert A rlm@239: rlm@239: 0xA0 rlm@239: 0x47 ;; now B_0 contains (VB==1) rlm@239: rlm@239: 0xFA ;; load v-blank-prev rlm@239: 0x1E rlm@239: 0xD3 rlm@239: rlm@239: 0x2F ;; complement v-blank-prev rlm@239: rlm@239: 0xA0 ;; A & B --> A rlm@239: 0x4F ;; now C_0 contains increment? rlm@239: rlm@239: rlm@239: 0x78 ;; B->A rlm@239: 0xEA ;; spit A --> vbprev rlm@239: 0x1E rlm@239: 0xD3 rlm@239: rlm@239: 0xCB ;test C_0 rlm@239: 0x41 rlm@239: 0x20 ; JUMP ahead to button input if nonzero rlm@239: 0x02 rlm@239: 0x18 ; JUMP back to frame metronome (D31F) rlm@239: 0xE7 rlm@239: rlm@239: ;; -------- GET BUTTON INPUT rlm@239: rlm@239: ;; btw, C_0 is now 1 rlm@239: ;; prepare to select bits rlm@239: rlm@239: 0x06 ;; load 0x00 into B rlm@239: 0x00 ;; to initialize for "OR" loop rlm@239: rlm@239: 0x3E ;; load 0x20 into A, to measure dpad rlm@239: 0x20 rlm@239: rlm@239: rlm@239: 0xE0 ;; load A into [FF00] ;; start of OR loop [D33C] rlm@239: 0x00 rlm@239: rlm@239: 0xF0 ;; load A from [FF00] rlm@239: 0x00 rlm@239: rlm@239: 0xE6 ;; bitmask 00001111 rlm@239: 0x0F rlm@239: rlm@239: 0xB0 ;; A or B --> A rlm@239: 0xCB rlm@239: 0x41 ;; test bit 0 of C rlm@239: 0x28 ;; JUMP forward if 0 rlm@239: 0x08 rlm@239: rlm@239: 0x47 ;; A -> B rlm@239: 0xCB ;; swap B nybbles rlm@239: 0x30 rlm@239: 0x0C ;; increment C rlm@239: 0x3E ;; load 0x10 into A, to measure btns rlm@239: 0x10 rlm@239: 0x18 ;; JUMP back to "load A into [FF00]" [20 steps?] rlm@239: 0xED rlm@239: rlm@239: rlm@239: ;; ------ TAKE ACTION BASED ON USER INPUT rlm@239: rlm@239: ;; "input mode" rlm@239: ;; mode 0x00 : select mode rlm@239: ;; mode 0x08 : select bytes-to-write rlm@239: ;; mode 0x10 : select hi-bit rlm@239: ;; mode 0x18 : select lo-bit rlm@239: rlm@239: ;; "output mode" rlm@239: ;; mode 0x20 : write bytes rlm@239: ;; mode 0xFF : jump PC rlm@239: rlm@239: rlm@239: ;; registers rlm@239: ;; D : mode select rlm@239: ;; E : count of bytes to write rlm@239: ;; H : address-high rlm@239: ;; L : address-low rlm@239: rlm@239: ;; now A contains the pressed keys rlm@239: 0x2F ; complement A, by request. [D34F] rlm@239: rlm@239: 0x47 ; A->B ;; now B contains the pressed keys rlm@239: 0x7B ; E->A ;; now A contains the count. rlm@239: rlm@239: 0xCB ; test bit 5 of D (are we in o/p mode?) rlm@239: 0x6A rlm@239: 0x28 ; if test == 0, skip this o/p section rlm@239: 0x13 ; JUMP rlm@239: rlm@239: 0xCB ; else, test bit 0 of D (fragile; are we in pc mode?) rlm@239: 0x42 rlm@239: 0x28 ; if test == 0, skip the following command rlm@239: 0x01 rlm@239: rlm@239: ;; output mode I: moving the program counter rlm@239: 0xE9 ; ** move PC to (HL) rlm@239: rlm@239: ;; output mode II: writing bytes rlm@239: 0xFE ; A compare 0. finished writing? rlm@239: 0x00 rlm@239: 0x20 ; if we are not finished, skip cleanup rlm@239: 0x04 ; JUMP rlm@239: rlm@239: ;; CLEANUP rlm@239: ;; btw, A is already zero. rlm@239: 0xAF ; zero A [D35F] rlm@239: 0x57 ; A->D; makes D=0. rlm@239: 0x18 ; end of frame rlm@239: 0xBC rlm@239: rlm@239: ;; ---- end of cleanup rlm@239: rlm@239: rlm@239: ;; continue writing bytes rlm@239: 0x1D ;; decrement E, the number of bytes to write [D363] rlm@239: 0x78 ;; B->A; now A contains the pressed keys rlm@239: 0x77 ;; copy A to (HL) rlm@239: 0x23 ;; increment HL rlm@239: 0x18 ;; end frame. [goto D31F] rlm@239: 0xB6 ;; TODO: set skip length backwards rlm@239: rlm@239: rlm@239: ;; ---- end of o/p section rlm@239: rlm@239: ;; i/p mode rlm@239: ;; adhere to the mode discipline: rlm@239: ;; D must be one of 0x00 0x08 0x10 0x18. rlm@239: rlm@239: 0x3E ;; load the constant 57 into A. [D369] rlm@239: 0x57 rlm@239: 0x82 ;; add the mode to A rlm@239: 0xEA ;; store A into "thing to execute" rlm@239: 0x74 rlm@239: 0xD3 rlm@239: rlm@239: 0x3E ;; load the constant 8 into A rlm@239: 0x08 rlm@239: 0x82 ;; add the mode to A rlm@239: rlm@239: 0x57 ;; store the incremented mode into D rlm@239: 0x78 ;; B->A; now A contains the pressed keys rlm@239: rlm@239: 0x00 ;; var: thing to execute [D374] rlm@239: rlm@239: 0x18 ;; end frame rlm@239: 0xA8]) rlm@239: rlm@239: (defn write-mem-compact [] rlm@239: (-> (tick (mid-game)) rlm@239: (IE! 0) rlm@239: (inject-item-assembly (write-memory-assembly-compact)))) rlm@239: rlm@377: (defn test-write-bytes-mode [] rlm@377: (let [target-address 0xD135 rlm@377: [target-high target-low] (disect-bytes-2 target-address) rlm@377: assembly [0xF3 0x18 0xFE 0x12] rlm@377: get-mem-region #(subvec (vec (memory %)) rlm@377: target-address (+ target-address 20)) rlm@377: before (write-mem-compact) rlm@377: after rlm@377: (-> before rlm@377: (step []) ; make sure it can handle blanks rlm@377: (step []) ; at the beginning. rlm@377: (step []) rlm@377: (step [:start]) ; select WRITE-BYTES mode rlm@377: (step (buttons 4)) ; write 4 bytes rlm@377: (step (buttons target-high)) rlm@377: (step (buttons target-low)) rlm@377: (step (buttons (nth assembly 0))) rlm@377: (step (buttons (nth assembly 1))) rlm@377: (step (buttons (nth assembly 2))) rlm@377: (step (buttons (nth assembly 3))) rlm@377: (step []) rlm@377: (step []) rlm@377: (step []))] rlm@377: (println "before :" (get-mem-region before)) rlm@377: (println "after :" (get-mem-region after)) rlm@377: (assert (= assembly (take 4 (get-mem-region after)))) rlm@377: after)) rlm@239: rlm@377: (defn test-jump-mode [] rlm@377: (let [target-address 0xC01F rlm@377: [target-high target-low] (disect-bytes-2 target-address) rlm@377: post-jump rlm@377: (-> (test-write-bytes-mode) rlm@377: (step []) rlm@377: (step []) rlm@377: (step []) rlm@377: (step (buttons 0xFF)) ; Select JUMP mode. rlm@377: (step (buttons target-high)) rlm@377: (step (buttons target-low))) rlm@377: program-counters rlm@377: (capture-program-counter rlm@377: post-jump rlm@377: 10000)] rlm@377: (println program-counters) rlm@377: (assert (contains? (set program-counters) target-address)) rlm@377: post-jump)) rlm@377: rlm@377: rlm@377: (defn test-loop [] rlm@377: (contains? rlm@377: (set rlm@377: (capture-program-counter rlm@377: (-> (mid-game) rlm@377: ;; (IE! 0) rlm@377: (set-memory-range 0xD135 [0xF3 0x18 0xFE]) rlm@377: (PC! 0xD135)) 10000)) rlm@377: 0xD136)) rlm@377: rlm@377: rlm@377: rlm@377: rlm@377: