Dolphin OS timing initialization -------------------------------- BOOTROM: =-=-=-=- Bootrom will call __SyncTime() in its main() before gfx menu. typedef s64 OSTime; typedef s32 OSTick; OS timer clock period is 1/12 of Gekko CPU core clock : 486 mhz / 12 = 40.5 mhz // update time-base registers by RTC (real-time clock) value // RTC is number of seconds, since 01 January 00:00:00 2000 (current epoch) void __SyncTime() { OSTick rtcValue; OSSram* sram = __OSLockSram(); if( __OSGetRTC(&rtcValue) == TRUE ) { // counterBias can be negative value rtcValue += (OSTick)sram->counterBias; __OSSetTime( (OSTime)rtcValue * OS_TIMER_CLOCK ); } __OSUnlockSram(0); } // here is SRAM structure typedef struct { u16 checkSum; // SRAM byte checksum (discovered by Costis) u16 checkSumInv; // inverted checksum (see YAGCD) u32 ead0; u32 ead1; s32 counterBias; // now we already know meaning of this one too :) s8 displayOffsetH; // used by VI library u8 ntd; u8 language; u8 flags; u8 dummy[44]; } SRAM; OS: =-= __OSSetTime is used only in bootrom, to set time-base registers according to battery-backed RTC value. // OS low-memory variable OSTime __OSSystemTime AT_ADDRESS(OS_BASE_CACHED | 0x30D8); void __OSSetTime(OSTime newValue) { BOOL enabled = OSDisableInterrupts(); // system time keep its value during soft resets (?) __OSSystemTime += newValue - OSGetTime(); // I'm unsure of that // 64 bit math is hard to disasm __SetTime(newValue); EXIProbeReset(); OSRestoreInterrupts(enabled); } // use undocumented SPR registers, to set time-base // SPR[284] = TBL, to write // SPR[285] = TBU, to write void __fastcall __SetTime(OSTime time) { __asm li r5, 0 __asm mtspr 284, r5 // clear TBL value (to avoid TBU++) __asm mtspr 285, r3 // set TBU __asm mtspr 284, r4 // set TBL __asm blr } Emulation issues : Games dont like real TBR value. At least they work better, when TBR is set to 0 before game start. Unemulated TBR is only affect on memcard save date, AFAIK. To convert TBR value to real-time I use Windows file time utility : // covert GC time to human-usable time string; // example output : "30 Jun 2004 3:06:14:127" char * OSTimeFormat(u64 tbr) { // FILETIME - number of 1/10000000 intervals, since Jan 1 1601 // GC time - number of 1/40500000 sec intervals, since Jan 1 2000 // To convert GCTIME -> FILETIME : // 1: adjust GCTIME by number of 1/10000000 intervals // between Jan 1 1601 and Jan 1 2000. // 2: assume X - 1/10000000 sec, Y - 1/40500000 sec, // FILETIME = (GCTIME * Y) / X // coversion GCTIME -> FILETIME #define MAGIK 0x0713AD7857941000 f64 x = 1.0 / 10000000.0, y = 1.0 / 40500000.0; tbr += MAGIK; u64 ft = (u64)( ((f64)(s64)tbr * y) / x ); FILETIME fileTime; SYSTEMTIME sysTime; fileTime.dwHighDateTime = (u32)(ft >> 32); fileTime.dwLowDateTime = (u32)(ft & 0x00000000ffffffff); FileTimeToSystemTime(&fileTime, &sysTime); // format string static char *mnstr[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static char gcTime[256]; sprintf( gcTime, "%i %s %i %02i:%02i:%02i:%03i", sysTime.wDay, mnstr[sysTime.wMonth - 1], sysTime.wYear, sysTime.wHour, sysTime.wMinute, sysTime.wSecond, sysTime.wMilliseconds ); return gcTime; } Although this conversion is not perfect :O) Play with MAGIK value to find suitable bias adjustment. Tell me please, if you find better way to format GC time. Document by org RTC: 11 Dec 2004 :)