//
// CPU detection routines for rc5 distributed.net effort
// Written in a dark and stormy night by Cyrus Patel <cyp@fb14.uni-mainz.de>
//
// Updated on April 5th, 1998 by Mike Silbersack and Dave Hart to
// properly detect K6 and 6x86 processors.  6x86 may still not work right
// on some processors because the BIOS may be disabling CPUID.  In this
// case, it'll be IDed as a Cx486.
//
// Updated on April 20th, 1997 by Mike Silbersack to include much greater
// accuracy for all CPUID processors.  Should detect nearly everything
// x86 made in recent history.
//
// _x86ident:  return u32
//
//             hiword = manufacturer ID (also for 386/486)
//                      GenuineIntel = 'eG'
//                      AuthenticAMD = 'uA'
//                      CyrixInstead = 'yC'
//                      NexGenDriven = 'eN'
//                      CentaurHauls = 'eC'
//                      UMC UMC UMC  = 'MU'
//             (Just using the two chars in BX to make it easy)
//             (It'll all interpreted in the C part anyway)
//             loword = 30 for 386
//                    = 40 for 486
//                      CPUID code from processor for CPUID processors
//                      (interpreted by C calling procedure)
//
// Tested implementation: see x86ident.txt
//
// Release note: (Jan 16, 1998)
//
//             AMD 486 detection is dodgy. (I don't have one)
//                     Does it use a non-generic 386/486 core?
//                     If so, I need 2^pi-1 result for that processor.
//
//                     push    eax             // make buffer space
//                     finit                   // initialize fpu
//                     fldpi                   // loads pi
//                     f2xm1                   // 2^pi-1 == "undefined"
//                     fstp    dword ptr [esp] // store in slot
//                     pop     eax             // get the result
//
typedef unsigned long u32;

extern "C" u32 x86ident( void );
static u32 __savident = 0;

__declspec(naked) u32 x86ident( void )
{
asm {
				mov     eax,[__savident]
                or      eax, eax
                jz      _Getx86FamID
                ret

_Getx86FamID:   push    ebx             // save ebx from damage
                pushf                   // save flags from damage

                pushf                   // copy EFLAGS
                pop     ecx             // ... into ecx

                // an interrupt would change the AC bit, so loop 'n'
                // times, incrementing a state counter for each state
                xor     ebx, ebx        // bl=is386 count, bh=not386 count
                mov     dl, 31          // the number of times we check
_386next:       mov     eax, ecx        // copy original EFLAGS
                xor     eax, 1 SHL 18   // toggle AC bit in eflags
                push    eax             // copy modified eflags
                popf                    // ... to EFLAGS
                pushf                   // copy (possible modified) EFLAGS
                pop     eax             // ... back to eax
                sub     eax, ecx        // will be 386 if no change
                cmp     eax, 1          // set carry if 386
                sbb     eax, eax        // make 0xffffffff if carry set
                dec     dl              // decrement loop count
                jz      _386eval        // break if done all
                inc     bl              // increment the is386 count
                or      al,al           // clear zero if is386
                jnz     _386next        // go loop if hit
                dec     bl              // undo last change
                inc     bh              // increment the not386 count
                jmp      _386next  // go loop for other hit
_386eval:       cmp     bl, bh          // is386 count > not386 count
                mov     eax, ('nI' SHL 16) + 0x30 // assume i386
                ja      _nocpuidxit     // exit if so
		
                pushf 		// added
                pop ecx 	// added
                mov     eax, ecx        // copy original flags
                xor     eax, 0x200000    // try to toggle ID bit
                push    eax             // copy modified eflags
                popf                    // ... to EFLAGS
                pushf                   // push back flags
                pop     eax             // ... into eax
                xor     eax, ecx        // must be 486 if no change
                jnz     _cpuidGI        // proceed if change

                xor     ah, ah          // for clearing flags
                sahf                    // scrub lower 8 bits (exc. bit 1)
                lahf                    // reload flags
                mov     ch, ah          // save copy
                mov     ax, 5           // 5/2 constant
                mov     cl, 2           // divisor
                div     cl              // won't change flags on cyrix
                lahf                    // load flags
                sub     ah, ch          // no changes?
                mov     eax, ('yC' SHL 16) + 0x40// assume cyrix 486 CPU
                jz      _nocpuidxit     // exit if cyrix cpu
                // could use Cyrix 'Device ID registers' to get
                // exact name and model
                // 6x86 MAY return 486 here...

                fninit                  // try to init coprocessor
                mov     ax, 0x5a5a       // rigged status word
                fnstsw  ax              // try to store
                or      ax, ax          // have an fpu?
                mov     eax, ('nI' SHL 16) + 0x40// no copro so must be intel
                jnz     _nocpuidxit     // if no fpu, go exit

                push    eax             // make buffer space
                finit                   // initialize fpu
                fldpi                   // loads pi
                f2xm1                   // 2^pi - 1 => "undefined" number
                fstp    dword ptr [esp] // store in slot
                pop     ecx             // get the result
                cmp     ecx, 0x3FC9      // cyrix=>3FC9h, amd=>???
                mov     eax, ('yC' SHL 16) + 0x40// assume cx486 CPU
                jz      _nocpuidxit     // exit if cyrix
                cmp     ecx, 0x3E46FEAC  // pi as a dword?
                mov     eax, ('nI' SHL 16) + 0x40// assume intel CPU
                jz      _nocpuidxit     // exit if intel (>=P5 it's (u32)PI)
                mov     eax, ('uA' SHL 16) + 0x40// assume amd CPU
_nocpuidxit:    jmp     _end            // go exit

_cpuidGI:       xor     eax, eax        // cpuid function zero
                cpuid                   // do the honky tonk

                mov     cx, bx          // move maker id
                xchg    ecx, eax        // save eax, set mfg id
                shl     eax, 16         // move it up
                mov     al, 0x40        // assume 486
                or      ecx, ecx        // only supports CPUID fxn 0?
                jz      _end            // exit if so

                push    bx              // save maker code
                mov     eax, 1          // family/model/stepping/features
                cpuid                   // do it
                mov     ecx, eax        // copy family/model/stepping bits

                xor     eax, eax        // zero the register
                pop     ax              // restore maker code
                shl     eax, 16         // shift it to the left 16 bits
                and     ecx, 0x0ffff     // mask out high bits, just in case
		or      eax, ecx        // add to eax's low 16 bits
		jmp     _end            // done, return
                // C calling routine interprets family/proc id

_end:           mov     [__savident],eax// save it for next time
                popf
                pop     ebx
                retn

}
}
