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
+ − 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)
+ − 66 #define WideTo64bit(w) (*(UInt64 *) &(w))
+ − 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)
+ − 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() {
+ − 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() {
+ − 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() {
+ − 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() {
+ − 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_() {
+ − 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_() {
+ − 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) {
+ − 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 */