//PROFILE-NO
unit Protmain;
{$O-}  // Do not remove! Delphi might crash !!!!
{$R-}
{$Q-}              
{$A+}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, Procal;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    f0: TLabel;
    f0d: TLabel;
    f100: TLabel;
    f1000: TLabel;
    f100d: TLabel;
    f1000d: TLabel;
    Bevel1: TBevel;
    Label20: TLabel;
    prom: TLabel;
    promd: TLabel;
    Label25: TLabel;
    Label26: TLabel;
    Bevel2: TBevel;
    Bevel4: TBevel;
    Bevel5: TBevel;
    Bevel6: TBevel;
    Bevel8: TBevel;
    Bevel9: TBevel;
    Bevel10: TBevel;
    Bevel11: TBevel;
    Label8: TLabel;
    tmlf: TLabel;
    tmlfd: TLabel;
    Label9: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Label15: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    Label2: TLabel;
    Bevel3: TBevel;
    Label3: TLabel;
    prouse: TLabel;
    proused: TLabel;
    Label14: TLabel;
    Label18: TLabel;
    Label19: TLabel;
    Label21: TLabel;
    f0ds: TLabel;
    f100ds: TLabel;
    f1000ds: TLabel;
    f0s: TLabel;
    f100s: TLabel;
    f1000s: TLabel;
    tmlfs: TLabel;
    tmlfds: TLabel;
    Label30: TLabel;
    Label31: TLabel;
    procedure StartItAll(Sender: TObject);
  private
    { Private-Deklarationen }
    FUNCTION  MBox : TMyLargeInteger;
    PROCEDURE UserMessage ( VAR Message ); Message WM_USER+5;
  private
    res     : Array[0..5] OF TMyLargeInteger;
    resstr  : Array[0..5] OF String;
    resstr2 : Array[0..5] OF String;

  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation


{$R *.dfm}

CONST
  MHZ1 = 0; MHZn = 28;
  MHzTab : Array[MHZ1..MHZn] OF Word =
          (33, 40, 50, 66, 75, 83, 90, 100, 120, 133, 150, 166, 180, 200, 233,
           266, 300, 333, 350, 366, 380, 400, 433, 450, 466, 500, 533, 550, 600 );

VAR
  MHZes  : Double;
  ta     : TMyLargeInteger;
  tsum   : TMyLargeInteger;

FUNCTION TForm1.MBox : TMyLargeInteger;
BEGIN
  asm
    DW 310FH;   // first PRTSC, get cycles before tested instruction
    mov ta.lowpart,eax
    mov ta.highpart,edx
  end;
  Result.lowpart  := 0;
  Result.highpart := 0;

  PostMessage(application.mainform.handle, WM_USER+5, 1, 2);
//  MessageBoxSimu(0, 'Messagebox demo, waiting for click should not be measured',
//                    'Protest', MB_OK);
// In the program protest2 you will find here MessageBox, it is here at the end
// of the procedure, in order not to be measured. A good profiler
// stops measuring before entering that procedure. The reason is, that the
// current process, so to say, hands over the cpu to another process. E.g. that
// the current process is interrupted and continued after returning from
// MessageBox. That's why this procedure shouldn't be measured.
  asm
    DW 310FH;   // get cycles after tested instructions
    // Next lines calculate the no of cycles now - no of cycles before first PRTSC
    sub eax,ta.lowpart
    sbb edx,ta.highpart
    // Next lines subtract no of cycles for the first PRTSC + mov instructions
    sub eax,QPCAss.lowpart
    sbb edx,QPCAss.highpart
    // = No of cycles for the measured instructions
    // stored in tsum
    mov tsum.lowpart,eax
    mov tsum.highpart,edx
  end;
  result.lowpart := tsum.lowpart;
  MessageBox(0, 'Messagebox demo, waiting for click should not be measured',
                'Protest', MB_OK);
END;

PROCEDURE TForm1.UserMessage ( VAR Message );
VAR
  i : Integer;
BEGIN
  asm
    DW 310FH;   // first PRTSC, get cycles before tested instruction
    mov ta.lowpart,eax
    mov ta.highpart,edx
  end;

  i := 0;
  WHILE i < 100000 DO
    INC(i);

  asm
    DW 310FH;   // get cycles after tested instructions
    // Next lines calculate the no of cycles now - no of cycles before first PRTSC
    sub eax,ta.lowpart
    sbb edx,ta.highpart
    // Next lines subtract no of cycles for the first PRTSC + mov instructions
    sub eax,QPCAss.lowpart
    sbb edx,QPCAss.highpart
    // = No of cycles for the measured instructions
    // stored in tsum
    mov tsum.lowpart,eax
    mov tsum.highpart,edx
  end;
  res[5].quadpart := tsum.quadpart;
END;

PROCEDURE ConvertTime ( VAR wertstr : String; wert : Double; AsCycles : Boolean );
VAR
  einheit : String;
BEGIN
  IF AsCycles = TRUE THEN BEGIN
    Str(wert:0:0, einheit);
    wertstr := '';
    WHILE Length(einheit) > 3 DO BEGIN
      wertstr := ',' + Copy(einheit, Length(einheit)-2, 3) + wertstr;
      einheit := Copy(einheit, 1, Length(einheit)-3);
    END;
    wertstr := einheit + wertstr;
    exit;
  END;
  wert := wert / MHZes;
  IF wert < 1000.0 THEN BEGIN       { < 1 ms -> micro sec}
    einheit := ' µS';
    END
  ELSE BEGIN
    IF wert < 1000000.0 THEN BEGIN  { < 1 sec -> milli sec }
      wert := wert / 1000;
      einheit := ' ms';
      END
    ELSE BEGIN
      wert := wert / 1000000.0;     { nano sec -> sec }
      IF wert < 60.0 THEN BEGIN
        einheit := '  s ';
        END
      ELSE BEGIN
        wert := wert / 60.0;        { sec -> min }
        einheit := '  m ';
        IF wert > 60 THEN BEGIN
          wert := wert / 60.0;      { min -> std }
          einheit := '  h ';
        END;
      END;
    END;
  END;
  Str(wert:0:3, wertstr);
  wertstr := wertstr + einheit;
