Mercurial > vba-clojure
view clojure/com/aurellem/gb/assembly.clj @ 293:4a0dbaed7078
preliminary idea for a better pre-bootstrapping program.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Fri, 30 Mar 2012 18:14:14 -0500 (2012-03-30) |
parents | 2e751984b42d |
children |
line wrap: on
line source
1 (ns com.aurellem.gb.assembly2 (:use (com.aurellem.gb gb-driver vbm util items))3 (:import [com.aurellem.gb.gb_driver SaveState]))5 (defn inject-assembly6 ([^SaveState state7 program-counter registers8 assembly-code]9 (let [scratch-memory (memory state)]10 ;; inject assembly code11 (dorun (map (fn [index val]12 (aset scratch-memory index val))13 (range program-counter14 (+ program-counter (count assembly-code)))15 assembly-code))16 (-> state17 (write-memory! scratch-memory)18 (write-registers! registers)19 (PC! program-counter)))))21 (defn inject-item-assembly22 ([^SaveState state assembly-code]23 (inject-assembly state (inc item-list-start)24 (registers state)25 assembly-code))26 ([assembly-code]27 (inject-item-assembly @current-state assembly-code)))29 (defn run-assembly30 ([info-fn assembly n]31 (let [final-state32 (reduce (fn [state _]33 (tick (info-fn state)))34 (inject-item-assembly35 (mid-game) assembly)36 (range n))]37 final-state))38 ([assembly n]39 (run-assembly d-tick assembly n)))41 (def buttons-port 0xFF00)43 (defn trace [state]44 (loop [program-counters [(first (registers @current-state)) ]45 opcodes [(aget (memory @current-state) (PC @current-state))]]46 (let [frame-boundary?47 (com.aurellem.gb.Gb/tick)]48 (if frame-boundary?49 [program-counters opcodes]50 (recur51 (conj program-counters52 (first (registers @current-state)))53 (conj opcodes54 (aget (memory @current-state)55 (PC @current-state))))))))57 (defn print-trace [state n]58 (let [[program-counters opcodes] (trace state)]59 (dorun (map (fn [pc op] (println (format "%04X: 0x%02X" pc op)))60 (take n program-counters)61 (take n opcodes)))))63 (defn good-trace []64 (-> (mid-game) (tick) (IE! 0)65 (set-inv-mem [0x00 0x00 0X00 0x00])66 (PC! item-list-start)(print-interrupt)67 (d-tick) (tick) (d-tick) (tick) (d-tick)))69 (defn read-down-button []70 (-> (tick (mid-game))71 (IE! 0) ; disable interrupts72 (inject-item-assembly73 ;; write 00010000 to 0xFF00 to select joypad74 [0x18 ;D31D ; jump over75 0x01 ;D31E ; the next 8 bits76 ;D31F77 (Integer/parseInt "00100000" 2) ; data section79 0xFA ;D320 ; load (D31F) into A80 0x1F ;D321 -->81 0xD3 ;D322 --> D31F83 0xEA ;D323 ; load (A), which is84 0x00 ;D324 --> ; 00010000, into FF0085 0xFF ;D325 --> FF0087 0x18 ;D326 ; this is the place where88 0x01 ;D327 ; we will store whether89 0x00 ;D328 ; "down" is pressed.91 0xFA ;D329 ; (FF00) -> A92 0x00 ;D32A93 0xFF ;D32B95 0xCB ;D32C ; Test whether "down"96 0x5F ;D32D ; is pressed.98 0x28 ;D32E ; if down is pressed,99 0x03 ;D32F ; skip the next section100 ; of code.101 ;; down-is-not-pressed102 0xC3 ;D330103 0x1D ;D331 ; return to beginning104 0xD3 ;D332106 ;; down-is-pressed107 0xEA ;D334 ; write A to D328 if108 0x28 ;D335 ; "down" was pressed109 0xD3 ;D336111 0xC3 ;D330112 0x1D ;D331 ; return to beginning113 0xD3 ;D332114 ])))116 (defn test-read-down []117 (= (view-memory (step (step (read-down-button) [:d])) 0xD328)118 (view-memory (step (step (read-down-button))) 0xD328)))120 (defn count-frames []121 (-> (tick (mid-game))122 (IE! 0) ; disable interrupts123 (inject-item-assembly124 [0x18 ;D31D ; jump over125 0x02 ;D31E ; the next 2 bytes126 0x00 ;D31F ; frame-count127 0x00 ;D320 ; v-blank-prev129 0xFA ;D321130 0x41 ;D322 ; load (FF41) into A131 0xFF ;D323 ; this contains mode flags133 ;; if we're in v-blank, the bit-1 is 0134 ;; and bit-2 is 1 Otherwise, it is not v-blank.135 0xCB ;D324 ; test bit-1 of A136 0x4F ;D325138 0xC2 ;D326 ; if bit-1 is not 0139 0x44 ;D327 ; GOTO not-v-blank140 0xD3 ;D328142 0xCB ;D329 ; test bit-0 of A143 0x47 ;D32A145 0xCA ;D32B ; if bit-0 is not 1146 0x44 ;D32C ; GOTO not-v-blank147 0xD3 ;D32D148 ;;; in v-blank mode149 ;; if v-blank-prev was 0,150 ;; increment frame-count152 0xFA ;D32E ; load v-blank-prev to A153 0x20 ;D32F154 0xD3 ;D330156 0xCB ;D331157 0x47 ;D332 ; test bit-0 of A159 0x20 ;D333 ; skip next section160 0x07 ;D334 ; if v-blank-prev was not zero162 ;; v-blank was 0, increment frame-count163 0xFA ;D335 ; load frame-count into A164 0x1F ;D336165 0xD3 ;D337167 0x3C ;D338 ; inc A169 0xEA ;D339 ; load A into frame-count170 0x1F ;D33A171 0xD3 ;D33B173 ;; set v-blank-prev to 1174 0x3E ;D33C ; load 1 into A175 0x01 ;D33D177 0xEA ;D33E ; load A into v-blank-prev178 0x20 ;D33F179 0xD3 ;D340181 0xC3 ;D341 ; return to beginning182 0x1D ;D342183 0xD3 ;D343185 ;;; not in v-blank mode186 ;; set v-blank-prev to 0187 0x3E ;D344 ; load 0 into A188 0x00 ;D345190 0xEA ;D346 ; load A into v-blank-prev191 0x20 ;D347192 0xD3 ;D348194 0xC3 ;D349 ; return to beginning195 0x1D ;D34A196 0xD3 ;D34B197 ])))199 (defn step-count-frames []200 (-> (read-down-button)201 (d-tick)202 (tick) ;; skip over data section203 (d-tick)204 (view-register "Register A" A)205 (tick) ;; load-data into A206 (view-register "Register A" A)207 (d-tick)208 (view-memory 0xFF00)209 (tick) ;; load A into 0xFF00210 (view-memory 0xFF00)211 (d-tick)212 (tick)213 (d-tick)214 (tick)215 (d-tick)216 (tick)217 (d-tick)218 (tick)219 (d-tick)220 (tick)221 (d-tick)222 (tick)223 (print-inventory)))225 (defn test-count-frames []226 (= 255 (aget (memory ((apply comp (repeat 255 step))227 (count-frames)))228 0xD31F)))230 ;; specs for main bootstrap program231 ;; starts in "mode-select" mode232 ;; Each button press takes place in a single frame.233 ;; mode-select-mode takes one of the main buttons234 ;; which selects one of up to eight modes235 ;; mode 1 activated by the "A" button236 ;; the next two button presses indicates the start237 ;; memory location which to which the bootstrap238 ;; program will write.239 ;; This is done by using each of the eight buttons to240 ;; spell out an 8 bit number. The order of buttons is241 ;; [:d :u :l :r :start :select :b :a]242 ;; [:a :start :l] --> 00101001244 ;; the next button press determines how many bytes are to be245 ;; written, starting at the start position.247 ;; then, the actual bytes are entered and are written to the248 ;; start address in sequence.250 (defn input-number-assembly []251 [0x18 ;D31D ; jump over252 0x02 ;D31E ; the next 2 bytes253 0x00 ;D31F ; frame-count254 0x00 ;D320 ; v-blank-prev256 0xFA ;D321257 0x41 ;D322 ; load (FF41) into A258 0xFF ;D323 ; this contains mode flags260 ;; if we're in v-blank, the bit-1 is 0261 ;; and bit-2 is 1 Otherwise, it is not v-blank.262 0xCB ;D324 ; test bit-1 of A263 0x4F ;D325265 0xC2 ;D326 ; if bit-1 is not 0266 0x44 ;D327 ; GOTO not-v-blank267 0xD3 ;D328269 0xCB ;D329 ; test bit-0 of A270 0x47 ;D32A272 0xCA ;D32B ; if bit-0 is not 1273 0x44 ;D32C ; GOTO not-v-blank274 0xD3 ;D32D276 ;;; in v-blank mode278 ;; if v-blank-prev was 0,279 ;; increment frame-count281 0xFA ;D32E ; load v-blank-prev to A282 0x20 ;D32F283 0xD3 ;D330285 0xCB ;D331286 0x47 ;D332 ; test bit-0 of A288 0x20 ;D333 ; skip next section289 0x07 ;D334 ; if v-blank-prev was not zero291 ;; v-blank was 0, increment frame-count292 0xFA ;D335 ; load frame-count into A293 0x1F ;D336294 0xD3 ;D337296 0x3C ;D338 ; inc A298 0xEA ;D339 ; load A into frame-count299 0x1F ;D33A300 0xD3 ;D33B302 ;; set v-blank-prev to 1303 0x3E ;D33C ; load 1 into A304 0x01 ;D33D306 0xEA ;D33E ; load A into v-blank-prev307 0x20 ;D33F308 0xD3 ;D340310 0xC3 ;D341 ; GOTO input handling code311 0x4E ;D342312 0xD3 ;D343314 ;;; not in v-blank mode315 ;; set v-blank-prev to 0316 0x3E ;D344 ; load 0 into A317 0x00 ;D345319 0xEA ;D346 ; load A into v-blank-prev320 0x20 ;D347321 0xD3 ;D348323 0xC3 ;D349 ; return to beginning324 0x1D ;D34A325 0xD3 ;D34B327 0x00 ;D34C ; these are here328 0x00 ;D34D ; for glue331 ;;; calculate input number based on button presses332 0x18 ;D34E ; skip next 3 bytes333 0x03 ;D34F334 ;D350335 (Integer/parseInt "00100000" 2) ; select directional pad336 ;D351337 (Integer/parseInt "00010000" 2) ; select buttons338 0x00 ;D352 ; input-number340 ;; select directional pad, store low bits in B342 0xFA ;D353 ; load (D350) into A343 0x50 ;D354 -->344 0xD3 ;D355 --> D31F346 0xEA ;D356 ; load A, which is347 0x00 ;D357 --> ; 00010000, into FF00348 0xFF ;D358 --> FF00350 0x06 ;D359351 ;D35A352 (Integer/parseInt "11110000" 2) ; "11110000" -> B353 0xFA ;D35B ; (FF00) -> A354 0x00 ;D35C355 0xFF ;D35D357 0xCB ;D35E ; swap nybbles on A358 0x37 ;D35F359 0xA0 ;D360 ; (AND A B) -> A360 0x47 ;D361 ; A -> B362 ;; select buttons store bottom bits in C364 0xFA ; ; load (D351) into A365 0x51 ; -->366 0xD3 ; --> D31F368 0xEA ; ; load (A), which is369 0x00 ; --> ; 00001000, into FF00370 0xFF ; --> FF00372 0x0E ;373 (Integer/parseInt "00001111" 2) ; "00001111" -> C375 0xFA ; ; (FF00) -> A376 0x00 ;377 0xFF ;379 0xA1 ; ; (AND A C) -> A380 0x4F ; ; A -> C382 ;; combine the B and C registers into the input number383 0x79 ; ; C -> A384 0xB0 ; ; (OR A B) -> A385 0x2F ; ; negate A387 0xEA ; ; store A into input-number388 0x52 ;389 0xD3 ;391 0xC3 ; ; return to beginning392 0x1D ;393 0xD3 ;394 ])398 (defn input-number []399 (-> (tick (mid-game))400 (IE! 0) ; disable interrupts401 (inject-item-assembly (input-number-assembly))))403 (defn test-input-number404 "Input freestyle buttons and observe the effects at the repl."405 []406 (set-state! (input-number))407 (dotimes [_ 90000] (step (view-memory @current-state 0xD352))))414 (defn write-memory-assembly*415 "A program for altering in-game memory by pressing buttons."416 []417 [418 ;; 0xF3 ; stop interrupts419 ;; --------- CLEANUP420 0xAF ; zero A [D31E]421 0x57 ; A->D; makes D=0.423 ;; --------- FRAME METRONOME424 0xF1 ;; pop AF (vblank prev) [D320]426 0x2F ;; invert A427 0x4F ;; A -> C429 0xF0 ;; copy STAT into A430 0x41432 0x47 ;; A->B433 0x1F ;; rotate A right434 0x2F ;; complement A435 0xA0 ;; A & B --> A437 0xF5 ;; push AF (vbprev)439 0xA1 ;; A & C --> A. Now A_0 contains "increment?"441 0x4F442 0xCB ;; test bit 0 of C. sadly, this result can't be reused below.443 0x41445 0x28 ;; end frame (JUMP) if A_0 = 0.446 0xF3 ;; TODO: set jump length448 ;; -------- GET BUTTON INPUT450 ;; prepare to select bits452 0x3E ;; load 0x20 into A, to measure dpad453 0x20455 0x06 ;; load 0x00 into B456 0x00 ;; to initialize for "OR" loop458 0xE0 ;; load A into [FF00] ;; start of OR loop [D33C]459 0x00461 0xF0 ;; load A from [FF00]462 0x00464 0xE6 ;; bitmask 00001111465 0x0F467 0xB0 ;; A or B --> A469 0xCB ;; check bit 0 of C470 0x41472 0x28 ;; JUMP forward if Z=0473 0x08475 0x47 ;; A -> B476 0xCB ;; swap B nybbles477 0x30479 0x3E ;; load 0x10 into A, to measure btns480 0x10482 0x18 ;; JUMP back to "load A into [FF00]"483 0xF0486 ;; ------ TAKE ACTION BASED ON USER INPUT488 ;; "input mode"489 ;; mode 0x00 : select mode490 ;; mode 0x08 : select bytes-to-write491 ;; mode 0x10 : select hi-bit492 ;; mode 0x18 : select lo-bit494 ;; "output mode"495 ;; mode 0x20 : write bytes496 ;; mode 0xFF : jump PC499 ;; registers500 ;; D : mode select501 ;; E : count of bytes to write502 ;; H : address-high503 ;; L : address-low505 ;; now A contains the pressed keys506 0x2F ; complement A, by request. [D34F]508 0x47 ; A->B ;; now B contains the pressed keys510 0xCB ; test bit 5 of D (are we in o/p mode?)511 0x6A512 0x28 ; if test == 0, skip this o/p section513 0x13 ; JUMP515 0xCB ; else, test bit 0 of D (fragile; are we in pc mode?)516 0x42517 0x28 ; if test == 0, skip the following command518 0x01520 ;; output mode I: moving the program counter521 0xE9 ; ** move PC to (HL)523 ;; output mode II: writing bytes524 0xAF ; zero A525 0xBB ; compare count to zero. finished writing?526 0x28 ; if we are finished, jump back to cleanup527 0x00 ; TODO: set jump length backwards.529 ;; continue writing bytes530 0x78 ;; B->A531 0x22 ;; copy A to (HL) and increment HL.532 0x18 ;; end frame. [goto D31F]533 0xB6 ;; JUMP536 ;; ---- end of o/p section538 ;; i/p mode539 ;; adhere to the mode discipline:540 ;; D must be one of 0x00 0x08 0x10 0x18.542 0x3E ;; load the constant 57 into A. [D369]543 0x57544 0x82 ;; add the mode to A545 0xEA ;; store A into "thing to execute"546 0x74 ;; ABSOLUTE LOCATION547 0xD3 ;; ABSOLUTE LOCATION549 0x3E ;; load the constant 8 into A550 0x08551 0x82 ;; add the mode to A553 0x57 ;; store the incremented mode into D554 0x78 ;; B->A; now A contains the pressed keys556 0x00 ;; var: thing to execute [D374]558 0x18 ;; end frame559 0xA8 ;; JUMP560 ]561 )563 (defn write-mem-dyl []564 (-> (tick (mid-game))565 (IE! 0)566 (inject-item-assembly (write-memory-assembly*))))568 (defn ntick [s n]569 (if (zero? n) s570 (do571 (set-state! s)572 (dorun (dotimes [_ n]573 (com.aurellem.gb.Gb/tick)))574 (update-state))))577 (defn find-frame-shift [state]578 ;;(restart!)579 (set-state! state)580 (loop [n 0]581 (if (>= (first (registers)) 0xD32D)582 (do (println n)583 (update-state))584 (do585 (com.aurellem.gb.Gb/tick)586 (recur (inc n) )))))588 (defn demo-assembly [n]589 (repeat n 0x00))592 (defn find-frame-shift* [state]593 (set-state! state)594 (loop []595 (com.aurellem.gb.Gb/tick)596 ;;(println (first (registers)))597 (if (>= (first (registers)) 0xD32D)598 (update-state)599 (recur))))602 (defn dylan**603 ([k]604 (->605 (tick (mid-game))606 (inject-item-assembly(write-memory-assembly*))607 ;;(find-frame-shift)608 (ntick k)609 (d-tick)610 (view-register "A" A)611 (view-register "B" B)612 (view-register "D" D)613 (view-register "E" E)614 (view-register "F" F)615 (#(do (println) %))616 ))617 ([] (dylan** 0)))622 (defn dylan* []623 (->624 (write-mem-dyl)626 (tick)627 (tick)628 (tick)629 (tick)630 (tick)631 (tick)632 (tick)633 (tick)634 (tick)635 (tick)636 (tick)637 (tick)638 (tick)639 (tick)640 (tick)641 (tick)642 (tick)643 (tick)644 (tick)645 (tick)646 (tick)647 (tick)648 (tick)649 (tick)650 (tick)651 (tick)652 (tick)653 (tick)654 (tick)655 (tick)656 (tick)657 (tick)658 (tick)659 (tick)660 (tick)661 (tick)663 ;;(view-memory 0xD374)664 (tick)665 (tick)666 (tick)667 (tick)668 (tick)669 (tick)670 (tick)671 (tick)672 (tick)673 (tick)674 (tick)675 (tick)676 (tick)677 (tick)678 (tick)679 ;;(view-memory 0xD374)680 (d-tick)682 (view-register "A" A)683 (view-register "B" B)684 (view-register "C" C))686 )689 (defn dylan []690 (->691 (write-mem-dyl)692 (tick)693 (tick)694 (tick)695 (tick)696 (tick)697 (tick)698 (tick)699 (tick)700 (tick)701 (tick)702 (tick)703 (tick)704 (tick)705 (tick)706 (tick) ;; first loop709 (tick)710 (tick)711 (tick)712 (tick)713 (tick)714 (tick)715 (tick)716 (tick)717 (tick)718 (tick)719 (tick)720 (tick)721 (tick) ;; dpad bits723 (tick)724 (tick)725 (tick)726 (tick)727 (tick)728 (tick)729 (tick)730 (tick)731 (d-tick)735 (view-register "A" A)736 (view-register "B" B)737 (view-register "C" C)739 ))744 (defn d2 []745 (->746 (write-mem-dyl)747 (view-memory 0xD31F)748 step step step step step749 (view-memory 0xD31F)))770 (defn write-memory-assembly []771 [772 ;; Main Timing Loop773 ;; Constantly check for v-blank and Trigger main state machine on774 ;; every transtion from v-blank to non-v-blank.776 0x18 ; D31D ; Variable declaration777 0x02 ; D31E778 0x00 ; D31F ; frame-count779 0x00 ; D320 ; v-blank-prev781 0xF0 ; D321 ; load v-blank mode flags into A782 0x41783 0x00786 ;; Branch dependent on v-blank. v-blank happens when the last two787 ;; bits in A are "01"788 0xCB ; D324789 0x4F ; D325791 0xC2 ; D326 ; if bit-1 is not 0, then792 0x3E ; D327 ; GOTO non-v-blank.793 0xD3 ; D328795 0xCB ; D329796 0x47 ; D32A798 0xCA ; D32B ; if bit-0 is not 1, then799 0x3E ; D32C ; GOTO non-v-blank.800 0xD3 ; D32D802 ;; V-Blank803 ;; Activate state-machine if this is a transition event.805 0xFA ; D32E ; load v-bank-prev into A806 0x20 ; D32F807 0xD3 ; D330809 0xFE ; D331 ; compare A to 0. >--------\810 0x00 ; D332 \811 ; |812 ;; set v-blank-prev to 1. |813 0x3E ; D333 ; load 1 into A. |814 0x01 ; D334 |815 ; |816 0xEA ; D335 ; load A into v-blank-prev |817 0x20 ; D336 |818 0xD3 ; D337 |819 ; /820 ;; if v-blank-prev was 0, activate state-machine <------/821 0xCA ; D338 ; if v-blank-prev822 0x46 ; D339 ; was 0,823 0xD3 ; D33A ; GOTO state-machine825 0xC3 ; D33B826 0x1D ; D33C827 0xD3 ; D33D ; GOTO beginning828 ;; END V-blank830 ;; Non-V-Blank831 ;; Set v-blank-prev to 0832 0x3E ; D33E ; load 0 into A833 0x00 ; D33F835 0xEA ; D340 ; load A into v-blank-prev836 0x20 ; D341837 0xD3 ; D342839 0xC3 ; D343840 0x1D ; D344841 0xD3 ; D345 ; GOTO beginning842 ;; END Not-V-Blank845 ;; Main State Machine -- Input Section846 ;; This is called once every frame.847 ;; It collects input and uses it to drive the848 ;; state transitions.850 ;; Increment frame-count851 0xFA ; D346 ; load frame-count into A852 0x1F ; D347853 0xD3 ; D348855 0x3C ; D349 ; inc A857 0xEA ; D34A858 0x1F ; D34B ; load A into frame-count859 0xD3 ; D34C861 0x00 ; D34D ; glue :)863 0x18 ;D34E ; skip next 3 bytes864 0x03 ;D34F865 ;D350866 (Integer/parseInt "00100000" 2) ; select directional pad867 ;D351868 (Integer/parseInt "00010000" 2) ; select buttons869 0x00 ;D352 ; input-number871 ;; select directional pad; store low bits in B873 0xFA ;D353 ; load (D350) into A874 0x50 ;D354 -->875 0xD3 ;D355 --> D350877 0xE0 ;D356 ; load (A), which is878 0x00 ;D357 --> ; 00010000, into FF00879 0x00 ;D358 --> FF00 ;; NO-OP881 0x06 ;D359882 ;D35A883 (Integer/parseInt "11110000" 2) ; "11110000" -> B884 0xF0 ;D35B ; (FF00) -> A885 0x00 ;D35C886 0x00 ;D35D ;; NO-OP888 0xCB ;D35E ; swap nybbles on A889 0x37 ;D35F890 0xA0 ;D360 ; (AND A B) -> A891 0x47 ;D361 ; A -> B893 ;; select buttons; store bottom bits in C895 0xFA ;D362 ; load (D351) into A896 0x51 ;D363 -->897 0xD3 ;D364 --> D351899 0xE0 ;D365 ; load (A), which is900 0x00 ;D366 --> ; 00001000, into FF00901 0x00 ;D367 --> FF00 ;; NO-OP903 0x0E ;D368904 ;D369905 (Integer/parseInt "00001111" 2) ; "00001111" -> C907 0xF0 ;D36A ; (FF00) -> A908 0x00 ;D36B909 0x00 ;D36C911 0xA1 ;D36D ; (AND A C) -> A912 0x4F ;D36E ; A -> C914 ;; combine the B and C registers into the input number915 0x79 ;D36F ; C -> A916 0xB0 ;D370 ; (OR A B) -> A917 0x2F ;D371 ; negate A919 0xEA ;D372 ; store A into input-number920 0x52 ;D373921 0xD3 ;D374923 0x00 ;D375924 0x00 ;D376925 0x00 ;D377926 0x00 ;D378927 0x00 ;D379928 0x00 ;D37A929 0x00 ;D37B ; these are here because930 0x00 ;D37C ; I messed up :(931 0x00 ;D37D932 0x00 ;D37E933 0x00 ;D37F935 ;; beginning of main state machine936 0x18 ;D380 ; Declaration of variables937 0x05 ;D381 ; 5 variables:938 0x00 ;D382 ; current-mode939 0x00 ;D383 ; bytes-to-write940 0x00 ;D384 ; bytes-written941 0x00 ;D385 ; start-point-high942 0x00 ;D386 ; start-point-low945 ;; banch on current mode946 0xFA ;D387 ; load current-mode (0xD382)947 0x82 ;D388 ; into A948 0xD3 ;D389949 0x00 ;D38A952 ;; GOTO Mode 0 (input-mode) if current-mode is 0953 0xFE ;D38B954 0x00 ;D38C ; compare A with 0x00956 0xCA ;D38D ; goto Mode 0 if A == 0957 0xA8 ;D38E958 0xD3 ;D38F960 ;; GOTO Mode 1 (set-length) if current-mode is 1961 0xFE ;D390962 0x01 ;D391 ; compare A with 0x01964 0xCA ;D392965 0xB1 ;D393966 0xD3 ;D394 ; goto Mode 1 if A == 1968 ;; GOTO Mode 2 (set-start-point-high) if current mode is 2969 0xFE ;D395970 0x02 ;D396 ; compare A with 0x02972 0xCA ;D397973 0xBF ;D398974 0xD3 ;D399 ; goto Mode 2 if A == 2976 ;; GOTO Mode 3 (set-start-point-low) if current mode is 3977 0xFE ;D39A978 0x03 ;D39B980 0xCA ;D39C981 0xCD ;D39D982 0xD3 ;D39E ; goto Mode 3 if A == 3984 ;; GOTO Mode 4 (write-memory) if current mode is 4985 0xFE ;D39F986 0x04 ;D3A0988 0xCA ;D3A1989 0xDB ;D3A2990 0xD3 ;D3A3992 0x00 ;D3A4993 ;; End of Mode checking, goto beginning994 0xC3 ;D3A5995 0x1D ;D3A6996 0xD3 ;D3A7999 ;; Mode 0 -- input-mode mode1000 ;; means that we are waiting for a mode, so set the mode to1001 ;; whatever is currently in input-number. If nothing is1002 ;; entered, then the program stays in input-mode mode1004 ;; set current-mode to input-number1005 0xFA ;D3A8 ; load input-number (0xD352)1006 0x52 ;D3A9 ; into A1007 0xD3 ;D3AA1009 0xEA ;D3AB ; load A into current-mode1010 0x82 ;D3AC ; (0xD382)1011 0xD3 ;D3AD1013 0xC3 ;D3AE ; go back to beginning1014 0x1D ;D3AF1015 0xD3 ;D3B01016 ;; End Mode 01019 ;; Mode 1 -- set-length mode1020 ;; This is the header for writing things to memory.1021 ;; User specifies the number of bytes to write.1022 ;; Mode is auto advanced to Mode 2 after this mode1023 ;; completes.1025 ;; Set bytes left to write to input-number;1026 ;; set current-mode to 0x02.1027 0xFA ;D3B1 ; load input-number (0xD352)1028 0x52 ;D3B2 ; into A1029 0xD3 ;D3B31031 0xEA ;D3B4 ; load A into bytes-left-to-write1032 0x83 ;D3B5 ; (0xD383)1033 0xD3 ;D3B61035 0x3E ;D3B7 ; load 0x02 into A.1036 0x02 ;D3B81038 0xEA ;D3B9 ; load A to current-mode1039 0x82 ;D3BA ; advancing from Mode 1 to1040 0xD3 ;D3BB ; Mode 21042 0xC3 ;D3BC ; go back to beginning1043 0x1D ;D3BD1044 0xD3 ;D3BE1045 ;; End Mode 11048 ;; Mode 2 -- set start-point-high mode1049 ;; Middle part of the header for writing things to memory.1050 ;; User specifies the start location in RAM to which1051 ;; data will be written.1052 ;; Mode is auto advanced to Mode 3 after this mode completes.1054 ;; Set start-point-high to input-number;1055 ;; set current mode to 0x03.1056 0xFA ;D3BF ; load input-number (0xD352)1057 0x52 ;D3C0 ; into A1058 0xD3 ;D3C11060 0xEA ;D3C2 ; load A into start-point-high1061 0x85 ;D3C3 ; (0xD385)1062 0xD3 ;D3C41064 0x3E ;D3C5 ; load 0x03 into A.1065 0x03 ;D3C61067 0xEA ;D3C7 ; load A to current-mode,1068 0x82 ;D3C8 ; advancing from Mode 2 to1069 0xD3 ;D3C9 ; Mode 3.1071 0xC3 ;D3CA ; go back to beginning1072 0x1D ;D3CB1073 0xD3 ;D3CC1074 ;;End Mode 21077 ;; Mode 3 -- set-start-point-low mode1078 ;; Final part of header for writing things to memory.1079 ;; User specifies the low bytes of 16 bit start-point.1081 ;; Set start-point-low to input-number;1082 ;; set current mode to 0x041083 0xFA ;D3CD ; load input-number into A1084 0x52 ;D3CE1085 0xD3 ;D3CF1087 0xEA ;D3D0 ; load A into start-point-low1088 0x86 ;D3D11089 0xD3 ;D3D21091 0x3E ;D3D3 ; load 0x04 into A.1092 0x04 ;D3D41094 0xEA ;D3D5 ; load A to current-mode,1095 0x82 ;D3D6 ; advancing from Mode 3 to1096 0xD3 ;D3D7 ; Mode 4.1098 0xC3 ;D3D8 ; go back to beginning1099 0x1D ;D3D91100 0xD3 ;D3DA1102 ;; Mode 4 -- write bytes mode1104 ;; This is where RAM manipulation happens. User supplies1105 ;; bytes every frame, which are written sequentially to1106 ;; start-point until bytes-to-write have been written. Once1107 ;; bytes-to-write have been written, the mode is reset to 0.1109 ;; compare bytes-written with bytes-to-write.1110 ;; if they are the same, then reset mode to 01112 0xFA ;D3DB ; load bytes-to-write into A1113 0x83 ;D3DC1114 0xD3 ;D3DD1116 0x47 ;D3DE ; load A into B1118 0xFA ;D3DF ; load bytes-written into A1119 0x84 ;D3E01120 0xD3 ;D3E11122 0xB8 ;D3E2 ; compare A with B1124 0xCA ;D3E3 ; if they are equal, go to cleanup1125 0x07 ;D3E41126 0xD4 ;D3E51128 ;; Write Memory Section1129 ;; Write the input-number, interpreted as an 8-bit number,1130 ;; into the current target register, determined by1131 ;; (+ start-point bytes-written).1132 ;; Then, increment bytes-written by 1.1134 0xFA ;D3E6 ; load start-point-high into A1135 0x85 ;D3E71136 0xD3 ;D3E81138 0x67 ;D3E9 ; load A into H1140 0xFA ;D3EA ; load start-point-low into A1141 0x86 ;D3EB1142 0xD3 ;D3EC1144 0x6F ;D3ED ; load A into L1146 0xFA ;D3EE ; load bytes-written into A1147 0x84 ;D3EF1148 0xD3 ;D3F01150 0x00 ;D3F1 ; These are here because1151 0x00 ;D3F2 ; I screwed up again.1152 0x00 ;D3F31154 0x85 ;D3F4 ; add L to A; store A in L.1155 0x6F ;D3F51157 0x30 ;D3F6 ; If the addition overflowed,1158 0x01 ;D3F71159 0x24 ;D3F8 ; increment H.1161 ;; Now, HL points to the correct place in memory1163 0xFA ;D3F9 ; load input-number into A1164 0x52 ;D3FA1165 0xD3 ;D3FB1167 0x77 ;D3FC ; load A into (HL)1169 0xFA ;D3FD ; load bytes-written into A1170 0x84 ;D3FE1171 0xD3 ;D3FF1173 0x3C ;D400 ; increment A1175 0xEA ;D401 ; load A into bytes-written1176 0x84 ;D4021177 0xD3 ;D4031179 0xC3 ;D404 ; go back to beginning.1180 0x1D ;D4051181 0xD3 ;D4061182 ;; End Write Memory Section1184 ;; Mode 4 Cleanup Section1185 ;; reset bytes-written to 01186 ;; set mode to 01187 0x3E ;D407 ; load 0 into A1188 0x00 ;D4081190 0xEA ;D409 ; load A into bytes-written1191 0x84 ;D40A1192 0xD3 ;D40B1194 0xEA ;D40C ; load A into current-mode1195 0x82 ;D40D1196 0xD3 ;D40E1198 0xC3 ;D40F ; go back to beginning1199 0x1D ;D4101200 0xD3 ;D4111202 ;; End Mode 41204 ])1208 (def frame-count 0xD31F)1209 (def input 0xD352)1210 (def current-mode 0xD382)1211 (def bytes-to-write 0xD383)1212 (def bytes-written 0xD384)1213 (def start-point-high 0xD385)1214 (def start-point-low 0xD386)1218 (defn write-memory []1219 (-> (tick (mid-game))1220 (IE! 0) ; disable interrupts1221 (inject-item-assembly (write-memory-assembly))))1223 (defn test-write-memory []1224 (set-state! (write-memory))1225 (dorun1226 (dotimes [_ 5000]1227 (view-memory (step @current-state) current-mode))))1229 (def bytes-to-write 0xD383)1230 (def start-point 0xD384)1232 (defn print-blank-assembly1233 [start end]1234 (dorun1235 (map1236 #(println (format "0x00 ;%04X " %))1237 (range start end))))1239 (defn test-mode-2 []1240 (->1241 (write-memory)1242 (view-memory frame-count)1243 (step)1244 (step [:a])1245 (step [:b])1246 (step [:start])1247 (step [])1248 (view-memory frame-count)))1252 (defn dylan-test-mode1253 ([] (dylan-test-mode (write-mem-dyl)))1254 ([target-state]1255 (let [1256 v-blank-prev 540461257 btn-register 652801258 eggs 0xD3741259 ]1261 (->1262 target-state1264 (tick)1265 (tick)1266 (tick)1267 (tick);; jumps back to beginning1269 (tick)1270 (tick)1271 (tick)1272 (tick)1273 (tick)1274 (tick)1275 (tick)1276 (tick)1277 (tick)1278 (tick)1279 (tick)1280 (tick)1283 (tick)1284 (tick)1285 (tick)1286 (tick)1287 (tick)1288 (tick)1289 (tick)1290 (tick)1291 (tick)1292 (tick)1293 (tick)1294 (tick)1295 (tick)1296 (tick)1297 (tick)1298 (tick)1299 (tick)1300 (tick)1301 (tick)1302 (tick)1303 (tick) ;; just complemented A1305 (tick)1306 (DE! 0x1800)1307 (AF! 0x7700) ;; change inputs @ A1308 (tick)1309 (tick)1310 (tick)1311 (tick)1312 (tick)1314 ;;(view-memory eggs)1315 (tick)1316 (tick)1317 ;;(view-memory eggs)1318 (tick)1319 (tick)1320 (tick)1321 (tick)1322 (tick)1323 (tick)1324 (d-tick)1327 ;;(view-memory btn-register)1328 (view-register "A" A)1329 (view-register "B" B)1331 ;;(view-register "C" C)1332 (view-register "D" D)1333 (view-register "E" E)1334 (view-register "H" H)1335 (view-register "L" L)1336 ))))1338 (defn test-mode-41339 ([] (test-mode-4 (write-memory)))1340 ([target-state]1341 (->1342 target-state1343 (#(do (println "memory from 0xC00F to 0xC01F:"1344 (subvec (vec (memory %)) 0xC00F 0xC01F)) %))1345 (view-memory current-mode)1346 (step [])1347 (step [])1348 (step [])1349 (#(do (println "after three steps") %))1350 (view-memory current-mode)1352 ;; Activate memory writing mode1354 (#(do (println "step with [:a]") %))1355 (step [:a])1356 (view-memory current-mode)1357 (view-memory bytes-to-write)1358 (view-memory start-point-high)1359 (view-memory start-point-low)1361 ;; Specify four bytes to be written1363 (#(do (println "step with [:select]")%))1364 (step [:select])1365 (view-memory current-mode)1366 (view-memory bytes-to-write)1367 (view-memory start-point-high)1368 (view-memory start-point-low)1370 ;; Specify target memory address as 0xC00F1372 (#(do (println "step with [:u :d]")%))1373 (step [:u :d])1374 (view-memory current-mode)1375 (view-memory bytes-to-write)1376 (view-memory start-point-high)1377 (view-memory start-point-low)1379 (#(do (println "step with [:a :b :start :select]")%))1380 (step [:a :b :start :select])1381 (view-memory current-mode)1382 (view-memory bytes-to-write)1383 (view-memory start-point-high)1384 (view-memory start-point-low)1386 ;; Start reprogramming memory1388 (#(do (println "step with [:a]")%))1389 (step [:a])1390 (view-memory current-mode)1391 (view-memory bytes-written)1393 (#(do (println "step with [:b]")%))1394 (step [:b])1395 (view-memory current-mode)1396 (view-memory bytes-written)1398 (#(do (println "step with [:a :b]")%))1399 (step [:a :b])1400 (view-memory current-mode)1401 (view-memory bytes-written)1403 (#(do (println "step with [:select]")%))1404 (step [:select])1405 (view-memory current-mode)1406 (view-memory bytes-written)1408 ;; Reprogramming done, program ready for more commands.1410 (#(do (println "step with []")%))1411 (step [])1412 (view-memory current-mode)1413 (view-memory bytes-written)1415 (#(do (println "memory from 0xC00F to 0xC01F:"1416 (subvec (vec (memory %)) 0xC00F 0xC01F)) %)))))1423 ;;; ASSEMBLY-READING UTILITIES1425 (def opcodes1426 [1427 "NOP"1428 "LD BC,nn"1429 "LD (BC),A"1430 "INC BC"1431 "INC B"1432 "DEC B"1433 "LD B,n"1434 "RLC A"1435 "LD (nn),SP"1436 "ADD HL,BC"1437 "LD A,(BC)"1438 "DEC BC"1439 "INC C"1440 "DEC C"1441 "LD C,n"1442 "RRC A"1444 "STOP"1445 "LD DE,nn"1446 "LD (DE),A"1447 "INC DE"1448 "INC D"1449 "DEC D"1450 "LD D,n"1451 "RL A"1452 "JR n"1453 "ADD HL,DE"1454 "LD A,(DE)"1455 "DEC DE"1456 "INC E"1457 "DEC E"1458 "LD E,n"1459 "RR A"1461 "JR NZ,n"1462 "LD HL,nn"1463 "LDI (HL),A"1464 "INC HL"1465 "INC H"1466 "DEC H"1467 "LD H,n"1468 "DAA"1469 "JR Z,n"1470 "ADD HL,HL"1471 "LDI A,(HL)"1472 "DEC HL"1473 "INC L"1474 "DEC L"1475 "LD L,n"1476 "CPL"1478 "JR NC,n"1479 "LD SP,nn"1480 "LDD (HL),A"1481 "INC SP"1482 "INC (HL)"1483 "DEC (HL)"1484 "LD (HL),n"1485 "SCF"1486 "JR C,n"1487 "ADD HL,SP"1488 "LDD A,(HL)"1489 "DEC SP"1490 "INC A"1491 "DEC A"1492 "LD A,n"1493 "CCF"1495 "LD B,B"1496 "LD B,C"1497 "LD B,D"1498 "LD B,E"1499 "LD B,H"1500 "LD B,L"1501 "LD B,(HL)"1502 "LD B,A"1503 "LD C,B"1504 "LD C,C"1505 "LD C,D"1506 "LD C,E"1507 "LD C,H"1508 "LD C,L"1509 "LD C,(HL)"1510 "LD C,A"1512 "LD D,B"1513 "LD D,C"1514 "LD D,D"1515 "LD D,E"1516 "LD D,H"1517 "LD D,L"1518 "LD D,(HL)"1519 "LD D,A"1520 "LD E,B"1521 "LD E,C"1522 "LD E,D"1523 "LD E,E"1524 "LD E,H"1525 "LD E,L"1526 "LD E,(HL)"1527 "LD E,A"1529 "LD H,B"1530 "LD H,C"1531 "LD H,D"1532 "LD H,E"1533 "LD H,H"1534 "LD H,L"1535 "LD H,(HL)"1536 "LD H,A"1537 "LD L,B"1538 "LD L,C"1539 "LD L,D"1540 "LD L,E"1541 "LD L,H"1542 "LD L,L"1543 "LD L,(HL)"1544 "LD L,A"1546 "LD (HL),B"1547 "LD (HL),C"1548 "LD (HL),D"1549 "LD (HL),E"1550 "LD (HL),H"1551 "LD (HL),L"1552 "HALT"1553 "LD (HL),A"1554 "LD A,B"1555 "LD A,C"1556 "LD A,D"1557 "LD A,E"1558 "LD A,H"1559 "LD A,L"1560 "LD A,(HL)"1561 "LD A,A"1563 "ADD A,B"1564 "ADD A,C"1565 "ADD A,D"1566 "ADD A,E"1567 "ADD A,H"1568 "ADD A,L"1569 "ADD A,(HL)"1570 "ADD A,A"1571 "ADC A,B"1572 "ADC A,C"1573 "ADC A,D"1574 "ADC A,E"1575 "ADC A,H"1576 "ADC A,L"1577 "ADC A,(HL)"1578 "ADC A,A"1580 "SUB A,B"1581 "SUB A,C"1582 "SUB A,D"1583 "SUB A,E"1584 "SUB A,H"1585 "SUB A,L"1586 "SUB A,(HL)"1587 "SUB A,A"1588 "SBC A,B"1589 "SBC A,C"1590 "SBC A,D"1591 "SBC A,E"1592 "SBC A,H"1593 "SBC A,L"1594 "SBC A,(HL)"1595 "SBC A,A"1597 "AND B"1598 "AND C"1599 "AND D"1600 "AND E"1601 "AND H"1602 "AND L"1603 "AND (HL)"1604 "AND A"1605 "XOR B"1606 "XOR C"1607 "XOR D"1608 "XOR E"1609 "XOR H"1610 "XOR L"1611 "XOR (HL)"1612 "XOR A"1614 "OR B"1615 "OR C"1616 "OR D"1617 "OR E"1618 "OR H"1619 "OR L"1620 "OR (HL)"1621 "OR A"1622 "CP B"1623 "CP C"1624 "CP D"1625 "CP E"1626 "CP H"1627 "CP L"1628 "CP (HL)"1629 "CP A"1631 "RET NZ"1632 "POP BC"1633 "JP NZ,nn"1634 "JP nn"1635 "CALL NZ,nn"1636 "PUSH BC"1637 "ADD A,n"1638 "RST 0"1639 "RET Z"1640 "RET"1641 "JP Z,nn"1642 "Ext ops"1643 "CALL Z,nn"1644 "CALL nn"1645 "ADC A,n"1646 "RST 8"1648 "RET NC"1649 "POP DE"1650 "JP NC,nn"1651 "XX"1652 "CALL NC,nn"1653 "PUSH DE"1654 "SUB A,n"1655 "RST 10"1656 "RET C"1657 "RETI"1658 "JP C,nn"1659 "XX"1660 "CALL C,nn"1661 "XX"1662 "SBC A,n"1663 "RST 18"1665 "LDH (n),A"1666 "POP HL"1667 "LDH (C),A"1668 "XX"1669 "XX"1670 "PUSH HL"1671 "AND n"1672 "RST 20"1673 "ADD SP,d"1674 "JP (HL)"1675 "LD (nn),A"1676 "XX"1677 "XX"1678 "XX"1679 "XOR n"1680 "RST 28"1682 "LDH A,(n)"1683 "POP AF"1684 "XX"1685 "DI"1686 "XX"1687 "PUSH AF"1688 "OR n"1689 "RST 30"1690 "LDHL SP,d"1691 "LD SP,HL"1692 "LD A,(nn)"1693 "EI"1694 "XX"1695 "XX"1696 "CP n"1697 "RST 38"])1700 (defn hex1701 "Converts the number into a hexadecimal-formatted symbol."1702 [n]1703 (symbol (str "0x" (.toUpperCase (Integer/toHexString n)))))1707 (defn arity1708 "Returns the arity of the given opcode (hex numeral)."1709 [op]1710 (cond1711 (#{0x06 0x0E 0x16 0x1E1712 0x20 0x26 0x28 0x2E1713 0x30 0x36 0x38 0x3E1714 0xC6 0xD6 0xCE 0xDE1715 0xE0 0xF0 0xE6 0xF61716 0xEE 0xFE} op)1717 11718 (#{0x01 0x08 0x11 0x211719 0x31 0xC2 0xC3 0xC41720 0xCA 0xDA 0xCC 0xDC1721 0xCD 0xEA 0xFA} op)1722 21723 :else1724 0))