src/timer/os2/SDL_systimer.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 29 May 2006 04:04:35 +0000
branchSDL-1.3
changeset 1668 4da1ee79c9af
parent 1662 782fd950bd46
permissions -rw-r--r--
more tweaking indent options

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2006 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

#ifdef SDL_TIMER_OS2

#define INCL_DOSMISC
#define INCL_DOSERRORS
#define INCL_DOSSEMAPHORES
#define INCL_DOSDATETIME
#define INCL_DOSPROCESS
#define INCL_DOSPROFILE
#define INCL_DOSEXCEPTIONS
#include <os2.h>

#include "SDL_thread.h"
#include "SDL_timer.h"
#include "../SDL_timer_c.h"


#define TIME_WRAP_VALUE (~(DWORD)0)

/* The first high-resolution ticks value of the application */
static long long hires_start_ticks;
/* The number of ticks per second of the high-resolution performance counter */
static ULONG hires_ticks_per_second;

void
SDL_StartTicks(void)
{
    DosTmrQueryFreq(&hires_ticks_per_second);
    DosTmrQueryTime((PQWORD) & hires_start_ticks);
}

DECLSPEC Uint32 SDLCALL
SDL_GetTicks(void)
{
    long long hires_now;
    ULONG ticks = ticks;

    DosTmrQueryTime((PQWORD) & hires_now);
/*
        hires_now -= hires_start_ticks;
        hires_now *= 1000;
        hires_now /= hires_ticks_per_second;
*/
    /* inline asm to avoid runtime inclusion */
    _asm {
    push edx
            push eax
            mov eax, dword ptr hires_now
            mov edx, dword ptr hires_now + 4
            sub eax, dword ptr hires_start_ticks
            sbb edx, dword ptr hires_start_ticks + 4
            mov ebx, 1000
            mov ecx, edx
            mul ebx
            push eax
            push edx
            mov eax, ecx
            mul ebx
            pop eax
            add edx, eax
            pop eax
            mov ebx, dword ptr hires_ticks_per_second
            div ebx mov dword ptr ticks, eax pop edx pop eax}

    return ticks;

}

/* High resolution sleep, originally made by Ilya Zakharevich */
DECLSPEC void SDLCALL
SDL_Delay(Uint32 ms)
{
    /* This is similar to DosSleep(), but has 8ms granularity in time-critical
       threads even on Warp3. */
    HEV hevEvent1 = 0;          /* Event semaphore handle    */
    HTIMER htimerEvent1 = 0;    /* Timer handle              */
    APIRET rc = NO_ERROR;       /* Return code               */
    int ret = 1;
    ULONG priority = 0, nesting;        /* Shut down the warnings */
    PPIB pib;
    PTIB tib;
    char *e = NULL;
    APIRET badrc;
    int switch_priority = 50;

    DosCreateEventSem(NULL,     /* Unnamed */
                      &hevEvent1,       /* Handle of semaphore returned */
                      DC_SEM_SHARED,    /* Shared needed for DosAsyncTimer */
                      FALSE);   /* Semaphore is in RESET state  */

    if (ms >= switch_priority)
        switch_priority = 0;
    if (switch_priority) {
        if (DosGetInfoBlocks(&tib, &pib) != NO_ERROR)
            switch_priority = 0;
        else {
            /* In Warp3, to switch scheduling to 8ms step, one needs to do 
               DosAsyncTimer() in time-critical thread.  On laters versions,
               more and more cases of wait-for-something are covered.

               It turns out that on Warp3fp42 it is the priority at the time
               of DosAsyncTimer() which matters.  Let's hope that this works
               with later versions too...  XXXX
             */
            priority = (tib->tib_ptib2->tib2_ulpri);
            if ((priority & 0xFF00) == 0x0300)  /* already time-critical */
                switch_priority = 0;
            /* Make us time-critical.  Just modifying TIB is not enough... */
            /* tib->tib_ptib2->tib2_ulpri = 0x0300; */
            /* We do not want to run at high priority if a signal causes us
               to longjmp() out of this section... */
            if (DosEnterMustComplete(&nesting))
                switch_priority = 0;
            else
                DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
        }
    }

    if ((badrc = DosAsyncTimer(ms, (HSEM) hevEvent1,    /* Semaphore to post        */
                               &htimerEvent1))) /* Timer handler (returned) */
        e = "DosAsyncTimer";

    if (switch_priority && tib->tib_ptib2->tib2_ulpri == 0x0300) {
        /* Nobody switched priority while we slept...  Ignore errors... */
        /* tib->tib_ptib2->tib2_ulpri = priority; *//* Get back... */
        if (!
            (rc = DosSetPriority(PRTYS_THREAD, (priority >> 8) & 0xFF, 0, 0)))
            rc = DosSetPriority(PRTYS_THREAD, 0, priority & 0xFF, 0);
    }
    if (switch_priority)
        rc = DosExitMustComplete(&nesting);     /* Ignore errors */

    /* The actual blocking call is made with "normal" priority.  This way we
       should not bother with DosSleep(0) etc. to compensate for us interrupting
       higher-priority threads.  The goal is to prohibit the system spending too
       much time halt()ing, not to run us "no matter what". */
    if (!e)                     /* Wait for AsyncTimer event */
        badrc = DosWaitEventSem(hevEvent1, SEM_INDEFINITE_WAIT);

    if (e);                     /* Do nothing */
    else if (badrc == ERROR_INTERRUPT)
        ret = 0;
    else if (badrc)
        e = "DosWaitEventSem";
    if ((rc = DosCloseEventSem(hevEvent1)) && !e) {     /* Get rid of semaphore */
        e = "DosCloseEventSem";
        badrc = rc;
    }
    if (e) {
        SDL_SetError("[SDL_Delay] : Had error in %s(), rc is 0x%x\n", e,
                     badrc);
    }
}

/* Data to handle a single periodic alarm */
static int timer_alive = 0;
static SDL_Thread *timer = NULL;

static int
RunTimer(void *unused)
{
    DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0);
    while (timer_alive) {
        if (SDL_timer_running) {
            SDL_ThreadedTimerCheck();
        }
        SDL_Delay(10);
    }
    return (0);
}

/* This is only called if the event thread is not running */
int
SDL_SYS_TimerInit(void)
{
    timer_alive = 1;
    timer = SDL_CreateThread(RunTimer, NULL);
    if (timer == NULL)
        return (-1);
    return (SDL_SetTimerThreaded(1));
}

void
SDL_SYS_TimerQuit(void)
{
    timer_alive = 0;
    if (timer) {
        SDL_WaitThread(timer, NULL);
        timer = NULL;
    }
}

int
SDL_SYS_StartTimer(void)
{
    SDL_SetError("Internal logic error: OS/2 uses threaded timer");
    return (-1);
}

void
SDL_SYS_StopTimer(void)
{
    return;
}

#endif /* SDL_TIMER_OS2 */
/* vi: set ts=4 sw=4 expandtab: */