unit uCPUSpeed;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  COSBI_Common, StdCtrls, ExtCtrls, ComCtrls, TeEngine, Series, TeeProcs,
  Chart, uCOSBI_SystemInfo, CosbiLogFile, CosbiCpuid, jpeg;

type
  enumRunPriority = (eRPNormal, eRPHigh, eRPRealTime);
  enumCpuVendor = (eIntel, eAMD, eVIA, eTransmeta);
  enumPanels = ( eGraph, eGetCpuSpeed, eViaRatio );
  setPanels = set of enumPanels;
  
  TfrmCPUSpeed = class(TForm)
    lblClockspeed: TLabel;
    chartClockSpeed: TChart;
    StatusBar1: TStatusBar;
    pnlGraphOptions: TPanel;
    ckbxAuto: TCheckBox;
    Label6: TLabel;
    tbTime: TTrackBar;
    pnlGetCpuSpeed: TPanel;
    btnGetCPUSpeed: TButton;
    Label1: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    btnCounter: TButton;
    edTicks: TEdit;
    pnlVia: TPanel;
    lblClockRatio: TLabel;
    cboViaRatio: TComboBox;
    btnSetClockRatio: TButton;
    tmrSpeedUpdate: TTimer;
    btnSave: TButton;
    btnSaveGraph: TButton;
    pnlLogCpuSpeed: TPanel;
    btnLogCpuSpeed: TButton;
    Label7: TLabel;
    cboLogPeriod: TComboBox;
    btnSaveLogFile: TButton;
    SaveDialog1: TSaveDialog;
    Label8: TLabel;
    cboxGraph: TCheckBox;
    cboxGetCpuSpeed: TCheckBox;
    cboxVIA: TCheckBox;
    Bevel1: TBevel;
    Series1: TLineSeries;
    TimerLog: TTimer;
    btnScrollLeft: TButton;
    btnScrollRight: TButton;
    Button1: TButton;
    lblUpdatePeriod: TLabel;
    btnLoadDownCPU: TButton;
    procedure btnGetCPUSpeedClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure btnCounterClick(Sender: TObject);
    procedure tmrSpeedUpdateTimer(Sender: TObject);
    procedure ckbxAutoClick(Sender: TObject);
    procedure btnSaveClick(Sender: TObject);
    procedure tbTimeChange(Sender: TObject);
    procedure btnSetClockRatioClick(Sender: TObject);
    procedure cboViaRatioChange(Sender: TObject);
    procedure cboxGraphClick(Sender: TObject);
    procedure cboxGetCpuSpeedClick(Sender: TObject);
    procedure cboxVIAClick(Sender: TObject);
    procedure btnLogCpuSpeedClick(Sender: TObject);
    procedure btnSaveLogFileClick(Sender: TObject);
    procedure TimerLogTimer(Sender: TObject);
    procedure btnScrollLeftClick(Sender: TObject);
    procedure btnScrollRightClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure btnLoadDownCPUClick(Sender: TObject);
  private
    { Private declarations }
    fi64_CPU_Ticks_per_sec  : int64;
    fi64_StartTick          : int64;
    fi64_StopTick           : int64;
    fi64_StartTickTmr       : int64;
    fi64_StopTickTmr        : int64;
    fiElapsedTime           : integer;
    fdClockRatio            : double;
    fSeries                 : TChartSeries;
    fCpuidLevel0            : TCpuidLevel0;
    fCpuidCentaur           : TCpuidCentaur;
    fIsCentaur              : Boolean;
    fIsTransmeta            : Boolean; // Transmeta needs to use a different method
    fIsC7                   : Boolean; // Centaur C7 expose clock through CPUID
    fCpuidTransmetaPerformanceData : TCpuidTransmetaPerformanceData;
    fCpuidCentaurPerformanceInfo : TCpuidCentaurPerformanceInfo;
    StopWatch               : TStopWatch;
    StopWatchLog            : TStopWatch;
    fTicks                  : integer;
    fMaxClockSpeed          : int64;
    fMaxClockSpeedAxis      : integer;
    fShowPanels             : setPanels;
    fCpuVendor              : enumCpuVendor;
    fLogArray               : array[0..ONE_MILLION] of int64;
    fLogArrayIndex          : integer;
    fLogStartTick           : int64;
    fLogStopTick            : int64;
    fNbrOfThreadsRunning    : integer;
    procedure SetClockRatio( Value : double );
    procedure ParseCommandLine;
    procedure InitializeFieldVariables;
    procedure StartLogging;
    procedure StopLogging;
    procedure UpdateLog;
  public
    { Public declarations }
    function GetCPUSpeed( var ai64TickCount : int64;
                          var adTime : double ): int64;
    function ChangeClockRatio: integer;
    function ClockRatioSettable : Boolean;
    procedure UpdateCPUSpeed;
    property ClockRatio : double read fdClockRatio write SetClockRatio;
    procedure EnableClockRatio;
    procedure DisableClockRatio;
    function DetectCpuVendor : enumCpuVendor;
    procedure ShowPanels( asetPanels : setPanels );
    function GetTransmetaClockSpeed : integer;
    function GetC7ClockSpeed : integer;
    procedure SetPanelVisibility;
    procedure ShowCpuSpeedSample;
    function DetermineMaximumClockSpeed: int64;
    procedure InitializeChart;
    procedure SaveChartData;
    procedure SaveLogData;
    procedure StatusBar0Update( astrMessage : string );
    procedure StatusBar1Update( astrMessage : string );
    procedure ChartSampleTimeChanged;
    procedure ShowHideCpuSpeedGraph( abVisible : Boolean );
    procedure SetPriority( aRunPriority : enumRunPriority ) ;
    procedure ThreadDone(Sender: TObject);
  end;


  // Class name: TFibThread
  // Author: Van Smith
  // Date: January 16, 2004
  TFibThread = class(TThread)
  private
    Fn: integer;
  protected
    procedure Execute; override;
    function fib(n : integer): integer;
  public
    constructor Create(n: integer);
  end; // TFibThread............................................................

