// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //
//
//  Project:   Talina Gaming System (TgS) (∂)
//  File:      TgS (W32) Common - Base - API - Platform.c
//  Author:    Andrew Aye (EMail: mailto:andrew.aye@gmail.com, Web: http://www.andrewaye.com)
//  Version:   4.0
//
// ------------------------------------------------------------------------------------------------------------------------------ //
//
//  Copyright: © 2002-2010, Andrew Aye.  All Rights Reserved.
//
//  This software is free for non-commercial use. Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met: 
//    Redistributions of source code must retain this copyright notice, this list of conditions and the following disclaimers. 
//    Redistributions in binary form must reproduce this copyright notice, this list of conditions and the following
//      disclaimers in the documentation and other materials provided with the distribution. 
//
//  Neither the names of the copyright owner nor the names of its contributors may be used to endorse or promote products derived
//  from this software without specific prior written permission. 
//
//  The intellectual property rights of the algorithms used reside with Andrew Aye.  You may not use this software, in whole or
//  in part, in support of any commercial product without the express written consent of the author.
//
//  There is no warranty or other guarantee of fitness of this software for any purpose. It is provided solely "as is".
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //

// START TGS - THREAD //////////////////////////////////////////////////////////////////////////////////////////////////////////////

// ---- tgTR_Create ------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgUINTPTR tgTR_Create(
    TgPLATFORM_THREAD_FCN pfnThread, PC_TgVOID pParam, C_TgUINT32 uiStack, C_ETgTHREAD_PRIORITY enPriority, CPCU_TgCHAR szName
) {
#pragma pack(push,8)
    typedef struct
    {
        DWORD                               dwType; // Must be 0x1000.
        LPCSTR                              szName; // Pointer to name (in user addr space).
        DWORD                               dwThreadID; // Thread ID (-1=caller thread).
        DWORD                               dwFlags; // Reserved for future use, must be zero.
    } THREADNAME_INFO;
#pragma pack(pop)

    TgUINT32                            uiThreadID;
    THREADNAME_INFO                     name_data;

    C_TgUINTPTR                         hThread = _beginthreadex( 0, uiStack, pfnThread, pParam, 0, &uiThreadID );

#if TgS_WIDE_CHAR
    TgANSICHAR                          szNameAnsi[1024];

    sprintf(szNameAnsi, "%S", szName);
    name_data.szName = szNameAnsi;
#else
    name_data.szName = szName;
#endif
    name_data.dwType = 0x1000;
    name_data.dwThreadID = uiThreadID;
    name_data.dwFlags = 0;

    Sleep(10);

    __try
    {
        RaiseException( 0x406D1388, 0, sizeof(name_data) / sizeof(ULONG_PTR), (ULONG_PTR*)&name_data );
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
    };

    switch (enPriority)
    {
        case ETgTHREAD_PRIORITY__CRITICAL:
            TgVERIFY(0 != SetThreadPriority( (HANDLE)hThread, THREAD_PRIORITY_TIME_CRITICAL ));
            break;
        case ETgTHREAD_PRIORITY__HIGHEST:
            TgVERIFY(0 != SetThreadPriority( (HANDLE)hThread, THREAD_PRIORITY_HIGHEST ));
            break;
        case ETgTHREAD_PRIORITY__HIGH:
            TgVERIFY(0 != SetThreadPriority( (HANDLE)hThread, THREAD_PRIORITY_ABOVE_NORMAL ));
            break;
        case ETgTHREAD_PRIORITY__NORMAL:
            TgVERIFY(0 != SetThreadPriority( (HANDLE)hThread, THREAD_PRIORITY_NORMAL ));
            break;
        case ETgTHREAD_PRIORITY__LOW:
            TgVERIFY(0 != SetThreadPriority( (HANDLE)hThread, THREAD_PRIORITY_BELOW_NORMAL ));
            break;
        case ETgTHREAD_PRIORITY__LOWEST:
            TgVERIFY(0 != SetThreadPriority( (HANDLE)hThread, THREAD_PRIORITY_LOWEST ));
            break;
        default:
            TgS_NO_DEFAULT(break);
    };

    return (hThread);
};


// ---- tgTR_Yield_Thread ------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgTR_Yield_Thread()
{
    SwitchToThread();
};


