/*
 Access Pentium/AMD specific features, e.g. RDTSC, CPUSERNO

copyright (c) 2005-2009 Roedy Green, Canadian Mind Products
may be copied and used freely for any purpose but military.

Roedy Green
Canadian Mind Products
#101 - 2536 Wark Street
Victoria, BC Canada
V8T 4G8
tel: (250) 361-9093
roedy g at mindprod dotcom
http://mindprod.com

version History

1.1 2005-07-30 - ANT build, consistent naming.
1.2 2006-03-06 - reformat with IntelliJ, add Javadoc.
1.3 2007-06-03 - add pad and icon
1.4 2008-09-18 - add cpuSerNo64 cpuSerNo96 display methods for serial number.
1.5 2008-09-23 - convert to jDK 1.5, deal with C++ missing DLLs.
*/
package com.mindprod.pentium;

import com.mindprod.common11.StringTools;

/**
 * Access Pentium/AMD specific features. Uses JNI native C++/ASM code that only works on Windows
 *
 * @author Roedy Green, Canadian Mind Products
 * @version 1.5 2008-09-23 - document need for C++ runtime library install.
 */
public final class Pentium
    {
    // ------------------------------ FIELDS ------------------------------

    /**
     * undisplayed copyright notice
     *
     * @noinspection UnusedDeclaration
     */
    public static final String EMBEDDED_COPYRIGHT =
            "copyright (c) 2005-2009 Roedy Green, Canadian Mind Products, http://mindprod.com";

    private static final String RELEASE_DATE = "2008-09-23";

    /**
     * embedded version string.
     */
    public static final String VERSION_STRING = "1.5";
    // -------------------------- PUBLIC STATIC METHODS --------------------------

    /**
     * Get the brand/model of the CPU.
     *
     * @return brand, e.g. "AMD Athlon(TM) XP 2000+"
     */
    public static native String cpuIdBrand();

    /**
     * Get the instruction set family of the CPU.
     *
     * @return family, possibly null if not Pentium.
     */
    public static native int cpuIdFamily();

    /**
     * Get the model of the CPU.
     *
     * @return model, possibly 0 if not Pentium.
     */
    public static native int cpuIdModel();

    /**
     * Get the stepping ID of the CPU.
     *
     * @return stepping ID.
     */
    public static native int cpuIdStep();

    /**
     * Get the vendor ID of the CPU.
     *
     * @return vendor, "GenuineIntel" for Intel chips, "AuthenticAMD" for AMD.
     */
    public static native String cpuIdVendor();

    /**
     * Get the 64-bit cpu Serial number for Pentium
     *
     * @return cpu serial number, possibly 0 if not Pentium.
     */
    public static native long cpuSerNo64();

    /**
     * Get the 96-bit cpu serial number for Xeon.
     *
     * @param select 0 for most significant 32 bits, 1 for middle, 2 for least significant.
     * @return family, possibly 0 if not Xeon.
     */
    public static native int cpuSerNo96( int select );

    /**
     * Get the time stamp counter register with the RDTSC instruction giving the count of machine cycles since last
     * boot. This is very high resolution timer, with frequency matching the cpu clock crystal, not any defined time
     * unit.
     *
     * @return RDTSC Timestamp counter.
     */
    public static native long rdtsc();

    // -------------------------- STATIC METHODS --------------------------

    /**
     * display a 64 bit serial number in hex in 4 groups of 4 hex digits
     *
     * @param serno serial number to display
     * @return hex string equivalent.
     */
    private static String display64BitSerialNumber( long serno )
        {
        final int ms = ( int ) ( serno >>> 32 );
        final int ls = ( int ) serno;
        final String mss = StringTools.toLzHexString( ms, 8 );
        final String lss = StringTools.toLzHexString( ls, 8 );
        final StringBuffer sb = new StringBuffer( 19 );
        sb.append( mss.substring( 0, 4 ) );
        sb.append( '-' );
        sb.append( mss.substring( 4, 8 ) );
        sb.append( '-' );
        sb.append( lss.substring( 0, 4 ) );
        sb.append( '-' );
        sb.append( lss.substring( 4, 8 ) );
        return sb.toString().toUpperCase();
        }

    /**
     * display a 96 bit serial number in hex in 6 groups of 4 hex digits
     *
     * @param serno serial number to display, three 32 bit numbers msb in serno[0]
     * @return hex string equivalent.
     */
    private static String display96BitSerialNumber( int[] serno )
        {
        final StringBuffer sb = new StringBuffer( 30 );
        for ( int i = 0; i < 3; i++ )
            {
            final String hex = StringTools.toLzHexString( serno[ i ], 8 );
            sb.append( hex.substring( 0, 4 ) );
            sb.append( '-' );
            sb.append( hex.substring( 4, 8 ) );
            sb.append( '-' );
            }
        sb.setLength( 29 );
        return sb.toString().toUpperCase();
        }

    // --------------------------- main() method ---------------------------

    /**
     * test harness
     *
     * @param args not used
     */
    public static void main( String[] args )
        {
        // This needs to be done only once. It could go in a static init block.
        // For debugging it is better to put it here.
        // Get DLL loaded from somewhere on java.library path.
        System.loadLibrary( "nativepentium" );
        // if have troubles change this code to use load(
        // "E:\\com\\mindprod\\pentium\\nativepentium.dll" );

        System.out.println( "cpuIdVendor:" + Pentium.cpuIdVendor() );
        System.out.println( "cpuIdBrand:" + Pentium.cpuIdBrand() );
        System.out.println( "cpuIdStep:" + StringTools.toLzHexString( Pentium.cpuIdStep(), 2 ) );
        System.out.println( "cpuIdModel:" + StringTools.toLzHexString( Pentium.cpuIdModel(), 2 ) );
        System.out.println( "cpuIdFamily:" + StringTools.toLzHexString( Pentium.cpuIdModel(), 2 ) );
        System.out.println( "cpuSerNo64:" + display64BitSerialNumber( Pentium.cpuSerNo64() ) );

        // collect Xeon serial number in three pieces.
        final int[] serno = new int[3];
        serno[ 0 ] = Pentium.cpuSerNo96( 0 );
        serno[ 1 ] = Pentium.cpuSerNo96( 1 );
        serno[ 2 ] = Pentium.cpuSerNo96( 2 );

        System.out.println( "cpuSerNo96:" + display96BitSerialNumber( serno ) );
        System.out.println( "rdtsc:" + Long.toHexString( Pentium.rdtsc() ) );
        }
    }