END;

FUNCTION Minimum ( a, b : TMyComp ) : TMyComp;
BEGIN
  IF a > b THEN
    Result := b
  ELSE
    Result := a;
END;

FUNCTION GetAssemblerQPC : TMyLargeInteger;
VAR
  n  : Integer;
  te : TMyLargeInteger;
  ts : TMyLargeInteger;
BEGIN
  Result.quadpart := 1000000000;
  FOR n := 1 TO 40 DO BEGIN
    // Until here a certain amount of instructions have been processed
    // The next instruction (PRTSC) gives how many
    asm
      DW 310FH;
      mov ts.lowpart,eax
      mov ts.highpart,edx
      // The next line results in how many cycles were used until here
      // ts - te : how many cycles were used by the previous 3 instruction or
      // by the next 3
      DW 310FH;
      mov te.lowpart,eax
      mov te.highpart,edx
    end;
    Result.quadpart := Minimum(Result.quadpart, ABS(te.Quadpart - ts.QuadPart));
  END;
END;

PROCEDURE EstimateMHz ;
VAR
  mega         : Double;
  takte        : TMyLargeInteger;
  dauer        : TMyLargeInteger;
  i            : Integer;
  tickx        : LongInt;
  tick1, tick2 : LongInt;
  startt, endt : TMyLargeInteger;
BEGIN
  startt.QuadPart := 0;
  dauer.quadpart  := 0;
  tick1 := GetTickCount;
  REPEAT
    tick2 := GetTickCount;
  UNTIL tick2 <> tick1;

  REPEAT
    tick1 := GetTickCount;
  UNTIL tick2 <> tick1;

  asm
    DW 310FH;
    mov startt.lowpart,eax
    mov startt.highpart,edx
  end;
  tickx := tick1;

  FOR i := 1 TO 66 DO BEGIN
    tick2 := tick1;
    REPEAT
      tick1 := GetTickCount;
    UNTIL tick1 <> tick2;
  END;
  asm
    DW 310FH;
    mov endt.lowpart,eax
    mov endt.highpart,edx
  end;

  dauer.lowpart  := tick1 - tickx ;
  takte.quadpart := endt.quadpart - startt.quadpart - QPCAss.QuadPart {- QPCAss.QuadPart};
  mega := takte.quadpart;
  mega := mega / dauer.lowpart / 1000;
  MHZes := Trunc(mega);

  FOR i := MHZ1 TO MHZn DO BEGIN
    IF Abs(MHZes - MHZTab[i]) < 3 THEN BEGIN
      MHZes := MHZTab[i];
      break;
    END;
  END;
END;

procedure TForm1.StartItAll(Sender: TObject);
VAR
  i, x     : Integer;
  xd       : Double;
  Ergebnis : Integer;
  resstrs  : Array[0..5] OF String;
  resstr2s : Array[0..5] OF String;
begin
  EstimateMHZ;
  QPCAss  := GetAssemblerQPC;

  FOR i := 0 TO 5 DO
    res[i].quadpart := 0;

  Ergebnis := 0;
  FOR i := 1 TO 100 DO
    res[3].lowpart := res[3].lowpart + TopFunction(Ergebnis).lowpart;

  Ergebnis := 0;
  FOR i := 1 TO 100 DO
    res[1].lowpart := res[1].lowpart + FunctionWith100(Ergebnis, 100).lowpart;

  Ergebnis := 0;
  FOR i := 1 TO 100 DO
    res[2].lowpart := res[2].lowpart + FunctionWith1000(Ergebnis, 1000).lowpart;

  res[4].lowpart := res[4].lowpart + MBox.lowpart;

  FOR i := 0 TO 5 DO BEGIN
    x := res[i].lowpart;
    IF i < 4 THEN
      x := x DIV 100;
    ConvertTime(resstr[i], x, TRUE);
    xd := Round(x);
    ConvertTime(resstr2[i], xd, FALSE);
    IF i < 4 THEN BEGIN
      x := res[i].lowpart;
      ConvertTime(resstrs[i], x, TRUE);
      xd := Round(x);
      ConvertTime(resstr2s[i], xd, FALSE);
    END;
  END;

  f0.caption     := '0';
  f100.caption   := resstr[1];
  f1000.caption  := resstr[2];
  tmlf.caption   := resstr[3];
  prom.caption   := resstr[4];
  prouse.caption := resstr[5];

  f0s.caption    := '0';
  f100s.caption  := resstrs[1];
  f1000s.caption := resstrs[2];
  tmlfs.caption  := resstrs[3];

  f0d.caption     := '0.000 µS';
  f0d.caption     := resstr2[0];
  f100d.caption   := resstr2[1];
  f1000d.caption  := resstr2[2];
  tmlfd.caption   := resstr2[3];
  promd.caption   := resstr2[4];
  proused.caption := resstr2[5];

  f0ds.caption   := '0.000 µS';
  f0ds.caption   := resstr2s[0];
  f100ds.caption := resstr2s[1];
  f1000ds.caption:= resstr2s[2];
  tmlfds.caption := resstr2s[3];
end;

end.
