ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.25
Committed: 2001-07-03T15:59:47Z (23 years, 4 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.24: +1 -0 lines
Log Message:
- added support for platform-independant mutexes, currently only properly
  implemented under Unix
- adb.cpp uses mutexes for thread-safe mouse handling
- video_x.cpp: pressing Ctrl-F5 in windowed mode switches to a "grabbed"
  relative mouse mode, useful for some games
- video_x.cpp: fixed some bugs relating to the hotkeys (key releases are no
  longer treated as hotkeys)

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 #ifdef HAVE_PTHREADS
135 static pthread_mutex_t vosf_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer (dirtyPages in fact)
136 #define LOCK_VOSF pthread_mutex_lock(&vosf_lock);
137 #define UNLOCK_VOSF pthread_mutex_unlock(&vosf_lock);
138 #else
139 #define LOCK_VOSF
140 #define UNLOCK_VOSF
141 #endif
142
143 static int log_base_2(uint32 x)
144 {
145 uint32 mask = 0x80000000;
146 int l = 31;
147 while (l >= 0 && (x & mask) == 0) {
148 mask >>= 1;
149 l--;
150 }
151 return l;
152 }
153
154 // Extend size to page boundary
155 static uint32 page_extend(uint32 size)
156 {
157 const uint32 page_size = getpagesize();
158 const uint32 page_mask = page_size - 1;
159 return (size + page_mask) & ~page_mask;
160 }
161
162
163 /*
164 * Initialize mainBuffer structure
165 */
166
167 static bool video_init_buffer(void)
168 {
169 if (use_vosf) {
170 const uint32 page_size = getpagesize();
171 const uint32 page_mask = page_size - 1;
172
173 mainBuffer.memBase = (uintptr) the_buffer;
174 // Round up frame buffer base to page boundary
175 mainBuffer.memStart = (uintptr)((((unsigned long) the_buffer) + page_mask) & ~page_mask);
176 mainBuffer.memLength = the_buffer_size;
177 mainBuffer.memEnd = mainBuffer.memStart + mainBuffer.memLength;
178
179 mainBuffer.pageSize = page_size;
180 mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
181 mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
182
183 if (mainBuffer.dirtyPages) {
184 free(mainBuffer.dirtyPages);
185 mainBuffer.dirtyPages = NULL;
186 }
187
188 mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
189
190 if (mainBuffer.pageInfo) {
191 free(mainBuffer.pageInfo);
192 mainBuffer.pageInfo = NULL;
193 }
194
195 mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
196
197 if ((mainBuffer.dirtyPages == NULL) || (mainBuffer.pageInfo == NULL))
198 return false;
199
200 mainBuffer.dirty = false;
201
202 PFLAG_CLEAR_ALL;
203 // Safety net to insure the loops in the update routines will terminate
204 // See "How can we deal with array overrun conditions ?" hereunder for further details
205 PFLAG_CLEAR(mainBuffer.pageCount);
206 PFLAG_SET(mainBuffer.pageCount+1);
207
208 uint32 a = 0;
209 for (int i = 0; i < mainBuffer.pageCount; i++) {
210 int y1 = a / VideoMonitor.mode.bytes_per_row;
211 if (y1 >= VideoMonitor.mode.y)
212 y1 = VideoMonitor.mode.y - 1;
213
214 int y2 = (a + mainBuffer.pageSize) / VideoMonitor.mode.bytes_per_row;
215 if (y2 >= VideoMonitor.mode.y)
216 y2 = VideoMonitor.mode.y - 1;
217
218 mainBuffer.pageInfo[i].top = y1;
219 mainBuffer.pageInfo[i].bottom = y2;
220
221 a += mainBuffer.pageSize;
222 if (a > mainBuffer.memLength)
223 a = mainBuffer.memLength;
224 }
225
226 // We can now write-protect the frame buffer
227 if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
228 return false;
229 }
230 return true;
231 }
232
233
234 /*
235 * Screen fault handler
236 */
237
238 static bool screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
239 {
240 D(bug("screen_fault_handler: ADDR=0x%08X from IP=0x%08X\n", fault_address, fault_instruction));
241 const uintptr addr = (uintptr)fault_address;
242
243 /* Someone attempted to write to the frame buffer. Make it writeable
244 * now so that the data could actually be written to. It will be made
245 * read-only back in one of the screen update_*() functions.
246 */
247 if ((addr >= mainBuffer.memStart) && (addr < mainBuffer.memEnd)) {
248 const int page = (addr - mainBuffer.memStart) >> mainBuffer.pageBits;
249 caddr_t page_ad = (caddr_t)(addr & -mainBuffer.pageSize);
250 LOCK_VOSF;
251 PFLAG_SET(page);
252 vm_protect((char *)page_ad, mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
253 mainBuffer.dirty = true;
254 UNLOCK_VOSF;
255 return true;
256 }
257
258 /* Otherwise, we don't know how to handle the fault, let it crash */
259 fprintf(stderr, "do_handle_screen_fault: unhandled address 0x%08X", addr);
260 if (fault_instruction != SIGSEGV_INVALID_PC)
261 fprintf(stderr, " [IP=0x%08X]", fault_instruction);
262 fprintf(stderr, "\n");
263 #if EMULATED_68K
264 uaecptr nextpc;
265 extern void m68k_dumpstate(uaecptr *nextpc);
266 m68k_dumpstate(&nextpc);
267 #endif
268 VideoQuitFullScreen();
269 #ifdef ENABLE_MON
270 char *arg[4] = {"mon", "-m", "-r", NULL};
271 mon(3, arg);
272 QuitEmulator();
273 #endif
274 return false;
275 }
276
277
278 /*
279 * Update display for Windowed mode and VOSF
280 */
281
282 // From video_blit.cpp
283 extern void (*Screen_blit)(uint8 * dest, const uint8 * source, uint32 length);
284 extern bool Screen_blitter_init(XVisualInfo * visual_info, bool native_byte_order, video_depth mac_depth);
285 extern uint32 ExpandMap[256];
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(driver_window *drv)
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 if (VideoMonitor.mode.depth < VDEPTH_8BIT) {
342
343 // Update the_host_buffer and copy of the_buffer
344 const int src_bytes_per_row = VideoMonitor.mode.bytes_per_row;
345 const int dst_bytes_per_row = drv->img->bytes_per_line;
346 const int pixels_per_byte = VideoMonitor.mode.x / src_bytes_per_row;
347 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
348 for (j = y1; j <= y2; j++) {
349 Screen_blit(the_host_buffer + i2, the_buffer + i1, VideoMonitor.mode.x / pixels_per_byte);
350 i1 += src_bytes_per_row;
351 i2 += dst_bytes_per_row;
352 }
353
354 } else {
355
356 // Update the_host_buffer and copy of the_buffer
357 const int src_bytes_per_row = VideoMonitor.mode.bytes_per_row;
358 const int dst_bytes_per_row = drv->img->bytes_per_line;
359 const int bytes_per_pixel = src_bytes_per_row / VideoMonitor.mode.x;
360 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
361 for (j = y1; j <= y2; j++) {
362 Screen_blit(the_host_buffer + i2, the_buffer + i1, bytes_per_pixel * VideoMonitor.mode.x);
363 i1 += src_bytes_per_row;
364 i2 += dst_bytes_per_row;
365 }
366 }
367
368 if (drv->have_shm)
369 XShmPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height, 0);
370 else
371 XPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height);
372 }
373 mainBuffer.dirty = false;
374 }
375
376
377 /*
378 * Update display for DGA mode and VOSF
379 * (only in Real or Direct Addressing mode)
380 */
381
382 #if REAL_ADDRESSING || DIRECT_ADDRESSING
383 static inline void update_display_dga_vosf(void)
384 {
385 int page = 0;
386 for (;;) {
387 const int first_page = find_next_page_set(page);
388 if (first_page >= mainBuffer.pageCount)
389 break;
390
391 page = find_next_page_clear(first_page);
392 PFLAG_CLEAR_RANGE(first_page, page);
393
394 // Make the dirty pages read-only again
395 const int32 offset = first_page << mainBuffer.pageBits;
396 const uint32 length = (page - first_page) << mainBuffer.pageBits;
397 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
398
399 // I am sure that y2 >= y1 and depth != 1
400 const int y1 = mainBuffer.pageInfo[first_page].top;
401 const int y2 = mainBuffer.pageInfo[page - 1].bottom;
402
403 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
404 const int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
405 int i, j;
406
407 // Check for first column from left and first column
408 // from right that have changed
409 int x1 = VideoMonitor.mode.x * bytes_per_pixel - 1;
410 for (j = y1; j <= y2; j++) {
411 uint8 * const p1 = &the_buffer[j * bytes_per_row];
412 uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
413 for (i = 0; i < x1; i++) {
414 if (p1[i] != p2[i]) {
415 x1 = i;
416 break;
417 }
418 }
419 }
420 x1 /= bytes_per_pixel;
421
422 int x2 = x1 * bytes_per_pixel;
423 for (j = y2; j >= y1; j--) {
424 uint8 * const p1 = &the_buffer[j * bytes_per_row];
425 uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
426 for (i = VideoMonitor.mode.x * bytes_per_pixel - 1; i > x2; i--) {
427 if (p1[i] != p2[i]) {
428 x2 = i;
429 break;
430 }
431 }
432 }
433 x2 /= bytes_per_pixel;
434
435 // Update the_host_buffer and copy of the_buffer
436 // There should be at least one pixel to copy
437 const int width = x2 - x1 + 1;
438 i = y1 * bytes_per_row + x1 * bytes_per_pixel;
439 for (j = y1; j <= y2; j++) {
440 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
441 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
442 i += bytes_per_row;
443 }
444 }
445 mainBuffer.dirty = false;
446 }
447 #endif
448
449 #endif /* ENABLE_VOSF */
450
451 #endif /* VIDEO_VOSF_H */