ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.39
Committed: 2004-06-24T15:19:56Z (20 years, 5 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.38: +2 -36 lines
Log Message:
Move VideoMode wrappers to video_blit.h.

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