; ; $Source: /cvs/cvsroot/d2x/arch/dos/timer.asm,v $ ; $Revision: 1.3 $ ; $Author: bradleyb $ ; $Date: 2001-10-19 09:01:56 $ ; ; DOS timer routines ; ; $Log: not supported by cvs2svn $ ; Revision 1.2 2001/01/29 13:35:08 bradleyb ; Fixed build system, minor fixes ; ; [BITS 32] [EXTERN _atexit] [EXTERN ___djgpp_base_address] [GLOBAL _timer_get_fixed_seconds] [GLOBAL _timer_get_fixed_secondsX] [GLOBAL _timer_get_approx_seconds] [GLOBAL _timer_set_rate] [GLOBAL _timer_set_function] [GLOBAL _timer_set_joyhandler] [GLOBAL _timer_init] [GLOBAL _timer_close] [SECTION .data] TDATA EQU 40h TCOMMAND EQU 43h PIC EQU 020h STACK_SIZE EQU 4096 %define df times 3 dw TimerData db 0 dd 0 dd 65536 dd 0 dd 0 df 0 df 0 df 0 df 0 dd 0 db 0 times STACK_SIZE db 0 td_in_timer equ TimerData td_nested_counter equ td_in_timer+1 td__timer_cnt equ td_nested_counter+4 td_dos_timer equ td__timer_cnt+4 td_joystick_poller equ td_dos_timer+4 td_user_function equ td_joystick_poller+4 td_org_interrupt equ td_user_function+6 td_saved_stack equ td_org_interrupt+6 td_new_stack equ td_saved_stack+6 td_tick_count equ td_new_stack+6 td_Installed equ td_tick_count+4 td_TimerStack equ td_Installed+1 [SECTION .text] TIMER_LOCKED_CODE_START: __my_ds dw 0 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 @ @: 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, [td__timer_cnt] shl eax, 1 sub eax, edx push ebx mov ebx, eax mov eax, [td_tick_count] imul dword [td__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, [td__timer_cnt] sub dx, ax ; BX = timer ticks mov ax, dx push ebx mov ebx, eax mov eax, [td_tick_count] imul dword [td__timer_cnt] ; edx:eax = Number of 1.19 MHz ticks elapsed... add eax, ebx adc edx, 0 pop ebx ret _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 _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 _timer_get_approx_seconds: push ebx push edx mov eax, [td_tick_count] imul dword [td__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 ;---------------------------------------------------------------------------- ;Prototype: extern void timer_set_rate(int count_val); ;---------------------------------------------------------------------------- _timer_set_rate: mov eax,[esp+4] ; 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 @@ mov eax, 65536 @@: cmp eax, 0 jne @@@ mov eax, 65536 @@@: ; Set the timer rate to eax cli mov dword [td_tick_count], 0 mov [td__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, [td__timer_cnt] out TDATA, al mov al, ah out TDATA, al sti popad ret ;---------------------------------------------------------------------------- ;Prototype: extern void timer_set_function( void * function ); ;---------------------------------------------------------------------------- _timer_set_function: ; arg1 = near pointer to user function push eax mov eax, [esp+8] ; arg 1 cli mov dword [td_user_function+0], eax ; offset mov word [td_user_function+4], cs ; selector sti pop eax ret _timer_set_joyhandler: mov eax, [esp+4] cli mov dword [td_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, [cs:__my_ds] ; Interrupt, so point to our data segment mov ds, ax mov es, ax ; Increment time counter... inc dword [td_tick_count] mov eax, [td__timer_cnt] add dword [td_dos_timer], eax ; Increment DOS timer cmp dword [td_dos_timer], 65536 jb NoChainToOld ; See if we need to chain to DOS 18.2 and dword [td_dos_timer], 0ffffh ; ; Call the original DOS handler.... ; pushfd call far dword [td_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 byte [td_in_timer], 0 jne ExitInterrupt mov byte [td_in_timer], 1 ; Mark that we're in a timer interrupt... ; Reenable interrupts sti ; Reenable interrupts cmp word [td_user_function+4], 0 ; Check the selector... je NoUserFunction ; Switch stacks while calling the user-definable function... pushad push fs push gs mov dword [td_saved_stack+0], esp mov word [td_saved_stack+4], ss lss esp, [td_new_stack] ; Switch to new stack call far dword [td_user_function] ; Call new function lss esp, [td_saved_stack] ; Switch back to original stack pop gs pop fs popad NoUserFunction: cmp dword [td_joystick_poller], 0 je NoJoyPolling mov eax, [td__timer_cnt] mov dword [td_saved_stack+0], esp mov word [td_saved_stack+4], ss lss esp, [td_new_stack] ; Switch to new stack pusha push eax call dword [td_joystick_poller] pop eax popa lss esp, [td_saved_stack] ; Switch back to original stack NoJoyPolling: cli mov byte [td_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 ***** ;***** ***** ;************************************************************************ ;************************************************************************ _timer_init: pushad push ds push es cmp byte [td_Installed], 1 je NEAR AlreadyInstalled mov [__my_ds],ds mov dword [td__timer_cnt], 65536 ; Set to BIOS's normal 18.2 Hz mov dword [td_dos_timer], 0 ; clear DOS Interrupt counter mov dword [td_user_function+0], 0 ; offset of user function mov word [td_user_function+4], 0 ; selector of user function lea eax, [ds:td_TimerStack] ; Use EAX as temp stack pointer add eax, STACK_SIZE ; Top of stack minus space for saving old ss:esp mov dword [td_new_stack+0], eax mov word [td_new_stack+4], ds ;--------------- lock data used in interrupt mov eax, (td_TimerStack-TimerData)+STACK_SIZE ;sizeof timer_data mov esi, eax shr esi, 16 mov edi, eax and edi, 0ffffh ; si:di = length of region to lock in bytes mov ebx, TimerData add ebx, [___djgpp_base_address] mov ecx, ebx 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 @@@@ int 3 ; LOCK FAILED!! @@@@: ;--------------- lock code used in interrupt mov eax, TIMER_LOCKED_CODE_STOP sub eax, TIMER_LOCKED_CODE_START 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 mov ebx, TIMER_LOCKED_CODE_START add ebx, [___djgpp_base_address] mov ecx, ebx 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 @@@@@ ;This is getting fun int 3 ; LOCK FAILED!! @@@@@: ;************************************************************** ;******************* SAVE OLD INT8 HANDLER ******************** ;************************************************************** mov ax,0204h mov bl,8 int 31h mov dword [td_org_interrupt+0],edx mov word [td_org_interrupt+4],cx ;************************************************************** ;***************** 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, [td__timer_cnt] out TDATA, al mov al, ah out TDATA, al mov dword [td_tick_count], 0 mov byte [td_Installed],1 mov ax,0205h mov bl,8 mov edx,timer_handler mov cx,cs int 31h sti push dword _timer_close call _atexit add esp,4 AlreadyInstalled: pop es pop ds popad ret ;************************************************************************ ;************************************************************************ ;***** ***** ;***** T I M E R _ C L O S E _ ***** ;***** ***** ;************************************************************************ ;************************************************************************ _timer_close: push eax push ebx push ecx push edx cmp byte [td_Installed], 0 je NotInstalled mov byte [td_Installed], 0 ;************************************************************** ;***************** RESTORE OLD INT8 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 mov ax,0205h mov bl,8 mov edx,dword [td_org_interrupt+0] mov cx,word [td_org_interrupt+4] int 31h sti cmp dword [td_nested_counter], 0 je NoNestedInterrupts mov eax, [td_nested_counter] ;int 3 ; Get John!! NoNestedInterrupts: NotInstalled: pop edx pop ecx pop ebx pop eax ret