Isn’t it frustrating that some of the Pokemon in the first generation Game Boy games are exclusive to one of the games?
There is no way to get Oddish in Blue and no way to get Bellsprout in Red.
The intended solution is to trade Pokemon with friends to get the missing ones. There are even Pokemon like Graveler that only evolve when traded.
Sadly, not a whole lot of people own a Game Boy, a Pokemon game, and a link cable. So I decided to get creative and trade Pokemon with my Arduino.
1 x Game Boy (Color and Advanced work too)
2 x Pokemon cartridge (Red and Blue)
1 x Arduino board
1 x Link connector
The Game Boy communicates over what is essentially a 5v SPI bus that can act both as a master and a slave. At 8KHz it is slow enough to bit-bang, so it works on any 3 GPIO pins.
I salvaged a connector from a GBA wireless adapter, and hooked it up to 3 Arduino pins with 1KΩ series resistors to be sure. Because both ends can in theory drive the clock line, I don’t want to short them out.
| 6 4 2 |
\_5__3__1_/ (at cable)
Connect to the Arduino as follows:
I read through the Pokemon disassembly in my previous post, so I won’t go into too much detail about the data transferred. Here is the data structure that contains the Pokemon, if you are interested.
Long story short, I emulated the protocol on the Arduino, acting as a slave. So go Check out the code, and finally catch ‘em all.
The Arduino has one Pokemon stored in its EEPROM, which it will trade with you. So even if you have one cartridge, you can trade Graveler with the Arduino and then trade it back to get a Golem.
If you have two cartridges, you can trade a Pokemon with the Arduino, swap the cartridge, and trade it back.
No Pokemon where harmed in the making of this program. Or, not that many anyway. One Weedle turned into Slowpoke and then into Missingno. Several other Pokemon are now nicknamed Pidgey, but are otherwise doing fine.
I think it’s now complete, allowing multiple trades in one session, including canceling trades. Don’t forget to reset the Arduino when you reset the Game Boy, or bad things will happen.
Reading the section in the CPU manual about serial communication, I found that it uses two registers at addresses ff01 and ff02. Grepping the source reveals constants/hardware_constants.asm that defines rSB and rSC.
Grepping those leads me to home/serial.asm and engine/overworld/cable_club_npc.asm. I’m not sure which does what, but in the former I find a promising label called Serial_ExchangeBytes. Grepping for that leads me to engine/cable_club.asm.
The function is called 3 times in this file. Looking at the comments and at my opcode reference, it seems to be using ld to load 3 16 bit registers for the arguments. hl stores the data to send, de the data to receive, and bc the number of bytes to send.
Grepping for wSerialRandomNumberListBlock leads me to wram.asm where all 3 are defined. Looking in the manual, WRAM refers to the 8 4Kb RAM banks that can be mapped into certain address spaces. I’m assuming ds n means a data section of n bytes, but I’m not sure.
So the first command sends 17 bytes of random data, while wSerialRandomNumberListBlock is only 7, but followed by 10 bytes of wLinkBattleRandomNumberList. I could grep further, but it’s random data, so whatever. On to wSerialPlayerDataBlock
This block contains all the good stuff. We know we are looking for 424 bytes of data, but here we see them clearly labeled.
First there is a preamble, like with the random block. No idea what it’s for. Then there is space for the player name, followed by the number of Pokemon you cary and their ID. PARTY_LENGTH is just defined as 6.
Then there are 6 party_struct that contain more info about your Pokemon. I know because I found this page earlier.
I’m grepping for those bytes in the preamble. They seem to be used for a bunch of other things, completely unrelated to the player data.
I’m just randomly trying to read bit and pieces of the code and comparing with bytes coming out of the Game Boy. In the data I see a lot of FD, and in the code I found SERIAL_PREAMBLE_BYTE which so happens to be defined as FD. These seem to be for synchronisation only.
That’s it for now. There is plenty of stuff I don’t understand, but at least I know the structure of the data that is sent over the link cable.
I was reading an old book about astrology, which contained a section about how to calculate your horoscope. It used crude lookup tables for the planets, and it was only valid until 2000.
I had also wondered if two planets were still considered conjunct if they where above/below each other.
So I set out to compute my horoscope in 3D using real orbits instead of lookup tables. I read some things on Wikipedia, but nothing I could translate to running code.
Then I finally found a NASA page that contains all the orbital parameters and a PDF explaining the math.
It took me a long time to get it working for two reasons. The first was solving Kepler’s equation to obtain the eccentric anomaly.
They mix and match degrees and radians in confusing ways. While it might make sense to take the cosine of a value in degrees, it certainly does not make sense to math.cos.
I suggest any math code to convert degrees to radians at the edges, and deal with radians only. Degrees should only be used for input and display. They make sense to humans.
Once you have an approximation of Kepler’s formula solved for eccentric anomaly, it is good to verify that the original formula returns your mean anomaly.
defsolve_kepler(eccentricity,mean_anomaly):# for the approximate formulae in the present context, tol = 10e-6 degrees is sufficienttolerance=10e-6# E0 = M + e sin Meccentric_anomaly=mean_anomaly+(eccentricity*math.sin(mean_anomaly))# and itterate the following equations with n = 0,1,2,... unitl |delta E| <= tolwhileTrue:# delta M = M - (En - e sin En)delta_mean_anomaly=mean_anomaly-(eccentric_anomaly-(eccentricity*math.sin(eccentric_anomaly)))# delta E = delta M / (1 - e cos En)delta_eccentric_anomaly=delta_mean_anomaly/(1-(eccentricity*math.cos(eccentric_anomaly)))# En+1 = En + delta Eeccentric_anomaly+=delta_eccentric_anomalyifabs(delta_eccentric_anomaly)<=tolerance:returneccentric_anomaly
The second problem was rotating the planet’s ellipse into the Sun’s coordinate system. Written out in Python it’s a screen full of trigonometric functions. Missing anything gives completely bogus results. It’s not hard, just tedious and error prone.
The tougher problem is rotating the ellipse to match the planet. While I successfully translated the math to code, I had no idea what is going on. I still have no clue how this pile of trigonometry works.
Instead I substituted the function arguments to ecliptic_coordinates for mouseX and mouseY to experiment what is going on. After some failed experiments I found some rotation commands that would align the ellipse with the planet. Then I removed the trigonometric mess and simply drew the planet in the same plane as the ellipse.
That’s it. The rest is mostly just drawing code. You can run the code for yourself.
I really like how it turned out. If you pan around you can clearly see that Pluto might not be as conjunct as it looks in 2D. If you let things rotate, you can visually see Mercury speed up near the sun. It’s also intuitively clear how retrograde motion works.
The only thing missing is the moon. I could not find orbital parameters for it.