/*
 *
 * 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, 1998 by Mike Silbersack to include much greater
 * accuracy for all CPUID processors.  Should detect nearly everything
 * x86 made in recent history.
 *
 * Converted by hand on May 30th, 1998 to AT&T format by
 * Remi Guyomarch <rguyom@mail.dotcom.fr>
 *
 * $Log: x86ident.S,v $
 * Revision 1.8  1998/10/31 22:04:55  silby
 * Added freebsd-specific skipping of alignment.  Hopefully it won't break anything. :)
 *
 * Revision 1.7  1998/06/15 23:46:53  daa
 * add a .align 4 after the _Id: to ensure the rest of .data is long aligned
 *
 * Revision 1.6  1998/06/14 11:36:46  remi
 * Added "@(#)$Id: x86ident.S,v 1.8 1998/10/31 22:04:55 silby Exp $".
 *
 * Revision 1.5  1998/06/14 09:57:18  skand
 * rename from .s so preprocessor magic can be used.
 * For one, define "cpuid" as a .byte sequence for BSDI, and a bit of
 * other magic.
 *
 * Revision 1.4  1998/06/13 09:02:19  remi
 * Fixed crash with 486s.
 * Replaced .align with .balign, needed for linux-aout and probably others.
 * (.balign 16 means align on a 16-byte boundary)
 * Added "$Log" in comments.
 *
 *
 * _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
 */

.data
_id:	        .ascii "@(#)$Id: x86ident.S,v 1.8 1998/10/31 22:04:55 silby Exp $"
		.byte 0
		.align 4
_savident:	.long 0              // avoid serialization caused by CPUID

#if defined(__bsdi__)
#define cpuid .byte 0x0f, 0xa2
#endif

#if defined(__FreeBSD__)
#define NO_BALIGN
#endif

#if !defined(NO_BALIGN)
.balign 16
#endif
.globl  _x86ident
.globl	x86ident
_x86ident:
x86ident:
		movl     _savident,%eax
                orl      %eax, %eax
                jz       _Getx86FamID
                ret

_Getx86FamID:   pushl    %ebx           // save ebx from damage
                pushf                   // save flags from damage

                pushf                   // copy EFLAGS
                popl     %ecx           // ... into ecx

                // an interrupt would change the AC bit, so loop 'n'
                // times, incrementing a state counter for each state
                xorl    %ebx, %ebx      // bl=is386 count, bh=not386 count
                movb    $31, %dl        // the number of times we check
_386next:       movl    %ecx, %eax      // copy original EFLAGS
                xorl    $(1 << 18),%eax // toggle AC bit in eflags
                pushl   %eax            // copy modified eflags
                popf                    // ... to EFLAGS
                pushf                   // copy (possible modified) EFLAGS
                popl    %eax            // ... back to eax
                subl    %ecx, %eax      // will be 386 if no change
                cmpl    $1, %eax        // set carry if 386
                sbbl    %eax, %eax      // make 0xffffffff if carry set
                decb    %dl             // decrement loop count
                jz      _386eval        // break if done all
                incb    %bl             // increment the is386 count
                orb     %al,%al         // clear zero if is386
                jnz     _386next        // go loop if hit
                decb    %bl             // undo last change
                incb    %bh             // increment the not386 count
                jmp     _386next        // go loop for other hit
_386eval:       cmpb    %bh, %bl        // is386 count > not386 count
                movl    $(('n << 24) + ('I << 16) + 0x30), %eax // assume i386
                ja      _nocpuidxit     // exit if so
		
                pushf                   // added
                popl    %ecx            // added
                movl    %ecx, %eax      // copy original flags
                xorl    $0x200000,%eax  // try to toggle ID bit
                pushl   %eax            // copy modified eflags
                popf                    // ... to EFLAGS
                pushf                   // push back flags
                popl    %eax            // ... into eax
                xor     %ecx,%eax       // must be 486 if no change
                jnz     _cpuidGI        // proceed if change

                xorb    %ah, %ah        // for clearing flags
                sahf                    // scrub lower 8 bits (exc. bit 1)
                lahf                    // reload flags
                movb    %ah, %ch        // save copy
                movw    $5, %ax         // 5/2 constant
                movb    $2, %cl         // divisor
                divb    %cl             // won't change flags on cyrix
                lahf                    // load flags
                subb    %ch, %ah        // no changes?
                movl    $(('y << 24) + ('C << 16) + 0x40), %eax // 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
                movw    $0x5a5a, %ax    // rigged status word
                fnstsw  %ax             // try to store
                orw     %ax, %ax        // have an fpu?
                movl    $(('n << 24) + ('I << 16) + 0x40), %eax // no copro so must be intel
                jnz     _nocpuidxit     // if no fpu, go exit

                pushl   %eax            // make buffer space
                finit                   // initialize fpu
                fldpi                   // loads pi
                f2xm1                   // 2^pi - 1 => "undefined" number
                fstps   (%esp)	        // store in slot
                popl    %ecx            // get the result
                cmpl    $0x3FC9, %ecx   // cyrix=>3FC9h, amd=>???
                movl    $(('y << 24) + ('C << 16) + 0x40), %eax // assume cx486 CPU
                jz      _nocpuidxit     // exit if cyrix
                cmpl    $0x3E46FEAC, %ecx // pi as a dword?
                movl    $(('n << 24) + ('I << 16) + 0x40), %eax // assume intel CPU
                jz      _nocpuidxit     // exit if intel (>=P5 it's (u32)PI)
                movl    $(('u << 24) + ('A << 16) + 0x40), %eax // assume amd CPU
_nocpuidxit:    jmp     _end            // go exit

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

                movw    %bx, %cx        // move maker id
                xchgl   %eax, %ecx      // save eax, set mfg id
                shll    $16, %eax       // move it up
                movb    $0x40, %al      // assume 486
                orl     %ecx, %ecx      // only supports CPUID fxn 0?
                jz      _end            // exit if so

                pushw   %bx             // save maker code
                movl    $1, %eax        // family/model/stepping/features
                cpuid                   // do it
                movl    %eax, %ecx      // copy family/model/stepping bits

                xorl    %eax, %eax      // zero the register
                popw    %ax             // restore maker code
                shll    $16, %eax       // shift it to the left 16 bits
                andl    $0xffff, %ecx   // mask out high bits, just in case
		orl     %ecx, %eax      // add to eax's low 16 bits
		jmp     _end            // done, return
                // C calling routine interprets family/proc id

_end:           movl    %eax, _savident // save it for next time
                popf
                popl    %ebx
                ret

