2 ; Xlib comptible vsync handler
\r
3 ; Written by Tore Bastiansen
\r
4 ; based on REND386 by Dave Stampe and Bernie Roehl
\r
16 TIMER_CONTROL equ 43h
\r
21 INT_IN_ADVANCE equ 100
\r
23 DOS_GETVECT equ 3500h
\r
24 DOS_SETVECT equ 2500h
\r
28 _TicksPerSecond dw 0
\r
29 _VsyncIntTicks label dword
\r
30 VsyncIntTicksLo dw 0
\r
31 VsyncIntTicksHi dw 0
\r
32 _VsyncPeriod dw 0 ;Time (in clicks) between each vsync
\r
33 ;1 click = 1.193 microseconds
\r
35 ClockRate dw 0 ;Clock rate (in clicks) for timer 0
\r
36 ClockCounter dw 0 ;Counts total clicks modulo 65536
\r
37 UserVsyncHandler label dword ;Pointer to user routine called
\r
38 UserVsyncOffs dw 0 ;called once each vsync period.
\r
42 LocalStack label byte ;Local stack for user handler
\r
51 get_vsync_period proc near
\r
52 mov al,TIMER_MODE ;Start timer
\r
53 out TIMER_CONTROL,al
\r
61 out TIMER_CONTROL,al
\r
65 mov ch,al ;cx=65536-clicks
\r
70 out TIMER_CONTROL,al
\r
74 mov dh,al ;dx=65536-clicks
\r
76 sub cx,dx ;cx=clicks between two vsyncs
\r
77 mov ax,cx ;return in ax
\r
79 get_vsync_period endp
\r
86 mov ax,@data ;Set the right datasegment
\r
88 add [VsyncIntTicksLo],1 ;Increment _VsyncIntTicks
\r
89 adc [VsyncIntTicksHi],0
\r
92 mov cx,[ElapsedVrts]
\r
96 cmp [_StartAddressFlag],1 ;Change in start address
\r
99 mov dx,CRTC_INDEX ;Yes, set start address
\r
100 mov ax,[_WaitingStartLow]
\r
101 mov bx,[_WaitingStartHigh]
\r
108 mov al,TIMER_MODE ;Stop the timer
\r
109 out TIMER_CONTROL,al ;Dont want any interrupts
\r
116 mov dx,INPUT_STATUS_0 ;Wait for vsync
\r
122 mov al,TIMER_MODE ;Start timer again
\r
123 out TIMER_CONTROL,al
\r
129 cmp cx,[VrtsToSkip]
\r
132 cmp [_StartAddressFlag],1 ;Any change in start address ?
\r
136 mov [ElapsedVrts],cx
\r
138 mov ax,[_WaitingPelPan] ;Yes, set pel pan register
\r
143 mov [_StartAddressFlag],0
\r
146 cmp [_VsyncPaletteCount],0 ;Any changes in the palette
\r
148 mov si, offset _VsyncPaletteBuffer ;Yes
\r
149 mov cx, [_VsyncPaletteCount]
\r
150 mov ax, [_VsyncPaletteStart]
\r
151 mov dx, DAC_WRITE_INDEX
\r
160 mov [_VsyncPaletteCount],0
\r
163 cmp [_MouseRefreshFlag],1 ; Does the mouse need refresh
\r
165 call dword ptr [_MouseVsyncHandler] ; Yes
\r
166 ;(this is not yet implemented)
\r
169 cmp [UserVsyncSeg], 0 ;Is the a user interrupt routine?
\r
171 cmp [InUserHandler],0 ;Yes, but is it already active?
\r
173 mov [InUserHandler],1 ;No, mark it as active
\r
174 mov [StackSeg],ss ;make a local stack
\r
178 mov sp, offset LocalStack
\r
180 call dword ptr [UserVsyncHandler]
\r
182 mov sp, [StackPtr] ;Restore old stack
\r
184 mov [InUserHandler],0 ;Mark as not active
\r
188 mov ax,[_VsyncPeriod] ;Count number of clicks
\r
189 add [ClockCounter],ax ;If it is bigger than 65536
\r
190 jnc short @@DontChainOld
\r
191 pop es ;more than 1/18.2 secs has gone
\r
195 db 0eah ; jmp instruction
\r
196 OldTimerInt dd 0 ; Pointer to old int8 routine
\r
197 ; Selfmodyfiing code
\r
198 ;jmp dword ptr [OldTimerInt] Chain to old
\r
201 ; CLEAN UP AND RETURN
\r
214 _x_install_vsync_handler proc
\r
215 ARG VrtSkipCount:word
\r
218 mov ax,[VrtSkipCount]
\r
223 mov [VrtsToSkip],ax
\r
224 mov [ElapsedVrts],0
\r
225 cmp [_VsyncHandlerActive],TRUE ;Is it already active
\r
227 call get_vsync_period ;no, get the vsync period
\r
229 mov [_VsyncPeriod],ax
\r
230 sub ax,INT_IN_ADVANCE ;We need a little extra
\r
231 mov [ClockRate],ax ;time
\r
233 mov dx,18 ;dx:ax=1193000
\r
235 idiv [_VsyncPeriod]
\r
236 mov [_TicksPerSecond],ax ;1193/_VsyncPeriod
\r
238 mov word ptr [_VsyncIntTicks],0
\r
239 mov word ptr [_VsyncIntTicks+2],0
\r
242 mov ax, DOS_GETVECT+TIMER_VECT ;Get address of old timer int
\r
245 mov word ptr cs:[OldTimerInt],bx ;Store in OldTimerInt
\r
246 mov word ptr cs:[OldTimerInt+2],ax
\r
248 mov [_VsyncHandlerActive],TRUE ;Mark handler as active
\r
249 mov ax,DOS_SETVECT+TIMER_VECT ;Set the new timer int
\r
251 mov dx,seg vsync_int
\r
253 mov dx,offset vsync_int
\r
257 mov al,TIMER_MODE ;Reprogram timer 0
\r
258 out TIMER_CONTROL,al
\r
267 _x_install_vsync_handler endp
\r
269 _x_remove_vsync_handler proc
\r
270 cmp [_VsyncHandlerActive],FALSE
\r
272 mov dx, word ptr cs:[OldTimerInt]
\r
273 mov ax, word ptr cs:[OldTimerInt+2]
\r
276 mov ax,DOS_SETVECT+TIMER_VECT ;Restore the old timer int
\r
280 mov al,TIMER_MODE ;Restore timer 0
\r
281 out TIMER_CONTROL,al
\r
288 _x_remove_vsync_handler endp
\r
291 ; WARNING: The user vsync handler cannot use the 386 specific registers
\r
292 ; (EAX,EBX,ECX,EDX,ESI,EDI,ESP,EBP,FS,GS)
\r
293 ; whithout saving them first.
\r
294 ; It must not do any drawing.
\r
295 ; Only 256 butes of stack is provided.
\r
297 _x_set_user_vsync_handler proc
\r
298 ARG handler_proc:dword
\r
301 mov ax, word ptr [handler_proc]
\r
302 mov dx, word ptr [handler_proc+2]
\r
304 mov word ptr [UserVsyncHandler],ax
\r
305 mov word ptr [UserVsyncHandler+2],dx
\r
309 _x_set_user_vsync_handler endp
\r