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