view clojure/com/aurellem/assembly.clj @ 135:eb6ba88088d3

Wrote a more efficient input-number-assembly program; 91 oc -> 60 oc.
author Dylan Holmes <ocsenave@gmail.com>
date Sun, 18 Mar 2012 05:13:19 -0500
parents 7f7cc8858d2e
children ffeeabae7dcd
line wrap: on
line source
1 (ns com.aurellem.assembly
2 (:use (com.aurellem gb-driver vbm title items))
3 (:import [com.aurellem.gb_driver SaveState]))
5 (defn mid-game []
6 (read-state "mid-game"))
8 (defn inject-assembly
9 ([^SaveState state
10 program-counter registers
11 assembly-code]
12 (let [scratch-memory (memory state)]
13 ;; inject assembly code
14 (dorun (map (fn [index val]
15 (aset scratch-memory index val))
16 (range program-counter
17 (+ program-counter (count assembly-code)))
18 assembly-code))
19 (-> state
20 (write-memory! scratch-memory)
21 (write-registers! registers)
22 (PC! program-counter)))))
24 (defn inject-item-assembly
25 ([^SaveState state assembly-code]
26 (inject-assembly state (inc item-list-start)
27 (registers state)
28 assembly-code))
29 ([assembly-code]
30 (inject-item-assembly @current-state assembly-code)))
32 (defn info
33 ([^SaveState state]
34 (println (format "PC: 0x%04X" (PC state)))
35 (println "Instruction:"
36 (format "0x%02X" (aget (memory state) (PC state))))
37 state))
39 (defn print-interrupt
40 [^SaveState state]
41 (println (format "IE: %d" (IE state)))
42 state)
44 (defn print-listing [state begin end]
45 (dorun (map
46 (fn [opcode line]
47 (println (format "0x%04X: 0x%02X" line opcode)))
48 (subvec (vec (memory state)) begin end)
49 (range begin end)))
50 state)
52 (defn run-assembly
53 ([info-fn assembly n]
54 (let [final-state
55 (reduce (fn [state _]
56 (tick (info-fn state)))
57 (inject-item-assembly
58 (mid-game) assembly)
59 (range n))]
60 final-state))
61 ([assembly n]
62 (run-assembly info assembly n)))
64 (def buttons-port 0xFF00)
66 (defn A [state]
67 (bit-shift-right (bit-and 0x0000FF00 (AF state)) 8))
69 (defn B [state]
70 (bit-shift-right (bit-and 0x0000FF00 (BC state)) 8))
72 (defn C [state]
73 (bit-and 0xFF (BC state)))
75 (defn binary-str [num]
76 (format "%08d"
77 (Integer/parseInt
78 (Integer/toBinaryString num) 10)))
80 (defn view-register [state name reg-fn]
81 (println (format "%s: %s" name
82 (binary-str (reg-fn state))))
83 state)
85 (defn view-memory [state mem]
86 (println (format "mem 0x%04X = %s" mem
87 (binary-str (aget (memory state) mem))))
88 state)
90 (defn trace [state]
91 (loop [program-counters [(first (registers @current-state)) ]
92 opcodes [(aget (memory @current-state) (PC @current-state))]]
93 (let [frame-boundary?
94 (com.aurellem.gb.Gb/tick)]
95 (if frame-boundary?
96 [program-counters opcodes]
97 (recur
98 (conj program-counters
99 (first (registers @current-state)))
100 (conj opcodes
101 (aget (memory @current-state)
102 (PC @current-state))))))))
104 (defn print-trace [state n]
105 (let [[program-counters opcodes] (trace state)]
106 (dorun (map (fn [pc op] (println (format "%04X: 0x%02X" pc op)))
107 (take n program-counters)
108 (take n opcodes)))))
110 (defn good-trace []
111 (-> (mid-game) (tick) (IE! 0)
112 (set-inv-mem [0x00 0x00 0X00 0x00])
113 (PC! item-list-start)(print-interrupt)
114 (info) (tick) (info) (tick) (info)))
116 (defn read-down-button []
117 (-> (tick (mid-game))
118 (IE! 0) ; disable interrupts
119 (inject-item-assembly
120 ;; write 00010000 to 0xFF00 to select joypad
121 [0x18 ;D31D ; jump over
122 0x01 ;D31E ; the next 8 bits
123 ;D31F
124 (Integer/parseInt "00100000" 2) ; data section
126 0xFA ;D320 ; load (D31F) into A
127 0x1F ;D321 -->
128 0xD3 ;D322 --> D31F
130 0xEA ;D323 ; load (A), which is
131 0x00 ;D324 --> ; 00010000, into FF00
132 0xFF ;D325 --> FF00
134 0x18 ;D326 ; this is the place where
135 0x01 ;D327 ; we will store whether
136 0x00 ;D328 ; "down" is pressed.
138 0xFA ;D329 ; (FF00) -> A
139 0x00 ;D32A
140 0xFF ;D32B
142 0xCB ;D32C ; Test whether "down"
143 0x5F ;D32D ; is pressed.
145 0x28 ;D32E ; if down is pressed,
146 0x03 ;D32F ; skip the next section
147 ; of code.
148 ;; down-is-not-pressed
149 0xC3 ;D330
150 0x1D ;D331 ; return to beginning
151 0xD3 ;D332
153 ;; down-is-pressed
154 0xEA ;D334 ; write A to D328 if
155 0x28 ;D335 ; "down" was pressed
156 0xD3 ;D336
158 0xC3 ;D330
159 0x1D ;D331 ; return to beginning
160 0xD3 ;D332
161 ])))
163 (defn test-read-down []
164 (= (view-memory (step (step (read-down-button) [:d])) 0xD328)
165 (view-memory (step (step (read-down-button))) 0xD328)))
167 (defn count-frames []
168 (-> (tick (mid-game))
169 (IE! 0) ; disable interrupts
170 (inject-item-assembly
171 [0x18 ;D31D ; jump over
172 0x02 ;D31E ; the next 2 bytes
173 0x00 ;D31F ; frame-count
174 0x00 ;D320 ; v-blank-prev
176 0xFA ;D321
177 0x41 ;D322 ; load (FF41) into A
178 0xFF ;D323 ; this contains mode flags
180 ;; if we're in v-blank, the bit-1 is 0
181 ;; and bit-2 is 1 Otherwise, it is not v-blank.
182 0xCB ;D324 ; test bit-1 of A
183 0x4F ;D325
185 0xC2 ;D326 ; if bit-1 is not 0
186 0x44 ;D327 ; GOTO not-v-blank
187 0xD3 ;D328
189 0xCB ;D329 ; test bit-0 of A
190 0x47 ;D32A
192 0xCA ;D32B ; if bit-0 is not 1
193 0x44 ;D32C ; GOTO not-v-blank
194 0xD3 ;D32D
195 ;;; in v-blank mode
196 ;; if v-blank-prev was 0,
197 ;; increment frame-count
199 0xFA ;D32E ; load v-blank-prev to A
200 0x20 ;D32F
201 0xD3 ;D330
203 0xCB ;D331
204 0x47 ;D332 ; test bit-0 of A
206 0x20 ;D333 ; skip next section
207 0x07 ;D334 ; if v-blank-prev was not zero
209 ;; v-blank was 0, increment frame-count
210 0xFA ;D335 ; load frame-count into A
211 0x1F ;D336
212 0xD3 ;D337
214 0x3C ;D338 ; inc A
216 0xEA ;D339 ; load A into frame-count
217 0x1F ;D33A
218 0xD3 ;D33B
220 ;; set v-blank-prev to 1
221 0x3E ;D33C ; load 1 into A
222 0x01 ;D33D
224 0xEA ;D33E ; load A into v-blank-prev
225 0x20 ;D33F
226 0xD3 ;D340
228 0xC3 ;D341 ; return to beginning
229 0x1D ;D342
230 0xD3 ;D343
232 ;;; not in v-blank mode
233 ;; set v-blank-prev to 0
234 0x3E ;D344 ; load 0 into A
235 0x00 ;D345
237 0xEA ;D346 ; load A into v-blank-prev
238 0x20 ;D347
239 0xD3 ;D348
241 0xC3 ;D349 ; return to beginning
242 0x1D ;D34A
243 0xD3 ;D34B
244 ])))
246 (defn step-count-frames []
247 (-> (read-down-button)
248 (info)
249 (tick) ;; skip over data section
250 (info)
251 (view-register "Register A" A)
252 (tick) ;; load-data into A
253 (view-register "Register A" A)
254 (info)
255 (view-memory 0xFF00)
256 (tick) ;; load A into 0xFF00
257 (view-memory 0xFF00)
258 (info)
259 (tick)
260 (info)
261 (tick)
262 (info)
263 (tick)
264 (info)
265 (tick)
266 (info)
267 (tick)
268 (info)
269 (tick)
270 (print-inventory)))
272 (defn test-count-frames []
273 (= 255 (aget (memory ((apply comp (repeat 255 step))
274 (count-frames)))
275 0xD31F)))
277 ;; specs for main bootstrap program
278 ;; starts in "mode-select" mode
279 ;; Each button press takes place in a single frame.
280 ;; mode-select-mode takes one of the main buttons
281 ;; which selects one of up to eight modes
282 ;; mode 1 activated by the "A" button
283 ;; the next two button presses indicates the start
284 ;; memory location which to which the bootstrap
285 ;; program will write.
286 ;; This is done by using each of the eight buttons to
287 ;; spell out an 8 bit number. The order of buttons is
288 ;; [:d :u :l :r :start :select :b :a]
289 ;; [:a :start :l] --> 00101001
291 ;; the next button press determines how many bytes are to be
292 ;; written, starting at the start position.
294 ;; then, the actual bytes are entered and are written to the
295 ;; start address in sequence.
297 (defn input-number-assembly []
298 [0x18 ;D31D ; jump over
299 0x02 ;D31E ; the next 2 bytes
300 0x00 ;D31F ; frame-count
301 0x00 ;D320 ; v-blank-prev
303 0xFA ;D321
304 0x41 ;D322 ; load (FF41) into A
305 0xFF ;D323 ; this contains mode flags
307 ;; if we're in v-blank, the bit-1 is 0
308 ;; and bit-2 is 1 Otherwise, it is not v-blank.
309 0xCB ;D324 ; test bit-1 of A
310 0x4F ;D325
312 0xC2 ;D326 ; if bit-1 is not 0
313 0x44 ;D327 ; GOTO not-v-blank
314 0xD3 ;D328
316 0xCB ;D329 ; test bit-0 of A
317 0x47 ;D32A
319 0xCA ;D32B ; if bit-0 is not 1
320 0x44 ;D32C ; GOTO not-v-blank
321 0xD3 ;D32D
323 ;;; in v-blank mode
325 ;; if v-blank-prev was 0,
326 ;; increment frame-count
328 0xFA ;D32E ; load v-blank-prev to A
329 0x20 ;D32F
330 0xD3 ;D330
332 0xCB ;D331
333 0x47 ;D332 ; test bit-0 of A
335 0x20 ;D333 ; skip next section
336 0x07 ;D334 ; if v-blank-prev was not zero
338 ;; v-blank was 0, increment frame-count
339 0xFA ;D335 ; load frame-count into A
340 0x1F ;D336
341 0xD3 ;D337
343 0x3C ;D338 ; inc A
345 0xEA ;D339 ; load A into frame-count
346 0x1F ;D33A
347 0xD3 ;D33B
349 ;; set v-blank-prev to 1
350 0x3E ;D33C ; load 1 into A
351 0x01 ;D33D
353 0xEA ;D33E ; load A into v-blank-prev
354 0x20 ;D33F
355 0xD3 ;D340
357 0xC3 ;D341 ; GOTO input handling code
358 0x4E ;D342
359 0xD3 ;D343
361 ;;; not in v-blank mode
362 ;; set v-blank-prev to 0
363 0x3E ;D344 ; load 0 into A
364 0x00 ;D345
366 0xEA ;D346 ; load A into v-blank-prev
367 0x20 ;D347
368 0xD3 ;D348
370 0xC3 ;D349 ; return to beginning
371 0x1D ;D34A
372 0xD3 ;D34B
374 0x00 ;D34C ; these are here
375 0x00 ;D34D ; for glue
378 ;;; calculate input number based on button presses
379 0x18 ;D34E ; skip next 3 bytes
380 0x03 ;D34F
381 ;D350
382 (Integer/parseInt "00100000" 2) ; select directional pad
383 ;D351
384 (Integer/parseInt "00010000" 2) ; select buttons
385 0x00 ;D352 ; input-number
387 ;; select directional pad, store low bits in B
389 0xFA ;D353 ; load (D350) into A
390 0x50 ;D354 -->
391 0xD3 ;D355 --> D31F
393 0xEA ;D356 ; load A, which is
394 0x00 ;D357 --> ; 00010000, into FF00
395 0xFF ;D358 --> FF00
397 0x06 ;D359
398 ;D35A
399 (Integer/parseInt "11110000" 2) ; "11110000" -> B
400 0xFA ;D35B ; (FF00) -> A
401 0x00 ;D35C
402 0xFF ;D35D
404 0xCB ;D35E ; swap nybbles on A
405 0x37 ;D35F
406 0xA0 ;D360 ; (AND A B) -> A
407 0x47 ;D361 ; A -> B
409 ;; select buttons store bottom bits in C
411 0xFA ; ; load (D351) into A
412 0x51 ; -->
413 0xD3 ; --> D31F
415 0xEA ; ; load (A), which is
416 0x00 ; --> ; 00001000, into FF00
417 0xFF ; --> FF00
419 0x0E ;
420 (Integer/parseInt "00001111" 2) ; "00001111" -> C
422 0xFA ; ; (FF00) -> A
423 0x00 ;
424 0xFF ;
426 0xA1 ; ; (AND A C) -> A
427 0x4F ; ; A -> C
429 ;; combine the B and C registers into the input number
430 0x79 ; ; C -> A
431 0xB0 ; ; (OR A B) -> A
432 0x2F ; ; negate A
434 0xEA ; ; store A into input-number
435 0x52 ;
436 0xD3 ;
438 0xC3 ; ; return to beginning
439 0x1D ;
440 0xD3 ;
441 ])
444 (defn print-pc [state]
445 (println (format "PC: 0x%04X" (PC state)))
446 state)
448 (defn print-op [state]
449 (println (format "OP: 0x%02X" (aget (memory state) (PC state))))
450 state)
452 (defn d-tick
453 ([state]
454 (-> state print-pc print-op tick)))
456 (defn input-number []
457 (-> (tick (mid-game))
458 (IE! 0) ; disable interrupts
459 (inject-item-assembly (input-number-assembly))))
461 (defn test-input-number
462 "Input freestyle buttons and observe the effects at the repl."
463 []
464 (set-state! (input-number))
465 (dotimes [_ 90000] (step (view-memory @current-state 0xD352))))
495 (defn write-memory-assembly* []
496 [
497 ;; --------- FRAME METRONOME
499 0x18 ;; D31D
500 0x01
501 0x00 ;; v-blank-prev D31F
503 0xFA ;; load modes into A
504 0x41
505 0xFF
507 0x47 ;; A -> B
508 0xCB ;; rotate A
509 0x2F
510 0x2F ;; invert A
512 0xA0
513 0x47 ;; now B_0 contains (VB==1)
515 0xFA ;; load v-blank-prev
516 0x1F
517 0xD3
519 0x2F ;; complement v-blank-prev
521 0xA0 ;; A & B --> A
522 0x4F ;; now C_0 contains increment?
525 0x78 ;; B->A
527 0xEA ;; spit A --> vbprev
528 0x1F
529 0xD3
531 0x41 ;test C_0
532 0x20 ; nonzero jump
533 0x04
535 0xC3 ; ; go back to beginning
536 0x1D ;
537 0xD3 ;
539 ;; -------- GET BUTTON INPUT
540 0x00 ;; var: which-input D339
541 ;; prepare to select bits
543 0x01 ;; load 0x0000 into BC
544 0x00
545 0x00
547 0x3E ;; load 0x20 into A
548 0x20
551 0xEA ;; load A into [FF00] ;; D33F (not D33C)
552 0x00
553 0xFF
555 0xFA ;; load A from [FF00]
556 0x00
557 0xFF
559 0xE6 ;; bitmask 00001111
560 0x0F
562 0xB0 ;; A or B --> A
564 0xCB
565 0x41 ;; test bit 0 of C
566 0x20 ;; jump forward if 1
567 0x08
569 0x47 ;; A -> B
570 0xCB ;; swap B nybbles
571 0x30
572 0x0C ;; increment C
573 0x3E ;; load 0x10 into A
574 0x10
575 0x18 ;; JUMP back to "load A into [FF00]" [20 steps?]
576 0xEB
578 ;; now A contains the pressed keys
579 0xEA ;; copy keys to input-number [D339]
580 0x39
581 0xD3
582 0x18 ;;
583 0xEB
586 ]
587 )
589 (defn write-mem-dyl []
590 (-> (tick (mid-game))
591 (IE! 0)
592 (inject-item-assembly (write-memory-assembly*))))
595 (defn dylan []
596 (->
597 (write-mem-dyl)
598 (tick)
599 (tick)
600 (tick)
601 (tick)
602 (tick)
603 (tick)
604 (tick)
605 (tick)
606 (tick)
607 (tick)
608 (tick)
609 (tick)
610 (tick)
611 (tick)
612 (tick)
613 (tick)
614 (tick)
615 (tick)
616 (tick)
617 (tick)
618 (tick)
619 (tick)
620 (tick)
621 (tick)
622 (tick)
623 (tick)
624 (tick)
625 (tick)
628 (d-tick)
631 (view-register "A" A)
632 (view-register "B" B)
633 (view-register "C" C)
635 ))
640 (defn d2 []
641 (->
642 (write-mem-dyl)
643 (view-memory 0xD31F)
644 step step step step step
645 (view-memory 0xD31F)))
666 (defn write-memory-assembly []
667 [
668 ;; Main Timing Loop
669 ;; Constantly check for v-blank and Trigger main state machine on
670 ;; every transtion from v-blank to non-v-blank.
672 0x18 ; D31D ; Variable declaration
673 0x02 ; D31E
674 0x00 ; D31F ; frame-count
675 0x00 ; D320 ; v-blank-prev
677 0xF0 ; D321 ; load v-blank mode flags into A
678 0x41
679 0x00
682 ;; Branch dependent on v-blank. v-blank happens when the last two
683 ;; bits in A are "01"
684 0xCB ; D324
685 0x4F ; D325
687 0xC2 ; D326 ; if bit-1 is not 0, then
688 0x3E ; D327 ; GOTO non-v-blank.
689 0xD3 ; D328
691 0xCB ; D329
692 0x47 ; D32A
694 0xCA ; D32B ; if bit-0 is not 1, then
695 0x3E ; D32C ; GOTO non-v-blank.
696 0xD3 ; D32D
698 ;; V-Blank
699 ;; Activate state-machine if this is a transition event.
701 0xFA ; D32E ; load v-bank-prev into A
702 0x20 ; D32F
703 0xD3 ; D330
705 0xFE ; D331 ; compare A to 0. >--------\
706 0x00 ; D332 \
707 ; |
708 ;; set v-blank-prev to 1. |
709 0x3E ; D333 ; load 1 into A. |
710 0x01 ; D334 |
711 ; |
712 0xEA ; D335 ; load A into v-blank-prev |
713 0x20 ; D336 |
714 0xD3 ; D337 |
715 ; /
716 ;; if v-blank-prev was 0, activate state-machine <------/
717 0xCA ; D338 ; if v-blank-prev
718 0x46 ; D339 ; was 0,
719 0xD3 ; D33A ; GOTO state-machine
721 0xC3 ; D33B
722 0x1D ; D33C
723 0xD3 ; D33D ; GOTO beginning
724 ;; END V-blank
726 ;; Non-V-Blank
727 ;; Set v-blank-prev to 0
728 0x3E ; D33E ; load 0 into A
729 0x00 ; D33F
731 0xEA ; D340 ; load A into v-blank-prev
732 0x20 ; D341
733 0xD3 ; D342
735 0xC3 ; D343
736 0x1D ; D344
737 0xD3 ; D345 ; GOTO beginning
738 ;; END Not-V-Blank
741 ;; Main State Machine -- Input Section
742 ;; This is called once every frame.
743 ;; It collects input and uses it to drive the
744 ;; state transitions.
746 ;; Increment frame-count
747 0xFA ; D346 ; load frame-count into A
748 0x1F ; D347
749 0xD3 ; D348
751 0x3C ; D349 ; inc A
753 0xEA ; D34A
754 0x1F ; D34B ; load A into frame-count
755 0xD3 ; D34C
757 0x00 ; D34D ; glue :)
759 0x18 ;D34E ; skip next 3 bytes
760 0x03 ;D34F
761 ;D350
762 (Integer/parseInt "00100000" 2) ; select directional pad
763 ;D351
764 (Integer/parseInt "00010000" 2) ; select buttons
765 0x00 ;D352 ; input-number
767 ;; select directional pad; store low bits in B
769 0xFA ;D353 ; load (D350) into A
770 0x50 ;D354 -->
771 0xD3 ;D355 --> D350
773 0xE0 ;D356 ; load (A), which is
774 0x00 ;D357 --> ; 00010000, into FF00
775 0x00 ;D358 --> FF00 ;; NO-OP
777 0x06 ;D359
778 ;D35A
779 (Integer/parseInt "11110000" 2) ; "11110000" -> B
780 0xF0 ;D35B ; (FF00) -> A
781 0x00 ;D35C
782 0x00 ;D35D ;; NO-OP
784 0xCB ;D35E ; swap nybbles on A
785 0x37 ;D35F
786 0xA0 ;D360 ; (AND A B) -> A
787 0x47 ;D361 ; A -> B
789 ;; select buttons; store bottom bits in C
791 0xFA ;D362 ; load (D351) into A
792 0x51 ;D363 -->
793 0xD3 ;D364 --> D351
795 0xE0 ;D365 ; load (A), which is
796 0x00 ;D366 --> ; 00001000, into FF00
797 0x00 ;D367 --> FF00 ;; NO-OP
799 0x0E ;D368
800 ;D369
801 (Integer/parseInt "00001111" 2) ; "00001111" -> C
803 0xF0 ;D36A ; (FF00) -> A
804 0x00 ;D36B
805 0x00 ;D36C
807 0xA1 ;D36D ; (AND A C) -> A
808 0x4F ;D36E ; A -> C
810 ;; combine the B and C registers into the input number
811 0x79 ;D36F ; C -> A
812 0xB0 ;D370 ; (OR A B) -> A
813 0x2F ;D371 ; negate A
815 0xEA ;D372 ; store A into input-number
816 0x52 ;D373
817 0xD3 ;D374
819 0x00 ;D375
820 0x00 ;D376
821 0x00 ;D377
822 0x00 ;D378
823 0x00 ;D379
824 0x00 ;D37A
825 0x00 ;D37B ; these are here because
826 0x00 ;D37C ; I messed up :(
827 0x00 ;D37D
828 0x00 ;D37E
829 0x00 ;D37F
831 ;; beginning of main state machine
832 0x18 ;D380 ; Declaration of variables
833 0x05 ;D381 ; 5 variables:
834 0x00 ;D382 ; current-mode
835 0x00 ;D383 ; bytes-to-write
836 0x00 ;D384 ; bytes-written
837 0x00 ;D385 ; start-point-high
838 0x00 ;D386 ; start-point-low
841 ;; banch on current mode
842 0xFA ;D387 ; load current-mode (0xD382)
843 0x82 ;D388 ; into A
844 0xD3 ;D389
845 0x00 ;D38A
848 ;; GOTO Mode 0 (input-mode) if current-mode is 0
849 0xFE ;D38B
850 0x00 ;D38C ; compare A with 0x00
852 0xCA ;D38D ; goto Mode 0 if A == 0
853 0xA8 ;D38E
854 0xD3 ;D38F
856 ;; GOTO Mode 1 (set-length) if current-mode is 1
857 0xFE ;D390
858 0x01 ;D391 ; compare A with 0x01
860 0xCA ;D392
861 0xB1 ;D393
862 0xD3 ;D394 ; goto Mode 1 if A == 1
864 ;; GOTO Mode 2 (set-start-point-high) if current mode is 2
865 0xFE ;D395
866 0x02 ;D396 ; compare A with 0x02
868 0xCA ;D397
869 0xBF ;D398
870 0xD3 ;D399 ; goto Mode 2 if A == 2
872 ;; GOTO Mode 3 (set-start-point-low) if current mode is 3
873 0xFE ;D39A
874 0x03 ;D39B
876 0xCA ;D39C
877 0xCD ;D39D
878 0xD3 ;D39E ; goto Mode 3 if A == 3
880 ;; GOTO Mode 4 (write-memory) if current mode is 4
881 0xFE ;D39F
882 0x04 ;D3A0
884 0xCA ;D3A1
885 0xDB ;D3A2
886 0xD3 ;D3A3
888 0x00 ;D3A4
889 ;; End of Mode checking, goto beginning
890 0xC3 ;D3A5
891 0x1D ;D3A6
892 0xD3 ;D3A7
895 ;; Mode 0 -- input-mode mode
896 ;; means that we are waiting for a mode, so set the mode to
897 ;; whatever is currently in input-number. If nothing is
898 ;; entered, then the program stays in input-mode mode
900 ;; set current-mode to input-number
901 0xFA ;D3A8 ; load input-number (0xD352)
902 0x52 ;D3A9 ; into A
903 0xD3 ;D3AA
905 0xEA ;D3AB ; load A into current-mode
906 0x82 ;D3AC ; (0xD382)
907 0xD3 ;D3AD
909 0xC3 ;D3AE ; go back to beginning
910 0x1D ;D3AF
911 0xD3 ;D3B0
912 ;; End Mode 0
915 ;; Mode 1 -- set-length mode
916 ;; This is the header for writing things to memory.
917 ;; User specifies the number of bytes to write.
918 ;; Mode is auto advanced to Mode 2 after this mode
919 ;; completes.
921 ;; Set bytes left to write to input-number;
922 ;; set current-mode to 0x02.
923 0xFA ;D3B1 ; load input-number (0xD352)
924 0x52 ;D3B2 ; into A
925 0xD3 ;D3B3
927 0xEA ;D3B4 ; load A into bytes-left-to-write
928 0x83 ;D3B5 ; (0xD383)
929 0xD3 ;D3B6
931 0x3E ;D3B7 ; load 0x02 into A.
932 0x02 ;D3B8
934 0xEA ;D3B9 ; load A to current-mode
935 0x82 ;D3BA ; advancing from Mode 1 to
936 0xD3 ;D3BB ; Mode 2
938 0xC3 ;D3BC ; go back to beginning
939 0x1D ;D3BD
940 0xD3 ;D3BE
941 ;; End Mode 1
944 ;; Mode 2 -- set start-point-high mode
945 ;; Middle part of the header for writing things to memory.
946 ;; User specifies the start location in RAM to which
947 ;; data will be written.
948 ;; Mode is auto advanced to Mode 3 after this mode completes.
950 ;; Set start-point-high to input-number;
951 ;; set current mode to 0x03.
952 0xFA ;D3BF ; load input-number (0xD352)
953 0x52 ;D3C0 ; into A
954 0xD3 ;D3C1
956 0xEA ;D3C2 ; load A into start-point-high
957 0x85 ;D3C3 ; (0xD385)
958 0xD3 ;D3C4
960 0x3E ;D3C5 ; load 0x03 into A.
961 0x03 ;D3C6
963 0xEA ;D3C7 ; load A to current-mode,
964 0x82 ;D3C8 ; advancing from Mode 2 to
965 0xD3 ;D3C9 ; Mode 3.
967 0xC3 ;D3CA ; go back to beginning
968 0x1D ;D3CB
969 0xD3 ;D3CC
970 ;;End Mode 2
973 ;; Mode 3 -- set-start-point-low mode
974 ;; Final part of header for writing things to memory.
975 ;; User specifies the low bytes of 16 bit start-point.
977 ;; Set start-point-low to input-number;
978 ;; set current mode to 0x04
979 0xFA ;D3CD ; load input-number into A
980 0x52 ;D3CE
981 0xD3 ;D3CF
983 0xEA ;D3D0 ; load A into start-point-low
984 0x86 ;D3D1
985 0xD3 ;D3D2
987 0x3E ;D3D3 ; load 0x04 into A.
988 0x04 ;D3D4
990 0xEA ;D3D5 ; load A to current-mode,
991 0x82 ;D3D6 ; advancing from Mode 3 to
992 0xD3 ;D3D7 ; Mode 4.
994 0xC3 ;D3D8 ; go back to beginning
995 0x1D ;D3D9
996 0xD3 ;D3DA
998 ;; Mode 4 -- write bytes mode
1000 ;; This is where RAM manipulation happens. User supplies
1001 ;; bytes every frame, which are written sequentially to
1002 ;; start-point until bytes-to-write have been written. Once
1003 ;; bytes-to-write have been written, the mode is reset to 0.
1005 ;; compare bytes-written with bytes-to-write.
1006 ;; if they are the same, then reset mode to 0
1008 0xFA ;D3DB ; load bytes-to-write into A
1009 0x83 ;D3DC
1010 0xD3 ;D3DD
1012 0x47 ;D3DE ; load A into B
1014 0xFA ;D3DF ; load bytes-written into A
1015 0x84 ;D3E0
1016 0xD3 ;D3E1
1018 0xB8 ;D3E2 ; compare A with B
1020 0xCA ;D3E3 ; if they are equal, go to cleanup
1021 0x07 ;D3E4
1022 0xD4 ;D3E5
1024 ;; Write Memory Section
1025 ;; Write the input-number, interpreted as an 8-bit number,
1026 ;; into the current target register, determined by
1027 ;; (+ start-point bytes-written).
1028 ;; Then, increment bytes-written by 1.
1030 0xFA ;D3E6 ; load start-point-high into A
1031 0x85 ;D3E7
1032 0xD3 ;D3E8
1034 0x67 ;D3E9 ; load A into H
1036 0xFA ;D3EA ; load start-point-low into A
1037 0x86 ;D3EB
1038 0xD3 ;D3EC
1040 0x6F ;D3ED ; load A into L
1042 0xFA ;D3EE ; load bytes-written into A
1043 0x84 ;D3EF
1044 0xD3 ;D3F0
1046 0x00 ;D3F1 ; These are here because
1047 0x00 ;D3F2 ; I screwed up again.
1048 0x00 ;D3F3
1050 0x85 ;D3F4 ; add L to A; store A in L.
1051 0x6F ;D3F5
1053 0x30 ;D3F6 ; If the addition overflowed,
1054 0x01 ;D3F7
1055 0x24 ;D3F8 ; increment H.
1057 ;; Now, HL points to the correct place in memory
1059 0xFA ;D3F9 ; load input-number into A
1060 0x52 ;D3FA
1061 0xD3 ;D3FB
1063 0x77 ;D3FC ; load A into (HL)
1065 0xFA ;D3FD ; load bytes-written into A
1066 0x84 ;D3FE
1067 0xD3 ;D3FF
1069 0x3C ;D400 ; increment A
1071 0xEA ;D401 ; load A into bytes-written
1072 0x84 ;D402
1073 0xD3 ;D403
1075 0xC3 ;D404 ; go back to beginning.
1076 0x1D ;D405
1077 0xD3 ;D406
1078 ;; End Write Memory Section
1080 ;; Mode 4 Cleanup Section
1081 ;; reset bytes-written to 0
1082 ;; set mode to 0
1083 0x3E ;D407 ; load 0 into A
1084 0x00 ;D408
1086 0xEA ;D409 ; load A into bytes-written
1087 0x84 ;D40A
1088 0xD3 ;D40B
1090 0xEA ;D40C ; load A into current-mode
1091 0x82 ;D40D
1092 0xD3 ;D40E
1094 0xC3 ;D40F ; go back to beginning
1095 0x1D ;D410
1096 0xD3 ;D411
1098 ;; End Mode 4
1100 ])
1104 (def frame-count 0xD31F)
1105 (def input 0xD352)
1106 (def current-mode 0xD382)
1107 (def bytes-to-write 0xD383)
1108 (def bytes-written 0xD384)
1109 (def start-point-high 0xD385)
1110 (def start-point-low 0xD386)
1114 (defn write-memory []
1115 (-> (tick (mid-game))
1116 (IE! 0) ; disable interrupts
1117 (inject-item-assembly (write-memory-assembly))))
1119 (defn test-write-memory []
1120 (set-state! (write-memory))
1121 (dorun
1122 (dotimes [_ 5000]
1123 (view-memory (step @current-state) current-mode))))
1125 (def bytes-to-write 0xD383)
1126 (def start-point 0xD384)
1128 (defn print-blank-assembly
1129 [start end]
1130 (dorun
1131 (map
1132 #(println (format "0x00 ;%04X " %))
1133 (range start end))))
1135 (defn test-mode-2 []
1136 (->
1137 (write-memory)
1138 (view-memory frame-count)
1139 (step)
1140 (step [:a])
1141 (step [:b])
1142 (step [:start])
1143 (step [])
1144 (view-memory frame-count)))
1146 (defn test-mode-4
1147 ([] (test-mode-4 (write-memory)))
1148 ([target-state]
1149 (->
1150 target-state
1151 (#(do (println "memory from 0xC00F to 0xC01F:"
1152 (subvec (vec (memory %)) 0xC00F 0xC01F)) %))
1153 (view-memory current-mode)
1154 (step [])
1155 (step [])
1156 (step [])
1157 (#(do (println "after three steps") %))
1158 (view-memory current-mode)
1160 ;; Activate memory writing mode
1162 (#(do (println "step with [:a]") %))
1163 (step [:a])
1164 (view-memory current-mode)
1165 (view-memory bytes-to-write)
1166 (view-memory start-point-high)
1167 (view-memory start-point-low)
1169 ;; Specify four bytes to be written
1171 (#(do (println "step with [:select]")%))
1172 (step [:select])
1173 (view-memory current-mode)
1174 (view-memory bytes-to-write)
1175 (view-memory start-point-high)
1176 (view-memory start-point-low)
1178 ;; Specify target memory address as 0xC00F
1180 (#(do (println "step with [:u :d]")%))
1181 (step [:u :d])
1182 (view-memory current-mode)
1183 (view-memory bytes-to-write)
1184 (view-memory start-point-high)
1185 (view-memory start-point-low)
1187 (#(do (println "step with [:a :b :start :select]")%))
1188 (step [:a :b :start :select])
1189 (view-memory current-mode)
1190 (view-memory bytes-to-write)
1191 (view-memory start-point-high)
1192 (view-memory start-point-low)
1194 ;; Start reprogramming memory
1196 (#(do (println "step with [:a]")%))
1197 (step [:a])
1198 (view-memory current-mode)
1199 (view-memory bytes-written)
1201 (#(do (println "step with [:b]")%))
1202 (step [:b])
1203 (view-memory current-mode)
1204 (view-memory bytes-written)
1206 (#(do (println "step with [:a :b]")%))
1207 (step [:a :b])
1208 (view-memory current-mode)
1209 (view-memory bytes-written)
1211 (#(do (println "step with [:select]")%))
1212 (step [:select])
1213 (view-memory current-mode)
1214 (view-memory bytes-written)
1216 ;; Reprogramming done, program ready for more commands.
1218 (#(do (println "step with []")%))
1219 (step [])
1220 (view-memory current-mode)
1221 (view-memory bytes-written)
1223 (#(do (println "memory from 0xC00F to 0xC01F:"
1224 (subvec (vec (memory %)) 0xC00F 0xC01F)) %)))))