home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 13 / CD_ASCQ_13_0494.iso / news / swag / timing.swg < prev    next >
Text File  |  1994-03-11  |  51KB  |  1 lines

  1. SWAGOLX.EXE (c) 1993 GDSOFT  ALL RIGHTS RESERVED 00016         TIMER/RESOLUTION ROUTINES                                         1      05-28-9314:09ALL                      SWAG SUPPORT TEAM        Millisecond Timer Unit   IMPORT              13     «uUe { millisecond timer Unit }ππUnit msecs;ππInterfaceππVarπ   timer:Word;                     { msec timer }π   idle:Procedure; {  you can change this to do something useful when Delaying}ππProcedure Delay_ticks(t:Word);     { resume Until t clock ticks have elapsed }πProcedure start_clock;             { starts the 1 msec timer }πProcedure stop_clock;              { stops the 1 msec timer }ππImplementationππUses Dos;ππProcedure Delay_ticks(t:Word);πbeginπ  inc(t,timer);π  Repeat idle Until Integer(timer - t) >= 0;πend;ππConst clock_active:Boolean = False;π      one_msec = 1193;πVar   save_clock:Pointer;π      clocks:Word;ππProcedure tick_int; Far; Assembler;πAsmπ  push axπ  push dsπ  mov ax,seg @dataπ  mov ds,axπ  mov al,$20π  out $20,alπ  inc [timer]π  add [clocks],one_msecπ  jnc @1π  pushfπ  call [save_clock]π@1:π  pop dsπ  pop axπ  iretπend;πππProcedure start_clock;πbeginπ  if clock_active then Exit;π  inc(clock_active);π  timer := 0;π  clocks := 0;π  getintvec($08,save_clock);π  setintvec($08,@tick_int);π  port[$43] := $36;π  port[$40] := lo(one_msec);π  port[$40] := hi(one_msec);πend;ππProcedure stop_clock;πbeginπ  if not clock_active then Exit;π  dec(clock_active);π  port[$43] := $36;π  port[$40] := 0;π  port[$40] := 0;π  setintvec($08,save_clock);πend;ππProcedure nothing; Far;πbeginπend;ππVar saveexit:Pointer;ππProcedure uninstall; Far;πbeginπ  Exitproc := saveexit;π  if clock_active then stop_clock;πend;ππbeginπ  timer := 0;π  idle := nothing;π  saveexit := Exitproc;π  Exitproc := @uninstall;πend.ππππ     2      05-28-9314:09ALL                      SWAG SUPPORT TEAM        TCTIMER.PAS              IMPORT              4      «u⌠┴ Unit tctimer;ππInterfaceπUses tptimer;ππ  Varπ    start : LongInt;ππProcedure StartTimer;ππProcedure WriteElapsedTime;ππππImplementationππProcedure StartTimer;π  beginπ    start := ReadTimer;π  end;ππProcedure  WriteElapsedTime;π  Var stop : LongInt;π  beginπ    stop := ReadTimer;π    Writeln('Elapsed time = ',(ElapsedTime(start,stop) / 1000):10:6,' seconds');π  end;πππend.π       3      05-28-9314:09ALL                      D.J. MURDOCH             Time Code Segments       IMPORT              26     «uf▌  {$G+,S-,R-,Q-}π program timer;ππ { Program to time short segments of code; inspired by Michael Abrash'sπ   Zen timer.  Donated to the public domain by D.J. Murdoch }ππ usesπ   opdos; { Object Professional unit, needed only for TimeMS,π            a millisecond timer. }ππ constπ   onetick = 1/33E6;  { This is the time in seconds for one cpu cycle.π                        I've got it set for a 33 Mhz machine. }ππ { Instructions:  put your code fragment into a short routine called Segment.π   It should leave the stack unchanged, or it'll blow up when we clone it.π   It *must* have a far return at the end.  Play around with declaring itπ   as an assembler procedure or not to see the cost of the TP entry andπ   exit code. }ππ { This example is Sean Palmer's "var2 := var1 div 2" replacement fragment. }ππ varπ   var1,var2 : integer;ππ procedure Segment; far; assembler;π asmπ    mov ax,var1π    sar ax,1π    jns @Sπ    adc ax,0π  @S:π    mov var2,axπ end;ππ { This is the comparison TP code.  Note that it includes entry/exit code;π   play around with variations on the assembler version to make it a fairπ   comparison }π (*π procedure Segment; far;π beginπ   var2 := var1 div 2;π end;π *)ππ { This procedure is essential!!! Do not move it. It must followπ   Segment directly. }π procedure Stop;π beginπ end;ππ { This routine will only be called once at the beginning of the program;π   set up any variables that Segment needs }ππ procedure Setup;π beginπ   var1 := 5;π   writeln('This run, var1=',var1);π end;ππ constπ   maxsize=65520;π   RETF   = $CB;π varπ   p : pointer;π   src,dest : ^byte;π   size : word;π   repeats : word;π   i : word;π   start,finish : longint;π   count : longint;π   main,overhead,millisecs : real;π beginππ   setup;ππ   { Get a segment of memory, and fill it up with as many copiesπ     of the segment as possible }ππ   size := ofs(stop) - ofs(Segment) -1;π   repeats := maxsize div size;π   getmem(p, size*repeats + 1);π   src := @Segment;π   dest := p;π   for i:=1 to repeats doπ   beginπ     move(src^,dest^,size);π     inc(dest,size);π   end;π   { Add a final RETF at the end. }π   dest^ := RETF;ππ   { Now do the timing.  Keep repeating one second loops indefinitely. }ππ   writeln(' Bytes     Clocks       ns       MIPS');π   repeatπ     { First loop:  one second worth of calls to the segment }π     start := timems;π     count := 0;π     repeatπ       asmπ         call dword ptr pπ       end;π       finish := timems;π       inc(count);π     until finish > 1000+start;π     main := (finish - start)/repeats/count;ππ     { Second loop:  1/2 second worth of calls to the RETF }π     start := timems;π     count := 0;π     repeatπ       asmπ         call dword ptr destπ       end;π       finish := timems;π       inc(count);π     until finish > 500+start;π     overhead := (finish-start)/count;π     millisecs := (main-overhead/repeats);π     writeln(size:6,millisecs/1000/onetick:11:1,π                    1.e6*millisecs:11:0,π                    1/millisecs/1000:11:3);π   until false;π end.πππ--- Msg V3.2π * Origin: Murdoch's Point, Kingston, Ont, Canada  - -   (1:249/99.5)π                                                                               4      05-28-9314:09ALL                      SWAG SUPPORT TEAM        TIMELOOP.PAS             IMPORT              5      «u»L {$A+,B-,D-,E-,F-,I-,N-,O-,R-,S-,V-}ππProgram TimeNullRoutine;ππUsesπ  TpTimer;ππVarπ  Count : Byte;ππProcedure DoNothing;πbeginπend;ππVarπ  Loop : Word;π  Start,π  Stop : LongInt;ππbeginπ  Start := ReadTimer;π  For Loop := 1 to 1000 doπ    DoNothing;π  Stop := ReadTimer;π  WriteLn('Time = ', ElapsedTimeString(Start, Stop), ' ms')πend.ππ{π  ...Well running the Program listed above, 1000 nul loops timeπ  in at 3.007 miliseconds on my 386SX-25.π}                                                                 5      05-28-9314:09ALL                      SWAG SUPPORT TEAM        Calculate Program Time   IMPORT              29     «u? { SB> Has anyone by any chance written a Procedure For calculating the amountπ SB> of time a Program runs.  I understand how to use getTime, etc, but I amπ SB> trying to figure out a way around all the possibilities...i.e. someoneπ SB> starts a Program at 23:59:03.44, and it's finished at 00:02:05.33.π SB>π SB> Anyway, if someone already has this figured out, I'd sure appreciate itπ SB> or even some ideas...ππScott,π    try:ππ    Varπ        Timer : LongInt Absolute $0040:$006c;ππ    That's the Tic counter, stored at Segment 0040h, offset 006Ch. Itπstores the number of ticks since you turned the Computer on and so willπonly wrap after MorE THAN 3 YEARS, if you never close the machine ;-)ππ    it is incremented 18.2 times/sec, so divide it by 18.2 to get theπnumber of seconds. You can figure out the rest ;-)ππ    Store its content to another LongInt at the start of the Program,πagain at the end. Substract the first value from the second and you haveπthe number of ticks elapsed during the Program's execution.ππOh what the heck, here is a Complete Unit, all you have to do is includeπit in your Uses clause nothing more unless you want to save the time inπa log File or something.π}ππ{$A+,B-,D+,E-,F+,G+,I-,L+,N-,O+,P+,Q-,R-,S-,T-,V-,X+,Y+}π{$M 8192,0,0}πUnit TimePrg;π(**) Interface (**)π(**) Implementation (**)πUsesπ  Dos;πTypeπ  CmdLine = String[127];πVarπ  TimerTicks : LongInt Absolute $0040:$006C;π  OldCommandLine, NewCommandline : CmdLine;π  CommandLine : ^CmdLine;π  TimeIn, TimeOut, Spent : LongInt;π  Years, Days, Hours, Minutes, Seconds, ms : Byte;π  ExitBeForeTimePrg : Pointer;π  D : DirStr;π  N : NameStr;π  E : ExtStr;π  Index : Integer;ππFunction Strfunc(Value:Byte):String;πVarπ  temp : String;πbeginπ  Str(Value:0, Temp);π  StrFunc := #32+temp;πend;ππProcedure TimePrgExit; Far;πbeginπ  TimeOut := TimerTicks;π  ExitProc := ExitBeForeTimePrg;π  Spent := TimeOut - TimeIn;π  ms := (Spent - trunc(Spent / 18.2))*55;π  Spent := Trunc(Spent / 18.2);π  Years := Spent div (3600*24*365);π  Spent := Spent mod (3600*24*365);π  Days := Spent div (3600*24);π  Spent := Spent mod (3600*24);π  Hours := Spent div 3600;π  Spent := Spent mod 3600;π  Minutes := Spent div 60;π  Spent := Spent mod 60;π  Seconds := Spent;π  CommandLine := Ptr(PrefixSeg, $80);π  OldCommandLine := CommandLine^;π  NewCommandLine := '';π  if Years>0 thenπ    NewCommandLine := NewCommandLine + Strfunc(Years) + ' Years';π  if Days>0 thenπ    NewCommandLine := NewCommandLine + Strfunc(Days) + ' Days';π  if Hours>0 thenπ    NewCommandLine := NewCommandLine + Strfunc(Hours) + ' Hours';π  if Minutes>0 thenπ    NewCommandLine := NewCommandLine + Strfunc(Minutes) + ' Minutes';π  if Seconds>0 thenπ    NewCommandLine := NewCommandLine + Strfunc(Seconds)    + ' Seconds';π  if ms>0 thenπ    NewCommandLine := NewCommandLine + Strfunc(ms) + ' milli-seconds';π  CommandLine^ := NewCommandLine;π  Write('Thanks For spending ');π  Case Paramcount ofπ    0: Write('so little time');π    2: Write(ParamStr(1),#32, Paramstr(2));π  elseπ    For Index := 1 to ParamCount - 3 do beginπ      Write(Paramstr(Index));π      if odd(Index) thenπ        Write(' ')π      elseπ        Write(', ');π    end;π    Write(Paramstr(Index+1), ' and ',π    Paramstr(Index+2), ' ', Paramstr(Index+3));π  end;π  CommandLine^ := OldCommandLine;π  Fsplit(Paramstr(0), D, N, E);π  Writeln(' In ', N);πend;ππbeginπ  TimeIn := TimerTicks;π  ExitBeForeTimePrg := ExitProc;π  ExitProc := @TimePrgExit;πend.π                                                                                                                      6      05-28-9314:09ALL                      SWAG SUPPORT TEAM        Timing Using TP Clock    IMPORT              8      «uh {π> Does anyone know of a proFiler For TP 6, or is there a specialπ> command using TPC to activate a proFiler to tell how much time theπ> Program takes doing a task. Thanks, LukeππTry this Unit.  Put a ClockOn and it will start timing then when the ClockOffπis reached it will tell you how long it took.  It's very nice For optimizingπpieces of code.π}ππUnit Timer;ππInterfaceππProcedure ClockOn;πProcedure ClockOff;ππImplementationπUses Dos;ππVarπ  H, M, S, S100 : Word;π  Startclock, Stopclock : Real;ππProcedure ClockOn;π beginπ   GetTime(H, M, S, S100);π   StartClock := (H * 3600) + (M * 60) + S + (S100 / 100);πend;ππProcedure ClockOff;π beginπ  GetTime(H, M, S, S100);π  StopClock := (H * 3600) + (M * 60) + S + (S100 / 100);π  WriteLn('Elapsed time = ', (StopClock - StartClock):0:2);π end;ππend.ππ                                                                                          7      05-28-9314:09ALL                      SWAG SUPPORT TEAM        High Resolution Timer    IMPORT              46     «udw {$S-,R-,I-,V-,B-}ππ{*********************************************************}π{*                   TPTIMER.PAS 2.00                    *}π{*                by TurboPower Software                 *}π{*********************************************************}ππUnit TpTimer;π  {-Allows events to be timed With 1 microsecond resolution}ππππInterfaceπConstπ  TimerResolution = 1193181.667;πProcedure InitializeTimer;π  {-ReProgram the timer chip to allow 1 microsecond resolution}ππProcedure RestoreTimer;π  {-Restore the timer chip to its normal state}ππFunction ReadTimer : LongInt;π  {-Read the timer With 1 microsecond resolution}ππFunction ElapsedTime(Start, Stop : LongInt) : Real;π  {-Calculate time elapsed (in milliseconds) between Start and Stop}ππFunction ElapsedTimeString(Start, Stop : LongInt) : String;π  {-Return time elapsed (in milliseconds) between Start and Stop as a String}ππ  {==========================================================================}ππImplementationππVarπ  SaveExitProc : Pointer;π  Delta : LongInt;ππ  Function Cardinal(L : LongInt) : Real;π    {-Return the unsigned equivalent of L as a Real}π  begin                      {Cardinal}π    if L < 0 thenπ      Cardinal := 4294967296.0+Lπ    elseπ      Cardinal := L;π  end;                       {Cardinal}ππ  Function ElapsedTime(Start, Stop : LongInt) : Real;π    {-Calculate time elapsed (in milliseconds) between Start and Stop}π  begin                      {ElapsedTime}π    ElapsedTime := 1000.0*Cardinal(Stop-(Start+Delta))/TimerResolution;π  end;                       {ElapsedTime}ππ  Function ElapsedTimeString(Start, Stop : LongInt) : String;π    {-Return time elapsed (in milliseconds) between Start and Stop as a String}π  Varπ    R : Real;π    S : String;π  begin                      {ElapsedTimeString}π    R := ElapsedTime(Start, Stop);π    Str(R:0:3, S);π    ElapsedTimeString := S;π  end;                       {ElapsedTimeString}ππ  Procedure InitializeTimer;π    {-ReProgram the timer chip to allow 1 microsecond resolution}π  begin                      {InitializeTimer}π    {select timer mode 2, read/Write channel 0}π    Port[$43] := $34;        {00110100b}π    Inline($EB/$00);         {jmp short $+2 ;Delay}π    Port[$40] := $00;        {LSB = 0}π    Inline($EB/$00);         {jmp short $+2 ;Delay}π    Port[$40] := $00;        {MSB = 0}π  end;                       {InitializeTimer}ππ  Procedure RestoreTimer;π    {-Restore the timer chip to its normal state}π  begin                      {RestoreTimer}π    {select timer mode 3, read/Write channel 0}π    Port[$43] := $36;        {00110110b}π    Inline($EB/$00);         {jmp short $+2 ;Delay}π    Port[$40] := $00;        {LSB = 0}π    Inline($EB/$00);         {jmp short $+2 ;Delay}π    Port[$40] := $00;        {MSB = 0}π  end;                       {RestoreTimer}ππ  Function ReadTimer : LongInt;π    {-Read the timer With 1 microsecond resolution}π  begin                      {ReadTimer}π    Inline(π      $FA/                   {cli             ;Disable interrupts}π      $BA/$20/$00/           {mov  dx,$20     ;Address PIC ocw3}π      $B0/$0A/               {mov  al,$0A     ;Ask to read irr}π      $EE/                   {out  dx,al}π      $B0/$00/               {mov  al,$00     ;Latch timer 0}π      $E6/$43/               {out  $43,al}π      $EC/                   {in   al,dx      ;Read irr}π      $89/$C7/               {mov  di,ax      ;Save it in DI}π      $E4/$40/               {in   al,$40     ;Counter --> bx}π      $88/$C3/               {mov  bl,al      ;LSB in BL}π      $E4/$40/               {in   al,$40}π      $88/$C7/               {mov  bh,al      ;MSB in BH}π      $F7/$D3/               {not  bx         ;Need ascending counter}π      $E4/$21/               {in   al,$21     ;Read PIC imr}π      $89/$C6/               {mov  si,ax      ;Save it in SI}π      $B0/$FF/               {mov  al,$0FF    ;Mask all interrupts}π      $E6/$21/               {out  $21,al}π      $B8/$40/$00/           {mov  ax,$40     ;read low Word of time}π      $8E/$C0/               {mov  es,ax      ;from BIOS data area}π      $26/$8B/$16/$6C/$00/   {mov  dx,es:[$6C]}π      $89/$F0/               {mov  ax,si      ;Restore imr from SI}π      $E6/$21/               {out  $21,al}π      $FB/                   {sti             ;Enable interrupts}π      $89/$F8/               {mov  ax,di      ;Retrieve old irr}π      $A8/$01/               {test al,$01     ;Counter hit 0?}π      $74/$07/               {jz   done       ;Jump if not}π      $81/$FB/$FF/$00/       {cmp  bx,$FF     ;Counter > $FF?}π      $77/$01/               {ja   done       ;Done if so}π      $42/                   {inc  dx         ;else count int req.}π      {done:}π      $89/$5E/$FC/           {mov [bp-4],bx   ;set Function result}π      $89/$56/$FE);          {mov [bp-2],dx}π  end;                       {ReadTimer}ππ  Procedure Calibrate;π    {-Calibrate the timer}π  Constπ    Reps = 1000;π  Varπ    I : Word;π    L1, L2, Diff : LongInt;π  begin                      {Calibrate}π    Delta := MaxInt;π    For I := 1 to Reps do beginπ      L1 := ReadTimer;π      L2 := ReadTimer;π      {use the minimum difference}π      Diff := L2-L1;π      if Diff < Delta thenπ        Delta := Diff;π    end;π  end;                       {Calibrate}ππ  {$F+}π  Procedure OurExitProc;π    {-Restore timer chip to its original state}π  begin                      {OurExitProc}π    ExitProc := SaveExitProc;π    RestoreTimer;π  end;                       {OurExitProc}π  {$F-}ππbeginπ  {set up our Exit handler}π  SaveExitProc := ExitProc;π  ExitProc := @OurExitProc;ππ  {reProgram the timer chip}π  InitializeTimer;ππ  {adjust For speed of machine}π  Calibrate;πend.π                                                      8      05-28-9314:09ALL                      SWAG SUPPORT TEAM        Release Time Slices      IMPORT              17     «u╚b {πSome months ago we discussed the problem With Dos Programsπthat eats CPU time in multitask environments (as OS/2),πwhen they're idle.  I have successfully used an Inlineπstatement in my Pascal Programs that calls intr $28, whichπis the Keyboard Busy Flag, For this purpose.  I found thatπInline statement in a TurboPower Program, which they useπto signalize to TSRs that it's OK to interrupt processing.ππHere's the Inline statement I use in keyboard loops:ππ    Inline($CD/$28);ππBut...  This statement doesn't work in the Idle method ofπTurbo Vision Programs...  In our previous discussion onπthis subject, somebody here looked up another intr inπRalph Brown's excellent Compilation list of interrupts.πThis intr, $2F, works in another way by releasing theπreminder of unused time-slice to the operating system.πCalled in a tight Program loop, this means that theπProgram will free up it's idle time to the OS.ππHere's a Function I made that I now use in TV's Idle method:π}ππUsesπ  Dos;ππFunction  ReleaseTimeSlice: Boolean;πVarπ  Regs: Registers;ππbeginπ  With Regs doπ  beginπ    AX := $1680;π    Intr($2F, Regs);π    ReleaseTimeSlice := (AL = $00);  { AL=$80 if not supported by OS }π  end;πend;ππ{π ...and here's how the Idle loop Uses it in a TV Program:π}ππProcedure TMyProgram.Idle;πbeginπ  TApplication.Idle;ππ  { more idle calls go here ... }π  {  :                          }ππ  { Inline($CD/$28); }  { this has no effect on PULSE.EXE by itself }π  ReleaseTimeSlice;     { remember to use $X+ when Compiling the Program }πend;ππ{π...This works fine, judging by PULSE.EXE in OS/2.πRalph Brown also says this works in Windows, tho Windowsπnative Programs may not use it.πMaybe someone can comment on if it's necesarry to alsoπput in the Inline statement above For servicing TSRs.πI can't see any reason For not doing it, but I might'veπoverlooked something here...  :-)ππBorland doesn't do this in their Idle method For TP/BP.πIt should be quite easy to patch this in the RTL code,πFor those of you that have it, and reCompile BP.π}ππ 9      08-27-9322:03ALL                      MARCO MILTENBURG         Giving Timeslices        IMPORT              7      «u   {πMARCO MILTENBURGππ>> if you find SOURCE to detect/give up time slices For Windows/OS/2/Desqview,π>> could you post it? I have stuff For Desqview, I believe.ππ>  Procedure GiveTimeSlice; Inline( $cd/$28 );ππThis is nice, but you have to be sure that you have enough stack space left,πbecause Dos or TSR's that hook this interrupt will use SS:SP For their ownπstack. I use the following in my multitasker detect Unit :π}ππProcedure TimeSlice;πVarπ  Regs : Registers;πbeginπ  Case OS_Type Ofπ    _Dos :π      beginπ      end;ππ    _DV,π    _DVX :π       beginπ         Regs.AX := $1000;π         Intr($15, Regs);π       end;ππ    _OS2,π    _WINS,π    _WIN3:π      beginπ        Regs.AX := $1680;π        Intr($2F, Regs);π      end;π  end;πend;π                         10     08-27-9322:04ALL                      DAVID DAHL               Controling DOS Timer     IMPORT              15     «u   {πDAVID DAHLππI never posted it as a Unit.  I just posted a couple routines to set theπtimer.  They're actually a part of another, larger project I've been workingπon to play digitized Sound out of several different output devices.  When Iπwas asked if it were possible to speed up the tick and still have Dos's timerπFunction behave normally, I threw them into a Unit and wrote the Program youπquoted from to illustrate how it would be done.  Here are the timer routinesπas a Unit:ππThe routines perform no error checking on input values, so be carefulπwith them.  The Procedure Set8253Channel should never have aπchannel value of more than 2 since the 8253 only has 3 channelsπ(0 - 2).π}ππUnit C8253;ππ(* PUBLIC DOMAIN *)ππInterfaceππProcedure SetPlaySpeed(Speed : LongInt);πProcedure SetDefaultTimerSpeed;πProcedure Set8253Channel(ChannelNumber : Byte; ProgramValue  : Word);ππImplementationππConstπ  C8253ModeControl   = $43;π  C8253OperatingFreq = 1193180;π  C8253Channel : Array [0..2] of Byte = ($40, $41, $42);ππ{=[ 8253 Timer Programming Routines ]=====================================}πProcedure Set8253Channel(ChannelNumber : Byte; ProgramValue  : Word);πbeginπ  Port[C8253ModeControl] := 54 or (ChannelNumber SHL 6); { XX110110 }π  Port[C8253Channel[ChannelNumber]] := Lo(ProgramValue);π  Port[C8253Channel[ChannelNumber]] := Hi(ProgramValue);πend;π{-[ Set Clock Channel 0 (INT 8, IRQ 0) To Input Speed ]-------------------}πProcedure SetPlaySpeed (Speed : LongInt);πVarπ  ProgramValue : Word;πbeginπ  ProgramValue := C8253OperatingFreq div Speed;π  Set8253Channel(0, ProgramValue);πend;π{-[ Set Clock Channel 0 Back To 18.2 Default Value ]----------------------}πProcedure SetDefaultTimerSpeed;πbeginπ  Set8253Channel (0, 0);πend;ππend.πππ                                        11     08-27-9322:05ALL                      DANNY MELTON             Free time for DV         IMPORT              8      «u   {π> Does anyone know how to give up your free time under dv or dv/x? Or makeπ> these programs desqview aware?ππDONATED TO THE PUBLIC DOMAIN by Danny Meltonπ}ππprogram YourProgramHere;ππusesπ  DOS, CRT;ππconstπ  MultiTasking : boolean = false;ππfunction UnderDV : boolean;πvarπ  R : registers;πbeginπ  if MultiTasking thenπ    exit;π  R.AX := $1022;π  R.BX := $0000;π  intr($15, R);π  MultiTasking := boolean(R.BX <> 0);π  UnderDV := MultiTasking;πend;ππprocedure GiveUpTimeSlice;πvarπ  R : registers;πbeginπ  if not MultiTasking thenπ    exit;π  R.AX := $1000;π  intr($15, R);πend;ππbeginπ  if UnderDV thenπ    writeln('Running under a multi-tasker.');π  writeln('Press a key when ready');π  while not keypressed doπ    GiveUpTimeSlice;π  writeln('You pressed a key.');πend.ππ                                                                                                                        12     11-02-9306:29ALL                      CEES BINKHORST           Setting Timing at 21Khz  SWAG9311            23     «u   {πCEES BINKHORSTππ>  Has anyone ever succeeded in setting the timer rate at a higher frequencyπ> than 21KHz in protected mode? I've tried every possible thing, and itπCould you give details on that 21KHz? Sounds rather a high rate.ππ> don't know whether I have enough IOPL as to make CLI and STI to work, butπTry the following:π}ππ{dr. dobb's 80286/386 #185}πFunction SensitiveOK : Boolean; Assembler; {sensitive instructions are: }π                                    {IN    read a port           }π                                    {OUT   Write to a port       }π                                    {INS   read a String from a port}π                                    {OUTS  Write a String to a port}π                                    {CLI   disable interrupts    }π                                    {STI   enable interrupts     }πAsmπ  push  axπ  push  bxπ  pushf                             {put flags 'I/O privilege level' (IOPL)}π  pop   ax                          { into ax }π  and   ax, 3000h                   {00110000 00000000 - mask all but iopl}π                                    {ax = 00??0000 00000000 now}π  shr   ax, 12                      {ax -> 00000000 000000??}π                                    {compile With 286 instructions enabled!!}π  mov   iopl, alπ  mov   bx, cs                      {current privilege level (cpl) is in cs}π  and   bx, 3                       {00000000 00000011 - mask all but cpl}π  mov   cpl, blπ  cmp   bx, ax                      {compare cpl and iopl}π  ja    @not_sensitive              {jump  if cpl > iopl}π  clcπ  mov   @result, True               {sensitive instructions ok}π  jmp   @exitπ @not_sensitive:π  stcπ  mov   @result, False              {sensitive instructions not ok}π @exit:π  pop   bxπ  pop   axπend;ππFunction PrivilegeOK: Boolean; Assembler; {privileged instructions are:}π                                    {HLT   halt the processor    }π                                    {LGDT  load the GDT register }π                                    {LIDT  load the interrupt-descriptor-}π                                    {      table register        }π                                    {LLDT  load the LDT register  }π                                    {CLTS  clear the task-switched flag}π                                    {LMSW  load the MSW          }π                                    {LTR   load the task register}πAsmπ  push  axπ  mov   ax, cs                    {cpl resides in cs}π  and   ax, 3                     {00000000 00000011 - mask all but cpl}π                                  {ax = 00000000 000000?? now}π  jnz   @lbl1π  mov   @result, True             {privileged}π  jmp   @exitπ @lbl1:π  mov   @result, False            {not privileged}π @exit:π  pop   axπend;π                                            13     09-26-9309:30ALL                      MARTIN RICHARDSON        Hi-Res Timer             SWAG9311            7      «u   {*****************************************************************************π * Function ...... Timerπ * Purpose ....... Returns the number of seconds since midnightπ * Parameters .... Noneπ * Returns ....... Number of seconds since midnight to the 100th decimial placeπ * Notes ......... Noneπ * Author ........ Martin Richardsonπ * Date .......... May 13, 1992π *****************************************************************************}πFUNCTION Timer : REAL;πVAR hour,π    minute,π    second,π    sec100  : WORD;πBEGINπ     GETTIME(hour, minute, second, sec100);π     Timer := ((hour*60*60) + (minute*60) + (second) + (sec100 * 0.01))πEND;ππ                                                                                                                      14     01-27-9412:23ALL                      CHRIS BOYD               Timing Unit              SWAG9402            40     «u   {π> Now what I want to do is calculate the total run-time of the overallπ> event, from start to finish, i.e., parse the log file taking the last andπ> first time entries and calculate the time. I'm sure there is an easier wayπ> to do this but I'm new to Pascal, and, open to suggestions.  Below is whatπ> appears in the event.log :π}ππUnit Timer;ππ{       SIMPLE TIMER 1.0π        =================ππ This is a Timer unit, it calculates time by system clock.  A few limitationsπ are:ππ   1) Must not modify clock.π   2) Must not time more than a dayπ   3) Must StopTimer before displaying Timeππ   Usage:ππ      StartTimer;   Starts Timerπ      StopTimer;    Stops Timerπ      CalcTimer;    Calculates timeπ      DispTime:     Displays time between StartTimer and StopTimer,π                    you don't need to call CalcTimer if you call DispTime.ππ This unit may be used in freeware and shareware programs as long as:ππ   1) The program is a DECENT program, no "Adult" or "XXX" type programsπ      shall lawfully contain any code found within this file (modified orπ      in original form) or this file after it's been compiled.ππ   2) This copyrighting is not added to, or removed from the program byπ      any other person other than I, the author.ππ This is copyrighted but may be used or modified in programs as long as theπ above conditions are followed.ππ I may be reached at:ππ   1:130/709                              - Fidonetπ   Chris.Boyd@f709.n130.z1.fidonet.org    - Internetπ   Alpha Zeta, Ft. Worth (817) 246-3058   - Bulletin Boardππ If you have any comments or suggestions (not complaints).  I assume noπ responsibility for anything resulting from the usage of this code.ππ                                                   -Chris Boydππ}ππInterfaceππUsesπ  Dos;ππTypeπ  TimeStruct = recordπ    Hour,π    Minute,π    Second,π    S100   : Word;π  End;ππVarπ  StartT,π  StopT,π  TimeT   : TimeStruct;π  Stopped : Boolean;ππprocedure StartTimer;πprocedure StopTimer;πprocedure DispTime;πprocedure CalcTimer;ππImplementationππprocedure TimerError(Err : Byte);πBeginπ  Case Err ofπ    1 :π    Beginπ      Writeln(' Error: Must Use StartTimer before StopTimer');π      Halt(1);π    End;ππ    2 :π    Beginπ      Writeln(' Error: Timer can not handle change of day');π      Halt(2);π    End;ππ    3 :π    Beginπ      Writeln(' Error: Internal - Must StopTimer before DispTime');π      Halt(3);π    End;π  End;πEnd;ππprocedure CalcTimer;πBeginπ  If (Stopped = True) Thenπ  Beginπ    If (StopT.Hour < StartT.Hour) Thenπ      TimerError(2);π    TimeT.Hour := StopT.Hour - StartT.Hour;ππ    If (StopT.Minute < StartT.Minute) Thenπ    Beginπ      TimeT.Hour   := TimeT.Hour - 1;π      StopT.Minute := StopT.Minute + 60;π    End;π    TimeT.Minute := StopT.Minute - StartT.Minute;ππ    If (StopT.Second < StartT.Second) Thenπ    Beginπ      TimeT.Minute := TimeT.Minute - 1;π      StopT.Second := StopT.Second + 60;π    End;π    TimeT.Second := StopT.Second - StartT.Second;ππ    If (StopT.S100 < StartT.S100) Thenπ    Beginπ      TimeT.Second := TimeT.Second - 1;π      StopT.S100   := StopT.S100 + 100;π    End;π    TimeT.S100 := StopT.S100 - StartT.S100;π  Endπ  Elseπ    TimerError(3);πEnd;ππprocedure DispTime;πBeginπ  CalcTimer;π  Write(' Time : ');π  Write(TimeT.Hour);π  Write(':');ππ  If (TimeT.Minute < 10) Thenπ    Write('0');π  Write(TimeT.Minute);π  Write(':');ππ  If (TimeT.Second < 10) Thenπ    Write('0');π  Write(TimeT.Second);π  Write('.');ππ  If (TimeT.S100 < 10) Thenπ    Write('0');π  Writeln(TimeT.S100);πEnd;ππprocedure StartTimer;πBeginπ  GetTime(StartT.Hour, StartT.Minute, StartT.Second, StartT.S100);π  Stopped := False;πEnd;ππprocedure StopTimer;πBeginπ  If (Stopped = False) Thenπ  Beginπ    GetTime(StopT.Hour, StopT.Minute, StopT.Second, StopT.S100);π    Stopped := TRUE;π  Endπ  Elseπ    TimerError(1);πEnd;ππEnd.ππ{πThis is a unit that I wrote.  It will not change day without calling an errorπin itself.  This can be modified though, I just haven't went about doing it.πFor example, if you started the timer at 11:29 pm and stopped it at 1:00 am, itπwouldn't work, but if you started the timer at 12:00 am and stopped it at 11:59πpm in that same day it would work.  The TimeStruct type doesn't store day, justπtime and the only thing you have to do to use it is:ππIn your main program:π}πProgram MyProg;ππUsesπ  Timer;ππBeginπ{ Program stuff.... }πStartTimer;π{ More Program Stuff... }πStopTimer;π{ If you don't want to display the time to the screen, then you need toπ  call CalcTimer, so that it modifies TimeT}πDispTime; {Whenever you want to display the time..  The calculated time isπstored in the record variable Timer.TimeT, if you wanted to access    it.  Allπthe fields of the record a word in type.  To access the hours for example,πyou'd go like:ππ                Timer.TimeT.Hour    or    TimeT.Hourππ           You probably will have to try both.}πEnd.ππ                                                                                     15     01-27-9413:34ALL                      TUBRO POWER SOFTWARE     Timer Code               SWAG9402            46     «u   {$S-,R-,I-,V-,B-}ππ{*********************************************************}π{*                   TPTIMER.PAS 2.00                    *}π{*                by TurboPower Software                 *}π{*********************************************************}ππunit TpTimer;π  {-Allows events to be timed with 1 microsecond resolution}ππinterfaceππprocedure InitializeTimer;π  {-Reprogram the timer chip to allow 1 microsecond resolution}ππprocedure RestoreTimer;π  {-Restore the timer chip to its normal state}ππfunction ReadTimer : LongInt;π  {-Read the timer with 1 microsecond resolution}ππfunction ElapsedTime(Start, Stop : LongInt) : Real;π  {-Calculate time elapsed (in milliseconds) between Start and Stop}ππfunction ElapsedTimeString(Start, Stop : LongInt) : string;π  {-Return time elapsed (in milliseconds) between Start and Stop as a string}ππ  {==========================================================================}ππimplementationππconstπ  TimerResolution = 1193181.667;πvarπ  SaveExitProc : Pointer;π  Delta : LongInt;ππ  function Cardinal(L : LongInt) : Real;π    {-Return the unsigned equivalent of L as a real}π  begin                      {Cardinal}π    if L < 0 thenπ      Cardinal := 4294967296.0+Lπ    elseπ      Cardinal := L;π  end;                       {Cardinal}ππ  function ElapsedTime(Start, Stop : LongInt) : Real;π    {-Calculate time elapsed (in milliseconds) between Start and Stop}π  begin                      {ElapsedTime}π    ElapsedTime := 1000.0*Cardinal(Stop-(Start+Delta))/TimerResolution;π  end;                       {ElapsedTime}ππ  function ElapsedTimeString(Start, Stop : LongInt) : string;π    {-Return time elapsed (in milliseconds) between Start and Stop as a string}π  varπ    R : Real;π    S : string;π  begin                      {ElapsedTimeString}π    R := ElapsedTime(Start, Stop);π    Str(R:0:3, S);π    ElapsedTimeString := S;π  end;                       {ElapsedTimeString}ππ  procedure InitializeTimer;π    {-Reprogram the timer chip to allow 1 microsecond resolution}π  begin                      {InitializeTimer}π    {select timer mode 2, read/write channel 0}π    Port[$43] := $34;        {00110100b}π    inline($EB/$00);         {jmp short $+2 ;delay}π    Port[$40] := $00;        {LSB = 0}π    inline($EB/$00);         {jmp short $+2 ;delay}π    Port[$40] := $00;        {MSB = 0}π  end;                       {InitializeTimer}ππ  procedure RestoreTimer;π    {-Restore the timer chip to its normal state}π  begin                      {RestoreTimer}π    {select timer mode 3, read/write channel 0}π    Port[$43] := $36;        {00110110b}π    inline($EB/$00);         {jmp short $+2 ;delay}π    Port[$40] := $00;        {LSB = 0}π    inline($EB/$00);         {jmp short $+2 ;delay}π    Port[$40] := $00;        {MSB = 0}π  end;                       {RestoreTimer}ππ  function ReadTimer : LongInt;π    {-Read the timer with 1 microsecond resolution}π  begin                      {ReadTimer}π    inline(π      $FA/                   {cli             ;Disable interrupts}π      $BA/$20/$00/           {mov  dx,$20     ;Address PIC ocw3}π      $B0/$0A/               {mov  al,$0A     ;Ask to read irr}π      $EE/                   {out  dx,al}π      $B0/$00/               {mov  al,$00     ;Latch timer 0}π      $E6/$43/               {out  $43,al}π      $EC/                   {in   al,dx      ;Read irr}π      $89/$C7/               {mov  di,ax      ;Save it in DI}π      $E4/$40/               {in   al,$40     ;Counter --> bx}π      $88/$C3/               {mov  bl,al      ;LSB in BL}π      $E4/$40/               {in   al,$40}π      $88/$C7/               {mov  bh,al      ;MSB in BH}π      $F7/$D3/               {not  bx         ;Need ascending counter}π      $E4/$21/               {in   al,$21     ;Read PIC imr}π      $89/$C6/               {mov  si,ax      ;Save it in SI}π      $B0/$FF/               {mov  al,$0FF    ;Mask all interrupts}π      $E6/$21/               {out  $21,al}π      $B8/$40/$00/           {mov  ax,$40     ;read low word of time}π      $8E/$C0/               {mov  es,ax      ;from BIOS data area}π      $26/$8B/$16/$6C/$00/   {mov  dx,es:[$6C]}π      $89/$F0/               {mov  ax,si      ;Restore imr from SI}π      $E6/$21/               {out  $21,al}π      $FB/                   {sti             ;Enable interrupts}π      $89/$F8/               {mov  ax,di      ;Retrieve old irr}π      $A8/$01/               {test al,$01     ;Counter hit 0?}π      $74/$07/               {jz   done       ;Jump if not}π      $81/$FB/$FF/$00/       {cmp  bx,$FF     ;Counter > $FF?}π      $77/$01/               {ja   done       ;Done if so}π      $42/                   {inc  dx         ;Else count int req.}π      {done:}π      $89/$5E/$FC/           {mov [bp-4],bx   ;set function result}π      $89/$56/$FE);          {mov [bp-2],dx}π  end;                       {ReadTimer}ππ  procedure Calibrate;π    {-Calibrate the timer}π  constπ    Reps = 1000;π  varπ    I : Word;π    L1, L2, Diff : LongInt;π  begin                      {Calibrate}π    Delta := MaxInt;π    for I := 1 to Reps do beginπ      L1 := ReadTimer;π      L2 := ReadTimer;π      {use the minimum difference}π      Diff := L2-L1;π      if Diff < Delta thenπ        Delta := Diff;π    end;π  end;                       {Calibrate}ππ  {$F+}π  procedure OurExitProc;π    {-Restore timer chip to its original state}π  begin                      {OurExitProc}π    ExitProc := SaveExitProc;π    RestoreTimer;π  end;                       {OurExitProc}π  {$F-}ππbeginπ  {set up our exit handler}π  SaveExitProc := ExitProc;π  ExitProc := @OurExitProc;ππ  {reprogram the timer chip}π  InitializeTimer;ππ  {adjust for speed of machine}π  Calibrate;πend.π                                                       16     02-03-9407:08ALL                      JAKE CHAPPLE             Events on IRQ/TIMERS     SWAG9402            101    «u   {πFrom: JAKE CHAPPLEπSubj: Events on IRQ/TIMERSπ---------------------------------------------------------------------------π}ππ{----------------------- Beginning of TIMER.PAS -----------------------}πUnit Timer;ππ{========================================================================}π{                           INTERFACE SECTION                            }π{========================================================================}π{                                                                        }π{ This unit implements a set of general purpose, low resolution timers   }π{ for use in any application that requires them.  The design of the      }π{ timer system is adapted from the following magazine article:           }π{                                                                        }π{   Jones S., A High-Performance Lightweight Timer Package, Tech         }π{      Specialist, Vol. 2, No. 1, Jan 1991, pp 17-27.                    }π{                                                                        }π{ Most of Jones' design has been copied, although this implementation is }π{ in Turbo Pascal rather than MASM.  By default, this unit provides 10   }π{ timers, although this can be increased by increasing the value of      }π{ MAX_TIMER and re-compiling.                                            }π{                                                                        }π{ Timers are referenced by "handles" i.e. small integers.  These are     }π{ actually indexes into the timer array.  To obtain a handle one must    }π{ ALLOCATE a timer.  The Allocate function also requires the address of  }π{ a routine to execute when the timer expires as well as a user context  }π{ variable.  The timer function must be compiled as a FAR routine.  The  }π{ user context variable is a 16 bit word of data that can be used for any}π{ application specific purpose.  It is passed to the timer routine when  }π{ the timer expires.  This is useful if a common timer routine is used   }π{ for multiple timers.  It allows the common timer routine to determine  }π{ which timer expired and take appropriate action.                       }π{                                                                        }π{ Once a timer is allocated, it must be STARTED.  The StartTimer         }π{ procedure requires the timer handle and a timer running time.  The     }π{ timer running timer is passed as a RELATIVE number of MILLISECONDS i.e.}π{ the number of milliseconds from now when the timer should expire.      }π{                                                                        }π{ A timer can be stopped before it expires with StopTimer which just     }π{ requires the timer handle.  There is the possibility that the StopTimer}π{ routine could be interrupted by a clock tick and the expiration routine}π{ could run before the StopTimer procedure actually stops the timer.     }π{ It's up to you to guard against this.                                  }π{                                                                        }π{ Finally, an allocated timer can be deallocated with DeallocateTimer    }π{========================================================================}ππINTERFACEππusesπ    Dos;ππtypeπ    UserProc = procedure(context : word);πππfunction  AllocateTimer(UserContext : word; UserRtn : UserProc) : integer;πprocedure StartTimer(handle : integer; rel_timeout : longint);πprocedure StopTimer(handle : integer);πprocedure DeallocateTimer(handle : integer);ππ{========================================================================}π{                        IMPLEMENTATION SECTION                          }π{========================================================================}ππIMPLEMENTATIONππconstπ     MAX_TIMER = 10;            {Total number of timers}π     MILLISECS_PER_TICK = 55;   {clock tick interval}π     TIMER_ALLOCATED = 1;       {bits in the timer flags word}π     TIMER_RUNNING   = 2;ππtypeπ    timer_rec = record                  {Timer descriptor record}π                  timeout : longint;    {Timeout.  Absolute number of millisecs}π                                        {From beginning of program execution}π                  routine : UserProc;   {User procedure to run on expiration}π                  flags   : word;       {Timer status flags}π                  context : word;       {User parameter to pass to User Proc}π                end;πvarπ   timers      : array[1..MAX_TIMER] of timer_rec;   {timer database}π   Int1CSave   : pointer;  {dword to hold original Int $1C vector}π   TimeCounter : longint;  {incremented by 55 millisecs on every entry to ISR}π   ExitSave    : pointer;  {Save the address of next unit exit proc in chain}π   i           : integer;  {loop counter}ππ{$F+}π{------------------------------------------------------------------------}πprocedure Clock_ISR; interrupt;π{------------------------------------------------------------------------}π{ Description:                                                           }π{   This is an interrupt service routine which is hooked into the PC's   }π{   $1C vector.  An Int $1C is generated at each clock tick.  Int $1C is }π{   executed by the hardware interrupt service routine after it has up-  }π{   dated the system time-of-day clock.                                  }π{ Parameters:                                                            }π{   None.                                                                }π{------------------------------------------------------------------------}πvarπ   i : integer;        {local loop counter}πbeginππ  {Update the current time, relative to the start of the program}ππ  inline($FA); {cli}π  TimeCounter := TimeCounter + MILLISECS_PER_TICK; {update millisecond counter}ππ  {Scan the array of timers looking for ones which have expired}ππ  for i := 1 to MAX_TIMER doπ    with timers[i] doπ      if (flags and TIMER_ALLOCATED) > 0 then   {Is this timer allocated? if no}π        if (flags and TIMER_RUNNING) > 0 then   {Is this timer running? if not}π          if timeout <= TimeCounter then begin  {Has this timer expired yet?}π            flags := flags and (not TIMER_RUNNING); {turn off running flag}π            inline($FB);          {sti}π            routine(context);     {call user expiration routine}π            inline($FA);          {cli}π          end;π  inline($FB); {sti}πend;π{$F-}ππ{------------------------------------------------------------------------}πfunction AllocateTimer(UserContext : word; UserRtn : UserProc) : integer;π{------------------------------------------------------------------------}π{ Description:                                                           }π{   Allocate the next available timer in the timer database for use by   }π{   application.                                                         }π{ Parameters:                                                            }π{   UserContext - application specific word of data to be passed to the  }π{                 expiration routine when it is called.                  }π{   UserProc - address of a procedure to be called when the timer expires}π{ Returns:                                                               }π{   Handle - integer from 1 to MAX_TIMER                                 }π{            OR -1 if no timers available.                               }π{------------------------------------------------------------------------}πvarπ   i : integer;πbeginπ  inline($FA); {cli}π  for i := 1 to MAX_TIMER do begin  {scan timer database looking for 1st free}π    with timers[i] do beginπ      if flags = 0 then beginπ         flags := TIMER_ALLOCATED;      {Mark timer as allocated}π         context := UserContext;        {Save users context variable}π         routine := UserRtn;            {Store user routine}π         AllocateTimer := i;            {Return handle to timer}π         inline($FB);                   {Enable interrupts}π         exit;π      end;π    end;π  end;π  { No timers available, return error}π  AllocateTimer := -1;π  inline($FB);πend;ππ{------------------------------------------------------------------------}πprocedure DeallocateTimer(handle : integer);π{------------------------------------------------------------------------}π{ Description:                                                           }π{   Return a previously allocated timer to the pool of available timers  }π{------------------------------------------------------------------------}πbeginπ  timers[handle].flags := 0;πend;πππ{------------------------------------------------------------------------}πprocedure StartTimer(handle : integer; rel_timeout : longint);π{------------------------------------------------------------------------}π{ Description:                                                           }π{    Start an allocated timer ticking.                                   }π{ Parameters:                                                            }π{    Handle - the handle of a previously allocated timer.                }π{    rel_timeout - number of milliseconds before the timer is to expire. }π{------------------------------------------------------------------------}πbeginπ  inline($FA);  {cli}π  with timers[handle] do beginπ    flags := flags or TIMER_RUNNING;       {set timmer running flag}π    timeout := TimeCounter + rel_timeout;  {Convert relative timeout to absolute}π  end;π  inline($FB);  {sti}πend;ππ{------------------------------------------------------------------------}πprocedure StopTimer(handle : integer);π{------------------------------------------------------------------------}π{ Description:                                                           }π{   Stop a ticking timer from running.  This routine does not deallocate }π{   the timer, just stops it.  Remember, it is possible for the clock    }π{   interrupt to interrupt this routine before it actually stops the     }π{   timer.  Therefore, it is possible for the expiration routine to run  }π{   before the timer is stopped i.e. unexpectedly.                       }π{ Parameters:                                                            }π{   Handle - handle of timer to stop.                                    }π{------------------------------------------------------------------------}πbeginπ  with timers[handle] doπ     flags := flags and (not TIMER_RUNNING);πend;ππ{$F+}π{------------------------------------------------------------------------}πProcedure myExitProc;π{------------------------------------------------------------------------}π{ Description:                                                           }π{  This is the unit exit procedure which is called as part of a chain of }π{  exit procedures at program termination.                               }π{------------------------------------------------------------------------}πbeginπ  ExitProc := ExitSave;  {Restore the chain so other units get a turn}π  SetIntVec($1C, Int1CSave);     {restore the original Int $1C vector}πend;π{$F-}ππ{=========================================================================}π{                        INITIALIZATION SECTION                           }π{=========================================================================}ππBegin {unit initialization code}ππ  (* Establish the unit exit procedure *)ππ  ExitSave := ExitProc;π  ExitProc := @myExitProc;ππ  {Initialize the timers database and install the custom Clock ISR}ππ  for i := 1 to MAX_TIMER do   {clear flag word for all timers}π     timers[i].flags := 0;π  TimeCounter := 0;              {clear current time counter}π  GetIntVec($1C, Int1CSave);     {Save original Int $1C vector}π  SetIntVec($1C, @Clock_ISR);    {install the the clock ISR}πend.ππ{------------------------- End of TIMER.PAS -----------------------------}ππ{---------------------- Beginning of TIMERTST.PAS -----------------------}πprogram timer_test;ππusesπ    Crt, timer;πvarπ    t1, t2 : integer; {timer handles}π    done   : boolean;ππ{---- Procedure to be run when timer 1 expires ----}πprocedure t1_proc(context1 : word); far;πbeginπ  writeln('Timer ',context1);π  StartTimer(t1, 1000);        {Keep timer 1 running}πend;ππ{---- Procedure to be run when timer 2 expires ----}πprocedure t2_proc(context2 : word); far;πbeginπ  done := true;π  writeln('Timer ',context2,' expired');πend;ππbeginπ  ClrScr;π  done := false;π  t1 := AllocateTimer(1, t1_proc);        {Create timer 1}π  t2 := AllocateTimer(2, t2_proc);        {Create timer 2}π  StartTimer(t2, 5000);        {Start timer 2 for 5 second delay}π  StartTimer(t1, 1000);        {Start timer 1 for 1 second delay}π  while not done do begin      {Do nothing until timer 2 expires}π     end;π  StopTimer(t1);πend.π