]> icculus.org git repositories - btb/d2x.git/blob - arch/dos_timer.asm
Minor dist fix
[btb/d2x.git] / arch / dos_timer.asm
1 ;
2 ; $Source: /cvs/cvsroot/d2x/arch/dos_timer.asm,v $
3 ; $Revision: 1.2 $
4 ; $Author: bradleyb $
5 ; $Date: 2001-01-29 13:35:08 $
6 ;
7 ; DOS timer routines
8 ;
9 ; $Log: not supported by cvs2svn $
10 ;
11
12
13 [BITS 32]
14
15 [EXTERN _atexit]
16 [EXTERN ___djgpp_base_address]
17
18 [GLOBAL _timer_get_fixed_seconds]
19 [GLOBAL _timer_get_fixed_secondsX]
20 [GLOBAL _timer_get_approx_seconds]
21 [GLOBAL _timer_set_rate]
22 [GLOBAL _timer_set_function]
23 [GLOBAL _timer_set_joyhandler]
24 [GLOBAL _timer_init]
25 [GLOBAL _timer_close]
26
27
28 [SECTION .data]
29 TDATA      EQU 40h
30 TCOMMAND   EQU 43h
31 PIC        EQU 020h
32 STACK_SIZE EQU 4096
33
34 %define df times 3 dw
35
36 TimerData  db 0
37            dd 0
38            dd 65536
39            dd 0
40            dd 0
41            df 0
42            df 0
43            df 0
44            df 0
45            dd 0
46            db 0
47 times STACK_SIZE db 0
48
49 td_in_timer        equ TimerData
50 td_nested_counter  equ td_in_timer+1
51 td__timer_cnt      equ td_nested_counter+4
52 td_dos_timer       equ td__timer_cnt+4
53 td_joystick_poller equ td_dos_timer+4
54 td_user_function   equ td_joystick_poller+4
55 td_org_interrupt   equ td_user_function+6
56 td_saved_stack     equ td_org_interrupt+6
57 td_new_stack       equ td_saved_stack+6
58 td_tick_count      equ td_new_stack+6
59 td_Installed       equ td_tick_count+4
60 td_TimerStack      equ td_Installed+1
61
62
63 [SECTION .text]
64
65 TIMER_LOCKED_CODE_START:
66
67 __my_ds dw 0
68
69 timer_get_stamp64:
70 ; Return a 64-bit stamp that is the number of 1.19Mhz pulses
71 ; since the time was initialized.  Returns in EDX:EAX.
72 ; Also, interrupts must be disabled.
73
74  xor     eax, eax            ; Clear all of EAX
75  out     TCOMMAND, al        ; Tell timer to latch
76
77  mov     al, 0ah             ; Find if interrupt pending
78  out     PIC, al
79  jmp     @
80  @:
81  in      al, PIC
82  and     eax, 01b
83  jz      NoInterrupt
84
85  in      al, TDATA           ; Read in lo byte
86  mov     dl, al
87  in      al, TDATA           ; Read in hi byte
88  mov     dh, al
89  and     edx, 0ffffh
90  mov     eax, [td__timer_cnt]
91  shl     eax, 1   
92  sub     eax, edx
93                         
94  push    ebx
95  mov     ebx, eax
96  mov     eax, [td_tick_count]
97  imul    dword [td__timer_cnt]    ; edx:eax = Number of 1.19 MHz ticks elapsed...
98  add     eax, ebx
99  adc     edx, 0                   
100  pop     ebx
101
102  ret
103
104 NoInterrupt:
105  in      al, TDATA           ; Read in lo byte
106  mov     ah, al
107  in      al, TDATA           ; Read in hi byte
108  xchg    ah, al              ; arrange em correctly
109  mov     edx, [td__timer_cnt]
110  sub     dx, ax              ; BX = timer ticks
111  mov     ax, dx
112
113  push    ebx
114  mov     ebx, eax
115  mov     eax, [td_tick_count]
116  imul    dword [td__timer_cnt]    ; edx:eax = Number of 1.19 MHz ticks elapsed...
117  add     eax, ebx
118  adc     edx, 0                   
119  pop     ebx
120
121  ret
122
123
124 _timer_get_fixed_seconds:
125  push    ebx
126  push    edx
127
128  cli
129  call    timer_get_stamp64
130  sti
131
132 ; Timing in fixed point (16.16) seconds.
133 ; Can be used for up to 1000 hours
134  shld    edx, eax, 16            ; Keep 32+11 bits
135  shl     eax, 16          
136 ; edx:eax = number of 1.19Mhz pulses elapsed.
137  mov     ebx, 1193180
138
139 ; Make sure we won't divide overflow.  Make time wrap at about 9 hours
140 sub_again:
141  sub     edx, ebx        ; subtract until negative...
142  jns     sub_again       ; ...to prevent divide overflow...
143  add     edx, ebx        ; ...then add in to get correct value.
144  div     ebx
145 ; eax = fixed point seconds elapsed...
146
147  pop     edx
148  pop     ebx
149
150  ret
151
152
153 _timer_get_fixed_secondsX:
154  push    ebx
155  push    edx
156
157  call    timer_get_stamp64
158
159 ; Timing in fixed point (16.16) seconds.
160 ; Can be used for up to 1000 hours
161  shld    edx, eax, 16            ; Keep 32+11 bits
162  shl     eax, 16          
163 ; edx:eax = number of 1.19Mhz pulses elapsed.
164  mov     ebx, 1193180
165
166 xsub_again:
167  sub     edx, ebx        ; subtract until negative...
168  jns     xsub_again      ; ...to prevent divide overflow...
169  add     edx, ebx        ; ...then add in to get correct value.
170
171  div     ebx
172 ; eax = fixed point seconds elapsed...
173
174  pop     edx
175  pop     ebx
176
177  ret
178
179 _timer_get_approx_seconds:
180  push    ebx
181  push    edx
182
183  mov     eax, [td_tick_count]
184  imul    dword [td__timer_cnt]   ; edx:eax = Number of 1.19 MHz ticks elapsed...
185  shld    edx, eax, 16            ; Keep 32+16 bits, for conversion to fixed point
186  shl     eax, 16          
187 ; edx:eax = number of 1.19Mhz pulses elapsed.
188  mov     ebx, 1193180
189
190 approx_sub_again:
191  sub     edx, ebx        ; subtract until negative...
192  jns     approx_sub_again        ; ...to prevent divide overflow...
193  add     edx, ebx        ; ...then add in to get correct value.
194
195  div     ebx
196 ; eax = fixed point seconds elapsed...
197
198  pop     edx
199  pop     ebx
200  ret
201
202 ;----------------------------------------------------------------------------
203 ;Prototype: extern void timer_set_rate(int count_val);
204 ;----------------------------------------------------------------------------
205 _timer_set_rate:
206  mov eax,[esp+4]
207 ; eax = rate
208  pushad
209
210 ; Make sure eax below or equal to 65535 and above 0
211 ; if its not, make it be 65536 which is normal dos
212 ; timing.
213  cmp     eax, 65535
214  jbe     @@
215  mov     eax, 65536
216 @@:
217  cmp     eax, 0
218  jne     @@@
219  mov     eax, 65536
220 @@@:    ; Set the timer rate to eax
221  cli
222  mov     dword [td_tick_count], 0
223  mov     [td__timer_cnt], eax        
224  mov     al, 34h         ; count in binary, mode 2, load low byte then hi byte, counter 0:  00 11 010 0
225  out     TCOMMAND, al    ; Reset PIT channel 0
226  mov     eax, [td__timer_cnt]
227  out     TDATA, al
228  mov     al, ah
229  out     TDATA, al
230  sti
231  popad
232  ret
233
234 ;----------------------------------------------------------------------------
235 ;Prototype: extern void timer_set_function( void * function );
236 ;----------------------------------------------------------------------------
237 _timer_set_function:
238 ; arg1 = near pointer to user function
239  push eax
240  mov     eax, [esp+8] ; arg 1
241  cli
242  mov     dword [td_user_function+0], eax       ; offset
243  mov     word  [td_user_function+4], cs         ; selector
244  sti
245  pop eax
246  ret
247
248 _timer_set_joyhandler:
249  mov    eax, [esp+4]
250  cli
251  mov    dword [td_joystick_poller], eax
252  sti
253  ret
254
255 ;************************************************************************
256 ;************************************************************************
257 ;*****                                                              *****
258 ;*****                T I M E R _ H A N D L E R                     *****
259 ;*****                                                              *****
260 ;************************************************************************
261 ;************************************************************************
262
263 timer_handler:
264  push    ds
265  push    es
266  push    eax
267
268  mov     ax, [cs:__my_ds]   ; Interrupt, so point to our data segment
269  mov     ds, ax
270  mov     es, ax
271
272 ; Increment time counter...
273  inc     dword [td_tick_count]
274
275  mov     eax, [td__timer_cnt]
276  add     dword [td_dos_timer], eax                ; Increment DOS timer
277  cmp     dword [td_dos_timer], 65536
278  jb      NoChainToOld            ; See if we need to chain to DOS 18.2
279  and     dword [td_dos_timer], 0ffffh
280
281 ;
282 ; Call the original DOS handler....
283 ;
284  pushfd
285  call far dword [td_org_interrupt]
286
287  jmp     NoReset         ;old handler has reset, so we don't
288
289 NoChainToOld:
290 ; Reset controller
291  mov     al, 20h         ; Reset interrupt controller
292  out     20h, al
293
294 NoReset:
295  cmp     byte [td_in_timer], 0
296  jne     ExitInterrupt
297
298  mov     byte [td_in_timer], 1           ; Mark that we're in a timer interrupt...
299
300  ; Reenable interrupts
301  sti                     ; Reenable interrupts
302
303  cmp     word [td_user_function+4], 0          ; Check the selector...
304  je      NoUserFunction
305
306 ; Switch stacks while calling the user-definable function...
307  pushad
308  push    fs
309  push    gs
310  mov     dword [td_saved_stack+0], esp
311  mov     word [td_saved_stack+4], ss
312  lss     esp, [td_new_stack]        ; Switch to new stack
313  call    far dword [td_user_function]     ; Call new function
314  lss     esp, [td_saved_stack]     ; Switch back to original stack
315  pop     gs
316  pop     fs
317  popad
318
319 NoUserFunction: 
320  cmp     dword [td_joystick_poller], 0
321  je      NoJoyPolling
322  mov     eax, [td__timer_cnt]
323  mov     dword [td_saved_stack+0], esp
324  mov     word [td_saved_stack+4], ss
325  lss     esp, [td_new_stack]        ; Switch to new stack
326  pusha
327  push eax
328  call    dword [td_joystick_poller]
329  pop eax
330  popa
331  lss     esp,  [td_saved_stack]    ; Switch back to original stack
332
333 NoJoyPolling:
334  cli
335  mov     byte [td_in_timer], 0
336
337 ExitInterrupt:
338 ;;mov     al, 20h         ; Reset interrupt controller
339 ;;out     20h, al
340  pop     eax
341  pop     es
342  pop     ds
343  iretd                           ; Return from timer interrupt
344
345 TIMER_LOCKED_CODE_STOP:
346
347 ;************************************************************************
348 ;************************************************************************
349 ;*****                                                              *****
350 ;*****                   T I M E R _ I N I T                        *****
351 ;*****                                                              *****
352 ;************************************************************************
353 ;************************************************************************
354
355 _timer_init:
356  pushad
357  push    ds
358  push    es
359
360  cmp     byte [td_Installed], 1
361  je      NEAR AlreadyInstalled
362
363  mov     [__my_ds],ds
364
365  mov     dword [td__timer_cnt], 65536     ; Set to BIOS's normal 18.2 Hz
366  mov     dword [td_dos_timer], 0          ; clear DOS Interrupt counter
367  mov     dword [td_user_function+0], 0 ; offset of user function
368  mov     word [td_user_function+4], 0  ; selector of user function
369
370  lea     eax, [ds:td_TimerStack]  ; Use EAX as temp stack pointer
371  add     eax, STACK_SIZE                 ; Top of stack minus space for saving old ss:esp
372  mov     dword [td_new_stack+0], eax
373  mov     word [td_new_stack+4], ds
374
375                 ;--------------- lock data used in interrupt
376  mov     eax, (td_TimerStack-TimerData)+STACK_SIZE ;sizeof timer_data
377  mov     esi, eax
378  shr     esi, 16          
379  mov     edi, eax
380  and     edi, 0ffffh     ; si:di = length of region to lock in bytes
381  mov     ebx, TimerData
382  add     ebx, [___djgpp_base_address]
383  mov     ecx, ebx
384  shr     ebx, 16
385  and     ecx, 0ffffh     ; bx:cx = start of linear address to lock
386  mov     eax, 0600h      ; DPMI lock address function
387  int     31h             ; call dpmi
388  jnc     @@@@
389  int     3               ; LOCK FAILED!!
390 @@@@:
391  ;--------------- lock code used in interrupt
392  mov     eax, TIMER_LOCKED_CODE_STOP
393  sub     eax, TIMER_LOCKED_CODE_START
394  inc     eax             ; EAX = size of timer interrupt handler
395  mov     esi, eax
396  shr     esi, 16          
397  mov     edi, eax
398  and     edi, 0ffffh     ; si:di = length of region to lock in bytes
399  mov     ebx, TIMER_LOCKED_CODE_START
400  add     ebx, [___djgpp_base_address]
401  mov     ecx, ebx
402  shr     ebx, 16
403  and     ecx, 0ffffh     ; bx:cx = start of linear address to lock
404  mov     eax, 0600h      ; DPMI lock address function
405  int     31h             ; call dpmi
406  jnc     @@@@@ ;This is getting fun
407  int     3               ; LOCK FAILED!!
408 @@@@@:
409
410 ;**************************************************************
411 ;******************* SAVE OLD INT8 HANDLER ********************
412 ;**************************************************************
413  mov ax,0204h
414  mov bl,8
415  int 31h
416  mov dword [td_org_interrupt+0],edx
417  mov word [td_org_interrupt+4],cx
418
419 ;**************************************************************
420 ;***************** INSTALL NEW INT8 HANDLER *******************
421 ;**************************************************************
422
423  cli
424
425  mov     al, 34h         ; count in binary, mode 2, load low byte then hi byte, counter 0:  00 11 010 0
426  out     TCOMMAND, al    ; Reset PIT channel 0
427  mov     eax, [td__timer_cnt]
428  out     TDATA, al
429  mov     al, ah
430  out     TDATA, al
431
432  mov     dword [td_tick_count], 0
433  mov     byte [td_Installed],1
434
435  mov ax,0205h
436  mov bl,8
437  mov edx,timer_handler
438  mov cx,cs
439  int 31h
440
441
442  sti
443  push    dword _timer_close
444  call    _atexit
445  add esp,4
446
447 AlreadyInstalled:
448
449  pop     es
450  pop     ds
451  popad
452
453  ret
454
455 ;************************************************************************
456 ;************************************************************************
457 ;*****                                                              *****
458 ;*****                  T I M E R _ C L O S E _                     *****
459 ;*****                                                              *****
460 ;************************************************************************
461 ;************************************************************************
462
463 _timer_close:
464  push    eax
465  push    ebx
466  push    ecx
467  push    edx
468
469  cmp     byte [td_Installed], 0
470  je      NotInstalled
471  mov     byte [td_Installed], 0
472
473 ;**************************************************************
474 ;***************** RESTORE OLD INT8 HANDLER *******************
475 ;**************************************************************
476
477  cli
478  mov     al, 36h         ; count in binary, mode 3, load low byte then hi byte, counter 0:  00 11 011 0
479  out     TCOMMAND, al    ; Reser PIT channel 0
480  mov     ax, 0h
481  out     TDATA, al
482  mov     al, ah
483  out     TDATA, al
484
485  mov ax,0205h
486  mov bl,8
487  mov edx,dword [td_org_interrupt+0]
488  mov cx,word [td_org_interrupt+4]
489  int 31h
490
491  sti
492                 
493  cmp     dword [td_nested_counter], 0
494  je      NoNestedInterrupts
495  mov     eax, [td_nested_counter]
496  ;int    3               ; Get John!!
497         
498 NoNestedInterrupts:
499                 
500
501 NotInstalled:
502  pop     edx
503  pop     ecx
504  pop     ebx
505  pop     eax
506
507  ret
508