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