// ---- tgTR_Sleep -------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgTR_Sleep( C_TgUINT32 uiSleep_MS )
{
    Sleep( uiSleep_MS );
};


// ---- tgTR_Close -------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgTR_Close( C_TgUINTPTR uiThread_Id )
{
    while (WAIT_OBJECT_0 != WaitForSingleObject( (HANDLE)uiThread_Id, INFINITE )); //« Wait for thread to close
    TgVERIFY( 0 != CloseHandle( (HANDLE)uiThread_Id ) ); //« Close the handle
};


// ---- tgTR_Query_Id ----------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgUINTPTR tgTR_Query_Id()
{
    return (GetCurrentThreadId());
};


// START TIME //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static TgFLOAT64                    s_fFreq;
static LARGE_INTEGER                s_iStart;
static TgFLOAT32                    s_fLast;
static TgFLOAT32                    s_fElapsed;
static TgFLOAT32                    s_fStop;


// ---- tgTM_Query_Absolute_Time ------------------------------------------------------------------------------------------------ //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgFLOAT32 tgTM_Query_Absolute_Time()
{
    LARGE_INTEGER                       iTime;
    QueryPerformanceCounter( &iTime );
    return ((TgFLOAT32)(((TgFLOAT64)(iTime.QuadPart - s_iStart.QuadPart)) * s_fFreq));
};


// ---- tgTM_Query_Time --------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgFLOAT32 tgTM_Query_Time()
{
    return (tgPM_FSEL_F32(-s_fStop, tgTM_Query_Absolute_Time(), s_fStop ));
};


// ---- tgTM_Elapsed_Time ------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgFLOAT32 tgTM_Elapsed_Time()
{
    return (s_fElapsed);
};


// ---- tgTM_Init --------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgTM_Init()
{
    LARGE_INTEGER                       iPerf;

    QueryPerformanceFrequency( &iPerf );

    s_fFreq = 1.0 / (TgFLOAT64)iPerf.QuadPart;

    tgTM_Reset();
};

// ---- tgTM_Reset -------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgTM_Reset()
{
    QueryPerformanceCounter( &s_iStart );
    s_fLast = tgTM_Query_Absolute_Time();
    s_fElapsed = 1.0F / 60.0F;
    s_fStop = 0.0F;
};


// ---- tgTM_Start -------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgTM_Start()
{
    if (!(s_fStop <= 0.0F))
    {
        s_fLast = tgTM_Query_Absolute_Time();
        s_fStop = 0.0F;
    };
};


// ---- tgTM_Stop --------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgTM_Stop()
{
    if (!(s_fStop > 0.0F))
    {
        s_fStop = s_fLast;
    };
};


// ---- tgTM_Update ------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgTM_Update()
{
    if (!(s_fStop > 0.0F))
    {
        C_TgFLOAT32 fTime                   = tgTM_Query_Absolute_Time();
        s_fElapsed                          = tgCM_MAX_F32( TgEPS_F32, fTime - s_fLast );
        s_fLast                             = fTime;
        s_fStop                             = 0.0F;
    };
};


// ---- tgTM_Step --------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgTM_Step( C_TgFLOAT32 fTimeAdvance )
{
    s_fStop += fTimeAdvance;
};


// START IO ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// ---- File_Error -------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
static TgVOID File_Error( C_TgSINTPTR iFile )
{
    const DWORD                         dwError = GetLastError();
    BY_HANDLE_FILE_INFORMATION          sData;
    TCHAR                               szBuf[256]; 
    LPVOID                              lpMsgBuf;

    TgASSERT((TgSINTPTR)INVALID_HANDLE_VALUE != iFile);

    GetFileInformationByHandle( (HANDLE)iFile, &sData );

    FormatMessage(
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 0, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPTSTR) &lpMsgBuf, 0, NULL
        );

    tgSZ_PrintF( szBuf, 256, TgT("File_Read failed with error %d: %s"), dwError, lpMsgBuf );
    LocalFree( lpMsgBuf );

    TgCRITICAL_MSGF( 0, TgT("%s\n"), szBuf );
    OutputDebugString( szBuf );

    TgCRITICAL( TgFALSE );
};


