ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.36
Committed: 2004-01-04T06:11:49Z (20 years, 10 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.35: +5 -5 lines
Log Message:
Always prefer our (inlined) spinlocks.

File Contents

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