ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.31
Committed: 2002-04-25T11:00:31Z (22 years, 6 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.30: +27 -21 lines
Log Message:
- added infrastructure for multi-monitor support; only video_x.cpp is
  converted for the new scheme; not actually tested with a mult-monitor
  setup yet but at least single-monitor display doesn't seem to be broken
  (UAE banked addressing would definitely require some extensions to handle
  multiple frame buffers)
- struct video_mode has an extra field that is free for use by platform-
  specific code

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