; MODULE XVSYNC ; Xlib comptible vsync handler ; Written by Tore Bastiansen ; based on REND386 by Dave Stampe and Bernie Roehl include xlib.inc include xmain.inc include xvsync.inc TIMER_VECT equ 08h PIC_CMD equ 20h NONSPEC_EOI equ 20h TIMER_MODE equ 34h TIMER_CONTROL equ 43h TIMER_0 equ 40h LATCH_COUNT equ 00h INT_IN_ADVANCE equ 100 DOS_GETVECT equ 3500h DOS_SETVECT equ 2500h .data _TicksPerSecond dw 0 _VsyncIntTicks label dword VsyncIntTicksLo dw 0 VsyncIntTicksHi dw 0 _VsyncPeriod dw 0 ;Time (in clicks) between each vsync ;1 click = 1.193 microseconds ClockRate dw 0 ;Clock rate (in clicks) for timer 0 ClockCounter dw 0 ;Counts total clicks modulo 65536 UserVsyncHandler label dword ;Pointer to user routine called UserVsyncOffs dw 0 ;called once each vsync period. UserVsyncSeg dw 0 InUserHandler dw 0 db 100h dup(?) LocalStack label byte ;Local stack for user handler StackSeg dw 0 StackPtr dw 0 ElapsedVrts dw 0 VrtsToSkip dw 1 .code get_vsync_period proc near mov al,TIMER_MODE ;Start timer out TIMER_CONTROL,al mov al,0 out TIMER_0,al out TIMER_0,al WaitVsyncStart mov al,LATCH_COUNT out TIMER_CONTROL,al in al,TIMER_0 mov cl,al in al,TIMER_0 mov ch,al ;cx=65536-clicks WaitVsyncStart mov al,LATCH_COUNT out TIMER_CONTROL,al in al,TIMER_0 mov dl,al in al,TIMER_0 mov dh,al ;dx=65536-clicks sub cx,dx ;cx=clicks between two vsyncs mov ax,cx ;return in ax ret get_vsync_period endp vsync_int proc far pusha ;Save regs push ds push es mov ax,@data ;Set the right datasegment mov ds,ax add [VsyncIntTicksLo],1 ;Increment _VsyncIntTicks adc [VsyncIntTicksHi],0 inc [ElapsedVrts] mov cx,[ElapsedVrts] cmp cx,[VrtsToSkip] jl @@StopClock cmp [_StartAddressFlag],1 ;Change in start address jne @@StopClock mov dx,CRTC_INDEX ;Yes, set start address mov ax,[_WaitingStartLow] mov bx,[_WaitingStartHigh] out dx,ax mov ax,bx out dx,ax @@StopClock: cli mov al,TIMER_MODE ;Stop the timer out TIMER_CONTROL,al ;Dont want any interrupts mov al,255 out TIMER_0,al out TIMER_0,al sti cli mov dx,INPUT_STATUS_0 ;Wait for vsync @@WaitVS: in al,dx test al,08h jz @@WaitVS mov al,TIMER_MODE ;Start timer again out TIMER_CONTROL,al mov ax,[ClockRate] out TIMER_0,al mov al,ah out TIMER_0,al cmp cx,[VrtsToSkip] jl @@PaletteInt cmp [_StartAddressFlag],1 ;Any change in start address ? jne @@PaletteInt xor cx,cx mov [ElapsedVrts],cx mov ax,[_WaitingPelPan] ;Yes, set pel pan register mov dx,AC_INDEX out dx,al mov al,ah out dx,al mov [_StartAddressFlag],0 @@PaletteInt: cmp [_VsyncPaletteCount],0 ;Any changes in the palette je @@MouseInt mov si, offset _VsyncPaletteBuffer ;Yes mov cx, [_VsyncPaletteCount] mov ax, [_VsyncPaletteStart] mov dx, DAC_WRITE_INDEX out dx, al mov dx, DAC_DATA @@DacOutLoop: outsb outsb outsb loop @@DacOutLoop mov [_VsyncPaletteCount],0 @@MouseInt: cmp [_MouseRefreshFlag],1 ; Does the mouse need refresh jne @@UserInt call dword ptr [_MouseVsyncHandler] ; Yes ;(this is not yet implemented) @@UserInt: cmp [UserVsyncSeg], 0 ;Is the a user interrupt routine? je short @@Sim182 cmp [InUserHandler],0 ;Yes, but is it already active? jne short @@Sim182 mov [InUserHandler],1 ;No, mark it as active mov [StackSeg],ss ;make a local stack mov [StackPtr],sp push ds pop ss mov sp, offset LocalStack sti call dword ptr [UserVsyncHandler] cli mov sp, [StackPtr] ;Restore old stack mov ss, [StackSeg] mov [InUserHandler],0 ;Mark as not active ; SIM 18.2 Hz @@Sim182: mov ax,[_VsyncPeriod] ;Count number of clicks add [ClockCounter],ax ;If it is bigger than 65536 jnc short @@DontChainOld pop es ;more than 1/18.2 secs has gone pop ds popa sti db 0eah ; jmp instruction OldTimerInt dd 0 ; Pointer to old int8 routine ; Selfmodyfiing code ;jmp dword ptr [OldTimerInt] Chain to old @@DontChainOld: ; CLEAN UP AND RETURN mov al,NONSPEC_EOI out PIC_CMD,al pop es pop ds popa sti iret vsync_int endp _x_install_vsync_handler proc ARG VrtSkipCount:word push bp mov bp,sp mov ax,[VrtSkipCount] or ax,ax jnz @@NonZeroCount mov ax,1 @@NonZeroCount: mov [VrtsToSkip],ax mov [ElapsedVrts],0 cmp [_VsyncHandlerActive],TRUE ;Is it already active je short @@Return call get_vsync_period ;no, get the vsync period mov [_VsyncPeriod],ax sub ax,INT_IN_ADVANCE ;We need a little extra mov [ClockRate],ax ;time mov dx,18 ;dx:ax=1193000 mov ax,13352 idiv [_VsyncPeriod] mov [_TicksPerSecond],ax ;1193/_VsyncPeriod mov word ptr [_VsyncIntTicks],0 mov word ptr [_VsyncIntTicks+2],0 cli mov ax, DOS_GETVECT+TIMER_VECT ;Get address of old timer int int 21h mov ax,es mov word ptr cs:[OldTimerInt],bx ;Store in OldTimerInt mov word ptr cs:[OldTimerInt+2],ax mov [_VsyncHandlerActive],TRUE ;Mark handler as active mov ax,DOS_SETVECT+TIMER_VECT ;Set the new timer int push ds mov dx,seg vsync_int mov ds,dx mov dx,offset vsync_int int 21h pop ds mov al,TIMER_MODE ;Reprogram timer 0 out TIMER_CONTROL,al mov ax,ClockRate out TIMER_0,al mov al,ah out TIMER_0,al sti @@Return: pop bp ret _x_install_vsync_handler endp _x_remove_vsync_handler proc cmp [_VsyncHandlerActive],FALSE je short @@Return mov dx, word ptr cs:[OldTimerInt] mov ax, word ptr cs:[OldTimerInt+2] push ds mov ds,ax mov ax,DOS_SETVECT+TIMER_VECT ;Restore the old timer int cli int 21h pop ds mov al,TIMER_MODE ;Restore timer 0 out TIMER_CONTROL,al mov al,0 out TIMER_0,al out TIMER_0,al sti @@Return: ret _x_remove_vsync_handler endp ; WARNING: The user vsync handler cannot use the 386 specific registers ; (EAX,EBX,ECX,EDX,ESI,EDI,ESP,EBP,FS,GS) ; whithout saving them first. ; It must not do any drawing. ; Only 256 butes of stack is provided. _x_set_user_vsync_handler proc ARG handler_proc:dword push bp mov bp,sp mov ax, word ptr [handler_proc] mov dx, word ptr [handler_proc+2] cli mov word ptr [UserVsyncHandler],ax mov word ptr [UserVsyncHandler+2],dx sti pop bp ret _x_set_user_vsync_handler endp end