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