Mercurial > sdl-ios-xcode
comparison src/timer/macos/FastTimes.c @ 0:74212992fb08
Initial revision
author | Sam Lantinga <slouken@lokigames.com> |
---|---|
date | Thu, 26 Apr 2001 16:45:43 +0000 |
parents | |
children | f12379c41042 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:74212992fb08 |
---|---|
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 */ |