var
  frmCPUSpeed: TfrmCPUSpeed;

implementation

{$R *.DFM}

const
  VERSION = 'v1.0a';
  CHANGE_RATIO_PROGRAM = 'ChgRatio.exe';
  WINDOW_SIZE = 60;
  ALL_PANELS = [ eGraph, eGetCpuSpeed, eViaRatio ];
  FIB_NUMBER = 38;

// TFibThread...................................................................
constructor TFibThread.Create(n: integer);
begin
  Fn := n;
  FreeOnTerminate := True;
  inherited Create(TRUE);  // creates suspended
end;

procedure TFibThread.Execute;
var
  n : integer;
begin
  n := Fib(Fn);
end; //procedure TFibThread.Execute;

function TFibThread.Fib( n : integer ) : integer;
begin
  if Terminated then begin
    result := -1;
    Exit;
  end; // if
  if n > 2 then
      result := Fib( n - 1 ) + Fib( n - 2 )
  else
      result := 1;
end;
// TFibThread ends..............................................................

procedure TfrmCPUSpeed.FormCreate(Sender: TObject);
var
  ldClockSpeed : double;
begin
  Height := 450;
  InitializeFieldVariables;
  if ParamCount > 0 then begin
    ParseCommandLine;
  end; // if
  fMaxClockSpeed := DetermineMaximumClockSpeed;
  ldClockSpeed := fMaxClockSpeed;
  if fIsC7 or fIsTransmeta then begin
    StatusBar1Update( 'Max CPU speed: ~' +
      FloatToStrF( ldClockSpeed, ffNumber, 11, 0 )  + ' MHz' );
  end else begin
    StatusBar1Update( 'Max CPU speed: ~' +
      FloatToStrF( ldClockSpeed, ffNumber, 11, 0 )  + ' Hz' );
  end; // if
  InitializeChart;
  caption := caption + ' ' + VERSION;
  SetPanelVisibility;
  ChartSampleTimeChanged;
  tmrSpeedUpdate.Enabled := TRUE;
end; // procedure TfrmCPUSpeed.FormCreate(Sender: TObject);

function TfrmCPUSpeed.GetCPUSpeed(var ai64TickCount : int64;
                                  var adTime : double ): int64;
var
  li64StartTick    : Int64;
  li64StopTick     : Int64;
begin
  if fIsTransmeta then begin
    result := GetTransmetaClockSpeed;
    ai64TickCount := -1;
    adTime := -1;
  end else if fIsC7 then begin
    result := GetC7ClockSpeed;
    ai64TickCount := -1;
    adTime := -1;
  end else begin
    StopWatch.Resolution := swresHigh;
    li64StartTick := GetCycleCount;
    StopWatch.StartTimer;
    Sleep( tbTime.Position * 1000 );
    adTime := StopWatch.StopTimer;
    li64StopTick := GetCycleCount;
    ai64TickCount := li64StopTick - li64StartTick;
    result := Round( ai64TickCount / adTime );
  end;
end; // function TfrmCPUSpeed.GetCPUSpeed

procedure TfrmCPUSpeed.btnGetCPUSpeedClick(Sender: TObject);
begin
  ShowCpuSpeedSample;
end; // procedure TfrmCPUSpeed.btnGetCPUSpeedClick(Sender: TObject);

procedure TfrmCPUSpeed.ParseCommandLine;
var
  i             : integer;
  lb_LogFile    : Boolean;
  lb_Run        : Boolean;
  le_CpuClockSpeed  : extended;
  li_EndMhz     : integer;
  li_StartMhz   : integer;
  ls_LogFile    : string;
  ls_LogMsg     : string;
