/*
            __
           / /_______________  ____  ___
          / __/ ___/ ___/ __ \/ __ \/ _ \
         / /_(__  ) /__/ /_/ / /_/ /  __/
         \__/____/\___/\____/ .___/\___/
                           /_/
    
    timing functions

    By Michael Stevens

    See license.html for copyright information
*/

#include "../include/tscope.h"
#include "../include/tscope/internal.h"


//------------------------------------------------------------------------
// reads timing parameters from a configuration file
void ts_timercfg(char *file)
{
    // helper variables for reading the config file
    int count;
    char **data;
    char msg[100];

    _setcfg(file);

    // priority
    data = get_config_argv("timing", "ts_priority", &count);
    if (count == 1) {
        sprintf(msg, "ts_priority: %s\n", data[0]);
        int val = _readcfg(_npriority, _spriority, data[0]);
        if (val != -1)
            ts_priority(_vpriority[val]);
        else
            ts_fatal("ts_timercfg: ts_priority entry not valid.");
    } else
        sprintf(msg, "ts_priority not set\n");
    if (_debug_flag > DEBUG0)
        fprintf(stderr, "%s", msg);

    // waiting mode
    data = get_config_argv("timing", "ts_waitmode", &count);
    if (count == 1) {
        sprintf(msg, "ts_waitmode: %s\n", data[0]);
        int val = _readcfg(_nwaitmode, _swaitmode, data[0]);
        if (val != -1)
            ts_waitmode(_vwaitmode[val]);
        else
            ts_fatal("ts_timercfg: ts_waitmode entry not valid.");
    } else
        sprintf(msg, "ts_waitmode not set\n");
    if (_debug_flag > DEBUG0)
        fprintf(stderr, "%s", msg);

    // vsync mode
    data = get_config_argv("timing", "ts_vsyncmode", &count);
    if (count == 1) {
        sprintf(msg, "ts_vsyncmode: %s\n", data[0]);
        int val = _readcfg(_nvsyncmode, _svsyncmode, data[0]);
        if (val != -1)
            ts_vsyncmode(_vvsyncmode[val]);
        else
            ts_fatal("ts_timercfg: ts_vsyncmode entry not valid.");
    } else
        sprintf(msg, "ts_vsyncmode not set\n");
    if (_debug_flag > DEBUG0)
        fprintf(stderr, "%s", msg);

    // vsync limit
    data = get_config_argv("timing", "ts_vsynclimit", &count);
    if (count == 1) {
        int tmp = atoi(data[0]);
        sprintf(msg, "ts_vsynclimit: %d\n", tmp);
        ts_vsynclimit(tmp);
    } else
        sprintf(msg, "ts_vsynclimit not set\n");
    if (_debug_flag > DEBUG0)
        fprintf(stderr, "%s", msg);

    // serial port number
    data = get_config_argv("timing", "ts_setserialport", &count);
    if (count == 1) {
        int tmp = atoi(data[0]);
        sprintf(msg, "ts_setserialport: %d\n", tmp);
        ts_setserialport(tmp);
    } else
        sprintf(msg, "ts_setserialport not set\n");
    if (_debug_flag > DEBUG0)
        fprintf(stderr, "%s", msg);

    // response keys
    data = get_config_argv("timing", "ts_defkey", &count);
    if (count) {
        ts_clrkeys();           // remove old key definition
        int i;
        for (i = 0; i < count; i++) {
            sprintf(msg, "ts_defkey: %s\n", data[i]);
            if (_debug_flag > DEBUG0)
                fprintf(stderr, "%s", msg);
            int val = _readcfg(_ndefkey, _sdefkey, data[i]);
            if (val != -1)
                ts_defkey(_vdefkey[val]);
            else
                ts_fatal("ts_timercfg: ts_defkey entry not valid.");
        }
    } else {
        sprintf(msg, "ts_defkey not set\n");
        if (_debug_flag > DEBUG0)
            fprintf(stderr, "%s", msg);
    }
}


