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
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.