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

    Unit Clock;
    {
      Author:  Trevor J Carlsen
      Purpose: Demonstrate a simple "on screen" clock.

      This demo Unit works by "hooking" the timer interrupt ($1c). This
      interrupt is called by the hardware interrupt ($08) approximately 18.2
      times every second and normally consists of a simple return instruction
      unless some other application has already hooked it.

      Because the routine is called roughly 18 times every second it is
      important that any processing it contains is optimised as much as
      possible.  Obviously the best way to do this is by assembly language but
      in this demo I have used almost pure Turbo Pascal and have set up a
      counter Variable and any processing is only done every 6 calls.  This is
      more than sufficient and minimises processing. The routine is by no
      means perfect - there will be a minor irregularity For the final 10
      seconds of each day and For about half a second each hour. Better this
      than to waste valuable processing time in the interrupt by coding it
      out.

      Because Dos is not re-entrant it is also important that the routine make
      no calls to any Procedure or Function that makes use of Dos For its
      operation. Thus no Writeln, Write can be used.  To display the time on
      screen an Array is addressed directly to screen memory.  Any changes in
      this Array are thus reflected on the screen.  The downside to this is
      that on older CGAs this would cause a "snow" effect and code would be
      needed to eliminate this. It also means that the TP Procedure GetTime
      cannot be used.  So the time is calculated from the value stored at the
      clock tick counter location.

      To display an on-screen clock all that is required is For a Programmer
      to include this Unit in the Uses declaration of the Program.}

    Interface

    Const
      DisplayClock : Boolean = True;

    Implementation
    { Everything is private to this Unit }

    Uses Dos;

    Const
      line          = 0;  { Change as required For position of display on screen }
      column        = 72;                               { Top left corner is 0,0 }
      ScreenPos     = (line * 160) + (column * 2);
      Colour        = $1f;                                       { White on Blue }
      ZeroChar      = Colour shl 8 + ord('0');
      Colon         = Colour shl 8 + ord(':');
    Type
      timestr       = Array[0..7] of Word;
      timeptr       = ^timestr;
    Var
      time          : timeptr;
      OldInt1c      : Pointer;
      ExitSave      : Pointer;

    {$F+}
     Procedure Int1cISR; interrupt;
      { This will be called every clock tick by hardware interrupt $08 }
      Const
        count       : Integer = 0;                  { To keep track of our calls }
      Var
        hr          : Word Absolute $40:$6e;
        ticks       : Word Absolute $40:$6c;
                      { This location keeps the number of clock ticks since 00:00}
        min,
        sec         : Byte;
        seconds     : Word;
      begin
        Asm cli end;
        if DisplayClock then begin
          inc(count);
          if count = 6 then { ticks to update the display } begin
            count       := 0;  { equality check and assignment faster than mod 9 }
            seconds     := ticks * LongInt(10) div 182;       { speed = no Reals }
            min         := (seconds div 60) mod 60;
            sec         := seconds mod 60;

          { The following statements are what actually display the on-screen time}

            time^[0]    := ZeroChar + (hr div 10);         { first Char of hours }
            time^[1]    := ZeroChar + (hr mod 10);        { second Char of hours }
            time^[2]    := Colon;
            time^[3]    := ZeroChar + (min div 10);      { first Char of minutes }
            time^[4]    := ZeroChar + (min mod 10);     { second Char of minutes }
            time^[5]    := Colon;
            time^[6]    := ZeroChar + (sec div 10);      { first Char of seconds }
            time^[7]    := ZeroChar + (sec mod 10);     { second Char of seconds }
          end;  { if count = 6 }
        end;  { if DisplayClock }
        Asm
          sti
          pushf                                  { push flags to set up For IRET }
          call OldInt1c;                              { Call old ISR entry point }
        end;
      end; { Int1cISR }

    Procedure ClockExitProc;
      { This Procedure is VERY important as you have hooked the timer interrupt  }
      { and therefore if this is omitted when the Unit is terminated your        }
      { system will crash in an unpredictable and possibly damaging way.         }
      begin
        ExitProc := ExitSave;
        SetIntVec($1c,OldInt1c);               { This "unhooks" the timer vector }
      end;
    {$F-}

    Procedure Initialise;
      Var
        mode : Byte Absolute $40:$49;
      begin
        if mode = 7 then                                { must be a mono adaptor }
          time := ptr($b000,ScreenPos)
        else                                       { colour adaptor of some kind }
          time := ptr($b800,ScreenPos);
        GetIntVec($1c,OldInt1c);              { Get old timer vector and save it }
        ExitSave := ExitProc;                          { Save old Exit Procedure }
        ExitProc := @ClockExitProc;                 { Setup a new Exit Procedure }
        SetIntVec($1c,@Int1cISR);   { Hook the timer vector to the new Procedure }
      end;  { Initialise }

    begin
      Initialise;
    end.

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