Yet Another Gamecube Documentation

18  Appendix


index

18.1  GCC Quick How To


index

18.1.1  compile ASM to object:


<DEVKITCUBE>/bin/powerpc-eabi-elf-as -c
-I <DEVKITCUBE>/powerpc-eabi-elf/include -I <additional includes>
testasm.s -o testasm.o
index

18.1.2  compile C to object:


<DEVKITCUBE>/bin/powerpc-eabi-elf-gcc -c
-I <DEVKITCUBE>/powerpc-eabi-elf/include -I <additional includes>
-nostdlib testc.c -o testc.o
index

18.1.3  compile C++ to object:


<DEVKITCUBE>/bin/powerpc-eabi-elf-g++ -c
-I <DEVKITCUBE>/powerpc-eabi-elf/include -I <additional includes>
-nostdlib -fno-exceptions testcpp.cpp -o testcpp.o
index

18.1.4  link objects


<DEVKITCUBE>/bin/powerpc-eabi-elf-ld -T ppc-ngcbin.x -o test.elf crt0.o
<DEVKITCUBE>/lib/gcc-lib/powerpc-eabi-elf/3.3/crtbegin.o
<DEVKITCUBE>/lib/gcc-lib/powerpc-eabi-elf/3.3/crtend.o
testasm.o testc.o testcpp.o -lg -lstdc++ -lm -lc -lnosys 
 
you only need to link against crtbegin.o/crtend.o if you are using c++, and you only need -lg,-lstdc++,-lc,-lm if you are actually using these libraries (of course:)). however if you do so, linking against -lnosys as well is essential.
index

18.1.5  remove unneeded sections (debug info etc) from object


<DEVKITCUBE>/bin/powerpc-eabi-elf-strip -s test.elf
index

18.1.6  convert object to plain binary


<DEVKITCUBE>/bin/powerpc-eabi-elf-objcopy -O binary test.elf test.bin
index

18.1.7  convert absolute address into filename/line number/function


compile with "-g" flag, then use

<DEVKITCUBE>/bin/powerpc-eabi-elf-addr2line -f -e test.elf 0x80003100
index

18.1.8  Building a Crosscompiler


configure options:
--target=powerpc-eabi-elf
--with-cpu=750
--disable-threads
--enable-languages=c
--disable-shared
--disable-nls
--with-newlib
index

18.1.9  Linker Script


to do
index

18.1.10  Startup Code


to do
index

18.2  Boot Process Details


The IPL (Initial Program Loader), or Bootrom, is located inside one Macronix chip (near Flipper, U10) and connected to the EXI bus. When the Gamecube is powered on, bit 25 (IP) in the Machine State Register is set, which means the system exception vector offset is 0xfff00000. Then a small (about 0x0700 bytes) program called 'BS' will be mapped to 0xfff00100 (the hardware reset vector) and control will be returned to the Gamecube like after a normal reset, which means 'BS' will be started.
index

18.2.1  BS - Bootstrap 1


index

18.2.2  BS2 - Bootstrap 2


BS2 is the Program that loads the game or shows the menus when the gamecube has been powered on without a game inserted. It was written in C, using official SDK libraries, probably earlier than 1.0. __start.c seems to be same as usual, except that there is no OSInit() call (old versions must call OSInit() in main, instead of __start).

note: this has been reversed from a PAL gamecube and looks different on a NTSC one.
18.2.2.1   short description of start() routine.

 
// 81300000
__start:
    __init_registers() // set stack pointer and static bases (r2, r13)
    __init_hardware() // paired-singles and cache init
    __init_data() // clear bss ?
    . // here goes Debug Monitor stuff
    .
    .
    DBInit() // debug monitor init :)
    __init_user() // cpp init
    main() // that's actually, IPL (BS2) code
    jmp exit() // halt CPU
