FastTimes.c

Go to the documentation of this file.
00001 /*  $Id: FastTimes.c 402 2005-01-18 05:39:13Z sethdill $    */
00002 
00003 /* File "FastTimes.c" - Original code by Matt Slot <fprefect@ambrosiasw.com>  */
00004 /* Created 4/24/99    - This file is hereby placed in the public domain       */
00005 /* Updated 5/21/99    - Calibrate to VIA, add TBR support, renamed functions  */
00006 /* Updated 10/4/99    - Use AbsoluteToNanoseconds() in case Absolute = double */
00007 /* Updated 2/15/00    - Check for native Time Manager, no need to calibrate   */
00008 /* Updated 2/19/00    - Fixed default value for gScale under native Time Mgr  */
00009 /* Updated 3/21/00    - Fixed ns conversion, create 2 different scale factors */
00010 /* Updated 5/03/00    - Added copyright and placed into PD. No code changes   */
00011 /* Updated 8/01/00    - Made "Carbon-compatible" by replacing LMGetTicks()    */
00012 /* Updated 8/22/00    - Fixed optimizer bug, changed GENPPC macros, extern C  */
00013 
00014 /* This file is Copyright (C) Matt Slot, 1999-2000. It is hereby placed into 
00015    the public domain. The author makes no warranty as to fitness or stability */
00016 
00017 #include "frontier.h"
00018 #include "standard.h"
00019 
00020 #if !FRONTIER_FRAMEWORK_INCLUDES
00021     #include <DriverServices.h>
00022     #include <Timer.h>
00023 #endif
00024 
00025 #if TARGET_API_MAC_CARBON
00026     #include "CallMachOFrameWork.h" /*2005-01-15 aradke*/
00027 #endif
00028 
00029 #include "FastTimes.h"
00030 
00031 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00032 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00033 /*
00034     On 680x0 machines, we just use Microseconds().
00035     
00036     On PowerPC machines, we try several methods:
00037       * DriverServicesLib is available on all PCI PowerMacs, and perhaps
00038         some NuBus PowerMacs. If it is, we use UpTime() : Overhead = 2.1 Ásec.
00039       * The PowerPC 601 has a built-in "real time clock" RTC, and we fall
00040         back to that, accessing it directly from asm. Overhead = 1.3 Ásec.
00041       * Later PowerPCs have an accurate "time base register" TBR, and we 
00042         fall back to that, access it from PowerPC asm. Overhead = 1.3 Ásec.
00043       * We can also try Microseconds() which is emulated : Overhead = 36 Ásec.
00044 
00045     On PowerPC machines, we avoid the following:
00046       * OpenTransport is available on all PCI and some NuBus PowerMacs, but it
00047         uses UpTime() if available and falls back to Microseconds() otherwise.
00048       * InputSprocket is available on many PowerMacs, but again it uses
00049         UpTime() if available and falls back to Microseconds() otherwise.
00050 
00051     Another PowerPC note: certain configurations, especially 3rd party upgrade
00052     cards, may return inaccurate timings for the CPU or memory bus -- causing
00053     skew in various system routines (up to 20% drift!). The VIA chip is very
00054     accurate, and it's the basis for the Time Manager and Microseconds().
00055     Unfortunately, it's also very slow because the MacOS has to (a) switch to
00056     68K and (b) poll for a VIA event.
00057     
00058     We compensate for the drift by calibrating a floating point scale factor
00059     between our fast method and the accurate timer at startup, then convert
00060     each sample quickly on the fly. I'd rather not have the initialization 
00061     overhead -- but it's simply necessary for accurate timing. You can drop
00062     it down to 30 ticks if you prefer, but that's as low as I'd recommend.
00063 
00064     Under MacOS 9, "new world" Macs (iMacs, B+W G3s and G+W G4s) have a native
00065     Time Manager implementation: UpTime(), Microseconds(), and TickCount() are
00066     all based on the same underlying counter. This makes it silly to calibrate
00067     UpTime() against TickCount(). We now check for this feature using Gestalt(),
00068     and skip the whole calibration step if possible.
00069 
00070 */
00071 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00072 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00073 
00074 #define RTCToNano(w)    ((double) (w).hi * 1000000000.0 + (double) (w).lo)
00075 #define WideTo64bit(w)  (*(UInt64 *) &(w))
00076 
00077 /* LMGetTicks() is not in Carbon and TickCount() has a fair bit of overhead,
00078    so for speed we always read lowmem directly. This is a MacOS X no-no, but 
00079    it always work on those systems that don't have a native Time Manager (ie,
00080    anything before MacOS 9) -- regardless whether we are in Carbon or not! */
00081 #define MyLMGetTicks()  (*(volatile UInt32 *) 0x16A)
00082 
00083 static Boolean          gInited = false;
00084 
00085 #if TARGET_CPU_PPC
00086 
00087 static Boolean          gNative = false;
00088 static Boolean          gUseRTC = false;
00089 static Boolean          gUseTBR = false;
00090 static double           gScaleUSec = 1.0 / 1000.0;    /* 1 / ( nsec / usec) */
00091 static double           gScaleMSec = 1.0 / 1000000.0; /* 1 / ( nsec / msec) */
00092 
00093     #if (! TARGET_API_MAC_CARBON)
00094     
00095     static __asm__ UnsignedWide PollRTC(void);
00096     static __asm__ UnsignedWide PollTBR(void);
00097 
00098     #endif
00099 
00100 static Ptr FindFunctionInSharedLib(StringPtr libName, StringPtr funcName);
00101 
00102 /* Functions loaded from DriverServicesLib */
00103 typedef AbsoluteTime    (*UpTimeProcPtr)(void);
00104 typedef Nanoseconds     (*A2NSProcPtr)(AbsoluteTime);
00105 static UpTimeProcPtr    gUpTime = NULL;
00106 static A2NSProcPtr      gA2NS = NULL;
00107 
00108 #endif /* TARGET_CPU_PPC */
00109 
00110 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00111 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00112 
00113 void FastInitialize() {
00114     SInt32          result;
00115 
00116     if (!gInited) {
00117 
00118 #if TARGET_CPU_PPC
00119 
00120         /* Initialize the feature flags */
00121         gNative = gUseRTC = gUseTBR = false;
00122 
00123         /* We use CFM to find and load needed symbols from shared libraries, so
00124            the application doesn't have to weak-link them, for convenience.   */
00125         gUpTime = (UpTimeProcPtr) FindFunctionInSharedLib(
00126                 "\pDriverServicesLib", "\pUpTime");
00127         if (gUpTime) gA2NS = (A2NSProcPtr) FindFunctionInSharedLib(
00128                 "\pDriverServicesLib", "\pAbsoluteToNanoseconds");
00129         if (!gA2NS) gUpTime = nil; /* Pedantic but necessary */
00130         
00131         #if TARGET_API_MAC_CARBON
00132         
00133         /* 2005-01-15 aradke: On OS X, DriverServicesLib is no longer present,
00134            but the functions we want are available in the CoreServices framework. */
00135 
00136         if (!gUpTime) {
00137 
00138             gUpTime = (UpTimeProcPtr) getframeworkfuncptr (
00139                     CFSTR("CoreServices.framework"), CFSTR("UpTime"));
00140 
00141             if (gUpTime)
00142                 gA2NS = (A2NSProcPtr) getframeworkfuncptr (
00143                     CFSTR("CoreServices.framework"), CFSTR("AbsoluteToNanoseconds"));
00144 
00145             if (!gA2NS)
00146                 gUpTime = nil; /* Pedantic but necessary */
00147             }
00148         
00149         #endif
00150         
00151         if (gUpTime) {
00152             /* If we loaded UpTime(), then we need to know if the system has
00153                a native implementation of the Time Manager. If so, then it's
00154                pointless to calculate a scale factor against the missing VIA */
00155 
00156             /* gestaltNativeTimeMgr = 4 in some future version of the headers */
00157             if (!Gestalt(gestaltTimeMgrVersion, &result) &&
00158                     (result > gestaltExtendedTimeMgr)) 
00159                 gNative = true;
00160             }
00161           else {
00162             /* If no DriverServicesLib, use Gestalt() to get the processor type. 
00163                Only NuBus PowerMacs with old System Software won't have DSL, so
00164                we know it should either be a 601 or 603. */
00165 
00166             /* Use the processor gestalt to determine which register to use */
00167             if (!Gestalt(gestaltNativeCPUtype, &result)) {
00168                 if (result == gestaltCPU601) gUseRTC = true;
00169                   else if (result > gestaltCPU601) gUseTBR = true;
00170                 }
00171             }
00172 
00173         /* Now calculate a scale factor to keep us accurate. */
00174         if ((gUpTime && !gNative) || gUseRTC || gUseTBR) {
00175             UInt64          tick, usec1, usec2;
00176             UnsignedWide    wide;
00177 
00178             /* Wait for the beginning of the very next tick */
00179 #if TARGET_API_MAC_CARBON
00180             for(tick = TickCount() + 1; tick > TickCount(); )
00181                 ;
00182 
00183             /* Poll the selected timer and prepare it (since we have time) */
00184             wide = (*gA2NS)((*gUpTime)());
00185             usec1 = WideTo64bit(wide);
00186             
00187             /* Wait for the exact 60th tick to roll over */
00188             while(tick + 60 > TickCount())
00189                 ;
00190 
00191             /* Poll the selected timer again and prepare it  */
00192             wide = (*gA2NS)((*gUpTime)());
00193             usec2 = WideTo64bit(wide);
00194 #else
00195             for(tick = MyLMGetTicks() + 1; tick > MyLMGetTicks(); )
00196                 ;
00197 
00198             /* Poll the selected timer and prepare it (since we have time) */
00199             wide = (gUpTime) ? (*gA2NS)((*gUpTime)()) : 
00200                     ((gUseRTC) ? PollRTC() : PollTBR());
00201             usec1 = (gUseRTC) ? RTCToNano(wide) : WideTo64bit(wide);
00202             
00203             /* Wait for the exact 60th tick to roll over */
00204             while(tick + 60 > MyLMGetTicks())
00205                 ;
00206 
00207             /* Poll the selected timer again and prepare it  */
00208             wide = (gUpTime) ? (*gA2NS)((*gUpTime)()) : 
00209                     ((gUseRTC) ? PollRTC() : PollTBR());
00210             usec2 = (gUseRTC) ? RTCToNano(wide) : WideTo64bit(wide);
00211 #endif /* TARGET_API_MAC_CARBON */
00212 
00213             /* Calculate a scale value that will give microseconds per second.
00214                Remember, there are actually 60.15 ticks in a second, not 60.  */
00215             gScaleUSec = (60.0 * 1000000.0) / ((usec2 - usec1) * 60.15);
00216             gScaleMSec = gScaleUSec / 1000.0;
00217             }
00218 
00219 #endif /* TARGET_CPU_PPC */
00220 
00221         /* We've initialized our globals */
00222         gInited = true;
00223         }
00224     }
00225 
00226 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00227 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00228 
00229 UInt64 FastMicroseconds() {
00230     UnsignedWide    wide;
00231     UInt64          usec;
00232     
00233 #if TARGET_CPU_PPC
00234     /* Initialize globals the first time we are called */
00235     if (!gInited) FastInitialize();
00236     
00237     if (gNative) {
00238         /* Use DriverServices if it's available -- it's fast and compatible */
00239         wide = (*gA2NS)((*gUpTime)());
00240         usec = (double) WideTo64bit(wide) * gScaleUSec + 0.5;
00241         }
00242       else if (gUpTime) {
00243         /* Use DriverServices if it's available -- it's fast and compatible */
00244         wide = (*gA2NS)((*gUpTime)());
00245         usec = (double) WideTo64bit(wide) * gScaleUSec + 0.5;
00246         }
00247 
00248     #if (! TARGET_API_MAC_CARBON)
00249     
00250       else if (gUseTBR) {
00251         /* On a recent PowerPC, we poll the TBR directly */
00252         wide = PollTBR();
00253         usec = (double) WideTo64bit(wide) * gScaleUSec + 0.5;
00254         }
00255       else if (gUseRTC) {
00256         /* On a 601, we can poll the RTC instead */
00257         wide = PollRTC();
00258         usec = (double) RTCToNano(wide) * gScaleUSec + 0.5;
00259         }
00260     
00261     #endif /* TARGET_API_MAC_CARBON */
00262 
00263       else 
00264 #endif /* TARGET_CPU_PPC */
00265         {
00266         /* If all else fails, suffer the mixed mode overhead */
00267         Microseconds(&wide);
00268         usec = WideTo64bit(wide);
00269         }
00270 
00271     return(usec);
00272     }
00273 
00274 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00275 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00276 
00277 UInt64 FastMilliseconds() {
00278     UnsignedWide    wide;
00279     UInt64          msec;   
00280     
00281 #if TARGET_CPU_PPC
00282     /* Initialize globals the first time we are called */
00283     if (!gInited) FastInitialize();
00284     
00285     if (gNative) {
00286         /* Use DriverServices if it's available -- it's fast and compatible */
00287         wide = (*gA2NS)((*gUpTime)());
00288         msec = (double) WideTo64bit(wide) * gScaleMSec + 0.5;
00289         }
00290       else if (gUpTime) {
00291         /* Use DriverServices if it's available -- it's fast and compatible */
00292         wide = (*gA2NS)((*gUpTime)());
00293         msec = (double) WideTo64bit(wide) * gScaleMSec + 0.5;
00294         }
00295     
00296     #if (! TARGET_API_MAC_CARBON)
00297     
00298       else if (gUseTBR) {
00299         /* On a recent PowerPC, we poll the TBR directly */
00300         wide = PollTBR();
00301         msec = (double) WideTo64bit(wide) * gScaleMSec + 0.5;
00302         }
00303       else if (gUseRTC) {
00304         /* On a 601, we can poll the RTC instead */
00305         wide = PollRTC();
00306         msec = (double) RTCToNano(wide) * gScaleMSec + 0.5;
00307         }
00308     
00309     #endif /* ! TARGET_API_MAC_CARBON */
00310     
00311       else 
00312 #endif /* TARGET_CPU_PPC */
00313         {
00314         /* If all else fails, suffer the mixed mode overhead */
00315         Microseconds(&wide);
00316         msec = ((double) WideTo64bit(wide) + 500.0) / 1000.0;
00317         }
00318 
00319     return(msec);
00320     }
00321 
00322 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00323 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00324 
00325 StringPtr FastMethod() {
00326     StringPtr   method = "\p<Unknown>";
00327 
00328 #if TARGET_CPU_PPC
00329     /* Initialize globals the first time we are called */
00330     if (!gInited) FastInitialize();
00331     
00332     if (gNative) {
00333         /* The Time Manager and UpTime() are entirely native on this machine */
00334         method = "\pNative UpTime()";
00335         }
00336       else if (gUpTime) {
00337         /* Use DriverServices if it's available -- it's fast and compatible */
00338         method = "\pUpTime()";
00339         }
00340     
00341     #if (! TARGET_API_MAC_CARBON)
00342       else if (gUseTBR) {
00343         /* On a recent PowerPC, we poll the TBR directly */
00344         method = "\pPowerPC TBR";
00345         }
00346       else if (gUseRTC) {
00347         /* On a 601, we can poll the RTC instead */
00348         method = "\pPowerPC RTC";
00349         }
00350     #endif
00351     
00352       else 
00353 #endif /* TARGET_CPU_PPC */
00354         {
00355         /* If all else fails, suffer the mixed mode overhead */
00356         method = "\pMicroseconds()";
00357         }
00358 
00359     return(method);
00360     }
00361 
00362 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00363 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00364 #pragma mark -
00365 
00366 #if TARGET_CPU_PPC
00367     #if (! TARGET_API_MAC_CARBON)
00368     
00369 __asm__ static UnsignedWide PollRTC_() {
00370 entry PollRTC /* Avoid CodeWarrior glue */
00371     machine 601
00372 @AGAIN:
00373     mfrtcu  r4 /* RTCU = SPR 4 */
00374     mfrtcl  r5 /* RTCL = SPR 5 */
00375     mfrtcu  r6
00376     cmpw    r4,r6
00377     bne     @AGAIN
00378     stw     r4,0(r3)
00379     stw     r5,4(r3)
00380     blr
00381     }
00382 
00383 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00384 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00385 
00386 __asm__ static UnsignedWide PollTBR_() {
00387 entry PollTBR /* Avoid CodeWarrior glue */
00388     machine 604
00389 @AGAIN:
00390     mftbu   r4 /* TBRU = SPR 268 */
00391     mftb    r5 /* TBRL = SPR 269 */
00392     mftbu   r6
00393     cmpw    r4,r6
00394     bne     @AGAIN
00395     stw     r4,0(r3)
00396     stw     r5,4(r3)
00397     blr
00398     }
00399     
00400     #endif
00401 
00402 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00403 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
00404 
00405 static Ptr FindFunctionInSharedLib(StringPtr libName, StringPtr funcName) {
00406     Str255              errorStr;
00407     Ptr                 func = NULL;
00408     Ptr                 entry = NULL;
00409     CFragSymbolClass    symClass;
00410     CFragConnectionID   connID;
00411     
00412     /* Find CFM containers for the current archecture -- CFM-PPC or CFM-68K */
00413     if (GetSharedLibrary(libName, kCompiledCFragArch, kLoadCFrag, &connID, &entry, errorStr) != noErr)
00414         return(NULL);
00415     
00416     if (FindSymbol(connID, funcName, &func, &symClass) != noErr)
00417         return(NULL);
00418     
00419     return(func);
00420     }
00421 #endif /* TARGET_CPU_PPC */

Generated on Wed May 31 18:19:46 2006 for frontierkernel 10.1.10a by  doxygen 1.4.6