Making some sort of Gameboy Printer simulator has been on the projects list for quite some time now - pewterfish may remember me poking at it once or twice while in the Brighton house. The idea simmered on the back burner for many years until I saw a couple of things recently and a lot of stuff just clicked together.Part 1: the hardware
The first was realising that the Gameboy link cable protocol is actually just SPI, which is a really basic serial interface that almost any microcontroller can speak in hardware. The only complication with it is that there's no such thing as standard SPI, but any half-decent µC can be configured to cope with the differences. In the case of the Gameboy, the details are that it runs at 5v, the idle clock state is logical 0 and the data is valid on the falling edge of the clock (for the PIC24FJ64GA004 that's CKP, SSEN and CKE all set to 0). The Gameboy drives the clock at a mere 8kHz, which is dirt slow (mystery project #2 involves a much faster 1MHz bus).
The second was checking the specification of the Bus Pirate and finding that it is 5v-tolerant, and so can be connected to a Gameboy without the magic smoke escaping. I had considered using the Lego Mindstorms NXT set I've got as the firmware for that is open, but while the ARM and AVR cores both have SPI modules they're already in use - the ARM has the Bluetooth and display chips which would object to 5v signalling, while the AVR already uses those pins for the motor control and ADC sampling. So I bought a Bus Pirate and used that instead. The Pirate also has the advantage that all the code for speaking to a computer has already been written and so there's a lot less work needed, while if I'd used the NXT I'd have had to write a lot of extra code to make it all work.
Once I had all the pieces, the next step was to actually wire the Pirate up to a Gameboy. In the name of science I sacrificed a Gameboy-to-parallel-port cable that came with an old Action Replay, and poked it with a multimeter until I had found the necessary pins. SPI uses two data connections (one in each direction) and a separate clock, plus a ground connection. For a Gameboy link cable, that's pins 2, 3, 5 and 6. Sometimes there's also a chip select line, but the Gameboy doesn't use this.
Check this carefully - your cable will almost certainly have different colours, and for that matter there are two different breakout cables for the Bus Pirate (mine uses the Sparkfun ordering, which is backwards from everyone else). I ended up with this combination of colours:
|GB pin||GB name||GB colour||BP colour||BP name||BP pin|
Having achieved that the next stage was to see if the Pirate could actually see any data on the bus. Ultimately I'd need to write some code to emulate at least the basic protocol, as the existing firmware is really designed for acting as the master in any system with the Pirate driving the clock. This is backwards from my desired setup, where the Pirate is the slave device and the clock is being driven by the Gameboy. Fortunately the firmware does have a SPI sniffer mode, and I could use that to capture the Gameboy trying to see if there's anything connected to it. It took a bit of experimentation to get the Pirate configured correctly, but eventually I got the right combination:
HiZ>m 1. HiZ 2. 1-WIRE 3. UART 4. I2C 5. SPI 6. 2WIRE 7. 3WIRE 8. LCD x. exit(without change) (1)>5 Set speed: 1. 30KHz 2. 125KHz 3. 250KHz 4. 1MHz (1)>1 Clock polarity: 1. Idle low *default 2. Idle high (1)>2 Output clock edge: 1. Idle to active 2. Active to idle *default (2)>1 Input sample phase: 1. Middle *default 2. End (1)>1 CS: 1. CS 2. /CS *default (2)>2 Select output type: 1. Open drain (H=Hi-Z, L=GND) 2. Normal (H=3.3V, L=GND) (1)>1 Ready SPI>
Once that's done, all that's needed is to enter the SPI sniffer mode (using the "(2)" macro command) and look to see if anything appears. If it's all working, you should see something like the following when you try to print:
SPI>(2) Sniffer Any key to exit [0xFF(0x88)0xFF(0x33)0xFF(0x01)0xFF(0x00)0xFF(0x00)0xFF(0x00)0xFF(0x01)0xFF(0x00 )0xFF(0x00)0xFF(0x00)
Now to decode it...