//------------------------------------------------------------------------
// adjust program priority  
int ts_priority(int prio)
{
#ifdef ALLEGRO_WINDOWS
    if (!_timer_flag)
        _timer_init();
    HANDLE h;
    int oldval;
    oldval = _priority;
    h = GetCurrentProcess();
    if (prio == REALTIME) {
        if (!SetPriorityClass(h, REALTIME_PRIORITY_CLASS))
            ts_fatal("ts_priority: error switching to realtime priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with realtime priority.\n\n");
            _priority = REALTIME;
        }
    } else if (prio == HIGH) {
        if (!SetPriorityClass(h, HIGH_PRIORITY_CLASS))
            ts_fatal("ts_priority: error switching to high priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with high priority.\n\n");
            _priority = HIGH;
        }
    } else if (prio == ABOVE_NORMAL) {
        if (!SetPriorityClass(h, ABOVE_NORMAL_PRIORITY_CLASS))
            ts_fatal
                ("ts_priority: error switching to above normal priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with above normal priority.\n\n");
            _priority = ABOVE_NORMAL;
        }
    } else if (prio == NORMAL) {
        if (!SetPriorityClass(h, NORMAL_PRIORITY_CLASS))
            ts_fatal("ts_priority: error switching to normal priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with normal priority.\n\n");
            _priority = NORMAL;
        }
    } else if (prio == BELOW_NORMAL) {
        if (!SetPriorityClass(h, BELOW_NORMAL_PRIORITY_CLASS))
            ts_fatal
                ("ts_priority: error switching to below normal priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with below normal priority.\n\n");
            _priority = BELOW_NORMAL;
        }
    } else if (prio == IDLE) {
        if (!SetPriorityClass(h, IDLE_PRIORITY_CLASS))
            ts_fatal("ts_priority: error switching to idle priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with idle priority.\n\n");
            _priority = IDLE;
        }
    } else
        ts_fatal("ts_priority: unknown priority requested.");
    return oldval;

#endif

#ifdef ALLEGRO_LINUX
    if (!_timer_flag)
        _timer_init();
    int oldval;
    oldval = _priority;

    if (geteuid() != 0 && prio < NORMAL) {
        if (_debug_flag > DEBUG0) {
            fprintf(stderr, "\n\tUser has no root privileges.\n");
            fprintf(stderr,
                    "\tProgram priority can not be increased.\n\n");
        }
        prio = NORMAL;
    }

    errno = 0;
    if (prio == REALTIME) {
        if (setpriority(PRIO_PGRP, 0, REALTIME) != 0)
            ts_fatal("ts_priority: error switching to realtime priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with realtime priority.\n\n");
            _priority = REALTIME;
        }
    } else if (prio == HIGH) {
        if (setpriority(PRIO_PGRP, 0, HIGH) != 0)
            ts_fatal("ts_priority: error switching to high priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with high priority.\n\n");
            _priority = HIGH;
        }
    } else if (prio == ABOVE_NORMAL) {
        if (setpriority(PRIO_PGRP, 0, ABOVE_NORMAL) != 0)
            ts_fatal
                ("ts_priority: error switching to above normal priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with above normal priority.\n\n");
            _priority = ABOVE_NORMAL;
        }
    } else if (prio == NORMAL) {
        if (setpriority(PRIO_PGRP, 0, NORMAL) != 0)
            ts_fatal("ts_priority: error switching to normal priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with normal priority.\n\n");
            _priority = NORMAL;
        }
    } else if (prio == BELOW_NORMAL) {
        if (setpriority(PRIO_PGRP, 0, BELOW_NORMAL) != 0)
            ts_fatal
                ("ts_priority: error switching to below normal priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with below normal priority.\n\n");
            _priority = BELOW_NORMAL;
        }
    } else if (prio == IDLE) {
        if (setpriority(PRIO_PROCESS, 0, IDLE) != 0)
            ts_fatal("ts_priority: error switching to idle priority.");
        else {
            if (_debug_flag > DEBUG1)
                fprintf(stderr, "Running with idle priority.\n\n");
            _priority = NORMAL;
        }
    } else
        ts_fatal("ts_priority: unknown priority requested.");
    return oldval;
