ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.19
Committed: 2001-06-28T21:20:00Z (23 years, 3 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.18: +212 -1 lines
Log Message:
video_x.cpp supports resolution switching in windowed mode: the available
resolutions are 512x384, 640x480, 800x600, 1024x768 and 1280x1024 (the prefs
editor has to be updated to reflect this). The resolution selected in the
prefs editor is used as the default, but it can be changed in the Monitors
control panel. So far only tested with direct addressing.

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     static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
38     static uint32 the_buffer_size; // Size of allocated the_buffer
39    
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     static int zero_fd = -1;
135    
136     #ifdef HAVE_PTHREADS
137     static pthread_mutex_t vosf_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer (dirtyPages in fact)
138     #define LOCK_VOSF pthread_mutex_lock(&vosf_lock);
139     #define UNLOCK_VOSF pthread_mutex_unlock(&vosf_lock);
140     #else
141     #define LOCK_VOSF
142     #define UNLOCK_VOSF
143     #endif
144    
145     static int log_base_2(uint32 x)
146     {
147     uint32 mask = 0x80000000;
148     int l = 31;
149     while (l >= 0 && (x & mask) == 0) {
150     mask >>= 1;
151     l--;
152     }
153     return l;
154     }
155    
156    
157     /*
158     * Initialize mainBuffer structure
159     */
160    
161     static bool video_init_buffer(void)
162     {
163     if (use_vosf) {
164     const uint32 page_size = getpagesize();
165     const uint32 page_mask = page_size - 1;
166    
167     mainBuffer.memBase = (uintptr) the_buffer;
168     // Align the frame buffer on page boundary
169     mainBuffer.memStart = (uintptr)((((unsigned long) the_buffer) + page_mask) & ~page_mask);
170     mainBuffer.memLength = the_buffer_size;
171     mainBuffer.memEnd = mainBuffer.memStart + mainBuffer.memLength;
172    
173     mainBuffer.pageSize = page_size;
174     mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
175     mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
176    
177     if (mainBuffer.dirtyPages) {
178     free(mainBuffer.dirtyPages);
179     mainBuffer.dirtyPages = NULL;
180     }
181    
182     mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
183    
184     if (mainBuffer.pageInfo) {
185     free(mainBuffer.pageInfo);
186     mainBuffer.pageInfo = NULL;
187     }
188    
189     mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
190    
191     if ((mainBuffer.dirtyPages == 0) || (mainBuffer.pageInfo == 0))
192     return false;
193    
194     mainBuffer.dirty = false;
195    
196     PFLAG_CLEAR_ALL;
197     // Safety net to insure the loops in the update routines will terminate
198     // See a discussion in <video_vosf.h> for further details
199     PFLAG_CLEAR(mainBuffer.pageCount);
200     PFLAG_SET(mainBuffer.pageCount+1);
201    
202     uint32 a = 0;
203     for (int i = 0; i < mainBuffer.pageCount; i++) {
204     int y1 = a / VideoMonitor.mode.bytes_per_row;
205     if (y1 >= VideoMonitor.mode.y)
206     y1 = VideoMonitor.mode.y - 1;
207    
208     int y2 = (a + mainBuffer.pageSize) / VideoMonitor.mode.bytes_per_row;
209     if (y2 >= VideoMonitor.mode.y)
210     y2 = VideoMonitor.mode.y - 1;
211    
212     mainBuffer.pageInfo[i].top = y1;
213     mainBuffer.pageInfo[i].bottom = y2;
214    
215     a += mainBuffer.pageSize;
216     if (a > mainBuffer.memLength)
217     a = mainBuffer.memLength;
218     }
219    
220     // We can now write-protect the frame buffer
221     if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
222     return false;
223     }
224     return true;
225     }
226    
227    
228 gbeauche 1.1 /*
229     * Page-aligned memory allocation
230     */
231    
232 gbeauche 1.17 // Extend size to page boundary
233     static uint32 page_extend(uint32 size)
234 gbeauche 1.1 {
235     const uint32 page_size = getpagesize();
236     const uint32 page_mask = page_size - 1;
237     return (size + page_mask) & ~page_mask;
238     }
239    
240 gbeauche 1.16 // Screen fault handler
241     static bool screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
242 gbeauche 1.1 {
243 gbeauche 1.16 D(bug("screen_fault_handler: ADDR=0x%08X from IP=0x%08X\n", fault_address, fault_instruction));
244     const uintptr addr = (uintptr)fault_address;
245    
246 gbeauche 1.11 /* Someone attempted to write to the frame buffer. Make it writeable
247     * now so that the data could actually be written. It will be made
248     * read-only back in one of the screen update_*() functions.
249     */
250     if ((addr >= mainBuffer.memStart) && (addr < mainBuffer.memEnd)) {
251     const int page = (addr - mainBuffer.memStart) >> mainBuffer.pageBits;
252 gbeauche 1.17 caddr_t page_ad = (caddr_t)(addr & -mainBuffer.pageSize);
253 gbeauche 1.11 LOCK_VOSF;
254     PFLAG_SET(page);
255 gbeauche 1.17 vm_protect((char *)page_ad, mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
256 gbeauche 1.13 mainBuffer.dirty = true;
257 gbeauche 1.11 UNLOCK_VOSF;
258 gbeauche 1.16 return true;
259 gbeauche 1.1 }
260    
261 gbeauche 1.11 /* Otherwise, we don't know how to handle the fault, let it crash */
262     fprintf(stderr, "do_handle_screen_fault: unhandled address 0x%08X", addr);
263 gbeauche 1.16 if (fault_instruction != SIGSEGV_INVALID_PC)
264     fprintf(stderr, " [IP=0x%08X]", fault_instruction);
265 gbeauche 1.11 fprintf(stderr, "\n");
266 cebix 1.19 #if EMULATED_68K
267     uaecptr nextpc;
268     extern void m68k_dumpstate(uaecptr *nextpc);
269     m68k_dumpstate(&nextpc);
270     #endif
271     #ifdef ENABLE_MON
272     char *arg[4] = {"mon", "-m", "-r", NULL};
273     mon(3, arg);
274     QuitEmulator();
275     #endif
276 gbeauche 1.16 return false;
277 gbeauche 1.1 }
278    
279     /*
280     * Update display for Windowed mode and VOSF
281     */
282    
283 gbeauche 1.13 // From video_blit.cpp
284     extern void (*Screen_blit)(uint8 * dest, const uint8 * source, uint32 length);
285     extern bool Screen_blitter_init(XVisualInfo * visual_info, bool native_byte_order);
286    
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 gbeauche 1.1 static inline void update_display_window_vosf(void)
321     {
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.18 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
342     const int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
343 cebix 1.15 int i = y1 * bytes_per_row, j;
344 gbeauche 1.1
345 cebix 1.19 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
346 cebix 1.6
347     // Update the_host_buffer and copy of the_buffer
348     for (j = y1; j <= y2; j++) {
349 cebix 1.18 Screen_blit(the_host_buffer + i, the_buffer + i, VideoMonitor.mode.x >> 3);
350 cebix 1.6 i += bytes_per_row;
351     }
352    
353     } else {
354    
355     // Update the_host_buffer and copy of the_buffer
356     for (j = y1; j <= y2; j++) {
357 cebix 1.18 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * VideoMonitor.mode.x);
358 cebix 1.6 i += bytes_per_row;
359     }
360 gbeauche 1.1 }
361 cebix 1.15
362 gbeauche 1.1 if (have_shm)
363 cebix 1.18 XShmPutImage(x_display, the_win, the_gc, img, 0, y1, 0, y1, VideoMonitor.mode.x, height, 0);
364 gbeauche 1.1 else
365 cebix 1.18 XPutImage(x_display, the_win, the_gc, img, 0, y1, 0, y1, VideoMonitor.mode.x, height);
366 gbeauche 1.1 }
367 cebix 1.15
368 gbeauche 1.13 mainBuffer.dirty = false;
369 gbeauche 1.1 }
370    
371    
372     /*
373     * Update display for DGA mode and VOSF
374     * (only in Direct Addressing mode)
375     */
376    
377     #if REAL_ADDRESSING || DIRECT_ADDRESSING
378     static inline void update_display_dga_vosf(void)
379     {
380     int page = 0;
381     for (;;) {
382 gbeauche 1.11 const int first_page = find_next_page_set(page);
383     if (first_page >= mainBuffer.pageCount)
384 gbeauche 1.1 break;
385 gbeauche 1.11
386     page = find_next_page_clear(first_page);
387     PFLAG_CLEAR_RANGE(first_page, page);
388    
389 gbeauche 1.1 // Make the dirty pages read-only again
390     const int32 offset = first_page << mainBuffer.pageBits;
391     const uint32 length = (page - first_page) << mainBuffer.pageBits;
392 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
393 gbeauche 1.1
394     // I am sure that y2 >= y1 and depth != 1
395     const int y1 = mainBuffer.pageInfo[first_page].top;
396     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
397    
398 cebix 1.18 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
399     const int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
400 gbeauche 1.1 int i, j;
401    
402     // Check for first column from left and first column
403     // from right that have changed
404 cebix 1.18 int x1 = VideoMonitor.mode.x * bytes_per_pixel - 1;
405 gbeauche 1.1 for (j = y1; j <= y2; j++) {
406     uint8 * const p1 = &the_buffer[j * bytes_per_row];
407     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
408     for (i = 0; i < x1; i++) {
409     if (p1[i] != p2[i]) {
410     x1 = i;
411     break;
412     }
413     }
414     }
415     x1 /= bytes_per_pixel;
416    
417     int x2 = x1 * bytes_per_pixel;
418     for (j = y2; j >= y1; j--) {
419     uint8 * const p1 = &the_buffer[j * bytes_per_row];
420     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
421 cebix 1.18 for (i = VideoMonitor.mode.x * bytes_per_pixel - 1; i > x2; i--) {
422 gbeauche 1.1 if (p1[i] != p2[i]) {
423     x2 = i;
424     break;
425     }
426     }
427     }
428     x2 /= bytes_per_pixel;
429    
430     // Update the_host_buffer and copy of the_buffer
431     // There should be at least one pixel to copy
432     const int width = x2 - x1 + 1;
433     i = y1 * bytes_per_row + x1 * bytes_per_pixel;
434     for (j = y1; j <= y2; j++) {
435 gbeauche 1.13 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
436 gbeauche 1.1 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
437     i += bytes_per_row;
438     }
439     }
440 gbeauche 1.13 mainBuffer.dirty = false;
441 gbeauche 1.1 }
442     #endif
443    
444     #endif /* ENABLE_VOSF */
445    
446     #endif /* VIDEO_VOSF_H */