├── Readme.md ├── demo.svg ├── gba.jpeg └── multiboot.py /Readme.md: -------------------------------------------------------------------------------- 1 | # GBA Multiboot uploader for Raspberry pi 2 | This is a Python port of the project hosted [here](https://github.com/akkera102/gba_01_multiboot). 3 | This Python script can be used to upload a multiboot ROM to a Gameboy Advance using the link cable. 4 | 5 | Multiboot is used by games like Mario Kart to play the game with other players, without having to use a game for each device. Instead, the link cable is used to boot the other GBA devices. 6 | 7 | Development kits like DevkitPro have the option to compile for multiboot. This tool can be used to upload the rom to a console. 8 | 9 | 10 | ![Gameboy + Raspberri Pi Zero W](gba.jpeg) 11 | 12 | ## Requirements 13 | * WiringPi (Python) 14 | * Python 3 15 | 16 | ## Wiring 17 | [Source](https://github.com/akkera102/gba_01_multiboot) 18 | 19 | ``` 20 | GBA connector(cable side). 21 | 22 | T 23 | 1 3 5 1 3v, xxx 3 SI, wht 5 SC, red 24 | 2 4 6 2 SO, blk 4 SD, grn 6 GN, xxx` 25 | 26 | Connect to: 27 | 28 | 6-GND GND 29 | 3-SI (SPI_MOSI) 30 | 2-SO (SPI_MISO) 31 | 5-SC (SPI_SCLK) 32 | ``` 33 | 34 | 35 | 36 | ## Usage 37 | 38 | `python3 multiboot.py filename.gba` 39 | 40 | Make sure the rom is a multiboot rom. 41 | 42 | ![Demo svg](demo.svg) 43 | -------------------------------------------------------------------------------- /demo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 20 | 41 | 42 | 43 | ~ ~ ~ s ~ ss ~ ssh ~ ssh ~ ssh p ~ ssh pi ~ ssh pi@ ~ ssh pi@1 ~ ssh pi@19 ~ ssh pi@192 ~ ssh pi@192. ~ ssh pi@192.1 ~ ssh pi@192.16 ~ ssh pi@192.168 ~ ssh pi@192.168. ~ ssh pi@192.168.1 ~ ssh pi@192.168.17 ~ ssh pi@192.168.178 ~ ssh pi@192.168.178. ~ ssh pi@192.168.178.6 ~ ssh pi@192.168.178.62 pi@raspberrypi:~ $ pi@raspberrypi:~ $ c pi@raspberrypi:~ $ cd pi@raspberrypi:~ $ cd m pi@raspberrypi:~ $ cd mu pi@raspberrypi:~ $ cd mul pi@raspberrypi:~ $ cd multiboot/ pi@raspberrypi:~/multiboot $ pi@raspberrypi:~/multiboot $ python3 multiboot.py test_mb.gba Filename test_mb.gba ~ ssh pi@192.168.178.62 Linux raspberrypi 4.9.41+ #1023 Tue Aug 8 15:47:12 BST 2017 armv6lThe programs included with the Debian GNU/Linux system are free software;the exact distribution terms for each program are described in theindividual files in /usr/share/doc/*/copyright.Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extentpermitted by applicable law.Last login: Wed Apr 17 16:49:56 2019 from 192.168.178.54SSH is enabled and the default password for the 'pi' user has not been changed.This is a security risk - please login as the 'pi' user and type 'passwd' to set a new password.pi@raspberrypi:~ $ cd multiboot/pi@raspberrypi:~/multiboot $ python3 multiboot.py test_mb.gbaFilename test_mb.gbafsize, 75696Looking for GameboyFound GBA!Send HeaderSent! Sent!Exchange master/slave info againSend palette dataSend handshakeSend game lengthSend encrypted data Send encrypted dataSentWait for GBA to respond with CRCGBA ready with CRC, exchanging GBA ready with CRC, exchangingCRC ...hope they match!MulitBoot donepi@raspberrypi:~/multiboot $ ^Cpi@raspberrypi:~/multiboot $ pi@raspberrypi:~/multiboot $ uitgelogdConnection to 192.168.178.62 closed.^D ~ 44 | -------------------------------------------------------------------------------- /gba.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bartjakobs/GBA-Multiboot-Python/efcf8efbecf39874daa111b92b73b921d35f02a2/gba.jpeg -------------------------------------------------------------------------------- /multiboot.py: -------------------------------------------------------------------------------- 1 | import wiringpi as wiringpi 2 | import time 3 | import math 4 | import sys 5 | def WriteSPI32(w): 6 | buf = [0,0,0,0] 7 | buf[3] = (w & 0x000000ff) 8 | buf[2] = (w & 0x0000ff00) >> 8 9 | buf[1] = (w & 0x00ff0000) >> 16 10 | buf[0] = (w & 0xff000000) >> 24 11 | length, buf = wiringpi.wiringPiSPIDataRW(0, bytes(buf)) 12 | r=0 13 | r += buf[0] << 24 14 | r += buf[1] << 16 15 | r += buf[2] << 8 16 | r += buf[3] 17 | return r 18 | 19 | 20 | def WaitSPI32(w, comp): 21 | r=-1 22 | while(r!=comp): 23 | r = WriteSPI32(w) 24 | time.sleep(0.01) 25 | 26 | 27 | def getNext(fp): 28 | n = fp.read(1) 29 | if(len(n) == 0): 30 | return 0 31 | return n[0] 32 | 33 | 34 | def main(): 35 | filename = sys.argv[1] 36 | print("Filename "+filename) 37 | fp = open(filename,'rb') 38 | 39 | fp.seek(0,2) 40 | fsize = (fp.tell() + 0x0f) & 0xfffffff0 41 | print("fsize, ", fsize) 42 | if (fsize > 0x40000): 43 | print("Err: Max file size 256kB\n") 44 | return 45 | fp.seek(0) 46 | 47 | wiringpi.wiringPiSetupGpio() 48 | wiringpi.wiringPiSPISetup(0, 100000) 49 | print("Looking for Gameboy") 50 | WaitSPI32(0x00006202, 0x72026202) ## Look for gba 51 | 52 | print("Found GBA!") 53 | r = WriteSPI32(0x00006202) 54 | r = WriteSPI32(0x00006102) 55 | 56 | fcnt = 0 57 | 58 | print("Send Header"); 59 | for i in range(0, 0x5f+1): 60 | 61 | w = fp.read(1)[0] 62 | w = fp.read(1)[0] << 8 | w 63 | fcnt += 2 64 | r = WriteSPI32(w) 65 | print("Sent!") 66 | 67 | r = WriteSPI32(0x00006200) ## Transfer comokete 68 | print("Exchange master/slave info again") 69 | r = WriteSPI32(0x00006202) 70 | print("Send palette data") 71 | r = WriteSPI32(0x000063d1) 72 | r = WriteSPI32(0x000063d1) 73 | 74 | m = ((r & 0x00ff0000) >> 8) + 0xffff00d1 75 | h = ((r & 0x00ff0000) >> 16) + 0xf 76 | print("Send handshake") 77 | r = WriteSPI32((((r >> 16) + 0xf) & 0xff) | 0x00006400) 78 | print("Send game length") 79 | r = WriteSPI32(int(math.ceil((fsize - 0x190) / 4))) 80 | 81 | f = (((r & 0x00ff0000) >> 8) + h) | 0xffff0000 82 | c = 0x0000c387 83 | 84 | print("Send encrypted data") 85 | 86 | while (fcnt < fsize): 87 | w = getNext(fp) 88 | w = getNext(fp) << 8 | w 89 | w = getNext(fp) << 16 | w 90 | w = getNext(fp) << 24 | w 91 | 92 | w2 = w 93 | for bit in range(0, 32): 94 | if ((c ^ w) & 0x01): 95 | c = (c >> 1) ^ 0x0000c37b 96 | else: 97 | c = c >> 1 98 | w = w >> 1 99 | 100 | w = w & 0xffffffff 101 | m = ((0xffffffff & (0x6f646573 * m)) + 1) 102 | WriteSPI32(0xffffffff & (w2 ^ ((~(0x02000000 + fcnt)) + 1) ^ m ^ 0x43202f2f)) 103 | fcnt = fcnt + 4 104 | 105 | 106 | print("Sent") 107 | fp.close() 108 | 109 | for bit in range(0, 32): 110 | if ((c ^ f) & 0x01): 111 | c = (c >> 1) ^ 0x0000c37b 112 | else: 113 | c = c >> 1 114 | f = f >> 1 115 | 116 | print("Wait for GBA to respond with CRC") 117 | WaitSPI32(0x00000065, 0x00750065) 118 | print("GBA ready with CRC, exchanging") 119 | r = WriteSPI32(0x00000066) 120 | r = WriteSPI32(c) 121 | 122 | print("CRC ...hope they match!") 123 | print("MulitBoot done") 124 | 125 | 126 | 127 | 128 | 129 | main() --------------------------------------------------------------------------------