/* 
 * Timing Intel execution
 *
 * Copyright (c) 1998 Criterion Software Ltd.
 */

#include <stdio.h>
#include <stdlib.h>

#include "rpplugin.h"
#include "rpdbgerr.h"
#include "rtintel.h"
#include "timing.h"

static const char __RWUNUSED__   rcsid[] =
    "@@(#)$Id: timing.c,v 1.8 2001/06/12 08:55:21 johns Exp $";

#if ( defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER>=1000) )
#if (defined(_XBOX))
#include <xtl.h>
#else /* (defined(_XBOX)) */
#include <windows.h>
#include <crtdbg.h>
#endif /* (defined(_XBOX)) */

#define   OUTPUTDEBUGSTRING(_msg)   OutputDebugString(_msg)
#endif /* ( defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER>=1000) ) */

#if (!defined(OUTPUTDEBUGSTRING))
#define OUTPUTDEBUGSTRING(_msg) /* Null op */
#endif /* (!defined(OUTPUTDEBUGSTRING)) */

/*
 * Code for monitoring performance transparently in real time on P5+
 * processors through built-in processor hardware. See
 * http://developer.intel.com/drg/mmx/AppNotes/perfmon.htm
 *   "The Pentium (and P55C) processor has built-in hardware features for
 *    monitoring performance transparently in real time.
 *    These consist of the Performance Monitoring Event Counters and the Time
 *    Stamp Counter, which are implemented as four hardware registers
 *    (Model Specific Registers 10h-13h)"
 */

#if ( defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER>=1000) )

#define  MSC_VER_OK

static __inline     RwFixed64
Fix64Sub(RwFixed64 a, RwFixed64 b)
{
    RwFixed64           result;

    RWFUNCTION(RWSTRING("Fix64Sub"));

    result.msb = a.msb - b.msb;
    if (a.msb < b.msb)
    {
        result.msb = a.msb - b.msb - 1;
    }
    else
    {
        result.msb = a.msb - b.msb;
    }

    RWRETURN(result);
}

#if (0)
#define RPINTELFUNCTION(name)                      \
do                                              \
{                                               \
   char buffer[256];                            \
                                                \
   _snprintf(buffer, 256, "%s:%d:   %s\n",      \
           __FILE__, __LINE__, name);           \
   OUTPUTDEBUGSTRING(buffer);                   \
} while(0)
#endif /* (0) */

#endif /* ( defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER>=1000) ) */

#if (!defined(RPINTELFUNCTION))
#define RPINTELFUNCTION(name)  /* Null Op */
#endif /* (!defined(RPINTELFUNCTION)) */

/*
 *  Define pre-execute and post execute call-backs for timing.
 */

typedef struct _timing_data timing_data;
struct _timing_data
{
    RwInt32             level;
    RwFixed64           start;
};

/**
 * \ingroup rtintel
 * \ref RtIntelStartTiming start timing
 *
 * \param  data   data
 *
 * \return Returns success flag
 */

RwBool
RtIntelStartTiming(void * __RWUNUSED__ data)
{
    RWAPIFUNCTION(RWSTRING("RtIntelStartTiming"));
#ifdef MSC_VER_OK
    {

        RwFixed64           start;
        timing_data        *timing;

        timing = (timing_data *) RwMalloc(sizeof(timing_data));
        *((timing_data **) data) = timing;
/* *INDENT-OFF* */
   _asm
   {
      ;                         /* pre-load memory variables into data cache */
      mov            edx, start.msb;
      mov            eax, start.msb;

      ;                         /*disable interrupts - requires priviledges */
      ; /* cli */ ;
   }
    _asm
    {
        ;                      /*RDTSC - get beginning timestamp to edx:eax */
        _emit 0x0F;
        _emit 0x31;
        ;                      /*save beginning timestamp (1 cycle) */
        mov             start.msb, edx;
        mov             start.msb, eax;
    }
/* *INDENT-ON* */

        timing->start = start;
    }

#endif /* MSC_VER_OK */

    RWRETURN((FALSE));
}

/**
 * \ingroup rtintel
 * \ref RtIntelStopTiming stop timing
 *
 * \param  data   data
 *
 * \return Returns success flag
 */