begin
  i := 1;
  lb_Run := FALSE;
  while i <= ParamCount do begin
    if LowerCase( ParamStr(i) ) = 'cr' then begin
      i := i + 1;
      ClockRatio := StrToFloat( ParamStr( i ) );
      lb_Run := TRUE;
    end else if LowerCase( ParamStr(i) ) = 'l' then begin
      i := i + 1;
      lb_LogFile := TRUE;
      Try
        ls_LogFile := ParamStr( i );
      Except
        ls_LogFile := '';
      end;
    end else begin
      ShowMessage(
        'Commandline parameters: cr nn.n = Set VIA clockratio to nn.n;'  +
        CR_LF +
        'l "filename" = log to new file specified by "filename"');
      exit;
    end; // if
    i := i + 1;
  end; // while
  if lb_Run then begin
    StopWatch.SleepTime := 100;
    le_CpuClockSpeed := StopWatch.GetCPUClockspeed( TRUE );
    li_StartMHz := Round( le_CpuClockSpeed / ONE_MILLION );
    // update cpu ratio and get new clockspeed:
    li_EndMHz := ChangeClockRatio;
    if lb_LogFile then begin
      ls_LogMsg  := 'Clock ratio has been changed.' + CR_LF +
        Format(  'Start MHz = %d, end MHz = %d', [li_StartMHz, li_EndMHz] );
      CreateLogFile(ls_LogMsg, ls_LogFile);
    end;
    Application.Terminate;
  end; // if
end; // procedure TfrmCPUSpeed.ParseCommandLine

procedure TfrmCPUSpeed.FormDestroy(Sender: TObject);
begin
  FreeAndNil( StopWatch );
  FreeAndNil( StopWatchLog );
  FreeAndNil( fCpuidTransmetaPerformanceData );
  FreeAndNil( fCpuidLevel0 );
end;

procedure TfrmCPUSpeed.btnCounterClick(Sender: TObject);
const
  BTN_START = 'Start';
  BTN_END   = 'Stop';
var
  li64_TickCount    : Int64;
  ld_TickCount      : Double;
begin
  if btnCounter.Caption = BTN_START then begin
    edTicks.Text := 'Counting... Press "Stop" for tick count.';
    btnCounter.Caption := BTN_END;
    fi64_StartTick := GetCycleCount;
  end else begin
    fi64_StopTick := GetCycleCount;
    li64_TickCount := fi64_StopTick - fi64_StartTick;
    ld_TickCount := li64_TickCount;
    ShowMessage( 'Elapsed CPU cycles: ' + Format('%n', [ld_TickCount] ) );
    edTicks.Text := 'Ticks: ' + IntToStr( li64_TickCount );
    btnCounter.Caption := BTN_START;
  end; // if btnCounter.Caption := BTN_START
end; //procedure TfrmCPUSpeed.btnCounterClick

procedure TfrmCPUSpeed.tmrSpeedUpdateTimer(Sender: TObject);
var
  li64Ticks    : int64;
  ldClockSpeed : double;
  ldElapsedTime : double;
  procedure CheckChartScroll;
  begin
    fTicks := fTicks + 1;
    if (fTicks mod WINDOW_SIZE = 0) and (fTicks <> 0) then begin
      Application.ProcessMessages;
      chartClockSpeed.BottomAxis.Scroll( WINDOW_SIZE, FALSE );
      Application.ProcessMessages;
    end;
  end; // procedure CheckScroll;
