rlm@608
|
1 #+title: Pokemon Yellow Total Control Hack
|
rlm@608
|
2 #+author: Robert McIntyre
|
rlm@608
|
3 #+email: rlm@mit.edu
|
rlm@608
|
4 #+description: Taking over Pokemon Yellow from the inside.
|
rlm@608
|
5 #+keywords: pokemon, pokemon yellow, rom, gameboy, assembly, hex, pointers, clojure
|
rlm@608
|
6 #+SETUPFILE: ../../aurellem/org/setup.org
|
rlm@608
|
7 #+INCLUDE: ../../aurellem/org/level-0.org
|
rlm@608
|
8 #+BABEL: :exports both :noweb yes :cache no :mkdirp yes
|
rlm@608
|
9 #+OPTIONS: num:2
|
rlm@608
|
10
|
rlm@608
|
11
|
rlm@609
|
12 Full Source : http://hg.bortreb.com/vba-clojure
|
rlm@609
|
13
|
rlm@619
|
14
|
rlm@619
|
15 #+BEGIN_HTML html
|
rlm@619
|
16 <div class="figure">
|
rlm@619
|
17 <p>
|
rlm@619
|
18 <iframe width="420" height="315"
|
rlm@619
|
19 src="https://www.youtube.com/embed/p5T81yHkHtI"
|
rlm@619
|
20 frameborder="0" allowfullscreen></iframe>
|
rlm@619
|
21 <p>
|
rlm@619
|
22 #+END_HTML
|
rlm@609
|
23 Youtube Video w/ Visual Keypresses: http://www.youtube.com/watch?v=p5T81yHkHtI
|
rlm@619
|
24 #+BEGIN_HTML
|
rlm@619
|
25 </div>
|
rlm@619
|
26 #+END_HTML
|
rlm@609
|
27
|
rlm@609
|
28 Special Thanks to:
|
rlm@609
|
29
|
rlm@609
|
30 - http://tasvideos.org/2913S.html for the save corruption hack which
|
rlm@609
|
31 is used at the start of this run.
|
rlm@609
|
32 - http://www.everyponysings.com/ for providing the midi file I used
|
rlm@609
|
33 to create the song at the end.
|
rlm@609
|
34 - http://www.zophar.net/ for the terminal font.
|
rlm@609
|
35
|
rlm@609
|
36
|
rlm@609
|
37 * Introduction
|
rlm@609
|
38
|
rlm@609
|
39 Think of pokemon yellow as creating a little universe with certain
|
rlm@609
|
40 rules. Inside that universe, you can buy items, defeat rival trainers,
|
rlm@609
|
41 and raise your pokemon. But within that universe, you are bound by the
|
rlm@609
|
42 rules of pokemon. You can't build new buildings, or change the music,
|
rlm@609
|
43 or change your clothes.. There are some games (like chess), where it
|
rlm@609
|
44 is not possible to alter the rules of the game from within the
|
rlm@609
|
45 game. No matter what moves you make in chess, you can never change the
|
rlm@609
|
46 rules of the game so that it becomes checkers or basketball. The point
|
rlm@609
|
47 of this run is to show that you CAN change the rules in pokemon
|
rlm@612
|
48 yellow. There is a certain sequence of valid actions (like walking
|
rlm@612
|
49 from one place to another or buying items) that will allow you to
|
rlm@612
|
50 transform pokemon yellow into Pacman, or Tetris, or Pong, or a MIDI
|
rlm@612
|
51 player, or anything else you can imagine.
|
rlm@609
|
52
|
rlm@609
|
53 * Background
|
rlm@609
|
54
|
rlm@609
|
55 [[http://tasvideos.org/2913S.html][This speedrun]] by Felipe Lopes de Freitas (p4wn3r), beats pokemon
|
rlm@609
|
56 yellow in only 1 minute and 36 seconds. It does it by corrupting the
|
rlm@609
|
57 in-game item list so that he can advance the list past its normal
|
rlm@609
|
58 limit of 20 items. The memory immediately after the item list includes
|
rlm@609
|
59 the warp points for the current map, and by treating that data as
|
rlm@609
|
60 items and switching and dropping them, he can make the door from his
|
rlm@609
|
61 house take him directly to the end of the game.
|
rlm@609
|
62
|
rlm@609
|
63 When I first saw that speedrun, I was amazed at how fast pokemon
|
rlm@609
|
64 yellow could be beaten, and that it was possible to manipulate the
|
rlm@611
|
65 game from the inside, using only the item list. I wondered how far I
|
rlm@609
|
66 could extend the techniques found in p4wn3r's run.
|
rlm@609
|
67
|
rlm@609
|
68 The gameboy is an 8 bit computer. That means that ultimately, anything
|
rlm@609
|
69 that happens in pokemon is a result of the gameboy's CPU reading a
|
rlm@609
|
70 stream of 8 bit numbers and doing whatever those numbers mean. For
|
rlm@609
|
71 example, in the gameboy, the numbers:
|
rlm@609
|
72
|
rlm@609
|
73 [62 16 37 224 47 240 37 230 15 55]
|
rlm@609
|
74
|
rlm@609
|
75 mean to check which buttons are currently pressed and copy that result
|
rlm@609
|
76 into the "A" register. With enough numbers, you can spell out an
|
rlm@609
|
77 interactive program that reads input from the buttons and allows you
|
rlm@609
|
78 to write any program you want to the gameboy. Once you have assembled
|
rlm@609
|
79 such a program and forced the game to run it, you have won, since you
|
rlm@611
|
80 can use that program to write any other program (like Tetris or
|
rlm@611
|
81 Pacman) over pokemon yellow's code. I call a program that allows you
|
rlm@609
|
82 to write any other program a "bootstrapping program". So, the goal is
|
rlm@609
|
83 to somehow get a bootstrapping program into pokemon yellow and then
|
rlm@609
|
84 force yellow to run that program instead of its own.
|
rlm@609
|
85
|
rlm@609
|
86 How can we spell out such a program? Everything in the game is
|
rlm@611
|
87 ultimately numbers, including all items, pokemon, levels, etc. In
|
rlm@609
|
88 particular, the item list looks like:
|
rlm@609
|
89
|
rlm@610
|
90 #+begin_example
|
rlm@609
|
91 item-one-id (0-255)
|
rlm@609
|
92 item-one-quantity (0-255)
|
rlm@609
|
93 item-two-id (0-255)
|
rlm@609
|
94 item-two-quantity (0-255)
|
rlm@609
|
95 .
|
rlm@609
|
96 .
|
rlm@609
|
97 .
|
rlm@610
|
98 #+end_example
|
rlm@609
|
99
|
rlm@609
|
100 Let's consider the button measuring program [37 62 16 37 224 37 240
|
rlm@609
|
101 37 230 15 55] from before. Interpreted as items and item quantities, it is
|
rlm@609
|
102
|
rlm@610
|
103 #+begin_example
|
rlm@609
|
104 lemonade x16
|
rlm@609
|
105 guard spec. x224
|
rlm@609
|
106 leaf stone x240
|
rlm@609
|
107 guard spec. x230
|
rlm@609
|
108 parlyz heal x55
|
rlm@610
|
109 #+end_example
|
rlm@609
|
110
|
rlm@609
|
111 So, if we can get the right items in the right quantities, we can
|
rlm@609
|
112 spell out a bootstrapping program. Likewise, when writing the
|
rlm@609
|
113 bootstrapping program, we must be careful to only use numbers that are
|
rlm@609
|
114 also valid items and quantities. This is hard because there aren't
|
rlm@609
|
115 many different items to work with, and many machine instructions
|
rlm@609
|
116 actually take 2 or even 3 numbers in a row, which severely restricts
|
rlm@609
|
117 the types of items you can use. I ended up needing about 92 numbers to
|
rlm@609
|
118 implement a bootstrap program. Half of those numbers were elaborate
|
rlm@609
|
119 ways of doing nothing and were just there so that the entire program
|
rlm@609
|
120 was also a valid item list.
|
rlm@609
|
121
|
rlm@609
|
122 The final part of the hack is getting pokemon yellow to execute the
|
rlm@609
|
123 new program after it has been assembled with items. Fortunately,
|
rlm@609
|
124 pokemon keeps a number called a function pointer within easy reach of
|
rlm@609
|
125 the corrupted item list. This function pointer is the starting point
|
rlm@609
|
126 (address) of a program which the game runs every so often to check for
|
rlm@611
|
127 poison and do general maintenance. By shifting an item over this
|
rlm@609
|
128 function pointer, I can rewrite that address to point to the
|
rlm@609
|
129 bootstrapping program, and make the game execute it. Without this
|
rlm@609
|
130 function pointer, it would not be possible to take over the game.
|
rlm@609
|
131
|
rlm@609
|
132 * The Run
|
rlm@609
|
133
|
rlm@611
|
134 I start off and name my rival Lp/k. These characters will eventually be
|
rlm@609
|
135 treated as items and shifted over the function pointer, causing it to
|
rlm@609
|
136 execute the bootstrapping program that will soon be constructed. I
|
rlm@609
|
137 start the run the same as p4wn3r's and restart the game while saving,
|
rlm@609
|
138 so that the pokemon list is corrupted. By switching the 8th and 10th
|
rlm@609
|
139 pokemon, I corrupt the item list and can now scroll down past the 20th
|
rlm@609
|
140 item. I shift items around to increase the text speed to maximum and
|
rlm@609
|
141 rewrite the warp point of my house to Celadon Dept. Store. (p4wn3r
|
rlm@609
|
142 used this to go directly to the hall of fame and win the game in his
|
rlm@609
|
143 run.) I deposit many 0x00 glitch items into the PC from my corrupted
|
rlm@611
|
144 inventory for later use. Then, I withdraw the potion from the
|
rlm@609
|
145 PC. This repairs my item list by overflowing the item counter from
|
rlm@609
|
146 0xFF back to 0x00, though the potion is obliterated in the process. I
|
rlm@609
|
147 then take 255 glitch items with ID 0x00 from the computer into my
|
rlm@609
|
148 personal items.
|
rlm@609
|
149
|
rlm@609
|
150 Leaving my house takes me directly to Celadon Dept. store, where I
|
rlm@609
|
151 sell two 0x00 items for 414925 each, giving myself essentially max
|
rlm@609
|
152 money. I hit every floor of the department store, gathering the
|
rlm@609
|
153 following items:
|
rlm@609
|
154
|
rlm@609
|
155 #+begin_example
|
rlm@609
|
156 +-------------------+----------+
|
rlm@609
|
157 |##| Item | Quantity |
|
rlm@609
|
158 +--+----------------+----------+
|
rlm@609
|
159 |1 | TM02 | 98 |
|
rlm@609
|
160 |2 | TM37 | 71 |
|
rlm@609
|
161 |3 | TM05 | 1 |
|
rlm@609
|
162 |4 | TM09 | 1 |
|
rlm@609
|
163 |5 | burn-heal | 12 |
|
rlm@609
|
164 |6 | ice-heal | 55 |
|
rlm@609
|
165 |7 | parlyz-heal | 99 |
|
rlm@609
|
166 |8 | parlyz-heal | 55 |
|
rlm@609
|
167 |9 | TM18 | 1 |
|
rlm@609
|
168 |10| fire-stone | 23 |
|
rlm@609
|
169 |11| water-stone | 29 |
|
rlm@609
|
170 |12| x-accuracy | 58 |
|
rlm@609
|
171 |13| guard-spec | 99 |
|
rlm@609
|
172 |14| guard-spec | 24 |
|
rlm@609
|
173 |15| lemonade | 16 |
|
rlm@609
|
174 |16| TM13 | 1 |
|
rlm@609
|
175 +--+----------------+----------+
|
rlm@609
|
176 #+end_example
|
rlm@609
|
177
|
rlm@609
|
178 After gathering these items, I deposit them in the appropriate order
|
rlm@609
|
179 into the item PC to spell out my bootstrapping program. Writing a full
|
rlm@609
|
180 bootstrap program in one go using only items turned out to be too
|
rlm@609
|
181 hard, so I split the process up into three parts. The program that I
|
rlm@609
|
182 actually construct using items is very limited. It reads only from the
|
rlm@614
|
183 A, B, start, and select buttons, and writes 4 bits each frame starting
|
rlm@614
|
184 at a fixed point in memory. After it writes 200 or so bytes, it jumps
|
rlm@609
|
185 directly to what it just wrote. In my run, I use this program to write
|
rlm@612
|
186 another bootstrapping program that can write any number of bytes to
|
rlm@609
|
187 any location in memory, and then jump to any location in memory. This
|
rlm@609
|
188 new program also can write 8 bits per frame by using all the
|
rlm@609
|
189 buttons. Using this new bootstrap program, I write a final
|
rlm@611
|
190 bootstrapping program that does everything the previous bootstrapping
|
rlm@609
|
191 program does except it also displays the bytes it is writing to memory
|
rlm@609
|
192 on the screen.
|
rlm@609
|
193
|
rlm@611
|
194 After completing this bootstrapping program, I go to the Celadon
|
rlm@609
|
195 mansion, because I find the metaness of that building to be
|
rlm@609
|
196 sufficiently high to serve as an exit point for the pokemon
|
rlm@609
|
197 universe. I corrupt my item list again by switching corrupted pokemon,
|
rlm@611
|
198 scroll down to my rival's name and discard until it is equal to the
|
rlm@609
|
199 address of my bootstrapping program, and then swap it with the
|
rlm@611
|
200 function pointer. Once the menu is closed, the bootstrapping program
|
rlm@609
|
201 takes over, and I write the payload....
|
rlm@609
|
202
|
rlm@609
|
203 * Infrastructure
|
rlm@609
|
204
|
rlm@609
|
205 The entire video was completely produced by bots --- I didn't manually
|
rlm@609
|
206 play the game at all to produce this speedrun. Here is a brief account
|
rlm@618
|
207 of the infrastructure I built to make the video. The entire source of
|
rlm@609
|
208 the project is available at http://hg.bortreb.com/vba-clojure
|
rlm@609
|
209
|
rlm@611
|
210 The first step was to build a programmatic interface to pokemon
|
rlm@609
|
211 yellow. So, I downloaded vba-rerecording from
|
rlm@609
|
212 http://code.google.com/p/vba-rerecording/. After repairing their
|
rlm@609
|
213 broken auto-tools scripts so that it would compile on GNU/Linux, I
|
rlm@609
|
214 added a low level C interface that I could call from Java via
|
rlm@609
|
215 JNI. This C interface gives me basic control over the emulator: I can
|
rlm@609
|
216 step the emulator either one clock cycle or one frame, and I can get
|
rlm@609
|
217 the contents of any memory location or register. The interface also
|
rlm@609
|
218 allows me to freeze the state of the emulator, save it to a Java
|
rlm@609
|
219 object, and reload that state again at any time.
|
rlm@609
|
220
|
rlm@609
|
221 I built a layer of [[http://clojure.org/][clojure]] code on top of the JNI bindings to get an
|
rlm@609
|
222 entirely functional interface to vba-rerecording. This interface
|
rlm@609
|
223 treats state of the emulator as an immutable object, and allows me to
|
rlm@609
|
224 do everything I could do with the lower level C interface in a
|
rlm@609
|
225 functional manner. Using this functional code, I wrote search programs
|
rlm@609
|
226 that take a particular game-state and try out different combinations
|
rlm@611
|
227 of button presses to get any desired effect. By combining different
|
rlm@609
|
228 styles of search with different initial conditions, I created high
|
rlm@609
|
229 level functions that could each accomplish a certain general task,
|
rlm@609
|
230 like walking and buying items. For example, here is some actual code:
|
rlm@609
|
231
|
rlm@609
|
232 #+begin_src clojure
|
rlm@609
|
233 (defn-memo viridian-store->oaks-lab
|
rlm@609
|
234 ([] (viridian-store->oaks-lab
|
rlm@609
|
235 (get-oaks-parcel)))
|
rlm@609
|
236 ([script]
|
rlm@609
|
237 (->> script
|
rlm@609
|
238 (walk [↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
|
rlm@609
|
239 ← ← ← ← ← ← ← ← ←
|
rlm@609
|
240 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
|
rlm@609
|
241 ← ←
|
rlm@609
|
242 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
|
rlm@609
|
243 ↓ ↓ ↓ ↓ ↓ ↓ ↓
|
rlm@609
|
244 → → → → → → → →
|
rlm@609
|
245 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
|
rlm@609
|
246 ← ← ← ← ←
|
rlm@609
|
247 ↓ ↓ ↓ ↓
|
rlm@609
|
248 ])
|
rlm@609
|
249 (walk-thru-grass
|
rlm@609
|
250 [↓ ↓ ↓ ↓ ↓ ↓ ↓])
|
rlm@609
|
251 (walk [↓ ↓ ← ↓ ↓ ↓ ←
|
rlm@609
|
252 ↓ ↓ ↓ ↓ ↓ ↓
|
rlm@609
|
253 → → → ↑])
|
rlm@609
|
254
|
rlm@609
|
255 (do-nothing 1))))
|
rlm@609
|
256 #+end_src
|
rlm@609
|
257
|
rlm@609
|
258 This script walks from the Viridian City pokemon store to Oak's
|
rlm@609
|
259 Lab in the most efficient way possible. The walk-thru-grass function
|
rlm@611
|
260 guarantees that no wild battles will happen by manipulating the game's
|
rlm@609
|
261 random number generator.
|
rlm@609
|
262
|
rlm@609
|
263 #+begin_src clojure
|
rlm@609
|
264 (defn-memo hacking-10
|
rlm@609
|
265 ([] (hacking-10 (hacking-9)))
|
rlm@609
|
266 ([script]
|
rlm@609
|
267 (->> script
|
rlm@609
|
268 begin-deposit
|
rlm@609
|
269 (deposit-held-item 17 230)
|
rlm@609
|
270 (deposit-held-item-named :parlyz-heal 55)
|
rlm@609
|
271 (deposit-held-item 14 178)
|
rlm@609
|
272 (deposit-held-item-named :water-stone 29)
|
rlm@609
|
273 (deposit-held-item 14 32)
|
rlm@609
|
274 (deposit-held-item-named :TM18 1)
|
rlm@609
|
275 (deposit-held-item 13 1)
|
rlm@609
|
276 (deposit-held-item 13 191)
|
rlm@609
|
277 (deposit-held-item-named :TM02 98)
|
rlm@609
|
278 (deposit-held-item-named :TM09 1)
|
rlm@609
|
279 close-menu)))
|
rlm@609
|
280 #+end_src
|
rlm@609
|
281
|
rlm@609
|
282 This script calculates the fastest sequence of key presses to deposit
|
rlm@611
|
283 the requested items into a PC, assuming that the character starts out
|
rlm@609
|
284 in front of a computer.
|
rlm@609
|
285
|
rlm@611
|
286 I also wrote functions that could grovel through the game's memory and
|
rlm@611
|
287 present the internal data structures in usable ways. For example, the
|
rlm@609
|
288 function =print-inventory= returns the current inventory in a human
|
rlm@609
|
289 readable format.
|
rlm@609
|
290
|
rlm@612
|
291 #+begin_src clojure :results output :exports both
|
rlm@609
|
292 (com.aurellem.gb.items/print-inventory)
|
rlm@609
|
293 #+end_src
|
rlm@609
|
294
|
rlm@609
|
295 #+results:
|
rlm@609
|
296 #+begin_example
|
rlm@609
|
297 +-------------------+----------+
|
rlm@609
|
298 |##| Item | Quantity |
|
rlm@609
|
299 +--+----------------+----------+
|
rlm@609
|
300 |0 | poke-ball | 14 |
|
rlm@609
|
301 |1 | TM28 | 1 |
|
rlm@609
|
302 |2 | TM11 | 1 |
|
rlm@609
|
303 |3 | TM45 | 1 |
|
rlm@609
|
304 |4 | nugget | 1 |
|
rlm@609
|
305 |5 | s.s.ticket | 1 |
|
rlm@609
|
306 |6 | helix-fossil | 1 |
|
rlm@609
|
307 |7 | moon-stone | 1 |
|
rlm@609
|
308 +--+----------------+----------+
|
rlm@609
|
309
|
rlm@609
|
310 #+end_example
|
rlm@609
|
311
|
rlm@609
|
312
|
rlm@609
|
313 Armed with these functions, I constructed a bootstrapping program that
|
rlm@611
|
314 could be expressed as items. This is particularly hard, since many
|
rlm@609
|
315 useful opcodes do not correspond any item, and the item quantities
|
rlm@609
|
316 must all be less than 99.
|
rlm@609
|
317
|
rlm@609
|
318 Here is the first bootstrapping program in all its glory.
|
rlm@609
|
319
|
rlm@609
|
320 #+begin_src clojure
|
rlm@609
|
321 (defn pc-item-writer-program
|
rlm@609
|
322 []
|
rlm@609
|
323 (let [;;limit 75
|
rlm@609
|
324 limit 201 ;; (item-hack 201 is the smallest I could make this.)
|
rlm@609
|
325 [target-high target-low] (disect-bytes-2 pokemon-list-start)]
|
rlm@609
|
326 (flatten
|
rlm@609
|
327 [[0x00 ;; (item-hack) no-op (can't buy repel (1E) at celadon)
|
rlm@609
|
328 0x1E ;; load limit into E
|
rlm@609
|
329 limit
|
rlm@609
|
330 0x3F ;; (item-hack) set carry flag no-op
|
rlm@609
|
331
|
rlm@609
|
332 ;; load 2 into C.
|
rlm@609
|
333 0x0E ;; C == 1 means input-first nybble
|
rlm@609
|
334 0x04 ;; C == 0 means input-second nybble
|
rlm@609
|
335
|
rlm@609
|
336 0x21 ;; load target into HL
|
rlm@609
|
337 target-low
|
rlm@609
|
338 target-high
|
rlm@609
|
339 0x37 ;; (item-hack) set carry flag no-op
|
rlm@609
|
340
|
rlm@609
|
341 0x00 ;; (item-hack) no-op
|
rlm@609
|
342 0x37 ;; (item-hack) set carry flag no-op
|
rlm@609
|
343
|
rlm@609
|
344 0x00 ;; (item-hack) no-op
|
rlm@609
|
345 0xF3 ;; disable interrupts
|
rlm@609
|
346 ;; Input Section
|
rlm@609
|
347
|
rlm@609
|
348 0x3E ;; load 0x20 into A, to measure buttons
|
rlm@609
|
349 0x10
|
rlm@609
|
350
|
rlm@609
|
351 0x00 ;; (item-hack) no-op
|
rlm@609
|
352 0xE0 ;; load A into [FF00]
|
rlm@609
|
353 0x00
|
rlm@609
|
354
|
rlm@609
|
355 0xF0 ;; load 0xFF00 into A to get
|
rlm@609
|
356 0x00 ;; button presses
|
rlm@609
|
357
|
rlm@609
|
358 0xE6
|
rlm@609
|
359 0x0F ;; select bottom four bits of A
|
rlm@609
|
360 0x37 ;; (item-hack) set carry flag no-op
|
rlm@609
|
361
|
rlm@609
|
362 0x00 ;; (item-hack) no-op
|
rlm@609
|
363 0xB8 ;; see if input is different (CP A B)
|
rlm@609
|
364
|
rlm@609
|
365 0x00 ;; (item-hack) (INC SP)
|
rlm@609
|
366 0x28 ;; repeat above steps if input is not different
|
rlm@609
|
367 ;; (jump relative backwards if B != A)
|
rlm@609
|
368 0xED ;; (literal -19) (item-hack) -19 == egg bomb (TM37)
|
rlm@609
|
369
|
rlm@609
|
370 0x47 ;; load A into B
|
rlm@609
|
371
|
rlm@609
|
372 0x0D ;; dec C
|
rlm@609
|
373 0x37 ;; (item-hack) set-carry flag
|
rlm@609
|
374 ;; branch based on C:
|
rlm@609
|
375 0x20 ;; JR NZ
|
rlm@609
|
376 23 ;; skip "input second nybble" and "jump to target" below
|
rlm@609
|
377
|
rlm@609
|
378 ;; input second nybble
|
rlm@609
|
379
|
rlm@609
|
380 0x0C ;; inc C
|
rlm@609
|
381 0x0C ;; inc C
|
rlm@609
|
382
|
rlm@609
|
383 0x00 ;; (item-hack) no-op
|
rlm@609
|
384 0xE6 ;; select bottom bits
|
rlm@609
|
385 0x0F
|
rlm@609
|
386 0x37 ;; (item-hack) set-carry flag no-op
|
rlm@609
|
387
|
rlm@609
|
388 0x00 ;; (item-hack) no-op
|
rlm@609
|
389 0xB2 ;; (OR A D) -> A
|
rlm@609
|
390
|
rlm@609
|
391 0x22 ;; (do (A -> (HL)) (INC HL))
|
rlm@609
|
392
|
rlm@609
|
393 0x1D ;; (DEC E)
|
rlm@609
|
394
|
rlm@609
|
395 0x00 ;; (item-hack)
|
rlm@609
|
396 0x20 ;; jump back to input section if not done
|
rlm@609
|
397 0xDA ;; literal -36 == TM 18 (counter)
|
rlm@609
|
398 0x01 ;; (item-hack) set BC to literal (no-op)
|
rlm@609
|
399
|
rlm@609
|
400 ;; jump to target
|
rlm@609
|
401 0x00 ;; (item-hack) these two bytes can be anything.
|
rlm@609
|
402 0x01
|
rlm@609
|
403
|
rlm@609
|
404 0x00 ;; (item-hack) no-op
|
rlm@609
|
405 0xBF ;; (CP A A) ensures Z
|
rlm@609
|
406
|
rlm@609
|
407 0xCA ;; (item-hack) jump if Z
|
rlm@609
|
408 target-low
|
rlm@609
|
409 target-high
|
rlm@609
|
410 0x01 ;; (item-hack) will never be reached.
|
rlm@609
|
411
|
rlm@609
|
412 ;; input first nybble
|
rlm@609
|
413 0x00
|
rlm@609
|
414 0xCB
|
rlm@609
|
415 0x37 ;; swap nybbles on A
|
rlm@609
|
416
|
rlm@609
|
417 0x57 ;; A -> D
|
rlm@609
|
418
|
rlm@609
|
419 0x37 ;; (item-hack) set carry flag no-op
|
rlm@609
|
420 0x18 ;; relative jump backwards
|
rlm@609
|
421 0xCD ;; literal -51 == TM05; go back to input section
|
rlm@609
|
422 0x01 ;; (item-hack) will never reach this instruction
|
rlm@609
|
423
|
rlm@609
|
424 ]
|
rlm@609
|
425 (repeat 8 [0x00 0x01]);; these can be anything
|
rlm@609
|
426
|
rlm@609
|
427 [;; jump to actual program
|
rlm@609
|
428 0x00
|
rlm@609
|
429 0x37 ;; (item-hack) set carry flag no-op
|
rlm@609
|
430
|
rlm@609
|
431 0x2E ;; 0x3A -> L
|
rlm@609
|
432 0x3A
|
rlm@609
|
433
|
rlm@609
|
434
|
rlm@609
|
435 0x00 ;; (item-hack) no-op
|
rlm@609
|
436 0x26 ;; 0xD5 -> L
|
rlm@609
|
437 0xD5
|
rlm@609
|
438 0x01 ;; (item-hack) set-carry BC
|
rlm@609
|
439
|
rlm@609
|
440 0x00 ;; (item-hack) these can be anything
|
rlm@609
|
441 0x01
|
rlm@609
|
442
|
rlm@609
|
443 0x00
|
rlm@609
|
444 0xE9 ;; jump to (HL)
|
rlm@609
|
445 ]])))
|
rlm@609
|
446
|
rlm@609
|
447 #+end_src
|
rlm@609
|
448
|
rlm@609
|
449 I use the glitch items 0x00 and 0xFF to great effect in my run. 0x00
|
rlm@612
|
450 sells for almost half of maximum money --- I use just 3 of them to
|
rlm@611
|
451 finance the purchase of all the other items I need. 0x00 is also a
|
rlm@609
|
452 NO-OP in the gameboy's machine language, which means that I can stick
|
rlm@618
|
453 them anywhere where I need to break up an otherwise illegal pair of
|
rlm@609
|
454 opcodes. 0xFF is also extremely useful because it is the end-of-list
|
rlm@609
|
455 sentinel. Normally, the game will "compact" your items whenever you
|
rlm@609
|
456 make a purchase or deposit. For example, if you deposit a pokeball,
|
rlm@609
|
457 then deposit another pokeball, the item list looks like:
|
rlm@609
|
458
|
rlm@613
|
459 #+begin_example
|
rlm@609
|
460 pokeball x2
|
rlm@613
|
461 #+end_example
|
rlm@609
|
462
|
rlm@609
|
463 instead of:
|
rlm@609
|
464
|
rlm@613
|
465 #+begin_example
|
rlm@609
|
466 pokeball x1
|
rlm@609
|
467 pokeball x1
|
rlm@613
|
468 #+end_example
|
rlm@609
|
469
|
rlm@609
|
470 However, the compaction stops after the first 0xFF item, so if there
|
rlm@609
|
471 is an 0xFF item at the beginning of the list, it will "shield" all the
|
rlm@609
|
472 items below it from compaction. It the beginning of the run, I stick
|
rlm@609
|
473 an 0xFF item at the top of the PC item list, allowing me to put items
|
rlm@609
|
474 in with impunity. At the end, I toss the 0xFF away to reveal the
|
rlm@609
|
475 completed bootstrap program.
|
rlm@609
|
476
|
rlm@614
|
477 The final payload program is multiple programs. I created a reduced
|
rlm@614
|
478 form of MIDI and implemented it in gameboy machine language. Then I
|
rlm@614
|
479 translated a midi file from http://www.everyponysings.com/ into this
|
rlm@614
|
480 reduced MIDI language. The payload program contains both the music
|
rlm@614
|
481 data and the MIDI interpreter to play that data. The picture works in
|
rlm@614
|
482 a similar way. There is code to translate a png file into a form that
|
rlm@614
|
483 can be displayed on a gameboy, and other code to actually display that
|
rlm@614
|
484 image. Both the image and the display code are also written by the
|
rlm@614
|
485 final bootstrapping program. Even though my final payload is rather
|
rlm@614
|
486 simple, you can write any program at all as the payload. The source
|
rlm@614
|
487 for the sound and image displaying code is at
|
rlm@614
|
488 http://hg.bortreb.com/vba-clojure.
|
rlm@609
|
489
|
rlm@609
|
490
|