• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-2012. 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 
FastInitialize()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 
FastMicroseconds()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 
FastMilliseconds()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 
FastMethod()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
PollRTC_()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 
PollTBR_()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 
FindFunctionInSharedLib(StringPtr libName,StringPtr funcName)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 */
353