ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.12
Committed: 2001-01-11T18:00:40Z (23 years, 10 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.11: +33 -0 lines
Log Message:
- Cleaned up some comments again
- The dirtyPages[] array overrun conditions should be really safe now

File Contents

# User Rev Content
1 gbeauche 1.1 /*
2     * video_vosf.h - Video/graphics emulation, video on SEGV signals support
3     *
4     * Basilisk II (C) 1997-2000 Christian Bauer
5     *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #ifndef VIDEO_VOSF_H
22     #define VIDEO_VOSF_H
23    
24     // Note: this file is #include'd in video_x.cpp
25     #ifdef ENABLE_VOSF
26    
27     /*
28     * Page-aligned memory allocation
29     */
30    
31     // Align on page boundaries
32 gbeauche 1.4 static uintptr align_on_page_boundary(uintptr size)
33 gbeauche 1.1 {
34     const uint32 page_size = getpagesize();
35     const uint32 page_mask = page_size - 1;
36     return (size + page_mask) & ~page_mask;
37     }
38    
39     // Allocate memory on page boundary
40     static void * allocate_framebuffer(uint32 size, uint8 * hint = 0)
41     {
42     // Remind that the system can allocate at 0x00000000...
43     return mmap((caddr_t)hint, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zero_fd, 0);
44     }
45    
46    
47     /*
48     * Screen depth identification
49     */
50    
51     enum {
52     ID_DEPTH_UNKNOWN = -1,
53     ID_DEPTH_1,
54     ID_DEPTH_8,
55     ID_DEPTH_15,
56     ID_DEPTH_16,
57     ID_DEPTH_24,
58     ID_DEPTH_32 = ID_DEPTH_24,
59     ID_DEPTH_COUNT
60     };
61    
62     static int depth_id(int depth)
63     {
64     int id;
65     switch (depth) {
66     case 1 : id = ID_DEPTH_1; break;
67     case 8 : id = ID_DEPTH_8; break;
68     case 15 : id = ID_DEPTH_15; break;
69     case 16 : id = ID_DEPTH_16; break;
70     case 24 : id = ID_DEPTH_24; break;
71     case 32 : id = ID_DEPTH_32; break;
72     default : id = ID_DEPTH_UNKNOWN;
73     }
74     return id;
75     }
76    
77    
78     /*
79     * Frame buffer copy function templates
80     */
81    
82     // No conversion required
83    
84     #define MEMCPY_PROFITABLE
85     #ifdef MEMCPY_PROFITABLE
86     static void do_fbcopy_raw(uint8 * dest, const uint8 * source, uint32 length)
87     {
88     memcpy(dest, source, length);
89     }
90     #else
91     #define FB_BLIT_1(dst, src) (dst = (src))
92     #define FB_BLIT_2(dst, src) (dst = (src))
93 gbeauche 1.2 #define FB_DEPTH 0
94 gbeauche 1.1 #define FB_FUNC_NAME do_fbcopy_raw
95     #include "video_blit.h"
96     #endif
97    
98    
99     // RGB 555
100    
101 gbeauche 1.2 #ifdef WORDS_BIGENDIAN
102     # define FB_FUNC_NAME do_fbcopy_15_obo
103     #else
104     # define FB_FUNC_NAME do_fbcopy_15_nbo
105     #endif
106    
107 gbeauche 1.1 #define FB_BLIT_1(dst, src) \
108     (dst = (((src) >> 8) & 0xff) | (((src) & 0xff) << 8))
109    
110     #define FB_BLIT_2(dst, src) \
111     (dst = (((src) >> 8) & 0x00ff00ff) | (((src) & 0x00ff00ff) << 8))
112    
113     #define FB_DEPTH 15
114     #include "video_blit.h"
115    
116    
117     // RGB 565
118    
119 gbeauche 1.2 #ifdef WORDS_BIGENDIAN
120    
121     // native byte order
122    
123     #define FB_BLIT_1(dst, src) \
124     (dst = (((src) & 0x1f) | (((src) << 1) & 0xffc0)))
125    
126     #define FB_BLIT_2(dst, src) \
127     (dst = (((src) & 0x001f001f) | (((src) << 1) & 0xffc0ffc0)))
128    
129     #define FB_DEPTH 16
130     #define FB_FUNC_NAME do_fbcopy_16_nbo
131     #include "video_blit.h"
132    
133 cebix 1.9 // opposite byte order
134 gbeauche 1.2
135     #define FB_BLIT_1(dst, src) \
136 cebix 1.9 (dst = ((((src) >> 7) & 0xff) | (((src) << 9) & 0xc000) | (((src) << 8) & 0x1f00)))
137 gbeauche 1.2
138     #define FB_BLIT_2(dst, src) \
139 cebix 1.9 (dst = ((((src) >> 7) & 0x00ff00ff) | (((src) << 9) & 0xc000c000) | (((src) << 8) & 0x1f001f00)))
140 gbeauche 1.2
141     #define FB_DEPTH 16
142     #define FB_FUNC_NAME do_fbcopy_16_obo
143     #include "video_blit.h"
144    
145     #else
146    
147     // native byte order
148    
149 gbeauche 1.1 #define FB_BLIT_1(dst, src) \
150     (dst = (((src) >> 8) & 0x001f) | (((src) << 9) & 0xfe00) | (((src) >> 7) & 0x01c0))
151    
152     #define FB_BLIT_2(dst, src) \
153     (dst = (((src) >> 8) & 0x001f001f) | (((src) << 9) & 0xfe00fe00) | (((src) >> 7) & 0x01c001c0))
154    
155     #define FB_DEPTH 16
156 gbeauche 1.2 #define FB_FUNC_NAME do_fbcopy_16_nbo
157 gbeauche 1.1 #include "video_blit.h"
158    
159 gbeauche 1.2 // opposite byte order (untested)
160    
161     #define FB_BLIT_1(dst, src) \
162     (dst = (((src) & 0x1f00) | (((src) << 1) & 0xe0fe) | (((src) >> 15) & 1)))
163    
164     #define FB_BLIT_2(dst, src) \
165     (dst = (((src) & 0x1f001f00) | (((src) << 1) & 0xe0fee0fe) | (((src) >> 15) & 0x10001)))
166    
167     #define FB_DEPTH 16
168     #define FB_FUNC_NAME do_fbcopy_16_obo
169     #include "video_blit.h"
170    
171     #endif
172 gbeauche 1.1
173     // RGB 888
174    
175 gbeauche 1.2 #ifdef WORDS_BIGENDIAN
176     # define FB_FUNC_NAME do_fbcopy_24_obo
177     #else
178     # define FB_FUNC_NAME do_fbcopy_24_nbo
179     #endif
180    
181 gbeauche 1.1 #define FB_BLIT_1(dst, src) \
182     (dst = (src))
183    
184     #define FB_BLIT_2(dst, src) \
185 gbeauche 1.4 (dst = (((src) >> 24) & 0xff) | (((src) >> 8) & 0xff00) | (((src) & 0xff00) << 8) | (((src) & 0xff) << 24))
186 gbeauche 1.1
187     #define FB_DEPTH 24
188     #include "video_blit.h"
189    
190    
191     /*
192     * Frame buffer copy functions map table
193     */
194    
195     typedef void (*fbcopy_func)(uint8 *, const uint8 *, uint32);
196     static fbcopy_func do_update_framebuffer;
197    
198     #define FBCOPY_FUNC(aHandler) do_ ## aHandler
199    
200     #if REAL_ADDRESSING || DIRECT_ADDRESSING
201     #define WD(X) { FBCOPY_FUNC(X), FBCOPY_FUNC(X) }
202     #else
203     #define WD(X) { FBCOPY_FUNC(fbcopy_raw), FBCOPY_FUNC(fbcopy_raw) }
204     #endif
205    
206     // fb_copy_funcs[depth_id][native_byte_order][dga_mode]
207     // NT : not tested
208     // OK : has been successfully tested
209 cebix 1.10 // NBO : native byte order (X server vs. client)
210     // OBO : opposite byte order (X server vs. client)
211 gbeauche 1.1 static fbcopy_func fbcopy_funcs[ID_DEPTH_COUNT][2][2] = {
212     #ifdef WORDS_BIGENDIAN
213 gbeauche 1.2 /* opposite byte order native byte order */
214     /* 1 bpp */ { WD(fbcopy_raw) , WD(fbcopy_raw) }, // NT
215     /* 8 bpp */ { WD(fbcopy_raw) , WD(fbcopy_raw) }, // OK (NBO)
216 cebix 1.10 /* 15 bpp */ { WD(fbcopy_15_obo) , WD(fbcopy_raw) }, // OK (OBO)
217 cebix 1.9 /* 16 bpp */ { WD(fbcopy_16_obo) , WD(fbcopy_16_nbo) }, // OK (OBO)
218 cebix 1.10 /* 24 bpp */ { WD(fbcopy_24_obo) , WD(fbcopy_raw) } // OK (OBO)
219 gbeauche 1.1 #else
220 gbeauche 1.2 /* opposite byte order native byte order */
221     /* 1 bpp */ { WD(fbcopy_raw) , WD(fbcopy_raw) }, // NT
222     /* 8 bpp */ { WD(fbcopy_raw) , WD(fbcopy_raw) }, // OK (NBO)
223     /* 15 bpp */ { WD(fbcopy_raw) , WD(fbcopy_15_nbo) }, // OK (NBO)
224     /* 16 bpp */ { WD(fbcopy_16_obo) , WD(fbcopy_16_nbo) }, // OK (NBO)
225 cebix 1.10 /* 24 bpp */ { WD(fbcopy_raw) , WD(fbcopy_24_nbo) } // OK (NBO)
226 gbeauche 1.1 #endif
227     };
228    
229     #undef WD
230    
231     #define FBCOPY_FUNC_ERROR \
232     ErrorAlert("Invalid screen depth")
233    
234     #define GET_FBCOPY_FUNC(aDepth, aNativeByteOrder, aDisplay) \
235     ((depth_id(aDepth) == ID_DEPTH_UNKNOWN) ? ( FBCOPY_FUNC_ERROR, (fbcopy_func)0 ) : \
236     fbcopy_funcs[depth_id(aDepth)][(aNativeByteOrder)][(aDisplay) == DISPLAY_DGA ? 1 : 0])
237    
238    
239     /*
240     * Screen fault handler
241     */
242    
243 gbeauche 1.11 const uintptr INVALID_PC = (uintptr)-1;
244    
245     static inline void do_handle_screen_fault(uintptr addr, uintptr pc = INVALID_PC)
246 gbeauche 1.1 {
247 gbeauche 1.11 /* Someone attempted to write to the frame buffer. Make it writeable
248     * now so that the data could actually be written. It will be made
249     * read-only back in one of the screen update_*() functions.
250     */
251     if ((addr >= mainBuffer.memStart) && (addr < mainBuffer.memEnd)) {
252     const int page = (addr - mainBuffer.memStart) >> mainBuffer.pageBits;
253     caddr_t page_ad = (caddr_t)(addr & ~(mainBuffer.pageSize - 1));
254     LOCK_VOSF;
255     PFLAG_SET(page);
256     mprotect(page_ad, mainBuffer.pageSize, PROT_READ | PROT_WRITE);
257     UNLOCK_VOSF;
258     return;
259 gbeauche 1.1 }
260    
261 gbeauche 1.11 /* Otherwise, we don't know how to handle the fault, let it crash */
262     fprintf(stderr, "do_handle_screen_fault: unhandled address 0x%08X", addr);
263     if (pc != INVALID_PC)
264     fprintf(stderr, " [IP=0x%08X]", pc);
265     fprintf(stderr, "\n");
266    
267     signal(SIGSEGV, SIG_DFL);
268 gbeauche 1.1 }
269    
270     #if defined(HAVE_SIGINFO_T)
271 cebix 1.6
272 gbeauche 1.1 static void Screen_fault_handler(int, siginfo_t * sip, void *)
273     {
274     D(bug("Screen_fault_handler: ADDR=0x%08X\n", sip->si_addr));
275 gbeauche 1.4 do_handle_screen_fault((uintptr)sip->si_addr);
276 gbeauche 1.1 }
277 cebix 1.6
278 gbeauche 1.1 #elif defined(HAVE_SIGCONTEXT_SUBTERFUGE)
279 cebix 1.6
280 gbeauche 1.1 # if defined(__i386__) && defined(__linux__)
281     static void Screen_fault_handler(int, struct sigcontext scs)
282     {
283     D(bug("Screen_fault_handler: ADDR=0x%08X from IP=0x%08X\n", scs.cr2, scs.eip));
284 gbeauche 1.11 do_handle_screen_fault((uintptr)scs.cr2, (uintptr)scs.eip);
285 gbeauche 1.1 }
286 cebix 1.6
287     # elif defined(__m68k__) && defined(__NetBSD__)
288    
289     # include <m68k/frame.h>
290     static void Screen_fault_handler(int, int code, struct sigcontext *scp)
291     {
292     D(bug("Screen_fault_handler: ADDR=0x%08X\n", code));
293     struct sigstate {
294     int ss_flags;
295     struct frame ss_frame;
296     };
297     struct sigstate *state = (struct sigstate *)scp->sc_ap;
298     uintptr fault_addr;
299     switch (state->ss_frame.f_format) {
300     case 7: // 68040 access error
301     // "code" is sometimes unreliable (i.e. contains NULL or a bogus address), reason unknown
302     fault_addr = state->ss_frame.f_fmt7.f_fa;
303     break;
304     default:
305     fault_addr = (uintptr)code;
306     break;
307     }
308     do_handle_screen_fault(fault_addr);
309     }
310    
311 gbeauche 1.1 # else
312     # error "No suitable subterfuge for Video on SEGV signals"
313     # endif
314     #else
315     # error "Can't do Video on SEGV signals"
316     #endif
317    
318    
319     /*
320     * Screen fault handler initialization
321     */
322    
323     #if defined(HAVE_SIGINFO_T)
324     static bool Screen_fault_handler_init()
325     {
326     // Setup SIGSEGV handler to process writes to frame buffer
327     sigemptyset(&vosf_sa.sa_mask);
328     vosf_sa.sa_sigaction = Screen_fault_handler;
329 cebix 1.5 vosf_sa.sa_flags = SA_SIGINFO;
330 gbeauche 1.1 return (sigaction(SIGSEGV, &vosf_sa, NULL) == 0);
331     }
332     #elif defined(HAVE_SIGCONTEXT_SUBTERFUGE)
333     static bool Screen_fault_handler_init()
334     {
335     // Setup SIGSEGV handler to process writes to frame buffer
336     sigemptyset(&vosf_sa.sa_mask);
337     vosf_sa.sa_handler = (void (*)(int)) Screen_fault_handler;
338 cebix 1.7 #if !EMULATED_68K && defined(__NetBSD__)
339     sigaddset(&vosf_sa.sa_mask, SIGALRM);
340     vosf_sa.sa_flags = SA_ONSTACK;
341     #else
342 gbeauche 1.1 vosf_sa.sa_flags = 0;
343 cebix 1.7 #endif
344 gbeauche 1.1 return (sigaction(SIGSEGV, &vosf_sa, NULL) == 0);
345     }
346     #endif
347    
348    
349     /*
350     * Update display for Windowed mode and VOSF
351     */
352    
353 gbeauche 1.12 /* How can we deal with array overrun conditions ?
354    
355     The state of the framebuffer pages that have been touched are maintained
356     in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
357    
358     Terminology
359    
360     "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
361     "CLEAR Page Guard" refers to the page following the Last Page but is always
362     in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
363     Page Guard but is always in the SET state.
364    
365     Rough process
366    
367     The update routines must determine which pages have to blitted to the
368     screen. This job consists in finding the first_page that was touched.
369     i.e. find the next page that is SET. Then, finding how many pages were
370     touched starting from first_page. i.e. find the next page that is CLEAR.
371    
372     Two cases
373    
374     - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
375     but it is beyond the valid pageCount value. Therefore, we exit from the
376     update routine.
377    
378     - Last Page is SET: first_page equals (pageCount - 1) and
379     find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
380     page to the screen. On the next iteration, page equals pageCount and
381     find_next_page_set() will reach the SET Page Guard. We still safely exit
382     from the update routine because the SET Page Guard position is greater
383     than pageCount.
384     */
385    
386 gbeauche 1.1 static inline void update_display_window_vosf(void)
387     {
388     int page = 0;
389     for (;;) {
390 gbeauche 1.11 const int first_page = find_next_page_set(page);
391     if (first_page >= mainBuffer.pageCount)
392 gbeauche 1.1 break;
393 gbeauche 1.11
394     page = find_next_page_clear(first_page);
395     PFLAG_CLEAR_RANGE(first_page, page);
396 cebix 1.7
397 gbeauche 1.1 // Make the dirty pages read-only again
398     const int32 offset = first_page << mainBuffer.pageBits;
399     const uint32 length = (page - first_page) << mainBuffer.pageBits;
400     mprotect((caddr_t)(mainBuffer.memStart + offset), length, PROT_READ);
401    
402     // There is at least one line to update
403     const int y1 = mainBuffer.pageInfo[first_page].top;
404     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
405     const int height = y2 - y1 + 1;
406    
407     const int bytes_per_row = VideoMonitor.bytes_per_row;
408     const int bytes_per_pixel = VideoMonitor.bytes_per_row / VideoMonitor.x;
409     int i, j;
410    
411     // Check for first column from left and first column
412     // from right that have changed
413 cebix 1.6 int x1, x2, width;
414     if (depth == 1) {
415    
416     x1 = VideoMonitor.x - 1;
417     for (j = y1; j <= y2; j++) {
418     uint8 * const p1 = &the_buffer[j * bytes_per_row];
419     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
420     for (i = 0; i < (x1>>3); i++) {
421     if (p1[i] != p2[i]) {
422     x1 = i << 3;
423     break;
424     }
425 gbeauche 1.1 }
426     }
427 cebix 1.6
428     x2 = x1;
429     for (j = y2; j >= y1; j--) {
430     uint8 * const p1 = &the_buffer[j * bytes_per_row];
431     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
432     for (i = (VideoMonitor.x>>3) - 1; i > (x2>>3); i--) {
433     if (p1[i] != p2[i]) {
434 cebix 1.7 x2 = (i << 3) + 7;
435 cebix 1.6 break;
436     }
437     }
438     }
439     width = x2 - x1 + 1;
440    
441     // Update the_host_buffer and copy of the_buffer
442     i = y1 * bytes_per_row + (x1 >> 3);
443     for (j = y1; j <= y2; j++) {
444     do_update_framebuffer(the_host_buffer + i, the_buffer + i, width >> 3);
445     memcpy(the_buffer_copy + i, the_buffer + i, width >> 3);
446     i += bytes_per_row;
447     }
448    
449     } else {
450    
451     x1 = VideoMonitor.x * bytes_per_pixel - 1;
452     for (j = y1; j <= y2; j++) {
453     uint8 * const p1 = &the_buffer[j * bytes_per_row];
454     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
455     for (i = 0; i < x1; i++) {
456     if (p1[i] != p2[i]) {
457     x1 = i;
458     break;
459     }
460     }
461     }
462     x1 /= bytes_per_pixel;
463 gbeauche 1.1
464 cebix 1.6 x2 = x1 * bytes_per_pixel;
465     for (j = y2; j >= y1; j--) {
466     uint8 * const p1 = &the_buffer[j * bytes_per_row];
467     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
468     for (i = VideoMonitor.x * bytes_per_pixel - 1; i > x2; i--) {
469     if (p1[i] != p2[i]) {
470     x2 = i;
471     break;
472     }
473 gbeauche 1.1 }
474     }
475 cebix 1.6 x2 /= bytes_per_pixel;
476     width = x2 - x1 + 1;
477    
478     // Update the_host_buffer and copy of the_buffer
479     i = y1 * bytes_per_row + x1 * bytes_per_pixel;
480     for (j = y1; j <= y2; j++) {
481     do_update_framebuffer(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
482     memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
483     i += bytes_per_row;
484     }
485 gbeauche 1.1 }
486    
487     if (have_shm)
488     XShmPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, width, height, 0);
489     else
490     XPutImage(x_display, the_win, the_gc, img, x1, y1, x1, y1, width, height);
491     }
492     }
493    
494    
495     /*
496     * Update display for DGA mode and VOSF
497     * (only in Direct Addressing mode)
498     */
499    
500     #if REAL_ADDRESSING || DIRECT_ADDRESSING
501     static inline void update_display_dga_vosf(void)
502     {
503     int page = 0;
504     for (;;) {
505 gbeauche 1.11 const int first_page = find_next_page_set(page);
506     if (first_page >= mainBuffer.pageCount)
507 gbeauche 1.1 break;
508 gbeauche 1.11
509     page = find_next_page_clear(first_page);
510     PFLAG_CLEAR_RANGE(first_page, page);
511    
512 gbeauche 1.1 // Make the dirty pages read-only again
513     const int32 offset = first_page << mainBuffer.pageBits;
514     const uint32 length = (page - first_page) << mainBuffer.pageBits;
515     mprotect((caddr_t)(mainBuffer.memStart + offset), length, PROT_READ);
516    
517     // I am sure that y2 >= y1 and depth != 1
518     const int y1 = mainBuffer.pageInfo[first_page].top;
519     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
520    
521     const int bytes_per_row = VideoMonitor.bytes_per_row;
522     const int bytes_per_pixel = VideoMonitor.bytes_per_row / VideoMonitor.x;
523     int i, j;
524    
525     // Check for first column from left and first column
526     // from right that have changed
527     int x1 = VideoMonitor.x * bytes_per_pixel - 1;
528     for (j = y1; j <= y2; j++) {
529     uint8 * const p1 = &the_buffer[j * bytes_per_row];
530     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
531     for (i = 0; i < x1; i++) {
532     if (p1[i] != p2[i]) {
533     x1 = i;
534     break;
535     }
536     }
537     }
538     x1 /= bytes_per_pixel;
539    
540     int x2 = x1 * bytes_per_pixel;
541     for (j = y2; j >= y1; j--) {
542     uint8 * const p1 = &the_buffer[j * bytes_per_row];
543     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
544     for (i = VideoMonitor.x * bytes_per_pixel - 1; i > x2; i--) {
545     if (p1[i] != p2[i]) {
546     x2 = i;
547     break;
548     }
549     }
550     }
551     x2 /= bytes_per_pixel;
552    
553     // Update the_host_buffer and copy of the_buffer
554     // There should be at least one pixel to copy
555     const int width = x2 - x1 + 1;
556     i = y1 * bytes_per_row + x1 * bytes_per_pixel;
557     for (j = y1; j <= y2; j++) {
558     do_update_framebuffer(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
559     memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
560     i += bytes_per_row;
561     }
562     }
563     }
564     #endif
565    
566     #endif /* ENABLE_VOSF */
567    
568     #endif /* VIDEO_VOSF_H */