RwBool
RtIntelStopTiming(void * __RWUNUSED__ data)
{
    RWAPIFUNCTION(RWSTRING("RtIntelStopTiming"));
#ifdef MSC_VER_OK
    {

        const static RwFixed64 bias = {
            0, 13 + 1
        };
        RwFixed64           start;
        RwFixed64           end;
        RwFixed64           elapsed;
        RwChar              buffer[256];
/* *INDENT-OFF* */
   _asm
   {
      ;                         /*RDTSC - get ending timestamp to edx:eax */
      ;                         /*(13 cycles) */
      _emit 0x0F;
      _emit 0x31;
      ;                         /*re-enable - requires priviledges */
      ; /* sti */ ;
      ;                         /*save ending timestamp */
      mov            end.msb, edx;
      mov            end.msb, eax;
   }
/* *INDENT-ON* */      

        start = ((timing_data *) data)->start;
        elapsed = Fix64Sub(end, start);
        elapsed = Fix64Sub(elapsed, bias);

        sprintf(buffer, "%s:%d:   start %24.0f cycles\n",
                __FILE__, __LINE__, doubleFromRwFixed64(start));
        OUTPUTDEBUGSTRING(buffer);

        sprintf(buffer, "%s:%d:     end %24.0f cycles\n",
                __FILE__, __LINE__, doubleFromRwFixed64(end));
        OUTPUTDEBUGSTRING(buffer);

        sprintf(buffer, "%s:%d: elapsed %24.0f cycles\n",
                __FILE__, __LINE__, doubleFromRwFixed64(elapsed));
        OUTPUTDEBUGSTRING(buffer);

        RwFree(data);
    }

#endif /* MSC_VER_OK */

    RWRETURN((FALSE));
}

/**
 * \ingroup rtintel
 * \ref RtIntelTime time execution of a function

 * \param result  clock ticks elapsed
 * \param func  function to be timed
 * \param data  data pointer passed to function
 * \return Returns success flag
 */

RwBool
RtIntelTime(RwFixed64 * result, RtIntelTimeFunction func, void *data)
{
    RwBool              status = FALSE;

#if ( defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER>=1000) )
    const static RwFixed64 bias = {
        0, 13 + 1
    };
    RwFixed64           start;
    RwFixed64           end;
    RwFixed64           elapsed;
    RwChar              buffer[256];

#endif /* ( defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER>=1000) ) */

    RWAPIFUNCTION(RWSTRING("RtIntelTime"));

#if ( defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER>=1000) )
/* *INDENT-OFF* */
   _asm
   {
      ;                         /* pre-load memory variables into data cache */
      mov            edx, start.msb;
      mov            eax, start.msb;

      ;                         /*disable interrupts - requires priviledges */
      ; /* cli */ ;
   }
/* *INDENT-ON* */      

    /* status = (*func)(data); */
/* *INDENT-OFF* */
   _asm
   {
      ;                         /*RDTSC - get beginning timestamp to edx:eax */
      _emit 0x0F;
      _emit 0x31;
      ;                         /*save beginning timestamp (1 cycle) */
      mov            start.msb, edx;
      mov            start.msb, eax;
   }
/* *INDENT-ON* */      

    status = (*func) (data);
/* *INDENT-OFF* */
   _asm
   {
      ;                         /*RDTSC - get ending timestamp to edx:eax */
      ;                         /*(13 cycles) */
      _emit 0x0F;
      _emit 0x31;
      ;                         /*re-enable - requires priviledges */
      ; /* sti */ ;
      ;                         /*save ending timestamp */
      mov            end.msb, edx;
      mov            end.msb, eax;
   }
/* *INDENT-ON* */      

    elapsed = Fix64Sub(end, start);
    elapsed = Fix64Sub(elapsed, bias);

    sprintf(buffer, "%s:%d:   start %24.0f cycles\n",
            __FILE__, __LINE__, doubleFromRwFixed64(start));
    OUTPUTDEBUGSTRING(buffer);

    sprintf(buffer, "%s:%d:     end %24.0f cycles\n",
            __FILE__, __LINE__, doubleFromRwFixed64(end));
    OUTPUTDEBUGSTRING(buffer);

    sprintf(buffer, "%s:%d: elapsed %24.0f cycles\n",
            __FILE__, __LINE__, doubleFromRwFixed64(elapsed));
    OUTPUTDEBUGSTRING(buffer);

    *result = elapsed;

#else /* ( defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER>=1000) ) */

    status = (*func) (data);

    result->msb = 0;
    result->lsb = 0;

#endif /* ( defined(_WIN32) && defined(_MSC_VER) && (_MSC_VER>=1000) ) */

    RWRETURN(status);
}
