-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtimerint.pas
executable file
·347 lines (303 loc) · 13.2 KB
/
timerint.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
{ +-------------------------------------------------------------------+
| Project : Generic | Filename : TIMERINT.PAS |
| Coded By : Lost (Jacob Lister) | Version : 1.0 |
|--========-------------+---------+--========-----------------------|
| Date : 05/07/97 | Created |
| 06/07/97 | Got it working |
| 24/07/97 | Interrupt handler now saves state of |
| | flags |
| 21/08/97 | Cleaned up for SWAG release |
|--========-------------+--========---------------------------------|
| Purpose : General purpose timer interrupt functions, allow many |
| handlers to be strung onto the one timer, and |
| re-programs the clock rate as nessacary |
| Notes : There are some very messy fiddles used here to get the|
| interrupt handler working with the sub-fuction record |
| table, this is OK because the crappy code is confined |
| to this unit. Take care of what procedures you use |
| this unit to call, for example 'writeln' will behave |
| unstabily if it is being called by both an IRQ and |
| other code at the same time, this goes for most |
| procedures. |
| Wish List: Optimize the interrupt handler (timer cirtical) |
|--========---------------------------------------------------------|
| Credits : Some interrupt programming info from SWAG |
+--========---------------------------------------------------------+ }
{ Usage : to install a handler, call the 'install_handler' with a pointer
to your procedure and the interval rate in Hz that it is to be
called. You handlers MUST be far code, force this by adding
the directive 'far' to the procedure header, eg
procedure mytest; far;
Your procedures are free to use all CPU registers as the unit
handles preserving these.
I beleive it is possible for procedures in different units to
use different data segments, if this is the case for your code,
your handlers must point DS to their own data segment, to do
this, put these lines at the start of your handler:
asm
mov ax, seg @data
mov ds, ax
end;
You can de-install handlers by calling 'remove_handler',
alternativly the program will handle all de-installation on
exit }
unit timerint;
INTERFACE
procedure install_handler(address : pointer; rate : longint);
procedure remove_handler(address : pointer);
IMPLEMENTATION
uses dos;
const USE32 = $66; {32 bit instruction prefix}
const clock_rate = 1192755; {Timer rate (Hz)}
dos_rate = 63356; {The rate dos service call is called}
var tick_rate : longint; {The current timer rate}
dos_tick : longint; {number of ticks since last dos
timer service called }
old_handler : pointer; {Variable to save old int 8 handler}
old_exit : pointer; {save for exit chain}
type timer_int_function = record
address : pointer; {Pointer to the handler}
rate : longint; {Required rate in timer ticks}
ticks : longint; {ticks since last call}
active : boolean; {Is this handler active?}
end;
const t_address = $0; {Address values for the timer record}
t_rate = $4; {Needed, as they cannot be directly}
t_ticks = $8; {derived for the inline ASM}
t_active = $C;
t_size = $D;
const num_handlers = 5;
var timer_ints : array[1..num_handlers] of timer_int_function;
{Variables for interrupt service rountine.}
var call_adr : pointer;
{Re-programs the timer to genererate an interrupt every 'ticks' ticks }
procedure set_clock_rate(ticks : longint);
begin
port[$43] := $36;
port[$40] := lo(ticks);
port[$40] := hi(ticks);
tick_rate := ticks;
end;
{ +-------------------------------------------------------------------+
| Procedure : find_clock_rate |
| Callers : install_handler, remove_handler |
| Purpose : Finds the maximum clock rate needed, and sets the |
| timer to run at that rate |
+--========---------------------------------------------------------+
| Input : nothing |
| Output : nothing |
| Destroys : nothing |
+--========---------------------------------------------------------+
| Notes : |
+--========---------------------------------------------------------+ }
procedure find_clock_rate;
var i : byte;
max_rate : longint;
begin
max_rate := dos_rate;
for i:=1 to num_handlers do
if (timer_ints[i].active = true) and (timer_ints[i].rate < max_rate)
then max_rate := timer_ints[i].rate;
set_clock_rate(max_rate);
end;
{ +-------------------------------------------------------------------+
| Procedure : install_handler |
| Callers : Anything |
| Purpose : Installs an interrupt handling funtion on to the |
| interrupt. If the requested rate cannot be handled |
| by the current clock rate, the clock rate is changed |
+--========---------------------------------------------------------+
| Input : address - Pointer to code to run on interrupt |
| rate - the rate at which the hander is to run |
| (in Hz) |
| Output : |
| Destroys : nothing |
+--========---------------------------------------------------------+
| Notes : |
+--========---------------------------------------------------------+ }
procedure install_handler(address : pointer; rate : longint);
var handler_tick_rate : longint;
i : byte;
begin
port[$43] := $36; {lower clock rate so interrupts don't}
port[$40] := 0; {occur while we are installing handler}
port[$40] := 0;
handler_tick_rate := clock_rate div rate;
for i:=1 to num_handlers do begin
if(timer_ints[i].active = false) then begin
timer_ints[i].active := true;
timer_ints[i].address := address;
timer_ints[i].rate := handler_tick_rate;
timer_ints[i].ticks := 0;
break;
end;
end;
find_clock_rate;
end;
{ +-------------------------------------------------------------------+
| Procedure : remove_handler |
| Callers : Anything |
| Purpose : Searches for the a specified interrupt handler, and |
| removes it, then re-adjusts the clock rate if |
| nessecary |
+--========---------------------------------------------------------+
| Input : address - pointer to handler routine to remove |
| Output : nothing |
| Destroys : info for specified handler |
+--========---------------------------------------------------------+
| Notes : Destroys the first found case of routine. If more |
| than one instance of the same handler function, only |
| the first will be destroyed. |
+--========---------------------------------------------------------+ }
procedure remove_handler(address : pointer);
var i : integer;
begin
port[$43] := $36; {lower clock rate so interrupts don't}
port[$40] := 0; {occur which we are removing handler}
port[$40] := 0;
for i:=1 to num_handlers do
if timer_ints[i].address = address then
timer_ints[i].active := false;
find_clock_rate;
end;
{ +-------------------------------------------------------------------+
| Procedure : interrupt_handler |
| Callers : interrupt 8 |
| Purpose : Dispacthes many timer functions off the one timer |
| interrupt as required. Calls the original DOS IRQ8 |
| handler as required |
+--========---------------------------------------------------------+
| Input : nothing |
| Output : nothing |
| Destroys : nothing |
+--========---------------------------------------------------------+
| Notes : |
+--========---------------------------------------------------------+ }
procedure interrupt_handler; assembler;
asm
pushf
db USE32; pusha
push ds
push es
push ss
mov ax, seg @data
mov ds, ax
mov di, OFFSET timer_ints {Point to our handlers}
mov cl, num_handlers {Get the number of handlers}
@@L0:
mov al, 0
cmp [di+t_active], al
jz @@Next_Handle {Check if handle is active}
db USE32; mov ax, [di+t_ticks]
db USE32; add ax, word ptr tick_rate
db USE32; cmp ax, [di+t_rate]
jl @@Next_Handle {Is it time for the next handle call?}
db USE32; sub ax, [di+t_rate] {reset our tick count}
db USE32; mov bx, [di+t_address]
db USE32; mov word ptr call_adr, bx
push ax
push cx
push di
push ds
CALL call_adr
pop ds
pop di
pop cx
pop ax
@@Next_Handle:
db USE32; mov [di+t_ticks], ax {Save back the handle tick info}
add di, t_size
dec cl
jnz @@L0
mov al,$20
out $20,al
db USE32; mov ax, word ptr dos_tick
db USE32; add ax, word ptr tick_rate
db USE32; mov word ptr dos_tick, ax
db USE32; cmp ax, 0000; dw 1 {next dos call?}
jl @@NoDos
db USE32; sub ax, 0000h; dw 1
db USE32; mov word ptr dos_tick, ax {reset dos rate counter}
pushf
call [old_handler] {never returns}
@@NoDos:
pop ss
pop es
pop ds
db USE32; popa
popf
iret
end;
{ Clean up code runs when program finishes, resets the timer interrupt
handler, and resets the clock }
procedure timer_exit; far;
begin
asm cli end;
ExitProc := old_exit;
port[$43] := $36;
port[$40] := 0;
port[$40] := 0;
setintvec(8, old_handler);
asm sti end;
end;
var i : byte;
begin
for i:=1 to num_handlers do timer_ints[i].active := false;
tick_rate := 65536;
dos_tick := 0;
getintvec(8,old_handler);
setintvec(8,@interrupt_handler);
old_exit := ExitProc;
ExitProc := @timer_exit;
end.
{---------------------------------- CUT HERE -------------------------------}
program timertst;
uses timerint, crt;
procedure toggle_light(x, y: byte);
const screen = $B800;
lchr = $0CFE;
dchr = $04FE;
var adr : word;
begin
adr := (y*80+x)*2;
if(memW[screen:adr] <> lchr) then memW[screen:adr] := lchr
else memW[screen:adr] := dchr;
end;
procedure test1; far;
begin
toggle_light(5,5);
end;
procedure test2; far;
begin
toggle_light(13,5);
end;
procedure test3; far;
begin
toggle_light(21,5);
end;
procedure test4; far;
begin
toggle_light(29,5);
end;
procedure test5; far;
begin
toggle_light(37,5);
end;
begin
clrscr;
writeln('+------------------------------------------+');
writeln('| TIMERINT UNIT EXAMPLE |');
writeln('+------------------------------------------+');
writeln('| 1 Hz 2 Hz 5 Hz 10 Hz 100 Hz |');
writeln('| +-+ +-+ +-+ +-+ +-+ |');
writeln('| | | | | | | | | | | |');
writeln('| +-+ +-+ +-+ +-+ +-+ |');
writeln('+------------------------------------------+');
install_handler(@test1, 1);
install_handler(@test2, 2);
install_handler(@test3, 5);
install_handler(@test4, 10);
install_handler(@test5, 100);
repeat
until keypressed;
end.