#endif

#ifdef ALLEGRO_MACOSX
    if (!_timer_flag)
        _timer_init();
    int oldval;
    oldval = _priority;

    if (prio >= IDLE && prio <= REALTIME) {
        if (_debug_flag > DEBUG0) {
            fprintf(stderr,
                    "\n\tProgram priority cannot be changed on macosx.\n");
            fprintf(stderr, "\tRunning with normal priority.\n\n");

        }
    } else
        ts_fatal("ts_priority: unknown priority requested.");
    return oldval;
#endif
}

//------------------------------------------------------------------------
// basic timing functions

// get the cpu clock time in tics
// does not check whether the timer is started
__int64 ts_time()
{
#ifdef ALLEGRO_WINDOWS
    QueryPerformanceCounter(&_clocktime);
    return _clocktime.QuadPart;
#else
    gettimeofday(&_clocktime, NULL);
    return (_clocktime.tv_sec * 1000000 + _clocktime.tv_usec);
#endif
}

// wait a few tics
void ts_wait(__int64 time)
{
    if (!_timer_flag)
        _timer_init();

    __int64 time1, time2;
    time1 = ts_time();
    do {
        if (_wait_flag >= SLEEP_WAIT)
            _rtc_nap();

        ts_respstatus();
        time2 = ts_time();
    } while (time2 - time1 < time);
}

int ts_waitmode(int mode)
{
    int oldval = _wait_flag;

#ifdef ALLEGRO_LINUX
    if (geteuid() != 0 && mode > BUSYLOOP) {
        if (_debug_flag > DEBUG0) {
            fprintf(stderr, "\n\tUser has no root privileges.\n");
            fprintf(stderr, "\tWaiting mode can not be adjusted.\n\n");
        }
        mode = BUSYLOOP;
    }
#endif

#ifdef ALLEGRO_MACOSX
    if (_debug_flag > DEBUG0) {
        fprintf(stderr,
                "\n\tOn mac the waiting mode can not be adjusted.\n\n");
    }
    mode = BUSYLOOP;
#endif

    if (mode == BUSYLOOP || mode == SLEEP_WAIT || mode == SLEEP_RESP
        || mode == SLEEP_VSYNC)
        _wait_flag = mode;
    else
        ts_fatal("ts_waitmode: unknown waiting mode requested.");
    if (_wait_flag > BUSYLOOP)
        _rtc_init();
    else
        _rtc_exit();

    return oldval;
}


//------------------------------------------------------------------------
// response definition functions

