ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.23
Committed: 2001-07-01T19:57:55Z (23 years ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.22: +10 -6 lines
Log Message:
added support for 8-bit windowed modes on 16 and 32-bit screens (for the
games, man, the games! :-)

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     #ifdef ENABLE_MON
269     char *arg[4] = {"mon", "-m", "-r", NULL};
270     mon(3, arg);
271     QuitEmulator();
272     #endif
273 gbeauche 1.16 return false;
274 gbeauche 1.1 }
275    
276 gbeauche 1.20
277 gbeauche 1.1 /*
278     * Update display for Windowed mode and VOSF
279     */
280    
281 gbeauche 1.13 // From video_blit.cpp
282     extern void (*Screen_blit)(uint8 * dest, const uint8 * source, uint32 length);
283 cebix 1.22 extern bool Screen_blitter_init(XVisualInfo * visual_info, bool native_byte_order, video_depth mac_depth);
284 cebix 1.23 extern uint32 ExpandMap[256];
285 gbeauche 1.13
286 gbeauche 1.12 /* How can we deal with array overrun conditions ?
287    
288     The state of the framebuffer pages that have been touched are maintained
289     in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
290    
291     Terminology
292    
293     "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
294     "CLEAR Page Guard" refers to the page following the Last Page but is always
295     in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
296     Page Guard but is always in the SET state.
297    
298     Rough process
299    
300 gbeauche 1.13 The update routines must determine which pages have to be blitted to the
301 gbeauche 1.12 screen. This job consists in finding the first_page that was touched.
302     i.e. find the next page that is SET. Then, finding how many pages were
303     touched starting from first_page. i.e. find the next page that is CLEAR.
304    
305 gbeauche 1.13 There are two cases to check:
306 gbeauche 1.12
307     - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
308     but it is beyond the valid pageCount value. Therefore, we exit from the
309     update routine.
310    
311     - Last Page is SET: first_page equals (pageCount - 1) and
312     find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
313     page to the screen. On the next iteration, page equals pageCount and
314     find_next_page_set() will reach the SET Page Guard. We still safely exit
315     from the update routine because the SET Page Guard position is greater
316     than pageCount.
317     */
318    
319 cebix 1.21 static inline void update_display_window_vosf(driver_window *drv)
320 gbeauche 1.1 {
321     int page = 0;
322     for (;;) {
323 gbeauche 1.11 const int first_page = find_next_page_set(page);
324     if (first_page >= mainBuffer.pageCount)
325 gbeauche 1.1 break;
326 gbeauche 1.11
327     page = find_next_page_clear(first_page);
328     PFLAG_CLEAR_RANGE(first_page, page);
329 cebix 1.7
330 gbeauche 1.1 // Make the dirty pages read-only again
331     const int32 offset = first_page << mainBuffer.pageBits;
332     const uint32 length = (page - first_page) << mainBuffer.pageBits;
333 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
334 gbeauche 1.1
335     // There is at least one line to update
336     const int y1 = mainBuffer.pageInfo[first_page].top;
337     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
338     const int height = y2 - y1 + 1;
339    
340 cebix 1.19 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
341 cebix 1.6
342     // Update the_host_buffer and copy of the_buffer
343 cebix 1.23 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
344     int i = y1 * bytes_per_row, j;
345 cebix 1.6 for (j = y1; j <= y2; j++) {
346 cebix 1.18 Screen_blit(the_host_buffer + i, the_buffer + i, VideoMonitor.mode.x >> 3);
347 cebix 1.6 i += bytes_per_row;
348     }
349    
350     } else {
351    
352     // Update the_host_buffer and copy of the_buffer
353 cebix 1.23 const int src_bytes_per_row = VideoMonitor.mode.bytes_per_row;
354     const int dst_bytes_per_row = drv->img->bytes_per_line;
355     const int bytes_per_pixel = src_bytes_per_row / VideoMonitor.mode.x;
356     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.23 Screen_blit(the_host_buffer + i2, the_buffer + i1, bytes_per_pixel * VideoMonitor.mode.x);
359     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     XShmPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height, 0);
366 gbeauche 1.1 else
367 cebix 1.21 XPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.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     int page = 0;
382     for (;;) {
383 gbeauche 1.11 const int first_page = find_next_page_set(page);
384     if (first_page >= mainBuffer.pageCount)
385 gbeauche 1.1 break;
386 gbeauche 1.11
387     page = find_next_page_clear(first_page);
388     PFLAG_CLEAR_RANGE(first_page, page);
389    
390 gbeauche 1.1 // Make the dirty pages read-only again
391     const int32 offset = first_page << mainBuffer.pageBits;
392     const uint32 length = (page - first_page) << mainBuffer.pageBits;
393 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
394 gbeauche 1.1
395     // I am sure that y2 >= y1 and depth != 1
396     const int y1 = mainBuffer.pageInfo[first_page].top;
397     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
398    
399 cebix 1.18 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
400     const int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
401 gbeauche 1.1 int i, j;
402    
403     // Check for first column from left and first column
404     // from right that have changed
405 cebix 1.18 int x1 = VideoMonitor.mode.x * bytes_per_pixel - 1;
406 gbeauche 1.1 for (j = y1; j <= y2; j++) {
407     uint8 * const p1 = &the_buffer[j * bytes_per_row];
408     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
409     for (i = 0; i < x1; i++) {
410     if (p1[i] != p2[i]) {
411     x1 = i;
412     break;
413     }
414     }
415     }
416     x1 /= bytes_per_pixel;
417    
418     int x2 = x1 * bytes_per_pixel;
419     for (j = y2; j >= y1; j--) {
420     uint8 * const p1 = &the_buffer[j * bytes_per_row];
421     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
422 cebix 1.18 for (i = VideoMonitor.mode.x * bytes_per_pixel - 1; i > x2; i--) {
423 gbeauche 1.1 if (p1[i] != p2[i]) {
424     x2 = i;
425     break;
426     }
427     }
428     }
429     x2 /= bytes_per_pixel;
430    
431     // Update the_host_buffer and copy of the_buffer
432     // There should be at least one pixel to copy
433     const int width = x2 - x1 + 1;
434     i = y1 * bytes_per_row + x1 * bytes_per_pixel;
435     for (j = y1; j <= y2; j++) {
436 gbeauche 1.13 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
437 gbeauche 1.1 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
438     i += bytes_per_row;
439     }
440     }
441 gbeauche 1.13 mainBuffer.dirty = false;
442 gbeauche 1.1 }
443     #endif
444    
445     #endif /* ENABLE_VOSF */
446    
447     #endif /* VIDEO_VOSF_H */