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