symbian-qemu-0.9.1-12/libsdl-trunk/src/timer/macos/FastTimes.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     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 #ifdef TARGET_CPU_PPC
       
    23 #undef GENERATINGPOWERPC /* stop whining */
       
    24 #define GENERATINGPOWERPC TARGET_CPU_PPC
       
    25 #endif
       
    26 
       
    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,
       
    74    so for speed we always read lowmem directly. This is a Mac OS X no-no, but 
       
    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 */