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