// define a response button
int ts_defkey(int key)
{
    if (!_timer_flag)
        _timer_init();

    // check whether button is already active
    if (_key_active[key])
        ts_fatal("ts_defkey: response button already defined.");

    // gameport and parallel port: check for buttons that are defined 
    // both as normal and as inversed button
    int doubledef = 0;

    if (key >= G1 && key <= G4 && _key_defined[key + 4])
        doubledef = 1;
    if (key >= P1 && key <= P5 && _key_defined[key + 5])
        doubledef = 1;
    if (key >= PP1 && key <= PP5 && _key_defined[key + 5])
        doubledef = 1;
    if (key >= PPP1 && key <= PP5 && _key_defined[key + 5])
        doubledef = 1;

    if (key >= IG1 && key <= IG4 && _key_defined[key - 4])
        doubledef = 1;
    if (key >= IP1 && key <= IP5 && _key_defined[key - 5])
        doubledef = 1;
    if (key >= IPP1 && key <= IPP5 && _key_defined[key - 5])
        doubledef = 1;
    if (key >= IPPP1 && key <= IPPP5 && _key_defined[key - 5])
        doubledef = 1;

    if (doubledef)
        ts_fatal
            ("ts_defkey: response button can't be both normal and inversed.");

    // load drivers if necessary
    // parport 1
    if (key >= P1 && key <= IP5) {
        if (!_parport1_flag)
            _parport_init(1);
        if (!_parport1_resp_flag)
            _parport1_resp_flag = 1;
    }
    // parport 2
    else if (key >= PP1 && key <= IPP5) {
        if (!_parport2_flag)
            _parport_init(2);
        if (!_parport2_resp_flag)
            _parport2_resp_flag = 1;
    }
    // parport 3
    else if (key >= PPP1 && key <= IPPP5) {
        if (!_parport3_flag)
            _parport_init(3);
        if (!_parport3_resp_flag)
            _parport3_resp_flag = 1;
    }
    // gameport 
    else if (key >= G1 && key <= IG4) {
        if (!_gameport_flag)
            _gameport_init();
        if (!_gameport_resp_flag)
            _gameport_resp_flag = 1;
    }
    // serial port
    else if (key >= S1 && key <= S7) {
        if (!_serialport_flag)
            _serialport_init();
        if (!_serialport_resp_flag)
            _serialport_resp_flag = 1;
    }
    // joystick 
    else if (key >= J1 && key <= J10) {
        if (!_joystick_flag)
            _joystick_init();
        if (!_joystick_resp_flag)
            _joystick_resp_flag = 1;
    }
    // mouse
    else if (key >= M1 && key <= M3) {
        if (!_mouse_flag)
            _mouse_init();
        if (!_mouse_resp_flag)
            _mouse_resp_flag = 1;
    }
    // keyboard
    else if (key > 0 && key < KEY_MAX) {
        if (!_keyboard_flag)
            _keyboard_init();
        if (!_keyboard_resp_flag)
            _keyboard_resp_flag = 1;
    } else
        ts_fatal("ts_defkey: unknown response button requested.");

    // define the key
    _n_active_keys++;
    _key_active[key] = _n_active_keys;
    _key_defined[key] = _n_active_keys;
    if (_debug_flag > DEBUG3)
        fprintf(stderr, "Button %d has response value %d.\n", key,
                _n_active_keys);
    return _n_active_keys;
}

// hide a response button 
int ts_hidekey(int key)
{
    int i, nkeys = 0;
    if (!_timer_flag)
        ts_fatal("ts_hidekey: timer is not running yet.");

    if (key == 0) {
        for (i = 0; i < NRESPKEYS; i++)
            _key_active[i] = _key_defined[i];
        if (_debug_flag > DEBUG3)
            fprintf(stderr, "All defined responses are active.\n");
        return 0;
    } else {
        for (i = 0; i < NRESPKEYS; i++)
            if (_key_active[i] == key) {
                _key_active[i] = 0;
                if (_debug_flag > DEBUG3)
                    fprintf(stderr, "Button %d with value %d hidden.\n",
                            i, key);
                break;
            }
    }
    if (i == NRESPKEYS)
        ts_fatal("ts_hidekey: undefined button requested.");
    for (i = 0; i < NRESPKEYS; i++)
        if (_key_active[i])
            nkeys++;
    if (nkeys == 0)
        ts_fatal("ts_hidekey: no response buttons left.");
    return i;
}

// reset the response definitions 
void ts_clrkeys()
{
    if (!_timer_flag)
        ts_fatal("ts_clrkeys: timer is not running yet.");
    int i;
    _gameport_resp_flag = _parport1_resp_flag = _parport2_resp_flag =
        _parport3_resp_flag = _keyboard_resp_flag = _mouse_resp_flag =
        _joystick_resp_flag = 0;
    for (i = 0; i < NRESPKEYS; i++) {
        _key_active[i] = 0;
        _key_defined[i] = 0;
    }
    _n_active_keys = 0;
    if (_debug_flag > DEBUG3)
        fprintf(stderr, "Reset button definitions.\n");
}

//------------------------------------------------------------------------
// response registration functions

// timed response, basic version
int ts_resp(__int64 * time, __int64 * error, __int64 maxtime)
{
    if (!_timer_flag)
        ts_fatal("ts_resp: timer is not running yet.");

    // check whether there are response buttons active
    if (!_n_active_keys)
        ts_fatal("ts_resp: no response buttons active.");

    int userinput = 0, deadline = 0;
    __int64 prev, start, tm, err;
    start = prev = ts_time();
    err = 0;

    // wait
    do {
        if (_wait_flag >= SLEEP_RESP)
            _rtc_nap();

        tm = ts_time();
        err = tm - prev;
        prev = tm;
        userinput = ts_respstatus();
        if (maxtime > 0)
            if (tm - start >= maxtime)
                deadline = 1;
    } while (!userinput && !deadline);
    *time = tm;
    *error = err;
    return userinput;
}

