Mercurial > vba-clojure
view clojure/com/aurellem/assembly.clj @ 122:e85b53994fac
working on proper timing for modes
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 16 Mar 2012 20:55:28 -0500 |
parents | 744de3427c05 |
children | c9a280b8bd1c |
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-to-write606 0x00 ;D384 ; start-point607 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 ;D38F626 ;; GOTO Mode 1 (set-length) if current-mode is 1627 0xFE ;D390628 0x01 ;D391 ; compare A with 0x01630 0xCA ;D392631 0xB1 ;D393632 0xD3 ;D394 ; goto Mode 1 if A == 1634 ;; GOTO Mode 2 (set-start-point) if current mode is 2635 0xFE ;D395636 0x02 ;D396 ; compare A with 0x02638 0xCA ;D397639 0xBF ;D398640 0xD3 ;D399 ; goto Mode 2 if A == 2642 0x00 ;D39A643 0x00 ;D39B644 0x00 ;D39C645 0x00 ;D39D646 0x00 ;D39E647 0x00 ;D39F648 0x00 ;D3A0649 0x00 ;D3A1650 0x00 ;D3A2651 0x00 ;D3A3652 0x00 ;D3A4653 ;; End of Mode checking, goto beginning654 0xC3 ;D3A5655 0x1D ;D3A6656 0xD3 ;D3A7659 ;; Mode 0 -- input-mode mode660 ;; means that we are waiting for a mode, so set the mode to661 ;; whatever is currently in input-number. If nothing is662 ;; entered, then the program stays in input-mode mode664 ;; set current-mode to input-number665 0xFA ;D3A8 ; load input-number (0xD352)666 0x52 ;D3A9 ; into A667 0xD3 ;D3AA669 0xEA ;D3AB ; load A into current-mode670 0x82 ;D3AC ; (0xD382)671 0xD3 ;D3AD673 0xC3 ;D3AE ; go back to beginning674 0x1D ;D3AF675 0xD3 ;D3B0676 ;; End Mode 0679 ;; Mode 1 -- set-length mode680 ;; This is the header for writing things to memory.681 ;; User specifies the number of bytes to write.682 ;; Mode is auto advanced to Mode 2 after this mode683 ;; completes.685 ;; Set bytes left to write to input-number;686 ;; set current-mode to 0x02.687 0xFA ;D3B1 ; load input-number (0xD352)688 0x52 ;D3B2 ; into A689 0xD3 ;D3B3691 0xEA ;D3B4 ; load A into bytes-left-to-write692 0x83 ;D3B5 ; (0xD383)693 0xD3 ;D3B6695 0x3E ;D3B7 ; load 0x02 into A.696 0x02 ;D3B8698 0xEA ;D3B9 ; load A to current-mode699 0x82 ;D3BA ; advancing from Mode 1 to700 0xD3 ;D3BB ; Mode 2702 0xC3 ;D3BC ; go back to beginning703 0x1D ;D3BD704 0xD3 ;D3BE705 ;; End Mode 1708 ;; Mode 2 -- set start-point mode709 ;; Final part of the header for writing things to memory.710 ;; User specifies the start location in RAM to which711 ;; data will be written.712 ;; Mode is auto advanced to Mode 3 after this mode completes.714 ;; Set start-point to input-number;715 ;; set current mode to 0x03.716 0xFA ;D3BF ; load input-number (0xD352)717 0x52 ;D3C0 ; into A718 0xD3 ;D3C1720 0xEA ;D3C2 ; load A into start-point721 0x84 ;D3C3 ; (0xD384)722 0xD3 ;D3C4724 0x3E ;D3C5 ; load 0x03 into A.725 0x03 ;D3C6727 0xEA ;D3C7 ; load A to current-mode,728 0x82 ;D3C8 ; advancing from Mode 2 to729 0xD3 ;D3C9 ; Mode 3.731 0xC3 ;D3CA ; go back to beginning732 0x1D ;D3CB733 0xD3 ;D3CC734 ;;End Mode 2736 0x00 ;D3CD737 0x00 ;D3CE738 0x00 ;D3CF739 0x00 ;D3D0740 0x00 ;D3D1741 0x00 ;D3D2742 0x00 ;D3D3743 0x00 ;D3D4744 0x00 ;D3D5745 0x00 ;D3D6747 ;; Mode 3 -- write bytes mode748 ;; This is where RAM manipulation happens.749 ;; User supplies bytes every frame, which are written750 ;; sequentially to755 0xC3 ; ; Complete Loop756 0x1D ;757 0xD3 ;761 ])764 (def frame-count 0xD31F)765 (def input 0xD352)766 (def current-mode 0xD382)768 (defn write-memory []769 (-> (tick (mid-game))770 (IE! 0) ; disable interrupts771 (inject-item-assembly (write-memory-assembly))))773 (defn test-write-memory []774 (set-state! (write-memory))775 (dorun776 (dotimes [_ 5000]777 (view-memory (step @current-state) current-mode))))779 (def bytes-to-write 0xD383)780 (def start-point 0xD384)782 (defn test-mode-2 []783 (->784 (write-memory)785 (view-memory bytes-to-write)786 (view-memory start-point)787 (step)788 (step [:a])789 (step [:b])790 (step [:start])791 (step [])792 (view-memory bytes-to-write)793 (view-memory start-point)))