/******************************************************************************

 Copyright (c) 2000 Advanced Micro Devices, Inc.

 LIMITATION OF LIABILITY:  THE MATERIALS ARE PROVIDED *AS IS* WITHOUT ANY
 EXPRESS OR IMPLIED WARRANTY OF ANY KIND INCLUDING WARRANTIES OF MERCHANTABILITY,
 NONINFRINGEMENT OF THIRD-PARTY INTELLECTUAL PROPERTY, OR FITNESS FOR ANY
 PARTICULAR PURPOSE.  IN NO EVENT SHALL AMD OR ITS SUPPLIERS BE LIABLE FOR ANY
 DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS,
 BUSINESS INTERRUPTION, LOSS OF INFORMATION) ARISING OUT OF THE USE OF OR
 INABILITY TO USE THE MATERIALS, EVEN IF AMD HAS BEEN ADVISED OF THE POSSIBILITY
 OF SUCH DAMAGES.  BECAUSE SOME JURISDICTIONS PROHIBIT THE EXCLUSION OR LIMITATION
 OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE LIMITATION MAY
 NOT APPLY TO YOU.

 AMD does not assume any responsibility for any errors which may appear in the
 Materials nor any responsibility to support or update the Materials.  AMD retains
 the right to make changes to its test specifications at any time, without notice.

 NO SUPPORT OBLIGATION: AMD is not obligated to furnish, support, or make any
 further information, software, technical information, know-how, or show-how
 available to you.

 So that all may benefit from your experience, please report  any  problems
 or  suggestions about this software to 3dsdk.support@amd.com

 AMD Developer Technologies, M/S 585
 Advanced Micro Devices, Inc.
 5900 E. Ben White Blvd.
 Austin, TX 78741
 3dsdk.support@amd.com
******************************************************************************/
/* Symbolic constants for application specific feature flags */
#include <windows.h>

#define FEATURE_CPUID          0x00000001
#define FEATURE_STD_FEATURES   0x00000002
#define FEATURE_EXT_FEATURES   0x00000004
#define FEATURE_TSC            0x00000010
#define FEATURE_MMX            0x00000020
#define FEATURE_CMOV           0x00000040
#define FEATURE_3DNOW          0x00000080
#define FEATURE_3DNOWEXT       0x00000100  // Integer SSE 
#define FEATURE_MMXEXT         0x00000200
#define FEATURE_SSEFP          0x00000400
#define FEATURE_K6_MTRR        0x00000800
#define FEATURE_P6_MTRR        0x00001000

/* Symbolic constants for feature flags in CPUID standard feature flags */

#define CPUID_STD_FPU          0x00000001
#define CPUID_STD_VME          0x00000002
#define CPUID_STD_DEBUGEXT     0x00000004
#define CPUID_STD_4MPAGE       0x00000008
#define CPUID_STD_TSC          0x00000010
#define CPUID_STD_MSR          0x00000020
#define CPUID_STD_PAE          0x00000040
#define CPUID_STD_MCHKXCP      0x00000080
#define CPUID_STD_CMPXCHG8B    0x00000100
#define CPUID_STD_APIC         0x00000200
#define CPUID_STD_SYSENTER     0x00000800
#define CPUID_STD_MTRR         0x00001000
#define CPUID_STD_GPE          0x00002000
#define CPUID_STD_MCHKARCH     0x00004000
#define CPUID_STD_CMOV         0x00008000
#define CPUID_STD_PAT          0x00010000
#define CPUID_STD_PSE36        0x00020000
#define CPUID_STD_MMX          0x00800000
#define CPUID_STD_FXSAVE       0x01000000
#define CPUID_STD_SSE          0x02000000

/* Symbolic constants for feature flags in CPUID extended feature flags */

#define CPUID_EXT_3DNOW        0x80000000
#define CPUID_EXT_AMD_3DNOWEXT 0x40000000
#define CPUID_EXT_AMD_MMXEXT   0x00400000

