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