1190
|
1 /*
|
|
2 SDL - Simple DirectMedia Layer
|
|
3 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
|
|
4
|
|
5 This library is free software; you can redistribute it and/or
|
|
6 modify it under the terms of the GNU Library General Public
|
|
7 License as published by the Free Software Foundation; either
|
|
8 version 2 of the License, or (at your option) any later version.
|
|
9
|
|
10 This library is distributed in the hope that it will be useful,
|
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13 Library General Public License for more details.
|
|
14
|
|
15 You should have received a copy of the GNU Library General Public
|
|
16 License along with this library; if not, write to the Free
|
|
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
18
|
|
19 Sam Lantinga
|
|
20 slouken@libsdl.org
|
|
21 */
|
|
22
|
|
23 #ifdef SAVE_RCSID
|
|
24 static char rcsid =
|
|
25 "@(#) $Id$";
|
|
26 #endif
|
|
27
|
|
28 #define INCL_DOSMISC
|
|
29 #define INCL_DOSERRORS
|
|
30 #define INCL_DOSSEMAPHORES
|
|
31 #define INCL_DOSDATETIME
|
|
32 #define INCL_DOSPROCESS
|
|
33 #define INCL_DOSPROFILE
|
|
34 #define INCL_DOSEXCEPTIONS
|
|
35 #include <os2.h>
|
|
36
|
|
37 #include "SDL_thread.h"
|
|
38 #include "SDL_timer.h"
|
|
39 #include "SDL_timer_c.h"
|
|
40 #include "SDL_error.h"
|
|
41
|
|
42
|
|
43 #define TIME_WRAP_VALUE (~(DWORD)0)
|
|
44
|
|
45 /* The first high-resolution ticks value of the application */
|
|
46 static long long hires_start_ticks;
|
|
47 /* The number of ticks per second of the high-resolution performance counter */
|
|
48 static ULONG hires_ticks_per_second;
|
|
49
|
|
50 void SDL_StartTicks(void)
|
|
51 {
|
|
52 DosTmrQueryFreq(&hires_ticks_per_second);
|
|
53 DosTmrQueryTime((PQWORD)&hires_start_ticks);
|
|
54 }
|
|
55
|
|
56 DECLSPEC Uint32 SDLCALL SDL_GetTicks(void)
|
|
57 {
|
|
58 long long hires_now;
|
|
59 ULONG ticks = ticks;
|
|
60
|
|
61 DosTmrQueryTime((PQWORD)&hires_now);
|
|
62 /*
|
|
63 hires_now -= hires_start_ticks;
|
|
64 hires_now *= 1000;
|
|
65 hires_now /= hires_ticks_per_second;
|
|
66 */
|
|
67 /* inline asm to avoid runtime inclusion */
|
|
68 _asm {
|
|
69 push edx
|
|
70 push eax
|
|
71 mov eax, dword ptr hires_now
|
|
72 mov edx, dword ptr hires_now+4
|
|
73 sub eax, dword ptr hires_start_ticks
|
|
74 sbb edx, dword ptr hires_start_ticks+4
|
|
75 mov ebx,1000
|
|
76 mov ecx,edx
|
|
77 mul ebx
|
|
78 push eax
|
|
79 push edx
|
|
80 mov eax,ecx
|
|
81 mul ebx
|
|
82 pop eax
|
|
83 add edx,eax
|
|
84 pop eax
|
|
85 mov ebx, dword ptr hires_ticks_per_second
|
|
86 div ebx
|
|
87 mov dword ptr ticks, eax
|
|
88 pop edx
|
|
89 pop eax
|
|
90 }
|
|
91
|
|
92 return ticks;
|
|
93
|
|
94 }
|
|
95
|
|
96 /* High resolution sleep, originally made by Ilya Zakharevich */
|
|
97 DECLSPEC void SDLCALL SDL_Delay(Uint32 ms)
|
|
98 {
|
|
99 /* This is similar to DosSleep(), but has 8ms granularity in time-critical
|
|
100 threads even on Warp3. */
|
|
101 HEV hevEvent1 = 0; /* Event semaphore handle */
|
|
102 HTIMER htimerEvent1 = 0; /* Timer handle */
|
|
103 APIRET rc = NO_ERROR; /* Return code */
|
|
104 int ret = 1;
|
|
105 ULONG priority = 0, nesting; /* Shut down the warnings */
|
|
106 PPIB pib;
|
|
107 PTIB tib;
|
|
108 char *e = NULL;
|
|
109 APIRET badrc;
|
|
110 int switch_priority = 50;
|
|
111
|
|
112 DosCreateEventSem(NULL, /* Unnamed */
|
|
113 &hevEvent1, /* Handle of semaphore returned */
|
|
114 DC_SEM_SHARED, /* Shared needed for DosAsyncTimer */
|
|
115 FALSE); /* Semaphore is in RESET state */
|
|
116
|
|
117 if (ms >= switch_priority)
|
|
118 switch_priority = 0;
|
|
119 if (switch_priority)
|
|
120 {
|
|
121 if (DosGetInfoBlocks(&tib, &pib)!=NO_ERROR)
|
|
122 switch_priority = 0;
|
|
123 else
|
|
124 {
|
|
125 /* In Warp3, to switch scheduling to 8ms step, one needs to do
|
|
126 DosAsyncTimer() in time-critical thread. On laters versions,
|
|
127 more and more cases of wait-for-something are covered.
|
|
128
|
|
129 It turns out that on Warp3fp42 it is the priority at the time
|
|
130 of DosAsyncTimer() which matters. Let's hope that this works
|
|
131 with later versions too... XXXX
|
|
132 */
|
|
133 priority = (tib->tib_ptib2->tib2_ulpri);
|
|
134 if ((priority & 0xFF00) == 0x0300) /* already time-critical */
|
|
135 switch_priority = 0;
|
|
136 /* Make us time-critical. Just modifying TIB is not enough... */
|
|
137 /* tib->tib_ptib2->tib2_ulpri = 0x0300;*/
|
|
138 /* We do not want to run at high priority if a signal causes us
|
|
139 to longjmp() out of this section... */
|
|
140 if (DosEnterMustComplete(&nesting))
|
|
141 switch_priority = 0;
|
|
142 else
|
|
143 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
|
|
144 }
|
|
145 }
|
|
146
|
|
147 if ((badrc = DosAsyncTimer(ms,
|
|
148 (HSEM) hevEvent1, /* Semaphore to post */
|
|
149 &htimerEvent1))) /* Timer handler (returned) */
|
|
150 e = "DosAsyncTimer";
|
|
151
|
|
152 if (switch_priority && tib->tib_ptib2->tib2_ulpri == 0x0300)
|
|
153 {
|
|
154 /* Nobody switched priority while we slept... Ignore errors... */
|
|
155 /* tib->tib_ptib2->tib2_ulpri = priority; */ /* Get back... */
|
|
156 if (!(rc = DosSetPriority(PRTYS_THREAD, (priority>>8) & 0xFF, 0, 0)))
|
|
157 rc = DosSetPriority(PRTYS_THREAD, 0, priority & 0xFF, 0);
|
|
158 }
|
|
159 if (switch_priority)
|
|
160 rc = DosExitMustComplete(&nesting); /* Ignore errors */
|
|
161
|
|
162 /* The actual blocking call is made with "normal" priority. This way we
|
|
163 should not bother with DosSleep(0) etc. to compensate for us interrupting
|
|
164 higher-priority threads. The goal is to prohibit the system spending too
|
|
165 much time halt()ing, not to run us "no matter what". */
|
|
166 if (!e) /* Wait for AsyncTimer event */
|
|
167 badrc = DosWaitEventSem(hevEvent1, SEM_INDEFINITE_WAIT);
|
|
168
|
|
169 if (e) ; /* Do nothing */
|
|
170 else if (badrc == ERROR_INTERRUPT)
|
|
171 ret = 0;
|
|
172 else if (badrc)
|
|
173 e = "DosWaitEventSem";
|
|
174 if ((rc = DosCloseEventSem(hevEvent1)) && !e) { /* Get rid of semaphore */
|
|
175 e = "DosCloseEventSem";
|
|
176 badrc = rc;
|
|
177 }
|
|
178 if (e)
|
|
179 {
|
|
180 SDL_SetError("[SDL_Delay] : Had error in %s(), rc is 0x%x\n", e, badrc);
|
|
181 }
|
|
182 }
|
|
183
|
|
184 /* Data to handle a single periodic alarm */
|
|
185 static int timer_alive = 0;
|
|
186 static SDL_Thread *timer = NULL;
|
|
187
|
|
188 static int RunTimer(void *unused)
|
|
189 {
|
|
190 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
|
|
191 while ( timer_alive ) {
|
|
192 if ( SDL_timer_running ) {
|
|
193 SDL_ThreadedTimerCheck();
|
|
194 }
|
|
195 SDL_Delay(10);
|
|
196 }
|
|
197 return(0);
|
|
198 }
|
|
199
|
|
200 /* This is only called if the event thread is not running */
|
|
201 int SDL_SYS_TimerInit(void)
|
|
202 {
|
|
203 timer_alive = 1;
|
|
204 timer = SDL_CreateThread(RunTimer, NULL);
|
|
205 if ( timer == NULL )
|
|
206 return(-1);
|
|
207 return(SDL_SetTimerThreaded(1));
|
|
208 }
|
|
209
|
|
210 void SDL_SYS_TimerQuit(void)
|
|
211 {
|
|
212 timer_alive = 0;
|
|
213 if ( timer ) {
|
|
214 SDL_WaitThread(timer, NULL);
|
|
215 timer = NULL;
|
|
216 }
|
|
217 }
|
|
218
|
|
219 int SDL_SYS_StartTimer(void)
|
|
220 {
|
|
221 SDL_SetError("Internal logic error: OS/2 uses threaded timer");
|
|
222 return(-1);
|
|
223 }
|
|
224
|
|
225 void SDL_SYS_StopTimer(void)
|
|
226 {
|
|
227 return;
|
|
228 }
|
|
229
|
|
230
|