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, 5 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

# Content
1 /*
2 * video_vosf.h - Video/graphics emulation, video on SEGV signals support
3 *
4 * Basilisk II (C) 1997-2001 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 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 /*
229 * Page-aligned memory allocation
230 */
231
232 // Extend size to page boundary
233 static uint32 page_extend(uint32 size)
234 {
235 const uint32 page_size = getpagesize();
236 const uint32 page_mask = page_size - 1;
237 return (size + page_mask) & ~page_mask;
238 }
239
240 // Screen fault handler
241 static bool screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
242 {
243 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 /* 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 caddr_t page_ad = (caddr_t)(addr & -mainBuffer.pageSize);
253 LOCK_VOSF;
254 PFLAG_SET(page);
255 vm_protect((char *)page_ad, mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
256 mainBuffer.dirty = true;
257 UNLOCK_VOSF;
258 return true;
259 }
260
261 /* 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 if (fault_instruction != SIGSEGV_INVALID_PC)
264 fprintf(stderr, " [IP=0x%08X]", fault_instruction);
265 fprintf(stderr, "\n");
266 #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 return false;
277 }
278
279 /*
280 * Update display for Windowed mode and VOSF
281 */
282
283 // 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 /* 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 The update routines must determine which pages have to be blitted to the
302 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 There are two cases to check:
307
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 static inline void update_display_window_vosf(void)
321 {
322 int page = 0;
323 for (;;) {
324 const int first_page = find_next_page_set(page);
325 if (first_page >= mainBuffer.pageCount)
326 break;
327
328 page = find_next_page_clear(first_page);
329 PFLAG_CLEAR_RANGE(first_page, page);
330
331 // 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 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
335
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 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 int i = y1 * bytes_per_row, j;
344
345 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
346
347 // Update the_host_buffer and copy of the_buffer
348 for (j = y1; j <= y2; j++) {
349 Screen_blit(the_host_buffer + i, the_buffer + i, VideoMonitor.mode.x >> 3);
350 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 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * VideoMonitor.mode.x);
358 i += bytes_per_row;
359 }
360 }
361
362 if (have_shm)
363 XShmPutImage(x_display, the_win, the_gc, img, 0, y1, 0, y1, VideoMonitor.mode.x, height, 0);
364 else
365 XPutImage(x_display, the_win, the_gc, img, 0, y1, 0, y1, VideoMonitor.mode.x, height);
366 }
367
368 mainBuffer.dirty = false;
369 }
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 const int first_page = find_next_page_set(page);
383 if (first_page >= mainBuffer.pageCount)
384 break;
385
386 page = find_next_page_clear(first_page);
387 PFLAG_CLEAR_RANGE(first_page, page);
388
389 // 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 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
393
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 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 int i, j;
401
402 // Check for first column from left and first column
403 // from right that have changed
404 int x1 = VideoMonitor.mode.x * bytes_per_pixel - 1;
405 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 for (i = VideoMonitor.mode.x * bytes_per_pixel - 1; i > x2; i--) {
422 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 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
436 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
437 i += bytes_per_row;
438 }
439 }
440 mainBuffer.dirty = false;
441 }
442 #endif
443
444 #endif /* ENABLE_VOSF */
445
446 #endif /* VIDEO_VOSF_H */