static unsigned int get_cpu_feature_flags();

/*********************************************************
MEMCPY_AMD.CPP
*********************************************************/

// Very optimized memcpy() routine for AMD Athlon and Duron family
// Uses new movnt "non-temporal move" instructions for large blocks
// Be sure you're running on Athlon/Duron or later CPU before calling!
//
// Inline assembly syntax for use with Visual C++

static int _3dnow_d;

/*extern "C"*/
void memcopy_init() {

   unsigned int r=get_cpu_feature_flags();
   _3dnow_d = r&FEATURE_3DNOWEXT;

}



void * memcopy_amd(void *dest, const void *src, size_t n)
{
  __asm {

	mov ecx,[n]		; number of bytes to copy
	mov esi,[src]		; source
	mov edi,[dest]		; destination
	mov ebx,ecx		; keep copy of count
	mov eax,edi		; for returning destination pointer

	cld
	cmp ecx,64
	jb $memcpy_2_1		; if less than 64 bytes, skip mmx copy

//check_align_problem:		; don't align between 32k-64k because
	cmp ecx,32*1024		;  it appears to be slower
	jbe $align_problem
	cmp ecx,64*1024
	jbe $no_align_problem
$align_problem:
	mov ecx,8
	sub ecx,edi		; align destination to qword
	and ecx,111b
	sub ebx,ecx		; update copy count
	neg ecx
	add ecx,offset $no_align_problem
	jmp ecx			; jump to array of movsb's

align 4
	movsb
	movsb
	movsb
	movsb
	movsb
	movsb
	movsb
	movsb

$no_align_problem:		; already dword aligned
	mov ecx,ebx
	shr ecx,6		; break down into 64-byte g_DVDGlobals.block count
	jz $memcpy_2_2

	cmp ecx,64*1024/64	; 64k+? if so, do movntq copy
	jae $memcpy_nt_1

align 16
$memcpy_1:			; do 64-byte g_DVDGlobals.block copies first

//	prefetch [esi + (200*64/34+192)]  //This code causes Intel Compiler to gag. Probably
									  //wrong syntax for the op or something.
									  //01/26/02 int21h

	movq mm0,[esi+0]
	movq mm1,[esi+8]
	movq [edi+0],mm0
	movq [edi+8],mm1
	movq mm2,[esi+16]
	movq mm3,[esi+24]
	movq [edi+16],mm2
	movq [edi+24],mm3
	movq mm0,[esi+32]
	movq mm1,[esi+40]
	movq [edi+32],mm0
	movq [edi+40],mm1
	movq mm2,[esi+48]
	movq mm3,[esi+56]
	movq [edi+48],mm2
	movq [edi+56],mm3

	add esi,64
	add edi,64
	dec ecx
	jnz $memcpy_1		; last 64-byte g_DVDGlobals.block?

$memcpy_2_2:
	mov ecx,ebx
$memcpy_2_1:
	shr ecx,2		; dword count
	and ecx,1111b
//memcpy_2:
	neg ecx
	add ecx,offset $done
	jmp ecx			; jump to array of movsd's

align 16
$memcpy_nt_1:			; 64-byte blocks, uncached copy


//	prefetch [esi + (200*64/34+192)] //Intel Compiler doesn't like this either.
									 //01/26/02 int21h
	movq mm0,[esi+0]
	add edi,64
	movq mm1,[esi+8]
	add esi,64
	movq mm2,[esi-48]
	movntq [edi-64], mm0
	movq mm0,[esi-40]
	movntq [edi-56], mm1
	movq mm1,[esi-32]
	movntq [edi-48], mm2
	movq mm2,[esi-24]
	movntq [edi-40], mm0
	movq mm0,[esi-16]
	movntq [edi-32], mm1
	movq mm1,[esi-8]
	movntq [edi-24], mm2
	movntq [edi-16], mm0

	dec ecx
	movntq [edi-8], mm1
	jnz $memcpy_nt_1	; last 64-byte g_DVDGlobals.block?

	mov ecx,ebx
	shr ecx,2		; dword count
	and ecx,1111b
	neg ecx
	add ecx,offset $done
	jmp ecx			; jump to array of movsd's

align 4
	movsd
	movsd			; perform last 1-15 dword copies
	movsd
	movsd
	movsd
	movsd
	movsd
	movsd
	movsd
	movsd			; perform last 1-7 dword copies
	movsd
	movsd
	movsd
	movsd
	movsd
	movsd

$done:				; dword aligned from before movsd's
	mov ecx,ebx
	and ecx,11b		; byte count
	jz $final
	rep movsb		; do last 1..3 bytes

$final: //femms			; clean up mmx //Intel Compiler hates this too.
										//01/26/02 int21h
        }
  return dest;
}