// wait until a response key is released
int ts_release(__int64 * time, __int64 * error, __int64 maxtime)
{
    if (!_timer_flag)
        ts_fatal("ts_release: timer is not running yet.");

    // check whether there are response buttons active
    if (!_n_active_keys)
        ts_fatal("ts_release: no response buttons active.");

    int userinput = 0, deadline = 0;
    __int64 prev, start, tm, err;
    start = prev = ts_time();
    err = 0;

    // wait
    do {
        if (_wait_flag >= SLEEP_RESP)
            _rtc_nap();

        tm = ts_time();
        err = tm - prev;
        prev = tm;
        userinput = ts_respstatus();
        if (maxtime > 0)
            if (tm - start >= maxtime)
                deadline = 1;
    } while (userinput && !deadline);
    *time = tm;
    *error = err;
    return userinput;
}

// check whether one of the response buttons is closed
// does not check whether timer is started
int ts_respstatus()
{
    int userinput = 0;
    if (_parport1_resp_flag) {

#if defined(ALLEGRO_WINDOWS) || defined(ALLEGRO_LINUX)
        userinput = inb(PAR1STATUS);
#elif defined(ALLEGRO_MACOSX)
        ts_fatal
            ("ts_respstatus: parallel port can not be read on macosx.");
#endif
        // normal keys
        if (_key_active[P1] && ((userinput & 8) >> 3))
            return _key_active[P1];
        if (_key_active[P2] && ((userinput & 16) >> 4))
            return _key_active[P2];
        if (_key_active[P3] && ((userinput & 32) >> 5))
            return _key_active[P3];
        if (_key_active[P4] && ((userinput & 64) >> 6))
            return _key_active[P4];
        if (_key_active[P5] && ((userinput & 128) >> 7))
            return _key_active[P5];

        // inverted keys
        if (_key_active[IP1] && (((userinput & 8) >> 3) ^ 1))
            return _key_active[IP1];
        if (_key_active[IP2] && (((userinput & 16) >> 4) ^ 1))
            return _key_active[IP2];
        if (_key_active[IP3] && (((userinput & 32) >> 5) ^ 1))
            return _key_active[IP3];
        if (_key_active[IP4] && (((userinput & 64) >> 6) ^ 1))
            return _key_active[IP4];
        if (_key_active[IP5] && (((userinput & 128) >> 7) ^ 1))
            return _key_active[IP5];
    }
    if (_parport2_resp_flag) {

#if defined(ALLEGRO_WINDOWS) || defined(ALLEGRO_LINUX)
        userinput = inb(PAR2STATUS);
#elif defined(ALLEGRO_MACOSX)
        ts_fatal
            ("ts_respstatus: parallel port can not be read on macosx.");
#endif

        // normal keys
        if (_key_active[PP1] && ((userinput & 8) >> 3))
            return _key_active[PP1];
        if (_key_active[PP2] && ((userinput & 16) >> 4))
            return _key_active[PP2];
        if (_key_active[PP3] && ((userinput & 32) >> 5))
            return _key_active[PP3];
        if (_key_active[PP4] && ((userinput & 64) >> 6))
            return _key_active[PP4];
        if (_key_active[PP5] && ((userinput & 128) >> 7))
            return _key_active[PP5];

        // inverted keys
        if (_key_active[IPP1] && (((userinput & 8) >> 3) ^ 1))
            return _key_active[IPP1];
        if (_key_active[IPP2] && (((userinput & 16) >> 4) ^ 1))
            return _key_active[IPP2];
        if (_key_active[IPP3] && (((userinput & 32) >> 5) ^ 1))
            return _key_active[IPP3];
        if (_key_active[IPP4] && (((userinput & 64) >> 6) ^ 1))
            return _key_active[IPP4];
        if (_key_active[IPP5] && (((userinput & 128) >> 7) ^ 1))
            return _key_active[IPP5];
    }
    if (_parport3_resp_flag) {

#if defined(ALLEGRO_WINDOWS) || defined(ALLEGRO_LINUX)
        userinput = inb(PAR3STATUS);
#elif defined(ALLEGRO_MACOSX)
        ts_fatal
            ("ts_respstatus: parallel port can not be read on macosx.");
#endif

        // normal keys
        if (_key_active[PPP1] && ((userinput & 8) >> 3))
            return _key_active[PPP1];
        if (_key_active[PPP2] && ((userinput & 16) >> 4))
            return _key_active[PPP2];
        if (_key_active[PPP3] && ((userinput & 32) >> 5))
            return _key_active[PPP3];
        if (_key_active[PPP4] && ((userinput & 64) >> 6))
            return _key_active[PPP4];
        if (_key_active[PPP5] && ((userinput & 128) >> 7))
            return _key_active[PPP5];

        // inverted keys
        if (_key_active[IPPP1] && (((userinput & 8) >> 3) ^ 1))
            return _key_active[IPPP1];
        if (_key_active[IPPP2] && (((userinput & 16) >> 4) ^ 1))
            return _key_active[IPPP2];
        if (_key_active[IPPP3] && (((userinput & 32) >> 5) ^ 1))
            return _key_active[IPPP3];
        if (_key_active[IPPP4] && (((userinput & 64) >> 6) ^ 1))
            return _key_active[IPPP4];
        if (_key_active[IPPP5] && (((userinput & 128) >> 7) ^ 1))
            return _key_active[IPPP5];
    }
    if (_gameport_resp_flag) {

#if defined(ALLEGRO_WINDOWS) || defined(ALLEGRO_LINUX)
        userinput = inb(GAMEPORT);
#elif defined(ALLEGRO_MACOSX)
        ts_fatal("ts_respstatus: game port can not be read on macosx.");
#endif

        // normal keys
        if (_key_active[G1] && (((userinput & 16) >> 4) ^ 1))
            return _key_active[G1];
        if (_key_active[G2] && (((userinput & 32) >> 5) ^ 1))
            return _key_active[G2];
        if (_key_active[G3] && (((userinput & 64) >> 6) ^ 1))
            return _key_active[G3];
        if (_key_active[G4] && (((userinput & 128) >> 7) ^ 1))
            return _key_active[G4];

        // inverted keys
        if (_key_active[IG1] && ((userinput & 16) >> 4))
            return _key_active[IG1];
        if (_key_active[IG2] && ((userinput & 32) >> 5))
            return _key_active[IG2];
        if (_key_active[IG3] && ((userinput & 64) >> 6))
            return _key_active[IG3];
        if (_key_active[IG4] && ((userinput & 128) >> 7))
            return _key_active[IG4];
    }
    if (_serialport_resp_flag) {
        userinput = _serialport_poll();
        if (_key_active[S1] && userinput == 1)
            return _key_active[S1];
        if (_key_active[S2] && userinput == 2)
            return _key_active[S2];
        if (_key_active[S3] && userinput == 3)
            return _key_active[S3];
        if (_key_active[S4] && userinput == 4)
            return _key_active[S4];
        if (_key_active[S5] && userinput == 5)
            return _key_active[S5];
        if (_key_active[S6] && userinput == 6)
            return _key_active[S6];
        if (_key_active[S7] && userinput == 7)
            return _key_active[S7];
    }
    if (_joystick_resp_flag) {
        int i;
        poll_joystick();
        for (i = 0; i < joy[0].num_buttons; i++)
            if (_key_active[i + J1] && joy[0].button[i].b)
                return _key_active[J1 + i + 1];
    }
    if (_mouse_resp_flag) {
        poll_mouse();
        if (_key_active[M1] && mouse_b & 1)
            return _key_active[M1];
        if (_key_active[M2] && mouse_b & 2)
            return _key_active[M2];
        if (_key_active[M3] && mouse_b & 4)
            return _key_active[M3];
    }
    if (_keyboard_resp_flag) {
        int i;
        poll_keyboard();
        for (i = 1; i < KEY_MAX; i++)
            if (_key_active[i] && key[i])
                return _key_active[i];
    }
    return 0;
}

