{$I-,S-}
{$IFNDEF OS2}
{$R-,V-,B-,F+,O+,A-}
{$ENDIF}

{Conditional defines that may affect this unit}
{$I APDEFINE.INC}

{*********************************************************}
{*                    APTIMER.PAS 2.03                   *}
{*     Copyright (c) TurboPower Software 1991.           *}
{*                 All rights reserved.                  *}
{*********************************************************}

Unit ApTimer;
  {-BIOS timing functions}

Interface

Uses
{$IFNDEF OS2}
  Dpmi;
{$ELSE}
  tMisc;
{$ENDIF}

Const
  TicsPerDay = 1573040;      {Assumes 18.20648 tics/sec}
  SecsPerDay = 86400;     {Number of seconds in one day}

  {Clock frequency of 1193180/65536 is reduced to 1675/92. This}
  {allows longint conversions of tics values upto TicsPerDay}
  TicsFreq = 1675;
  SecsFreq = 92;

Var
  CountsPerMs : Word;         {Tight loop count for one millisecond}

{$I APTIMER.PA0}

Implementation

Var
  {!!.01 moved semicolons for Stony Brook}
  {Data area for BIOS tics value}
  BiosTics : ^LongInt {$IFDEF STONYBROOK} VOLATILE {$ENDIF} ; {absolute $40:$6C;}
  BiosTicsLow : ^Word {$IFDEF STONYBROOK} VOLATILE {$ENDIF} ; {absolute $40:$6C;}

Function Tics2Secs (Tics : LongInt) : LongInt;
  {-Returns seconds value for Tics tics}
Begin
  Tics2Secs := ( (Tics + 9) * SecsFreq) Div TicsFreq;
End;

Function Secs2Tics (Secs : LongInt) : LongInt;
  {-Returns tics value for Secs seconds}
Begin
  Secs2Tics := (Secs * TicsFreq) Div SecsFreq;
End;

Procedure NewTimer (Var ET : EventTimer; Tics : LongInt);
  {-Returns a set EventTimer that will expire in Tics}
Begin
  {Max acceptable value is 24 hours}
  If Tics > TicsPerDay Then
    Tics := TicsPerDay;

  With ET Do Begin
    {$IFNDEF OS2}
    StartTics := BiosTics^;
    {$ELSE}
    StartTics := Secs2Tics (MidSec);
    {$ENDIF}

    ExpireTics := StartTics + Tics;
  End;
End;

Procedure NewTimerSecs (Var ET : EventTimer; Secs : LongInt);
  {-Returns a set EventTimer}
Begin
  NewTimer (ET, Secs2Tics (Secs) );
End;

Function TimerExpired (ET : EventTimer) : Boolean;
  {-Returns True if ET has expired}
Var
  CurTics : LongInt;