// ---- tgIO_File_Open ---------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_File_Open( P_TgSINTPTR piFile, CPC_TgCHAR pszFile, ETgFILE_IO_ACCESS enAccess )
{
    DWORD dwDesiredAccess=0, dwShareMode=0;
    HANDLE hFile;

    TgASSERT((0 != piFile) && (0 != pszFile));

    switch (enAccess)
    {
        case ETgFILE_IO_ACCESS_INVALID:
            return (TgE_FAIL);

        case ETgFILE_IO_ACCESS_READ_SHARED:
            dwShareMode = FILE_SHARE_READ;
        case ETgFILE_IO_ACCESS_READ:
            dwDesiredAccess = FILE_GENERIC_READ;
            break;

        case ETgFILE_IO_ACCESS_WRITE_SHARED:
            dwShareMode = FILE_SHARE_WRITE;
        case ETgFILE_IO_ACCESS_WRITE:
            dwDesiredAccess = FILE_GENERIC_WRITE;
            break;

        case ETgFILE_IO_ACCESS_READ_WRITE_SHARED:
            dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
        case ETgFILE_IO_ACCESS_READ_WRITE:
            dwDesiredAccess = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
            break;

        default:
            TgS_NO_DEFAULT(break);
    };

    hFile = CreateFile( pszFile, dwDesiredAccess, dwShareMode, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, 0 );
    *piFile = (TgSINTPTR)hFile;
    return (hFile != INVALID_HANDLE_VALUE ? TgS_OK : TgE_FAIL);
};


// ---- tgIO_File_Read ---------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_File_Read( C_TgSINTPTR iFile, PC_TgVOID pDest, C_TgSIZE nBytesToRead )
{
    DWORD                               nBytesRead;

    TgASSERT((TgSINTPTR)INVALID_HANDLE_VALUE != iFile);

    if (!(ReadFile( (HANDLE)iFile, pDest, (DWORD)nBytesToRead, &nBytesRead, NULL)))
    {
        File_Error( iFile );
    };

    return (nBytesRead <= nBytesToRead ? TgS_OK : TgE_FAIL);
};


// ---- tgIO_File_Write --------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_File_Write( C_TgSINTPTR iFile, CPC_TgVOID pSource, C_TgSIZE nBytesToWrite )
{
    DWORD                               nBytesWrite;

    TgASSERT((TgSINTPTR)INVALID_HANDLE_VALUE != iFile);

    if (!(WriteFile( (HANDLE)iFile, pSource, (DWORD)nBytesToWrite, &nBytesWrite, NULL)))
    {
        File_Error( iFile );
    };

    return (nBytesWrite == nBytesToWrite ? TgS_OK : TgE_FAIL);
};


// ---- tgIO_File_Flush --------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_File_Flush( C_TgSINTPTR iFile )
{
    TgASSERT((TgSINTPTR)INVALID_HANDLE_VALUE != iFile);
    TgVERIFY(0 != FlushFileBuffers( (HANDLE)iFile ));
    return (TgS_OK);
};


// ---- tgIO_File_Close --------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_File_Close( C_TgSINTPTR iFile )
{
    TgASSERT((TgSINTPTR)INVALID_HANDLE_VALUE != iFile);
    TgVERIFY(0 != CloseHandle( (HANDLE)iFile ));
    return (TgS_OK);
};


// ---- tgIO_File_Valid --------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgBOOL tgIO_File_Valid( C_TgSINTPTR iFile )
{
    TgASSERT((TgSINTPTR)INVALID_HANDLE_VALUE != iFile);
    return (INVALID_HANDLE_VALUE != (HANDLE)iFile);
};


