replaced by maths.h
[btb/d2x.git] / unused / bios / timer.asm
1 ; THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
2 ; SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
3 ; END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
4 ; ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
5 ; IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
6 ; SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
7 ; FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
8 ; CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
9 ; AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
10 ; COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
11 ;***************************************************************************
12 ;***************************************************************************
13 ;*****                                                                 *****
14 ;*****                     T I M E R . A S M                           *****
15 ;*****                                                                 *****
16 ;*****                                                                 *****
17 ;***** PROCEDURES                                                      *****
18 ;*****                                                                 *****
19 ;*****                                                                 *****
20 ;*****                                                                 *****
21 ;***** VARIABLES                                                       *****
22 ;*****                                                                 *****
23 ;*****                                                                 *****
24 ;***** CONSTANTS                                                       *****
25 ;*****                                                                 *****
26 ;*****                                                                 *****
27 ;***************************************************************************
28 ;***************************************************************************
29
30 .386
31
32 ;************************************************************************
33 ;**************** FLAT MODEL DATA SEGMENT STUFF *************************
34 ;************************************************************************
35
36 _DATA   SEGMENT BYTE PUBLIC USE32 'DATA'
37
38 rcsid   db      "$Id: timer.asm,v 1.1.1.2 2001-01-19 03:33:50 bradleyb Exp $"
39
40 TDATA           EQU     40h
41 TCOMMAND        EQU     43h
42 PIC             EQU     020h
43 STACK_SIZE      EQU     4096    ; A 4K stack
44
45 TIMER_DATA STRUCT   
46         in_timer        db  0
47
48         nested_counter  dd      0
49
50         _timer_cnt      dd  65536
51         dos_timer       dd  ?
52
53         joystick_poller dd  0
54
55         user_function   df  0
56         
57         org_interrupt   df  0   
58
59         saved_stack     df  0
60
61         new_stack       df  0
62
63         tick_count      dd  0
64
65         Installed db    0
66
67         TimerStack      db  STACK_SIZE dup (?)          
68 TIMER_DATA ENDS
69
70         TimerData       TIMER_DATA <>   
71
72 _DATA   ENDS
73
74 DGROUP  GROUP _DATA
75
76
77 ;************************************************************************
78 ;**************** FLAT MODEL CODE SEGMENT STUFF *************************
79 ;************************************************************************
80
81 _TEXT   SEGMENT BYTE PUBLIC USE32 'CODE'
82
83                 ASSUME  ds:_DATA
84                 ASSUME  cs:_TEXT
85
86 INCLUDE PSMACROS.INC
87 INCLUDE FIX.INC
88
89 TIMER_LOCKED_CODE_START:
90
91                 extn atexit_
92
93 PUBLIC timer_get_stamp64
94
95 timer_get_stamp64:
96                 ; Return a 64-bit stamp that is the number of 1.19Mhz pulses
97                 ; since the time was initialized.  Returns in EDX:EAX.
98                 ; Also, interrupts must be disabled.
99
100                 xor     eax, eax            ; Clear all of EAX
101                 out     TCOMMAND, al        ; Tell timer to latch
102
103                 mov     al, 0ah             ; Find if interrupt pending
104                 out     PIC, al
105                 jmp     @f
106 @@:             in      al, PIC
107                 and     eax, 01b
108                 jz      NoInterrupt
109
110                 in      al, TDATA           ; Read in lo byte
111                 mov     dl, al
112                 in      al, TDATA           ; Read in hi byte
113                 mov     dh, al
114                 and     edx, 0ffffh
115                 mov     eax, TimerData._timer_cnt
116                 shl     eax, 1          
117                 sub     eax, edx
118                         
119                 push    ebx
120                 mov     ebx, eax
121                 mov     eax, TimerData.tick_count
122                 imul    TimerData._timer_cnt    ; edx:eax = Number of 1.19 MHz ticks elapsed...
123                 add     eax, ebx
124                 adc     edx, 0                          
125                 pop     ebx
126
127                 ret
128
129 NoInterrupt:
130                 in      al, TDATA           ; Read in lo byte
131                 mov     ah, al
132                 in      al, TDATA           ; Read in hi byte
133                 xchg    ah, al              ; arrange em correctly
134                 mov     edx, TimerData._timer_cnt
135                 sub     dx, ax              ; BX = timer ticks
136                 mov     ax, dx
137
138                 push    ebx
139                 mov     ebx, eax
140                 mov     eax, TimerData.tick_count
141                 imul    TimerData._timer_cnt    ; edx:eax = Number of 1.19 MHz ticks elapsed...
142                 add     eax, ebx
143                 adc     edx, 0                          
144                 pop     ebx
145
146                 ret
147
148
149 PUBLIC  timer_get_fixed_seconds_
150
151 timer_get_fixed_seconds_:
152                 push    ebx
153                 push    edx
154
155                 cli
156                 call    timer_get_stamp64
157                 sti
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 ; Make sure we won't divide overflow.  Make time wrap at about 9 hours
167 sub_again:              sub     edx, ebx        ; subtract until negative...
168                 jns     sub_again       ; ...to prevent divide overflow...
169                 add     edx, ebx        ; ...then add in to get correct value.
170                 div     ebx
171                 ; eax = fixed point seconds elapsed...
172
173                 pop     edx
174                 pop     ebx
175
176                 ret
177
178 PUBLIC  timer_get_fixed_secondsX_
179
180 timer_get_fixed_secondsX_:
181                 push    ebx
182                 push    edx
183
184                 call    timer_get_stamp64
185
186                 ; Timing in fixed point (16.16) seconds.
187                 ; Can be used for up to 1000 hours
188                 shld    edx, eax, 16            ; Keep 32+11 bits
189                 shl     eax, 16                 
190                 ; edx:eax = number of 1.19Mhz pulses elapsed.
191                 mov     ebx, 1193180
192
193 xsub_again:             sub     edx, ebx        ; subtract until negative...
194                 jns     xsub_again      ; ...to prevent divide overflow...
195                 add     edx, ebx        ; ...then add in to get correct value.
196
197                 div     ebx
198                 ; eax = fixed point seconds elapsed...
199
200                 pop     edx
201                 pop     ebx
202
203                 ret
204
205 PUBLIC timer_get_approx_seconds_
206 timer_get_approx_seconds_:
207         push    ebx
208         push    edx
209
210         mov     eax, TimerData.tick_count
211         imul    TimerData._timer_cnt    ; edx:eax = Number of 1.19 MHz ticks elapsed...
212         shld    edx, eax, 16            ; Keep 32+16 bits, for conversion to fixed point
213         shl     eax, 16                 
214         ; edx:eax = number of 1.19Mhz pulses elapsed.
215         mov     ebx, 1193180
216
217 approx_sub_again:       sub     edx, ebx        ; subtract until negative...
218         jns     approx_sub_again        ; ...to prevent divide overflow...
219         add     edx, ebx        ; ...then add in to get correct value.
220
221         div     ebx
222         ; eax = fixed point seconds elapsed...
223
224         pop     edx
225         pop     ebx
226         ret
227
228
229
230 ;extern void timer_set_rate(int count_val);
231 ;extern void timer_set_function( void _far * function );
232
233 PUBLIC  timer_set_rate_
234 timer_set_rate_:
235         ; eax = rate
236         pushad
237
238         ; Make sure eax below or equal to 65535 and above 0
239         ; if its not, make it be 65536 which is normal dos
240         ; timing.
241         cmp     eax, 65535
242         jbe     @f
243         mov     eax, 65536
244 @@:     cmp     eax, 0
245         jne     @f
246         mov     eax, 65536
247 @@:     ; Set the timer rate to eax
248         cli
249         mov     TimerData.tick_count, 0
250         mov     TimerData._timer_cnt, eax               
251         mov     al, 34h         ; count in binary, mode 2, load low byte then hi byte, counter 0:  00 11 010 0
252         out     TCOMMAND, al    ; Reset PIT channel 0
253         mov     eax, TimerData._timer_cnt
254         out     TDATA, al
255         mov     al, ah
256         out     TDATA, al
257         sti
258         popad
259         ret
260
261 PUBLIC  timer_set_function_
262 timer_set_function_:
263         ; dx:eax = far pointer to user function
264         pushad
265         cli
266         mov     dword ptr TimerData.user_function[0], eax       ; offset
267         mov     word ptr TimerData.user_function[4], dx         ; selector
268         sti
269         popad
270         ret
271
272 PUBLIC timer_set_joyhandler_
273 timer_set_joyhandler_:
274         cli
275         mov     TimerData.joystick_poller, eax
276         sti
277         ret
278
279 PUBLIC timer_delay_
280 timer_delay_:
281         pushad
282         mov     edx, 18
283         mov     ecx, [ds:046Ch]         ; Get Current DOS Ticks
284         fixmul  edx     
285         add     ecx, eax
286 timeloop:
287         cmp     ecx, [ds:046ch]
288         jg      timeloop
289         popad
290         ret
291
292
293
294 ;************************************************************************
295 ;************************************************************************
296 ;*****                                                              *****
297 ;*****                T I M E R _ H A N D L E R                     *****
298 ;*****                                                              *****
299 ;************************************************************************
300 ;************************************************************************
301
302 timer_handler:
303                 push    ds
304                 push    es
305                 push    eax
306
307                 mov     ax, DGROUP       ; Interrupt, so point to our data segment
308                 mov     ds, ax
309                 mov     es, ax
310
311                 ; Increment time counter...
312                 inc     TimerData.tick_count
313
314                 mov     eax, TimerData._timer_cnt
315                 add     TimerData.dos_timer, eax                ; Increment DOS timer
316                 cmp     TimerData.dos_timer, 65536
317                 jb      NoChainToOld            ; See if we need to chain to DOS 18.2
318                 and     TimerData.dos_timer, 0ffffh
319
320                 ;
321                 ; Call the original DOS handler....
322                 ;
323                 pushfd
324                 call    fword ptr [TimerData.org_interrupt]
325
326                 jmp     NoReset         ;old handler has reset, so we don't
327
328 NoChainToOld:
329                 ; Reset controller
330                 mov     al, 20h         ; Reset interrupt controller
331                 out     20h, al
332
333 NoReset:
334                 cmp     TimerData.in_timer, 0
335                 jne     ExitInterrupt
336
337                 mov     TimerData.in_timer, 1           ; Mark that we're in a timer interrupt...
338
339                 ; Reenable interrupts
340                 sti                     ; Reenable interrupts
341
342                 cmp     word ptr TimerData.user_function[4], 0          ; Check the selector...
343                 je      NoUserFunction
344
345                 ; Switch stacks while calling the user-definable function...
346                 pushad
347                 push    fs
348                 push    gs
349                 mov     dword ptr TimerData.saved_stack[0], esp
350                 mov     word ptr TimerData.saved_stack[4], ss
351                 lss     esp, TimerData.new_stack        ; Switch to new stack
352                 call    fword ptr [TimerData.user_function]     ; Call new function
353                 lss     esp,  TimerData.saved_stack     ; Switch back to original stack
354                 pop     gs
355                 pop     fs
356                 popad
357
358 NoUserFunction: 
359                 cmp     dword ptr TimerData.joystick_poller, 0
360                 je      NoJoyPolling
361                 mov     eax, TimerData._timer_cnt
362                 mov     dword ptr TimerData.saved_stack[0], esp
363                 mov     word ptr TimerData.saved_stack[4], ss
364                 lss     esp, TimerData.new_stack        ; Switch to new stack
365                 call    dword ptr TimerData.joystick_poller
366                 lss     esp,  TimerData.saved_stack     ; Switch back to original stack
367
368 NoJoyPolling:
369                 cli
370                 mov     TimerData.in_timer, 0
371
372 ExitInterrupt:
373                 ;;mov     al, 20h         ; Reset interrupt controller
374                 ;;out     20h, al
375                 pop     eax
376                 pop     es
377                 pop     ds
378                 iretd                           ; Return from timer interrupt
379
380
381 TIMER_LOCKED_CODE_STOP:
382
383 ;************************************************************************
384 ;************************************************************************
385 ;*****                                                              *****
386 ;*****                   T I M E R _ I N I T                        *****
387 ;*****                                                              *****
388 ;************************************************************************
389 ;************************************************************************
390
391
392 PUBLIC  timer_init_
393
394 timer_init_:
395                 pushad
396                 push    ds
397                 push    es
398
399                 cmp     TimerData.Installed, 1
400                 je      AlreadyInstalled
401
402                 mov     TimerData._timer_cnt, 65536     ; Set to BIOS's normal 18.2 Hz
403                 mov     TimerData.dos_timer, 0          ; clear DOS Interrupt counter
404                 mov     dword ptr TimerData.user_function[0], 0 ; offset of user function
405                 mov     word ptr TimerData.user_function[4], 0  ; selector of user function
406
407                 lea     eax, ds:[TimerData.TimerStack]  ; Use EAX as temp stack pointer
408                 add     eax, STACK_SIZE                 ; Top of stack minus space for saving old ss:esp
409                 mov     dword ptr TimerData.new_stack[0], eax
410                 mov     word ptr TimerData.new_stack[4], ds
411
412                 ;--------------- lock data used in interrupt
413                 mov     eax, SIZEOF TIMER_DATA
414                 mov     esi, eax
415                 shr     esi, 16                 
416                 mov     edi, eax
417                 and     edi, 0ffffh     ; si:di = length of region to lock in bytes
418                 lea     ebx, ds:TimerData
419                 lea     ecx, ds:TimerData
420                 shr     ebx, 16
421                 and     ecx, 0ffffh     ; bx:cx = start of linear address to lock
422                 mov     eax, 0600h      ; DPMI lock address function
423                 int     31h             ; call dpmi
424                 jnc     @f
425                 int     3               ; LOCK FAILED!!
426 @@:
427                 ;--------------- lock code used in interrupt
428                 lea     eax, cs:TIMER_LOCKED_CODE_STOP
429                 lea     ecx, cs:TIMER_LOCKED_CODE_START
430                 sub     eax, ecx
431                 inc     eax             ; EAX = size of timer interrupt handler
432                 mov     esi, eax
433                 shr     esi, 16                 
434                 mov     edi, eax
435                 and     edi, 0ffffh     ; si:di = length of region to lock in bytes
436                 lea     ebx, cs:TIMER_LOCKED_CODE_START
437                 lea     ecx, cs:TIMER_LOCKED_CODE_START
438                 shr     ebx, 16
439                 and     ecx, 0ffffh     ; bx:cx = start of linear address to lock
440                 mov     eax, 0600h      ; DPMI lock address function
441                 int     31h             ; call dpmi
442                 jnc     @f
443                 int     3               ; LOCK FAILED!!
444 @@:
445
446                 ;**************************************************************
447                 ;******************* SAVE OLD INT8 HANDLER ********************
448                 ;**************************************************************
449                 mov     eax, 03508h             ; DOS Get Vector 08h
450                 int     21h                     ; Call DOS
451
452                 mov     dword ptr TimerData.org_interrupt[0], ebx       ; offset of user function
453                 mov     word ptr TimerData.org_interrupt[4], es         ; selector of user function
454
455                 ;**************************************************************
456                 ;***************** INSTALL NEW INT8 HANDLER *******************
457                 ;**************************************************************
458
459                 cli
460
461                 mov     al, 34h         ; count in binary, mode 2, load low byte then hi byte, counter 0:  00 11 010 0
462                 out     TCOMMAND, al    ; Reset PIT channel 0
463                 mov     eax, TimerData._timer_cnt
464                 out     TDATA, al
465                 mov     al, ah
466                 out     TDATA, al
467
468                 mov     TimerData.tick_count, 0
469                 mov     TimerData.Installed,1
470
471                 mov     eax, 02508h             ; DOS Set Vector 08h
472                 mov     edx, offset timer_handler  ; Point DS:EDX to new handler
473                 mov     bx, cs
474                 push    ds
475                 mov     ds, bx
476                 int     21h
477                 pop     ds
478                 sti
479                 lea     eax, cs:timer_close_
480                 call    atexit_
481
482 AlreadyInstalled:
483
484                 pop     es
485                 pop     ds
486                 popad
487
488                 ret
489
490
491 ;************************************************************************
492 ;************************************************************************
493 ;*****                                                              *****
494 ;*****                  T I M E R _ C L O S E _                     *****
495 ;*****                                                              *****
496 ;************************************************************************
497 ;************************************************************************
498
499 PUBLIC  timer_close_
500
501 timer_close_:
502                 push    eax
503                 push    edx
504                 push    ds
505
506
507                 cmp     TimerData.Installed, 0
508                 je      NotInstalled
509                 mov     TimerData.Installed, 0
510
511                 ;**************************************************************
512                 ;***************** RESTORE OLD INT9 HANDLER *******************
513                 ;**************************************************************
514
515                 cli
516                 mov     al, 36h         ; count in binary, mode 3, load low byte then hi byte, counter 0:  00 11 011 0
517                 out     TCOMMAND, al    ; Reser PIT channel 0
518                 mov     ax, 0h
519                 out     TDATA, al
520                 mov     al, ah
521                 out     TDATA, al
522
523                 push    ds
524                 mov     eax, 02508h         ; DOS Set Vector 08h
525                 mov     edx, dword ptr TimerData.org_interrupt[0]
526                 mov     ds, word ptr TimerData.org_interrupt[4]
527                 int     21h
528                 pop     ds
529
530                 sti
531                 
532                 cmp     TimerData.nested_counter, 0
533                 je      NoNestedInterrupts
534                 mov     eax, TimerData.nested_counter
535                 ;int    3               ; Get John!!
536         
537 NoNestedInterrupts:
538                 
539
540 NotInstalled:
541                 pop     ds
542                 pop     edx
543                 pop     eax
544
545                 ret
546
547
548 _TEXT           ENDS
549
550
551                 END