552 lines
15 KiB
NASM
552 lines
15 KiB
NASM
; THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
|
|
; SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
|
|
; END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
|
|
; ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
|
|
; IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
|
|
; SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
|
|
; FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
|
|
; CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
|
|
; AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
|
|
; COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
|
|
;***************************************************************************
|
|
;***************************************************************************
|
|
;***** *****
|
|
;***** T I M E R . A S M *****
|
|
;***** *****
|
|
;***** *****
|
|
;***** PROCEDURES *****
|
|
;***** *****
|
|
;***** *****
|
|
;***** *****
|
|
;***** VARIABLES *****
|
|
;***** *****
|
|
;***** *****
|
|
;***** CONSTANTS *****
|
|
;***** *****
|
|
;***** *****
|
|
;***************************************************************************
|
|
;***************************************************************************
|
|
|
|
.386
|
|
|
|
;************************************************************************
|
|
;**************** FLAT MODEL DATA SEGMENT STUFF *************************
|
|
;************************************************************************
|
|
|
|
_DATA SEGMENT BYTE PUBLIC USE32 'DATA'
|
|
|
|
rcsid db "$Id: timer.asm,v 1.1.1.2 2001-01-19 03:33:50 bradleyb Exp $"
|
|
|
|
TDATA EQU 40h
|
|
TCOMMAND EQU 43h
|
|
PIC EQU 020h
|
|
STACK_SIZE EQU 4096 ; A 4K stack
|
|
|
|
TIMER_DATA STRUCT
|
|
in_timer db 0
|
|
|
|
nested_counter dd 0
|
|
|
|
_timer_cnt dd 65536
|
|
dos_timer dd ?
|
|
|
|
joystick_poller dd 0
|
|
|
|
user_function df 0
|
|
|
|
org_interrupt df 0
|
|
|
|
saved_stack df 0
|
|
|
|
new_stack df 0
|
|
|
|
tick_count dd 0
|
|
|
|
Installed db 0
|
|
|
|
TimerStack db STACK_SIZE dup (?)
|
|
TIMER_DATA ENDS
|
|
|
|
TimerData TIMER_DATA <>
|
|
|
|
_DATA ENDS
|
|
|
|
DGROUP GROUP _DATA
|
|
|
|
|
|
;************************************************************************
|
|
;**************** FLAT MODEL CODE SEGMENT STUFF *************************
|
|
;************************************************************************
|
|
|
|
_TEXT SEGMENT BYTE PUBLIC USE32 'CODE'
|
|
|
|
ASSUME ds:_DATA
|
|
ASSUME cs:_TEXT
|
|
|
|
INCLUDE PSMACROS.INC
|
|
INCLUDE FIX.INC
|
|
|
|
TIMER_LOCKED_CODE_START:
|
|
|
|
extn atexit_
|
|
|
|
PUBLIC timer_get_stamp64
|
|
|
|
timer_get_stamp64:
|
|
; Return a 64-bit stamp that is the number of 1.19Mhz pulses
|
|
; since the time was initialized. Returns in EDX:EAX.
|
|
; Also, interrupts must be disabled.
|
|
|
|
xor eax, eax ; Clear all of EAX
|
|
out TCOMMAND, al ; Tell timer to latch
|
|
|
|
mov al, 0ah ; Find if interrupt pending
|
|
out PIC, al
|
|
jmp @f
|
|
@@: in al, PIC
|
|
and eax, 01b
|
|
jz NoInterrupt
|
|
|
|
in al, TDATA ; Read in lo byte
|
|
mov dl, al
|
|
in al, TDATA ; Read in hi byte
|
|
mov dh, al
|
|
and edx, 0ffffh
|
|
mov eax, TimerData._timer_cnt
|
|
shl eax, 1
|
|
sub eax, edx
|
|
|
|
push ebx
|
|
mov ebx, eax
|
|
mov eax, TimerData.tick_count
|
|
imul TimerData._timer_cnt ; edx:eax = Number of 1.19 MHz ticks elapsed...
|
|
add eax, ebx
|
|
adc edx, 0
|
|
pop ebx
|
|
|
|
ret
|
|
|
|
NoInterrupt:
|
|
in al, TDATA ; Read in lo byte
|
|
mov ah, al
|
|
in al, TDATA ; Read in hi byte
|
|
xchg ah, al ; arrange em correctly
|
|
mov edx, TimerData._timer_cnt
|
|
sub dx, ax ; BX = timer ticks
|
|
mov ax, dx
|
|
|
|
push ebx
|
|
mov ebx, eax
|
|
mov eax, TimerData.tick_count
|
|
imul TimerData._timer_cnt ; edx:eax = Number of 1.19 MHz ticks elapsed...
|
|
add eax, ebx
|
|
adc edx, 0
|
|
pop ebx
|
|
|
|
ret
|
|
|
|
|
|
PUBLIC timer_get_fixed_seconds_
|
|
|
|
timer_get_fixed_seconds_:
|
|
push ebx
|
|
push edx
|
|
|
|
cli
|
|
call timer_get_stamp64
|
|
sti
|
|
|
|
; Timing in fixed point (16.16) seconds.
|
|
; Can be used for up to 1000 hours
|
|
shld edx, eax, 16 ; Keep 32+11 bits
|
|
shl eax, 16
|
|
; edx:eax = number of 1.19Mhz pulses elapsed.
|
|
mov ebx, 1193180
|
|
|
|
; Make sure we won't divide overflow. Make time wrap at about 9 hours
|
|
sub_again: sub edx, ebx ; subtract until negative...
|
|
jns sub_again ; ...to prevent divide overflow...
|
|
add edx, ebx ; ...then add in to get correct value.
|
|
div ebx
|
|
; eax = fixed point seconds elapsed...
|
|
|
|
pop edx
|
|
pop ebx
|
|
|
|
ret
|
|
|
|
PUBLIC timer_get_fixed_secondsX_
|
|
|
|
timer_get_fixed_secondsX_:
|
|
push ebx
|
|
push edx
|
|
|
|
call timer_get_stamp64
|
|
|
|
; Timing in fixed point (16.16) seconds.
|
|
; Can be used for up to 1000 hours
|
|
shld edx, eax, 16 ; Keep 32+11 bits
|
|
shl eax, 16
|
|
; edx:eax = number of 1.19Mhz pulses elapsed.
|
|
mov ebx, 1193180
|
|
|
|
xsub_again: sub edx, ebx ; subtract until negative...
|
|
jns xsub_again ; ...to prevent divide overflow...
|
|
add edx, ebx ; ...then add in to get correct value.
|
|
|
|
div ebx
|
|
; eax = fixed point seconds elapsed...
|
|
|
|
pop edx
|
|
pop ebx
|
|
|
|
ret
|
|
|
|
PUBLIC timer_get_approx_seconds_
|
|
timer_get_approx_seconds_:
|
|
push ebx
|
|
push edx
|
|
|
|
mov eax, TimerData.tick_count
|
|
imul TimerData._timer_cnt ; edx:eax = Number of 1.19 MHz ticks elapsed...
|
|
shld edx, eax, 16 ; Keep 32+16 bits, for conversion to fixed point
|
|
shl eax, 16
|
|
; edx:eax = number of 1.19Mhz pulses elapsed.
|
|
mov ebx, 1193180
|
|
|
|
approx_sub_again: sub edx, ebx ; subtract until negative...
|
|
jns approx_sub_again ; ...to prevent divide overflow...
|
|
add edx, ebx ; ...then add in to get correct value.
|
|
|
|
div ebx
|
|
; eax = fixed point seconds elapsed...
|
|
|
|
pop edx
|
|
pop ebx
|
|
ret
|
|
|
|
|
|
|
|
;extern void timer_set_rate(int count_val);
|
|
;extern void timer_set_function( void _far * function );
|
|
|
|
PUBLIC timer_set_rate_
|
|
timer_set_rate_:
|
|
; eax = rate
|
|
pushad
|
|
|
|
; Make sure eax below or equal to 65535 and above 0
|
|
; if its not, make it be 65536 which is normal dos
|
|
; timing.
|
|
cmp eax, 65535
|
|
jbe @f
|
|
mov eax, 65536
|
|
@@: cmp eax, 0
|
|
jne @f
|
|
mov eax, 65536
|
|
@@: ; Set the timer rate to eax
|
|
cli
|
|
mov TimerData.tick_count, 0
|
|
mov TimerData._timer_cnt, eax
|
|
mov al, 34h ; count in binary, mode 2, load low byte then hi byte, counter 0: 00 11 010 0
|
|
out TCOMMAND, al ; Reset PIT channel 0
|
|
mov eax, TimerData._timer_cnt
|
|
out TDATA, al
|
|
mov al, ah
|
|
out TDATA, al
|
|
sti
|
|
popad
|
|
ret
|
|
|
|
PUBLIC timer_set_function_
|
|
timer_set_function_:
|
|
; dx:eax = far pointer to user function
|
|
pushad
|
|
cli
|
|
mov dword ptr TimerData.user_function[0], eax ; offset
|
|
mov word ptr TimerData.user_function[4], dx ; selector
|
|
sti
|
|
popad
|
|
ret
|
|
|
|
PUBLIC timer_set_joyhandler_
|
|
timer_set_joyhandler_:
|
|
cli
|
|
mov TimerData.joystick_poller, eax
|
|
sti
|
|
ret
|
|
|
|
PUBLIC timer_delay_
|
|
timer_delay_:
|
|
pushad
|
|
mov edx, 18
|
|
mov ecx, [ds:046Ch] ; Get Current DOS Ticks
|
|
fixmul edx
|
|
add ecx, eax
|
|
timeloop:
|
|
cmp ecx, [ds:046ch]
|
|
jg timeloop
|
|
popad
|
|
ret
|
|
|
|
|
|
|
|
;************************************************************************
|
|
;************************************************************************
|
|
;***** *****
|
|
;***** T I M E R _ H A N D L E R *****
|
|
;***** *****
|
|
;************************************************************************
|
|
;************************************************************************
|
|
|
|
timer_handler:
|
|
push ds
|
|
push es
|
|
push eax
|
|
|
|
mov ax, DGROUP ; Interrupt, so point to our data segment
|
|
mov ds, ax
|
|
mov es, ax
|
|
|
|
; Increment time counter...
|
|
inc TimerData.tick_count
|
|
|
|
mov eax, TimerData._timer_cnt
|
|
add TimerData.dos_timer, eax ; Increment DOS timer
|
|
cmp TimerData.dos_timer, 65536
|
|
jb NoChainToOld ; See if we need to chain to DOS 18.2
|
|
and TimerData.dos_timer, 0ffffh
|
|
|
|
;
|
|
; Call the original DOS handler....
|
|
;
|
|
pushfd
|
|
call fword ptr [TimerData.org_interrupt]
|
|
|
|
jmp NoReset ;old handler has reset, so we don't
|
|
|
|
NoChainToOld:
|
|
; Reset controller
|
|
mov al, 20h ; Reset interrupt controller
|
|
out 20h, al
|
|
|
|
NoReset:
|
|
cmp TimerData.in_timer, 0
|
|
jne ExitInterrupt
|
|
|
|
mov TimerData.in_timer, 1 ; Mark that we're in a timer interrupt...
|
|
|
|
; Reenable interrupts
|
|
sti ; Reenable interrupts
|
|
|
|
cmp word ptr TimerData.user_function[4], 0 ; Check the selector...
|
|
je NoUserFunction
|
|
|
|
; Switch stacks while calling the user-definable function...
|
|
pushad
|
|
push fs
|
|
push gs
|
|
mov dword ptr TimerData.saved_stack[0], esp
|
|
mov word ptr TimerData.saved_stack[4], ss
|
|
lss esp, TimerData.new_stack ; Switch to new stack
|
|
call fword ptr [TimerData.user_function] ; Call new function
|
|
lss esp, TimerData.saved_stack ; Switch back to original stack
|
|
pop gs
|
|
pop fs
|
|
popad
|
|
|
|
NoUserFunction:
|
|
cmp dword ptr TimerData.joystick_poller, 0
|
|
je NoJoyPolling
|
|
mov eax, TimerData._timer_cnt
|
|
mov dword ptr TimerData.saved_stack[0], esp
|
|
mov word ptr TimerData.saved_stack[4], ss
|
|
lss esp, TimerData.new_stack ; Switch to new stack
|
|
call dword ptr TimerData.joystick_poller
|
|
lss esp, TimerData.saved_stack ; Switch back to original stack
|
|
|
|
NoJoyPolling:
|
|
cli
|
|
mov TimerData.in_timer, 0
|
|
|
|
ExitInterrupt:
|
|
;;mov al, 20h ; Reset interrupt controller
|
|
;;out 20h, al
|
|
pop eax
|
|
pop es
|
|
pop ds
|
|
iretd ; Return from timer interrupt
|
|
|
|
|
|
TIMER_LOCKED_CODE_STOP:
|
|
|
|
;************************************************************************
|
|
;************************************************************************
|
|
;***** *****
|
|
;***** T I M E R _ I N I T *****
|
|
;***** *****
|
|
;************************************************************************
|
|
;************************************************************************
|
|
|
|
|
|
PUBLIC timer_init_
|
|
|
|
timer_init_:
|
|
pushad
|
|
push ds
|
|
push es
|
|
|
|
cmp TimerData.Installed, 1
|
|
je AlreadyInstalled
|
|
|
|
mov TimerData._timer_cnt, 65536 ; Set to BIOS's normal 18.2 Hz
|
|
mov TimerData.dos_timer, 0 ; clear DOS Interrupt counter
|
|
mov dword ptr TimerData.user_function[0], 0 ; offset of user function
|
|
mov word ptr TimerData.user_function[4], 0 ; selector of user function
|
|
|
|
lea eax, ds:[TimerData.TimerStack] ; Use EAX as temp stack pointer
|
|
add eax, STACK_SIZE ; Top of stack minus space for saving old ss:esp
|
|
mov dword ptr TimerData.new_stack[0], eax
|
|
mov word ptr TimerData.new_stack[4], ds
|
|
|
|
;--------------- lock data used in interrupt
|
|
mov eax, SIZEOF TIMER_DATA
|
|
mov esi, eax
|
|
shr esi, 16
|
|
mov edi, eax
|
|
and edi, 0ffffh ; si:di = length of region to lock in bytes
|
|
lea ebx, ds:TimerData
|
|
lea ecx, ds:TimerData
|
|
shr ebx, 16
|
|
and ecx, 0ffffh ; bx:cx = start of linear address to lock
|
|
mov eax, 0600h ; DPMI lock address function
|
|
int 31h ; call dpmi
|
|
jnc @f
|
|
int 3 ; LOCK FAILED!!
|
|
@@:
|
|
;--------------- lock code used in interrupt
|
|
lea eax, cs:TIMER_LOCKED_CODE_STOP
|
|
lea ecx, cs:TIMER_LOCKED_CODE_START
|
|
sub eax, ecx
|
|
inc eax ; EAX = size of timer interrupt handler
|
|
mov esi, eax
|
|
shr esi, 16
|
|
mov edi, eax
|
|
and edi, 0ffffh ; si:di = length of region to lock in bytes
|
|
lea ebx, cs:TIMER_LOCKED_CODE_START
|
|
lea ecx, cs:TIMER_LOCKED_CODE_START
|
|
shr ebx, 16
|
|
and ecx, 0ffffh ; bx:cx = start of linear address to lock
|
|
mov eax, 0600h ; DPMI lock address function
|
|
int 31h ; call dpmi
|
|
jnc @f
|
|
int 3 ; LOCK FAILED!!
|
|
@@:
|
|
|
|
;**************************************************************
|
|
;******************* SAVE OLD INT8 HANDLER ********************
|
|
;**************************************************************
|
|
mov eax, 03508h ; DOS Get Vector 08h
|
|
int 21h ; Call DOS
|
|
|
|
mov dword ptr TimerData.org_interrupt[0], ebx ; offset of user function
|
|
mov word ptr TimerData.org_interrupt[4], es ; selector of user function
|
|
|
|
;**************************************************************
|
|
;***************** INSTALL NEW INT8 HANDLER *******************
|
|
;**************************************************************
|
|
|
|
cli
|
|
|
|
mov al, 34h ; count in binary, mode 2, load low byte then hi byte, counter 0: 00 11 010 0
|
|
out TCOMMAND, al ; Reset PIT channel 0
|
|
mov eax, TimerData._timer_cnt
|
|
out TDATA, al
|
|
mov al, ah
|
|
out TDATA, al
|
|
|
|
mov TimerData.tick_count, 0
|
|
mov TimerData.Installed,1
|
|
|
|
mov eax, 02508h ; DOS Set Vector 08h
|
|
mov edx, offset timer_handler ; Point DS:EDX to new handler
|
|
mov bx, cs
|
|
push ds
|
|
mov ds, bx
|
|
int 21h
|
|
pop ds
|
|
sti
|
|
lea eax, cs:timer_close_
|
|
call atexit_
|
|
|
|
AlreadyInstalled:
|
|
|
|
pop es
|
|
pop ds
|
|
popad
|
|
|
|
ret
|
|
|
|
|
|
;************************************************************************
|
|
;************************************************************************
|
|
;***** *****
|
|
;***** T I M E R _ C L O S E _ *****
|
|
;***** *****
|
|
;************************************************************************
|
|
;************************************************************************
|
|
|
|
PUBLIC timer_close_
|
|
|
|
timer_close_:
|
|
push eax
|
|
push edx
|
|
push ds
|
|
|
|
|
|
cmp TimerData.Installed, 0
|
|
je NotInstalled
|
|
mov TimerData.Installed, 0
|
|
|
|
;**************************************************************
|
|
;***************** RESTORE OLD INT9 HANDLER *******************
|
|
;**************************************************************
|
|
|
|
cli
|
|
mov al, 36h ; count in binary, mode 3, load low byte then hi byte, counter 0: 00 11 011 0
|
|
out TCOMMAND, al ; Reser PIT channel 0
|
|
mov ax, 0h
|
|
out TDATA, al
|
|
mov al, ah
|
|
out TDATA, al
|
|
|
|
push ds
|
|
mov eax, 02508h ; DOS Set Vector 08h
|
|
mov edx, dword ptr TimerData.org_interrupt[0]
|
|
mov ds, word ptr TimerData.org_interrupt[4]
|
|
int 21h
|
|
pop ds
|
|
|
|
sti
|
|
|
|
cmp TimerData.nested_counter, 0
|
|
je NoNestedInterrupts
|
|
mov eax, TimerData.nested_counter
|
|
;int 3 ; Get John!!
|
|
|
|
NoNestedInterrupts:
|
|
|
|
|
|
NotInstalled:
|
|
pop ds
|
|
pop edx
|
|
pop eax
|
|
|
|
ret
|
|
|
|
|
|
_TEXT ENDS
|
|
|
|
|
|
END
|