view clojure/com/aurellem/gb/assembly.clj @ 513:3dbb863eb801

accuracy of displayed image is much improved, but there the palettes are still messed up.
author Robert McIntyre <rlm@mit.edu>
date Fri, 22 Jun 2012 18:58:47 -0500
parents 2e751984b42d
children
line wrap: on
line source
1 (ns com.aurellem.gb.assembly
2 (:use (com.aurellem.gb gb-driver vbm util items))
3 (:import [com.aurellem.gb.gb_driver SaveState]))
5 (defn inject-assembly
6 ([^SaveState state
7 program-counter registers
8 assembly-code]
9 (let [scratch-memory (memory state)]
10 ;; inject assembly code
11 (dorun (map (fn [index val]
12 (aset scratch-memory index val))
13 (range program-counter
14 (+ program-counter (count assembly-code)))
15 assembly-code))
16 (-> state
17 (write-memory! scratch-memory)
18 (write-registers! registers)
19 (PC! program-counter)))))
21 (defn inject-item-assembly
22 ([^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-assembly
30 ([info-fn assembly n]
31 (let [final-state
32 (reduce (fn [state _]
33 (tick (info-fn state)))
34 (inject-item-assembly
35 (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 (recur
51 (conj program-counters
52 (first (registers @current-state)))
53 (conj opcodes
54 (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 interrupts
72 (inject-item-assembly
73 ;; write 00010000 to 0xFF00 to select joypad
74 [0x18 ;D31D ; jump over
75 0x01 ;D31E ; the next 8 bits
76 ;D31F
77 (Integer/parseInt "00100000" 2) ; data section
79 0xFA ;D320 ; load (D31F) into A
80 0x1F ;D321 -->
81 0xD3 ;D322 --> D31F
83 0xEA ;D323 ; load (A), which is
84 0x00 ;D324 --> ; 00010000, into FF00
85 0xFF ;D325 --> FF00
87 0x18 ;D326 ; this is the place where
88 0x01 ;D327 ; we will store whether
89 0x00 ;D328 ; "down" is pressed.
91 0xFA ;D329 ; (FF00) -> A
92 0x00 ;D32A
93 0xFF ;D32B
95 0xCB ;D32C ; Test whether "down"
96 0x5F ;D32D ; is pressed.
98 0x28 ;D32E ; if down is pressed,
99 0x03 ;D32F ; skip the next section
100 ; of code.
101 ;; down-is-not-pressed
102 0xC3 ;D330
103 0x1D ;D331 ; return to beginning
104 0xD3 ;D332
106 ;; down-is-pressed
107 0xEA ;D334 ; write A to D328 if
108 0x28 ;D335 ; "down" was pressed
109 0xD3 ;D336
111 0xC3 ;D330
112 0x1D ;D331 ; return to beginning
113 0xD3 ;D332
114 ])))
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 interrupts
123 (inject-item-assembly
124 [0x18 ;D31D ; jump over
125 0x02 ;D31E ; the next 2 bytes
126 0x00 ;D31F ; frame-count
127 0x00 ;D320 ; v-blank-prev
129 0xFA ;D321
130 0x41 ;D322 ; load (FF41) into A
131 0xFF ;D323 ; this contains mode flags
133 ;; if we're in v-blank, the bit-1 is 0
134 ;; and bit-2 is 1 Otherwise, it is not v-blank.
135 0xCB ;D324 ; test bit-1 of A
136 0x4F ;D325
138 0xC2 ;D326 ; if bit-1 is not 0
139 0x44 ;D327 ; GOTO not-v-blank
140 0xD3 ;D328
142 0xCB ;D329 ; test bit-0 of A
143 0x47 ;D32A
145 0xCA ;D32B ; if bit-0 is not 1
146 0x44 ;D32C ; GOTO not-v-blank
147 0xD3 ;D32D
148 ;;; in v-blank mode
149 ;; if v-blank-prev was 0,
150 ;; increment frame-count
152 0xFA ;D32E ; load v-blank-prev to A
153 0x20 ;D32F
154 0xD3 ;D330
156 0xCB ;D331
157 0x47 ;D332 ; test bit-0 of A
159 0x20 ;D333 ; skip next section
160 0x07 ;D334 ; if v-blank-prev was not zero
162 ;; v-blank was 0, increment frame-count
163 0xFA ;D335 ; load frame-count into A
164 0x1F ;D336
165 0xD3 ;D337
167 0x3C ;D338 ; inc A
169 0xEA ;D339 ; load A into frame-count
170 0x1F ;D33A
171 0xD3 ;D33B
173 ;; set v-blank-prev to 1
174 0x3E ;D33C ; load 1 into A
175 0x01 ;D33D
177 0xEA ;D33E ; load A into v-blank-prev
178 0x20 ;D33F
179 0xD3 ;D340
181 0xC3 ;D341 ; return to beginning
182 0x1D ;D342
183 0xD3 ;D343
185 ;;; not in v-blank mode
186 ;; set v-blank-prev to 0
187 0x3E ;D344 ; load 0 into A
188 0x00 ;D345
190 0xEA ;D346 ; load A into v-blank-prev
191 0x20 ;D347
192 0xD3 ;D348
194 0xC3 ;D349 ; return to beginning
195 0x1D ;D34A
196 0xD3 ;D34B
197 ])))
199 (defn step-count-frames []
200 (-> (read-down-button)
201 (d-tick)
202 (tick) ;; skip over data section
203 (d-tick)
204 (view-register "Register A" A)
205 (tick) ;; load-data into A
206 (view-register "Register A" A)
207 (d-tick)
208 (view-memory 0xFF00)
209 (tick) ;; load A into 0xFF00
210 (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 program
231 ;; starts in "mode-select" mode
232 ;; Each button press takes place in a single frame.
233 ;; mode-select-mode takes one of the main buttons
234 ;; which selects one of up to eight modes
235 ;; mode 1 activated by the "A" button
236 ;; the next two button presses indicates the start
237 ;; memory location which to which the bootstrap
238 ;; program will write.
239 ;; This is done by using each of the eight buttons to
240 ;; spell out an 8 bit number. The order of buttons is
241 ;; [:d :u :l :r :start :select :b :a]
242 ;; [:a :start :l] --> 00101001
244 ;; the next button press determines how many bytes are to be
245 ;; written, starting at the start position.
247 ;; then, the actual bytes are entered and are written to the
248 ;; start address in sequence.
250 (defn input-number-assembly []
251 [0x18 ;D31D ; jump over
252 0x02 ;D31E ; the next 2 bytes
253 0x00 ;D31F ; frame-count
254 0x00 ;D320 ; v-blank-prev
256 0xFA ;D321
257 0x41 ;D322 ; load (FF41) into A
258 0xFF ;D323 ; this contains mode flags
260 ;; if we're in v-blank, the bit-1 is 0
261 ;; and bit-2 is 1 Otherwise, it is not v-blank.
262 0xCB ;D324 ; test bit-1 of A
263 0x4F ;D325
265 0xC2 ;D326 ; if bit-1 is not 0
266 0x44 ;D327 ; GOTO not-v-blank
267 0xD3 ;D328
269 0xCB ;D329 ; test bit-0 of A
270 0x47 ;D32A
272 0xCA ;D32B ; if bit-0 is not 1
273 0x44 ;D32C ; GOTO not-v-blank
274 0xD3 ;D32D
276 ;;; in v-blank mode
278 ;; if v-blank-prev was 0,
279 ;; increment frame-count
281 0xFA ;D32E ; load v-blank-prev to A
282 0x20 ;D32F
283 0xD3 ;D330
285 0xCB ;D331
286 0x47 ;D332 ; test bit-0 of A
288 0x20 ;D333 ; skip next section
289 0x07 ;D334 ; if v-blank-prev was not zero
291 ;; v-blank was 0, increment frame-count
292 0xFA ;D335 ; load frame-count into A
293 0x1F ;D336
294 0xD3 ;D337
296 0x3C ;D338 ; inc A
298 0xEA ;D339 ; load A into frame-count
299 0x1F ;D33A
300 0xD3 ;D33B
302 ;; set v-blank-prev to 1
303 0x3E ;D33C ; load 1 into A
304 0x01 ;D33D
306 0xEA ;D33E ; load A into v-blank-prev
307 0x20 ;D33F
308 0xD3 ;D340
310 0xC3 ;D341 ; GOTO input handling code
311 0x4E ;D342
312 0xD3 ;D343
314 ;;; not in v-blank mode
315 ;; set v-blank-prev to 0
316 0x3E ;D344 ; load 0 into A
317 0x00 ;D345
319 0xEA ;D346 ; load A into v-blank-prev
320 0x20 ;D347
321 0xD3 ;D348
323 0xC3 ;D349 ; return to beginning
324 0x1D ;D34A
325 0xD3 ;D34B
327 0x00 ;D34C ; these are here
328 0x00 ;D34D ; for glue
331 ;;; calculate input number based on button presses
332 0x18 ;D34E ; skip next 3 bytes
333 0x03 ;D34F
334 ;D350
335 (Integer/parseInt "00100000" 2) ; select directional pad
336 ;D351
337 (Integer/parseInt "00010000" 2) ; select buttons
338 0x00 ;D352 ; input-number
340 ;; select directional pad, store low bits in B
342 0xFA ;D353 ; load (D350) into A
343 0x50 ;D354 -->
344 0xD3 ;D355 --> D31F
346 0xEA ;D356 ; load A, which is
347 0x00 ;D357 --> ; 00010000, into FF00
348 0xFF ;D358 --> FF00
350 0x06 ;D359
351 ;D35A
352 (Integer/parseInt "11110000" 2) ; "11110000" -> B
353 0xFA ;D35B ; (FF00) -> A
354 0x00 ;D35C
355 0xFF ;D35D
357 0xCB ;D35E ; swap nybbles on A
358 0x37 ;D35F
359 0xA0 ;D360 ; (AND A B) -> A
360 0x47 ;D361 ; A -> B
362 ;; select buttons store bottom bits in C
364 0xFA ; ; load (D351) into A
365 0x51 ; -->
366 0xD3 ; --> D31F
368 0xEA ; ; load (A), which is
369 0x00 ; --> ; 00001000, into FF00
370 0xFF ; --> FF00
372 0x0E ;
373 (Integer/parseInt "00001111" 2) ; "00001111" -> C
375 0xFA ; ; (FF00) -> A
376 0x00 ;
377 0xFF ;
379 0xA1 ; ; (AND A C) -> A
380 0x4F ; ; A -> C
382 ;; combine the B and C registers into the input number
383 0x79 ; ; C -> A
384 0xB0 ; ; (OR A B) -> A
385 0x2F ; ; negate A
387 0xEA ; ; store A into input-number
388 0x52 ;
389 0xD3 ;
391 0xC3 ; ; return to beginning
392 0x1D ;
393 0xD3 ;
394 ])
398 (defn input-number []
399 (-> (tick (mid-game))
400 (IE! 0) ; disable interrupts
401 (inject-item-assembly (input-number-assembly))))
403 (defn test-input-number
404 "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 interrupts
419 ;; --------- CLEANUP
420 0xAF ; zero A [D31E]
421 0x57 ; A->D; makes D=0.
423 ;; --------- FRAME METRONOME
424 0xF1 ;; pop AF (vblank prev) [D320]
426 0x2F ;; invert A
427 0x4F ;; A -> C
429 0xF0 ;; copy STAT into A
430 0x41
432 0x47 ;; A->B
433 0x1F ;; rotate A right
434 0x2F ;; complement A
435 0xA0 ;; A & B --> A
437 0xF5 ;; push AF (vbprev)
439 0xA1 ;; A & C --> A. Now A_0 contains "increment?"
441 0x4F
442 0xCB ;; test bit 0 of C. sadly, this result can't be reused below.
443 0x41
445 0x28 ;; end frame (JUMP) if A_0 = 0.
446 0xF3 ;; TODO: set jump length
448 ;; -------- GET BUTTON INPUT
450 ;; prepare to select bits
452 0x3E ;; load 0x20 into A, to measure dpad
453 0x20
455 0x06 ;; load 0x00 into B
456 0x00 ;; to initialize for "OR" loop
458 0xE0 ;; load A into [FF00] ;; start of OR loop [D33C]
459 0x00
461 0xF0 ;; load A from [FF00]
462 0x00
464 0xE6 ;; bitmask 00001111
465 0x0F
467 0xB0 ;; A or B --> A
469 0xCB ;; check bit 0 of C
470 0x41
472 0x28 ;; JUMP forward if Z=0
473 0x08
475 0x47 ;; A -> B
476 0xCB ;; swap B nybbles
477 0x30
479 0x3E ;; load 0x10 into A, to measure btns
480 0x10
482 0x18 ;; JUMP back to "load A into [FF00]"
483 0xF0
486 ;; ------ TAKE ACTION BASED ON USER INPUT
488 ;; "input mode"
489 ;; mode 0x00 : select mode
490 ;; mode 0x08 : select bytes-to-write
491 ;; mode 0x10 : select hi-bit
492 ;; mode 0x18 : select lo-bit
494 ;; "output mode"
495 ;; mode 0x20 : write bytes
496 ;; mode 0xFF : jump PC
499 ;; registers
500 ;; D : mode select
501 ;; E : count of bytes to write
502 ;; H : address-high
503 ;; L : address-low
505 ;; now A contains the pressed keys
506 0x2F ; complement A, by request. [D34F]
508 0x47 ; A->B ;; now B contains the pressed keys
510 0xCB ; test bit 5 of D (are we in o/p mode?)
511 0x6A
512 0x28 ; if test == 0, skip this o/p section
513 0x13 ; JUMP
515 0xCB ; else, test bit 0 of D (fragile; are we in pc mode?)
516 0x42
517 0x28 ; if test == 0, skip the following command
518 0x01
520 ;; output mode I: moving the program counter
521 0xE9 ; ** move PC to (HL)
523 ;; output mode II: writing bytes
524 0xAF ; zero A
525 0xBB ; compare count to zero. finished writing?
526 0x28 ; if we are finished, jump back to cleanup
527 0x00 ; TODO: set jump length backwards.
529 ;; continue writing bytes
530 0x78 ;; B->A
531 0x22 ;; copy A to (HL) and increment HL.
532 0x18 ;; end frame. [goto D31F]
533 0xB6 ;; JUMP
536 ;; ---- end of o/p section
538 ;; i/p mode
539 ;; 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 0x57
544 0x82 ;; add the mode to A
545 0xEA ;; store A into "thing to execute"
546 0x74 ;; ABSOLUTE LOCATION
547 0xD3 ;; ABSOLUTE LOCATION
549 0x3E ;; load the constant 8 into A
550 0x08
551 0x82 ;; add the mode to A
553 0x57 ;; store the incremented mode into D
554 0x78 ;; B->A; now A contains the pressed keys
556 0x00 ;; var: thing to execute [D374]
558 0x18 ;; end frame
559 0xA8 ;; JUMP
560 ]
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) s
570 (do
571 (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 (do
585 (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 loop
709 (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 bits
723 (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 step
749 (view-memory 0xD31F)))
770 (defn write-memory-assembly []
771 [
772 ;; Main Timing Loop
773 ;; Constantly check for v-blank and Trigger main state machine on
774 ;; every transtion from v-blank to non-v-blank.
776 0x18 ; D31D ; Variable declaration
777 0x02 ; D31E
778 0x00 ; D31F ; frame-count
779 0x00 ; D320 ; v-blank-prev
781 0xF0 ; D321 ; load v-blank mode flags into A
782 0x41
783 0x00
786 ;; Branch dependent on v-blank. v-blank happens when the last two
787 ;; bits in A are "01"
788 0xCB ; D324
789 0x4F ; D325
791 0xC2 ; D326 ; if bit-1 is not 0, then
792 0x3E ; D327 ; GOTO non-v-blank.
793 0xD3 ; D328
795 0xCB ; D329
796 0x47 ; D32A
798 0xCA ; D32B ; if bit-0 is not 1, then
799 0x3E ; D32C ; GOTO non-v-blank.
800 0xD3 ; D32D
802 ;; V-Blank
803 ;; Activate state-machine if this is a transition event.
805 0xFA ; D32E ; load v-bank-prev into A
806 0x20 ; D32F
807 0xD3 ; D330
809 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-prev
822 0x46 ; D339 ; was 0,
823 0xD3 ; D33A ; GOTO state-machine
825 0xC3 ; D33B
826 0x1D ; D33C
827 0xD3 ; D33D ; GOTO beginning
828 ;; END V-blank
830 ;; Non-V-Blank
831 ;; Set v-blank-prev to 0
832 0x3E ; D33E ; load 0 into A
833 0x00 ; D33F
835 0xEA ; D340 ; load A into v-blank-prev
836 0x20 ; D341
837 0xD3 ; D342
839 0xC3 ; D343
840 0x1D ; D344
841 0xD3 ; D345 ; GOTO beginning
842 ;; END Not-V-Blank
845 ;; Main State Machine -- Input Section
846 ;; This is called once every frame.
847 ;; It collects input and uses it to drive the
848 ;; state transitions.
850 ;; Increment frame-count
851 0xFA ; D346 ; load frame-count into A
852 0x1F ; D347
853 0xD3 ; D348
855 0x3C ; D349 ; inc A
857 0xEA ; D34A
858 0x1F ; D34B ; load A into frame-count
859 0xD3 ; D34C
861 0x00 ; D34D ; glue :)
863 0x18 ;D34E ; skip next 3 bytes
864 0x03 ;D34F
865 ;D350
866 (Integer/parseInt "00100000" 2) ; select directional pad
867 ;D351
868 (Integer/parseInt "00010000" 2) ; select buttons
869 0x00 ;D352 ; input-number
871 ;; select directional pad; store low bits in B
873 0xFA ;D353 ; load (D350) into A
874 0x50 ;D354 -->
875 0xD3 ;D355 --> D350
877 0xE0 ;D356 ; load (A), which is
878 0x00 ;D357 --> ; 00010000, into FF00
879 0x00 ;D358 --> FF00 ;; NO-OP
881 0x06 ;D359
882 ;D35A
883 (Integer/parseInt "11110000" 2) ; "11110000" -> B
884 0xF0 ;D35B ; (FF00) -> A
885 0x00 ;D35C
886 0x00 ;D35D ;; NO-OP
888 0xCB ;D35E ; swap nybbles on A
889 0x37 ;D35F
890 0xA0 ;D360 ; (AND A B) -> A
891 0x47 ;D361 ; A -> B
893 ;; select buttons; store bottom bits in C
895 0xFA ;D362 ; load (D351) into A
896 0x51 ;D363 -->
897 0xD3 ;D364 --> D351
899 0xE0 ;D365 ; load (A), which is
900 0x00 ;D366 --> ; 00001000, into FF00
901 0x00 ;D367 --> FF00 ;; NO-OP
903 0x0E ;D368
904 ;D369
905 (Integer/parseInt "00001111" 2) ; "00001111" -> C
907 0xF0 ;D36A ; (FF00) -> A
908 0x00 ;D36B
909 0x00 ;D36C
911 0xA1 ;D36D ; (AND A C) -> A
912 0x4F ;D36E ; A -> C
914 ;; combine the B and C registers into the input number
915 0x79 ;D36F ; C -> A
916 0xB0 ;D370 ; (OR A B) -> A
917 0x2F ;D371 ; negate A
919 0xEA ;D372 ; store A into input-number
920 0x52 ;D373
921 0xD3 ;D374
923 0x00 ;D375
924 0x00 ;D376
925 0x00 ;D377
926 0x00 ;D378
927 0x00 ;D379
928 0x00 ;D37A
929 0x00 ;D37B ; these are here because
930 0x00 ;D37C ; I messed up :(
931 0x00 ;D37D
932 0x00 ;D37E
933 0x00 ;D37F
935 ;; beginning of main state machine
936 0x18 ;D380 ; Declaration of variables
937 0x05 ;D381 ; 5 variables:
938 0x00 ;D382 ; current-mode
939 0x00 ;D383 ; bytes-to-write
940 0x00 ;D384 ; bytes-written
941 0x00 ;D385 ; start-point-high
942 0x00 ;D386 ; start-point-low
945 ;; banch on current mode
946 0xFA ;D387 ; load current-mode (0xD382)
947 0x82 ;D388 ; into A
948 0xD3 ;D389
949 0x00 ;D38A
952 ;; GOTO Mode 0 (input-mode) if current-mode is 0
953 0xFE ;D38B
954 0x00 ;D38C ; compare A with 0x00
956 0xCA ;D38D ; goto Mode 0 if A == 0
957 0xA8 ;D38E
958 0xD3 ;D38F
960 ;; GOTO Mode 1 (set-length) if current-mode is 1
961 0xFE ;D390
962 0x01 ;D391 ; compare A with 0x01
964 0xCA ;D392
965 0xB1 ;D393
966 0xD3 ;D394 ; goto Mode 1 if A == 1
968 ;; GOTO Mode 2 (set-start-point-high) if current mode is 2
969 0xFE ;D395
970 0x02 ;D396 ; compare A with 0x02
972 0xCA ;D397
973 0xBF ;D398
974 0xD3 ;D399 ; goto Mode 2 if A == 2
976 ;; GOTO Mode 3 (set-start-point-low) if current mode is 3
977 0xFE ;D39A
978 0x03 ;D39B
980 0xCA ;D39C
981 0xCD ;D39D
982 0xD3 ;D39E ; goto Mode 3 if A == 3
984 ;; GOTO Mode 4 (write-memory) if current mode is 4
985 0xFE ;D39F
986 0x04 ;D3A0
988 0xCA ;D3A1
989 0xDB ;D3A2
990 0xD3 ;D3A3
992 0x00 ;D3A4
993 ;; End of Mode checking, goto beginning
994 0xC3 ;D3A5
995 0x1D ;D3A6
996 0xD3 ;D3A7
999 ;; Mode 0 -- input-mode mode
1000 ;; means that we are waiting for a mode, so set the mode to
1001 ;; whatever is currently in input-number. If nothing is
1002 ;; entered, then the program stays in input-mode mode
1004 ;; set current-mode to input-number
1005 0xFA ;D3A8 ; load input-number (0xD352)
1006 0x52 ;D3A9 ; into A
1007 0xD3 ;D3AA
1009 0xEA ;D3AB ; load A into current-mode
1010 0x82 ;D3AC ; (0xD382)
1011 0xD3 ;D3AD
1013 0xC3 ;D3AE ; go back to beginning
1014 0x1D ;D3AF
1015 0xD3 ;D3B0
1016 ;; End Mode 0
1019 ;; Mode 1 -- set-length mode
1020 ;; 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 mode
1023 ;; 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 A
1029 0xD3 ;D3B3
1031 0xEA ;D3B4 ; load A into bytes-left-to-write
1032 0x83 ;D3B5 ; (0xD383)
1033 0xD3 ;D3B6
1035 0x3E ;D3B7 ; load 0x02 into A.
1036 0x02 ;D3B8
1038 0xEA ;D3B9 ; load A to current-mode
1039 0x82 ;D3BA ; advancing from Mode 1 to
1040 0xD3 ;D3BB ; Mode 2
1042 0xC3 ;D3BC ; go back to beginning
1043 0x1D ;D3BD
1044 0xD3 ;D3BE
1045 ;; End Mode 1
1048 ;; Mode 2 -- set start-point-high mode
1049 ;; Middle part of the header for writing things to memory.
1050 ;; User specifies the start location in RAM to which
1051 ;; 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 A
1058 0xD3 ;D3C1
1060 0xEA ;D3C2 ; load A into start-point-high
1061 0x85 ;D3C3 ; (0xD385)
1062 0xD3 ;D3C4
1064 0x3E ;D3C5 ; load 0x03 into A.
1065 0x03 ;D3C6
1067 0xEA ;D3C7 ; load A to current-mode,
1068 0x82 ;D3C8 ; advancing from Mode 2 to
1069 0xD3 ;D3C9 ; Mode 3.
1071 0xC3 ;D3CA ; go back to beginning
1072 0x1D ;D3CB
1073 0xD3 ;D3CC
1074 ;;End Mode 2
1077 ;; Mode 3 -- set-start-point-low mode
1078 ;; 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 0x04
1083 0xFA ;D3CD ; load input-number into A
1084 0x52 ;D3CE
1085 0xD3 ;D3CF
1087 0xEA ;D3D0 ; load A into start-point-low
1088 0x86 ;D3D1
1089 0xD3 ;D3D2
1091 0x3E ;D3D3 ; load 0x04 into A.
1092 0x04 ;D3D4
1094 0xEA ;D3D5 ; load A to current-mode,
1095 0x82 ;D3D6 ; advancing from Mode 3 to
1096 0xD3 ;D3D7 ; Mode 4.
1098 0xC3 ;D3D8 ; go back to beginning
1099 0x1D ;D3D9
1100 0xD3 ;D3DA
1102 ;; Mode 4 -- write bytes mode
1104 ;; This is where RAM manipulation happens. User supplies
1105 ;; bytes every frame, which are written sequentially to
1106 ;; start-point until bytes-to-write have been written. Once
1107 ;; 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 0
1112 0xFA ;D3DB ; load bytes-to-write into A
1113 0x83 ;D3DC
1114 0xD3 ;D3DD
1116 0x47 ;D3DE ; load A into B
1118 0xFA ;D3DF ; load bytes-written into A
1119 0x84 ;D3E0
1120 0xD3 ;D3E1
1122 0xB8 ;D3E2 ; compare A with B
1124 0xCA ;D3E3 ; if they are equal, go to cleanup
1125 0x07 ;D3E4
1126 0xD4 ;D3E5
1128 ;; Write Memory Section
1129 ;; Write the input-number, interpreted as an 8-bit number,
1130 ;; into the current target register, determined by
1131 ;; (+ start-point bytes-written).
1132 ;; Then, increment bytes-written by 1.
1134 0xFA ;D3E6 ; load start-point-high into A
1135 0x85 ;D3E7
1136 0xD3 ;D3E8
1138 0x67 ;D3E9 ; load A into H
1140 0xFA ;D3EA ; load start-point-low into A
1141 0x86 ;D3EB
1142 0xD3 ;D3EC
1144 0x6F ;D3ED ; load A into L
1146 0xFA ;D3EE ; load bytes-written into A
1147 0x84 ;D3EF
1148 0xD3 ;D3F0
1150 0x00 ;D3F1 ; These are here because
1151 0x00 ;D3F2 ; I screwed up again.
1152 0x00 ;D3F3
1154 0x85 ;D3F4 ; add L to A; store A in L.
1155 0x6F ;D3F5
1157 0x30 ;D3F6 ; If the addition overflowed,
1158 0x01 ;D3F7
1159 0x24 ;D3F8 ; increment H.
1161 ;; Now, HL points to the correct place in memory
1163 0xFA ;D3F9 ; load input-number into A
1164 0x52 ;D3FA
1165 0xD3 ;D3FB
1167 0x77 ;D3FC ; load A into (HL)
1169 0xFA ;D3FD ; load bytes-written into A
1170 0x84 ;D3FE
1171 0xD3 ;D3FF
1173 0x3C ;D400 ; increment A
1175 0xEA ;D401 ; load A into bytes-written
1176 0x84 ;D402
1177 0xD3 ;D403
1179 0xC3 ;D404 ; go back to beginning.
1180 0x1D ;D405
1181 0xD3 ;D406
1182 ;; End Write Memory Section
1184 ;; Mode 4 Cleanup Section
1185 ;; reset bytes-written to 0
1186 ;; set mode to 0
1187 0x3E ;D407 ; load 0 into A
1188 0x00 ;D408
1190 0xEA ;D409 ; load A into bytes-written
1191 0x84 ;D40A
1192 0xD3 ;D40B
1194 0xEA ;D40C ; load A into current-mode
1195 0x82 ;D40D
1196 0xD3 ;D40E
1198 0xC3 ;D40F ; go back to beginning
1199 0x1D ;D410
1200 0xD3 ;D411
1202 ;; End Mode 4
1204 ])
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 interrupts
1221 (inject-item-assembly (write-memory-assembly))))
1223 (defn test-write-memory []
1224 (set-state! (write-memory))
1225 (dorun
1226 (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-assembly
1233 [start end]
1234 (dorun
1235 (map
1236 #(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-mode
1253 ([] (dylan-test-mode (write-mem-dyl)))
1254 ([target-state]
1255 (let [
1256 v-blank-prev 54046
1257 btn-register 65280
1258 eggs 0xD374
1261 (->
1262 target-state
1264 (tick)
1265 (tick)
1266 (tick)
1267 (tick);; jumps back to beginning
1269 (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 A
1305 (tick)
1306 (DE! 0x1800)
1307 (AF! 0x7700) ;; change inputs @ A
1308 (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-4
1339 ([] (test-mode-4 (write-memory)))
1340 ([target-state]
1341 (->
1342 target-state
1343 (#(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 mode
1354 (#(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 written
1363 (#(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 0xC00F
1372 (#(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 memory
1388 (#(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 UTILITIES
1425 (def opcodes
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 hex
1701 "Converts the number into a hexadecimal-formatted symbol."
1702 [n]
1703 (symbol (str "0x" (.toUpperCase (Integer/toHexString n)))))
1707 (defn arity
1708 "Returns the arity of the given opcode (hex numeral)."
1709 [op]
1710 (cond
1711 (#{0x06 0x0E 0x16 0x1E
1712 0x20 0x26 0x28 0x2E
1713 0x30 0x36 0x38 0x3E
1714 0xC6 0xD6 0xCE 0xDE
1715 0xE0 0xF0 0xE6 0xF6
1716 0xEE 0xFE} op)
1718 (#{0x01 0x08 0x11 0x21
1719 0x31 0xC2 0xC3 0xC4
1720 0xCA 0xDA 0xCC 0xDC
1721 0xCD 0xEA 0xFA} op)
1723 :else
1724 0))