begin
  ldClockSpeed := 0;
  if ckbxAuto.Checked then begin
    if fIsTransmeta then begin
      ldClockSpeed := GetTransmetaClockSpeed;
      fiElapsedTime := fiElapsedTime + ( tmrSpeedUpdate.Interval DIV 1000);
      fSeries.Add(ldClockSpeed, '', clRed);
      lblClockspeed.Caption := FloatToStrF( ldClockSpeed, ffNumber, 11, 0 ) +
                 ' MHz';
      CheckChartScroll;
    end else if fIsC7 then begin
      ldClockSpeed := GetC7ClockSpeed;
      fiElapsedTime := fiElapsedTime + ( tmrSpeedUpdate.Interval DIV 1000);
      fSeries.Add(ldClockSpeed, '', clRed);
      lblClockspeed.Caption := FloatToStrF( ldClockSpeed, ffNumber, 11, 0 ) +
                 ' MHz';
      CheckChartScroll;
    end else begin
      if fi64_StartTickTmr <> 0 then begin
        ldElapsedTime := StopWatch.StopTimer;
        fi64_StopTickTmr := GetCycleCount;
        ldClockSpeed := ( fi64_StopTickTmr - fi64_StartTickTmr ) / ldElapsedTime;
        lblClockspeed.Caption := FloatToStrF( ldClockSpeed, ffNumber, 11, 0 ) +
                 ' Hz';
        fiElapsedTime := fiElapsedTime + round( ldElapsedTime );
        fSeries.Add( round( ldClockSpeed / ONE_MILLION ), '' , clRed);
        CheckChartScroll;
        fi64_StartTickTmr := GetCycleCount;
        StopWatch.StartTimer;
      end else begin
        lblClockspeed.Caption := 'Updating...';
        fi64_StartTickTmr := GetCycleCount;
        StopWatch.StartTimer;
      end;
    end; // if
    if fIsC7 or fIsTransmeta then begin
      if fMaxClockSpeedAxis < ( ldClockSpeed ) then begin
        fMaxClockSpeedAxis := round( 1.1 * ( ldClockSpeed ) );
        fMaxClockSpeed := round( chartClockSpeed.SeriesList.Series[0].MaxYValue );
        chartClockSpeed.LeftAxis.Maximum := fMaxClockSpeedAxis;
        chartClockSpeed.LeftAxis.Minimum := 0;
        chartClockSpeed.MaxPointsPerPage := WINDOW_SIZE;
        chartClockSpeed.BottomAxis.Increment := 5;
      end; // if
    end else if fMaxClockSpeedAxis < ( ldClockSpeed / ONE_MILLION ) then begin
      fMaxClockSpeedAxis := round( 1.1 * ( ldClockSpeed / ONE_MILLION ) );
      fMaxClockSpeed := round( chartClockSpeed.SeriesList.Series[0].MaxYValue );
      chartClockSpeed.LeftAxis.Maximum := fMaxClockSpeedAxis;
      chartClockSpeed.LeftAxis.Minimum := 0;
      chartClockSpeed.MaxPointsPerPage := WINDOW_SIZE;
      chartClockSpeed.BottomAxis.Increment := 5;
    end;
//    UpdateCPUSpeed;
  end; // if ckbxAuto.Checked then begin
end; // procedure TfrmCPUSpeed.tmrSpeedUpdateTimer

procedure TfrmCPUSpeed.UpdateCPUSpeed;
var
  ld_ClockSpeed   : double;
  li64_TickCount    : Int64;
  ld_Time           : double;
begin
  if fIsTransmeta then begin
    fCpuidTransmetaPerformanceData.Initialize;
    ld_ClockSpeed := fCpuidTransmetaPerformanceData.CpuMHz;
  end else if fIsC7 then begin
    fCpuidCentaurPerformanceInfo.Initialize;
    ld_ClockSpeed := fCpuidCentaurPerformanceInfo.CurrentClockMultiplier *
                     fCpuidCentaurPerformanceInfo.FrontSideBusClock;
  end else begin
    ld_ClockSpeed := GetCPUSpeed( li64_TickCount, ld_Time );
  end;
  lblClockspeed.Caption := FloatToStrF( ld_ClockSpeed, ffNumber, 11, 0 ) +
               ' Hz';
end;

procedure TfrmCPUSpeed.ckbxAutoClick(Sender: TObject);
begin
  fi64_StartTickTmr := 0;
  btnCounter.Enabled := not ckbxAuto.Checked;
  tmrSpeedUpdate.Enabled := ckbxAuto.Checked;
//  btnScrollLeft.Enabled := not ckbxAuto.Checked;
//  btnScrollRight.Enabled := not ckbxAuto.Checked;
end;

procedure TfrmCPUSpeed.btnSaveClick(Sender: TObject);
begin
  SaveChartData;
end;

procedure TfrmCPUSpeed.tbTimeChange(Sender: TObject);
begin
  ChartSampleTimeChanged;
end;

procedure TfrmCPUSpeed.ChartSampleTimeChanged;
var
  liChartSampleTime : integer;
  s : string;
begin
  liChartSampleTime := tbTime.Position;
  tmrSpeedUpdate.Interval := liChartSampleTime * 100;
  s := FloatToStr( liChartSampleTime / 10 );
  chartClockSpeed.BottomAxis.Title.Caption := 'Sample number x '
    + s + ' second(s)';
  lblUpdatePeriod.Caption := s + 's';
end;

procedure TfrmCPUSpeed.SetClockRatio( Value : double );
begin
  fdClockRatio := Value;
end;

procedure TfrmCPUSpeed.btnSetClockRatioClick(Sender: TObject);
begin
  ChangeClockRatio;
end; //procedure TfrmCPUSpeed.btnSetClockRatioClick(Sender: TObject);

function TfrmCPUSpeed.ChangeClockRatio: integer;
var
  ls_RunProgram : string;
  le_CpuClockSpeed : extended;
  li_MHz : integer;
