ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.30
Committed: 2002-01-15T14:58:37Z (22 years, 5 months ago) by cebix
Content type: text/plain
Branch: MAIN
CVS Tags: snapshot-15012002
Changes since 1.29: +1 -1 lines
Log Message:
- documentation updates
- 2001 -> 2002
- version 0.9 -> 1.0

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