Begin
  With ET Do Begin
    {Get current tics; assume timer has expired}
    {$IFNDEF OS2}
    CurTics := BiosTics^;
    {$ELSE}
    CurTics := Secs2Tics (MidSec);
    {$ENDIF}

    TimerExpired := True;

    {Check normal expiration}
    If CurTics > ExpireTics Then
      Exit;
    {Check wrapped CurTics}
    If (CurTics < StartTics) And ( (CurTics + TicsPerDay) > ExpireTics) Then
      Exit;

    {If we get here, timer hasn't expired yet}
    TimerExpired := False;
  End;
End;

Function ElapsedTime (ET : EventTimer) : LongInt;
  {-Returns elapsed time, in tics, for this timer}
Var
  CurTics : LongInt;
Begin
  With ET Do Begin
    {$IFNDEF OS2}
    CurTics := BiosTics^;
    {$ELSE}
    CurTics := Secs2Tics (MidSec);
    {$ENDIF}

    If CurTics >= StartTics Then
      {No midnight wrap yet}
      ElapsedTime := CurTics - StartTics
    Else
      {Got a midnight wrap, account for it}
      ElapsedTime := (TicsPerDay - StartTics) + CurTics;
  End;
End;

Function ElapsedTimeInSecs (ET : EventTimer) : LongInt;
  {-Returns elapsed time, in seconds, for this timer}
Begin
  ElapsedTimeInSecs := Tics2Secs (ElapsedTime (ET) );
End;

Function ElapsedTimeInMSecs (ET : EventTimer) : LongInt;
  {-Returns elapsed time, in milliseconds, for this timer}
Begin
  ElapsedTimeInMSecs := ElapsedTime (ET) * 55;
End;

Function RemainingTime (ET : EventTimer) : LongInt;
  {-Returns remaining time, in tics, for this timer}
Var
  CurTics : LongInt;
  RemainingTics : LongInt;
Begin
  With ET Do Begin
    {$IFNDEF OS2}
    CurTics := BiosTics^;
    {$ELSE}
    CurTics := Secs2Tics (MidSec);
    {$ENDIF}

    If CurTics >= StartTics Then
      {No midnight wrap yet}
      RemainingTics := ExpireTics - CurTics
    Else
      {Got a midnight wrap, account for it}
      RemainingTics := (ExpireTics - TicsPerDay) - CurTics;
  End;
  If RemainingTics < 0 Then
    RemainingTime := 0
  Else
    RemainingTime := RemainingTics;
End;

Function RemainingTimeInSecs (ET : EventTimer) : LongInt;
  {-Returns remaining time, in seconds, for this timer}
Begin
  RemainingTimeInSecs := Tics2Secs (RemainingTime (ET) );
End;

Function RemainingTimeInMSecs (ET : EventTimer) : LongInt;
  {-Returns remaining time (in milliseconds) for this timer}
Begin
  RemainingTimeInMSecs := RemainingTime (ET) * 55;
End;

Procedure DelayTics (Tics : LongInt);
  {-Delay for Tics tics}
Var
  ET : EventTimer;
Begin
  If Tics <= 0 Then
    Exit
  Else If Tics > TicsPerDay Then
    Tics := TicsPerDay;

  NewTimer (ET, Tics);
  Repeat
  Until TimerExpired (ET);
End;

{!!.02}
{The following 3 BASM routines completely replace the old Pascal}
{versions of Delay and CalibrateDelay.                          }

{$IFNDEF OS2}
Procedure DelayMS; Assembler;
Asm
  @@DMSdec:
  sub   AX, 1
  sbb   DX, 0
  jc    @@DMSexit
  cmp   BL, ES: [DI]
  je    @@DMSdec
  @@DMSexit:
End;

Procedure Delay (MS : Word); Assembler;
Asm
  mov   CX, MS
  jcxz  @@DelayExit
  mov   ES, BiosDataSele
  XOr   DI, DI
  mov   BL, ES: [DI];
  @@DelayLoop:
  mov   AX, CountsPerMS
  XOr   DX, DX
  Call  DelayMS
  loop  @@DelayLoop
  @@DelayExit:
End;

Procedure CalibrateDelay; Assembler;
Asm
  mov   ES, BiosDataSele
  mov   DI, 6CH
  sti
  mov   BL, ES: [DI]
  @@WaitForChange:
  cmp   BL, ES: [DI]
  jz    @@WaitForChange

  mov   AX, 0ffe4h
  cwd
  mov   BL, ES: [DI]
  Call  DelayMS
  Not   AX
  Not   DX

  mov   CX, 55
  cmp   dx, cx
  jb    @@1
  mov   ax, -1
  jmp   @@2
@@1:
  Div   CX
@@2:
  mov   CountsPerMS, AX
End;
{$ENDIF}

Begin
  {$IFNDEF OS2}
  BiosTics := Ptr (BiosDataSele, $6C);
  BiosTicsLow := Ptr (BiosDataSele, $6C);
  CalibrateDelay;  {initializes CountsPerMs for Delay}
  {$ENDIF}
End.
