{----------------------------------------------------------------------} 
{                                                                      } 
{  TIMER.INC - simple timer interrupt routine                          } 
{  increments a counter 18.2 times a second                            } 
{                                                                      } 
{  This code is intended to be included within a larger program.       } 
{  The main program must first call Timer_Init to initialize the       } 
{  interrupt vector. Before terminating, Timer_Close must be called    } 
{  to restore the interrupt vector contents.                           } 
{  Timer_Counter contains the elapsed time and canbe interrogated and } 
{  reset by the main routine.                                          } 
{                                                                      } 
{  This code is heavily dependant on the characteristics of the IBM PC } 
{  and PC DOS release 2.0.                                             } 
{                                                                      } 
{  Michael Quinlan                                                     } 
{                                                                      } 
{----------------------------------------------------------------------} 
 
{ CONST areas are used because on entry, the interrupt service routine 
  only has the correct Code Segment; the Data Segment register value must 
  be obtained from the Code Segment } 
 
const 
  Timer_DSeg_Save : Integer = 0;  { save DS register in Code segment for ISR } 
 
const 
  Timer_Vector = $1C;  { Interrupt vector for timer routine } 
 
const 
  Timer_Old_Int : record  { area to save the old interrupt vector value } 
                    o, s : integer 
                  end = (o:0; s:0);  { must be in CODE segment for ISR } 
 
var 
 Timer_Counter : Integer;  { counter incremented 18.2 times a second } 
 
var 
  DOS_Regs : Record 
               ax, bx, cx, dx, bp, si, di, ds, es, flag : integer 
             end; 
 
procedure DOS_Set_Intrpt(v, s, o : integer); 
{ call PC DOS to set an interrupt vector } 
begin 
  with DOS_Regs do 
    begin 
      ax := $2500 + (v and $00FF); 
      ds := s; 
      dx := o; 
      MsDos(DOS_Regs) 
    end 
end; { DOS_Set_Intrpt } 
 
procedure DOS_Get_Intrpt(v : integer; var s, o : integer); 
{ call PC DOS to get the value of an interrupt vector } 
begin 
  with DOS_Regs do 
    begin 
      ax := $3500 + (v and $00FF); 
      MsDos(Dos_Regs); 
      s := es; 
      o := bx 
    end 
end; { DOS_Get_Intrpt } 
 
{----------------------------------------------------------------------} 
{                                                                      } 
{  TIMERISR -- PCDOS Timer Interrupt. Increments a counter 18.2 times  } 
{  a second.                                                           } 
{                                                                      } 
{----------------------------------------------------------------------} 
 
procedure Timer_Isr; 
begin 
 
    { on entry, Turbo Pascal has saved BP and SP on the stack } 
    { the manual is incorrect } 
 
  Inline( 
      { save all registers used } 
    $50/                             { PUSH AX } 
    $1E/                             { PUSH DS } 
      { set up the DS register to point to the data segment } 
    $2E/$FF/$36/Timer_DSeg_Save/     { PUSH CS:Timer_DSeg_Save } 
    $1F/                             { POP DS } 
    $FA/                             { CLI } 
    $A1/Timer_Counter/               { MOV AX,Timer_Counter } 
    $3D/MaxInt/                      { CMP AX,MaxInt } 
    $74/$04/                         { JE L001 } 
    $FF/$06/Timer_Counter/           { INC Timer_Counter } 
{L001:} 
    $FB/                             { STI } 
      { invoke any other timer routines installed } 
      { it will be her responsibility to issue the IRET } 
      { restore the registers first } 
    $1F/                             { POP DS } 
    $58/                             { POP AX } 
    $5C/                             { POP SP } 
    $5D/                             { POP BP } 
    $2E/$FF/$2E/Timer_Old_Int)       { JMP CS:Timer_Old_Int (cross-segment, 
                                                             indirect) } 
end; { Timer_Isr } 
 
procedure Timer_Init; 
{ install the timer interrupt } 
begin 
  Timer_DSeg_Save := DSeg;  { save DS register for ISR } 
    { get the current contents of the timer interrupt vector } 
    { the TIMER ISR will invoke this to finish any timer processing } 
  DOS_Get_Intrpt(Timer_Vector, Timer_Old_Int.s, Timer_Old_Int.o); 
    { begin executing the timer interrupt } 
  DOS_Set_Intrpt(Timer_Vector, CSeg, Ofs(Timer_Isr)) 
end; { Timer_Init } 
 
procedure Timer_Close; 
{ remove the timer interrupt } 
begin
  DOS_Set_Intrpt(Timer_Vector, Timer_Old_Int.s, Timer_Old_Int.o)
end; { Timer_Close }
    d                                                                                                                                                                                                                                                          