18.2.2.2   IPL main() reversing

 
// 813006D4
main()
{
    BS2Init();
    OSInit();
    AD16Init();
    AD16WriteReg(0x800);
    DVDInit();
    AD16WriteReg(0x900);
    CARDInit();
    AD16WriteReg(0xa00);
    0x81302104(); // SRAM, real-time clock (check ?)
    __VIInit(0);
    VIInit();
    AD16WriteReg(0xb00);
    0x813004e4(); // setup performance. monitor
    0x8130222c(); // update time-base by SRAM clock
    0x813022c0(); // perform initial DVD actions and fall back into menu
    PADSetSpec(5); // sed PAD type ('spec') to 'production'
    PADInit();
    AD16WriteReg(0xc00);
    BS2Menu(); // here goes intro and main menu... (BIG one!)
    OSPanic(__FILE__, __LINE__, "BS2 ERROR > > > SHOULD NEVER REAC
HERE");

}
float NaN;
// 8130045C
void BS2Init()
{
    // clear LoMem and OSMem
    memset(0x80000000, 0, 256);
    memset(0x80003000, 0, 256);
    BATInit();
    // set memory size to 24MB
    *0x80000028 = 0x01800000;
    // set console type to default retail 1
    *0x8000002c = 1;
    // upgrade retail
    *0x8000002c += *0xcc00302c > > 28;
    (u32)NaN = -1;
    FPUInit();
}
// 813003A0
void BATInit()
{
    __asm
    {
        isync
        li r4, 0
        mtspr DBAT2L, r4
        mtspr DBAT2U, r4
        mtspr DBAT3L, r4
        mtspr DBAT3U, r4
        mtspr IBAT1L, r4
        mtspr IBAT1U, r4
        mtspr IBAT2L, r4
        mtspr IBAT2U, r4
        mtspr IBAT3L, r4
        mtspr IBAT3U, r4
        isync
    }
}
// 813003D8
void FPUInit()
{
    // FPU already initialized in __start(),
    // so just invalidate all FPRs.
    __asm
    {
    lfs f0, NaN
    fmr f1, f0
    fmr f2, f0
    fmr f3, f0
    . e
    . t
    . c
    fmr f31, f0
    }
}
// maybe later
0x81302104()
{
    __OSLockSram();
    __OSCheckSram();
    __OSGetRTC();
    OSTickToCalendarTime();
    memset();
    __OSUnlockSram();
    __OSSyncSram();
}
// maybe later
0x813004e4()
{
    OSDisableInterrupts();
    OSGetTick();
    OSGetTick();
    OSGetTick();
    __div2i();
    __div2i();
    PPCMtpmc1();
    PPCMtmmcr0();
    OSGetTick();
    OSGetTick();
    PPCMtmmcr0();
    PPCMfpmc1();
    __div2i();
    __div2i();
    __div2i();
    OSRestoreInterrupts();
}
// maybe later
0x8130222c()
{
    __OSLockSram();
    __OSGetRTC();
    __OSSetTime();
    __OSUnlockSram();
}
static int BS2State = 0;
// just layer..
0x813022c0()
{
    BS2State = BS2Mach();
}
// 81300A70
// located in __FILE__ = "BS2Mach.c"
int BS2Mach()
{
    static int state = 0;
    BOOL level = OSDisableInterrupts();
    switch(state)
    {
        case 0:
            [r13 - 0x7dc8] = 0x800030d4;
            state = 1;
        case 1:
            __OSGetSystemTime();
... some checks
            if(fail) break;
            state = 2;
        // Install DVD cover callback
        case 2:
            if([r13 - 0x7da8] == 0)
            {
                    r3 = [r13 - 0x7dc8]
                    [r3] = 0
                    [r13 - 0x7dc4] = 0
                    [r13 - 0x7dac] = 1
                    DVDLowSetResetCoverCallback(0);
                    DVDReset();
                    [r13 - 0x7da8] = 1
                    (s64)[r13 - 0x7d9c] = __OSGetSystemTime();
                break;
            }
            __OSGetSystemTime();



            if(fail) break;
            DVDLowSetResetCoverCallback(0x813007d8);
            DVDReset();
            state = 3;
        // Read Disk information (ID)
        case 3:
            DVDReadDiskID(0x8145e620 + 64, 0x80000000, 0x813007e4);
            state = 4;
        break;
.
.
.
        // Leave immediately ?
        case 16:
            break;
        default:
            OSPanic(__FILE__, __LINE__, "BS2 ERROR > > UNKNOWN STATE");
    }
    OSRestoreInterrupts(level);
    return (DVDLowGetCoverStatus() == 1) ? 19 : step;
}
// 81301154
void BS2Menu()
{
    BS2InitAlloc();
}
static OSHeapHandler BS2Heap;
// 81307EA8
void BS2InitAlloc()
{
    u8 *arenaLo;
    u8 *arenaHi;
    u8 *arenaNew;
    arenaLo = OSGetArenaLo();
    arenaLo = (void *)OSRoundUp32B(arenaLo);
    arenaHi = OSGetArenaHi();
    arenaHi = (void *)OSRoundDown32B(arenaHi);
    arenaNew = OSInitAlloc(0x80800000, arenaHi, 2);
    OSSetArenaLo(arenaHi);
    BS2Heap = OSCreateHeap(arenaLo, arenaHi);
    OSSetCurrentHeap(BS2Heap);
    OSAddToHeap(BS2Heap, arenaNew, 0x81100000);
    BS2CheckAlloc();
}
// 81307F34
void BS2CheckAlloc()
{
    OSCheckHeap(BS2Heap);
}
// 81307F58
void *OSAlloc(long size)
{
    void *ptr;
    if((ptr = OSAlloc(size)) == 0)
    {
        OSPanic(?);
    }
    return ptr;
} 
18.2.2.3   Map of IPL Library code

 
Address Name Libray
0x813014C8 DEMOInit (*) DEMO
0x81307F58 OSAlloc (*) OS
0x813327BC PPCMtmmcr0  
0x813327C4 PPCMfpmc1  
0x813327CC PPCMtpmc1  
0x81332814 OSInit OS
0x81332EF0 OSInitAlarm OS
0x81332F3C OSCreateAlarm  
0x81333688 OSAllocFromHeap  
0x81333784 OSSetCurrentHeap  
0x81333794 OSInitAlloc  
0x81333804 OSCreateHeap  
0x81333870 OSAddToHeap  
0x813338D0 OSCheckHeap  
0x813344C0 OSGetStackPointer  
0x8133491C OSReport  
0x8133499C OSPanic  
0x81334AA4 PPCHalt  
0x81334D4C EXIImm EXI
0x81335134 EXISync  
0x813353C8 EXIProbeReset  
0x8133570C EXISelect  
0x81335838 EXIDeselect  
0x81335D6C EXILock  
0x81335E60 EXIUnlock  
0x81335F54 AD16Init  
0x81336090 AD16WriteReg  
0x813361B0 OSDisableInterrupts  
0x813361C4 OSEnableInterrupts  
0x813361D8 OSRestoreInterrupts  
0x81336DD8 __OSGetRTC  
0x813372B0 __OSLockSram  
0x81337658 __OSUnlockSram  
0x813376A0 __OSSyncSram  
0x813376B0 __OSCheckSram  
0x81338504 OSInitThreadQueue  
0x8133939C OSGetTick  
0x813393B8 __OSSetTime  
0x8133943C __OSGetSystemTime  
0x8133963C OSTicksToCalendarTime  
0x8133AC50 DVDLowGetCoverStatus DVD
0x8133AB18 DVDLowReset  
0x8133ABD4 DVDLowSetResetCoverCallback  
0x8133B5F0 DVDInit  
0x8133CD18 DVDReadDiskID  
0x8133D0EC DVDReset  
0x8133DBE0 __VIInit VI
0x8133DDC8 VIInit  
0x8133E6C0 VIConfigure  
0x8133F0B4 VIGetTvFormat  


Address Name Libray
0x8134052c PADInit PAD
0x8134092c PADSetSpec  
0x81343114 CARDInit  
0x813480D4 GXInit  
0x81349148 GXInitFifoBase  
0x81349230 GXSetCPUFifo  
0x81349340 GXSetGPFifo  
0x813494B8 __GXFifoInit  
0x8134B0AC __GXPEInit  
0x8135A178 __div2i gcc
0x8135A394 __mod2i gcc
0x8135B494 vprintf stdlib

(*) these functions were slightly modified for the IPL.
index

18.2.3  Apploader


The Apploader provides functions to the bootrom that load the game (using bootrom read DVD functions). The bootrom calls the Init function, then the Main function in a loop, then the Closing function. At first, the BIOS calls the Apploader entrypoint with r3, r4, and r5 pointing to a free space for a 32 bit value.

// info based on Luigi Mansion appldr.bin file
// (built date is 17 Dec 2001).
// Apploader Entrypoint
// Input values :
// r3 = Address where to put the address of the Init function
// r4 = Address where to put the address of the Main Loading function
// r5 = Address where to put the address of the Closing function
// Return values :
// none
//
// file:[0010-0013] = 0x81200288 (apploader entrypoint)
void Entrypoint(r3, r4, r5)
{
    [r3] = 0x81200290 // Init
    [r4] = 0x81200580 // Main
    [r5] = 0x81200D50 // Close
}
// Init function
// Input values :
// ?
// Return values :
// none
void Init(void (*OSReport)(char *fmt, ...))
{
    // clear some important memory areas
    memset(OSAppLdr + 32, 0, 32);
    memset(&OSAppLdr.DolImage, 0, sizeof(DolImage));
    [+0x140] = 0
    OSAppLdr.pass = 0
    [+0x148] = 0
    OSAppLdr.OSReport = OSReport // save report callback
    OSAppLdr.OSReport("Apploader Initialized. $ Revision: 28 $n");
    OSAppLdr.OSReport("This Apploader built %s %sn", __DATE__, __TIME__);
}
// Main Loader function
//
// Input values :
// r3 = Address where to put the Memory destination of the disk read
// r4 = Address where to put the Size of the disk read
// r5 = Address where to put the Starting position of the disk read
//
// Return value:
// r3 = 0 if everything is already loaded
// = 1 (or !=0) if main function should be called again
//
// at 0x81200580
// helper functions (below)
u32 DOLSize(void);
// 0x812013E0 seems to be a big structure, like that :
struct OSAppLdr
{
    // untouched
    u32 SecondTimeForThePart;
    u8 [28]
    u8 [32] // "BB2" structure ?
    DolImage DolImage; // main DOL executable header
    // flags or something
    u32 +0x140
    u32 pass; // 0...12
    u32 +0x148
    // report routine itself is placed somewhere in bootrom
    void (*OSReport)(char *fmt, ...);
    // flags or something
    u32 +0x150
    u32 +0x154
    u32 +0x158
    u32 +0x15C
    u8 [32]
} OSAppLdr; // 0x174 total
int Main(r3, r4, r5)
{
    int pass = OSAppLdr.pass;
    if(pass <= 12)
    {
        switch(pass)
        {
            // read "BB2" structure (DVD offset at 0x0420)
            case 0:
            case 1:
                // "BB2" structure ?
                // 0420-0424 offset of main executable DOL
                // 0424-0427 offset of the FST
                // 0428-042B size of FST
                // 042C-042F maximum size of FST
                [r3] = OSAppLdr + 32
                [r4] = 32
                [r5] = 0x420
                OSAppLdr.pass = 2
                DCInvalidateRange([r3], [r4])
            break;
            // check "BB2" structure FST sizes
            case 2:
                FSTLength = [OSAppLdr + 32 + 8]
                FSTMaxLength = [OSAppLdr + 32 + 12]
                if(FSTLength > FSTMaxLength)
                {
                    OSAppLdr.OSReport(
                    "APPLOADER ERROR > > > FSTLength(%d) i
BB2 is greater

                    than FSTMaxLength(%d)n", FSTLength, FSTMaxLength);
                    PPCHalt();
                }
                [r3] = OSAppLdr + 0x160
                [r4] = 32
                [r5] = 0x440
                OSAppLdr.pass = 3
                DCInvalidateRange([r3], [r4])
            break;
            case 3:
                [0x800000E8] = [OSAppLdr + 0x160] // word
            break;
            case 4:
            // load main DOL header (256 bytes)
            case 5:
                [r3] = &OSAppLdr.DolImage
                [r4] = 256
                [r5] = [OSAppLdr + 32] // from BB2
                OSAppLdr.pass = 6
                DCInvalidateRange([r3], [r4])
            break;
            case 6:
                totalSize = DOLSize();
                maxSize = [[800000F4] + 0x28]; // PadSpec ?
                if((totalSize > maxSize) && maxSize)
                {
                    OSAppLdr.OSReport(
                    "APPLOADER ERROR > > > Total size of text/dat
sections

                    of the dol file are too big (%d(0x%08x) bytes). Currently
                    the limit is set as %d(0x%08x) bytesn", totalSize, maxSize);
                    PPCHalt();
                }
.
.
.
            case 7:
            case 8:
            case 9:
            case 10:
            case 11:
            case 12:
                if(SecondTimeForThePart == TRUE)
                {
                    OSAppLdr.OSReport(
                    "Failed assertion SecondTimeForThePart == TRUE");
                    PPCHalt();
                }
.
.
.
            break;
            }
        return 1;
    }
    else
    {
        return 0;
    }
}
// helper functions
// at 0x81200338
u32 DOLSize(void)
{
    DolImage *dol = &OSAppLdr.DolImage;
    u32 totalBytes = 0;
    int i;
    for(i=0; i<DOL_MAX_TEXT; i++)
    {
        if(dol->textData[i])
        {
        // aligned to 32 byte boundary
        totalBytes += (dol->textLen[i] + 31) & ~31;
        }
    }
    for(i=0; i<DOL_MAX_DATA; i++)
    {
        if(dol->dataData[i])
        {
            // aligned to 32 byte boundary
            totalBytes += (dol->dataLen[i] + 31) & ~31;
        }
    }
    return totalBytes;
}
// Closing function
//
// Return value: r3 = entry point
//
// at 0x81200D50
u32 Close(void)
{
    // provide entrypoint of main DOL executable to IPL
    return OSAppLdr.DolImage.entry;
}
index

18.2.4  Main DOL executable


index

18.3  Game and Maker Codes


index

18.3.1  Gamecodes


offset size Description
1 1 System ID
   
value id Description
0x47 G Gamecube (standard value)
0x44 D
used by Legend Of Zelda: Ocarina Of Time (Master Quest)
Might be a indicator for emulated/ported/promotional titles.
0x55 U used by GBA-Player Boot CD
2-3 2 Game ID/serial Number
4 1 Country/Region Code
   
value id Country
0x45 E USA/NTSC
0x50 P Europe/PAL
0x4a J Japan/NTSC
0x55 U used by the European version of The Legend Of Zelda: Ocarina Of Time (Master Quest)

index

18.3.2  Game Serial ID


Characters Description
3 System ID 'DOL'
4 Gamecode
3 Country ID
 
ID Country
USA guess what :)
NOE Nintendo of Europe
NOA Nintendo of America
JPN Japan


for example
index

18.3.3  Makercodes


The ID (2 Bytes ASCII) belongs to the publisher, not the developer. Hence, even though Rare developed Star Fox Adventures, and Retro Studios developed Metroid Prime, they both have the Vendor ID of Nintendo (01).
It is unknown how vendor IDs are allocated; However, all IDs thus far seem to be alphanumeric. If this is accurate, then as a result the maximum number of unique vendors is 1,296. Vendor IDs seem to be region-independent.
ID Vendor
01 Nintendo
08 Capcom
41 Ubisoft
4F Eidos
51 Acclaim
52 Activision
5D Midway
5G Hudson
64 Lucas Arts
69 Electronic Arts
6S TDK Mediactive
8P Sega
A4 Mirage Studios
AF Namco
B2 Bandai
DA Tomy
EM Konami

index

18.4  Macronix Chip IDs


  MX ff t mm b p r s
MX MX', vendor id
ff 2 digits, device family
 
17 auto focus controller
23 mask rom
25 spi serial flash memory
26 mtp eeprom
27 eeprom
28 flash memory
29 flash memory (single voltage)
53 memory card (smc)
67 flash memory
69 flash memory + sram (stacked chip)
88 digital camera/flat panel display controller
89 flat panel display controller
92 sound generator
93 single chip answering machine/digital recorder controller
97 isdn controller
98 network
99 bluetooth
t 1 character, device type
 
c cmos
f flash
l low-voltage
w srw
v 2.2v
u 1.8v
x 1.5v
vw 2.25v+srw
mm 2 to 4 digits, mode/density
 
004 4M, x8 Boot Block
040 4M, x8 Equal Sector
400 4M, x8/x16 Boot Block
   
   
   
   
   
   
   
   
   
b 1 character, bootblock type (rom only)
 
t top
b bottom


  MX ff t mm b p r s
p 1 character, package type
 
p plastic dip
m plastic sop
q plastic plcc
t tsop normal
d qeramic dip
x8 0.8mm ball pitch, bga, csp
x csp
r 1 character, temperature range
 
c commercial
i industrial
m military
s 1 character, speed
 
45 45ns
55 55ns
70 70ns
85 85ns
90 90ns
10 100ns
12 120ns
15 150ns
20 200ns
25 250ns

index

18.5  chip simelarities


index

18.6  Easter Eggs


index

18.7  Terms and Acronyms



index