begin
  tmrSpeedUpdate.Enabled := FALSE;
  ls_RunProgram := CHANGE_RATIO_PROGRAM + ' ' + cboViaRatio.Text;
  StartProgramWait( ls_RunProgram , SW_SHOWMINIMIZED );
  StopWatch.SleepTime := 100;
  le_CpuClockSpeed := StopWatch.GetCPUClockspeed( TRUE );
  li_MHz := Round( le_CpuClockSpeed / ONE_MILLION );
  SetWinMHz( li_MHz );
  fi64_StartTickTmr := 0;
  tmrSpeedUpdate.Enabled := TRUE;
  result := li_MHz;
end; //procedure TfrmCPUSpeed.ChangeClockRatio

procedure TfrmCPUSpeed.EnableClockRatio;
begin
  cboViaRatio.Enabled := TRUE;
  btnSetClockRatio.Enabled := TRUE;
  lblClockRatio.Font.Color := clActiveCaption;
  lblClockRatio.Caption := 'Set VIA CPU Clock Ratio';
end; // procedure TfrmCPUSpeed.EnableClockRatio;

procedure TfrmCPUSpeed.DisableClockRatio;
begin
  cboViaRatio.Enabled := FALSE;
  btnSetClockRatio.Enabled := FALSE;
  lblClockRatio.Font.Color := clInactiveCaption;
  lblClockRatio.Caption := 'DISABLED: Set VIA CPU Clock Ratio' + CR_LF
    + '(needs ChgRatio.exe in same directory)';
end; // procedure TfrmCPUSpeed.DisableClockRatio;

{ The clock ratio can only be changed for certain processors (VIA) and only
  if CHANGE_RATIO_PROGRAM is in the same directory. }
function TfrmCPUSpeed.ClockRatioSettable: Boolean;
begin
  result := fIsCentaur and ( FileExists( CHANGE_RATIO_PROGRAM ) );
end; // function TfrmCPUSpeed.ClockRatioSettable: Boolean;

procedure TfrmCPUSpeed.ShowPanels( asetPanels : setPanels );
begin
  pnlGetCpuSpeed.Visible := eGetCpuSpeed in asetPanels;
  pnlVia.Visible := eViaRatio in asetPanels;
  chartClockSpeed.Visible := eGraph in asetPanels;
  pnlGraphOptions.Visible := eGraph in asetPanels;
end; // procedure TfrmCPUSpeed.ShowPanels( asetPanels : setPanels );

function TfrmCPUSpeed.DetectCpuVendor : enumCpuVendor;
begin
  if not assigned( fCpuidLevel0 ) then fCpuidLevel0 := TCpuidLevel0.Create;
  if fCpuidLevel0.VendorID = 'GenuineTMx86' then begin
    result := eTransmeta;
    fIsTransmeta := TRUE;
  end else if fCpuidLevel0.VendorID = 'CentaurHauls' then begin
    result := eVIA;
    fIsCentaur := TRUE;
    fCpuidCentaur := TCpuidCentaur.Create;
    try
      if IntToHex( fCpuidCentaur.MaxSupportedLevel, 8 ) = 'C0000002' then begin
        fIsC7 := TRUE;
      end;
    finally
      FreeAndNil( fCpuidCentaur );
    end;
  end else if Pos( 'AMD', fCpuidLevel0.VendorID ) > 0 then begin
    result := eAMD;
  end else if Pos( 'Intel', fCpuidLevel0.VendorID ) > 0 then begin
    result := eIntel;
  end; // if
end; // function TfrmCPUSpeed.DetectCpuVendor

function TfrmCPUSpeed.GetTransmetaClockSpeed : integer;
begin
  fCpuidTransmetaPerformanceData.Initialize;
  result := fCpuidTransmetaPerformanceData.CpuMHz;
end; // function TfrmCPUSpeed.GetTransmetaClockSpeed

function TfrmCPUSpeed.GetC7ClockSpeed : integer;
begin
  fCpuidCentaurPerformanceInfo.Initialize;
  result := fCpuidCentaurPerformanceInfo.CurrentClockMultiplier *
            fCpuidCentaurPerformanceInfo.FrontSideBusClock;
end; // function TfrmCPUSpeed.GetTransmetaClockSpeed

procedure TfrmCPUSpeed.SetPanelVisibility;
begin
  case fCpuVendor of
    eAMD, eIntel: begin
      fShowPanels := ALL_PANELS - [ eViaRatio, eGetCpuSpeed ];
    end; // eAMD
    eVIA: begin
      fShowPanels := ALL_PANELS - [ eGetCpuSpeed ];
      cboxVIA.Visible := TRUE;
      if ClockRatioSettable then begin
        EnableClockRatio;
      end else begin
        DisableClockRatio;
      end; // if
    end; // eVIA
    eTransmeta: begin
      fShowPanels := ALL_PANELS - [ eViaRatio, eGetCpuSpeed ];
    end; // eTransmeta
  end; //case
  ShowPanels( fShowPanels );
end; // procedure TfrmCPUSpeed.SetPanelVisibility;

procedure TfrmCPUSpeed.InitializeFieldVariables;
var
  lsCpuVendor : string;
