Skip to content
This repository has been archived by the owner on Feb 11, 2021. It is now read-only.

Latest commit

 

History

History
348 lines (291 loc) · 13.2 KB

FastTimes.c

File metadata and controls

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