// wait until the reponse button is released
void ts_flushresp()
{
    if (!_timer_flag)
        ts_fatal("ts_flushresp: timer is not running yet.");
    // flush
    do {
        if (_wait_flag >= SLEEP_RESP)
            _rtc_nap();
    } while (ts_respstatus());
}

//------------------------------------------------------------------------
// vsync functions

// wait until the next vsync
void ts_vsync(__int64 * time, __int64 * error)
{

    if (!_timer_flag)
        _timer_init();
    __int64 prev;

    do {
    } while (ts_vsyncstatus());

    prev = ts_time();
    do {
        if (_wait_flag >= SLEEP_VSYNC)
            _rtc_nap();

        *time = ts_time();
        *error = *time - prev;
        prev = *time;
    } while (!ts_vsyncstatus());
}

// wait a few vsyncs 
void ts_vsyncs(__int64 * time, __int64 * error, int nsync)
{
    if (!_timer_flag)
        _timer_init();

    int i;
    for (i = 0; i < nsync; i++)
        ts_vsync(time, error);

}

// wait a few vsyncs, abort if response button is pressed
int ts_vsyncresp(__int64 * time, __int64 * error, int maxsync)
{
    if (!_timer_flag)
        ts_fatal("ts_vsyncresp: timer is not running yet.");
    if (!_n_active_keys)
        ts_fatal("ts_defkey: no response buttons active.");

    int userinput = 0;
    __int64 prev, start, tm, err;
    start = prev = ts_time();
    err = 0;

    // wait
    do {
        if (_wait_flag >= SLEEP_VSYNC)
            _rtc_nap();

        tm = ts_time();
        err = tm - prev;
        prev = tm;

        // watch the buttons
        userinput = ts_respstatus();

        // vsync
        if (ts_vsyncstatus()) {
            maxsync--;
            do {
            } while (ts_vsyncstatus());
        }
    } while (!userinput && maxsync > 0);
    *time = tm;
    *error = err;
    return userinput;
}

