Mercurial > vba-clojure
view clojure/com/aurellem/assembly.clj @ 120:d6f2a06cb128
finished Mode 1 of bootstrapping program
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 16 Mar 2012 20:31:53 -0500 |
parents | 6cbea8ab65b6 |
children | 744de3427c05 |
line wrap: on
line source
1 (ns com.aurellem.assembly2 (:use (com.aurellem gb-driver vbm title items))3 (:import [com.aurellem.gb_driver SaveState]))5 (defn mid-game []6 (read-state "mid-game"))8 (defn inject-assembly9 ([^SaveState state10 program-counter registers11 assembly-code]12 (let [scratch-memory (memory state)]13 ;; inject assembly code14 (dorun (map (fn [index val]15 (aset scratch-memory index val))16 (range program-counter17 (+ program-counter (count assembly-code)))18 assembly-code))19 (-> state20 (write-memory! scratch-memory)21 (write-registers! registers)22 (PC! program-counter)))))24 (defn inject-item-assembly25 ([^SaveState state assembly-code]26 (inject-assembly state (inc item-list-start)27 (registers state)28 assembly-code))29 ([assembly-code]30 (inject-item-assembly @current-state assembly-code)))32 (defn info33 ([^SaveState state]34 (println (format "PC: 0x%04X" (PC state)))35 (println "Instruction:"36 (format "0x%02X" (aget (memory state) (PC state))))37 state))39 (defn print-interrupt40 [^SaveState state]41 (println (format "IE: %d" (IE state)))42 state)44 (defn print-listing [state begin end]45 (dorun (map46 (fn [opcode line]47 (println (format "0x%04X: 0x%02X" line opcode)))48 (subvec (vec (memory state)) begin end)49 (range begin end)))50 state)52 (defn run-assembly53 ([info-fn assembly n]54 (let [final-state55 (reduce (fn [state _]56 (tick (info-fn state)))57 (inject-item-assembly58 (mid-game) assembly)59 (range n))]60 final-state))61 ([assembly n]62 (run-assembly info assembly n)))64 (def buttons-port 0xFF00)66 (defn A [state]67 (bit-shift-right (bit-and 0x0000FF00 (AF state)) 8))69 (defn binary-str [num]70 (format "%08d"71 (Integer/parseInt72 (Integer/toBinaryString num) 10)))74 (defn view-register [state name reg-fn]75 (println (format "%s: %s" name76 (binary-str (reg-fn state))))77 state)79 (defn view-memory [state mem]80 (println (format "mem 0x%04X = %s" mem81 (binary-str (aget (memory state) mem))))82 state)84 (defn trace [state]85 (loop [program-counters []86 opcodes []]87 (let [frame-boundary?88 (com.aurellem.gb.Gb/tick)]89 (println (count opcodes))90 (if frame-boundary?91 [program-counters opcodes]92 (recur93 (conj program-counters94 (first (registers @current-state)))95 (conj opcodes96 (aget (memory @current-state)97 (PC @current-state))))))))99 (defn good-trace []100 (-> (mid-game) (tick) (IE! 0)101 (set-inv-mem [0x00 0x00 0X00 0x00])102 (PC! item-list-start)(print-interrupt)103 (info) (tick) (info) (tick) (info)))105 (defn read-down-button []106 (-> (tick (mid-game))107 (IE! 0) ; disable interrupts108 (inject-item-assembly109 ;; write 00010000 to 0xFF00 to select joypad110 [0x18 ;D31D ; jump over111 0x01 ;D31E ; the next 8 bits112 ;D31F113 (Integer/parseInt "00100000" 2) ; data section115 0xFA ;D320 ; load (D31F) into A116 0x1F ;D321 -->117 0xD3 ;D322 --> D31F119 0xEA ;D323 ; load (A), which is120 0x00 ;D324 --> ; 00010000, into FF00121 0xFF ;D325 --> FF00123 0x18 ;D326 ; this is the place where124 0x01 ;D327 ; we will store whether125 0x00 ;D328 ; "down" is pressed.127 0xFA ;D329 ; (FF00) -> A128 0x00 ;D32A129 0xFF ;D32B131 0xCB ;D32C ; Test whether "down"132 0x5F ;D32D ; is pressed.134 0x28 ;D32E ; if down is pressed,135 0x03 ;D32F ; skip the next section136 ; of code.137 ;; down-is-not-pressed138 0xC3 ;D330139 0x1D ;D331 ; return to beginning140 0xD3 ;D332142 ;; down-is-pressed143 0xEA ;D334 ; write A to D328 if144 0x28 ;D335 ; "down" was pressed145 0xD3 ;D336147 0xC3 ;D330148 0x1D ;D331 ; return to beginning149 0xD3 ;D332150 ])))152 (defn test-read-down []153 (= (view-memory (step (step (read-down-button) [:d])) 0xD328)154 (view-memory (step (step (read-down-button))) 0xD328)))156 (defn count-frames []157 (-> (tick (mid-game))158 (IE! 0) ; disable interrupts159 (inject-item-assembly160 [0x18 ;D31D ; jump over161 0x02 ;D31E ; the next 2 bytes162 0x00 ;D31F ; frame-count163 0x00 ;D320 ; v-blank-prev165 0xFA ;D321166 0x41 ;D322 ; load (FF41) into A167 0xFF ;D323 ; this contains mode flags169 ;; if we're in v-blank, the bit-1 is 0170 ;; and bit-2 is 1 Otherwise, it is not v-blank.171 0xCB ;D324 ; test bit-1 of A172 0x4F ;D325174 0xC2 ;D326 ; if bit-1 is not 0175 0x44 ;D327 ; GOTO not-v-blank176 0xD3 ;D328178 0xCB ;D329 ; test bit-0 of A179 0x47 ;D32A181 0xCA ;D32B ; if bit-0 is not 1182 0x44 ;D32C ; GOTO not-v-blank183 0xD3 ;D32D185 ;;; in v-blank mode187 ;; if v-blank-prev was 0,188 ;; increment frame-count190 0xFA ;D32E ; load v-blank-prev to A191 0x20 ;D32F192 0xD3 ;D330194 0xCB ;D331195 0x47 ;D332 ; test bit-0 of A197 0x20 ;D333 ; skip next section198 0x07 ;D334 ; if v-blank-prev was not zero200 ;; v-blank was 0, increment frame-count201 0xFA ;D335 ; load frame-count into A202 0x1F ;D336203 0xD3 ;D337205 0x3C ;D338 ; inc A207 0xEA ;D339 ; load A into frame-count208 0x1F ;D33A209 0xD3 ;D33B211 ;; set v-blank-prev to 1212 0x3E ;D33C ; load 1 into A213 0x01 ;D33D215 0xEA ;D33E ; load A into v-blank-prev216 0x20 ;D33F217 0xD3 ;D340219 0xC3 ;D341 ; return to beginning220 0x1D ;D342221 0xD3 ;D343223 ;;; not in v-blank mode224 ;; set v-blank-prev to 0225 0x3E ;D344 ; load 0 into A226 0x00 ;D345228 0xEA ;D346 ; load A into v-blank-prev229 0x20 ;D347230 0xD3 ;D348232 0xC3 ;D349 ; return to beginning233 0x1D ;D34A234 0xD3 ;D34B235 ])))237 (defn step-count-frames []238 (-> (read-down-button)239 (info)240 (tick) ;; skip over data section241 (info)242 (view-register "Register A" A)243 (tick) ;; load-data into A244 (view-register "Register A" A)245 (info)246 (view-memory 0xFF00)247 (tick) ;; load A into 0xFF00248 (view-memory 0xFF00)249 (info)250 (tick)251 (info)252 (tick)253 (info)254 (tick)255 (info)256 (tick)257 (info)258 (tick)259 (info)260 (tick)261 (print-inventory)))263 (defn test-count-frames []264 (= 255 (aget (memory ((apply comp (repeat 255 step))265 (count-frames)))266 0xD31F)))268 ;; specs for main bootstrap program269 ;; starts in "mode-select" mode270 ;; Each button press takes place in a single frame.271 ;; mode-select-mode takes one of the main buttons272 ;; which selects one of up to eight modes273 ;; mode 1 activated by the "A" button274 ;; the next two button presses indicates the start275 ;; memory location which to which the bootstrap276 ;; program will write.277 ;; This is done by using each of the eight buttons to278 ;; spell out an 8 bit number. The order of buttons is279 ;; [:d :u :l :r :start :select :b :a]280 ;; [:a :start :l] --> 00101001282 ;; the next button press determines how many bytes are to be283 ;; written, starting at the start position.285 ;; then, the actual bytes are entered and are written to the286 ;; start address in sequence.288 (defn input-number-assembly []289 [0x18 ;D31D ; jump over290 0x02 ;D31E ; the next 2 bytes291 0x00 ;D31F ; frame-count292 0x00 ;D320 ; v-blank-prev294 0xFA ;D321295 0x41 ;D322 ; load (FF41) into A296 0xFF ;D323 ; this contains mode flags298 ;; if we're in v-blank, the bit-1 is 0299 ;; and bit-2 is 1 Otherwise, it is not v-blank.300 0xCB ;D324 ; test bit-1 of A301 0x4F ;D325303 0xC2 ;D326 ; if bit-1 is not 0304 0x44 ;D327 ; GOTO not-v-blank305 0xD3 ;D328307 0xCB ;D329 ; test bit-0 of A308 0x47 ;D32A310 0xCA ;D32B ; if bit-0 is not 1311 0x44 ;D32C ; GOTO not-v-blank312 0xD3 ;D32D314 ;;; in v-blank mode316 ;; if v-blank-prev was 0,317 ;; increment frame-count319 0xFA ;D32E ; load v-blank-prev to A320 0x20 ;D32F321 0xD3 ;D330323 0xCB ;D331324 0x47 ;D332 ; test bit-0 of A326 0x20 ;D333 ; skip next section327 0x07 ;D334 ; if v-blank-prev was not zero329 ;; v-blank was 0, increment frame-count330 0xFA ;D335 ; load frame-count into A331 0x1F ;D336332 0xD3 ;D337334 0x3C ;D338 ; inc A336 0xEA ;D339 ; load A into frame-count337 0x1F ;D33A338 0xD3 ;D33B340 ;; set v-blank-prev to 1341 0x3E ;D33C ; load 1 into A342 0x01 ;D33D344 0xEA ;D33E ; load A into v-blank-prev345 0x20 ;D33F346 0xD3 ;D340348 0xC3 ;D341 ; GOTO input handling code349 0x4E ;D342350 0xD3 ;D343352 ;;; not in v-blank mode353 ;; set v-blank-prev to 0354 0x3E ;D344 ; load 0 into A355 0x00 ;D345357 0xEA ;D346 ; load A into v-blank-prev358 0x20 ;D347359 0xD3 ;D348361 0xC3 ;D349 ; return to beginning362 0x1D ;D34A363 0xD3 ;D34B365 0x00 ;D34C ; these are here366 0x00 ;D34D ; for glue369 ;;; calculate input number based on button presses370 0x18 ;D34E ; skip next 3 bytes371 0x03 ;D34F372 ;D350373 (Integer/parseInt "00100000" 2) ; select directional pad374 ;D351375 (Integer/parseInt "00010000" 2) ; select buttons376 0x00 ;D352 ; input-number378 ;; select directional pad, store low bits in B380 0xFA ;D353 ; load (D350) into A381 0x50 ;D354 -->382 0xD3 ;D355 --> D31F384 0xEA ;D356 ; load (A), which is385 0x00 ;D357 --> ; 00010000, into FF00386 0xFF ;D358 --> FF00388 0x06 ;D359389 ;D35A390 (Integer/parseInt "11110000" 2) ; "11110000" -> B391 0xFA ;D35B ; (FF00) -> A392 0x00 ;D35C393 0xFF ;D35D395 0xCB ;D35E ; swap nybbles on A396 0x37 ;D35F397 0xA0 ;D360 ; (AND A B) -> A398 0x47 ;D361 ; A -> B400 ;; select buttons store bottom bits in C402 0xFA ; ; load (D351) into A403 0x51 ; -->404 0xD3 ; --> D31F406 0xEA ; ; load (A), which is407 0x00 ; --> ; 00001000, into FF00408 0xFF ; --> FF00410 0x0E ;411 (Integer/parseInt "00001111" 2) ; "00001111" -> C413 0xFA ; ; (FF00) -> A414 0x00 ;415 0xFF ;417 0xA1 ; ; (AND A C) -> A418 0x4F ; ; A -> C420 ;; combine the B and C registers into the input number421 0x79 ; ; C -> A422 0xB0 ; ; (OR A B) -> A423 0x2F ; ; negate A425 0xEA ; ; store A into input-number426 0x52 ;427 0xD3 ;429 0xC3 ; ; return to beginning430 0x1D ;431 0xD3 ;432 ])434 (defn input-number []435 (-> (tick (mid-game))436 (IE! 0) ; disable interrupts437 (inject-item-assembly (input-number-assembly))))439 (defn test-input-number440 "Input freestyle buttons and observe the effects at the repl."441 []442 (set-state! (input-number))443 (dotimes [_ 90000] (step (view-memory @current-state 0xD352))))445 (defn write-memory-assembly []446 [0x18 ;D31D ; jump over447 0x02 ;D31E ; the next 2 bytes448 0x00 ;D31F ; frame-count449 0x00 ;D320 ; v-blank-prev451 0xFA ;D321452 0x41 ;D322 ; load (FF41) into A453 0xFF ;D323 ; this contains mode flags455 ;; if we're in v-blank, the bit-1 is 0456 ;; and bit-2 is 1 Otherwise, it is not v-blank.457 0xCB ;D324 ; test bit-1 of A458 0x4F ;D325460 0xC2 ;D326 ; if bit-1 is not 0461 0x44 ;D327 ; GOTO not-v-blank462 0xD3 ;D328464 0xCB ;D329 ; test bit-0 of A465 0x47 ;D32A467 0xCA ;D32B ; if bit-0 is not 1468 0x44 ;D32C ; GOTO not-v-blank469 0xD3 ;D32D471 ;;; in v-blank mode473 ;; if v-blank-prev was 0,474 ;; increment frame-count476 0xFA ;D32E ; load v-blank-prev to A477 0x20 ;D32F478 0xD3 ;D330480 0xCB ;D331481 0x47 ;D332 ; test bit-0 of A483 0x20 ;D333 ; skip next section484 0x07 ;D334 ; if v-blank-prev was not zero486 ;; v-blank was 0, increment frame-count487 0xFA ;D335 ; load frame-count into A488 0x1F ;D336489 0xD3 ;D337491 0x3C ;D338 ; inc A493 0xEA ;D339 ; load A into frame-count494 0x1F ;D33A495 0xD3 ;D33B497 ;; set v-blank-prev to 1498 0x3E ;D33C ; load 1 into A499 0x01 ;D33D501 0xEA ;D33E ; load A into v-blank-prev502 0x20 ;D33F503 0xD3 ;D340505 0xC3 ;D341 ; GOTO input handling code506 0x4E ;D342507 0xD3 ;D343509 ;;; not in v-blank mode510 ;; set v-blank-prev to 0511 0x3E ;D344 ; load 0 into A512 0x00 ;D345514 0xEA ;D346 ; load A into v-blank-prev515 0x20 ;D347516 0xD3 ;D348518 0xC3 ;D349 ; return to beginning519 0x1D ;D34A520 0xD3 ;D34B522 0x00 ;D34C ; these are here523 0x00 ;D34D ; for glue526 ;;; calculate input number based on button presses527 0x18 ;D34E ; skip next 3 bytes528 0x03 ;D34F529 ;D350530 (Integer/parseInt "00100000" 2) ; select directional pad531 ;D351532 (Integer/parseInt "00010000" 2) ; select buttons533 0x00 ;D352 ; input-number535 ;; select directional pad, store low bits in B537 0xFA ;D353 ; load (D350) into A538 0x50 ;D354 -->539 0xD3 ;D355 --> D31F541 0xEA ;D356 ; load (A), which is542 0x00 ;D357 --> ; 00010000, into FF00543 0xFF ;D358 --> FF00545 0x06 ;D359546 ;D35A547 (Integer/parseInt "11110000" 2) ; "11110000" -> B548 0xFA ;D35B ; (FF00) -> A549 0x00 ;D35C550 0xFF ;D35D552 0xCB ;D35E ; swap nybbles on A553 0x37 ;D35F554 0xA0 ;D360 ; (AND A B) -> A555 0x47 ;D361 ; A -> B557 ;; select buttons store bottom bits in C559 0xFA ;D362 ; load (D351) into A560 0x51 ;D363 -->561 0xD3 ;D364 --> D31F563 0xEA ;D365 ; load (A), which is564 0x00 ;D366 --> ; 00001000, into FF00565 0xFF ;D367 --> FF00567 0x0E ;D368568 ;D369569 (Integer/parseInt "00001111" 2) ; "00001111" -> C571 0xFA ;D36A ; (FF00) -> A572 0x00 ;D36B573 0xFF ;D36C575 0xA1 ;D36D ; (AND A C) -> A576 0x4F ;D36E ; A -> C578 ;; combine the B and C registers into the input number579 0x79 ;D36F ; C -> A580 0xB0 ;D370 ; (OR A B) -> A581 0x2F ;D371 ; negate A583 0xEA ;D372 ; store A into input-number584 0x52 ;D373585 0xD3 ;D374587 0xC3 ;D375 ; GOTO state machine588 ;;0x1D589 0x80 ;D376590 0xD3 ;D377592 0x00 ;D378593 0x00 ;D379594 0x00 ;D37A595 0x00 ;D37B ; these are here because596 0x00 ;D37C ; I messed up :(597 0x00 ;D37D598 0x00 ;D37E599 0x00 ;D37F601 ;; beginning of main state machine602 0x18 ;D380 ; Declaration of variables603 0x05 ;D381 ; 5 variables:604 0x00 ;D382 ; current-mode605 0x00 ;D383 ; bytes-left-to-write606 0x00 ;D384 ; unused607 0x00 ;D385 ; unused608 0x00 ;D386 ; unused611 ;; banch on current mode612 0xFA ;D387 ; load current-mode (0xD382)613 0x82 ;D388 ; into A614 0xD3 ;D389615 0x00 ;D38A618 ;; GOTO Mode 0 (input-mode) if current-mode is 0619 0xFE ;D38B620 0x00 ;D38C ; compare A with 0x00622 0xCA ;D38D ; goto Mode 0 if A == 0623 0xA8 ;D38E624 0xD3 ;D38F627 ;; GOTO Mode 1 (set-bytes) if current-mode is 1628 0xFE ;D390629 0x01 ;D391 ; compare A with 0x01631 0xCA ;D392632 0xB1 ;D393633 0xD3 ;D394 ; goto Mode 1 if A == 1635 0x00 ;D395636 0x00 ;D396637 0x00 ;D397638 0x00 ;D398639 0x00 ;D399640 0x00 ;D39A641 0x00 ;D39B642 0x00 ;D39C643 0x00 ;D39D644 0x00 ;D39E645 0x00 ;D39F646 0x00 ;D3A0647 0x00 ;D3A1648 0x00 ;D3A2649 0x00 ;D3A3650 0x00 ;D3A4651 ;; End of Mode checking, goto beginning652 0xC3 ;D3A5653 0x1D ;D3A6654 0xD3 ;D3A7657 ;; Mode 0 -- input-mode mode658 ;; means that we are waiting for a mode, so set the mode to659 ;; whatever is currently in input-number. If nothing is660 ;; entered, then the program stays in input-mode mode662 ;; set current-mode to input-number663 0xFA ;D3A8 ; load input-number (0xD352)664 0x52 ;D3A9 ; into A665 0xD3 ;D3AA667 0xEA ;D3AB ; load A into current-mode668 0x82 ;D3AC ; (0xD382)669 0xD3 ;D3AD671 0xC3 ;D3AE ; go back to beginning672 0x1D ;D3AF673 0xD3 ;D3B0674 ;; End Mode 0677 ;; Mode 1 -- input bytes mode678 ;; This is the header for writing things to memory679 ;; specifies the number of bytes to write.680 ;; Mode is auto advanced to Mode 2 after this mode681 ;; completes.683 ;; set bytes left to write to input-number,684 ;; set current-mode to 0x02.685 0xFA ;D3B1 ; load input-number (0xD352)686 0x52 ;D3B2 ; into A687 0xD3 ;D3B3689 0xEA ;D3B4 ; load A into bytes-left-to-write690 0x83 ;D3B5 ; (0xD383)691 0xD3 ;D3B6693 0x3E ;D3B7 ; load 0x02 to A694 0x02 ;D3B8696 0xEA ;D3B9 ; load A to current-mode697 0x82 ;D3BA ; advancing from Mode 1 to698 0xD3 ;D3BB ; Mode 2700 0xC3 ;D3BC ; go back to beginning701 0x1D ;D3BD702 0xD3 ;D3BE703 ;; End Mode 1705 0x00 ;D3BF706 0x00 ;D3C0707 0x00 ;D3C1708 0x00 ;D3C2709 0x00 ;D3C3710 0x00 ;D3C4711 0x00 ;D3C5712 0x00 ;D3C6713 0x00 ;D3C7714 0x00 ;D3C8715 0x00 ;D3C9716 0x00 ;D3CA717 0x00 ;D3CB718 0x00 ;D3CC719 0x00 ;D3CD720 0x00 ;D3CE721 0x00 ;D3CF722 0x00 ;D3D0723 0x00 ;D3D1724 0x00 ;D3D2725 0x00 ;D3D3726 0x00 ;D3D4727 0x00 ;D3D5728 0x00 ;D3D6733 0xC3 ; ; Complete Loop734 0x1D ;735 0xD3 ;739 ])742 (def frame-count 0xD31F)743 (def input 0xD352)744 (def current-mode 0xD382)746 (defn write-memory []747 (-> (tick (mid-game))748 (IE! 0) ; disable interrupts749 (inject-item-assembly (write-memory-assembly))))751 (defn test-write-memory []752 (set-state! (write-memory))753 (dorun754 (dotimes [_ 5000]755 (view-memory (step @current-state) current-mode))))