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