src/timer/os2/SDL_systimer.c
changeset 1895 c121d94672cb
parent 1850 d5d3a6fe05a1
child 2049 5f6550e5184f
equal deleted inserted replaced
1894:c69cee13dd76 1895:c121d94672cb
    42 /* The first high-resolution ticks value of the application */
    42 /* The first high-resolution ticks value of the application */
    43 static long long hires_start_ticks;
    43 static long long hires_start_ticks;
    44 /* The number of ticks per second of the high-resolution performance counter */
    44 /* The number of ticks per second of the high-resolution performance counter */
    45 static ULONG hires_ticks_per_second;
    45 static ULONG hires_ticks_per_second;
    46 
    46 
    47 void SDL_StartTicks(void)
    47 void
    48 {
    48 SDL_StartTicks(void)
    49         DosTmrQueryFreq(&hires_ticks_per_second);
    49 {
    50         DosTmrQueryTime((PQWORD)&hires_start_ticks);
    50     DosTmrQueryFreq(&hires_ticks_per_second);
    51 }
    51     DosTmrQueryTime((PQWORD) & hires_start_ticks);
    52 
    52 }
    53 DECLSPEC Uint32 SDLCALL SDL_GetTicks(void)
    53 
    54 {
    54 DECLSPEC Uint32 SDLCALL
    55         long long hires_now;
    55 SDL_GetTicks(void)
    56         ULONG ticks = ticks;
    56 {
    57 
    57     long long hires_now;
    58         DosTmrQueryTime((PQWORD)&hires_now);
    58     ULONG ticks = ticks;
       
    59 
       
    60     DosTmrQueryTime((PQWORD) & hires_now);
    59 /*
    61 /*
    60         hires_now -= hires_start_ticks;
    62         hires_now -= hires_start_ticks;
    61         hires_now *= 1000;
    63         hires_now *= 1000;
    62         hires_now /= hires_ticks_per_second;
    64         hires_now /= hires_ticks_per_second;
    63 */
    65 */
    64         /* inline asm to avoid runtime inclusion */
    66     /* inline asm to avoid runtime inclusion */
    65         _asm {
    67     _asm {
    66            push edx
    68     push edx
    67            push eax
    69             push eax
    68            mov eax, dword ptr hires_now
    70             mov eax, dword ptr hires_now
    69            mov edx, dword ptr hires_now+4
    71             mov edx, dword ptr hires_now + 4
    70            sub eax, dword ptr hires_start_ticks
    72             sub eax, dword ptr hires_start_ticks
    71            sbb edx, dword ptr hires_start_ticks+4
    73             sbb edx, dword ptr hires_start_ticks + 4
    72            mov ebx,1000
    74             mov ebx, 1000
    73            mov ecx,edx
    75             mov ecx, edx
    74            mul ebx
    76             mul ebx
    75            push eax
    77             push eax
    76            push edx
    78             push edx
    77            mov eax,ecx
    79             mov eax, ecx
    78            mul ebx
    80             mul ebx
    79            pop eax
    81             pop eax
    80            add edx,eax
    82             add edx, eax
    81            pop eax
    83             pop eax
    82            mov ebx, dword ptr hires_ticks_per_second
    84             mov ebx, dword ptr hires_ticks_per_second
    83            div ebx
    85             div ebx mov dword ptr ticks, eax pop edx pop eax}
    84            mov dword ptr ticks, eax
    86 
    85            pop edx
    87     return ticks;
    86            pop eax
    88 
       
    89 }
       
    90 
       
    91 /* High resolution sleep, originally made by Ilya Zakharevich */
       
    92 DECLSPEC void SDLCALL
       
    93 SDL_Delay(Uint32 ms)
       
    94 {
       
    95     /* This is similar to DosSleep(), but has 8ms granularity in time-critical
       
    96        threads even on Warp3. */
       
    97     HEV hevEvent1 = 0;          /* Event semaphore handle    */
       
    98     HTIMER htimerEvent1 = 0;    /* Timer handle              */
       
    99     APIRET rc = NO_ERROR;       /* Return code               */
       
   100     int ret = 1;
       
   101     ULONG priority = 0, nesting;        /* Shut down the warnings */
       
   102     PPIB pib;
       
   103     PTIB tib;
       
   104     char *e = NULL;
       
   105     APIRET badrc;
       
   106     int switch_priority = 50;
       
   107 
       
   108     DosCreateEventSem(NULL,     /* Unnamed */
       
   109                       &hevEvent1,       /* Handle of semaphore returned */
       
   110                       DC_SEM_SHARED,    /* Shared needed for DosAsyncTimer */
       
   111                       FALSE);   /* Semaphore is in RESET state  */
       
   112 
       
   113     if (ms >= switch_priority)
       
   114         switch_priority = 0;
       
   115     if (switch_priority) {
       
   116         if (DosGetInfoBlocks(&tib, &pib) != NO_ERROR)
       
   117             switch_priority = 0;
       
   118         else {
       
   119             /* In Warp3, to switch scheduling to 8ms step, one needs to do 
       
   120                DosAsyncTimer() in time-critical thread.  On laters versions,
       
   121                more and more cases of wait-for-something are covered.
       
   122 
       
   123                It turns out that on Warp3fp42 it is the priority at the time
       
   124                of DosAsyncTimer() which matters.  Let's hope that this works
       
   125                with later versions too...  XXXX
       
   126              */
       
   127             priority = (tib->tib_ptib2->tib2_ulpri);
       
   128             if ((priority & 0xFF00) == 0x0300)  /* already time-critical */
       
   129                 switch_priority = 0;
       
   130             /* Make us time-critical.  Just modifying TIB is not enough... */
       
   131             /* tib->tib_ptib2->tib2_ulpri = 0x0300; */
       
   132             /* We do not want to run at high priority if a signal causes us
       
   133                to longjmp() out of this section... */
       
   134             if (DosEnterMustComplete(&nesting))
       
   135                 switch_priority = 0;
       
   136             else
       
   137                 DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
    87         }
   138         }
    88 
   139     }
    89         return ticks;
   140 
    90 
   141     if ((badrc = DosAsyncTimer(ms, (HSEM) hevEvent1,    /* Semaphore to post        */
    91 }
   142                                &htimerEvent1))) /* Timer handler (returned) */
    92 
   143         e = "DosAsyncTimer";
    93 /* High resolution sleep, originally made by Ilya Zakharevich */
   144 
    94 DECLSPEC void SDLCALL SDL_Delay(Uint32 ms)
   145     if (switch_priority && tib->tib_ptib2->tib2_ulpri == 0x0300) {
    95 {
   146         /* Nobody switched priority while we slept...  Ignore errors... */
    96   /* This is similar to DosSleep(), but has 8ms granularity in time-critical
   147         /* tib->tib_ptib2->tib2_ulpri = priority; *//* Get back... */
    97      threads even on Warp3. */
   148         if (!
    98   HEV     hevEvent1     = 0;   /* Event semaphore handle    */
   149             (rc = DosSetPriority(PRTYS_THREAD, (priority >> 8) & 0xFF, 0, 0)))
    99   HTIMER  htimerEvent1  = 0;   /* Timer handle              */
   150             rc = DosSetPriority(PRTYS_THREAD, 0, priority & 0xFF, 0);
   100   APIRET  rc            = NO_ERROR;  /* Return code               */
   151     }
   101   int ret = 1;
   152     if (switch_priority)
   102   ULONG priority = 0, nesting;   /* Shut down the warnings */
   153         rc = DosExitMustComplete(&nesting);     /* Ignore errors */
   103   PPIB pib;
   154 
   104   PTIB tib;
   155     /* The actual blocking call is made with "normal" priority.  This way we
   105   char *e = NULL;
   156        should not bother with DosSleep(0) etc. to compensate for us interrupting
   106   APIRET badrc;
   157        higher-priority threads.  The goal is to prohibit the system spending too
   107   int switch_priority = 50;
   158        much time halt()ing, not to run us "no matter what". */
   108 
   159     if (!e)                     /* Wait for AsyncTimer event */
   109   DosCreateEventSem(NULL,      /* Unnamed */
   160         badrc = DosWaitEventSem(hevEvent1, SEM_INDEFINITE_WAIT);
   110                     &hevEvent1,  /* Handle of semaphore returned */
   161 
   111                     DC_SEM_SHARED, /* Shared needed for DosAsyncTimer */
   162     if (e);                     /* Do nothing */
   112                     FALSE);      /* Semaphore is in RESET state  */
   163     else if (badrc == ERROR_INTERRUPT)
   113 
   164         ret = 0;
   114   if (ms >= switch_priority)
   165     else if (badrc)
   115     switch_priority = 0;
   166         e = "DosWaitEventSem";
   116   if (switch_priority)
   167     if ((rc = DosCloseEventSem(hevEvent1)) && !e) {     /* Get rid of semaphore */
   117   {
   168         e = "DosCloseEventSem";
   118     if (DosGetInfoBlocks(&tib, &pib)!=NO_ERROR)
   169         badrc = rc;
   119       switch_priority = 0;
   170     }
   120     else
   171     if (e) {
   121     {
   172         SDL_SetError("[SDL_Delay] : Had error in %s(), rc is 0x%x\n", e,
   122  /* In Warp3, to switch scheduling to 8ms step, one needs to do 
   173                      badrc);
   123     DosAsyncTimer() in time-critical thread.  On laters versions,
   174     }
   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 }
   175 }
   180 
   176 
   181 /* Data to handle a single periodic alarm */
   177 /* Data to handle a single periodic alarm */
   182 static int timer_alive = 0;
   178 static int timer_alive = 0;
   183 static SDL_Thread *timer = NULL;
   179 static SDL_Thread *timer = NULL;
   184 
   180 
   185 static int SDLCALL RunTimer(void *unused)
   181 static int
   186 {
   182 RunTimer(void *unused)
   187         DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
   183 {
   188         while ( timer_alive ) {
   184     DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
   189                 if ( SDL_timer_running ) {
   185     while (timer_alive) {
   190                         SDL_ThreadedTimerCheck();
   186         if (SDL_timer_running) {
   191                 }
   187             SDL_ThreadedTimerCheck();
   192                 SDL_Delay(10);
       
   193         }
   188         }
   194         return(0);
   189         SDL_Delay(10);
       
   190     }
       
   191     return (0);
   195 }
   192 }
   196 
   193 
   197 /* This is only called if the event thread is not running */
   194 /* This is only called if the event thread is not running */
   198 int SDL_SYS_TimerInit(void)
   195 int
   199 {
   196 SDL_SYS_TimerInit(void)
   200         timer_alive = 1;
   197 {
   201         timer = SDL_CreateThread(RunTimer, NULL);
   198     timer_alive = 1;
   202         if ( timer == NULL )
   199     timer = SDL_CreateThread(RunTimer, NULL);
   203                 return(-1);
   200     if (timer == NULL)
   204         return(SDL_SetTimerThreaded(1));
   201         return (-1);
   205 }
   202     return (SDL_SetTimerThreaded(1));
   206 
   203 }
   207 void SDL_SYS_TimerQuit(void)
   204 
   208 {
   205 void
   209         timer_alive = 0;
   206 SDL_SYS_TimerQuit(void)
   210         if ( timer ) {
   207 {
   211                 SDL_WaitThread(timer, NULL);
   208     timer_alive = 0;
   212                 timer = NULL;
   209     if (timer) {
   213         }
   210         SDL_WaitThread(timer, NULL);
   214 }
   211         timer = NULL;
   215 
   212     }
   216 int SDL_SYS_StartTimer(void)
   213 }
   217 {
   214 
   218         SDL_SetError("Internal logic error: OS/2 uses threaded timer");
   215 int
   219         return(-1);
   216 SDL_SYS_StartTimer(void)
   220 }
   217 {
   221 
   218     SDL_SetError("Internal logic error: OS/2 uses threaded timer");
   222 void SDL_SYS_StopTimer(void)
   219     return (-1);
   223 {
   220 }
   224         return;
   221 
       
   222 void
       
   223 SDL_SYS_StopTimer(void)
       
   224 {
       
   225     return;
   225 }
   226 }
   226 
   227 
   227 #endif /* SDL_TIMER_OS2 */
   228 #endif /* SDL_TIMER_OS2 */
       
   229 /* vi: set ts=4 sw=4 expandtab: */