begin
  fIsC7 := FALSE;
  fMaxClockSpeed := 0;
  fTicks := -1;
  fi64_StartTickTmr := 0;
  fiElapsedTime := 0;
  fCpuVendor := DetectCpuVendor;
  fLogArrayIndex := 0;
  case fCpuVendor of
  eIntel : lsCpuVendor := 'Intel';
  eAMD : lsCpuVendor := 'AMD';
  eVIA : lsCpuVendor := 'VIA';
  eTransmeta : lsCpuVendor := 'Transmeta';
  end; // case
  if not assigned( StopWatch ) then StopWatch := TStopWatch.Create;
  StopWatch.Resolution := swresHigh;
  if not assigned( StopWatchLog ) then StopWatchLog := TStopWatch.Create;
  StopWatchLog.Resolution := swresHigh;
  if fIsTransmeta then begin
    fCpuidTransmetaPerformanceData := TCpuidTransmetaPerformanceData.Create;
  end else if fIsC7 then begin
    fCpuidCentaurPerformanceInfo := TCpuidCentaurPerformanceInfo.Create;
    lsCpuVendor := lsCpuVendor + ' C7';
  end;
  StatusBar0Update( lsCpuVendor + ' CPU detected.' );
end; // procedure TfrmCPUSpeed.InitializeFieldVariables;

procedure TfrmCPUSpeed.ShowCpuSpeedSample;
var
  ldClockSpeed   : double;
  li64TickCount    : Int64;
  ldTime           : double;
begin
  ckbxAuto.Checked := FALSE;
  ldClockSpeed := GetCPUSpeed( li64TickCount, ldTime );
  if fIsTransmeta then begin
    ShowMessage( 'Transmeta Clockspeed = ' +
                 FloatToStrF( ldClockSpeed, ffNumber, 11, 0 ) +
                 ' MHz' );
  end else if fIsC7 then begin
    ShowMessage( 'VIA C7 Clockspeed = ' +
                 FloatToStrF( ldClockSpeed, ffNumber, 11, 0 ) +
                 ' MHz' );
  end else begin
    ShowMessage( 'Measured time = ' + FloatToStr( ldTime ) +
                 's, Ticks = ' + IntToStr( li64TickCount ) +
                 ', Clockspeed = ' +
                 FloatToStrF( ldClockSpeed, ffNumber, 11, 0 ) +
                 ' Hz' );
  end; // if
end; // procedure TfrmCPUSpeed.ShowCpuSpeedSample;

procedure TfrmCPUSpeed.SetPriority( aRunPriority : enumRunPriority );
var
  H : THandle;
begin
  H := GetCurrentProcess();
  case aRunPriority of
  eRPNormal  : SetPriorityClass( H, NORMAL_PRIORITY_CLASS );
  eRPHigh    : SetPriorityClass( H, HIGH_PRIORITY_CLASS );
  eRPRealTime: SetPriorityClass( H, REALTIME_PRIORITY_CLASS );
  else raise exception.Create('Unknown program priority.');
  end; // case
end; // procedure TfrmCPUSpeed.SetPriority

function TfrmCPUSpeed.DetermineMaximumClockSpeed: int64;
var
  li64StartTick : int64;
  li64StopTick  : int64;
  li64TickCount : int64;
  ldTime        : double;
begin
  if fIsC7 then begin
    result := fCpuidCentaurPerformanceInfo.HighestClockMultiplier *
              fCpuidCentaurPerformanceInfo.FrontSideBusClock;
    fMaxClockSpeedAxis := round( 1.1 * ( result ) );
  end else if fIsTransmeta then begin
    SetPriority( eRPRealTime );
    Fibonacci( FIB_NUMBER );
    result := GetTransmetaClockSpeed;
    SetPriority( eRPNormal );
    fMaxClockSpeedAxis := round( 1.1 * ( result ) );
  end else begin
//    SetPriority( eRPRealTime );
//    application.ProcessMessages;
    Fibonacci( FIB_NUMBER );
//    application.ProcessMessages;
    StopWatch.Resolution := swresHigh;
    li64StartTick := GetCycleCount;
    StopWatch.StartTimer;
    Fibonacci( FIB_NUMBER );
    ldTime := StopWatch.StopTimer;
    li64StopTick := GetCycleCount;
//    SetPriority( eRPNormal );
    li64TickCount := li64StopTick - li64StartTick;
    result := Round( li64TickCount / ldTime );
    fMaxClockSpeedAxis := round( 1.1 * ( result / ONE_MILLION ) );
  end; // if
end; // function TfrmCPUSpeed.DetermineMaximumClockSpeed

