------------------------------------------------------------------------------- -= NINTENDO GAMECUBE Initial Program Loader RE =- ------------------------------------------------------------------------------- IPL, or bootrom is located inside on-board Macronix chip (near Flipper), and connected to EXI bus. IPL have mapped CPU address space at 0xFFFnnnnn, so exception prefix bit 25 (IP) in MSR is set after CPU reset and system reset vector offset is 0xFFF00100. Small program, called 'BS' is loading actual program code (menu, RTC stuff) at 0x81300000. This part is called 'BS2'. Probably 'BS' stands for 'Boot System', like OS is 'Operating System'. IPL cant be readed after boot neither using CPU, not special EXI bus reads. After loading of BS2, BS disabling IPL access, by clearing 17th bit of 0xCC006800, then it sets MSR[IP] bit to normal mode (pointing lower memory), and jumps to BS2 code entrypoint. BS2 was written on 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 should call OSInit() in main, instead of __start). Here is the short description of start() routine (you can easily find full version in your SDK ;) : // 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 main() reversing : ------------------ // 813006D4 (in BS2.c) 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 perf. monitor __SyncTime(); // update CPU timer by RTC value 0x813022c0(); // perform initial DVD actions and fall back into menu PADSetSpec(5); // set pad type ('spec') to production PADInit(); AD16WriteReg(0xc00); BS2Menu(); // here goes intro and main menu... (BIG one!) OSHalt("BS2 ERROR >>> SHOULD NEVER REACH HERE"); } // ---------------------------------------------------------------------------- static float ZeroF; // 8130045C void BS2Init() { // clear lower memory memset(0x80000000, 0, 256); memset(0x80003000, 0, 256); __OSInitBATs(); // set memory size to 24MB *(u32 *)0x80000028 = 0x01800000; // set console type to default retail 1 *(u32 *)0x8000002c = 1; // upgrade retail *(u32 *)0x8000002c += *(u32 *)0xcc00302c >> 28; *(u32 *)&ZeroF = -1; __OSInitFPRs(); } // 813003A0 (removed after SDK 1.0 release) void __OSInitBATs() { __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 (from OS.c) void __OSInitFPRs() { // FP is already initialzied in __start(), // so just invalidate all FPRs. __asm { lfs f0, ZeroF 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(); } // update time-base registers by RTC (real-time clock) value // RTC is number of seconds, since 01 January 00:00:00 2000 void __SyncTime() { OSTick rtcValue; OSSram* sram = __OSLockSram(); if( __OSGetRTC(&rtcValue) == TRUE ) { // counterBias can be negative value rtcValue += sram->counterBias; __OSSetTime( (OSTime)rtcValue * OS_TIMER_CLOCK ); } __OSUnlockSram(0); } static int BS2State = 0; // just wrapper.. 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 ... lot of mulhwu, subfe and other strange instructions :) ... lazy to find corresponding macro in os.h 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(); bla bla bla another checks :) if(fail) break; DVDLowSetResetCoverCallback(0x813007d8); DVDReset(); state = 3; // Read Disk information (ID) case 3: DVDReadDiskID(0x8145e620 + 64, 0x80000000, 0x813007e4); state = 4; break; . . . . . . . // Leave immidiately ? case 16: break; default: OSHalt("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; } here is the MAP of BS2 libraries code : --------------------------------------- 813014C8 DEMOInit (*) 81307F58 OSAlloc (*) 813327BC PPCMtmmcr0 ;; PPC 813327C4 PPCMfpmc1 813327CC PPCMtpmc1 81332814 OSInit ;; OS 81332EF0 OSInitAlarm 81332F3C OSCreateAlarm 81333688 OSAllocFromHeap 81333784 OSSetCurrentHeap 81333794 OSInitAlloc 81333804 OSCreateHeap 81333870 OSAddToHeap 813338D0 OSCheckHeap 813344C0 OSGetStackPointer 8133491C OSReport 8133499C OSPanic 81334AA4 PPCHalt 81334D4C EXIImm ;; OS EXI (not standalone) 81335134 EXISync 813353C8 EXIProbeReset 8133570C EXISelect 81335838 EXIDeselect 81335D6C EXILock 81335E60 EXIUnlock 81335F54 AD16Init 81336090 AD16WriteReg 813361B0 OSDisableInterrupts 813361C4 OSEnableInterrupts 813361D8 OSRestoreInterrupts 81336DD8 __OSGetRTC 813372B0 __OSLockSram 81337658 __OSUnlockSram 813376A0 __OSSyncSram 813376B0 __OSCheckSram 81338504 OSInitThreadQueue 8133939C OSGetTick 813393B8 __OSSetTime 8133943C __OSGetSystemTime 8133963C OSTicksToCalendarTime 8133AC50 DVDLowGetCoverStatus ;; DVD 8133AB18 DVDLowReset 8133ABD4 DVDLowSetResetCoverCallback 8133B5F0 DVDInit 8133CD18 DVDReadDiskID 8133D0EC DVDReset 8133DBE0 __VIInit ;; VI 8133DDC8 VIInit 8133E6C0 VIConfigure 8133F0B4 VIGetTvFormat 81343114 CARDInit ;; CARD 813480D4 GXInit ;; GX 81349148 GXInitFifoBase 81349230 GXSetCPUFifo 81349340 GXSetGPFifo 813494B8 __GXFifoInit 8134B0AC __GXPEInit 8135A178 __div2i ;; LIBC 8135A394 __mod2i 8135B494 vprintf (*) that function was slighty modified particulary for BS2 ? note : European PAL IPL v. 1.0 was used. ------------------------------------------------------------------------------- org -- ogamespec@gmail.com Thanks to tmbinc and titanik for their great work! links : GCDEV headquarters : http://www.gcdev.com, http://www.gcdev.com/forums brief description of BS2 run-flow you can find in awesome Titanik's 'GAMECUBE LOW-LEVEL INFO'.