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."))