procedure TfrmCPUSpeed.InitializeChart;
begin
  if not assigned( fSeries ) then fSeries := chartClockSpeed.SeriesList.Series[0];
  fSeries.Clear;
  chartClockSpeed.LeftAxis.Maximum := fMaxClockSpeedAxis;
  chartClockSpeed.LeftAxis.Minimum := 0;
  chartClockSpeed.MaxPointsPerPage := WINDOW_SIZE;
  chartClockSpeed.BottomAxis.Increment := 10;
end;

procedure TfrmCPUSpeed.SaveChartData;
var
  lTextFileOut  : TextFile;
  lsFileName    : string;
  lsOutLine     : string;
  s             : string;
  x             : double;
  n             : integer;
const
  DEFAULT_FILENAME = 'CpuClockSpeed_OUT.txt';
begin
  SaveDialog1.Filter := '*.txt';
  SaveDialog1.FileName := DEFAULT_FILENAME;
  if SaveDialog1.Execute then begin
    lsFileName := Trim( SaveDialog1.FileName );
    if lsFileName = '' then lsFileName := DEFAULT_FILENAME;
    AssignFile( lTextFileOut, lsFileName );
    try
      Rewrite( lTextFileOut );
      WriteLn( lTextFileOut, 'Cpu Speed Output File');
      WriteLn( lTextFileOut, '' );
      lsOutLine := 'Date: ' + DateToStr( Date ) + ', Time: ' + TimeToStr( Time );
      WriteLn( lTextFileOut, lsOutLine );
      WriteLn( lTextFileOut, '' );
      WriteLn( lTextFileOut, 'Sample    Clock speed (MHz)');
      WriteLn( lTextFileOut, '========  =================');
      for n := 0 to fSeries.Count - 1 do begin
        s := fSeries.XLabel[n];
        x := n;
        lsOutline := Format('%6.0n' + Chr(9) + '   %11n', [ x, fSeries.YValue[n] ]);
        WriteLn( lTextFileOut, lsOutline);
      end; // for
    finally
      CloseFile( lTextFileOut );
    end; // try..finally
  end; // if
end; // procedure TfrmCPUSpeed.SaveChartData

procedure TfrmCPUSpeed.SaveLogData;
var
  lTextFileOut  : TextFile;
  lsFileName    : string;
  lsOutLine     : string;
  x             : double;
  n             : integer;
const
  DEFAULT_FILENAME = 'CpuClockSpeed_LOG.txt';
begin
  SaveDialog1.Filter := '*.txt';
  SaveDialog1.FileName := DEFAULT_FILENAME;
  if SaveDialog1.Execute then begin
    lsFileName := Trim( SaveDialog1.FileName );
    if lsFileName = '' then lsFileName := DEFAULT_FILENAME;
    AssignFile( lTextFileOut, lsFileName );
    try
      Rewrite( lTextFileOut );
      WriteLn( lTextFileOut, 'Cpu Speed Log File');
      WriteLn( lTextFileOut, '' );
      lsOutLine := 'Date: ' + DateToStr( Date ) + ', Time: ' + TimeToStr( Time )
        + ', Update Interval: ' + cboLogPeriod.Text + ' seconds';
      WriteLn( lTextFileOut, lsOutLine );
      WriteLn( lTextFileOut, '' );
      WriteLn( lTextFileOut, 'Sample    Clock speed (MHz)');
      WriteLn( lTextFileOut, '========  =================');
      for n := 0 to fLogArrayIndex do begin
        x := n;
        lsOutline := Format('%6.0n' + Chr(9) + '   %11d', [ x, fLogArray[n] ]);
        WriteLn( lTextFileOut, lsOutline);
      end; // for
    finally
      CloseFile( lTextFileOut );
    end; // try
  end; // if
end; // procedure TfrmCPUSpeed.SaveChartData


procedure TfrmCPUSpeed.cboViaRatioChange(Sender: TObject);
begin
  ClockRatio := StrToFloat( cboViaRatio.Text );
end;

procedure TfrmCPUSpeed.StatusBar0Update( astrMessage : string );
begin
  StatusBar1.Panels[0].Text := astrMessage;
end; // procedure TfrmCPUSpeed.StatusBar0Update

procedure TfrmCPUSpeed.StatusBar1Update( astrMessage : string );
begin
  StatusBar1.Panels[1].Text := astrMessage;
end; // procedure TfrmCPUSpeed.StatusBar0Update

procedure TfrmCPUSpeed.cboxGraphClick(Sender: TObject);
begin
  ShowHideCpuSpeedGraph( not cboxGraph.Checked );
end;

procedure TfrmCPUSpeed.ShowHideCpuSpeedGraph(abVisible : Boolean);
var
  liChartHeight : integer;
begin
  liChartHeight := chartClockSpeed.Height;
  if liChartHeight <= 0 then liChartHeight := 200;
  if abVisible and not chartClockSpeed.Visible then begin
    height := height + liChartHeight + pnlGraphOptions.height;
  end else begin
    height := height - liChartHeight - pnlGraphOptions.height;
  end;
  chartClockSpeed.Visible := abVisible;
  pnlGraphOptions.Visible := abVisible;
