GameBoy Advance Multiboot

Boot up a GameBoy Advance though it's serial port

The main purpose of this program is to load a program onto the GameBoy with out the need for a flash cartridge.

I have a couple of gba multiboot files that will work with this setup on my web site. I originally did this with a PIC24.

//  mbed GBA Loader - Boot up a GameBoy Advance using Multiboot (no cart req.)
//
//  Ken Kaarvik   kkaarvik@yahoo.com
//  Nov 13, 2010
//
//  mbed            gba serial port
//   1-0V           6-0V
//  p5-mosi         3-SI
//  p6-miso         2-SO
//  p7-sck          5-SC
//  p8-input        1-3V3
//
//  p15-analogIn    0-3V3 test voltage pot
//  p16-analogIn    0-3V3 test voltage pot
//
//  note: the GBA does not power the mbed
//
//  The GBA file that you wish to load into the GBA is stored on the mbed's local file system.
//  (Just drag and drop it onto the drive where mbed is located.)
//  The GBA can be multibooted with a maximum file size of 256kB (the mbed shows up on my
//  computer as a flash drive with 2MB of memory, room for several GBA files!)
//
//  Apply power to the mbed (or reset) before turning on the GBA.
//

#include "mbed.h"

Serial pc(USBTX, USBRX);    //debug info - see raw data being transferred back and forth as GBA is booting

SPI spi(p5, p6, p7); // mosi, miso, sclk

LocalFileSystem local("local");

DigitalIn gba_present(p8);

DigitalOut myled1(LED1);    //used for my demo program (volt4.gba)
DigitalOut myled2(LED2);    //used for my demo program (volt4.gba)
DigitalOut myled3(LED3);    //used for my demo program (volt4.gba)
DigitalOut myled4(LED4);    //used for my demo program (volt4.gba)
AnalogIn voltmeter1(p15);   //used for my demo program (volt4.gba)
AnalogIn voltmeter2(p16);   //used for my demo program (volt4.gba)

unsigned long read_from_gba;
//unsigned long test_counter;

long writeSPI32bits(long write32)
{
    pc.printf("0x%08x ",write32);
    long read32 = (spi.write(write32>>16))<<16;
    read32 = (spi.write(write32))|read32;
    pc.printf("0x%08x ",read32);
    return  read32;
}

long writeSPI32bits_nodebug(long write32)
{
    long read32 = (spi.write(write32>>16))<<16;
    read32 = (spi.write(write32))|read32;
    return  read32;
}

int main() 
{
    // Setup the spi for 8 bit data, high steady state clock,
    // second edge capture, with a 100 kHz clock rate
    spi.format(16,3);
    spi.frequency(100000);

    pc.printf("\n\n\r.....GBA Multiboot Hack......\n\n\r");
    if (gba_present)
        pc.printf(".....Please turn off GBA.....\n\r");
    while(gba_present);    
    wait(0.2);    
    if(!gba_present)    
        pc.printf(".....Please turn on GBA......\n\r");
    while(!gba_present);            
    wait(0.2);
    pc.printf(".....GBA power up detected!..\n\r");        

    FILE *fp = fopen("/local/volt4.gba", "rb");             //*****change .gba file here***** 
    pc.printf(".....Opening GBA file readonly\r\n");

    fseek(fp, 0L, SEEK_END);                                //get *.gba file length
    long gba_file_length= ftell(fp);
    gba_file_length = (gba_file_length+0x0f)&0xfffffff0;    //align length to xfer to 16
    fseek(fp, 0L, SEEK_SET);
    pc.printf(".....GBA file length 0x%08x\r\n\n",gba_file_length);    
    pc.printf("MBED(mstr) GBA(slave) \r\n\n");
      
    while(read_from_gba!=0x72026202)
        {
        wait(0.01);
        read_from_gba = writeSPI32bits(0x00006202);
        pc.printf(" ;Looking for GBA\r\n");        
        }
        
    read_from_gba = writeSPI32bits(0x00006202);
    pc.printf(" ;Found GBA\r\n");

    read_from_gba = writeSPI32bits(0x00006102);
    pc.printf(" ;Recognition ok\r\n");

    int i;
    for(int Counter=0;Counter<=0x5f;Counter++)
    {
        i = getc(fp);                //Read in 16 bits of .gba file
        i = getc(fp)<<8|i;           //Read in 16 bits of .gba file

        read_from_gba =    writeSPI32bits_nodebug(0x0000ffff&i);
       // pc.printf(" ;send header a\r\n");
    }

    read_from_gba = writeSPI32bits(0x00006200);
    pc.printf(" ;Transfer of header data complete\r\n");
 
    read_from_gba = writeSPI32bits(0x00006202);
    pc.printf(" ;Exchange master/slave info again\r\n");
    
    read_from_gba = writeSPI32bits(0x000063d1);  
    pc.printf(" ;Send palette data\r\n");

    read_from_gba = writeSPI32bits(0x000063d1);  
    pc.printf(" ;Send palette data, receive 0x73hh****\r\n");

    long m    =    ((read_from_gba&0x00ff0000)>>8)+0xffff00d1;
    long hh    =    ((read_from_gba&0x00ff0000)>>16)+0xf;

    writeSPI32bits((((read_from_gba>>16)+0x0000000f)&0xff)|0x00006400);
    pc.printf(" ;Send handshake data\r\n");

    read_from_gba = writeSPI32bits((gba_file_length-0x190)/4);
    pc.printf(" ;Send length info, receive seed 0x**cc****\r\n");

    long    c    =    0x0000c387;
    long    f   =   ((((read_from_gba&0x00ff0000)>>8)+hh)+0x00)|0xffff0000;

    pc.printf("                       ;Send encrypted data (takes too long to show it all!)\r\n");

    unsigned int gba_file_location = 0xc0;
    while(gba_file_location>1)^0x0000c37b;
            else
                c = c>>1;      
            gba_data = gba_data>>1;        
         }            
         m = (0x6f646573*m)+1;
         writeSPI32bits_nodebug(gba_data2^((~(0x02000000+gba_file_location))+1)^m^0x43202f2f);//This line or next two
