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

# User Rev Content
1 gbeauche 1.1 /*
2     * video_vosf.h - Video/graphics emulation, video on SEGV signals support
3     *
4 cebix 1.30 * Basilisk II (C) 1997-2002 Christian Bauer
5 gbeauche 1.1 *
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 gbeauche 1.34 // Note: this file must be #include'd only in video_x.cpp
25 gbeauche 1.1 #ifdef ENABLE_VOSF
26    
27 cebix 1.19 #include <fcntl.h>
28     #include <sys/mman.h>
29     #include "sigsegv.h"
30     #include "vm_alloc.h"
31    
32 gbeauche 1.33 // 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 cebix 1.19 // Variables for Video on SEGV support
76 gbeauche 1.20 static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
77 cebix 1.19
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 gbeauche 1.27 uintptr pageSize; // Size of a page
87 cebix 1.19 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 gbeauche 1.20 // 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 cebix 1.19
199     /*
200 gbeauche 1.27 * Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
201 cebix 1.19 */
202    
203 gbeauche 1.33 static bool video_vosf_init(X11_MONITOR_INIT)
204 cebix 1.19 {
205 gbeauche 1.33 VIDEO_MODE_INIT;
206 cebix 1.31
207 gbeauche 1.27 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 gbeauche 1.29 mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
224     if (mainBuffer.dirtyPages == NULL)
225 gbeauche 1.27 return false;
226 cebix 1.19
227 gbeauche 1.27 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 gbeauche 1.29 mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
233     if (mainBuffer.pageInfo == NULL)
234 gbeauche 1.27 return false;
235    
236     uint32 a = 0;
237 cebix 1.28 for (unsigned i = 0; i < mainBuffer.pageCount; i++) {
238 gbeauche 1.33 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 gbeauche 1.27
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 cebix 1.19
263    
264 gbeauche 1.27 /*
265     * Deinitialize VOSF system
266     */
267 cebix 1.19
268 gbeauche 1.27 static void video_vosf_exit(void)
269     {
270 gbeauche 1.29 if (mainBuffer.pageInfo) {
271     free(mainBuffer.pageInfo);
272     mainBuffer.pageInfo = NULL;
273 gbeauche 1.27 }
274 gbeauche 1.29 if (mainBuffer.dirtyPages) {
275     free(mainBuffer.dirtyPages);
276     mainBuffer.dirtyPages = NULL;
277 cebix 1.19 }
278     }
279    
280    
281 gbeauche 1.1 /*
282 gbeauche 1.20 * Screen fault handler
283 gbeauche 1.1 */
284    
285 gbeauche 1.33 bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
286 gbeauche 1.1 {
287 gbeauche 1.16 const uintptr addr = (uintptr)fault_address;
288    
289 gbeauche 1.11 /* Someone attempted to write to the frame buffer. Make it writeable
290 gbeauche 1.20 * now so that the data could actually be written to. It will be made
291 gbeauche 1.11 * read-only back in one of the screen update_*() functions.
292     */
293 gbeauche 1.27 if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
294     const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
295 gbeauche 1.11 LOCK_VOSF;
296     PFLAG_SET(page);
297 gbeauche 1.27 vm_protect((char *)(addr & -mainBuffer.pageSize), mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
298 gbeauche 1.13 mainBuffer.dirty = true;
299 gbeauche 1.11 UNLOCK_VOSF;
300 gbeauche 1.16 return true;
301 gbeauche 1.1 }
302    
303 gbeauche 1.11 /* Otherwise, we don't know how to handle the fault, let it crash */
304 gbeauche 1.16 return false;
305 gbeauche 1.1 }
306    
307 gbeauche 1.20
308 gbeauche 1.1 /*
309     * Update display for Windowed mode and VOSF
310     */
311    
312 gbeauche 1.13 // From video_blit.cpp
313     extern void (*Screen_blit)(uint8 * dest, const uint8 * source, uint32 length);
314 gbeauche 1.33 extern bool Screen_blitter_init(XVisualInfo * visual_info, bool native_byte_order, int mac_depth);
315 cebix 1.23 extern uint32 ExpandMap[256];
316 gbeauche 1.13
317 gbeauche 1.12 /* 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 gbeauche 1.13 The update routines must determine which pages have to be blitted to the
332 gbeauche 1.12 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 gbeauche 1.13 There are two cases to check:
337 gbeauche 1.12
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 gbeauche 1.33 static inline void update_display_window_vosf(VIDEO_DRV_INIT)
351 gbeauche 1.1 {
352 gbeauche 1.33 VIDEO_MODE_INIT;
353 cebix 1.31
354 gbeauche 1.1 int page = 0;
355     for (;;) {
356 cebix 1.28 const unsigned first_page = find_next_page_set(page);
357 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
358 gbeauche 1.1 break;
359 gbeauche 1.11
360     page = find_next_page_clear(first_page);
361     PFLAG_CLEAR_RANGE(first_page, page);
362 cebix 1.7
363 gbeauche 1.1 // 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 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
367 gbeauche 1.1
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 gbeauche 1.33 if (VIDEO_MODE_DEPTH < VIDEO_DEPTH_8BIT) {
374 cebix 1.6
375     // Update the_host_buffer and copy of the_buffer
376 gbeauche 1.33 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 cebix 1.24 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
380 cebix 1.6 for (j = y1; j <= y2; j++) {
381 gbeauche 1.33 Screen_blit(the_host_buffer + i2, the_buffer + i1, VIDEO_MODE_X / pixels_per_byte);
382 cebix 1.24 i1 += src_bytes_per_row;
383     i2 += dst_bytes_per_row;
384 cebix 1.6 }
385    
386     } else {
387    
388     // Update the_host_buffer and copy of the_buffer
389 gbeauche 1.33 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 cebix 1.23 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
393 cebix 1.6 for (j = y1; j <= y2; j++) {
394 gbeauche 1.33 Screen_blit(the_host_buffer + i2, the_buffer + i1, bytes_per_pixel * VIDEO_MODE_X);
395 cebix 1.23 i1 += src_bytes_per_row;
396     i2 += dst_bytes_per_row;
397 cebix 1.6 }
398 gbeauche 1.1 }
399 cebix 1.15
400 gbeauche 1.33 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 gbeauche 1.1 else
403 gbeauche 1.33 XPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height);
404 gbeauche 1.1 }
405 gbeauche 1.13 mainBuffer.dirty = false;
406 gbeauche 1.1 }
407    
408    
409     /*
410     * Update display for DGA mode and VOSF
411 gbeauche 1.20 * (only in Real or Direct Addressing mode)
412 gbeauche 1.1 */
413    
414     #if REAL_ADDRESSING || DIRECT_ADDRESSING
415     static inline void update_display_dga_vosf(void)
416     {
417 gbeauche 1.33 VIDEO_MODE_INIT;
418 cebix 1.31
419 gbeauche 1.1 int page = 0;
420     for (;;) {
421 cebix 1.28 const unsigned first_page = find_next_page_set(page);
422 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
423 gbeauche 1.1 break;
424 gbeauche 1.11
425     page = find_next_page_clear(first_page);
426     PFLAG_CLEAR_RANGE(first_page, page);
427    
428 gbeauche 1.1 // 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 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
432 gbeauche 1.1
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 gbeauche 1.33 const int bytes_per_row = VIDEO_MODE_ROW_BYTES;
438     const int bytes_per_pixel = VIDEO_MODE_ROW_BYTES / VIDEO_MODE_X;
439 gbeauche 1.1 int i, j;
440    
441     // Check for first column from left and first column
442     // from right that have changed
443 gbeauche 1.33 int x1 = VIDEO_MODE_X * bytes_per_pixel - 1;
444 gbeauche 1.1 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 gbeauche 1.33 for (i = VIDEO_MODE_X * bytes_per_pixel - 1; i > x2; i--) {
461 gbeauche 1.1 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 gbeauche 1.13 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
475 gbeauche 1.1 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
476     i += bytes_per_row;
477     }
478     }
479 gbeauche 1.13 mainBuffer.dirty = false;
480 gbeauche 1.1 }
481     #endif
482    
483     #endif /* ENABLE_VOSF */
484    
485     #endif /* VIDEO_VOSF_H */