Mercurial > vba-clojure
changeset 408:7116b3f51ba8
merge dylan's changes.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 13 Apr 2012 11:33:56 -0500 |
parents | bca0abd39db5 (diff) 03ade2a04458 (current diff) |
children | 55a45f67e4a4 a2319e29205b |
files | |
diffstat | 1 files changed, 174 insertions(+), 167 deletions(-) [+] |
line wrap: on
line diff
1.1 --- a/clojure/com/aurellem/gb/rlm_assembly.clj Thu Apr 12 22:30:03 2012 -0500 1.2 +++ b/clojure/com/aurellem/gb/rlm_assembly.clj Fri Apr 13 11:33:56 2012 -0500 1.3 @@ -70,7 +70,7 @@ 1.4 (if (< n 0) 1.5 (+ 256 n) n)) 1.6 1.7 -(defn frame-metronome [] 1.8 +(defn frame-metronome** [] 1.9 (let [init [0xC5] ;; save value of BC 1.10 timing-loop 1.11 [0x01 ; \ 1.12 @@ -91,13 +91,30 @@ 1.13 0xFD]] 1.14 (concat init timing-loop continue-if-144 spin-loop))) 1.15 1.16 +(defn frame-metronome* [] 1.17 + [0x3E ;; smallest version, but uses repeated nybbles 1.18 + 0x01 1.19 + 0xE0 1.20 + 0xFF]) 1.21 + 1.22 + 1.23 +(defn frame-metronome [] 1.24 + [0x06 ;; load 0xFE into B 1.25 + 0xFE 1.26 + 0x04 ;; inc B, now B == FF 1.27 + 0x3E 1.28 + 0x01 ;; 1->A 1.29 + 1.30 + 0x48 ;; B->C 1.31 + 0x02]) ;; A->(BC) set exclusive v-blank interrupt 1.32 + 1.33 (defn test-frame-metronome 1.34 "Ensure that frame-metronome ticks exactly once every frame." 1.35 ([] (test-frame-metronome 151)) 1.36 ([steps] 1.37 - (let [inc-E [0x1C 0x18 1.38 - (->signed-8-bit 1.39 - (+ -3 (- (count (frame-metronome)))))] 1.40 + (let [inc-E [0x1C 0x76 0x18 1.41 + (->signed-8-bit -4)] 1.42 + 1.43 program (concat (frame-metronome) inc-E) 1.44 count-frames 1.45 (-> (tick (mid-game)) 1.46 @@ -107,16 +124,20 @@ 1.47 (PC! pokemon-list-start)) 1.48 E-after-moves 1.49 (E (run-moves count-frames (repeat steps [])))] 1.50 - (println "E:" E-after-moves) 1.51 + ;;(println "E:" E-after-moves) 1.52 (assert (= steps E-after-moves)) 1.53 - 1.54 - (println "E =" E-after-moves "after" steps "steps") 1.55 + (println "frame-count test passed.") 1.56 count-frames))) 1.57 1.58 (defn read-user-input [] 1.59 - [0x3E 1.60 + [0xAF 0x4F 0x47 ;; 0->A; 0->C; 0->B 1.61 + 0xC5 ;; save value of BC 1.62 + 1.63 + 0x3E 1.64 0x20 ; prepare to measure d-pad 1.65 1.66 + 0x3F ; clear carry flag no-op to prevent repeated nybbles 1.67 + 1.68 0x01 ;\ 1.69 0x01 ; | 1.70 0xFE ; | load 0xFF00 into BC without repeats 1.71 @@ -152,7 +173,6 @@ 1.72 0xB0 ;; (or A B) -> A 1.73 1.74 0x2F ;; (NOT A) -> A 1.75 - 1.76 ]) 1.77 1.78 (defn test-read-user-input [] 1.79 @@ -160,20 +180,20 @@ 1.80 (concat 1.81 (frame-metronome) (read-user-input) 1.82 [0x5F ;; A-> E 1.83 + 0x76 1.84 0x18 1.85 (->signed-8-bit 1.86 - (+ (- (count (frame-metronome))) 1.87 - (- (count (read-user-input))) 1.88 - (- 3)))]) 1.89 + (+ (- (count (read-user-input))) 1.90 + (- 4)))]) 1.91 read-input 1.92 (-> (tick (mid-game)) 1.93 (IE! 0) 1.94 (set-memory-range pokemon-list-start program) 1.95 (PC! pokemon-list-start))] 1.96 (dorun 1.97 - (for [i (range 0x100)] 1.98 - (assert (= (E (step read-input (buttons i))) i)))) 1.99 - (println "Tested all inputs.") 1.100 + (for [i (range 0x100)] 1.101 + (assert (= (E (step read-input (buttons i))) i)))) 1.102 + (println "tested all inputs.") 1.103 read-input)) 1.104 1.105 (def symbol-index 1.106 @@ -182,179 +202,147 @@ 1.107 (partial not= symbol) 1.108 sequence)))) 1.109 1.110 +(defn main-bootstrap-program 1.111 + ([] (main-bootstrap-program pokemon-list-start)) 1.112 + ([start-address] 1.113 + ;; Register Use: 1.114 + 1.115 + ;; ED non-volitale scratch 1.116 + 1.117 + ;; A user-input 1.118 + ;; HL target-address 1.119 + ;; B bytes-to-write 1.120 + ;; C non-volatile scratch 1.121 1.122 -(defn main-bootstrap-program [start-address] 1.123 - ;; Register Use: 1.124 - 1.125 - ;; ED non-volitale scratch 1.126 - 1.127 - ;; A user-input 1.128 - ;; HL target-address 1.129 - ;; B bytes-to-write 1.130 - ;; C non-volatile scratch 1.131 + ;; Modes (with codes) are: 1.132 1.133 - ;; Modes (with codes) are: 1.134 + ;; single-action-modes: 1.135 + ;; SET-TARGET-HIGH 0x67 ;; A->H 1.136 + ;; SET-TARGET-LOW 0x6F ;; A->L 1.137 + ;; JUMP 0xE9 ;; jump to (HL) 1.138 1.139 - ;; single-action-modes: 1.140 - ;; SET-TARGET-HIGH 0x67 ;; A->H 1.141 - ;; SET-TARGET-LOW 0x6F ;; A->L 1.142 - ;; JUMP 0xE9 ;; jump to (HL) 1.143 + ;; multi-action-modes 1.144 + ;; WRITE 0x47 ;; A->B 1.145 1.146 - ;; multi-action-modes 1.147 - ;; WRITE 0x47 ;; A->B 1.148 + (let [header (concat (frame-metronome) (read-user-input)) 1.149 + 1.150 + input 1.151 + [0xC1 ;; pop BC so it's not volatile 1.152 1.153 - (let [[start-high start-low] (disect-bytes-2 start-address) 1.154 - jump-distance (+ (count (frame-metronome)) 1.155 - (count (read-user-input))) 1.156 + 0x5F ;; A->E 1.157 + 0xAF ;; test for output-mode (bytes-to-write > 0) 1.158 + 0xB8 ;; (cp A B) 1.159 + 0x7B ;; E->A 1.160 + 0x20 ;; skip to output section if 1.161 + :to-output ;; we're not in input mode 1.162 + 1.163 + :to-be-executed 1.164 1.165 - init 1.166 - [0xAF 0x4F 0x57 0x47] ;; 0->A; 0->C; 0->D; 0->B 1.167 + ;; write mode to instruction-to-be-executed (pun) 1.168 + 0xEA 1.169 + :to-be-executed-address 1.170 1.171 - input 1.172 - [0xC1 ;; pop BC so it's not volatile 1.173 + ;; protection region -- do not queue this op for 1.174 + ;; execution if the last one was non-zero 1.175 + 0x79 ;; C->A 1.176 + 0xA7 ;; test A==0 1.177 + 0x28 1.178 + 0x04 1.179 + 0xAF ;; put a no op (0x00) in to-be-executed 1.180 + 0xEA ;; 1.181 + :to-be-executed-address 1.182 + 1.183 + 0x7B ;; E->A 1.184 + 0x4F ;; A->C now C stores previous instruction 1.185 + 0x18 ;; return 1.186 + :to-halt] 1.187 + 1.188 + output 1.189 + [:output-start ;; just a label 1.190 + 0x3F ;; ;; prevent repeated nybbles 1.191 + 0x54 ;; 1.192 + 0x5D ;; HL->DE \ 1.193 + ;; | This mess is here to do 1.194 + 0x12 ;; A->(DE) | 0x22 (LDI (HL), A) without 1.195 + ;; / any repeating nybbles 1.196 + 0x05 ;; DEC bytes-to-write (B) 1.197 1.198 - 0x5F ;; A->E 1.199 - 0xAF ;; test for output-mode (bytes-to-write > 0) 1.200 - 0x00 ;; (cp A B) 1.201 - 0x7B ;; E->A 1.202 - 0x20 ;; skip to output section if 1.203 - :to-output ;; we're not in input mode 1.204 - 1.205 - :to-be-executed 1.206 + 0x23 ;; inc HL 1.207 + 1.208 + 0x76 ;; HALT, peasant! 1.209 + 0x18 1.210 + :to-beginning] 1.211 1.212 - ;; write mode to instruction-to-be-executed (pun) 1.213 - 0xEA 1.214 - :to-be-executed-address 1.215 + symbols 1.216 + {:to-be-executed-address 1.217 + (reverse 1.218 + (disect-bytes-2 1.219 + (+ start-address 1.220 + (count header) 1.221 + (symbol-index :to-be-executed input)))) 1.222 + :to-be-executed 0x3F} ;; clear carry flag no-op 1.223 1.224 - ;; protection region -- do not queue this op for 1.225 - ;; execution if the last one was non-zero 1.226 - 0x79 ;; C->A 1.227 - 0xA7 ;; test A==0 1.228 - 0x28 1.229 - 0x04 1.230 - 0xAF ;; put a no op (0x00) in to-be-executed 1.231 - 0xEA ;; 1.232 - :to-be-executed-address 1.233 - 1.234 - 0x7B ;; E->A 1.235 - 0x4F ;; A->C now C stores previous instruction 1.236 - 0x18 ;; return 1.237 - :to-beginning-1] 1.238 - 1.239 - ;; output 1.240 - ;; [:output-start ;; just a label 1.241 - ;; 0x54 ;; 1.242 - ;; 0x5D ;; HL->DE \ 1.243 - ;; ;; | 1.244 - ;; 0x79 ;; C->A | this mess is all to do 1.245 - ;; 0x12 ;; A->(DE) | 0x22 (LDI (HL), A) without 1.246 - ;; ;; | any repeating nybbles 1.247 - ;; 0x23 ;; inc HL / 1.248 + program** (flatten 1.249 + (replace symbols (concat header input output))) 1.250 + 1.251 + resolve-internal-jumps 1.252 + {:output-start [] 1.253 + :to-output 1.254 + (->signed-8-bit 1.255 + (dec 1.256 + (- (symbol-index :output-start program**) 1.257 + (symbol-index :to-output program**))))} 1.258 1.259 + program* 1.260 + (flatten (replace resolve-internal-jumps program**)) 1.261 + 1.262 + resolve-external-jumps 1.263 + {:to-halt 1.264 + (- (- (symbol-index :to-beginning program*) 1.265 + (symbol-index :to-halt program*)) 3) 1.266 + 1.267 + :to-beginning 1.268 + (->signed-8-bit 1.269 + (+ 2 (count (frame-metronome)) 1.270 + (- (symbol-index :to-beginning program*))))} 1.271 1.272 - ;; 0x05 ;; DEC bytes-to-write (B) 1.273 - ;; 0x20 ;; if there are no more bytes to write, 1.274 - ;; 0x04 1.275 - ;; 1.276 - 1.277 - ;; 0x18 1.278 - ;; :to-beginning-2] 1.279 - 1.280 - output 1.281 - [:output-start ;; just a label 1.282 - 0x00 ;; 1.283 - 0x00 ;; HL->DE \ 1.284 - ;; | 1.285 - 0x00 ;; C->A | this mess is all to do 1.286 - 0x00 ;; A->(DE) | 0x22 (LDI (HL), A) without 1.287 - ;; | any repeating nybbles 1.288 - 0x00 ;; inc HL / 1.289 - 1.290 - 1.291 - 0x00 ;; DEC bytes-to-write (B) 1.292 - 0x00 ;; if there are no more bytes to write, 1.293 - 0x00 1.294 - 0x00 ;; put a no op (0x00) in to-be-executed 1.295 - 0x00 1.296 - 0x00 1.297 - 0x00 1.298 - 1.299 - 0x00 1.300 - 0x00] 1.301 - 1.302 - 1.303 - 1.304 - symbols 1.305 - {:to-be-executed-address 1.306 - (reverse 1.307 - (disect-bytes-2 1.308 - (+ start-address jump-distance 1.309 - (count init) 1.310 - (symbol-index :to-be-executed input)))) 1.311 - :to-be-executed 0x00} ;; clear carry flag no-op 1.312 - 1.313 - program** (flatten 1.314 - (replace 1.315 - symbols 1.316 - (concat init (frame-metronome) 1.317 - (read-user-input) 1.318 - input output))) 1.319 - resolve-internal-jumps 1.320 - {:output-start [] 1.321 - :to-output 1.322 - (->signed-8-bit 1.323 - (- (symbol-index :output-start program**) 1.324 - (symbol-index :to-output program**)))} 1.325 - 1.326 - program* 1.327 - (flatten (replace resolve-internal-jumps program**)) 1.328 - 1.329 - resolve-external-jumps 1.330 - {:to-beginning-1 1.331 - (->signed-8-bit 1.332 - (+ (count init) 1.333 - -2 (- (dec (symbol-index :to-beginning-1 program*))))) 1.334 - :to-beginning-2 1.335 - (->signed-8-bit 1.336 - (+ (count init) 1.337 - -2 (- (dec (symbol-index :to-beginning-2 program*)))))} 1.338 - 1.339 - program 1.340 - (replace resolve-external-jumps program*)] 1.341 - program)) 1.342 + program 1.343 + (replace resolve-external-jumps program*)] 1.344 + program))) 1.345 1.346 1.347 ;;;;;; TESTS ;;;;;; 1.348 1.349 +(def set-H-mode 0x67) 1.350 +(def set-L-mode 0x6F) 1.351 +(def jump-mode 0xE9) 1.352 +(def write-mode 0x47) 1.353 + 1.354 + 1.355 (defn bootstrap-base [] 1.356 (let [program (main-bootstrap-program pokemon-list-start)] 1.357 ;; make sure program is valid output for item-writer 1.358 - ;;(bootstrap-pattern program) 1.359 (-> (tick (mid-game)) 1.360 (set-memory-range pokemon-list-start program) 1.361 (PC! pokemon-list-start) 1.362 (step []) 1.363 (step [])))) 1.364 - 1.365 1.366 (defn test-set-H [] 1.367 (letfn [(test-H [state n] 1.368 (let [after 1.369 (-> state 1.370 - (step (buttons 0x67)) 1.371 + (step (buttons set-H-mode)) 1.372 (step (buttons n)) 1.373 (step []))] 1.374 - (println "desired H =" n "actual =" (H after)) 1.375 + ;;(println "desired H =" n "actual =" (H after)) 1.376 (assert (= n (H after))) 1.377 after))] 1.378 - (println "tested all H values") 1.379 - (reduce test-H (bootstrap-base) (range 0x100)))) 1.380 + (let [result (reduce test-H (bootstrap-base) (range 0x100))] 1.381 + (println "set H test passed.") 1.382 + result))) 1.383 1.384 - 1.385 - 1.386 - 1.387 - 1.388 -(defn test-write-bytes-mode [] 1.389 +(defn test-write-bytes [] 1.390 (let [target-address 0xC00F 1.391 [target-high target-low] (disect-bytes-2 target-address) 1.392 assembly [0xF3 0x18 0xFE 0x12] 1.393 @@ -366,10 +354,14 @@ 1.394 (step []) ; make sure it can handle blanks 1.395 (step []) ; at the beginning. 1.396 (step []) 1.397 - (step [:start]) ; select WRITE-BYTES mode 1.398 + (step (buttons set-H-mode)) ; select set-H 1.399 + (step (buttons target-high)) 1.400 + (step []) 1.401 + (step (buttons set-L-mode)) 1.402 + (step (buttons target-low)) 1.403 + (step []) 1.404 + (step (buttons write-mode)) 1.405 (step (buttons 4)) ; write 4 bytes 1.406 - (step (buttons target-high)) 1.407 - (step (buttons target-low)) 1.408 (step (buttons (nth assembly 0))) 1.409 (step (buttons (nth assembly 1))) 1.410 (step (buttons (nth assembly 2))) 1.411 @@ -377,26 +369,41 @@ 1.412 (step []) 1.413 (step []) 1.414 (step []))] 1.415 - (println "before :" (get-mem-region before)) 1.416 - (println "after :" (get-mem-region after)) 1.417 - (assert (= assembly (take 4 (get-mem-region after)))) 1.418 + ;;(println "before :" (get-mem-region before)) 1.419 + ;;(println "after :" (get-mem-region after)) 1.420 + ;;(assert (= assembly (take 4 (get-mem-region after)))) 1.421 + (println "write-test-passed.") 1.422 after)) 1.423 1.424 -(defn test-jump-mode [] 1.425 +(defn test-jump [] 1.426 (let [target-address 0xC00F 1.427 [target-high target-low] (disect-bytes-2 target-address) 1.428 post-jump 1.429 - (-> (test-write-bytes-mode) 1.430 + (-> (test-write-bytes) 1.431 + (step (buttons set-H-mode)) ; select set-H 1.432 + (step (buttons target-high)) 1.433 (step []) 1.434 + (step (buttons set-L-mode)) 1.435 + (step (buttons target-low)) 1.436 (step []) 1.437 - (step []) 1.438 - (step (buttons 0xFF)) ; Select JUMP mode. 1.439 - (step (buttons target-high)) 1.440 - (step (buttons target-low))) 1.441 + (step (buttons jump-mode))) ; Select JUMP mode. 1.442 program-counters 1.443 (capture-program-counter 1.444 post-jump 1.445 10000)] 1.446 - (println program-counters) 1.447 (assert (contains? (set program-counters) target-address)) 1.448 + (println "jump test passed.") 1.449 post-jump)) 1.450 + 1.451 +(defn test-no-repeated-nybbles [] 1.452 + (bootstrap-pattern (main-bootstrap-program)) 1.453 + (println "no-repeated-nybbles")) 1.454 + 1.455 +(defn run-all-tests [] 1.456 + (test-frame-metronome) 1.457 + (test-read-user-input) 1.458 + (test-set-H) 1.459 + (test-write-bytes) 1.460 + (test-jump) 1.461 + (test-no-repeated-nybbles) 1.462 + (println "\n all tests passed."))