src/timer/macos/FastTimes.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. RTCToNano
  2. WideTo64bit
  3. MyLMGetTicks
  4. FastInitialize
  5. FastMicroseconds
  6. FastMilliseconds
  7. FastMethod
  8. PollRTC_
  9. PollTBR_
  10. FindFunctionInSharedLib

   1 /* File "FastTimes.c" - Original code by Matt Slot <fprefect@ambrosiasw.com>  */
   2 /* Created 4/24/99    - This file is hereby placed in the public domain       */
   3 /* Updated 5/21/99    - Calibrate to VIA, add TBR support, renamed functions  */
   4 /* Updated 10/4/99    - Use AbsoluteToNanoseconds() in case Absolute = double */
   5 /* Updated 2/15/00    - Check for native Time Manager, no need to calibrate   */
   6 /* Updated 2/19/00    - Fixed default value for gScale under native Time Mgr  */
   7 /* Updated 3/21/00    - Fixed ns conversion, create 2 different scale factors */
   8 /* Updated 5/03/00    - Added copyright and placed into PD. No code changes   */
   9 /* Updated 8/01/00    - Made "Carbon-compatible" by replacing LMGetTicks()    */
  10 
  11 /* This file is Copyright (C) Matt Slot, 1999-2000. It is hereby placed into 
  12    the public domain. The author makes no warranty as to fitness or stability */
  13 
  14 #include <Gestalt.h>
  15 #include <LowMem.h>
  16 #include <CodeFragments.h>
  17 #include <DriverServices.h>
  18 #include <Timer.h>
  19 
  20 #include "FastTimes.h"
  21 
  22 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
  23 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
  24 /*
  25         On 680x0 machines, we just use Microseconds().
  26         
  27         On PowerPC machines, we try several methods:
  28           * DriverServicesLib is available on all PCI PowerMacs, and perhaps
  29             some NuBus PowerMacs. If it is, we use UpTime() : Overhead = 2.1 µsec.
  30           * The PowerPC 601 has a built-in "real time clock" RTC, and we fall
  31             back to that, accessing it directly from asm. Overhead = 1.3 µsec.
  32           * Later PowerPCs have an accurate "time base register" TBR, and we 
  33             fall back to that, access it from PowerPC asm. Overhead = 1.3 µsec.
  34           * We can also try Microseconds() which is emulated : Overhead = 36 µsec.
  35 
  36         On PowerPC machines, we avoid the following:
  37           * OpenTransport is available on all PCI and some NuBus PowerMacs, but it
  38             uses UpTime() if available and falls back to Microseconds() otherwise.
  39           * InputSprocket is available on many PowerMacs, but again it uses
  40             UpTime() if available and falls back to Microseconds() otherwise.
  41 
  42         Another PowerPC note: certain configurations, especially 3rd party upgrade
  43         cards, may return inaccurate timings for the CPU or memory bus -- causing
  44         skew in various system routines (up to 20% drift!). The VIA chip is very
  45         accurate, and it's the basis for the Time Manager and Microseconds().
  46         Unfortunately, it's also very slow because the MacOS has to (a) switch to
  47         68K and (b) poll for a VIA event.
  48         
  49         We compensate for the drift by calibrating a floating point scale factor
  50         between our fast method and the accurate timer at startup, then convert
  51         each sample quickly on the fly. I'd rather not have the initialization 
  52         overhead -- but it's simply necessary for accurate timing. You can drop
  53         it down to 30 ticks if you prefer, but that's as low as I'd recommend.
  54 
  55         Under MacOS 9, "new world" Macs (iMacs, B+W G3s and G+W G4s) have a native
  56         Time Manager implementation: UpTime(), Microseconds(), and TickCount() are
  57         all based on the same underlying counter. This makes it silly to calibrate
  58         UpTime() against TickCount(). We now check for this feature using Gestalt(),
  59         and skip the whole calibration step if possible.
  60 
  61 */
  62 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
  63 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
  64 
  65 #define RTCToNano(w)    ((double) (w).hi * 1000000000.0 + (double) (w).lo)
     /* [<][>][^][v][top][bottom][index][help] */
  66 #define WideTo64bit(w)  (*(UInt64 *) &(w))
     /* [<][>][^][v][top][bottom][index][help] */
  67 
  68 /* LMGetTicks() is not in Carbon and TickCount() has a fair bit of overhead,
  69    so for speed we always read lowmem directly. This is a MacOS X no-no, but 
  70    it always work on those systems that don't have a native Time Manager (ie,
  71    anything before MacOS 9) -- regardless whether we are in Carbon or not! */
  72 #define MyLMGetTicks()  (*(volatile UInt32 *) 0x16A)
     /* [<][>][^][v][top][bottom][index][help] */
  73 
  74 #if GENERATINGPOWERPC
  75 
  76 static asm UnsignedWide PollRTC(void);
  77 static asm UnsignedWide PollTBR(void);
  78 static Ptr FindFunctionInSharedLib(StringPtr libName, StringPtr funcName);
  79 
  80 static Boolean                  gInited = false;
  81 static Boolean                  gNative = false;
  82 static Boolean                  gUseRTC = false;
  83 static Boolean                  gUseTBR = false;
  84 static double                   gScaleUSec = 1.0 / 1000.0;    /* 1 / ( nsec / usec) */
  85 static double                   gScaleMSec = 1.0 / 1000000.0; /* 1 / ( nsec / msec) */
  86 
  87 /* Functions loaded from DriverServicesLib */
  88 typedef AbsoluteTime    (*UpTimeProcPtr)(void);
  89 typedef Nanoseconds     (*A2NSProcPtr)(AbsoluteTime);
  90 static UpTimeProcPtr    gUpTime = NULL;
  91 static A2NSProcPtr              gA2NS = NULL;
  92 
  93 #endif /* GENERATINGPOWERPC */
  94 
  95 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
  96 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
  97 
  98 void FastInitialize() {
     /* [<][>][^][v][top][bottom][index][help] */
  99         SInt32                  result;
 100 
 101         if (!gInited) {
 102 
 103 #if GENERATINGPOWERPC
 104 
 105                 /* Initialize the feature flags */
 106                 gNative = gUseRTC = gUseTBR = false;
 107 
 108                 /* We use CFM to find and load needed symbols from shared libraries, so
 109                    the application doesn't have to weak-link them, for convenience.   */
 110                 gUpTime = (UpTimeProcPtr) FindFunctionInSharedLib(
 111                                 "\pDriverServicesLib", "\pUpTime");
 112                 if (gUpTime) gA2NS = (A2NSProcPtr) FindFunctionInSharedLib(
 113                                 "\pDriverServicesLib", "\pAbsoluteToNanoseconds");
 114                 if (!gA2NS) gUpTime = nil; /* Pedantic but necessary */
 115 
 116                 if (gUpTime) {
 117                         /* If we loaded UpTime(), then we need to know if the system has
 118                            a native implementation of the Time Manager. If so, then it's
 119                            pointless to calculate a scale factor against the missing VIA */
 120 
 121                         /* gestaltNativeTimeMgr = 4 in some future version of the headers */
 122                         if (!Gestalt(gestaltTimeMgrVersion, &result) &&
 123                                         (result > gestaltExtendedTimeMgr)) 
 124                                 gNative = true;
 125                         }
 126                   else {
 127                         /* If no DriverServicesLib, use Gestalt() to get the processor type. 
 128                            Only NuBus PowerMacs with old System Software won't have DSL, so
 129                            we know it should either be a 601 or 603. */
 130 
 131                         /* Use the processor gestalt to determine which register to use */
 132                         if (!Gestalt(gestaltNativeCPUtype, &result)) {
 133                                 if (result == gestaltCPU601) gUseRTC = true;
 134                                   else if (result > gestaltCPU601) gUseTBR = true;
 135                                 }
 136                         }
 137 
 138                 /* Now calculate a scale factor to keep us accurate. */
 139                 if ((gUpTime && !gNative) || gUseRTC || gUseTBR) {
 140                         UInt64                  tick, usec1, usec2;
 141                         UnsignedWide    wide;
 142 
 143                         /* Wait for the beginning of the very next tick */
 144                         for(tick = MyLMGetTicks() + 1; tick > MyLMGetTicks(); );
 145                         
 146                         /* Poll the selected timer and prepare it (since we have time) */
 147                         wide = (gUpTime) ? (*gA2NS)((*gUpTime)()) : 
 148                                         ((gUseRTC) ? PollRTC() : PollTBR());
 149                         usec1 = (gUseRTC) ? RTCToNano(wide) : WideTo64bit(wide);
 150                         
 151                         /* Wait for the exact 60th tick to roll over */
 152                         while(tick + 60 > MyLMGetTicks());
 153 
 154                         /* Poll the selected timer again and prepare it  */
 155                         wide = (gUpTime) ? (*gA2NS)((*gUpTime)()) : 
 156                                         ((gUseRTC) ? PollRTC() : PollTBR());
 157                         usec2 = (gUseRTC) ? RTCToNano(wide) : WideTo64bit(wide);
 158                         
 159                         /* Calculate a scale value that will give microseconds per second.
 160                            Remember, there are actually 60.15 ticks in a second, not 60.  */
 161                         gScaleUSec = (60.0 * 1000000.0) / ((usec2 - usec1) * 60.15);
 162                         gScaleMSec = gScaleUSec / 1000.0;
 163                         }
 164 
 165 #endif /* GENERATINGPOWERPC */
 166 
 167                 /* We've initialized our globals */
 168                 gInited = true;
 169                 }
 170         }
 171 
 172 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 173 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 174 
 175 UInt64 FastMicroseconds() {
     /* [<][>][^][v][top][bottom][index][help] */
 176         UnsignedWide    wide;
 177         UInt64                  usec;
 178         
 179 #if GENERATINGPOWERPC
 180         /* Initialize globals the first time we are called */
 181         if (!gInited) FastInitialize();
 182         
 183         if (gNative) {
 184                 /* Use DriverServices if it's available -- it's fast and compatible */
 185                 wide = (*gA2NS)((*gUpTime)());
 186                 usec = (double) WideTo64bit(wide) * gScaleUSec + 0.5;
 187                 }
 188           else if (gUpTime) {
 189                 /* Use DriverServices if it's available -- it's fast and compatible */
 190                 wide = (*gA2NS)((*gUpTime)());
 191                 usec = (double) WideTo64bit(wide) * gScaleUSec + 0.5;
 192                 }
 193           else if (gUseTBR) {
 194                 /* On a recent PowerPC, we poll the TBR directly */
 195                 wide = PollTBR();
 196                 usec = (double) WideTo64bit(wide) * gScaleUSec + 0.5;
 197                 }
 198           else if (gUseRTC) {
 199                 /* On a 601, we can poll the RTC instead */
 200                 wide = PollRTC();
 201                 usec = (double) RTCToNano(wide) * gScaleUSec + 0.5;
 202                 }
 203           else 
 204 #endif /* GENERATINGPOWERPC */
 205                 {
 206                 /* If all else fails, suffer the mixed mode overhead */
 207                 Microseconds(&wide);
 208                 usec = WideTo64bit(wide);
 209                 }
 210 
 211         return(usec);
 212         }
 213 
 214 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 215 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 216 
 217 UInt64 FastMilliseconds() {
     /* [<][>][^][v][top][bottom][index][help] */
 218         UnsignedWide    wide;
 219         UInt64                  msec;   
 220         
 221 #if GENERATINGPOWERPC
 222         /* Initialize globals the first time we are called */
 223         if (!gInited) FastInitialize();
 224         
 225         if (gNative) {
 226                 /* Use DriverServices if it's available -- it's fast and compatible */
 227                 wide = (*gA2NS)((*gUpTime)());
 228                 msec = (double) WideTo64bit(wide) * gScaleMSec + 0.5;
 229                 }
 230           else if (gUpTime) {
 231                 /* Use DriverServices if it's available -- it's fast and compatible */
 232                 wide = (*gA2NS)((*gUpTime)());
 233                 msec = (double) WideTo64bit(wide) * gScaleMSec + 0.5;
 234                 }
 235           else if (gUseTBR) {
 236                 /* On a recent PowerPC, we poll the TBR directly */
 237                 wide = PollTBR();
 238                 msec = (double) WideTo64bit(wide) * gScaleMSec + 0.5;
 239                 }
 240           else if (gUseRTC) {
 241                 /* On a 601, we can poll the RTC instead */
 242                 wide = PollRTC();
 243                 msec = (double) RTCToNano(wide) * gScaleMSec + 0.5;
 244                 }
 245           else 
 246 #endif /* GENERATINGPOWERPC */
 247                 {
 248                 /* If all else fails, suffer the mixed mode overhead */
 249                 Microseconds(&wide);
 250                 msec = ((double) WideTo64bit(wide) + 500.0) / 1000.0;
 251                 }
 252 
 253         return(msec);
 254         }
 255 
 256 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 257 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 258 
 259 StringPtr FastMethod() {
     /* [<][>][^][v][top][bottom][index][help] */
 260         StringPtr       method = "\p<Unknown>";
 261 
 262 #if GENERATINGPOWERPC
 263         /* Initialize globals the first time we are called */
 264         if (!gInited) FastInitialize();
 265         
 266         if (gNative) {
 267                 /* The Time Manager and UpTime() are entirely native on this machine */
 268                 method = "\pNative UpTime()";
 269                 }
 270           else if (gUpTime) {
 271                 /* Use DriverServices if it's available -- it's fast and compatible */
 272                 method = "\pUpTime()";
 273                 }
 274           else if (gUseTBR) {
 275                 /* On a recent PowerPC, we poll the TBR directly */
 276                 method = "\pPowerPC TBR";
 277                 }
 278           else if (gUseRTC) {
 279                 /* On a 601, we can poll the RTC instead */
 280                 method = "\pPowerPC RTC";
 281                 }
 282           else 
 283 #endif /* GENERATINGPOWERPC */
 284                 {
 285                 /* If all else fails, suffer the mixed mode overhead */
 286                 method = "\pMicroseconds()";
 287                 }
 288 
 289         return(method);
 290         }
 291 
 292 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 293 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 294 #pragma mark -
 295 
 296 #if GENERATINGPOWERPC
 297 asm static UnsignedWide PollRTC_() {
     /* [<][>][^][v][top][bottom][index][help] */
 298 entry PollRTC /* Avoid CodeWarrior glue */
 299         machine 601
 300 @AGAIN:
 301         mfrtcu  r4 /* RTCU = SPR 4 */
 302         mfrtcl  r5 /* RTCL = SPR 5 */
 303         mfrtcu  r6
 304         cmpw    r4,r6
 305         bne             @AGAIN
 306         stw             r4,0(r3)
 307         stw             r5,4(r3)
 308         blr
 309         }
 310 
 311 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 312 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 313 
 314 asm static UnsignedWide PollTBR_() {
     /* [<][>][^][v][top][bottom][index][help] */
 315 entry PollTBR /* Avoid CodeWarrior glue */
 316         machine 604
 317 @AGAIN:
 318         mftbu   r4 /* TBRU = SPR 268 */
 319         mftb    r5 /* TBRL = SPR 269 */
 320         mftbu   r6
 321         cmpw    r4,r6
 322         bne             @AGAIN
 323         stw             r4,0(r3)
 324         stw             r5,4(r3)
 325         blr
 326         }
 327 
 328 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 329 /* **** **** **** **** **** **** **** **** **** **** **** **** **** **** **** */
 330 
 331 static Ptr FindFunctionInSharedLib(StringPtr libName, StringPtr funcName) {
     /* [<][>][^][v][top][bottom][index][help] */
 332         OSErr                           error = noErr;
 333         Str255                          errorStr;
 334         Ptr                                     func = NULL;
 335         Ptr                                     entry = NULL;
 336         CFragSymbolClass        symClass;
 337         CFragConnectionID       connID;
 338         
 339         /* Find CFM containers for the current archecture -- CFM-PPC or CFM-68K */
 340         if (/* error = */ GetSharedLibrary(libName, kCompiledCFragArch,
 341                         kLoadCFrag, &connID, &entry, errorStr)) return(NULL);
 342         if (/* error = */ FindSymbol(connID, funcName, &func, &symClass))
 343                 return(NULL);
 344         
 345         return(func);
 346         }
 347 #endif /* GENERATINGPOWERPC */

/* [<][>][^][v][top][bottom][index][help] */