]> 4ch.mooo.com Git - 16.git/blob - 16/xlib/xvsync.asm
code miraculously works on real hardware
[16.git] / 16 / xlib / xvsync.asm
1 ; MODULE XVSYNC\r
2 ; Xlib comptible vsync handler\r
3 ; Written by Tore Bastiansen\r
4 ; based on REND386 by Dave Stampe and Bernie Roehl\r
5 \r
6 include xlib.inc\r
7 include xmain.inc\r
8 include xvsync.inc\r
9 \r
10 \r
11 TIMER_VECT                      equ 08h\r
12 \r
13 PIC_CMD                         equ 20h\r
14 NONSPEC_EOI                     equ 20h\r
15 TIMER_MODE                      equ 34h\r
16 TIMER_CONTROL                   equ 43h\r
17 TIMER_0                         equ 40h\r
18 \r
19 LATCH_COUNT                     equ 00h\r
20 \r
21 INT_IN_ADVANCE                  equ 100\r
22 \r
23 DOS_GETVECT                     equ 3500h\r
24 DOS_SETVECT                     equ 2500h\r
25 \r
26 \r
27 .data\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
34 \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
39 UserVsyncSeg            dw 0\r
40 InUserHandler           dw 0\r
41                         db 100h dup(?)\r
42 LocalStack              label byte       ;Local stack for user handler\r
43 StackSeg                dw 0\r
44 StackPtr                dw 0\r
45 \r
46 \r
47 ElapsedVrts            dw 0\r
48 VrtsToSkip             dw 1\r
49 \r
50 .code\r
51 get_vsync_period proc near\r
52         mov    al,TIMER_MODE            ;Start timer\r
53         out    TIMER_CONTROL,al\r
54         mov    al,0\r
55         out    TIMER_0,al\r
56         out    TIMER_0,al\r
57 \r
58         WaitVsyncStart\r
59 \r
60         mov    al,LATCH_COUNT\r
61         out    TIMER_CONTROL,al\r
62         in     al,TIMER_0\r
63         mov    cl,al\r
64         in     al,TIMER_0\r
65         mov    ch,al                    ;cx=65536-clicks\r
66 \r
67         WaitVsyncStart\r
68 \r
69         mov    al,LATCH_COUNT\r
70         out    TIMER_CONTROL,al\r
71         in     al,TIMER_0\r
72         mov    dl,al\r
73         in     al,TIMER_0\r
74         mov    dh,al                    ;dx=65536-clicks\r
75 \r
76         sub    cx,dx                    ;cx=clicks between two vsyncs\r
77         mov    ax,cx                    ;return in ax\r
78         ret\r
79 get_vsync_period endp\r
80 \r
81 vsync_int proc far\r
82         pusha                            ;Save regs\r
83         push   ds\r
84         push   es\r
85 \r
86         mov    ax,@data                 ;Set the right datasegment\r
87         mov    ds,ax\r
88         add    [VsyncIntTicksLo],1      ;Increment _VsyncIntTicks\r
89         adc    [VsyncIntTicksHi],0\r
90 \r
91         inc    [ElapsedVrts]\r
92         mov    cx,[ElapsedVrts]\r
93         cmp    cx,[VrtsToSkip]\r
94         jl     @@StopClock\r
95 \r
96         cmp    [_StartAddressFlag],1    ;Change in start address\r
97         jne    @@StopClock\r
98 \r
99         mov    dx,CRTC_INDEX            ;Yes, set start address\r
100         mov    ax,[_WaitingStartLow]\r
101         mov    bx,[_WaitingStartHigh]\r
102         out    dx,ax\r
103         mov    ax,bx\r
104         out    dx,ax\r
105 \r
106 @@StopClock:\r
107         cli\r
108         mov    al,TIMER_MODE            ;Stop the timer\r
109         out    TIMER_CONTROL,al         ;Dont want any interrupts\r
110         mov    al,255\r
111         out    TIMER_0,al\r
112         out    TIMER_0,al\r
113         sti\r
114 \r
115         cli\r
116         mov    dx,INPUT_STATUS_0                   ;Wait for vsync\r
117 @@WaitVS:\r
118         in     al,dx\r
119         test   al,08h\r
120         jz     @@WaitVS\r
121 \r
122         mov    al,TIMER_MODE            ;Start timer again\r
123         out    TIMER_CONTROL,al\r
124         mov    ax,[ClockRate]\r
125         out    TIMER_0,al\r
126         mov    al,ah\r
127         out    TIMER_0,al\r
128 \r
129         cmp    cx,[VrtsToSkip]\r
130         jl     @@PaletteInt\r
131 \r
132         cmp    [_StartAddressFlag],1    ;Any change in start address ?\r
133         jne    @@PaletteInt\r
134 \r
135         xor    cx,cx\r
136         mov    [ElapsedVrts],cx\r
137 \r
138         mov    ax,[_WaitingPelPan]      ;Yes, set pel pan register\r
139         mov    dx,AC_INDEX\r
140         out    dx,al\r
141         mov    al,ah\r
142         out    dx,al\r
143         mov    [_StartAddressFlag],0\r
144 \r
145 @@PaletteInt:\r
146         cmp    [_VsyncPaletteCount],0   ;Any changes in the palette\r
147         je     @@MouseInt\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
152         out    dx, al\r
153         mov    dx, DAC_DATA\r
154 \r
155 @@DacOutLoop:\r
156         outsb\r
157         outsb\r
158         outsb\r
159         loop    @@DacOutLoop\r
160         mov     [_VsyncPaletteCount],0\r
161 \r
162 @@MouseInt:\r
163         cmp    [_MouseRefreshFlag],1             ; Does the mouse need refresh\r
164         jne    @@UserInt\r
165         call   dword ptr [_MouseVsyncHandler]    ; Yes\r
166                                                                                           ;(this is not yet implemented)\r
167 \r
168 @@UserInt:\r
169         cmp    [UserVsyncSeg], 0       ;Is the a user interrupt routine?\r
170         je     short @@Sim182\r
171         cmp    [InUserHandler],0       ;Yes, but is it already active?\r
172         jne    short @@Sim182\r
173         mov    [InUserHandler],1       ;No, mark it as active\r
174         mov    [StackSeg],ss           ;make a local stack\r
175         mov    [StackPtr],sp\r
176         push    ds\r
177         pop     ss\r
178         mov     sp, offset LocalStack\r
179         sti\r
180         call    dword ptr [UserVsyncHandler]\r
181         cli\r
182         mov     sp, [StackPtr]          ;Restore old stack\r
183         mov     ss, [StackSeg]\r
184         mov     [InUserHandler],0       ;Mark as not active\r
185 \r
186 ;       SIM 18.2 Hz\r
187 @@Sim182:\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
192         pop     ds\r
193         popa\r
194         sti\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
199 @@DontChainOld:\r
200 \r
201 ; CLEAN UP AND RETURN\r
202         mov    al,NONSPEC_EOI\r
203         out    PIC_CMD,al\r
204 \r
205 \r
206         pop    es\r
207         pop    ds\r
208         popa\r
209         sti\r
210         iret\r
211 vsync_int endp\r
212 \r
213 \r
214 _x_install_vsync_handler proc\r
215 ARG VrtSkipCount:word\r
216         push    bp\r
217         mov     bp,sp\r
218         mov     ax,[VrtSkipCount]\r
219         or      ax,ax\r
220         jnz     @@NonZeroCount\r
221         mov     ax,1\r
222 @@NonZeroCount:\r
223         mov     [VrtsToSkip],ax\r
224         mov     [ElapsedVrts],0\r
225         cmp     [_VsyncHandlerActive],TRUE      ;Is it already active\r
226         je      short @@Return\r
227         call    get_vsync_period                ;no, get the vsync period\r
228 \r
229         mov     [_VsyncPeriod],ax\r
230         sub     ax,INT_IN_ADVANCE               ;We need a little extra\r
231         mov     [ClockRate],ax                  ;time\r
232 \r
233         mov     dx,18                           ;dx:ax=1193000\r
234         mov     ax,13352\r
235         idiv    [_VsyncPeriod]\r
236         mov     [_TicksPerSecond],ax            ;1193/_VsyncPeriod\r
237 \r
238         mov     word ptr [_VsyncIntTicks],0\r
239         mov     word ptr [_VsyncIntTicks+2],0\r
240 \r
241         cli\r
242         mov     ax, DOS_GETVECT+TIMER_VECT      ;Get address of old timer int\r
243         int     21h\r
244         mov     ax,es\r
245         mov     word ptr cs:[OldTimerInt],bx       ;Store in OldTimerInt\r
246         mov     word ptr cs:[OldTimerInt+2],ax\r
247 \r
248         mov     [_VsyncHandlerActive],TRUE      ;Mark handler as active\r
249         mov     ax,DOS_SETVECT+TIMER_VECT       ;Set the new timer int\r
250         push    ds\r
251         mov     dx,seg vsync_int\r
252         mov     ds,dx\r
253         mov     dx,offset vsync_int\r
254         int     21h\r
255         pop     ds\r
256 \r
257         mov     al,TIMER_MODE                   ;Reprogram timer 0\r
258         out     TIMER_CONTROL,al\r
259         mov     ax,ClockRate\r
260         out     TIMER_0,al\r
261         mov     al,ah\r
262         out     TIMER_0,al\r
263         sti\r
264 @@Return:\r
265         pop     bp\r
266         ret\r
267 _x_install_vsync_handler endp\r
268 \r
269 _x_remove_vsync_handler proc\r
270         cmp     [_VsyncHandlerActive],FALSE\r
271         je      short @@Return\r
272         mov     dx, word ptr cs:[OldTimerInt]\r
273         mov     ax, word ptr cs:[OldTimerInt+2]\r
274         push    ds\r
275         mov     ds,ax\r
276         mov     ax,DOS_SETVECT+TIMER_VECT       ;Restore the old timer int\r
277         cli\r
278         int     21h\r
279         pop     ds\r
280         mov     al,TIMER_MODE                   ;Restore timer 0\r
281         out     TIMER_CONTROL,al\r
282         mov     al,0\r
283         out     TIMER_0,al\r
284         out     TIMER_0,al\r
285         sti\r
286 @@Return:\r
287         ret\r
288 _x_remove_vsync_handler endp\r
289 \r
290 \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
296 \r
297 _x_set_user_vsync_handler proc\r
298 ARG handler_proc:dword\r
299         push    bp\r
300         mov     bp,sp\r
301         mov     ax, word ptr [handler_proc]\r
302         mov     dx, word ptr [handler_proc+2]\r
303         cli\r
304         mov     word ptr [UserVsyncHandler],ax\r
305         mov     word ptr [UserVsyncHandler+2],dx\r
306         sti\r
307         pop     bp\r
308         ret\r
309 _x_set_user_vsync_handler endp\r
310 \r
311 end\r