end;

procedure TfrmCPUSpeed.cboxGetCpuSpeedClick(Sender: TObject);
begin
  if cboxGetCpuSpeed.Checked and pnlGetCpuSpeed.Visible then begin
    pnlGetCpuSpeed.Visible := FALSE;
    height := height - pnlGetCpuSpeed.Height;
  end else begin
    pnlGetCpuSpeed.Visible := TRUE;
    height := height + 230;
  end; // if
end;

procedure TfrmCPUSpeed.cboxVIAClick(Sender: TObject);
begin
  if fIsCentaur then pnlVIA.Visible := not cboxVia.Checked;
end;

procedure TfrmCPUSpeed.StartLogging;
begin
  fLogArrayIndex := -1;
  fLogStartTick := 0;
  TimerLog.Interval := round( StrToFloat( cboLogPeriod.Text ) * 1000 );
  TimerLog.Enabled := TRUE;
  btnLogCpuSpeed.Caption := 'Stop Logging';
  btnSaveLogFile.Enabled := FALSE;
  cboLogPeriod.Enabled := FALSE;
end;

procedure TfrmCPUSpeed.StopLogging;
begin
  TimerLog.Enabled := FALSE;
  btnLogCpuSpeed.Caption := 'Start Logging';
  btnSaveLogFile.Enabled := TRUE;
  cboLogPeriod.Enabled := TRUE;
end;

procedure TfrmCPUSpeed.btnLogCpuSpeedClick(Sender: TObject);
begin
  if TimerLog.Enabled then begin
    StopLogging;
  end else begin
    StartLogging;
  end; //  if
end;

procedure TfrmCPUSpeed.UpdateLog;
var
  ldElapsedTime : double;
begin
  fLogArrayIndex := fLogArrayIndex + 1;
  if fIsTransmeta then begin
    fLogArray[ fLogArrayIndex ] := GetTransmetaClockSpeed;
  end else begin
    if fLogStartTick <> 0 then begin
      ldElapsedTime := StopWatchLog.StopTimer;
      fLogStopTick  := GetCycleCount;
      fLogArray[ fLogArrayIndex ] := round(
        ( fLogStopTick - fLogStartTick ) / ldElapsedTime );
      fLogStartTick := GetCycleCount;
      StopWatchLog.StartTimer;
    end else begin
      fLogStartTick   := GetCycleCount;
      fLogArrayIndex  := -1;
      StopWatchLog.StartTimer;
    end;
  end; // if
end; // procedure TfrmCPUSpeed.UpdateLog;

procedure TfrmCPUSpeed.btnSaveLogFileClick(Sender: TObject);
begin
  SaveLogData;
end;

procedure TfrmCPUSpeed.TimerLogTimer(Sender: TObject);
begin
  UpdateLog;
end; // procedure TfrmCPUSpeed.TimerLogTimer(Sender: TObject);

procedure TfrmCPUSpeed.btnScrollLeftClick(Sender: TObject);
begin
  if chartClockSpeed.BottomAxis.Automatic then begin
    chartClockSpeed.BottomAxis.Automatic := FALSE;
    chartClockSpeed.BottomAxis.SetMinMax(0, 60);
  end else begin
    chartClockSpeed.BottomAxis.Scroll( - ( WINDOW_SIZE div 2 ), TRUE );
  end; // if
end;

procedure TfrmCPUSpeed.btnScrollRightClick(Sender: TObject);
begin
  if chartClockSpeed.BottomAxis.Automatic then begin
    chartClockSpeed.BottomAxis.Automatic := FALSE;
    chartClockSpeed.BottomAxis.SetMinMax(0, 60);
  end else begin
    chartClockSpeed.BottomAxis.Scroll( WINDOW_SIZE div 2, FALSE );
  end; // if
end;

procedure TfrmCPUSpeed.Button1Click(Sender: TObject);
begin
  ShowMessage( 'Interrupt intensive background tasks can sometimes affect calculated CPU speed.' );
end;

procedure TfrmCPUSpeed.ThreadDone(Sender: TObject);
begin
  dec( fNbrOfThreadsRunning );
  application.ProcessMessages;
end; // procedure TfrmCPUSpeed.FibThreadDone;

procedure TfrmCPUSpeed.btnLoadDownCPUClick(Sender: TObject);
var
  lFibThread : TFibThread;
begin
  lFibThread := TFibThread.Create( 45 );
  try
    lFibThread.OnTerminate := ThreadDone;
    lFibThread.Resume;
    inc( fNbrOfThreadsRunning );
    // wait until thread is finished
    while ( fNbrOfThreadsRunning = 1 ) do begin
      sleep( 10 );
      application.processmessages;
    end; // while
  finally

  end; // try..finally
end;

end.
