ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.32
Committed: 2002-05-16T15:48:06Z (22 years, 1 month ago) by gbeauche
Content type: text/plain
Branch: MAIN
CVS Tags: nigel-build-12, nigel-build-13
Changes since 1.31: +0 -19 lines
Log Message:
- video_vosh.h (Screen_fault_handler): Move unrecoverable fault case to...
- main_unix.cpp (sigsegv_dump_state): ... Here.
- sigsegv.h (sigsegv_fault_handler_t): Rename from sigsegv_handler_t.
- sigsegv.h (sigsegv_state_dumper_t): New.

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