ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.34
Committed: 2003-09-29T07:02:58Z (21 years, 1 month ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.33: +1 -12 lines
Log Message:
New SIGSEGV API so that skip-instruction requests are more explicit. Yes,
that's api change, but that's cooler now for SheepShaver. ;-)

File Contents

# Content
1 /*
2 * video_vosf.h - Video/graphics emulation, video on SEGV signals support
3 *
4 * Basilisk II (C) 1997-2002 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 must be #include'd only in video_x.cpp
25 #ifdef ENABLE_VOSF
26
27 #include <fcntl.h>
28 #include <sys/mman.h>
29 #include "sigsegv.h"
30 #include "vm_alloc.h"
31
32 // Glue for SheepShaver and BasiliskII
33 #if POWERPC_ROM
34 #define X11_MONITOR_INIT /* nothing */
35 #define VIDEO_DRV_INIT /* nothing */
36 #define VIDEO_DRV_WINDOW the_win
37 #define VIDEO_DRV_GC the_gc
38 #define VIDEO_DRV_IMAGE img
39 #define VIDEO_DRV_HAVE_SHM have_shm
40 #define VIDEO_MODE_INIT VideoInfo const & mode = VModes[cur_mode]
41 #define VIDEO_MODE_ROW_BYTES mode.viRowBytes
42 #define VIDEO_MODE_X mode.viXsize
43 #define VIDEO_MODE_Y mode.viYsize
44 #define VIDEO_MODE_DEPTH mode.viAppleMode
45 enum {
46 VIDEO_DEPTH_1BIT = APPLE_1_BIT,
47 VIDEO_DEPTH_2BIT = APPLE_2_BIT,
48 VIDEO_DEPTH_4BIT = APPLE_4_BIT,
49 VIDEO_DEPTH_8BIT = APPLE_8_BIT,
50 VIDEO_DEPTH_16BIT = APPLE_16_BIT,
51 VIDEO_DEPTH_32BIT = APPLE_32_BIT
52 };
53 #else
54 #define X11_MONITOR_INIT X11_monitor_desc &monitor
55 #define VIDEO_DRV_INIT driver_window *drv
56 #define VIDEO_DRV_WINDOW drv->w
57 #define VIDEO_DRV_GC drv->gc
58 #define VIDEO_DRV_IMAGE drv->img
59 #define VIDEO_DRV_HAVE_SHM drv->have_shm
60 #define VIDEO_MODE_INIT video_mode const & mode = drv->monitor.get_current_mode();
61 #define VIDEO_MODE_ROW_BYTES mode.bytes_per_row
62 #define VIDEO_MODE_X mode.x
63 #define VIDEO_MODE_Y mode.y
64 #define VIDEO_MODE_DEPTH (int)mode.depth
65 enum {
66 VIDEO_DEPTH_1BIT = VDEPTH_1BIT,
67 VIDEO_DEPTH_2BIT = VDEPTH_2BIT,
68 VIDEO_DEPTH_4BIT = VDEPTH_4BIT,
69 VIDEO_DEPTH_8BIT = VDEPTH_8BIT,
70 VIDEO_DEPTH_16BIT = VDEPTH_16BIT,
71 VIDEO_DEPTH_32BIT = VDEPTH_32BIT
72 };
73 #endif
74
75 // Variables for Video on SEGV support
76 static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
77
78 struct ScreenPageInfo {
79 int top, bottom; // Mapping between this virtual page and Mac scanlines
80 };
81
82 struct ScreenInfo {
83 uintptr memStart; // Start address aligned to page boundary
84 uint32 memLength; // Length of the memory addressed by the screen pages
85
86 uintptr pageSize; // Size of a page
87 int pageBits; // Shift count to get the page number
88 uint32 pageCount; // Number of pages allocated to the screen
89
90 bool dirty; // Flag: set if the frame buffer was touched
91 char * dirtyPages; // Table of flags set if page was altered
92 ScreenPageInfo * pageInfo; // Table of mappings page -> Mac scanlines
93 };
94
95 static ScreenInfo mainBuffer;
96
97 #define PFLAG_SET_VALUE 0x00
98 #define PFLAG_CLEAR_VALUE 0x01
99 #define PFLAG_SET_VALUE_4 0x00000000
100 #define PFLAG_CLEAR_VALUE_4 0x01010101
101 #define PFLAG_SET(page) mainBuffer.dirtyPages[page] = PFLAG_SET_VALUE
102 #define PFLAG_CLEAR(page) mainBuffer.dirtyPages[page] = PFLAG_CLEAR_VALUE
103 #define PFLAG_ISSET(page) (mainBuffer.dirtyPages[page] == PFLAG_SET_VALUE)
104 #define PFLAG_ISCLEAR(page) (mainBuffer.dirtyPages[page] != PFLAG_SET_VALUE)
105
106 #ifdef UNALIGNED_PROFITABLE
107 # define PFLAG_ISSET_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_SET_VALUE_4)
108 # define PFLAG_ISCLEAR_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_CLEAR_VALUE_4)
109 #else
110 # define PFLAG_ISSET_4(page) \
111 PFLAG_ISSET(page ) && PFLAG_ISSET(page+1) \
112 && PFLAG_ISSET(page+2) && PFLAG_ISSET(page+3)
113 # define PFLAG_ISCLEAR_4(page) \
114 PFLAG_ISCLEAR(page ) && PFLAG_ISCLEAR(page+1) \
115 && PFLAG_ISCLEAR(page+2) && PFLAG_ISCLEAR(page+3)
116 #endif
117
118 // Set the selected page range [ first_page, last_page [ into the SET state
119 #define PFLAG_SET_RANGE(first_page, last_page) \
120 memset(mainBuffer.dirtyPages + (first_page), PFLAG_SET_VALUE, \
121 (last_page) - (first_page))
122
123 // Set the selected page range [ first_page, last_page [ into the CLEAR state
124 #define PFLAG_CLEAR_RANGE(first_page, last_page) \
125 memset(mainBuffer.dirtyPages + (first_page), PFLAG_CLEAR_VALUE, \
126 (last_page) - (first_page))
127
128 #define PFLAG_SET_ALL do { \
129 PFLAG_SET_RANGE(0, mainBuffer.pageCount); \
130 mainBuffer.dirty = true; \
131 } while (0)
132
133 #define PFLAG_CLEAR_ALL do { \
134 PFLAG_CLEAR_RANGE(0, mainBuffer.pageCount); \
135 mainBuffer.dirty = false; \
136 } while (0)
137
138 // Set the following macro definition to 1 if your system
139 // provides a really fast strchr() implementation
140 //#define HAVE_FAST_STRCHR 0
141
142 static inline int find_next_page_set(int page)
143 {
144 #if HAVE_FAST_STRCHR
145 char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_SET_VALUE);
146 return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
147 #else
148 while (PFLAG_ISCLEAR_4(page))
149 page += 4;
150 while (PFLAG_ISCLEAR(page))
151 page++;
152 return page;
153 #endif
154 }
155
156 static inline int find_next_page_clear(int page)
157 {
158 #if HAVE_FAST_STRCHR
159 char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_CLEAR_VALUE);
160 return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
161 #else
162 while (PFLAG_ISSET_4(page))
163 page += 4;
164 while (PFLAG_ISSET(page))
165 page++;
166 return page;
167 #endif
168 }
169
170 #ifdef HAVE_PTHREADS
171 static pthread_mutex_t vosf_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer (dirtyPages in fact)
172 #define LOCK_VOSF pthread_mutex_lock(&vosf_lock);
173 #define UNLOCK_VOSF pthread_mutex_unlock(&vosf_lock);
174 #else
175 #define LOCK_VOSF
176 #define UNLOCK_VOSF
177 #endif
178
179 static int log_base_2(uint32 x)
180 {
181 uint32 mask = 0x80000000;
182 int l = 31;
183 while (l >= 0 && (x & mask) == 0) {
184 mask >>= 1;
185 l--;
186 }
187 return l;
188 }
189
190 // Extend size to page boundary
191 static uint32 page_extend(uint32 size)
192 {
193 const uint32 page_size = getpagesize();
194 const uint32 page_mask = page_size - 1;
195 return (size + page_mask) & ~page_mask;
196 }
197
198
199 /*
200 * Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
201 */
202
203 static bool video_vosf_init(X11_MONITOR_INIT)
204 {
205 VIDEO_MODE_INIT;
206
207 const uintptr page_size = getpagesize();
208 const uintptr page_mask = page_size - 1;
209
210 // Round up frame buffer base to page boundary
211 mainBuffer.memStart = (((uintptr) the_buffer) + page_mask) & ~page_mask;
212
213 // The frame buffer size shall already be aligned to page boundary (use page_extend)
214 mainBuffer.memLength = the_buffer_size;
215
216 mainBuffer.pageSize = page_size;
217 mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
218 mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
219
220 // The "2" more bytes requested are a safety net to insure the
221 // loops in the update routines will terminate.
222 // See "How can we deal with array overrun conditions ?" hereunder for further details.
223 mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
224 if (mainBuffer.dirtyPages == NULL)
225 return false;
226
227 PFLAG_CLEAR_ALL;
228 PFLAG_CLEAR(mainBuffer.pageCount);
229 PFLAG_SET(mainBuffer.pageCount+1);
230
231 // Allocate and fill in pageInfo with start and end (inclusive) row in number of bytes
232 mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
233 if (mainBuffer.pageInfo == NULL)
234 return false;
235
236 uint32 a = 0;
237 for (unsigned i = 0; i < mainBuffer.pageCount; i++) {
238 unsigned y1 = a / VIDEO_MODE_ROW_BYTES;
239 if (y1 >= VIDEO_MODE_Y)
240 y1 = VIDEO_MODE_Y - 1;
241
242 unsigned y2 = (a + mainBuffer.pageSize) / VIDEO_MODE_ROW_BYTES;
243 if (y2 >= VIDEO_MODE_Y)
244 y2 = VIDEO_MODE_Y - 1;
245
246 mainBuffer.pageInfo[i].top = y1;
247 mainBuffer.pageInfo[i].bottom = y2;
248
249 a += mainBuffer.pageSize;
250 if (a > mainBuffer.memLength)
251 a = mainBuffer.memLength;
252 }
253
254 // We can now write-protect the frame buffer
255 if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
256 return false;
257
258 // The frame buffer is sane, i.e. there is no write to it yet
259 mainBuffer.dirty = false;
260 return true;
261 }
262
263
264 /*
265 * Deinitialize VOSF system
266 */
267
268 static void video_vosf_exit(void)
269 {
270 if (mainBuffer.pageInfo) {
271 free(mainBuffer.pageInfo);
272 mainBuffer.pageInfo = NULL;
273 }
274 if (mainBuffer.dirtyPages) {
275 free(mainBuffer.dirtyPages);
276 mainBuffer.dirtyPages = NULL;
277 }
278 }
279
280
281 /*
282 * Screen fault handler
283 */
284
285 bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
286 {
287 const uintptr addr = (uintptr)fault_address;
288
289 /* Someone attempted to write to the frame buffer. Make it writeable
290 * now so that the data could actually be written to. It will be made
291 * read-only back in one of the screen update_*() functions.
292 */
293 if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
294 const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
295 LOCK_VOSF;
296 PFLAG_SET(page);
297 vm_protect((char *)(addr & -mainBuffer.pageSize), mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
298 mainBuffer.dirty = true;
299 UNLOCK_VOSF;
300 return true;
301 }
302
303 /* Otherwise, we don't know how to handle the fault, let it crash */
304 return false;
305 }
306
307
308 /*
309 * Update display for Windowed mode and VOSF
310 */
311
312 // From video_blit.cpp
313 extern void (*Screen_blit)(uint8 * dest, const uint8 * source, uint32 length);
314 extern bool Screen_blitter_init(XVisualInfo * visual_info, bool native_byte_order, int mac_depth);
315 extern uint32 ExpandMap[256];
316
317 /* How can we deal with array overrun conditions ?
318
319 The state of the framebuffer pages that have been touched are maintained
320 in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
321
322 Terminology
323
324 "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
325 "CLEAR Page Guard" refers to the page following the Last Page but is always
326 in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
327 Page Guard but is always in the SET state.
328
329 Rough process
330
331 The update routines must determine which pages have to be blitted to the
332 screen. This job consists in finding the first_page that was touched.
333 i.e. find the next page that is SET. Then, finding how many pages were
334 touched starting from first_page. i.e. find the next page that is CLEAR.
335
336 There are two cases to check:
337
338 - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
339 but it is beyond the valid pageCount value. Therefore, we exit from the
340 update routine.
341
342 - Last Page is SET: first_page equals (pageCount - 1) and
343 find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
344 page to the screen. On the next iteration, page equals pageCount and
345 find_next_page_set() will reach the SET Page Guard. We still safely exit
346 from the update routine because the SET Page Guard position is greater
347 than pageCount.
348 */
349
350 static inline void update_display_window_vosf(VIDEO_DRV_INIT)
351 {
352 VIDEO_MODE_INIT;
353
354 int page = 0;
355 for (;;) {
356 const unsigned first_page = find_next_page_set(page);
357 if (first_page >= mainBuffer.pageCount)
358 break;
359
360 page = find_next_page_clear(first_page);
361 PFLAG_CLEAR_RANGE(first_page, page);
362
363 // Make the dirty pages read-only again
364 const int32 offset = first_page << mainBuffer.pageBits;
365 const uint32 length = (page - first_page) << mainBuffer.pageBits;
366 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
367
368 // There is at least one line to update
369 const int y1 = mainBuffer.pageInfo[first_page].top;
370 const int y2 = mainBuffer.pageInfo[page - 1].bottom;
371 const int height = y2 - y1 + 1;
372
373 if (VIDEO_MODE_DEPTH < VIDEO_DEPTH_8BIT) {
374
375 // Update the_host_buffer and copy of the_buffer
376 const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
377 const int dst_bytes_per_row = VIDEO_DRV_IMAGE->bytes_per_line;
378 const int pixels_per_byte = VIDEO_MODE_X / src_bytes_per_row;
379 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
380 for (j = y1; j <= y2; j++) {
381 Screen_blit(the_host_buffer + i2, the_buffer + i1, VIDEO_MODE_X / pixels_per_byte);
382 i1 += src_bytes_per_row;
383 i2 += dst_bytes_per_row;
384 }
385
386 } else {
387
388 // Update the_host_buffer and copy of the_buffer
389 const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
390 const int dst_bytes_per_row = VIDEO_DRV_IMAGE->bytes_per_line;
391 const int bytes_per_pixel = src_bytes_per_row / VIDEO_MODE_X;
392 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
393 for (j = y1; j <= y2; j++) {
394 Screen_blit(the_host_buffer + i2, the_buffer + i1, bytes_per_pixel * VIDEO_MODE_X);
395 i1 += src_bytes_per_row;
396 i2 += dst_bytes_per_row;
397 }
398 }
399
400 if (VIDEO_DRV_HAVE_SHM)
401 XShmPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height, 0);
402 else
403 XPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height);
404 }
405 mainBuffer.dirty = false;
406 }
407
408
409 /*
410 * Update display for DGA mode and VOSF
411 * (only in Real or Direct Addressing mode)
412 */
413
414 #if REAL_ADDRESSING || DIRECT_ADDRESSING
415 static inline void update_display_dga_vosf(void)
416 {
417 VIDEO_MODE_INIT;
418
419 int page = 0;
420 for (;;) {
421 const unsigned first_page = find_next_page_set(page);
422 if (first_page >= mainBuffer.pageCount)
423 break;
424
425 page = find_next_page_clear(first_page);
426 PFLAG_CLEAR_RANGE(first_page, page);
427
428 // Make the dirty pages read-only again
429 const int32 offset = first_page << mainBuffer.pageBits;
430 const uint32 length = (page - first_page) << mainBuffer.pageBits;
431 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
432
433 // I am sure that y2 >= y1 and depth != 1
434 const int y1 = mainBuffer.pageInfo[first_page].top;
435 const int y2 = mainBuffer.pageInfo[page - 1].bottom;
436
437 const int bytes_per_row = VIDEO_MODE_ROW_BYTES;
438 const int bytes_per_pixel = VIDEO_MODE_ROW_BYTES / VIDEO_MODE_X;
439 int i, j;
440
441 // Check for first column from left and first column
442 // from right that have changed
443 int x1 = VIDEO_MODE_X * bytes_per_pixel - 1;
444 for (j = y1; j <= y2; j++) {
445 uint8 * const p1 = &the_buffer[j * bytes_per_row];
446 uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
447 for (i = 0; i < x1; i++) {
448 if (p1[i] != p2[i]) {
449 x1 = i;
450 break;
451 }
452 }
453 }
454 x1 /= bytes_per_pixel;
455
456 int x2 = x1 * bytes_per_pixel;
457 for (j = y2; j >= y1; j--) {
458 uint8 * const p1 = &the_buffer[j * bytes_per_row];
459 uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
460 for (i = VIDEO_MODE_X * bytes_per_pixel - 1; i > x2; i--) {
461 if (p1[i] != p2[i]) {
462 x2 = i;
463 break;
464 }
465 }
466 }
467 x2 /= bytes_per_pixel;
468
469 // Update the_host_buffer and copy of the_buffer
470 // There should be at least one pixel to copy
471 const int width = x2 - x1 + 1;
472 i = y1 * bytes_per_row + x1 * bytes_per_pixel;
473 for (j = y1; j <= y2; j++) {
474 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
475 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
476 i += bytes_per_row;
477 }
478 }
479 mainBuffer.dirty = false;
480 }
481 #endif
482
483 #endif /* ENABLE_VOSF */
484
485 #endif /* VIDEO_VOSF_H */