ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.25
Committed: 2001-07-03T15:59:47Z (23 years, 2 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.24: +1 -0 lines
Log Message:
- added support for platform-independant mutexes, currently only properly
  implemented under Unix
- adb.cpp uses mutexes for thread-safe mouse handling
- video_x.cpp: pressing Ctrl-F5 in windowed mode switches to a "grabbed"
  relative mouse mode, useful for some games
- video_x.cpp: fixed some bugs relating to the hotkeys (key releases are no
  longer treated as hotkeys)

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