//         writeSPI32bits(gba_data2^((~(0x02000000+gba_file_location))+1)^m^0x43202f2f);   //*****Uncomment two line to see all data being transfered*****
//         pc.printf(" ;\n\r");                                                            //*****Uncomment two line to see all data being transfered*****
         gba_file_location=gba_file_location+4;
     }

    for(int bit=0;bit<32;bit++)
    {
        if((c^f)&0x01)
            c =(c>>1)^0x0000c37b;
        else
            c = c>>1;               
        f = f>>1;
    }
        
    while (writeSPI32bits(0x00000065)!=0x00750065)
    {
        pc.printf(" ;Wait for GBA to respond with CRC\n\r");
    }
    
    pc.printf(" ;GBA ready with CRC\n\r");
    
    writeSPI32bits(0x00000066);
    
    pc.printf(" ;Let's exchange CRC!\n\r");

    writeSPI32bits(c);

    pc.printf(" ;CRC ...hope they match!\n\r");
    pc.printf("                       ;All done!\n\r\n");

    fclose(fp);         //****Done with multiboot********************************************************
    
                        //****Following is code to talk to GBA running my custom program (volt4.gba)*****
    
    while(1)   //read in analog values on p15 and p16, only use 9 bits from both, pack up and send to GBA 
    {
    read_from_gba = writeSPI32bits((voltmeter1.read_u16()>>7)|((voltmeter2.read_u16()&0xff80)<<2));
    pc.printf("\r");
    read_from_gba =(~read_from_gba)&0x3ff;
    myled1 = (read_from_gba & 0x1); //turn on led1 when "A" button on GBA is pressed
    myled2 = (read_from_gba & 0x2); //B
    myled3 = (read_from_gba & 0x4); //Select
    myled4 = (read_from_gba & 0x8); //Start
    }
}


4 comments

23 Apr 2011

Awesome! I was looking at your GBA source files and was wondering if its possible to make them in C++ because assembly just shoots way over my head

23 Apr 2011

The GBA source code is in C already. Now all you have to do is get up to speed on how to program the GBA! Lots of info out there. Hint: google "gba programming manual".

Cheers and good luck!

Ken

24 Apr 2011

Well I hooked it all up and was unable to get it to work. I'll have to play with it more. As far as developing my own gba rom goes I'll have to do a few more years of learning before I can grasp that sort of stuff. Thanks for sharing and pointing me in the right direction!

@Ken Kaarvik @John Warren Have you been able to develop successfully any other multiboot programs? I'm interested in making use of multiboot in an upcoming project. Thanks !

You need to log in to post a comment