~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Frontier Kernel
Frontier/Common/source/FastTimes.c

Version: ~ [ 10.0 ] ~

** Warning: Cannot open xref database.

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.