ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.29
Committed: 2001-07-14T18:41:01Z (22 years, 11 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.28: +10 -10 lines
Log Message:
- video_x.cpp: the_buffer shall always be mapped through vm_alloc() so
  that it can be vm_protect()'ed at will afterwards
- video_x.cpp: let driver_base free() the_buffer_copy and the_host_buffer,
  if necessary
- video_vosf.h: reverted to use malloc() for mainBuffer.dirtyPages and
  mainBuffer.pageInfo
- vm_alloc.cpp: small cleanups
- fixed typos

File Contents

# User Rev Content
1 gbeauche 1.1 /*
2     * video_vosf.h - Video/graphics emulation, video on SEGV signals support
3     *
4 gbeauche 1.13 * Basilisk II (C) 1997-2001 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     static bool video_vosf_init(void)
167 cebix 1.19 {
168 gbeauche 1.27 const uintptr page_size = getpagesize();
169     const uintptr page_mask = page_size - 1;
170    
171     // Round up frame buffer base to page boundary
172     mainBuffer.memStart = (((uintptr) the_buffer) + page_mask) & ~page_mask;
173    
174     // The frame buffer size shall already be aligned to page boundary (use page_extend)
175     mainBuffer.memLength = the_buffer_size;
176    
177     mainBuffer.pageSize = page_size;
178     mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
179     mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
180    
181     // The "2" more bytes requested are a safety net to insure the
182     // loops in the update routines will terminate.
183     // See "How can we deal with array overrun conditions ?" hereunder for further details.
184 gbeauche 1.29 mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
185     if (mainBuffer.dirtyPages == NULL)
186 gbeauche 1.27 return false;
187 cebix 1.19
188 gbeauche 1.27 PFLAG_CLEAR_ALL;
189     PFLAG_CLEAR(mainBuffer.pageCount);
190     PFLAG_SET(mainBuffer.pageCount+1);
191    
192     // Allocate and fill in pageInfo with start and end (inclusive) row in number of bytes
193 gbeauche 1.29 mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
194     if (mainBuffer.pageInfo == NULL)
195 gbeauche 1.27 return false;
196    
197     uint32 a = 0;
198 cebix 1.28 for (unsigned i = 0; i < mainBuffer.pageCount; i++) {
199     unsigned y1 = a / VideoMonitor.mode.bytes_per_row;
200 gbeauche 1.27 if (y1 >= VideoMonitor.mode.y)
201     y1 = VideoMonitor.mode.y - 1;
202    
203 cebix 1.28 unsigned y2 = (a + mainBuffer.pageSize) / VideoMonitor.mode.bytes_per_row;
204 gbeauche 1.27 if (y2 >= VideoMonitor.mode.y)
205     y2 = VideoMonitor.mode.y - 1;
206    
207     mainBuffer.pageInfo[i].top = y1;
208     mainBuffer.pageInfo[i].bottom = y2;
209    
210     a += mainBuffer.pageSize;
211     if (a > mainBuffer.memLength)
212     a = mainBuffer.memLength;
213     }
214    
215     // We can now write-protect the frame buffer
216     if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
217     return false;
218    
219     // Initialize the handler for SIGSEGV
220     if (!sigsegv_install_handler(screen_fault_handler))
221     return false;
222    
223     // The frame buffer is sane, i.e. there is no write to it yet
224     mainBuffer.dirty = false;
225     return true;
226     }
227 cebix 1.19
228    
229 gbeauche 1.27 /*
230     * Deinitialize VOSF system
231     */
232 cebix 1.19
233 gbeauche 1.27 static void video_vosf_exit(void)
234     {
235 gbeauche 1.29 if (mainBuffer.pageInfo) {
236     free(mainBuffer.pageInfo);
237     mainBuffer.pageInfo = NULL;
238 gbeauche 1.27 }
239 gbeauche 1.29 if (mainBuffer.dirtyPages) {
240     free(mainBuffer.dirtyPages);
241     mainBuffer.dirtyPages = NULL;
242 cebix 1.19 }
243     }
244    
245    
246 gbeauche 1.1 /*
247 gbeauche 1.20 * Screen fault handler
248 gbeauche 1.1 */
249    
250 gbeauche 1.16 static bool screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
251 gbeauche 1.1 {
252 gbeauche 1.27 // D(bug("screen_fault_handler: ADDR=%p from IP=%p\n", fault_address, fault_instruction));
253 gbeauche 1.16 const uintptr addr = (uintptr)fault_address;
254    
255 gbeauche 1.11 /* Someone attempted to write to the frame buffer. Make it writeable
256 gbeauche 1.20 * now so that the data could actually be written to. It will be made
257 gbeauche 1.11 * read-only back in one of the screen update_*() functions.
258     */
259 gbeauche 1.27 if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
260     const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
261 gbeauche 1.11 LOCK_VOSF;
262     PFLAG_SET(page);
263 gbeauche 1.27 vm_protect((char *)(addr & -mainBuffer.pageSize), mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
264 gbeauche 1.13 mainBuffer.dirty = true;
265 gbeauche 1.11 UNLOCK_VOSF;
266 gbeauche 1.16 return true;
267 gbeauche 1.1 }
268    
269 gbeauche 1.11 /* Otherwise, we don't know how to handle the fault, let it crash */
270 gbeauche 1.27 fprintf(stderr, "do_handle_screen_fault: unhandled address %p", fault_address);
271 gbeauche 1.16 if (fault_instruction != SIGSEGV_INVALID_PC)
272 gbeauche 1.27 fprintf(stderr, " [IP=%p]", fault_instruction);
273 gbeauche 1.11 fprintf(stderr, "\n");
274 cebix 1.19 #if EMULATED_68K
275     uaecptr nextpc;
276     extern void m68k_dumpstate(uaecptr *nextpc);
277     m68k_dumpstate(&nextpc);
278     #endif
279 cebix 1.25 VideoQuitFullScreen();
280 cebix 1.19 #ifdef ENABLE_MON
281     char *arg[4] = {"mon", "-m", "-r", NULL};
282     mon(3, arg);
283     QuitEmulator();
284     #endif
285 gbeauche 1.16 return false;
286 gbeauche 1.1 }
287    
288 gbeauche 1.20
289 gbeauche 1.1 /*
290     * Update display for Windowed mode and VOSF
291     */
292    
293 gbeauche 1.13 // From video_blit.cpp
294     extern void (*Screen_blit)(uint8 * dest, const uint8 * source, uint32 length);
295 cebix 1.22 extern bool Screen_blitter_init(XVisualInfo * visual_info, bool native_byte_order, video_depth mac_depth);
296 cebix 1.23 extern uint32 ExpandMap[256];
297 gbeauche 1.13
298 gbeauche 1.12 /* How can we deal with array overrun conditions ?
299    
300     The state of the framebuffer pages that have been touched are maintained
301     in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
302    
303     Terminology
304    
305     "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
306     "CLEAR Page Guard" refers to the page following the Last Page but is always
307     in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
308     Page Guard but is always in the SET state.
309    
310     Rough process
311    
312 gbeauche 1.13 The update routines must determine which pages have to be blitted to the
313 gbeauche 1.12 screen. This job consists in finding the first_page that was touched.
314     i.e. find the next page that is SET. Then, finding how many pages were
315     touched starting from first_page. i.e. find the next page that is CLEAR.
316    
317 gbeauche 1.13 There are two cases to check:
318 gbeauche 1.12
319     - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
320     but it is beyond the valid pageCount value. Therefore, we exit from the
321     update routine.
322    
323     - Last Page is SET: first_page equals (pageCount - 1) and
324     find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
325     page to the screen. On the next iteration, page equals pageCount and
326     find_next_page_set() will reach the SET Page Guard. We still safely exit
327     from the update routine because the SET Page Guard position is greater
328     than pageCount.
329     */
330    
331 cebix 1.21 static inline void update_display_window_vosf(driver_window *drv)
332 gbeauche 1.1 {
333     int page = 0;
334     for (;;) {
335 cebix 1.28 const unsigned first_page = find_next_page_set(page);
336 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
337 gbeauche 1.1 break;
338 gbeauche 1.11
339     page = find_next_page_clear(first_page);
340     PFLAG_CLEAR_RANGE(first_page, page);
341 cebix 1.7
342 gbeauche 1.1 // Make the dirty pages read-only again
343     const int32 offset = first_page << mainBuffer.pageBits;
344     const uint32 length = (page - first_page) << mainBuffer.pageBits;
345 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
346 gbeauche 1.1
347     // There is at least one line to update
348     const int y1 = mainBuffer.pageInfo[first_page].top;
349     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
350     const int height = y2 - y1 + 1;
351    
352 cebix 1.24 if (VideoMonitor.mode.depth < VDEPTH_8BIT) {
353 cebix 1.6
354     // Update the_host_buffer and copy of the_buffer
355 cebix 1.24 const int src_bytes_per_row = VideoMonitor.mode.bytes_per_row;
356     const int dst_bytes_per_row = drv->img->bytes_per_line;
357     const int pixels_per_byte = VideoMonitor.mode.x / src_bytes_per_row;
358     int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
359 cebix 1.6 for (j = y1; j <= y2; j++) {
360 cebix 1.24 Screen_blit(the_host_buffer + i2, the_buffer + i1, VideoMonitor.mode.x / pixels_per_byte);
361     i1 += src_bytes_per_row;
362     i2 += dst_bytes_per_row;
363 cebix 1.6 }
364    
365     } else {
366    
367     // Update the_host_buffer and copy of the_buffer
368 cebix 1.23 const int src_bytes_per_row = VideoMonitor.mode.bytes_per_row;
369     const int dst_bytes_per_row = drv->img->bytes_per_line;
370     const int bytes_per_pixel = src_bytes_per_row / VideoMonitor.mode.x;
371     int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
372 cebix 1.6 for (j = y1; j <= y2; j++) {
373 cebix 1.23 Screen_blit(the_host_buffer + i2, the_buffer + i1, bytes_per_pixel * VideoMonitor.mode.x);
374     i1 += src_bytes_per_row;
375     i2 += dst_bytes_per_row;
376 cebix 1.6 }
377 gbeauche 1.1 }
378 cebix 1.15
379 cebix 1.21 if (drv->have_shm)
380     XShmPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height, 0);
381 gbeauche 1.1 else
382 cebix 1.21 XPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height);
383 gbeauche 1.1 }
384 gbeauche 1.13 mainBuffer.dirty = false;
385 gbeauche 1.1 }
386    
387    
388     /*
389     * Update display for DGA mode and VOSF
390 gbeauche 1.20 * (only in Real or Direct Addressing mode)
391 gbeauche 1.1 */
392    
393     #if REAL_ADDRESSING || DIRECT_ADDRESSING
394     static inline void update_display_dga_vosf(void)
395     {
396     int page = 0;
397     for (;;) {
398 cebix 1.28 const unsigned first_page = find_next_page_set(page);
399 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
400 gbeauche 1.1 break;
401 gbeauche 1.11
402     page = find_next_page_clear(first_page);
403     PFLAG_CLEAR_RANGE(first_page, page);
404    
405 gbeauche 1.1 // Make the dirty pages read-only again
406     const int32 offset = first_page << mainBuffer.pageBits;
407     const uint32 length = (page - first_page) << mainBuffer.pageBits;
408 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
409 gbeauche 1.1
410     // I am sure that y2 >= y1 and depth != 1
411     const int y1 = mainBuffer.pageInfo[first_page].top;
412     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
413    
414 cebix 1.18 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
415     const int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
416 gbeauche 1.1 int i, j;
417    
418     // Check for first column from left and first column
419     // from right that have changed
420 cebix 1.18 int x1 = VideoMonitor.mode.x * bytes_per_pixel - 1;
421 gbeauche 1.1 for (j = y1; j <= y2; j++) {
422     uint8 * const p1 = &the_buffer[j * bytes_per_row];
423     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
424     for (i = 0; i < x1; i++) {
425     if (p1[i] != p2[i]) {
426     x1 = i;
427     break;
428     }
429     }
430     }
431     x1 /= bytes_per_pixel;
432    
433     int x2 = x1 * bytes_per_pixel;
434     for (j = y2; j >= y1; j--) {
435     uint8 * const p1 = &the_buffer[j * bytes_per_row];
436     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
437 cebix 1.18 for (i = VideoMonitor.mode.x * bytes_per_pixel - 1; i > x2; i--) {
438 gbeauche 1.1 if (p1[i] != p2[i]) {
439     x2 = i;
440     break;
441     }
442     }
443     }
444     x2 /= bytes_per_pixel;
445    
446     // Update the_host_buffer and copy of the_buffer
447     // There should be at least one pixel to copy
448     const int width = x2 - x1 + 1;
449     i = y1 * bytes_per_row + x1 * bytes_per_pixel;
450     for (j = y1; j <= y2; j++) {
451 gbeauche 1.13 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
452 gbeauche 1.1 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
453     i += bytes_per_row;
454     }
455     }
456 gbeauche 1.13 mainBuffer.dirty = false;
457 gbeauche 1.1 }
458     #endif
459    
460     #endif /* ENABLE_VOSF */
461    
462     #endif /* VIDEO_VOSF_H */