changeset 609:65b7c5b47de1

first draft of explination.
author Robert McIntyre <rlm@mit.edu>
date Thu, 22 Nov 2012 10:54:59 -0600
parents 2c348cc68bac
children 4dd5ebf224cd
files org/total-control.org
diffstat 1 files changed, 459 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
     1.1 --- a/org/total-control.org	Thu Nov 22 07:51:36 2012 -0600
     1.2 +++ b/org/total-control.org	Thu Nov 22 10:54:59 2012 -0600
     1.3 @@ -9,3 +9,462 @@
     1.4  #+OPTIONS: num:2
     1.5  
     1.6  
     1.7 +Full Source : http://hg.bortreb.com/vba-clojure
     1.8 +
     1.9 +Youtube Video w/ Visual Keypresses: http://www.youtube.com/watch?v=p5T81yHkHtI
    1.10 +
    1.11 +Special Thanks to: 
    1.12 +
    1.13 + - http://tasvideos.org/2913S.html for the save corruption hack which
    1.14 +   is used at the start of this run.
    1.15 + - http://www.everyponysings.com/ for providing the midi file I used
    1.16 +   to create the song at the end.
    1.17 + - http://www.zophar.net/ for the terminal font.
    1.18 +
    1.19 +
    1.20 +* Introduction
    1.21 +
    1.22 +Think of pokemon yellow as creating a little universe with certain
    1.23 +rules. Inside that universe, you can buy items, defeat rival trainers,
    1.24 +and raise your pokemon. But within that universe, you are bound by the
    1.25 +rules of pokemon. You can't build new buildings, or change the music,
    1.26 +or change your clothes.. There are some games (like chess), where it
    1.27 +is not possible to alter the rules of the game from within the
    1.28 +game. No matter what moves you make in chess, you can never change the
    1.29 +rules of the game so that it becomes checkers or basketball. The point
    1.30 +of this run is to show that you CAN change the rules in pokemon
    1.31 +yellow. There is a certain sequence of valid actions like walking from
    1.32 +one place to another or buying items that will allow you to transform
    1.33 +pokemon yellow into Pacman, or Tetris, or Pong, or a MIDI player, or
    1.34 +anything else you can imagine.
    1.35 +
    1.36 +* Background
    1.37 +
    1.38 +[[http://tasvideos.org/2913S.html][This speedrun]] by Felipe Lopes de Freitas (p4wn3r), beats pokemon
    1.39 +yellow in only 1 minute and 36 seconds. It does it by corrupting the
    1.40 +in-game item list so that he can advance the list past its normal
    1.41 +limit of 20 items. The memory immediately after the item list includes
    1.42 +the warp points for the current map, and by treating that data as
    1.43 +items and switching and dropping them, he can make the door from his
    1.44 +house take him directly to the end of the game. 
    1.45 +
    1.46 +When I first saw that speedrun, I was amazed at how fast pokemon
    1.47 +yellow could be beaten, and that it was possible to manipulate the
    1.48 +game from the inside, using only the item list. I wondeered how far I
    1.49 +could extend the techniques found in p4wn3r's run.
    1.50 +
    1.51 +The gameboy is an 8 bit computer. That means that ultimately, anything
    1.52 +that happens in pokemon is a result of the gameboy's CPU reading a
    1.53 +stream of 8 bit numbers and doing whatever those numbers mean. For
    1.54 +example, in the gameboy, the numbers: 
    1.55 +
    1.56 +[62 16 37 224 47 240 37 230 15 55] 
    1.57 +
    1.58 +mean to check which buttons are currently pressed and copy that result
    1.59 +into the "A" register. With enough numbers, you can spell out an
    1.60 +interactive program that reads input from the buttons and allows you
    1.61 +to write any program you want to the gameboy. Once you have assembled
    1.62 +such a program and forced the game to run it, you have won, since you
    1.63 +can use that program to write any other program (like tetirs or
    1.64 +pacman) over pokemon yellow's code. I call a program that allows you
    1.65 +to write any other program a "bootstrapping program". So, the goal is
    1.66 +to somehow get a bootstrapping program into pokemon yellow and then
    1.67 +force yellow to run that program instead of its own.
    1.68 +
    1.69 +How can we spell out such a program? Everything in the game is
    1.70 +ultimately nunbers, including all items, pokemon, levels, etc. In
    1.71 +particular, the item list looks like:
    1.72 +
    1.73 +item-one-id         (0-255)
    1.74 +item-one-quantity   (0-255)
    1.75 +item-two-id         (0-255)
    1.76 +item-two-quantity   (0-255)
    1.77 +.
    1.78 +.
    1.79 +.
    1.80 +
    1.81 +Let's consider the button measuring program  [37 62 16 37 224 37 240
    1.82 +37 230 15 55] from before. Interpreted as items and item quantities, it is 
    1.83 +
    1.84 +lemonade     x16
    1.85 +guard spec.  x224
    1.86 +leaf stone   x240
    1.87 +guard spec.  x230
    1.88 +parlyz heal  x55
    1.89 +
    1.90 +So, if we can get the right items in the right quantities, we can
    1.91 +spell out a bootstrapping program. Likewise, when writing the
    1.92 +bootstrapping program, we must be careful to only use numbers that are
    1.93 +also valid items and quantities. This is hard because there aren't
    1.94 +many different items to work with, and many machine instructions
    1.95 +actually take 2 or even 3 numbers in a row, which severely restricts
    1.96 +the types of items you can use. I ended up needing about 92 numbers to
    1.97 +implement a bootstrap program. Half of those numbers were elaborate
    1.98 +ways of doing nothing and were just there so that the entire program
    1.99 +was also a valid item list.
   1.100 +
   1.101 +The final part of the hack is getting pokemon yellow to execute the
   1.102 +new program after it has been assembled with items. Fortunately,
   1.103 +pokemon keeps a number called a function pointer within easy reach of
   1.104 +the corrupted item list. This function pointer is the starting point
   1.105 +(address) of a program which the game runs every so often to check for
   1.106 +poison and do general maintaiance. By shifting an item over this
   1.107 +function pointer, I can rewrite that address to point to the
   1.108 +bootstrapping program, and make the game execute it. Without this
   1.109 +function pointer, it would not be possible to take over the game.
   1.110 +
   1.111 +* The Run
   1.112 +
   1.113 +I start off and name my rival Lpk. These characters will eventually be
   1.114 +treated as items and shifted over the function pointer, causing it to
   1.115 +execute the bootstrapping program that will soon be constructed. I
   1.116 +start the run the same as p4wn3r's and restart the game while saving,
   1.117 +so that the pokemon list is corrupted. By switching the 8th and 10th
   1.118 +pokemon, I corrupt the item list and can now scroll down past the 20th
   1.119 +item. I shift items around to increase the text speed to maximum and
   1.120 +rewrite the warp point of my house to Celadon Dept. Store. (p4wn3r
   1.121 +used this to go directly to the hall of fame and win the game in his
   1.122 +run.) I deposit many 0x00 glitch items into the PC from my corrupted
   1.123 +inventory for later use. Then, I widthdraw the potion from the
   1.124 +PC. This repairs my item list by overflowing the item counter from
   1.125 +0xFF back to 0x00, though the potion is obliterated in the process. I
   1.126 +then take 255 glitch items with ID 0x00 from the computer into my
   1.127 +personal items.
   1.128 +
   1.129 +Leaving my house takes me directly to Celadon Dept. store, where I
   1.130 +sell two 0x00 items for 414925 each, giving myself essentially max
   1.131 +money. I hit every floor of the department store, gathering the
   1.132 +following items:
   1.133 +
   1.134 +#+begin_example
   1.135 ++-------------------+----------+
   1.136 +|##| Item           | Quantity |
   1.137 ++--+----------------+----------+
   1.138 +|1 | TM02           |  98      |
   1.139 +|2 | TM37           |  71      |
   1.140 +|3 | TM05           |   1      |
   1.141 +|4 | TM09           |   1      |
   1.142 +|5 | burn-heal      |  12      |
   1.143 +|6 | ice-heal       |  55      |
   1.144 +|7 | parlyz-heal    |  99      |
   1.145 +|8 | parlyz-heal    |  55      |
   1.146 +|9 | TM18           |   1      |
   1.147 +|10| fire-stone     |  23      |
   1.148 +|11| water-stone    |  29      |
   1.149 +|12| x-accuracy     |  58      |
   1.150 +|13| guard-spec     |  99      |
   1.151 +|14| guard-spec     |  24      |
   1.152 +|15| lemonade       |  16      |
   1.153 +|16| TM13           |   1      |
   1.154 ++--+----------------+----------+
   1.155 +#+end_example
   1.156 +
   1.157 +After gathering these items, I deposit them in the appropriate order
   1.158 +into the item PC to spell out my bootstrapping program. Writing a full
   1.159 +bootstrap program in one go using only items turned out to be too
   1.160 +hard, so I split the process up into three parts. The program that I
   1.161 +actually construct using items is very limited. It reads only from the
   1.162 +A, B, start, and select buttons, and writes 4 bits each frame to a
   1.163 +fixed point in memory. After it writes 200 or so bytes, it jumps
   1.164 +directly to what it just wrote. In my run, I use this program to write
   1.165 +another bootstrapping program that can write to any number of bytes to
   1.166 +any location in memory, and then jump to any location in memory. This
   1.167 +new program also can write 8 bits per frame by using all the
   1.168 +buttons. Using this new bootstrap program, I write a final
   1.169 +bootstrapping program that does everything the provious bootstrapping
   1.170 +program does except it also displays the bytes it is writing to memory
   1.171 +on the screen.
   1.172 +
   1.173 +After completing this bootstrapping program, I go to the celadon
   1.174 +mansion, because I find the metaness of that building to be
   1.175 +sufficiently high to serve as an exit point for the pokemon
   1.176 +universe. I corrupt my item list again by switching corrupted pokemon,
   1.177 +scroll down to my rival's name and discard untill it is equal to the
   1.178 +address of my bootstrapping program, and then swap it with the
   1.179 +function pointer. Once the menu is closed, the boostrapping program
   1.180 +takes over, and I write the payload....
   1.181 +
   1.182 +* Infrastructure
   1.183 +
   1.184 +The entire video was completely produced by bots --- I didn't manually
   1.185 +play the game at all to produce this speedrun. Here is a brief account
   1.186 +of the infrastructure I build to make the video.  The entire source of
   1.187 +the project is available at http://hg.bortreb.com/vba-clojure
   1.188 +
   1.189 +The first step was to build a programatic interface to pokemon
   1.190 +yellow. So, I downloaded vba-rerecording from
   1.191 +http://code.google.com/p/vba-rerecording/. After repairing their
   1.192 +broken auto-tools scripts so that it would compile on GNU/Linux, I
   1.193 +added a low level C interface that I could call from Java via
   1.194 +JNI. This C interface gives me basic control over the emulator: I can
   1.195 +step the emulator either one clock cycle or one frame, and I can get
   1.196 +the contents of any memory location or register. The interface also
   1.197 +allows me to freeze the state of the emulator, save it to a Java
   1.198 +object, and reload that state again at any time.
   1.199 +
   1.200 +I built a layer of [[http://clojure.org/][clojure]] code on top of the JNI bindings to get an
   1.201 +entirely functional interface to vba-rerecording. This interface
   1.202 +treats state of the emulator as an immutable object, and allows me to
   1.203 +do everything I could do with the lower level C interface in a
   1.204 +functional manner. Using this functional code, I wrote search programs
   1.205 +that take a particular game-state and try out different combinations
   1.206 +of button prosses to get any desired effect. By combining different
   1.207 +styles of search with different initial conditions, I created high
   1.208 +level functions that could each accomplish a certain general task,
   1.209 +like walking and buying items. For example, here is some actual code:
   1.210 +
   1.211 +#+begin_src clojure
   1.212 +(defn-memo viridian-store->oaks-lab
   1.213 +  ([] (viridian-store->oaks-lab
   1.214 +       (get-oaks-parcel)))
   1.215 +  ([script]
   1.216 +     (->> script
   1.217 +          (walk [↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
   1.218 +                 ← ← ← ← ← ← ← ← ← 
   1.219 +                 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
   1.220 +                 ← ←
   1.221 +                 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
   1.222 +                 ↓ ↓ ↓ ↓ ↓ ↓ ↓
   1.223 +                 → → → → → → → →
   1.224 +                 ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
   1.225 +                 ← ← ← ← ←
   1.226 +                 ↓ ↓ ↓ ↓
   1.227 +                 ])
   1.228 +          (walk-thru-grass
   1.229 +           [↓ ↓ ↓ ↓ ↓ ↓ ↓])
   1.230 +          (walk [↓ ↓ ← ↓ ↓ ↓ ←
   1.231 +                 ↓ ↓ ↓ ↓ ↓ ↓
   1.232 +                 → → → ↑])
   1.233 +                
   1.234 +          (do-nothing 1))))
   1.235 +#+end_src
   1.236 +
   1.237 +This script walks from the Viridian City pokemon store to Oak's
   1.238 +Lab in the most efficient way possible. The walk-thru-grass function
   1.239 +gaurantees that no wild battles will happen by manipulating the game's
   1.240 +random number generator.
   1.241 +
   1.242 +#+begin_src clojure
   1.243 +(defn-memo hacking-10
   1.244 +  ([] (hacking-10 (hacking-9)))
   1.245 +  ([script]
   1.246 +     (->> script
   1.247 +          begin-deposit
   1.248 +          (deposit-held-item 17 230)
   1.249 +          (deposit-held-item-named :parlyz-heal 55)
   1.250 +          (deposit-held-item 14 178)
   1.251 +          (deposit-held-item-named :water-stone 29)
   1.252 +          (deposit-held-item 14 32)
   1.253 +          (deposit-held-item-named :TM18 1)
   1.254 +          (deposit-held-item 13 1)
   1.255 +          (deposit-held-item 13 191)
   1.256 +          (deposit-held-item-named :TM02 98)
   1.257 +          (deposit-held-item-named :TM09 1)
   1.258 +          close-menu)))
   1.259 +#+end_src
   1.260 +
   1.261 +This script calculates the fastest sequence of key presses to deposit
   1.262 +the requested items into a pc, assuming that the character starts out
   1.263 +in front of a computer.
   1.264 +
   1.265 +I also wrote functions that coudl grovel through the game's memory and
   1.266 +present the internal data structures in useable ways. For example, the
   1.267 +function =print-inventory= returns the current inventory in a human
   1.268 +readable format.
   1.269 +
   1.270 +#+begin_src clojure :results output
   1.271 +(com.aurellem.gb.items/print-inventory)
   1.272 +#+end_src
   1.273 +
   1.274 +#+results:
   1.275 +#+begin_example
   1.276 ++-------------------+----------+
   1.277 +|##| Item           | Quantity |
   1.278 ++--+----------------+----------+
   1.279 +|0 | poke-ball      |  14      |
   1.280 +|1 | TM28           |   1      |
   1.281 +|2 | TM11           |   1      |
   1.282 +|3 | TM45           |   1      |
   1.283 +|4 | nugget         |   1      |
   1.284 +|5 | s.s.ticket     |   1      |
   1.285 +|6 | helix-fossil   |   1      |
   1.286 +|7 | moon-stone     |   1      |
   1.287 ++--+----------------+----------+
   1.288 +
   1.289 +#+end_example
   1.290 +
   1.291 +
   1.292 +Armed with these functions, I constructed a bootstrapping program that
   1.293 +could be expressed as items. This is particurally hard, since many
   1.294 +useful opcodes do not correspond any item, and the item quantities
   1.295 +must all be less than 99.
   1.296 +
   1.297 +Here is the first bootstrapping program in all its glory.
   1.298 +
   1.299 +#+begin_src clojure
   1.300 +(defn pc-item-writer-program
   1.301 +  []
   1.302 +  (let [;;limit 75
   1.303 +        limit 201 ;; (item-hack 201 is the smallest I could make this.) 
   1.304 +        [target-high target-low] (disect-bytes-2 pokemon-list-start)]
   1.305 +    (flatten
   1.306 +     [[0x00  ;; (item-hack) no-op (can't buy repel (1E) at celadon)
   1.307 +       0x1E  ;; load limit into E
   1.308 +       limit
   1.309 +       0x3F  ;; (item-hack) set carry flag no-op
   1.310 +
   1.311 +       ;; load 2 into C.
   1.312 +       0x0E   ;; C == 1 means input-first nybble
   1.313 +       0x04   ;; C == 0 means input-second nybble
   1.314 +
   1.315 +       0x21 ;; load target into HL
   1.316 +       target-low
   1.317 +       target-high
   1.318 +       0x37 ;; (item-hack) set carry flag no-op
   1.319 +
   1.320 +       0x00 ;; (item-hack) no-op
   1.321 +       0x37 ;; (item-hack) set carry flag no-op
   1.322 +       
   1.323 +       0x00 ;; (item-hack) no-op
   1.324 +       0xF3 ;; disable interrupts
   1.325 +       ;; Input Section
   1.326 +
   1.327 +       0x3E ;; load 0x20 into A, to measure buttons
   1.328 +       0x10 
   1.329 +
   1.330 +       0x00 ;; (item-hack) no-op
   1.331 +       0xE0 ;; load A into [FF00]
   1.332 +       0x00
   1.333 +
   1.334 +       0xF0 ;; load 0xFF00 into A to get
   1.335 +       0x00 ;; button presses
   1.336 +       
   1.337 +       0xE6
   1.338 +       0x0F ;; select bottom four bits of A
   1.339 +       0x37 ;; (item-hack) set carry flag no-op
   1.340 +
   1.341 +       0x00 ;; (item-hack) no-op
   1.342 +       0xB8 ;; see if input is different (CP A B)
   1.343 +
   1.344 +       0x00 ;; (item-hack) (INC SP)
   1.345 +       0x28 ;; repeat above steps if input is not different
   1.346 +       ;; (jump relative backwards if B != A)
   1.347 +       0xED ;; (literal -19) (item-hack) -19 == egg bomb (TM37)
   1.348 +
   1.349 +       0x47 ;; load A into B
   1.350 +       
   1.351 +       0x0D ;; dec C
   1.352 +       0x37 ;; (item-hack) set-carry flag
   1.353 +       ;; branch based on C:
   1.354 +       0x20 ;; JR NZ
   1.355 +       23 ;; skip "input second nybble" and "jump to target" below
   1.356 +       
   1.357 +       ;; input second nybble
   1.358 +
   1.359 +       0x0C ;; inc C
   1.360 +       0x0C ;; inc C
   1.361 +
   1.362 +       0x00 ;; (item-hack) no-op
   1.363 +       0xE6 ;; select bottom bits
   1.364 +       0x0F
   1.365 +       0x37 ;; (item-hack) set-carry flag no-op
   1.366 +
   1.367 +       0x00 ;; (item-hack) no-op
   1.368 +       0xB2 ;; (OR A D) -> A
   1.369 +
   1.370 +       0x22 ;; (do (A -> (HL)) (INC HL))
   1.371 +
   1.372 +       0x1D ;; (DEC E)
   1.373 +
   1.374 +       0x00 ;; (item-hack) 
   1.375 +       0x20 ;; jump back to input section if not done
   1.376 +       0xDA ;; literal -36 == TM 18 (counter)
   1.377 +       0x01 ;; (item-hack) set BC to literal (no-op)
   1.378 +
   1.379 +       ;; jump to target
   1.380 +       0x00  ;; (item-hack) these two bytes can be anything.
   1.381 +       0x01 
   1.382 +
   1.383 +       0x00   ;; (item-hack) no-op
   1.384 +       0xBF   ;; (CP A A) ensures Z
   1.385 +       
   1.386 +       0xCA   ;; (item-hack) jump if Z
   1.387 +       target-low
   1.388 +       target-high
   1.389 +       0x01   ;; (item-hack) will never be reached.
   1.390 +       
   1.391 +       ;; input first nybble
   1.392 +       0x00
   1.393 +       0xCB
   1.394 +       0x37  ;; swap nybbles on A
   1.395 +
   1.396 +       0x57  ;; A -> D
   1.397 +
   1.398 +       0x37  ;; (item-hack) set carry flag no-op
   1.399 +       0x18  ;; relative jump backwards
   1.400 +       0xCD  ;; literal -51 == TM05; go back to input section
   1.401 +       0x01  ;; (item-hack) will never reach this instruction
   1.402 +
   1.403 +       ]
   1.404 +      (repeat 8 [0x00 0x01]);; these can be anything
   1.405 +
   1.406 +      [;; jump to actual program
   1.407 +       0x00
   1.408 +       0x37  ;; (item-hack) set carry flag no-op
   1.409 +
   1.410 +       0x2E  ;; 0x3A -> L
   1.411 +       0x3A
   1.412 +
   1.413 +
   1.414 +       0x00  ;; (item-hack) no-op
   1.415 +       0x26  ;; 0xD5 -> L
   1.416 +       0xD5  
   1.417 +       0x01  ;; (item-hack) set-carry BC
   1.418 +
   1.419 +       0x00  ;; (item-hack) these can be anything
   1.420 +       0x01  
   1.421 +
   1.422 +       0x00
   1.423 +       0xE9 ;; jump to (HL)
   1.424 +       ]])))
   1.425 +
   1.426 +#+end_src
   1.427 +
   1.428 +I use the glitch items 0x00 and 0xFF to great effect in my run. 0x00
   1.429 +sells for almost half of max_money, and I use just 3 of them to
   1.430 +finance the purchace of all the other items I need. 0x00 is also a
   1.431 +NO-OP in the gameboy's machine language, which means that I can stick
   1.432 +them anywhere where I need to break up an other wise illegal pair of
   1.433 +opcodes. 0xFF is also extremely useful because it is the end-of-list
   1.434 +sentinel. Normally, the game will "compact" your items whenever you
   1.435 +make a purchase or deposit. For example, if you deposit a pokeball,
   1.436 +then deposit another pokeball, the item list looks like:
   1.437 +
   1.438 +pokeball x2
   1.439 +
   1.440 +instead of:
   1.441 +
   1.442 +pokeball x1
   1.443 +pokeball x1
   1.444 +
   1.445 +However, the compaction stops after the first 0xFF item, so if there
   1.446 +is an 0xFF item at the beginning of the list, it will "shield" all the
   1.447 +items below it from compaction. It the beginning of the run, I stick
   1.448 +an 0xFF item at the top of the PC item list, allowing me to put items
   1.449 +in with impunity. At the end, I toss the 0xFF away to reveal the
   1.450 +completed bootstrap program.
   1.451 +
   1.452 +The final payload program is actually multiple programs. I created a
   1.453 +reduced form of MIDI and implemented it in gameboy machine
   1.454 +language. Then I translated a midi file from
   1.455 +http://www.everyponysings.com/ into this reduced MIDI language. The
   1.456 +payload program contains both the music data and the MIDI interpreter
   1.457 +to play that data. The picture works in a similiar way. There is code
   1.458 +to translate a png file into a form that can be displayed on a
   1.459 +gameboy, and other code to actually display that image. Both the image
   1.460 +and the display code are also written by the final bootstrapping
   1.461 +program.  Even though my final payload is rather simple, you can write
   1.462 +any program at all as the payload. The source for the sound and image
   1.463 +displaying code is at http://hg.bortreb.com/vba-clojure.
   1.464 +
   1.465 +