home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga ACS 1998 #6
/
amigaacscoverdisc1998-061998.iso
/
games
/
descent
/
source
/
bios
/
csstuff
/
timer.asm
< prev
Wrap
Assembly Source File
|
1998-06-08
|
20KB
|
686 lines
;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-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
;
; $Source: f:/miner/source/bios/rcs/timer.asm $
; $Revision: 1.28 $
; $Author: matt $
; $Date: 1995/02/15 01:36:56 $
;
; Routines for setting and using system timers
;
; $Log: timer.asm $
; Revision 1.28 1995/02/15 01:36:56 matt
; Cleaned up code to avoid doing out 20,20 more than once
;
; Revision 1.27 1995/02/14 11:39:37 john
; Fixed bug with joystick handler not having enough
; stack space under Windows.
;
; Revision 1.26 1995/02/09 21:51:39 john
; Made so that DOS timer interrupt gets called before interrupts are
; enabled in timer interpt because not doing this cause conflicts with
; ps2 style mice and smartdrv with write caching enabled that hangs the
; program and trashes the FAT.
;
; Revision 1.25 1995/02/04 15:39:33 john
; More time interrupt changes.
;
; Revision 1.24 1995/02/03 23:25:21 john
; Made so that when interrupts are nested, the time interrupt still
; calls the DOS one.
;
; Revision 1.23 1995/01/29 19:00:42 john
; Made latching timer value more readable.
;
; Revision 1.22 1995/01/17 10:34:53 mike
; prevent divide overflows.
;
; Revision 1.21 1994/12/15 11:10:54 john
; Added code that should make DOS 18.2 callbacks
; a bit more accurate.
;
; Revision 1.20 1994/12/10 12:47:34 john
; Made so that timer_get_fixed seconds and get_approx seconds are always equal.
;
; Revision 1.19 1994/12/10 12:27:34 john
; Added timer_get_approx_seconds.
;
; Revision 1.18 1994/12/10 12:07:14 john
; Added tick counter variable.
;
; Revision 1.17 1994/12/04 11:55:16 john
; Made stick read at whatever rate the clock is at, not
; at 18.2 times/second.
;
; Revision 1.16 1994/11/28 15:24:10 john
; Cleaned up timer interrupt a bit.
;
; Revision 1.15 1994/11/22 17:00:43 john
; Made the timer handler fill in ES along with DS.
; The HMI drivers expect this.
;
; Revision 1.14 1994/11/15 12:04:38 john
; Cleaned up timer code a bit... took out unused functions
; like timer_get_milliseconds, etc.
;
; Revision 1.13 1994/10/05 16:17:40 john
; Made interrupts more stable.
;
; Revision 1.12 1994/09/29 18:29:57 john
; Enabled interrupts whil calling the user_function.
;
; Revision 1.11 1994/09/23 16:00:31 john
; MAde the timer interrupt switch to a 4K stack
; before calling the timer_function.
;
; Revision 1.10 1994/09/22 16:09:21 john
; Fixed some virtual memory lockdown problems with timer and
; joystick.
;
; Revision 1.9 1994/04/29 12:13:48 john
; Locked all memory used during interrupts so that program
; won't hang when using virtual memory.
;
; Revision 1.8 1994/04/28 23:50:49 john
; Changed calling for init_timer. Made the function that the
; timer calls be a far function. All of this was done to make
; our timer system compatible with the HMI sound stuff.
;
; Revision 1.7 1994/02/17 15:57:15 john
; Changed key libary to C.
;
; Revision 1.6 1994/01/18 20:19:17 john
; Fixed minor flaws with pending interrupts,
; interfaced with joystick code.
;
; Revision 1.5 1994/01/18 13:54:18 john
; Fixed a few miner flaws.
;
; Revision 1.4 1994/01/18 10:58:25 john
; Added timer_get_fixed_seconds
;
; Revision 1.3 1993/12/20 15:40:59 john
; *** empty log message ***
;
; Revision 1.2 1993/09/23 18:08:44 john
; added code so that timer_init can handle multiple calls.
; added code so that atexit is called.
;
; Revision 1.1 1993/07/10 13:10:43 matt
; Initial revision
;
;
;
;***************************************************************************
;***************************************************************************
;***** *****
;***** 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 1.28 1995/02/15 01:36:56 matt 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 0
sound_timer dd 0
joystick_timer dd 0
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 (0)
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
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
;************************************************************************
;************************************************************************
;***** *****
;***** 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
cmp TimerData._timer_cnt, 9940
ja NotEveryOtherTime
test TimerData.tick_count, 1
jz NotEveryOtherTime
; every other time, do vsync...
push edx
mov dx, 3DAh
VS2B: in al, dx
and al, 08h
jz VS2B ; Loop until in vertical retrace
pop edx
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
NotEveryOtherTime:
mov eax, TimerData._timer_cnt
add TimerData.dos_timer, eax ; Increment DOS timer
add TimerData.sound_timer, eax
add TimerData.joystick_timer, eax
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
cmp TimerData.sound_timer, 9943
jb NoUserFunction ; See if we need to call the 120 hz sound mixer.
sub TimerData.sound_timer, 9943
; 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, 39772
cmp TimerData.joystick_timer, eax
jb NoJoyPolling ; See if we poll the joystick buttons
sub TimerData.joystick_timer, eax
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:
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 TimerData.sound_timer, 0 ; clear 120 hz handler
mov TimerData.joystick_timer, 0 ; clear joystick poller
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