// check status of vsync signal
int ts_vsyncstatus()
{
    if (_vsync_flag == REALSYNC) {
#if defined(ALLEGRO_WINDOWS) || defined(ALLEGRO_LINUX)
        return (inb(VSYNCPORT) & 0x8);
#elif defined(ALLEGRO_MACOSX)
        ts_fatal
            ("ts_vsyncstatus: vsync register can not be read on macosx.");
        return 0;
#endif
    } else if (_vsync_flag == SIMSYNC_ACTUAL || _vsync_flag == SIMSYNC_REQ) {
        static int prev = 0, current = 0, sync = 0;
        if (ts_time() % _vsyncinterval > _vsyncinterval / 2)
            current = 1;
        else
            current = 0;
        if (current != prev && current == 1)
            sync = 1;
        else
            sync = 0;

        prev = current;
        return sync;
    } else
        return 0;
}

// set vsync mode 
int ts_vsyncmode(int mode)
{
    int oldmode = _vsync_requestedmode;

    if (mode == WHATEVER || mode == REALSYNC || mode == SIMSYNC_ACTUAL
        || mode == SIMSYNC_REQ)
        _vsync_requestedmode = mode;
    else
        ts_fatal("ts_vsyncmode: undefined vsync mode requested");

#ifdef ALLEGRO_MACOSX
    if (_debug_flag > DEBUG0) {
        fprintf(stderr, "\n\tVsync mode cannot be changed on macosx.\n");
        fprintf(stderr,
                "\tSimulating vsync at requested refreshrate.\n\n");
    }
    _vsync_requestedmode = SIMSYNC_REQ;
#endif

    // if vsync already running update it
    if (_vsync_flag) {
        if (_debug_flag > DEBUG1)
            fprintf(stderr, "ts_vsyncmode: updating vsync system\n");
        _vsync_exit();
        _vsync_init();
    }

    return oldmode;
}

