rlm@239
|
1 (ns com.aurellem.gb.dylan-assembly
|
rlm@239
|
2 "A much more compact version of write-memory-assembly"
|
rlm@239
|
3 {:author "Dylan Holmes"}
|
rlm@377
|
4 (:use (com.aurellem.gb gb-driver assembly util vbm))
|
rlm@239
|
5 (:import [com.aurellem.gb.gb_driver SaveState]))
|
rlm@239
|
6
|
rlm@376
|
7 ;; Specs for main bootstrap program
|
rlm@376
|
8
|
rlm@376
|
9 ;; Number-Input
|
rlm@376
|
10 ;; Number input works using all eight buttons to
|
rlm@376
|
11 ;; spell out an 8 bit number. The order of buttons is
|
rlm@376
|
12 ;; [:d :u :l :r :start :select :b :a] --> 11111111
|
rlm@376
|
13 ;; [ :l :start :a] --> 00101001
|
rlm@376
|
14
|
rlm@376
|
15 ;;; MODE-SELECT
|
rlm@376
|
16 ;; The bootstrap program starts in MODE-SELECT mode.
|
rlm@376
|
17 ;; MODE-SELECT transitions to one of three modes depending
|
rlm@376
|
18 ;; on which buttons are pressed:
|
rlm@376
|
19 ;; 0 (no-buttons) : MODE-SELECT
|
rlm@376
|
20 ;; 8 [:start] : WRITE-BYTES
|
rlm@376
|
21 ;; 0xFF (all-buttons) : JUMP
|
rlm@376
|
22
|
rlm@376
|
23
|
rlm@376
|
24 ;;; WRITE-BYTES
|
rlm@376
|
25
|
rlm@376
|
26 ;; WRITE-BYTES mode writes sequences of arbitray values to
|
rlm@376
|
27 ;; arbitray memory locations. It expects you to enter a
|
rlm@376
|
28 ;; header of three bytes describing what to write:
|
rlm@376
|
29
|
rlm@376
|
30 ;; Byte 0 : Number of Bytes to Write
|
rlm@376
|
31 ;; Byte 1 : Start Address High Byte
|
rlm@376
|
32 ;; Byte 1 : Start Address Low Byte
|
rlm@376
|
33
|
rlm@376
|
34 ;; Then, you enter the number of bytes specified in Byte 0
|
rlm@376
|
35 ;; they are written to the start address in
|
rlm@376
|
36 ;; sequence. After the last byte is written control
|
rlm@376
|
37 ;; returns to MODE-SELECT mode.
|
rlm@376
|
38
|
rlm@376
|
39 ;; Example: to write the sequence [1 2 3 4] starting at
|
rlm@376
|
40 ;; address 0xC01F enter
|
rlm@376
|
41 ;; Byte 0 : 4 (will write four bytes)
|
rlm@376
|
42 ;; Byte 1 : 0xC0 (high byte of 0xC01F)
|
rlm@376
|
43 ;; Byte 2 : 0x1F (low byte of 0xC01F)
|
rlm@376
|
44 ;; Byte 3 : 1 (write 1 to 0xC01F)
|
rlm@376
|
45 ;; Byte 4 : 2 (write 2 to 0xC020)
|
rlm@376
|
46 ;; Byte 5 : 3 (write 3 to 0xC021)
|
rlm@376
|
47 ;; Byte 6 : 4 (write 4 to 0xC022)
|
rlm@376
|
48
|
rlm@376
|
49 ;;; JUMP
|
rlm@376
|
50 ;; JUMP mode jumps program control to any arbitray
|
rlm@376
|
51 ;; location. It expects you to enter two bytes which
|
rlm@376
|
52 ;; correspond to the high and low bytes of the memory
|
rlm@376
|
53 ;; address to which you want to jump.
|
rlm@376
|
54 ;; Byte 0 : Jump Address High Byte
|
rlm@376
|
55 ;; Byte 1 : Jump Address Low Byte
|
rlm@376
|
56
|
rlm@376
|
57 ;; Example: to jump to address 0x1234 enter
|
rlm@376
|
58 ;; Byte 0 : 0x12 (high byte of 0x1234)
|
rlm@376
|
59 ;; Byte 1 : 0x34 (low byte of 0x1234)
|
rlm@376
|
60
|
rlm@376
|
61
|
rlm@239
|
62 (defn write-memory-assembly-compact
|
rlm@239
|
63 "Currently, grabs input from the user each frame."
|
rlm@239
|
64 []
|
rlm@239
|
65 [
|
rlm@239
|
66 ;; --------- FRAME METRONOME
|
rlm@239
|
67 0x18 ;; jump ahead to cleanup. first time only.
|
rlm@239
|
68 0x40 ;; v-blank-prev [D31E]
|
rlm@239
|
69
|
rlm@239
|
70 0xFA ;; load modes into A [D31F]
|
rlm@239
|
71 0x41
|
rlm@239
|
72 0xFF
|
rlm@239
|
73
|
rlm@239
|
74 0x47 ;; A -> B
|
rlm@239
|
75 0xCB ;; rotate A
|
rlm@239
|
76 0x2F
|
rlm@239
|
77 0x2F ;; invert A
|
rlm@239
|
78
|
rlm@239
|
79 0xA0
|
rlm@239
|
80 0x47 ;; now B_0 contains (VB==1)
|
rlm@239
|
81
|
rlm@239
|
82 0xFA ;; load v-blank-prev
|
rlm@239
|
83 0x1E
|
rlm@239
|
84 0xD3
|
rlm@239
|
85
|
rlm@239
|
86 0x2F ;; complement v-blank-prev
|
rlm@239
|
87
|
rlm@239
|
88 0xA0 ;; A & B --> A
|
rlm@239
|
89 0x4F ;; now C_0 contains increment?
|
rlm@239
|
90
|
rlm@239
|
91
|
rlm@239
|
92 0x78 ;; B->A
|
rlm@239
|
93 0xEA ;; spit A --> vbprev
|
rlm@239
|
94 0x1E
|
rlm@239
|
95 0xD3
|
rlm@239
|
96
|
rlm@239
|
97 0xCB ;test C_0
|
rlm@239
|
98 0x41
|
rlm@239
|
99 0x20 ; JUMP ahead to button input if nonzero
|
rlm@239
|
100 0x02
|
rlm@239
|
101 0x18 ; JUMP back to frame metronome (D31F)
|
rlm@239
|
102 0xE7
|
rlm@239
|
103
|
rlm@239
|
104 ;; -------- GET BUTTON INPUT
|
rlm@239
|
105
|
rlm@239
|
106 ;; btw, C_0 is now 1
|
rlm@239
|
107 ;; prepare to select bits
|
rlm@239
|
108
|
rlm@239
|
109 0x06 ;; load 0x00 into B
|
rlm@239
|
110 0x00 ;; to initialize for "OR" loop
|
rlm@239
|
111
|
rlm@239
|
112 0x3E ;; load 0x20 into A, to measure dpad
|
rlm@239
|
113 0x20
|
rlm@239
|
114
|
rlm@239
|
115
|
rlm@239
|
116 0xE0 ;; load A into [FF00] ;; start of OR loop [D33C]
|
rlm@239
|
117 0x00
|
rlm@239
|
118
|
rlm@239
|
119 0xF0 ;; load A from [FF00]
|
rlm@239
|
120 0x00
|
rlm@239
|
121
|
rlm@239
|
122 0xE6 ;; bitmask 00001111
|
rlm@239
|
123 0x0F
|
rlm@239
|
124
|
rlm@239
|
125 0xB0 ;; A or B --> A
|
rlm@239
|
126 0xCB
|
rlm@239
|
127 0x41 ;; test bit 0 of C
|
rlm@239
|
128 0x28 ;; JUMP forward if 0
|
rlm@239
|
129 0x08
|
rlm@239
|
130
|
rlm@239
|
131 0x47 ;; A -> B
|
rlm@239
|
132 0xCB ;; swap B nybbles
|
rlm@239
|
133 0x30
|
rlm@239
|
134 0x0C ;; increment C
|
rlm@239
|
135 0x3E ;; load 0x10 into A, to measure btns
|
rlm@239
|
136 0x10
|
rlm@239
|
137 0x18 ;; JUMP back to "load A into [FF00]" [20 steps?]
|
rlm@239
|
138 0xED
|
rlm@239
|
139
|
rlm@239
|
140
|
rlm@239
|
141 ;; ------ TAKE ACTION BASED ON USER INPUT
|
rlm@239
|
142
|
rlm@239
|
143 ;; "input mode"
|
rlm@239
|
144 ;; mode 0x00 : select mode
|
rlm@239
|
145 ;; mode 0x08 : select bytes-to-write
|
rlm@239
|
146 ;; mode 0x10 : select hi-bit
|
rlm@239
|
147 ;; mode 0x18 : select lo-bit
|
rlm@239
|
148
|
rlm@239
|
149 ;; "output mode"
|
rlm@239
|
150 ;; mode 0x20 : write bytes
|
rlm@239
|
151 ;; mode 0xFF : jump PC
|
rlm@239
|
152
|
rlm@239
|
153
|
rlm@239
|
154 ;; registers
|
rlm@239
|
155 ;; D : mode select
|
rlm@239
|
156 ;; E : count of bytes to write
|
rlm@239
|
157 ;; H : address-high
|
rlm@239
|
158 ;; L : address-low
|
rlm@239
|
159
|
rlm@239
|
160 ;; now A contains the pressed keys
|
rlm@239
|
161 0x2F ; complement A, by request. [D34F]
|
rlm@239
|
162
|
rlm@239
|
163 0x47 ; A->B ;; now B contains the pressed keys
|
rlm@239
|
164 0x7B ; E->A ;; now A contains the count.
|
rlm@239
|
165
|
rlm@239
|
166 0xCB ; test bit 5 of D (are we in o/p mode?)
|
rlm@239
|
167 0x6A
|
rlm@239
|
168 0x28 ; if test == 0, skip this o/p section
|
rlm@239
|
169 0x13 ; JUMP
|
rlm@239
|
170
|
rlm@239
|
171 0xCB ; else, test bit 0 of D (fragile; are we in pc mode?)
|
rlm@239
|
172 0x42
|
rlm@239
|
173 0x28 ; if test == 0, skip the following command
|
rlm@239
|
174 0x01
|
rlm@239
|
175
|
rlm@239
|
176 ;; output mode I: moving the program counter
|
rlm@239
|
177 0xE9 ; ** move PC to (HL)
|
rlm@239
|
178
|
rlm@239
|
179 ;; output mode II: writing bytes
|
rlm@239
|
180 0xFE ; A compare 0. finished writing?
|
rlm@239
|
181 0x00
|
rlm@239
|
182 0x20 ; if we are not finished, skip cleanup
|
rlm@239
|
183 0x04 ; JUMP
|
rlm@239
|
184
|
rlm@239
|
185 ;; CLEANUP
|
rlm@239
|
186 ;; btw, A is already zero.
|
rlm@239
|
187 0xAF ; zero A [D35F]
|
rlm@239
|
188 0x57 ; A->D; makes D=0.
|
rlm@239
|
189 0x18 ; end of frame
|
rlm@239
|
190 0xBC
|
rlm@239
|
191
|
rlm@239
|
192 ;; ---- end of cleanup
|
rlm@239
|
193
|
rlm@239
|
194
|
rlm@239
|
195 ;; continue writing bytes
|
rlm@239
|
196 0x1D ;; decrement E, the number of bytes to write [D363]
|
rlm@239
|
197 0x78 ;; B->A; now A contains the pressed keys
|
rlm@239
|
198 0x77 ;; copy A to (HL)
|
rlm@239
|
199 0x23 ;; increment HL
|
rlm@239
|
200 0x18 ;; end frame. [goto D31F]
|
rlm@239
|
201 0xB6 ;; TODO: set skip length backwards
|
rlm@239
|
202
|
rlm@239
|
203
|
rlm@239
|
204 ;; ---- end of o/p section
|
rlm@239
|
205
|
rlm@239
|
206 ;; i/p mode
|
rlm@239
|
207 ;; adhere to the mode discipline:
|
rlm@239
|
208 ;; D must be one of 0x00 0x08 0x10 0x18.
|
rlm@239
|
209
|
rlm@239
|
210 0x3E ;; load the constant 57 into A. [D369]
|
rlm@239
|
211 0x57
|
rlm@239
|
212 0x82 ;; add the mode to A
|
rlm@239
|
213 0xEA ;; store A into "thing to execute"
|
rlm@239
|
214 0x74
|
rlm@239
|
215 0xD3
|
rlm@239
|
216
|
rlm@239
|
217 0x3E ;; load the constant 8 into A
|
rlm@239
|
218 0x08
|
rlm@239
|
219 0x82 ;; add the mode to A
|
rlm@239
|
220
|
rlm@239
|
221 0x57 ;; store the incremented mode into D
|
rlm@239
|
222 0x78 ;; B->A; now A contains the pressed keys
|
rlm@239
|
223
|
rlm@239
|
224 0x00 ;; var: thing to execute [D374]
|
rlm@239
|
225
|
rlm@239
|
226 0x18 ;; end frame
|
rlm@239
|
227 0xA8])
|
rlm@239
|
228
|
rlm@239
|
229 (defn write-mem-compact []
|
rlm@239
|
230 (-> (tick (mid-game))
|
rlm@239
|
231 (IE! 0)
|
rlm@239
|
232 (inject-item-assembly (write-memory-assembly-compact))))
|
rlm@239
|
233
|
rlm@377
|
234 (defn test-write-bytes-mode []
|
rlm@377
|
235 (let [target-address 0xD135
|
rlm@377
|
236 [target-high target-low] (disect-bytes-2 target-address)
|
rlm@377
|
237 assembly [0xF3 0x18 0xFE 0x12]
|
rlm@377
|
238 get-mem-region #(subvec (vec (memory %))
|
rlm@377
|
239 target-address (+ target-address 20))
|
rlm@377
|
240 before (write-mem-compact)
|
rlm@377
|
241 after
|
rlm@377
|
242 (-> before
|
rlm@377
|
243 (step []) ; make sure it can handle blanks
|
rlm@377
|
244 (step []) ; at the beginning.
|
rlm@377
|
245 (step [])
|
rlm@377
|
246 (step [:start]) ; select WRITE-BYTES mode
|
rlm@377
|
247 (step (buttons 4)) ; write 4 bytes
|
rlm@377
|
248 (step (buttons target-high))
|
rlm@377
|
249 (step (buttons target-low))
|
rlm@377
|
250 (step (buttons (nth assembly 0)))
|
rlm@377
|
251 (step (buttons (nth assembly 1)))
|
rlm@377
|
252 (step (buttons (nth assembly 2)))
|
rlm@377
|
253 (step (buttons (nth assembly 3)))
|
rlm@377
|
254 (step [])
|
rlm@377
|
255 (step [])
|
rlm@377
|
256 (step []))]
|
rlm@377
|
257 (println "before :" (get-mem-region before))
|
rlm@377
|
258 (println "after :" (get-mem-region after))
|
rlm@377
|
259 (assert (= assembly (take 4 (get-mem-region after))))
|
rlm@377
|
260 after))
|
rlm@239
|
261
|
rlm@377
|
262 (defn test-jump-mode []
|
rlm@377
|
263 (let [target-address 0xC01F
|
rlm@377
|
264 [target-high target-low] (disect-bytes-2 target-address)
|
rlm@377
|
265 post-jump
|
rlm@377
|
266 (-> (test-write-bytes-mode)
|
rlm@377
|
267 (step [])
|
rlm@377
|
268 (step [])
|
rlm@377
|
269 (step [])
|
rlm@377
|
270 (step (buttons 0xFF)) ; Select JUMP mode.
|
rlm@377
|
271 (step (buttons target-high))
|
rlm@377
|
272 (step (buttons target-low)))
|
rlm@377
|
273 program-counters
|
rlm@377
|
274 (capture-program-counter
|
rlm@377
|
275 post-jump
|
rlm@377
|
276 10000)]
|
rlm@377
|
277 (println program-counters)
|
rlm@377
|
278 (assert (contains? (set program-counters) target-address))
|
rlm@377
|
279 post-jump))
|
rlm@377
|
280
|
rlm@377
|
281
|
rlm@377
|
282 (defn test-loop []
|
rlm@377
|
283 (contains?
|
rlm@377
|
284 (set
|
rlm@377
|
285 (capture-program-counter
|
rlm@377
|
286 (-> (mid-game)
|
rlm@377
|
287 ;; (IE! 0)
|
rlm@377
|
288 (set-memory-range 0xD135 [0xF3 0x18 0xFE])
|
rlm@377
|
289 (PC! 0xD135)) 10000))
|
rlm@377
|
290 0xD136))
|
rlm@377
|
291
|
rlm@377
|
292
|
rlm@377
|
293
|
rlm@377
|
294
|
rlm@377
|
295 |