/* taken from AMD's CPUID_EX example */

static unsigned int get_cpu_feature_flags()
{
  unsigned int result    = 0;
  unsigned int signature = 0;
  char vendor[13]        = "UnknownVendr";  /* Needs to be exactly 12 chars */

  /* Define known vendor strings here */

  char vendorAMD[13]     = "AuthenticAMD";  /* Needs to be exactly 12 chars */
  /*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Step 1: Check if processor has CPUID support. Processor faults
    ;; with an illegal instruction exception if the instruction is not
    ;; supported. This step catches the exception and immediately returns
    ;; with feature string bits with all 0s, if the exception occurs.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
  #define cpuid __asm _emit 0x0F __asm _emit 0xA2

  __try {
	_asm {
	 xor    eax, eax
	 xor    ebx, ebx
	 xor    ecx, ecx
	 xor    edx, edx
	 cpuid
	}
  }

  __except (EXCEPTION_EXECUTE_HANDLER)
  {
    return (0);
  }

  result |= FEATURE_CPUID;

  _asm {
         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         //;; Step 2: Check if CPUID supports function 1 (signature/std features)
         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

         xor     eax, eax                       //; CPUID function #0
         cpuid                                  //; largest std func/vendor string
         mov     dword ptr [vendor], ebx        //; save
         mov     dword ptr [vendor+4], edx      //;  vendor
         mov     dword ptr [vendor+8], ecx      //;   string
         test    eax, eax                       //; largest standard function==0?
         jz      $no_standard_features          //; yes, no standard features func
         or      [result], FEATURE_STD_FEATURES //; does have standard features

         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         //;; Step 3: Get standard feature flags and signature
         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

         mov     eax, 1                        //; CPUID function #1
         cpuid                                 //; get signature/std feature flgs
         mov     [signature], eax              //; save processor signature

         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         //;; Step 4: Extract desired features from standard feature flags
         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

         //;; Check for time stamp counter support

         mov     ecx, CPUID_STD_TSC            //; bit 4 indicates TSC support
         and     ecx, edx                      //; supports TSC ? CPUID_STD_TSC:0
         neg     ecx                           //; supports TSC ? CY : NC
         sbb     ecx, ecx                      //; supports TSC ? 0xffffffff:0
         and     ecx, FEATURE_TSC              //; supports TSC ? FEATURE_TSC:0
         or      [result], ecx                 //; merge into feature flags

         //;; Check for MMX support

         mov     ecx, CPUID_STD_MMX            //; bit 23 indicates MMX support
         and     ecx, edx                      //; supports MMX ? CPUID_STD_MMX:0
         neg     ecx                           //; supports MMX ? CY : NC
         sbb     ecx, ecx                      //; supports MMX ? 0xffffffff:0
         and     ecx, FEATURE_MMX              //; supports MMX ? FEATURE_MMX:0
         or      [result], ecx                 //; merge into feature flags

         //;; Check for CMOV support

         mov     ecx, CPUID_STD_CMOV           //; bit 15 indicates CMOV support
         and     ecx, edx                      //; supports CMOV?CPUID_STD_CMOV:0
         neg     ecx                           //; supports CMOV ? CY : NC
         sbb     ecx, ecx                      //; supports CMOV ? 0xffffffff:0
         and     ecx, FEATURE_CMOV             //; supports CMOV ? FEATURE_CMOV:0
         or      [result], ecx                 //; merge into feature flags

         //;; Check support for P6-style MTRRs

         mov     ecx, CPUID_STD_MTRR           //; bit 12 indicates MTRR support
         and     ecx, edx                      //; supports MTRR?CPUID_STD_MTRR:0
         neg     ecx                           //; supports MTRR ? CY : NC
         sbb     ecx, ecx                      //; supports MTRR ? 0xffffffff:0
         and     ecx, FEATURE_P6_MTRR          //; supports MTRR ? FEATURE_MTRR:0
         or      [result], ecx                 //; merge into feature flags

         //;; Check for initial SSE support. There can still be partial SSE
         //;; support. Step 9 will check for partial support.

         mov     ecx, CPUID_STD_SSE            //; bit 25 indicates SSE support
         and     ecx, edx                      //; supports SSE ? CPUID_STD_SSE:0
         neg     ecx                           //; supports SSE ? CY : NC
         sbb     ecx, ecx                      //; supports SSE ? 0xffffffff:0
         and     ecx, (FEATURE_MMXEXT+FEATURE_SSEFP) //; supports SSE ?
                                               //; FEATURE_MMXEXT+FEATURE_SSEFP:0
         or      [result], ecx                 //; merge into feature flags

         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         //;; Step 5: Check for CPUID extended functions
         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

         mov     eax, 0x80000000                //; extended function 0x80000000
         cpuid                                  //; largest extended function
         cmp     eax, 0x80000000                //; no function > 0x80000000 ?
         jbe     $no_extended_features          //; yes, no extended feature flags
         or      [result], FEATURE_EXT_FEATURES //; does have ext. feature flags

         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         //;; Step 6: Get extended feature flags
         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

         mov     eax, 0x80000001               //; CPUID ext. function 0x80000001
         cpuid                                 //; EDX = extended feature flags

         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         //;; Step 7: Extract vendor independent features from extended flags
         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

         //;; Check for 3DNow! support (vendor independent)

         mov     ecx, CPUID_EXT_3DNOW          //; bit 31 indicates 3DNow! supprt
         and     ecx, edx                      //; supp 3DNow! ?CPUID_EXT_3DNOW:0
         neg     ecx                           //; supports 3DNow! ? CY : NC
         sbb     ecx, ecx                      //; supports 3DNow! ? 0xffffffff:0
         and     ecx, FEATURE_3DNOW            //; support 3DNow!?FEATURE_3DNOW:0
         or      [result], ecx                 //; merge into feature flags

         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         //;; Step 8: Determine CPU vendor
         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

         lea     esi, vendorAMD                //; AMD's vendor string
         lea     edi, vendor                   //; this CPU's vendor string
         mov     ecx, 12                       //; strings are 12 characters
         cld                                   //; compare lowest to highest
         repe    cmpsb                         //; current vendor str == AMD's ?
         jnz     $not_AMD                      //; no, CPU vendor is not AMD

         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         //;; Step 9: Check AMD specific extended features
         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

         mov     ecx, CPUID_EXT_AMD_3DNOWEXT   //; bit 30 indicates 3DNow! ext.
         and     ecx, edx                      //; 3DNow! ext?
         neg     ecx                           //; 3DNow! ext ? CY : NC
         sbb     ecx, ecx                      //; 3DNow! ext ? 0xffffffff : 0
         and     ecx, FEATURE_3DNOWEXT         //; 3DNow! ext?FEATURE_3DNOWEXT:0
         or      [result], ecx                 //; merge into feature flags

         test    [result], FEATURE_MMXEXT      //; determined SSE MMX support?
         jnz     $has_mmxext                   //; yes, don't need to check again

         //;; Check support for AMD's multimedia instruction set additions

         mov     ecx, CPUID_EXT_AMD_MMXEXT     //; bit 22 indicates MMX extension
         and     ecx, edx                      //; MMX ext?CPUID_EXT_AMD_MMXEXT:0
         neg     ecx                           //; MMX ext? CY : NC
         sbb     ecx, ecx                      //; MMX ext? 0xffffffff : 0
         and     ecx, FEATURE_MMXEXT           //; MMX ext ? FEATURE_MMXEXT:0
         or      [result], ecx                 //; merge into feature flags

      $has_mmxext:

         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         //;; Step 10: Check AMD-specific features not reported by CPUID
         //;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

         //;; Check support for AMD-K6 processor-style MTRRs

         mov     eax, [signature] 	//; get processor signature
         and     eax, 0xFFF 		//; extract family/model/stepping
         cmp     eax, 0x588 		//; CPU < AMD-K6-2/CXT ? CY : NC
         sbb     edx, edx 		//; CPU < AMD-K6-2/CXT ? 0xffffffff:0
         not     edx 			//; CPU < AMD-K6-2/CXT ? 0:0xffffffff
         cmp     eax, 0x600 		//; CPU < AMD Athlon ? CY : NC
         sbb     ecx, ecx 		//; CPU < AMD-K6 ? 0xffffffff:0
         and     ecx, edx 		//; (CPU>=AMD-K6-2/CXT)&&
					//; (CPU<AMD Athlon) ? 0xffffffff:0
         and     ecx, FEATURE_K6_MTRR 	//; (CPU>=AMD-K6-2/CXT)&&
					//; (CPU<AMD Athlon) ? FEATURE_K6_MTRR:0
         or      [result], ecx 		//; merge into feature flags

         jmp     $all_done 		//; desired features determined

      $not_AMD:

         /* Extract features specific to non AMD CPUs */

      $no_extended_features:
      $no_standard_features:
      $all_done:
  }
   /* The FP part of SSE introduces a new architectural state and therefore
      requires support from the operating system. So even if CPUID indicates
      support for SSE FP, the application might not be able to use it. If
      CPUID indicates support for SSE FP, check here whether it is also
      supported by the OS, and turn off the SSE FP feature bit if there
      is no OS support for SSE FP.

      Operating systems that do not support SSE FP return an illegal
      instruction exception if execution of an SSE FP instruction is performed.
      Here, a sample SSE FP instruction is executed, and is checked for an
      exception using the (non-standard) __try/__except mechanism
      of Microsoft Visual C.
   */

   return (result);
}

/*extern "C" {*/

	void memcopy(void *dest, void *src, int n) {

 // if( _3dnow_d )
//	  memcopy_amd(dest,src, (size_t)n);
// else
	__asm
	{
      mov  edi,dest
      mov  esi,src
      mov  ecx,n
      push ecx
	  
	  // align 64 byte
      and  ecx,63
      rep  movsb
      pop  ecx
	  shr  ecx,6
	  cmp  ecx,0
	  je   _mmx_copy_end

_mmx_copy:

	movq mm0,[esi+0]
	movq mm1,[esi+8]
	movq [edi+0],mm0
	movq [edi+8],mm1
	movq mm2,[esi+16]
	movq mm3,[esi+24]
	movq [edi+16],mm2
	movq [edi+24],mm3
	movq mm0,[esi+32]
	movq mm1,[esi+40]
	movq [edi+32],mm0
	movq [edi+40],mm1
	movq mm2,[esi+48]
	movq mm3,[esi+56]
	movq [edi+48],mm2
	movq [edi+56],mm3

	add esi,64
	add edi,64
	dec ecx
    jnz _mmx_copy

_mmx_copy_end:
	  emms

	}
  
}
