home *** CD-ROM | disk | FTP | other *** search
- {
- 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.