Mercurial > vba-clojure
view clojure/com/aurellem/gb/assembly.clj @ 239:19fd38fe376e
revived a functional version of Dylan's assembly.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sun, 25 Mar 2012 00:38:45 -0500 |
parents | d585b91de06c |
children | 2e751984b42d |
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 0xCB ;; test A_0. this result will be used twice.442 0x47444 0x28 ;; end frame (JUMP) if A_0 = 0.445 0xF3 ;; TODO: set jump length447 ;; -------- GET BUTTON INPUT449 ;; btw, Z bit is now 1450 ;; 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 0x28 ;; JUMP forward if Z=0470 0x08472 0x47 ;; A -> B473 0xCB ;; swap B nybbles474 0x30476 0x3E ;; load 0x10 into A, to measure btns477 0x10479 0xBF ;; compare(A,A) sets Z=0481 0x18 ;; JUMP back to "load A into [FF00]"482 0xEF485 ;; ------ TAKE ACTION BASED ON USER INPUT487 ;; "input mode"488 ;; mode 0x00 : select mode489 ;; mode 0x08 : select bytes-to-write490 ;; mode 0x10 : select hi-bit491 ;; mode 0x18 : select lo-bit493 ;; "output mode"494 ;; mode 0x20 : write bytes495 ;; mode 0xFF : jump PC498 ;; registers499 ;; D : mode select500 ;; E : count of bytes to write501 ;; H : address-high502 ;; L : address-low504 ;; now A contains the pressed keys505 0x2F ; complement A, by request. [D34F]507 0x47 ; A->B ;; now B contains the pressed keys509 0xCB ; test bit 5 of D (are we in o/p mode?)510 0x6A511 0x28 ; if test == 0, skip this o/p section512 0x13 ; JUMP514 0xCB ; else, test bit 0 of D (fragile; are we in pc mode?)515 0x42516 0x28 ; if test == 0, skip the following command517 0x01519 ;; output mode I: moving the program counter520 0xE9 ; ** move PC to (HL)522 ;; output mode II: writing bytes523 0xAF ; zero A524 0xBB ; compare count to zero. finished writing?525 0x28 ; if we are finished, jump back to cleanup526 0x00 ; TODO: set jump length backwards.528 ;; continue writing bytes529 0x78 ;; B->A530 0x22 ;; copy A to (HL) and increment HL.531 0x18 ;; end frame. [goto D31F]532 0xB6 ;; JUMP535 ;; ---- end of o/p section537 ;; i/p mode538 ;; adhere to the mode discipline:539 ;; D must be one of 0x00 0x08 0x10 0x18.541 0x3E ;; load the constant 57 into A. [D369]542 0x57543 0x82 ;; add the mode to A544 0xEA ;; store A into "thing to execute"545 0x74546 0xD3548 0x3E ;; load the constant 8 into A549 0x08550 0x82 ;; add the mode to A552 0x57 ;; store the incremented mode into D553 0x78 ;; B->A; now A contains the pressed keys555 0x00 ;; var: thing to execute [D374]557 0x18 ;; end frame558 0xA8 ;; JUMP559 ]560 )562 (defn write-mem-dyl []563 (-> (tick (mid-game))564 (IE! 0)565 (inject-item-assembly (write-memory-assembly*))))567 (defn ntick [s n]568 (if (zero? n) s569 (do570 (set-state! s)571 (dorun (dotimes [_ n]572 (com.aurellem.gb.Gb/tick)))573 (update-state))))576 (defn find-frame-shift [state]577 ;;(restart!)578 (set-state! state)579 (loop [n 0]580 (if (>= (first (registers)) 0xD32D)581 (do (println n)582 (update-state))583 (do584 (com.aurellem.gb.Gb/tick)585 (recur (inc n) )))))587 (defn demo-assembly [n]588 (repeat n 0x00))591 (defn find-frame-shift* [state]592 (set-state! state)593 (loop []594 (com.aurellem.gb.Gb/tick)595 ;;(println (first (registers)))596 (if (>= (first (registers)) 0xD32D)597 (update-state)598 (recur))))601 (defn dylan**602 ([k]603 (->604 (tick (mid-game))605 (inject-item-assembly(write-memory-assembly*))606 ;;(find-frame-shift)607 (ntick k)608 (d-tick)609 (view-register "A" A)610 (view-register "B" B)611 (view-register "D" D)612 (view-register "E" E)613 (view-register "F" F)614 (#(do (println) %))615 ))616 ([] (dylan** 0)))621 (defn dylan* []622 (->623 (write-mem-dyl)625 (tick)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)662 ;;(view-memory 0xD374)663 (tick)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 ;;(view-memory 0xD374)679 (d-tick)681 (view-register "A" A)682 (view-register "B" B)683 (view-register "C" C))685 )688 (defn dylan []689 (->690 (write-mem-dyl)691 (tick)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) ;; first loop708 (tick)709 (tick)710 (tick)711 (tick)712 (tick)713 (tick)714 (tick)715 (tick)716 (tick)717 (tick)718 (tick)719 (tick)720 (tick) ;; dpad bits722 (tick)723 (tick)724 (tick)725 (tick)726 (tick)727 (tick)728 (tick)729 (tick)730 (d-tick)734 (view-register "A" A)735 (view-register "B" B)736 (view-register "C" C)738 ))743 (defn d2 []744 (->745 (write-mem-dyl)746 (view-memory 0xD31F)747 step step step step step748 (view-memory 0xD31F)))769 (defn write-memory-assembly []770 [771 ;; Main Timing Loop772 ;; Constantly check for v-blank and Trigger main state machine on773 ;; every transtion from v-blank to non-v-blank.775 0x18 ; D31D ; Variable declaration776 0x02 ; D31E777 0x00 ; D31F ; frame-count778 0x00 ; D320 ; v-blank-prev780 0xF0 ; D321 ; load v-blank mode flags into A781 0x41782 0x00785 ;; Branch dependent on v-blank. v-blank happens when the last two786 ;; bits in A are "01"787 0xCB ; D324788 0x4F ; D325790 0xC2 ; D326 ; if bit-1 is not 0, then791 0x3E ; D327 ; GOTO non-v-blank.792 0xD3 ; D328794 0xCB ; D329795 0x47 ; D32A797 0xCA ; D32B ; if bit-0 is not 1, then798 0x3E ; D32C ; GOTO non-v-blank.799 0xD3 ; D32D801 ;; V-Blank802 ;; Activate state-machine if this is a transition event.804 0xFA ; D32E ; load v-bank-prev into A805 0x20 ; D32F806 0xD3 ; D330808 0xFE ; D331 ; compare A to 0. >--------\809 0x00 ; D332 \810 ; |811 ;; set v-blank-prev to 1. |812 0x3E ; D333 ; load 1 into A. |813 0x01 ; D334 |814 ; |815 0xEA ; D335 ; load A into v-blank-prev |816 0x20 ; D336 |817 0xD3 ; D337 |818 ; /819 ;; if v-blank-prev was 0, activate state-machine <------/820 0xCA ; D338 ; if v-blank-prev821 0x46 ; D339 ; was 0,822 0xD3 ; D33A ; GOTO state-machine824 0xC3 ; D33B825 0x1D ; D33C826 0xD3 ; D33D ; GOTO beginning827 ;; END V-blank829 ;; Non-V-Blank830 ;; Set v-blank-prev to 0831 0x3E ; D33E ; load 0 into A832 0x00 ; D33F834 0xEA ; D340 ; load A into v-blank-prev835 0x20 ; D341836 0xD3 ; D342838 0xC3 ; D343839 0x1D ; D344840 0xD3 ; D345 ; GOTO beginning841 ;; END Not-V-Blank844 ;; Main State Machine -- Input Section845 ;; This is called once every frame.846 ;; It collects input and uses it to drive the847 ;; state transitions.849 ;; Increment frame-count850 0xFA ; D346 ; load frame-count into A851 0x1F ; D347852 0xD3 ; D348854 0x3C ; D349 ; inc A856 0xEA ; D34A857 0x1F ; D34B ; load A into frame-count858 0xD3 ; D34C860 0x00 ; D34D ; glue :)862 0x18 ;D34E ; skip next 3 bytes863 0x03 ;D34F864 ;D350865 (Integer/parseInt "00100000" 2) ; select directional pad866 ;D351867 (Integer/parseInt "00010000" 2) ; select buttons868 0x00 ;D352 ; input-number870 ;; select directional pad; store low bits in B872 0xFA ;D353 ; load (D350) into A873 0x50 ;D354 -->874 0xD3 ;D355 --> D350876 0xE0 ;D356 ; load (A), which is877 0x00 ;D357 --> ; 00010000, into FF00878 0x00 ;D358 --> FF00 ;; NO-OP880 0x06 ;D359881 ;D35A882 (Integer/parseInt "11110000" 2) ; "11110000" -> B883 0xF0 ;D35B ; (FF00) -> A884 0x00 ;D35C885 0x00 ;D35D ;; NO-OP887 0xCB ;D35E ; swap nybbles on A888 0x37 ;D35F889 0xA0 ;D360 ; (AND A B) -> A890 0x47 ;D361 ; A -> B892 ;; select buttons; store bottom bits in C894 0xFA ;D362 ; load (D351) into A895 0x51 ;D363 -->896 0xD3 ;D364 --> D351898 0xE0 ;D365 ; load (A), which is899 0x00 ;D366 --> ; 00001000, into FF00900 0x00 ;D367 --> FF00 ;; NO-OP902 0x0E ;D368903 ;D369904 (Integer/parseInt "00001111" 2) ; "00001111" -> C906 0xF0 ;D36A ; (FF00) -> A907 0x00 ;D36B908 0x00 ;D36C910 0xA1 ;D36D ; (AND A C) -> A911 0x4F ;D36E ; A -> C913 ;; combine the B and C registers into the input number914 0x79 ;D36F ; C -> A915 0xB0 ;D370 ; (OR A B) -> A916 0x2F ;D371 ; negate A918 0xEA ;D372 ; store A into input-number919 0x52 ;D373920 0xD3 ;D374922 0x00 ;D375923 0x00 ;D376924 0x00 ;D377925 0x00 ;D378926 0x00 ;D379927 0x00 ;D37A928 0x00 ;D37B ; these are here because929 0x00 ;D37C ; I messed up :(930 0x00 ;D37D931 0x00 ;D37E932 0x00 ;D37F934 ;; beginning of main state machine935 0x18 ;D380 ; Declaration of variables936 0x05 ;D381 ; 5 variables:937 0x00 ;D382 ; current-mode938 0x00 ;D383 ; bytes-to-write939 0x00 ;D384 ; bytes-written940 0x00 ;D385 ; start-point-high941 0x00 ;D386 ; start-point-low944 ;; banch on current mode945 0xFA ;D387 ; load current-mode (0xD382)946 0x82 ;D388 ; into A947 0xD3 ;D389948 0x00 ;D38A951 ;; GOTO Mode 0 (input-mode) if current-mode is 0952 0xFE ;D38B953 0x00 ;D38C ; compare A with 0x00955 0xCA ;D38D ; goto Mode 0 if A == 0956 0xA8 ;D38E957 0xD3 ;D38F959 ;; GOTO Mode 1 (set-length) if current-mode is 1960 0xFE ;D390961 0x01 ;D391 ; compare A with 0x01963 0xCA ;D392964 0xB1 ;D393965 0xD3 ;D394 ; goto Mode 1 if A == 1967 ;; GOTO Mode 2 (set-start-point-high) if current mode is 2968 0xFE ;D395969 0x02 ;D396 ; compare A with 0x02971 0xCA ;D397972 0xBF ;D398973 0xD3 ;D399 ; goto Mode 2 if A == 2975 ;; GOTO Mode 3 (set-start-point-low) if current mode is 3976 0xFE ;D39A977 0x03 ;D39B979 0xCA ;D39C980 0xCD ;D39D981 0xD3 ;D39E ; goto Mode 3 if A == 3983 ;; GOTO Mode 4 (write-memory) if current mode is 4984 0xFE ;D39F985 0x04 ;D3A0987 0xCA ;D3A1988 0xDB ;D3A2989 0xD3 ;D3A3991 0x00 ;D3A4992 ;; End of Mode checking, goto beginning993 0xC3 ;D3A5994 0x1D ;D3A6995 0xD3 ;D3A7998 ;; Mode 0 -- input-mode mode999 ;; means that we are waiting for a mode, so set the mode to1000 ;; whatever is currently in input-number. If nothing is1001 ;; entered, then the program stays in input-mode mode1003 ;; set current-mode to input-number1004 0xFA ;D3A8 ; load input-number (0xD352)1005 0x52 ;D3A9 ; into A1006 0xD3 ;D3AA1008 0xEA ;D3AB ; load A into current-mode1009 0x82 ;D3AC ; (0xD382)1010 0xD3 ;D3AD1012 0xC3 ;D3AE ; go back to beginning1013 0x1D ;D3AF1014 0xD3 ;D3B01015 ;; End Mode 01018 ;; Mode 1 -- set-length mode1019 ;; This is the header for writing things to memory.1020 ;; User specifies the number of bytes to write.1021 ;; Mode is auto advanced to Mode 2 after this mode1022 ;; completes.1024 ;; Set bytes left to write to input-number;1025 ;; set current-mode to 0x02.1026 0xFA ;D3B1 ; load input-number (0xD352)1027 0x52 ;D3B2 ; into A1028 0xD3 ;D3B31030 0xEA ;D3B4 ; load A into bytes-left-to-write1031 0x83 ;D3B5 ; (0xD383)1032 0xD3 ;D3B61034 0x3E ;D3B7 ; load 0x02 into A.1035 0x02 ;D3B81037 0xEA ;D3B9 ; load A to current-mode1038 0x82 ;D3BA ; advancing from Mode 1 to1039 0xD3 ;D3BB ; Mode 21041 0xC3 ;D3BC ; go back to beginning1042 0x1D ;D3BD1043 0xD3 ;D3BE1044 ;; End Mode 11047 ;; Mode 2 -- set start-point-high mode1048 ;; Middle part of the header for writing things to memory.1049 ;; User specifies the start location in RAM to which1050 ;; data will be written.1051 ;; Mode is auto advanced to Mode 3 after this mode completes.1053 ;; Set start-point-high to input-number;1054 ;; set current mode to 0x03.1055 0xFA ;D3BF ; load input-number (0xD352)1056 0x52 ;D3C0 ; into A1057 0xD3 ;D3C11059 0xEA ;D3C2 ; load A into start-point-high1060 0x85 ;D3C3 ; (0xD385)1061 0xD3 ;D3C41063 0x3E ;D3C5 ; load 0x03 into A.1064 0x03 ;D3C61066 0xEA ;D3C7 ; load A to current-mode,1067 0x82 ;D3C8 ; advancing from Mode 2 to1068 0xD3 ;D3C9 ; Mode 3.1070 0xC3 ;D3CA ; go back to beginning1071 0x1D ;D3CB1072 0xD3 ;D3CC1073 ;;End Mode 21076 ;; Mode 3 -- set-start-point-low mode1077 ;; Final part of header for writing things to memory.1078 ;; User specifies the low bytes of 16 bit start-point.1080 ;; Set start-point-low to input-number;1081 ;; set current mode to 0x041082 0xFA ;D3CD ; load input-number into A1083 0x52 ;D3CE1084 0xD3 ;D3CF1086 0xEA ;D3D0 ; load A into start-point-low1087 0x86 ;D3D11088 0xD3 ;D3D21090 0x3E ;D3D3 ; load 0x04 into A.1091 0x04 ;D3D41093 0xEA ;D3D5 ; load A to current-mode,1094 0x82 ;D3D6 ; advancing from Mode 3 to1095 0xD3 ;D3D7 ; Mode 4.1097 0xC3 ;D3D8 ; go back to beginning1098 0x1D ;D3D91099 0xD3 ;D3DA1101 ;; Mode 4 -- write bytes mode1103 ;; This is where RAM manipulation happens. User supplies1104 ;; bytes every frame, which are written sequentially to1105 ;; start-point until bytes-to-write have been written. Once1106 ;; bytes-to-write have been written, the mode is reset to 0.1108 ;; compare bytes-written with bytes-to-write.1109 ;; if they are the same, then reset mode to 01111 0xFA ;D3DB ; load bytes-to-write into A1112 0x83 ;D3DC1113 0xD3 ;D3DD1115 0x47 ;D3DE ; load A into B1117 0xFA ;D3DF ; load bytes-written into A1118 0x84 ;D3E01119 0xD3 ;D3E11121 0xB8 ;D3E2 ; compare A with B1123 0xCA ;D3E3 ; if they are equal, go to cleanup1124 0x07 ;D3E41125 0xD4 ;D3E51127 ;; Write Memory Section1128 ;; Write the input-number, interpreted as an 8-bit number,1129 ;; into the current target register, determined by1130 ;; (+ start-point bytes-written).1131 ;; Then, increment bytes-written by 1.1133 0xFA ;D3E6 ; load start-point-high into A1134 0x85 ;D3E71135 0xD3 ;D3E81137 0x67 ;D3E9 ; load A into H1139 0xFA ;D3EA ; load start-point-low into A1140 0x86 ;D3EB1141 0xD3 ;D3EC1143 0x6F ;D3ED ; load A into L1145 0xFA ;D3EE ; load bytes-written into A1146 0x84 ;D3EF1147 0xD3 ;D3F01149 0x00 ;D3F1 ; These are here because1150 0x00 ;D3F2 ; I screwed up again.1151 0x00 ;D3F31153 0x85 ;D3F4 ; add L to A; store A in L.1154 0x6F ;D3F51156 0x30 ;D3F6 ; If the addition overflowed,1157 0x01 ;D3F71158 0x24 ;D3F8 ; increment H.1160 ;; Now, HL points to the correct place in memory1162 0xFA ;D3F9 ; load input-number into A1163 0x52 ;D3FA1164 0xD3 ;D3FB1166 0x77 ;D3FC ; load A into (HL)1168 0xFA ;D3FD ; load bytes-written into A1169 0x84 ;D3FE1170 0xD3 ;D3FF1172 0x3C ;D400 ; increment A1174 0xEA ;D401 ; load A into bytes-written1175 0x84 ;D4021176 0xD3 ;D4031178 0xC3 ;D404 ; go back to beginning.1179 0x1D ;D4051180 0xD3 ;D4061181 ;; End Write Memory Section1183 ;; Mode 4 Cleanup Section1184 ;; reset bytes-written to 01185 ;; set mode to 01186 0x3E ;D407 ; load 0 into A1187 0x00 ;D4081189 0xEA ;D409 ; load A into bytes-written1190 0x84 ;D40A1191 0xD3 ;D40B1193 0xEA ;D40C ; load A into current-mode1194 0x82 ;D40D1195 0xD3 ;D40E1197 0xC3 ;D40F ; go back to beginning1198 0x1D ;D4101199 0xD3 ;D4111201 ;; End Mode 41203 ])1207 (def frame-count 0xD31F)1208 (def input 0xD352)1209 (def current-mode 0xD382)1210 (def bytes-to-write 0xD383)1211 (def bytes-written 0xD384)1212 (def start-point-high 0xD385)1213 (def start-point-low 0xD386)1217 (defn write-memory []1218 (-> (tick (mid-game))1219 (IE! 0) ; disable interrupts1220 (inject-item-assembly (write-memory-assembly))))1222 (defn test-write-memory []1223 (set-state! (write-memory))1224 (dorun1225 (dotimes [_ 5000]1226 (view-memory (step @current-state) current-mode))))1228 (def bytes-to-write 0xD383)1229 (def start-point 0xD384)1231 (defn print-blank-assembly1232 [start end]1233 (dorun1234 (map1235 #(println (format "0x00 ;%04X " %))1236 (range start end))))1238 (defn test-mode-2 []1239 (->1240 (write-memory)1241 (view-memory frame-count)1242 (step)1243 (step [:a])1244 (step [:b])1245 (step [:start])1246 (step [])1247 (view-memory frame-count)))1251 (defn dylan-test-mode1252 ([] (dylan-test-mode (write-mem-dyl)))1253 ([target-state]1254 (let [1255 v-blank-prev 540461256 btn-register 652801257 eggs 0xD3741258 ]1260 (->1261 target-state1263 (tick)1264 (tick)1265 (tick)1266 (tick);; jumps back to beginning1268 (tick)1269 (tick)1270 (tick)1271 (tick)1272 (tick)1273 (tick)1274 (tick)1275 (tick)1276 (tick)1277 (tick)1278 (tick)1279 (tick)1282 (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) ;; just complemented A1304 (tick)1305 (DE! 0x1800)1306 (AF! 0x7700) ;; change inputs @ A1307 (tick)1308 (tick)1309 (tick)1310 (tick)1311 (tick)1313 ;;(view-memory eggs)1314 (tick)1315 (tick)1316 ;;(view-memory eggs)1317 (tick)1318 (tick)1319 (tick)1320 (tick)1321 (tick)1322 (tick)1323 (d-tick)1326 ;;(view-memory btn-register)1327 (view-register "A" A)1328 (view-register "B" B)1330 ;;(view-register "C" C)1331 (view-register "D" D)1332 (view-register "E" E)1333 (view-register "H" H)1334 (view-register "L" L)1335 ))))1337 (defn test-mode-41338 ([] (test-mode-4 (write-memory)))1339 ([target-state]1340 (->1341 target-state1342 (#(do (println "memory from 0xC00F to 0xC01F:"1343 (subvec (vec (memory %)) 0xC00F 0xC01F)) %))1344 (view-memory current-mode)1345 (step [])1346 (step [])1347 (step [])1348 (#(do (println "after three steps") %))1349 (view-memory current-mode)1351 ;; Activate memory writing mode1353 (#(do (println "step with [:a]") %))1354 (step [:a])1355 (view-memory current-mode)1356 (view-memory bytes-to-write)1357 (view-memory start-point-high)1358 (view-memory start-point-low)1360 ;; Specify four bytes to be written1362 (#(do (println "step with [:select]")%))1363 (step [:select])1364 (view-memory current-mode)1365 (view-memory bytes-to-write)1366 (view-memory start-point-high)1367 (view-memory start-point-low)1369 ;; Specify target memory address as 0xC00F1371 (#(do (println "step with [:u :d]")%))1372 (step [:u :d])1373 (view-memory current-mode)1374 (view-memory bytes-to-write)1375 (view-memory start-point-high)1376 (view-memory start-point-low)1378 (#(do (println "step with [:a :b :start :select]")%))1379 (step [:a :b :start :select])1380 (view-memory current-mode)1381 (view-memory bytes-to-write)1382 (view-memory start-point-high)1383 (view-memory start-point-low)1385 ;; Start reprogramming memory1387 (#(do (println "step with [:a]")%))1388 (step [:a])1389 (view-memory current-mode)1390 (view-memory bytes-written)1392 (#(do (println "step with [:b]")%))1393 (step [:b])1394 (view-memory current-mode)1395 (view-memory bytes-written)1397 (#(do (println "step with [:a :b]")%))1398 (step [:a :b])1399 (view-memory current-mode)1400 (view-memory bytes-written)1402 (#(do (println "step with [:select]")%))1403 (step [:select])1404 (view-memory current-mode)1405 (view-memory bytes-written)1407 ;; Reprogramming done, program ready for more commands.1409 (#(do (println "step with []")%))1410 (step [])1411 (view-memory current-mode)1412 (view-memory bytes-written)1414 (#(do (println "memory from 0xC00F to 0xC01F:"1415 (subvec (vec (memory %)) 0xC00F 0xC01F)) %)))))1422 ;;; ASSEMBLY-READING UTILITIES1424 (def opcodes1425 [1426 "NOP"1427 "LD BC,nn"1428 "LD (BC),A"1429 "INC BC"1430 "INC B"1431 "DEC B"1432 "LD B,n"1433 "RLC A"1434 "LD (nn),SP"1435 "ADD HL,BC"1436 "LD A,(BC)"1437 "DEC BC"1438 "INC C"1439 "DEC C"1440 "LD C,n"1441 "RRC A"1443 "STOP"1444 "LD DE,nn"1445 "LD (DE),A"1446 "INC DE"1447 "INC D"1448 "DEC D"1449 "LD D,n"1450 "RL A"1451 "JR n"1452 "ADD HL,DE"1453 "LD A,(DE)"1454 "DEC DE"1455 "INC E"1456 "DEC E"1457 "LD E,n"1458 "RR A"1460 "JR NZ,n"1461 "LD HL,nn"1462 "LDI (HL),A"1463 "INC HL"1464 "INC H"1465 "DEC H"1466 "LD H,n"1467 "DAA"1468 "JR Z,n"1469 "ADD HL,HL"1470 "LDI A,(HL)"1471 "DEC HL"1472 "INC L"1473 "DEC L"1474 "LD L,n"1475 "CPL"1477 "JR NC,n"1478 "LD SP,nn"1479 "LDD (HL),A"1480 "INC SP"1481 "INC (HL)"1482 "DEC (HL)"1483 "LD (HL),n"1484 "SCF"1485 "JR C,n"1486 "ADD HL,SP"1487 "LDD A,(HL)"1488 "DEC SP"1489 "INC A"1490 "DEC A"1491 "LD A,n"1492 "CCF"1494 "LD B,B"1495 "LD B,C"1496 "LD B,D"1497 "LD B,E"1498 "LD B,H"1499 "LD B,L"1500 "LD B,(HL)"1501 "LD B,A"1502 "LD C,B"1503 "LD C,C"1504 "LD C,D"1505 "LD C,E"1506 "LD C,H"1507 "LD C,L"1508 "LD C,(HL)"1509 "LD C,A"1511 "LD D,B"1512 "LD D,C"1513 "LD D,D"1514 "LD D,E"1515 "LD D,H"1516 "LD D,L"1517 "LD D,(HL)"1518 "LD D,A"1519 "LD E,B"1520 "LD E,C"1521 "LD E,D"1522 "LD E,E"1523 "LD E,H"1524 "LD E,L"1525 "LD E,(HL)"1526 "LD E,A"1528 "LD H,B"1529 "LD H,C"1530 "LD H,D"1531 "LD H,E"1532 "LD H,H"1533 "LD H,L"1534 "LD H,(HL)"1535 "LD H,A"1536 "LD L,B"1537 "LD L,C"1538 "LD L,D"1539 "LD L,E"1540 "LD L,H"1541 "LD L,L"1542 "LD L,(HL)"1543 "LD L,A"1545 "LD (HL),B"1546 "LD (HL),C"1547 "LD (HL),D"1548 "LD (HL),E"1549 "LD (HL),H"1550 "LD (HL),L"1551 "HALT"1552 "LD (HL),A"1553 "LD A,B"1554 "LD A,C"1555 "LD A,D"1556 "LD A,E"1557 "LD A,H"1558 "LD A,L"1559 "LD A,(HL)"1560 "LD A,A"1562 "ADD A,B"1563 "ADD A,C"1564 "ADD A,D"1565 "ADD A,E"1566 "ADD A,H"1567 "ADD A,L"1568 "ADD A,(HL)"1569 "ADD A,A"1570 "ADC A,B"1571 "ADC A,C"1572 "ADC A,D"1573 "ADC A,E"1574 "ADC A,H"1575 "ADC A,L"1576 "ADC A,(HL)"1577 "ADC A,A"1579 "SUB A,B"1580 "SUB A,C"1581 "SUB A,D"1582 "SUB A,E"1583 "SUB A,H"1584 "SUB A,L"1585 "SUB A,(HL)"1586 "SUB A,A"1587 "SBC A,B"1588 "SBC A,C"1589 "SBC A,D"1590 "SBC A,E"1591 "SBC A,H"1592 "SBC A,L"1593 "SBC A,(HL)"1594 "SBC A,A"1596 "AND B"1597 "AND C"1598 "AND D"1599 "AND E"1600 "AND H"1601 "AND L"1602 "AND (HL)"1603 "AND A"1604 "XOR B"1605 "XOR C"1606 "XOR D"1607 "XOR E"1608 "XOR H"1609 "XOR L"1610 "XOR (HL)"1611 "XOR A"1613 "OR B"1614 "OR C"1615 "OR D"1616 "OR E"1617 "OR H"1618 "OR L"1619 "OR (HL)"1620 "OR A"1621 "CP B"1622 "CP C"1623 "CP D"1624 "CP E"1625 "CP H"1626 "CP L"1627 "CP (HL)"1628 "CP A"1630 "RET NZ"1631 "POP BC"1632 "JP NZ,nn"1633 "JP nn"1634 "CALL NZ,nn"1635 "PUSH BC"1636 "ADD A,n"1637 "RST 0"1638 "RET Z"1639 "RET"1640 "JP Z,nn"1641 "Ext ops"1642 "CALL Z,nn"1643 "CALL nn"1644 "ADC A,n"1645 "RST 8"1647 "RET NC"1648 "POP DE"1649 "JP NC,nn"1650 "XX"1651 "CALL NC,nn"1652 "PUSH DE"1653 "SUB A,n"1654 "RST 10"1655 "RET C"1656 "RETI"1657 "JP C,nn"1658 "XX"1659 "CALL C,nn"1660 "XX"1661 "SBC A,n"1662 "RST 18"1664 "LDH (n),A"1665 "POP HL"1666 "LDH (C),A"1667 "XX"1668 "XX"1669 "PUSH HL"1670 "AND n"1671 "RST 20"1672 "ADD SP,d"1673 "JP (HL)"1674 "LD (nn),A"1675 "XX"1676 "XX"1677 "XX"1678 "XOR n"1679 "RST 28"1681 "LDH A,(n)"1682 "POP AF"1683 "XX"1684 "DI"1685 "XX"1686 "PUSH AF"1687 "OR n"1688 "RST 30"1689 "LDHL SP,d"1690 "LD SP,HL"1691 "LD A,(nn)"1692 "EI"1693 "XX"1694 "XX"1695 "CP n"1696 "RST 38"])1699 (defn hex1700 "Converts the number into a hexadecimal-formatted symbol."1701 [n]1702 (symbol (str "0x" (.toUpperCase (Integer/toHexString n)))))1706 (defn arity1707 "Returns the arity of the given opcode (hex numeral)."1708 [op]1709 (cond1710 (#{0x06 0x0E 0x16 0x1E1711 0x20 0x26 0x28 0x2E1712 0x30 0x36 0x38 0x3E1713 0xC6 0xD6 0xCE 0xDE1714 0xE0 0xF0 0xE6 0xF61715 0xEE 0xFE} op)1716 11717 (#{0x01 0x08 0x11 0x211718 0x31 0xC2 0xC3 0xC41719 0xCA 0xDA 0xCC 0xDC1720 0xCD 0xEA 0xFA} op)1721 21722 :else1723 0))