ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.32
Committed: 2002-05-16T15:48:06Z (22 years, 6 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
CVS Tags: nigel-build-12, nigel-build-13
Changes since 1.31: +0 -19 lines
Log Message:
- video_vosh.h (Screen_fault_handler): Move unrecoverable fault case to...
- main_unix.cpp (sigsegv_dump_state): ... Here.
- sigsegv.h (sigsegv_fault_handler_t): Rename from sigsegv_handler_t.
- sigsegv.h (sigsegv_state_dumper_t): New.

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