Mercurial > vba-clojure
view clojure/com/aurellem/assembly.clj @ 123:c9a280b8bd1c
saving progress.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 17 Mar 2012 00:29:34 -0500 |
parents | e85b53994fac |
children | f8dadd9478a5 |
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 ;D32D184 ;;; in v-blank mode185 ;; if v-blank-prev was 0,186 ;; increment frame-count188 0xFA ;D32E ; load v-blank-prev to A189 0x20 ;D32F190 0xD3 ;D330192 0xCB ;D331193 0x47 ;D332 ; test bit-0 of A195 0x20 ;D333 ; skip next section196 0x07 ;D334 ; if v-blank-prev was not zero198 ;; v-blank was 0, increment frame-count199 0xFA ;D335 ; load frame-count into A200 0x1F ;D336201 0xD3 ;D337203 0x3C ;D338 ; inc A205 0xEA ;D339 ; load A into frame-count206 0x1F ;D33A207 0xD3 ;D33B209 ;; set v-blank-prev to 1210 0x3E ;D33C ; load 1 into A211 0x01 ;D33D213 0xEA ;D33E ; load A into v-blank-prev214 0x20 ;D33F215 0xD3 ;D340217 0xC3 ;D341 ; return to beginning218 0x1D ;D342219 0xD3 ;D343221 ;;; not in v-blank mode222 ;; set v-blank-prev to 0223 0x3E ;D344 ; load 0 into A224 0x00 ;D345226 0xEA ;D346 ; load A into v-blank-prev227 0x20 ;D347228 0xD3 ;D348230 0xC3 ;D349 ; return to beginning231 0x1D ;D34A232 0xD3 ;D34B233 ])))235 (defn step-count-frames []236 (-> (read-down-button)237 (info)238 (tick) ;; skip over data section239 (info)240 (view-register "Register A" A)241 (tick) ;; load-data into A242 (view-register "Register A" A)243 (info)244 (view-memory 0xFF00)245 (tick) ;; load A into 0xFF00246 (view-memory 0xFF00)247 (info)248 (tick)249 (info)250 (tick)251 (info)252 (tick)253 (info)254 (tick)255 (info)256 (tick)257 (info)258 (tick)259 (print-inventory)))261 (defn test-count-frames []262 (= 255 (aget (memory ((apply comp (repeat 255 step))263 (count-frames)))264 0xD31F)))266 ;; specs for main bootstrap program267 ;; starts in "mode-select" mode268 ;; Each button press takes place in a single frame.269 ;; mode-select-mode takes one of the main buttons270 ;; which selects one of up to eight modes271 ;; mode 1 activated by the "A" button272 ;; the next two button presses indicates the start273 ;; memory location which to which the bootstrap274 ;; program will write.275 ;; This is done by using each of the eight buttons to276 ;; spell out an 8 bit number. The order of buttons is277 ;; [:d :u :l :r :start :select :b :a]278 ;; [:a :start :l] --> 00101001280 ;; the next button press determines how many bytes are to be281 ;; written, starting at the start position.283 ;; then, the actual bytes are entered and are written to the284 ;; start address in sequence.286 (defn input-number-assembly []287 [0x18 ;D31D ; jump over288 0x02 ;D31E ; the next 2 bytes289 0x00 ;D31F ; frame-count290 0x00 ;D320 ; v-blank-prev292 0xFA ;D321293 0x41 ;D322 ; load (FF41) into A294 0xFF ;D323 ; this contains mode flags296 ;; if we're in v-blank, the bit-1 is 0297 ;; and bit-2 is 1 Otherwise, it is not v-blank.298 0xCB ;D324 ; test bit-1 of A299 0x4F ;D325301 0xC2 ;D326 ; if bit-1 is not 0302 0x44 ;D327 ; GOTO not-v-blank303 0xD3 ;D328305 0xCB ;D329 ; test bit-0 of A306 0x47 ;D32A308 0xCA ;D32B ; if bit-0 is not 1309 0x44 ;D32C ; GOTO not-v-blank310 0xD3 ;D32D312 ;;; in v-blank mode314 ;; if v-blank-prev was 0,315 ;; increment frame-count317 0xFA ;D32E ; load v-blank-prev to A318 0x20 ;D32F319 0xD3 ;D330321 0xCB ;D331322 0x47 ;D332 ; test bit-0 of A324 0x20 ;D333 ; skip next section325 0x07 ;D334 ; if v-blank-prev was not zero327 ;; v-blank was 0, increment frame-count328 0xFA ;D335 ; load frame-count into A329 0x1F ;D336330 0xD3 ;D337332 0x3C ;D338 ; inc A334 0xEA ;D339 ; load A into frame-count335 0x1F ;D33A336 0xD3 ;D33B338 ;; set v-blank-prev to 1339 0x3E ;D33C ; load 1 into A340 0x01 ;D33D342 0xEA ;D33E ; load A into v-blank-prev343 0x20 ;D33F344 0xD3 ;D340346 0xC3 ;D341 ; GOTO input handling code347 0x4E ;D342348 0xD3 ;D343350 ;;; not in v-blank mode351 ;; set v-blank-prev to 0352 0x3E ;D344 ; load 0 into A353 0x00 ;D345355 0xEA ;D346 ; load A into v-blank-prev356 0x20 ;D347357 0xD3 ;D348359 0xC3 ;D349 ; return to beginning360 0x1D ;D34A361 0xD3 ;D34B363 0x00 ;D34C ; these are here364 0x00 ;D34D ; for glue367 ;;; calculate input number based on button presses368 0x18 ;D34E ; skip next 3 bytes369 0x03 ;D34F370 ;D350371 (Integer/parseInt "00100000" 2) ; select directional pad372 ;D351373 (Integer/parseInt "00010000" 2) ; select buttons374 0x00 ;D352 ; input-number376 ;; select directional pad, store low bits in B378 0xFA ;D353 ; load (D350) into A379 0x50 ;D354 -->380 0xD3 ;D355 --> D31F382 0xEA ;D356 ; load (A), which is383 0x00 ;D357 --> ; 00010000, into FF00384 0xFF ;D358 --> FF00386 0x06 ;D359387 ;D35A388 (Integer/parseInt "11110000" 2) ; "11110000" -> B389 0xFA ;D35B ; (FF00) -> A390 0x00 ;D35C391 0xFF ;D35D393 0xCB ;D35E ; swap nybbles on A394 0x37 ;D35F395 0xA0 ;D360 ; (AND A B) -> A396 0x47 ;D361 ; A -> B398 ;; select buttons store bottom bits in C400 0xFA ; ; load (D351) into A401 0x51 ; -->402 0xD3 ; --> D31F404 0xEA ; ; load (A), which is405 0x00 ; --> ; 00001000, into FF00406 0xFF ; --> FF00408 0x0E ;409 (Integer/parseInt "00001111" 2) ; "00001111" -> C411 0xFA ; ; (FF00) -> A412 0x00 ;413 0xFF ;415 0xA1 ; ; (AND A C) -> A416 0x4F ; ; A -> C418 ;; combine the B and C registers into the input number419 0x79 ; ; C -> A420 0xB0 ; ; (OR A B) -> A421 0x2F ; ; negate A423 0xEA ; ; store A into input-number424 0x52 ;425 0xD3 ;427 0xC3 ; ; return to beginning428 0x1D ;429 0xD3 ;430 ])432 (defn input-number []433 (-> (tick (mid-game))434 (IE! 0) ; disable interrupts435 (inject-item-assembly (input-number-assembly))))437 (defn test-input-number438 "Input freestyle buttons and observe the effects at the repl."439 []440 (set-state! (input-number))441 (dotimes [_ 90000] (step (view-memory @current-state 0xD352))))443 (defn write-memory-assembly []444 [445 ;; Main Timing Loop446 ;; Constantly check for v-blank and Trigger main state machine on447 ;; every transtion from v-blank to non-v-blank.449 0x18 ; D31D ; Variable declaration450 0x02 ; D31E451 0x00 ; D31F ; frame-count452 0x00 ; D320 ; v-blank-prev454 0xFA ; D321 ; load v-blank mode flags into A455 0x41 ; D322456 0xFF ; D323458 ;; Branch dependent on v-blank. v-blank happens when the last two459 ;; bits in A are "01"460 0xCB ; D324461 0x4F ; D325463 0xC2 ; D326 ; if bit-1 is not 0, then464 0x44 ; D327 ; GOTO non-v-blank.465 0xD3 ; D328467 0xCB ; D329468 0x47 ; D32A470 0xCA ; D32B ; if bit-0 is not 1, then471 0x44 ; D32C ; GOTO non-v-blank.472 0xD3 ; D32D474 ;; V-Blank475 ;; Activate state-machine if this is a transition event.477 0xFA ; D32E ; load v-bank-prev into A478 0x20 ; D32F479 0xD3 ; D330481 0xFE ; D331 ; compare A to 0.482 0x00 ; D332484 ;; set v-blank-prev to 1.485 0x3E ; D333 ; load 1 into A.486 0x01 ; D334488 0xEA ; D335 ; load A into v-blank-prev489 0x20 ; D336490 0xD3 ; D337492 ;; if v-blank-prev was 0, activate state-machine493 0xC2 ; D338 ; if v-blank-prev494 0x46 ; D339 ; was 0,495 0xD3 ; D33A ; GOTO state-machine497 0xC3 ; D33B498 0x1D ; D33C499 0xD3 ; D33D ; GOTO beginning500 ;; END V-blank502 ;; Non-V-Blank503 ;; Set v-blank-prev to 0504 0x3E ; D33E ; load 0 into A505 0x00 ; D33F507 0xEA ; D340 ; load A into v-blank-prev508 0x20 ; D341509 0xD3 ; D342511 0xC3 ; D343512 0x1D ; D344513 0xD3 ; D345 ; GOTO beginning514 ;; END Not-V-Blank516 ;; Main State Machine -- Input Section517 ;; This is called once every frame.518 ;; It collects input and uses it to drive the519 ;; state transitions.521 ;; Increment frame-count522 0xFA ; D346 ; load frame-count into A523 0x1F ; D347524 0xD3 ; D348526 0x3C ; D349 ; inc A528 0xEA ; D34A529 0x1F ; D34B ; load A into frame-count530 0xD3 ; D34C532 0x00 ; D34D ; glue :)535 ;;;;;;;;; BEGIN RLM DEBUG536 0xC3 ;; jump to beginning537 0x1D538 0xD3540 ;;;;;;;;; END RLM DEBUG543 0x18 ;D34E ; skip next 3 bytes544 0x03 ;D34F545 ;D350546 (Integer/parseInt "00100000" 2) ; select directional pad547 ;D351548 (Integer/parseInt "00010000" 2) ; select buttons549 0x00 ;D352 ; input-number551 ;; select directional pad; store low bits in B553 0xFA ;D353 ; load (D350) into A554 0x50 ;D354 -->555 0xD3 ;D355 --> D350557 0xEA ;D356 ; load (A), which is558 0x00 ;D357 --> ; 00010000, into FF00559 0xFF ;D358 --> FF00561 0x06 ;D359562 ;D35A563 (Integer/parseInt "11110000" 2) ; "11110000" -> B564 0xFA ;D35B ; (FF00) -> A565 0x00 ;D35C566 0xFF ;D35D568 0xCB ;D35E ; swap nybbles on A569 0x37 ;D35F570 0xA0 ;D360 ; (AND A B) -> A571 0x47 ;D361 ; A -> B573 ;; select buttons; store bottom bits in C575 0xFA ;D362 ; load (D351) into A576 0x51 ;D363 -->577 0xD3 ;D364 --> D351579 0xEA ;D365 ; load (A), which is580 0x00 ;D366 --> ; 00001000, into FF00581 0xFF ;D367 --> FF00583 0x0E ;D368584 ;D369585 (Integer/parseInt "00001111" 2) ; "00001111" -> C587 0xFA ;D36A ; (FF00) -> A588 0x00 ;D36B589 0xFF ;D36C591 0xA1 ;D36D ; (AND A C) -> A592 0x4F ;D36E ; A -> C594 ;; combine the B and C registers into the input number595 0x79 ;D36F ; C -> A596 0xB0 ;D370 ; (OR A B) -> A597 0x2F ;D371 ; negate A599 0xEA ;D372 ; store A into input-number600 0x52 ;D373601 0xD3 ;D374603 0xC3 ;D375 ; secret jump :)604 0x1D ;D376605 0xD3 ;D377606 0x00 ;D378607 0x00 ;D379608 0x00 ;D37A609 0x00 ;D37B ; these are here because610 0x00 ;D37C ; I messed up :(611 0x00 ;D37D612 0x00 ;D37E613 0x00 ;D37F615 ;; beginning of main state machine616 0x18 ;D380 ; Declaration of variables617 0x05 ;D381 ; 5 variables:618 0x00 ;D382 ; current-mode619 0x00 ;D383 ; bytes-to-write620 0x00 ;D384 ; start-point621 0x00 ;D385 ; unused622 0x00 ;D386 ; unused625 ;; banch on current mode626 0xFA ;D387 ; load current-mode (0xD382)627 0x82 ;D388 ; into A628 0xD3 ;D389629 0x00 ;D38A632 ;; GOTO Mode 0 (input-mode) if current-mode is 0633 0xFE ;D38B634 0x00 ;D38C ; compare A with 0x00636 0xCA ;D38D ; goto Mode 0 if A == 0637 0xA8 ;D38E638 0xD3 ;D38F640 ;; GOTO Mode 1 (set-length) if current-mode is 1641 0xFE ;D390642 0x01 ;D391 ; compare A with 0x01644 0xCA ;D392645 0xB1 ;D393646 0xD3 ;D394 ; goto Mode 1 if A == 1648 ;; GOTO Mode 2 (set-start-point) if current mode is 2649 0xFE ;D395650 0x02 ;D396 ; compare A with 0x02652 0xCA ;D397653 0xBF ;D398654 0xD3 ;D399 ; goto Mode 2 if A == 2656 0x00 ;D39A657 0x00 ;D39B658 0x00 ;D39C659 0x00 ;D39D660 0x00 ;D39E661 0x00 ;D39F662 0x00 ;D3A0663 0x00 ;D3A1664 0x00 ;D3A2665 0x00 ;D3A3666 0x00 ;D3A4667 ;; End of Mode checking, goto beginning668 0xC3 ;D3A5669 0x1D ;D3A6670 0xD3 ;D3A7673 ;; Mode 0 -- input-mode mode674 ;; means that we are waiting for a mode, so set the mode to675 ;; whatever is currently in input-number. If nothing is676 ;; entered, then the program stays in input-mode mode678 ;; set current-mode to input-number679 0xFA ;D3A8 ; load input-number (0xD352)680 0x52 ;D3A9 ; into A681 0xD3 ;D3AA683 0xEA ;D3AB ; load A into current-mode684 0x82 ;D3AC ; (0xD382)685 0xD3 ;D3AD687 0xC3 ;D3AE ; go back to beginning688 0x1D ;D3AF689 0xD3 ;D3B0690 ;; End Mode 0693 ;; Mode 1 -- set-length mode694 ;; This is the header for writing things to memory.695 ;; User specifies the number of bytes to write.696 ;; Mode is auto advanced to Mode 2 after this mode697 ;; completes.699 ;; Set bytes left to write to input-number;700 ;; set current-mode to 0x02.701 0xFA ;D3B1 ; load input-number (0xD352)702 0x52 ;D3B2 ; into A703 0xD3 ;D3B3705 0xEA ;D3B4 ; load A into bytes-left-to-write706 0x83 ;D3B5 ; (0xD383)707 0xD3 ;D3B6709 0x3E ;D3B7 ; load 0x02 into A.710 0x02 ;D3B8712 0xEA ;D3B9 ; load A to current-mode713 0x82 ;D3BA ; advancing from Mode 1 to714 0xD3 ;D3BB ; Mode 2716 0xC3 ;D3BC ; go back to beginning717 0x1D ;D3BD718 0xD3 ;D3BE719 ;; End Mode 1722 ;; Mode 2 -- set start-point mode723 ;; Final part of the header for writing things to memory.724 ;; User specifies the start location in RAM to which725 ;; data will be written.726 ;; Mode is auto advanced to Mode 3 after this mode completes.728 ;; Set start-point to input-number;729 ;; set current mode to 0x03.730 0xFA ;D3BF ; load input-number (0xD352)731 0x52 ;D3C0 ; into A732 0xD3 ;D3C1734 0xEA ;D3C2 ; load A into start-point735 0x84 ;D3C3 ; (0xD384)736 0xD3 ;D3C4738 0x3E ;D3C5 ; load 0x03 into A.739 0x03 ;D3C6741 0xEA ;D3C7 ; load A to current-mode,742 0x82 ;D3C8 ; advancing from Mode 2 to743 0xD3 ;D3C9 ; Mode 3.745 0xC3 ;D3CA ; go back to beginning746 0x1D ;D3CB747 0xD3 ;D3CC748 ;;End Mode 2750 0x00 ;D3CD751 0x00 ;D3CE752 0x00 ;D3CF753 0x00 ;D3D0754 0x00 ;D3D1755 0x00 ;D3D2756 0x00 ;D3D3757 0x00 ;D3D4758 0x00 ;D3D5759 0x00 ;D3D6761 ;; Mode 3 -- write bytes mode762 ;; This is where RAM manipulation happens.763 ;; User supplies bytes every frame, which are written764 ;; sequentially to769 0xC3 ; ; Complete Loop770 0x1D ;771 0xD3 ;775 ])779 (def frame-count 0xD31F)780 (def input 0xD352)781 (def current-mode 0xD382)783 (defn write-memory []784 (-> (tick (mid-game))785 (IE! 0) ; disable interrupts786 (inject-item-assembly (write-memory-assembly))))788 (defn test-write-memory []789 (set-state! (write-memory))790 (dorun791 (dotimes [_ 5000]792 (view-memory (step @current-state) current-mode))))794 (def bytes-to-write 0xD383)795 (def start-point 0xD384)797 (defn test-mode-2 []798 (->799 (write-memory)800 (view-memory frame-count)801 (step)802 (step [:a])803 (step [:b])804 (step [:start])805 (step [])806 (view-memory frame-count)))