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
14 ;***** T I M E R . A S M *****
\r
17 ;***** PROCEDURES *****
\r
21 ;***** VARIABLES *****
\r
24 ;***** CONSTANTS *****
\r
27 ;***************************************************************************
\r
28 ;***************************************************************************
\r
32 ;************************************************************************
\r
33 ;**************** FLAT MODEL DATA SEGMENT STUFF *************************
\r
34 ;************************************************************************
\r
36 _DATA SEGMENT BYTE PUBLIC USE32 'DATA'
\r
38 rcsid db "$Id: timer.asm,v 1.1.1.1 2001-01-19 03:30:14 bradleyb Exp $"
\r
43 STACK_SIZE EQU 4096 ; A 4K stack
\r
53 joystick_poller dd 0
\r
67 TimerStack db STACK_SIZE dup (?)
\r
70 TimerData TIMER_DATA <>
\r
77 ;************************************************************************
\r
78 ;**************** FLAT MODEL CODE SEGMENT STUFF *************************
\r
79 ;************************************************************************
\r
81 _TEXT SEGMENT BYTE PUBLIC USE32 'CODE'
\r
86 INCLUDE PSMACROS.INC
\r
89 TIMER_LOCKED_CODE_START:
\r
93 PUBLIC 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
100 xor eax, eax ; Clear all of EAX
\r
101 out TCOMMAND, al ; Tell timer to latch
\r
103 mov al, 0ah ; Find if interrupt pending
\r
110 in al, TDATA ; Read in lo byte
\r
112 in al, TDATA ; Read in hi byte
\r
115 mov eax, TimerData._timer_cnt
\r
121 mov eax, TimerData.tick_count
\r
122 imul TimerData._timer_cnt ; edx:eax = Number of 1.19 MHz ticks elapsed...
\r
130 in al, TDATA ; Read in lo byte
\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
140 mov eax, TimerData.tick_count
\r
141 imul TimerData._timer_cnt ; edx:eax = Number of 1.19 MHz ticks elapsed...
\r
149 PUBLIC timer_get_fixed_seconds_
\r
151 timer_get_fixed_seconds_:
\r
156 call timer_get_stamp64
\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
163 ; edx:eax = number of 1.19Mhz pulses elapsed.
\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
171 ; eax = fixed point seconds elapsed...
\r
178 PUBLIC timer_get_fixed_secondsX_
\r
180 timer_get_fixed_secondsX_:
\r
184 call timer_get_stamp64
\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
190 ; edx:eax = number of 1.19Mhz pulses elapsed.
\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
198 ; eax = fixed point seconds elapsed...
\r
205 PUBLIC timer_get_approx_seconds_
\r
206 timer_get_approx_seconds_:
\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
214 ; edx:eax = number of 1.19Mhz pulses elapsed.
\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
222 ; eax = fixed point seconds elapsed...
\r
230 ;extern void timer_set_rate(int count_val);
\r
231 ;extern void timer_set_function( void _far * function );
\r
233 PUBLIC timer_set_rate_
\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
247 @@: ; Set the timer rate to eax
\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
261 PUBLIC timer_set_function_
\r
262 timer_set_function_:
\r
263 ; dx:eax = far pointer to user function
\r
266 mov dword ptr TimerData.user_function[0], eax ; offset
\r
267 mov word ptr TimerData.user_function[4], dx ; selector
\r
272 PUBLIC timer_set_joyhandler_
\r
273 timer_set_joyhandler_:
\r
275 mov TimerData.joystick_poller, eax
\r
279 PUBLIC timer_delay_
\r
283 mov ecx, [ds:046Ch] ; Get Current DOS Ticks
\r
287 cmp ecx, [ds:046ch]
\r
294 ;************************************************************************
\r
295 ;************************************************************************
\r
297 ;***** T I M E R _ H A N D L E R *****
\r
299 ;************************************************************************
\r
300 ;************************************************************************
\r
307 mov ax, DGROUP ; Interrupt, so point to our data segment
\r
311 ; Increment time counter...
\r
312 inc TimerData.tick_count
\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
321 ; Call the original DOS handler....
\r
324 call fword ptr [TimerData.org_interrupt]
\r
326 jmp NoReset ;old handler has reset, so we don't
\r
330 mov al, 20h ; Reset interrupt controller
\r
334 cmp TimerData.in_timer, 0
\r
337 mov TimerData.in_timer, 1 ; Mark that we're in a timer interrupt...
\r
339 ; Reenable interrupts
\r
340 sti ; Reenable interrupts
\r
342 cmp word ptr TimerData.user_function[4], 0 ; Check the selector...
\r
345 ; Switch stacks while calling the user-definable function...
\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
359 cmp dword ptr TimerData.joystick_poller, 0
\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
370 mov TimerData.in_timer, 0
\r
373 ;;mov al, 20h ; Reset interrupt controller
\r
378 iretd ; Return from timer interrupt
\r
381 TIMER_LOCKED_CODE_STOP:
\r
383 ;************************************************************************
\r
384 ;************************************************************************
\r
386 ;***** T I M E R _ I N I T *****
\r
388 ;************************************************************************
\r
389 ;************************************************************************
\r
399 cmp TimerData.Installed, 1
\r
400 je AlreadyInstalled
\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
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
412 ;--------------- lock data used in interrupt
\r
413 mov eax, SIZEOF TIMER_DATA
\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
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
425 int 3 ; LOCK FAILED!!
\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
431 inc eax ; EAX = size of timer interrupt handler
\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
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
443 int 3 ; LOCK FAILED!!
\r
446 ;**************************************************************
\r
447 ;******************* SAVE OLD INT8 HANDLER ********************
\r
448 ;**************************************************************
\r
449 mov eax, 03508h ; DOS Get Vector 08h
\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
455 ;**************************************************************
\r
456 ;***************** INSTALL NEW INT8 HANDLER *******************
\r
457 ;**************************************************************
\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
468 mov TimerData.tick_count, 0
\r
469 mov TimerData.Installed,1
\r
471 mov eax, 02508h ; DOS Set Vector 08h
\r
472 mov edx, offset timer_handler ; Point DS:EDX to new handler
\r
479 lea eax, cs:timer_close_
\r
491 ;************************************************************************
\r
492 ;************************************************************************
\r
494 ;***** T I M E R _ C L O S E _ *****
\r
496 ;************************************************************************
\r
497 ;************************************************************************
\r
499 PUBLIC timer_close_
\r
507 cmp TimerData.Installed, 0
\r
509 mov TimerData.Installed, 0
\r
511 ;**************************************************************
\r
512 ;***************** RESTORE OLD INT9 HANDLER *******************
\r
513 ;**************************************************************
\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
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
532 cmp TimerData.nested_counter, 0
\r
533 je NoNestedInterrupts
\r
534 mov eax, TimerData.nested_counter
\r
535 ;int 3 ; Get John!!
\r
537 NoNestedInterrupts:
\r