annotate clojure/com/aurellem/gb/dylan_assembly.clj @ 438:067ea3f0d951

can now play midi files with two tracks.
author Robert McIntyre <rlm@mit.edu>
date Wed, 25 Apr 2012 13:46:31 -0500
parents 1f14c1b8af7e
children
rev   line source
rlm@239 1 (ns com.aurellem.gb.dylan-assembly
rlm@239 2 "A much more compact version of write-memory-assembly"
rlm@239 3 {:author "Dylan Holmes"}
rlm@377 4 (:use (com.aurellem.gb gb-driver assembly util vbm))
rlm@239 5 (:import [com.aurellem.gb.gb_driver SaveState]))
rlm@239 6
rlm@376 7 ;; Specs for main bootstrap program
rlm@376 8
rlm@376 9 ;; Number-Input
rlm@376 10 ;; Number input works using all eight buttons to
rlm@376 11 ;; spell out an 8 bit number. The order of buttons is
rlm@376 12 ;; [:d :u :l :r :start :select :b :a] --> 11111111
rlm@376 13 ;; [ :l :start :a] --> 00101001
rlm@376 14
rlm@376 15 ;;; MODE-SELECT
rlm@376 16 ;; The bootstrap program starts in MODE-SELECT mode.
rlm@376 17 ;; MODE-SELECT transitions to one of three modes depending
rlm@376 18 ;; on which buttons are pressed:
rlm@376 19 ;; 0 (no-buttons) : MODE-SELECT
rlm@376 20 ;; 8 [:start] : WRITE-BYTES
rlm@376 21 ;; 0xFF (all-buttons) : JUMP
rlm@376 22
rlm@376 23
rlm@376 24 ;;; WRITE-BYTES
rlm@376 25
rlm@376 26 ;; WRITE-BYTES mode writes sequences of arbitray values to
rlm@376 27 ;; arbitray memory locations. It expects you to enter a
rlm@376 28 ;; header of three bytes describing what to write:
rlm@376 29
rlm@376 30 ;; Byte 0 : Number of Bytes to Write
rlm@376 31 ;; Byte 1 : Start Address High Byte
rlm@376 32 ;; Byte 1 : Start Address Low Byte
rlm@376 33
rlm@376 34 ;; Then, you enter the number of bytes specified in Byte 0
rlm@376 35 ;; they are written to the start address in
rlm@376 36 ;; sequence. After the last byte is written control
rlm@376 37 ;; returns to MODE-SELECT mode.
rlm@376 38
rlm@376 39 ;; Example: to write the sequence [1 2 3 4] starting at
rlm@376 40 ;; address 0xC01F enter
rlm@376 41 ;; Byte 0 : 4 (will write four bytes)
rlm@376 42 ;; Byte 1 : 0xC0 (high byte of 0xC01F)
rlm@376 43 ;; Byte 2 : 0x1F (low byte of 0xC01F)
rlm@376 44 ;; Byte 3 : 1 (write 1 to 0xC01F)
rlm@376 45 ;; Byte 4 : 2 (write 2 to 0xC020)
rlm@376 46 ;; Byte 5 : 3 (write 3 to 0xC021)
rlm@376 47 ;; Byte 6 : 4 (write 4 to 0xC022)
rlm@376 48
rlm@376 49 ;;; JUMP
rlm@376 50 ;; JUMP mode jumps program control to any arbitray
rlm@376 51 ;; location. It expects you to enter two bytes which
rlm@376 52 ;; correspond to the high and low bytes of the memory
rlm@376 53 ;; address to which you want to jump.
rlm@376 54 ;; Byte 0 : Jump Address High Byte
rlm@376 55 ;; Byte 1 : Jump Address Low Byte
rlm@376 56
rlm@376 57 ;; Example: to jump to address 0x1234 enter
rlm@376 58 ;; Byte 0 : 0x12 (high byte of 0x1234)
rlm@376 59 ;; Byte 1 : 0x34 (low byte of 0x1234)
rlm@376 60
rlm@376 61
rlm@239 62 (defn write-memory-assembly-compact
rlm@239 63 "Currently, grabs input from the user each frame."
rlm@239 64 []
rlm@239 65 [
rlm@239 66 ;; --------- FRAME METRONOME
rlm@239 67 0x18 ;; jump ahead to cleanup. first time only.
rlm@239 68 0x40 ;; v-blank-prev [D31E]
rlm@239 69
rlm@239 70 0xFA ;; load modes into A [D31F]
rlm@239 71 0x41
rlm@239 72 0xFF
rlm@239 73
rlm@239 74 0x47 ;; A -> B
rlm@239 75 0xCB ;; rotate A
rlm@239 76 0x2F
rlm@239 77 0x2F ;; invert A
rlm@239 78
rlm@239 79 0xA0
rlm@239 80 0x47 ;; now B_0 contains (VB==1)
rlm@239 81
rlm@239 82 0xFA ;; load v-blank-prev
rlm@239 83 0x1E
rlm@239 84 0xD3
rlm@239 85
rlm@239 86 0x2F ;; complement v-blank-prev
rlm@239 87
rlm@239 88 0xA0 ;; A & B --> A
rlm@239 89 0x4F ;; now C_0 contains increment?
rlm@239 90
rlm@239 91
rlm@239 92 0x78 ;; B->A
rlm@239 93 0xEA ;; spit A --> vbprev
rlm@239 94 0x1E
rlm@239 95 0xD3
rlm@239 96
rlm@239 97 0xCB ;test C_0
rlm@239 98 0x41
rlm@239 99 0x20 ; JUMP ahead to button input if nonzero
rlm@239 100 0x02
rlm@239 101 0x18 ; JUMP back to frame metronome (D31F)
rlm@239 102 0xE7
rlm@239 103
rlm@239 104 ;; -------- GET BUTTON INPUT
rlm@239 105
rlm@239 106 ;; btw, C_0 is now 1
rlm@239 107 ;; prepare to select bits
rlm@239 108
rlm@239 109 0x06 ;; load 0x00 into B
rlm@239 110 0x00 ;; to initialize for "OR" loop
rlm@239 111
rlm@239 112 0x3E ;; load 0x20 into A, to measure dpad
rlm@239 113 0x20
rlm@239 114
rlm@239 115
rlm@239 116 0xE0 ;; load A into [FF00] ;; start of OR loop [D33C]
rlm@239 117 0x00
rlm@239 118
rlm@239 119 0xF0 ;; load A from [FF00]
rlm@239 120 0x00
rlm@239 121
rlm@239 122 0xE6 ;; bitmask 00001111
rlm@239 123 0x0F
rlm@239 124
rlm@239 125 0xB0 ;; A or B --> A
rlm@239 126 0xCB
rlm@239 127 0x41 ;; test bit 0 of C
rlm@239 128 0x28 ;; JUMP forward if 0
rlm@239 129 0x08
rlm@239 130
rlm@239 131 0x47 ;; A -> B
rlm@239 132 0xCB ;; swap B nybbles
rlm@239 133 0x30
rlm@239 134 0x0C ;; increment C
rlm@239 135 0x3E ;; load 0x10 into A, to measure btns
rlm@239 136 0x10
rlm@239 137 0x18 ;; JUMP back to "load A into [FF00]" [20 steps?]
rlm@239 138 0xED
rlm@239 139
rlm@239 140
rlm@239 141 ;; ------ TAKE ACTION BASED ON USER INPUT
rlm@239 142
rlm@239 143 ;; "input mode"
rlm@239 144 ;; mode 0x00 : select mode
rlm@239 145 ;; mode 0x08 : select bytes-to-write
rlm@239 146 ;; mode 0x10 : select hi-bit
rlm@239 147 ;; mode 0x18 : select lo-bit
rlm@239 148
rlm@239 149 ;; "output mode"
rlm@239 150 ;; mode 0x20 : write bytes
rlm@239 151 ;; mode 0xFF : jump PC
rlm@239 152
rlm@239 153
rlm@239 154 ;; registers
rlm@239 155 ;; D : mode select
rlm@239 156 ;; E : count of bytes to write
rlm@239 157 ;; H : address-high
rlm@239 158 ;; L : address-low
rlm@239 159
rlm@239 160 ;; now A contains the pressed keys
rlm@239 161 0x2F ; complement A, by request. [D34F]
rlm@239 162
rlm@239 163 0x47 ; A->B ;; now B contains the pressed keys
rlm@239 164 0x7B ; E->A ;; now A contains the count.
rlm@239 165
rlm@239 166 0xCB ; test bit 5 of D (are we in o/p mode?)
rlm@239 167 0x6A
rlm@239 168 0x28 ; if test == 0, skip this o/p section
rlm@239 169 0x13 ; JUMP
rlm@239 170
rlm@239 171 0xCB ; else, test bit 0 of D (fragile; are we in pc mode?)
rlm@239 172 0x42
rlm@239 173 0x28 ; if test == 0, skip the following command
rlm@239 174 0x01
rlm@239 175
rlm@239 176 ;; output mode I: moving the program counter
rlm@239 177 0xE9 ; ** move PC to (HL)
rlm@239 178
rlm@239 179 ;; output mode II: writing bytes
rlm@239 180 0xFE ; A compare 0. finished writing?
rlm@239 181 0x00
rlm@239 182 0x20 ; if we are not finished, skip cleanup
rlm@239 183 0x04 ; JUMP
rlm@239 184
rlm@239 185 ;; CLEANUP
rlm@239 186 ;; btw, A is already zero.
rlm@239 187 0xAF ; zero A [D35F]
rlm@239 188 0x57 ; A->D; makes D=0.
rlm@239 189 0x18 ; end of frame
rlm@239 190 0xBC
rlm@239 191
rlm@239 192 ;; ---- end of cleanup
rlm@239 193
rlm@239 194
rlm@239 195 ;; continue writing bytes
rlm@239 196 0x1D ;; decrement E, the number of bytes to write [D363]
rlm@239 197 0x78 ;; B->A; now A contains the pressed keys
rlm@239 198 0x77 ;; copy A to (HL)
rlm@239 199 0x23 ;; increment HL
rlm@239 200 0x18 ;; end frame. [goto D31F]
rlm@239 201 0xB6 ;; TODO: set skip length backwards
rlm@239 202
rlm@239 203
rlm@239 204 ;; ---- end of o/p section
rlm@239 205
rlm@239 206 ;; i/p mode
rlm@239 207 ;; adhere to the mode discipline:
rlm@239 208 ;; D must be one of 0x00 0x08 0x10 0x18.
rlm@239 209
rlm@239 210 0x3E ;; load the constant 57 into A. [D369]
rlm@239 211 0x57
rlm@239 212 0x82 ;; add the mode to A
rlm@239 213 0xEA ;; store A into "thing to execute"
rlm@239 214 0x74
rlm@239 215 0xD3
rlm@239 216
rlm@239 217 0x3E ;; load the constant 8 into A
rlm@239 218 0x08
rlm@239 219 0x82 ;; add the mode to A
rlm@239 220
rlm@239 221 0x57 ;; store the incremented mode into D
rlm@239 222 0x78 ;; B->A; now A contains the pressed keys
rlm@239 223
rlm@239 224 0x00 ;; var: thing to execute [D374]
rlm@239 225
rlm@239 226 0x18 ;; end frame
rlm@239 227 0xA8])
rlm@239 228
rlm@239 229 (defn write-mem-compact []
rlm@239 230 (-> (tick (mid-game))
rlm@239 231 (IE! 0)
rlm@239 232 (inject-item-assembly (write-memory-assembly-compact))))
rlm@239 233
rlm@377 234 (defn test-write-bytes-mode []
rlm@377 235 (let [target-address 0xD135
rlm@377 236 [target-high target-low] (disect-bytes-2 target-address)
rlm@377 237 assembly [0xF3 0x18 0xFE 0x12]
rlm@377 238 get-mem-region #(subvec (vec (memory %))
rlm@377 239 target-address (+ target-address 20))
rlm@377 240 before (write-mem-compact)
rlm@377 241 after
rlm@377 242 (-> before
rlm@377 243 (step []) ; make sure it can handle blanks
rlm@377 244 (step []) ; at the beginning.
rlm@377 245 (step [])
rlm@377 246 (step [:start]) ; select WRITE-BYTES mode
rlm@377 247 (step (buttons 4)) ; write 4 bytes
rlm@377 248 (step (buttons target-high))
rlm@377 249 (step (buttons target-low))
rlm@377 250 (step (buttons (nth assembly 0)))
rlm@377 251 (step (buttons (nth assembly 1)))
rlm@377 252 (step (buttons (nth assembly 2)))
rlm@377 253 (step (buttons (nth assembly 3)))
rlm@377 254 (step [])
rlm@377 255 (step [])
rlm@377 256 (step []))]
rlm@377 257 (println "before :" (get-mem-region before))
rlm@377 258 (println "after :" (get-mem-region after))
rlm@377 259 (assert (= assembly (take 4 (get-mem-region after))))
rlm@377 260 after))
rlm@239 261
rlm@377 262 (defn test-jump-mode []
rlm@377 263 (let [target-address 0xC01F
rlm@377 264 [target-high target-low] (disect-bytes-2 target-address)
rlm@377 265 post-jump
rlm@377 266 (-> (test-write-bytes-mode)
rlm@377 267 (step [])
rlm@377 268 (step [])
rlm@377 269 (step [])
rlm@377 270 (step (buttons 0xFF)) ; Select JUMP mode.
rlm@377 271 (step (buttons target-high))
rlm@377 272 (step (buttons target-low)))
rlm@377 273 program-counters
rlm@377 274 (capture-program-counter
rlm@377 275 post-jump
rlm@377 276 10000)]
rlm@377 277 (println program-counters)
rlm@377 278 (assert (contains? (set program-counters) target-address))
rlm@377 279 post-jump))
rlm@377 280
rlm@377 281
rlm@377 282 (defn test-loop []
rlm@377 283 (contains?
rlm@377 284 (set
rlm@377 285 (capture-program-counter
rlm@377 286 (-> (mid-game)
rlm@377 287 ;; (IE! 0)
rlm@377 288 (set-memory-range 0xD135 [0xF3 0x18 0xFE])
rlm@377 289 (PC! 0xD135)) 10000))
rlm@377 290 0xD136))
rlm@377 291
rlm@377 292
rlm@377 293
rlm@377 294
rlm@377 295