// ---- tgIO_File_Seek ---------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_File_Seek( C_TgSINTPTR iFile, C_ETgIO_SEEK enSeek_Mode, C_TgSINTPTR iPos )
{
    BOOL                                bSuccess = FALSE;
    LARGE_INTEGER                       uiSet_Position;

    TgASSERT((TgSINTPTR)INVALID_HANDLE_VALUE != iFile);

    uiSet_Position.QuadPart = iPos;

    switch (enSeek_Mode)
    {
        case ETgIO_SEEK_BEGIN:
            bSuccess = SetFilePointerEx( (HANDLE)iFile, uiSet_Position, 0, FILE_BEGIN );
            break;
        case ETgIO_SEEK_END:
            bSuccess = SetFilePointerEx( (HANDLE)iFile, uiSet_Position, 0, FILE_END );
            break;
        case ETgIO_SEEK_CURRENT:
            bSuccess = SetFilePointerEx( (HANDLE)iFile, uiSet_Position, 0, FILE_CURRENT );
            break;

        default:
            TgS_NO_DEFAULT(return (TgE_FAIL));
    }

#if defined(_DEBUG)
    {
        LARGE_INTEGER                       uiNew_Position;

        uiSet_Position.QuadPart = 0;

        SetFilePointerEx( (HANDLE)iFile, uiSet_Position, &uiNew_Position, FILE_CURRENT );
        bSuccess &= (TgSIZE)uiNew_Position.QuadPart <= tgIO_File_Size( iFile );
    }
#endif // defined(_DEBUG)

    return (bSuccess ? TgS_OK : TgE_FAIL);

};


// ---- tgIO_File_Size ---------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgSIZE tgIO_File_Size( C_TgSINTPTR iFile )
{
    DWORD uiHigh, uiLow;

    TgASSERT((TgSINTPTR)INVALID_HANDLE_VALUE != iFile);
    uiLow = GetFileSize( (HANDLE)iFile, &uiHigh );
    return (((TgSIZE)uiHigh << 31) | uiLow);
};


// ---- tgIO_Directory_Make ----------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_Directory_Make( CPC_TgCHAR pszPath )
{
    return (CreateDirectory( pszPath, 0 ) ? TgS_OK : TgE_FAIL);
};


// ---- tgIO_Directory_Remove --------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_Directory_Remove( CPC_TgCHAR pszPath )
{
    return (RemoveDirectory( pszPath ) ? TgS_OK : TgE_FAIL);
};


// ---- tgIO_Directory_Exists --------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgBOOL tgIO_Directory_Exists( CPC_TgCHAR pszPath )
{
    const DWORD dwAttrib = GetFileAttributes( pszPath );
    return (~(DWORD)0 != dwAttrib && 0 != (FILE_ATTRIBUTE_DIRECTORY & dwAttrib) ? TgTRUE : TgFALSE);
};


// ---- tgIO_File_Delete -------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_File_Delete( CPC_TgCHAR pszFile )
{
    return (DeleteFile( pszFile ) ? TgS_OK : TgE_FAIL);
};


// ---- tgIO_File_Copy ---------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_File_Copy( CPC_TgCHAR pszExisting, CPC_TgCHAR pszDest )
{
    return (CopyFile( pszExisting, pszDest, TgTRUE ) ? TgS_OK : TgE_FAIL);
};


// ---- tgIO_File_Move ---------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgIO_File_Move( CPC_TgCHAR pszExisting, CPC_TgCHAR pszDest )
{
    return (MoveFile( pszExisting, pszDest ) ? TgS_OK : TgE_FAIL);
};


// ---- tgIO_File_Exists -------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgBOOL tgIO_File_Exists( CPC_TgCHAR pszFile )
{
    const DWORD dwAttrib = GetFileAttributes( pszFile );
    return (dwAttrib != -1 && dwAttrib != FILE_ATTRIBUTE_DIRECTORY ? TgTRUE : TgFALSE);
};


// START ATOMIC ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

TgVOID tgAM_WRITE_FENCE()
{
    _WriteBarrier();
};


TgVOID tgAM_READ_FENCE()
{
    _ReadBarrier();
};


TgSINT32 tgAM32_ADD( TgATOMIC_INT32 *piTarget, C_TgSINT32 iVal )
{
#if defined(WIN32)
    TgATOMIC_INT32 iCurrent_Value, iNew_Value;

    do
    {
        iCurrent_Value = *piTarget;
        iNew_Value = iCurrent_Value + iVal;
    }
    while (_InterlockedCompareExchange( piTarget, iNew_Value, iCurrent_Value ) != iCurrent_Value);

    return (iNew_Value);
#else
    return (_InterlockedAdd( (volatile LONG*)piTarget, iVal ));
#endif
};