// set maximum refreshrate for real vsync
int ts_vsynclimit(int limit)
{
    if (limit < 50 || limit > 150)
        ts_fatal("ts_vsynclimit: requested limit not sensible.");

    int oldlimit = _vsync_limit;
    _vsync_limit = limit;

    // if vsync already running update it
    if (_vsync_flag) {
        if (_debug_flag > DEBUG1)
            fprintf(stderr, "ts_vsynclimit: updating vsync system\n");
        _vsync_exit();
        _vsync_init();
    }
    return oldlimit;
}

int ts_setserialport(int port)
{
#ifdef ALLEGRO_WINDOWS
    if (port < 1)
        ts_fatal("ts_setserialport: port number should be >0.");

    int oldport = _serialport_num;
    _serialport_num = port;

    if (_serialport_flag) {
        if (_debug_flag > DEBUG1)
            fprintf(stderr, "ts_setserialport: updating serial port\n");
        _serialport_exit();
        _serialport_init();
    }
    return oldport;
#endif

#ifdef ALLEGRO_LINUX
    ts_fatal
        ("ts_setserialport: serial port not implemented in linux version.");
    return 1;
#endif

#ifdef ALLEGRO_MACOSX
    ts_fatal
        ("ts_setserialport: serial port not implemented in macosx version.");
    return 1;
#endif
}

//------------------------------------------------------------------------
// parallel port trigger functions

// set parallell port trigger parameters
void ts_settrigger(int port, __int64 time)
{
    if (!_trigger_flag)
        _trigger_flag = 1;

    // open port if necessary
    if (port == 1 && !_parport1_flag)
        _parport_init(1);
    else if (port == 2 && !_parport2_flag)
        _parport_init(2);
    else if (port == 3 && !_parport3_flag)
        _parport_init(3);
    else
        ts_fatal("ts_settrigger: unknown port requested");

    // compute address
    if (port == 1)
        _trigport = PAR1DATA;
    else if (port == 2)
        _trigport = PAR2DATA;
    else
        _trigport = PAR3DATA;

    //DATA port by default, increment 2 if you want to write to the control port
    _trigaddress = _trigport;
    _trigtime = time;
    ts_trigger(0);
}

// send a trigger value to the parallel port
void ts_trigger(char val)
{
    if (!_timer_flag)
        _timer_init();
    if (!_trigger_flag)
        ts_fatal("ts_trigger: parallel port parameters not set");

#if defined(ALLEGRO_WINDOWS) || defined(ALLEGRO_LINUX)
    outb(val, _trigaddress);
    ts_wait(_trigtime);
    outb(0, _trigaddress);
#elif defined(ALLEGRO_MACOSX)
    ts_fatal("ts_trigger: parallel port can not be read on macosx.");
#endif
}

//------------------------------------------------------------------------
// convert between time units

// tics -> seconds
__int64 tts(__int64 time)
{
    if (!_timer_flag)
        _timer_init();
    return time / _clockfreq;
}

// tics -> milliseconds
__int64 ttm(__int64 time)
{
    if (!_timer_flag)
        _timer_init();
    return (time * 1000) / _clockfreq;
}

// tics -> microseconds
__int64 ttmu(__int64 time)
{
    if (!_timer_flag)
        _timer_init();
    return (time * 1000000) / _clockfreq;
}

// seconds -> tics
__int64 stt(__int64 time)
{
    if (!_timer_flag)
        _timer_init();
    return time * _clockfreq;
}

// milliseconds -> tics
__int64 mtt(__int64 time)
{
    if (!_timer_flag)
        _timer_init();
    return (time * _clockfreq) / 1000;
}

// microseconds -> tics
__int64 mutt(__int64 time)
{
    if (!_timer_flag)
        _timer_init();
    return (time * _clockfreq) / 1000000;
}


top
Persoonlijke pagina Universiteit GentTscope
Allegro | Cygwin | Gcc
© See license.html for copyright information