[Back to TIMING SWAG index]  [Back to Main SWAG index]  [Original]


UNIT asytimer;
{Purpose  : High resolution timer which runs asynchronous to the     }
{           rest of the program                                      }
{Author   : Kai Rohrbacher, kai.rohrbacher@logo.ka.sub.org           }
{Language : BorlandPascal 7.0 }
{Date     : 26.06.1994        }
{Remarks  : - Runs both in real- and protected mode.                 }
{           - Only available on AT-style machines or better (uses    }
{             real time clock services)                              }
{           - Will "fall through" on PC's transparently: behaves as  }
{             if time ran off immediately}

INTERFACE

VAR TimeFlag:^BYTE;

FUNCTION ATClockAvailable:BOOLEAN;
PROCEDURE SetCycleTime(microseconds:LONGINT);
FUNCTION TimeOver:BOOLEAN;
  INLINE($C4/$1E/TimeFlag/   {LES BX,TimeFlag}
         $26/$8A/$07/        {MOV AL,ES:[BX] }
         $B1/$07/            {MOV CL,7 }
         $D2/$E8);           {SHR AL,CL}
PROCEDURE Trigger;

IMPLEMENTATION

USES CRT;

{$IFDEF DPMI}
TYPE Treg=RECORD  {stuff for that dumb DPMI-server}
           CASE BYTE OF
            0:(LoLo,LoHi,HiLo,HiHi:BYTE);
            1:(Lo16,Hi16:WORD);
          END;
     Tregisters32=
       RECORD
         EDI,ESI,EBP,junk32,EBX,EDX,ECX,EAX:Treg;
         Flags32,ES,DS,FS,GS,IP,CS,SP,SS:WORD
       END;
VAR regs32:Tregisters32;

 FUNCTION EmulateInt(IntNr:BYTE; VAR regs32:Tregisters32):BOOLEAN;
 ASSEMBLER; {emulate real mode interrupt IntNr with registers regs32}
 ASM
   MOV AX,300h   {emulate INT}
   XOR BH,BH     {no A20 gate reset, please}
   MOV BL,IntNr  {INT to emulate}
   XOR CX,CX     {no parameter passing via PM stack}
   LES DI,regs32 {pointer to register set}
   INT 31h       {go for it}
   CMC           {carry flag set if error, reflect this}
   MOV AX,0      {as a BOOLEAN value: return TRUE if C=0}
   ADC AX,AX     {and FALES otherwise}
 END;
{$ENDIF}

VAR CycleTimeLo16,CycleTimeHi16:WORD;
    IsAT:BYTE;

{$IFDEF DPMI}
FUNCTION ATClockAvailable:BOOLEAN; {protected mode function}
BEGIN
 TimeFlag^:=0;             {reset flag}
 FillChar(regs32,SizeOf(regs32),0);
 regs32.ECX.Lo16:=0;
 regs32.EDX.Lo16:=1;       {trigger flag after 1us}
 regs32.ES      :=$40;     {_segment_ address of Timeflag}
 regs32.EBX.Lo16:=Ofs(TimeFlag^); {offset part = $F0}
 regs32.EAX.Lo16:=$8300;

 IF NOT EmulateInt($15,regs32)
  THEN WRITELN('Something went wrong in the INT-emulation!?');

 Delay(1); {INT-emulation went ok, look for timer event:}
           {wait 1000us, so event must have happened:}
 {Flag now should have been set to $80:}
 ATClockAvailable:=TimeFlag^=$80;
END;

{$ELSE}

FUNCTION ATClockAvailable:BOOLEAN; {real mode function}
BEGIN
 TimeFlag^:=0;             {reset flag}
 IF Test8086<>0  {is it at least an AT?}
  THEN ASM {yes, have a closer look:}
         STI
         XOR CX,CX       {trigger after 1us}
         MOV DX,1
         LES BX,TimeFlag {set Flag to $80 after this time}
         MOV AX,8300h    {run asynchron to rest of program}
         INT 15h         {go!}
       END;
 Delay(1);               {wait a 1000us}
 ATClockAvailable:=TimeFlag^=$80 {Flag=$80, if it worked}
END;
{$ENDIF}

PROCEDURE SetCycleTime(microseconds:LONGINT);
BEGIN
 TimeFlag^:=$80;
 CycleTimeHi16:=microseconds SHR 16;
 CycleTimeLo16:=microseconds AND $FFFF;
 IF (microseconds<>0) AND ATClockAvailable
  THEN IsAT:=0     {ja, Zeitberwachung soll benutzt werden  }
  ELSE IsAT:=$80   {nein, keine m”glich oder nicht gewnscht }
END;

PROCEDURE Trigger;
{starts timer, which must have previously been set by SetCycleTime()}
BEGIN
 IF IsAT<>0 THEN EXIT; {jmp out, if timer services unavailable}
 TimeFlag^:=0;
{$IFDEF DPMI}
 regs32.ECX.Lo16:=CycleTimeHi16;
 regs32.EDX.Lo16:=CycleTimeLo16;  {trigger flag after t us}
 regs32.ES      :=$40;            {_segment_ address of Timeflag}
 regs32.EBX.Lo16:=Ofs(TimeFlag^); {offset part = $F0}
 regs32.EAX.Lo16:=$8300;

 IF NOT EmulateInt($15,regs32)
  THEN WRITELN('Something went wrong in the INT-emulation!?');
{$ELSE}
ASM
  MOV CX,CycleTimeHi16
  MOV DX,CycleTimeLo16
  LES BX,TimeFlag {set Flag to $80 after this time}
  MOV AX,8300h    {run asynchron to rest of program}
  INT 15h         {go!}
END;
{$ENDIF}
END;

BEGIN
 TimeFlag:=Ptr(Seg0040,$F0); {available byte in 1st MB}
 SetCycleTime(0)
END.

____

PROGRAM TestUnit_asytimer;
{Kai Rohrbacher, kai.rohrbacher@logo.ka.sub.org}
USES asytimer;
CONST wait:LONGINT=5000000; {trigger time in us -> 5sec}

 PROCEDURE SomeThing;
 CONST s:ARRAY[0..3] OF CHAR='\|/-';
       help:BYTE=0;
 BEGIN WRITE(s[help]+^H); help:=(help+1) AND 3 END;

BEGIN
 IF ATClockAvailable
  THEN WRITELN('INT15h-timer-routine available!')
  ELSE WRITELN('INT15h-timer-routine doesn''t work!');

 SetCycleTime(wait);
 WRITELN('Between the following 2 bells, there should be a delay of ',
         wait,' microseconds');
 Trigger;    {wait 5s = 5000ms}
 WRITE(#7);
 WHILE NOT TimeOver DO SomeThing;
 WRITELN(#7'Done!');
END.

[Back to TIMING SWAG index]  [Back to Main SWAG index]  [Original]