Gameboy Player Techdoc ------------------------------------------------------------------------------- 2005-05-02, Fredrik "FluBBa" Olsson My hacking of the GBP started somewhere in the late 2003 but I really never got anywhere before 2005, I have succesfully implemented rumble in 3 applications now (RumblePong, Remudvance & Goomba), Tepples has supposedly implemented it in his "Tetanus on Drugs" also. ------------------------------------------------------------------------------- Index: ------ About the Gameboy Player: The basics: Talking with the GBP: The complete correct sequence: Example source code: ------------------------------------------------------------------------------- About the Gameboy Player: ------------------------- The Gameboy Player is an adapter for the Nintendo GameCube that essentially is a Gameboy Advance in a box, it allows you to play (allmost) all the same games that you can play on the GBA (Gameboy, Gameboy Color & Gameboy Advance games) but on your TV through the GameCube instead. Games that shouldn't be played on the GBP are those that use a tilt sensor or rumble motor in the cart (you shouldn't tilt or shake your GameCube while playing games on it). The GBP offers (at least) one extra feature over a normal GBA and that is the control of the rumble motor in the GameCube control pad, this document tries to document that feature. The GBP can also use some of the extra controllers for the GC like the Bongas from Donkey Konga. ------------------------------------------------------------------------------- The basics: ----------- The first thing to do to check for the GBP is show the Gameboy Player logo, this also enables the GBP specific features. The logo requires at least 256 colors, it doesn't matter if you use a tiled screen mode or a bitmapped one, the logo can be ripped from either "Pokemon Pinball" or "Super Mario Advance 4". When displaying the logo check REG_P1 (0x04000130) for a value of 0x030F, this should come every third frame. I don't know how long the logo needs to be showed to enable the rumble feature, but 60 frames (1 second) should be enough. Now you "just" have to setup the serial communications registers to talk to the GBP and tell it to turn on the rumble motor. ------------------------------------------------------------------------------- Talking with the GBP: --------------------- First you have to setup REG_RCNT (0x04000134) & REG_SIOCNT (0x04000128). REG_RCNT = 0x0; REG_SIOCNT = 0x1008; REG_SIOCNT |= 0x4000; REG_SIOCNT &=~1; REG_SIOCNT |= 0x0080; The GBP will now send a packet to the serial port and you have to answear it by supplying new data in REG_SIODATA32 and set the send bit in REG_SIOCNT, this is easiest done in a serial interrupt. The communication is 32bit and the first packet is 0x000:494E, the 0x494E is ASCII for NI (from NINTENDO). You will have to send out the whole NINTENDO plus some more data to establish a complete connection. In the first step you allways send out the low 16bits as the bitwise inverted value you received and the high 16bits as new data. So you bitwise invert 0x494E and get 0xB6B1 add 0x494E (as this also is what you should send back to the GBP) and get 0x494E:B6B1, now you should make sure you allways get back in the high 16bit, what you sent in the low 16bit otherwise restart, when you get 0xB6B1:494E (bitwise inverted 0x494E:B6B1) it's time to send out the next data. This is 0x544E for NT, the whole data is 0x544E:B6B1 and you should still receive 0xB6B1 in the high 16bit and now 0x544E in the low 16bit and thus invert it for the next send (~0x544E = 0xABB1) which gives 0x544E:ABB1. When you have sent out the whole NI_NT_EN_DO sequence there are some more control data also, it all ends in the GBP sending 0x3000:0003 and you can reply either 0x4000:0004 or 0x4000:0026 depending if you want to run the rumble motor or not. ------------------------------------------------------------------------------- The complete correct sequence: ------------------------------ Receive: Send: 0000:494E 494E:B6B1 xxxx:494E 494E:B6B1 B6B1:494E 544E:B6B1 B6B1:544E 544E:ABB1 ABB1:544E 4E45:ABB1 ABB1:4E45 4E45:B1BA B1BA:4E45 4F44:B1BA B1BA:4F44 4F44:B0BB B0BB:4F44 8000:B0BB B0BB:8002 1000:0010 1000:0010 2000:0013 2000:0013 4000:0004 3000:0003 4000:0004 3000:0003 4000:0004 3000:0003 4000:0004 3000:0003 4000:0026 -> Rumble 3000:0003 4000:0004 ------------------------------------------------------------------------------- Example source code: -------------------- // Setup your IRQ handler to redirect Serial Interrupts to RumbleInterrupt(). // Call StartRumbleComs() once a second or so. // Set DoRumble to the number of frames to rumble. #define REG_SIODATA32 *(vu32*)0x4000120 #define REG_SIOCNT *(vu16*)0x4000128 #define REG_RCNT *(vu16*)0x4000134 u32 SerialIn,DoRumble; u16 stage,ind; u16 SerOut0, SerOut1; char const GBPData[]={"NINTENDO"}; void RumbleInterrupt(void) { u32 OutData=0; u16 SerIn0, SerIn1; u16 *GBPD2 = (u16*)GBPData; SerialIn = REG_SIODATA32; switch(stage) { case 0: SerIn0 = SerialIn>>16; SerIn1 = SerialIn; if(SerIn0 == SerOut1){ if(ind <=3){ if( SerialIn == ~(SerOut1 | (SerOut0<<16)) ){ ind++; } }else{ if(SerIn1 == 0x8002){ OutData = 0x10000010; stage=1; break; } } }else{ ind = 0; } if(ind <=3){ SerOut0 = GBPD2[ind]; }else{ SerOut0 = 0x8000; } SerOut1 = ~SerIn1; OutData = SerOut1 | (SerOut0<<16); break; case 1: if(SerialIn == 0x10000010){ OutData = 0x20000013; stage=2; }else{ stage = 4; } break; case 2: if(SerialIn == 0x20000013){ OutData = 0x40000004; stage=3; }else{ stage = 4; } break; case 3: if(SerialIn == 0x30000003){ if(DoRumble){ DoRumble--; OutData = 0x40000026; }else{ OutData = 0x40000004; } }else{ stage = 4; } break; case 4: SerialIn = 0; DoRumble = 0; stage = 0; ind = 0; SerOut0 = 0; SerOut1 = 0; return; } REG_SIODATA32 = OutData; REG_SIOCNT |= 0x80; } void StartRumbleComs(void) { if(SerialIn != 0x30000003){ REG_RCNT = 0x0; REG_SIOCNT = 0x1008; REG_SIOCNT |= 0x4000; REG_SIOCNT &=~1; REG_SIOCNT |= 0x0080; } }