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

# Content
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 static uintptr align_on_page_boundary(uintptr size)
33 {
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 #define FB_DEPTH 0
94 #define FB_FUNC_NAME do_fbcopy_raw
95 #include "video_blit.h"
96 #endif
97
98
99 // RGB 555
100
101 #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 #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 #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 // opposite byte order
134
135 #define FB_BLIT_1(dst, src) \
136 (dst = ((((src) >> 7) & 0xff) | (((src) << 9) & 0xc000) | (((src) << 8) & 0x1f00)))
137
138 #define FB_BLIT_2(dst, src) \
139 (dst = ((((src) >> 7) & 0x00ff00ff) | (((src) << 9) & 0xc000c000) | (((src) << 8) & 0x1f001f00)))
140
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 #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 #define FB_FUNC_NAME do_fbcopy_16_nbo
157 #include "video_blit.h"
158
159 // 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
173 // RGB 888
174
175 #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 #define FB_BLIT_1(dst, src) \
182 (dst = (src))
183
184 #define FB_BLIT_2(dst, src) \
185 (dst = (((src) >> 24) & 0xff) | (((src) >> 8) & 0xff00) | (((src) & 0xff00) << 8) | (((src) & 0xff) << 24))
186
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 // NBO : native byte order (X server vs. client)
210 // OBO : opposite byte order (X server vs. client)
211 static fbcopy_func fbcopy_funcs[ID_DEPTH_COUNT][2][2] = {
212 #ifdef WORDS_BIGENDIAN
213 /* 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 /* 15 bpp */ { WD(fbcopy_15_obo) , WD(fbcopy_raw) }, // OK (OBO)
217 /* 16 bpp */ { WD(fbcopy_16_obo) , WD(fbcopy_16_nbo) }, // OK (OBO)
218 /* 24 bpp */ { WD(fbcopy_24_obo) , WD(fbcopy_raw) } // OK (OBO)
219 #else
220 /* 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 /* 24 bpp */ { WD(fbcopy_raw) , WD(fbcopy_24_nbo) } // OK (NBO)
226 #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 const uintptr INVALID_PC = (uintptr)-1;
244
245 static inline void do_handle_screen_fault(uintptr addr, uintptr pc = INVALID_PC)
246 {
247 /* 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 }
260
261 /* 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 }
269
270 #if defined(HAVE_SIGINFO_T)
271
272 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 do_handle_screen_fault((uintptr)sip->si_addr);
276 }
277
278 #elif defined(HAVE_SIGCONTEXT_SUBTERFUGE)
279
280 # 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 do_handle_screen_fault((uintptr)scs.cr2, (uintptr)scs.eip);
285 }
286
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 # 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 vosf_sa.sa_flags = SA_SIGINFO;
330 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 #if !EMULATED_68K && defined(__NetBSD__)
339 sigaddset(&vosf_sa.sa_mask, SIGALRM);
340 vosf_sa.sa_flags = SA_ONSTACK;
341 #else
342 vosf_sa.sa_flags = 0;
343 #endif
344 return (sigaction(SIGSEGV, &vosf_sa, NULL) == 0);
345 }
346 #endif
347
348
349 /*
350 * Update display for Windowed mode and VOSF
351 */
352
353 /* 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 static inline void update_display_window_vosf(void)
387 {
388 int page = 0;
389 for (;;) {
390 const int first_page = find_next_page_set(page);
391 if (first_page >= mainBuffer.pageCount)
392 break;
393
394 page = find_next_page_clear(first_page);
395 PFLAG_CLEAR_RANGE(first_page, page);
396
397 // 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 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 }
426 }
427
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 x2 = (i << 3) + 7;
435 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
464 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 }
474 }
475 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 }
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 const int first_page = find_next_page_set(page);
506 if (first_page >= mainBuffer.pageCount)
507 break;
508
509 page = find_next_page_clear(first_page);
510 PFLAG_CLEAR_RANGE(first_page, page);
511
512 // 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 */