TgSINT32 tgAM32_SUB( TgATOMIC_INT32 *piTarget, C_TgSINT32 iVal )
{
#if defined(WIN32)
    TgATOMIC_INT32 iCurrent_Value, iNew_Value;

    do
    {
        iCurrent_Value = *piTarget;
        iNew_Value = iCurrent_Value - iVal;
    }
    while (_InterlockedCompareExchange( piTarget, iNew_Value, iCurrent_Value ) != iCurrent_Value);

    return (iNew_Value);
#else
    return (_InterlockedAdd( (volatile LONG*)piTarget, -iVal ));
#endif
};


TgSINT32 tgAM32_AND( TgATOMIC_INT32 *piTarget, C_TgSINT32 iVal )
{
    return (_InterlockedAnd( (volatile LONG*)piTarget, iVal ));
};


TgSINT32 tgAM32_OR( TgATOMIC_INT32 *piTarget, C_TgSINT32 iVal )
{
    return (_InterlockedOr( (volatile LONG*)piTarget, iVal ));
};


TgSINT32 tgAM32_INC( TgATOMIC_INT32 *piTarget )
{
    return (_InterlockedIncrement( (volatile LONG*)piTarget ));
};


TgSINT32 tgAM32_DEC( TgATOMIC_INT32 *piTarget )
{
    return (_InterlockedDecrement( (volatile LONG*)piTarget ));
};


TgSINT32 tgAM32_READ( TgATOMIC_INT32 *piTarget )
{
    return (*(volatile LONG*)piTarget);
};


TgVOID tgAM32_WRITE( TgATOMIC_INT32 *piTarget, C_TgSINT32 iVal )
{
    _InterlockedExchange( (volatile LONG*)piTarget, iVal );
};


TgSINT32 tgAM32_XCHG( TgATOMIC_INT32 *piTarget, C_TgSINT32 iVal )
{
    return (_InterlockedExchange( (volatile LONG*)piTarget, iVal ));
};


TgSINT32 tgAM32_XCMP( TgATOMIC_INT32 *piTarget, C_TgSINT32 iVal, C_TgSINT32 iCmp )
{
    return (_InterlockedCompareExchange( (volatile LONG*)piTarget, iVal, iCmp ));
};


#if defined(_WIN64)

TgSINT64 tgAM64_ADD( TgATOMIC_INT64 *piTarget, C_TgSINT64 iVal )
{
    return (_InterlockedAdd64( (volatile LONGLONG*)piTarget, iVal ));
};


TgSINT64 tgAM64_SUB( TgATOMIC_INT64 *piTarget, C_TgSINT64 iVal )
{
    return (_InterlockedAdd64( (volatile LONGLONG*)piTarget, -iVal ));
};


TgSINT64 tgAM64_AND( TgATOMIC_INT64 *piTarget, C_TgSINT64 iVal )
{
    return (_InterlockedAnd64( (volatile LONGLONG*)piTarget, iVal ));
};


TgSINT64 tgAM64_OR( TgATOMIC_INT64 *piTarget, C_TgSINT64 iVal )
{
    return (_InterlockedOr64( (volatile LONGLONG*)piTarget, iVal ));
};


TgSINT64 tgAM64_INC( TgATOMIC_INT64 *piTarget )
{
    return (_InterlockedIncrement64( (volatile LONGLONG*)piTarget ));
};


TgSINT64 tgAM64_DEC( TgATOMIC_INT64 *piTarget )
{
    return (_InterlockedDecrement64( (volatile LONGLONG*)piTarget ));
};


TgSINT64 tgAM64_READ( TgATOMIC_INT64 *piTarget )
{
    return (*(volatile LONGLONG*)piTarget);
};


TgVOID tgAM64_WRITE( TgATOMIC_INT64 *piTarget, C_TgSINT64 iVal )
{
    _InterlockedExchange64( (volatile LONGLONG*)piTarget, iVal );
};


TgSINT64 tgAM64_XCHG( TgATOMIC_INT64 *piTarget, C_TgSINT64 iVal )
{
    return (_InterlockedExchange64( (volatile LONGLONG*)piTarget, iVal ));
};


TgSINT64 tgAM64_XCMP( TgATOMIC_INT64 *piTarget, C_TgSINT64 iVal, C_TgSINT64 iCmp )
{
    return (_InterlockedCompareExchange64( (volatile LONGLONG*)piTarget, iVal, iCmp ));
};

#endif // defined(_WIN64)