ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.23
Committed: 2001-07-01T19:57:55Z (23 years, 5 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.22: +10 -6 lines
Log Message:
added support for 8-bit windowed modes on 16 and 32-bit screens (for the
games, man, the games! :-)

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 #ifdef ENABLE_MON
269 char *arg[4] = {"mon", "-m", "-r", NULL};
270 mon(3, arg);
271 QuitEmulator();
272 #endif
273 return false;
274 }
275
276
277 /*
278 * Update display for Windowed mode and VOSF
279 */
280
281 // From video_blit.cpp
282 extern void (*Screen_blit)(uint8 * dest, const uint8 * source, uint32 length);
283 extern bool Screen_blitter_init(XVisualInfo * visual_info, bool native_byte_order, video_depth mac_depth);
284 extern uint32 ExpandMap[256];
285
286 /* How can we deal with array overrun conditions ?
287
288 The state of the framebuffer pages that have been touched are maintained
289 in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
290
291 Terminology
292
293 "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
294 "CLEAR Page Guard" refers to the page following the Last Page but is always
295 in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
296 Page Guard but is always in the SET state.
297
298 Rough process
299
300 The update routines must determine which pages have to be blitted to the
301 screen. This job consists in finding the first_page that was touched.
302 i.e. find the next page that is SET. Then, finding how many pages were
303 touched starting from first_page. i.e. find the next page that is CLEAR.
304
305 There are two cases to check:
306
307 - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
308 but it is beyond the valid pageCount value. Therefore, we exit from the
309 update routine.
310
311 - Last Page is SET: first_page equals (pageCount - 1) and
312 find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
313 page to the screen. On the next iteration, page equals pageCount and
314 find_next_page_set() will reach the SET Page Guard. We still safely exit
315 from the update routine because the SET Page Guard position is greater
316 than pageCount.
317 */
318
319 static inline void update_display_window_vosf(driver_window *drv)
320 {
321 int page = 0;
322 for (;;) {
323 const int first_page = find_next_page_set(page);
324 if (first_page >= mainBuffer.pageCount)
325 break;
326
327 page = find_next_page_clear(first_page);
328 PFLAG_CLEAR_RANGE(first_page, page);
329
330 // Make the dirty pages read-only again
331 const int32 offset = first_page << mainBuffer.pageBits;
332 const uint32 length = (page - first_page) << mainBuffer.pageBits;
333 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
334
335 // There is at least one line to update
336 const int y1 = mainBuffer.pageInfo[first_page].top;
337 const int y2 = mainBuffer.pageInfo[page - 1].bottom;
338 const int height = y2 - y1 + 1;
339
340 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
341
342 // Update the_host_buffer and copy of the_buffer
343 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
344 int i = y1 * bytes_per_row, j;
345 for (j = y1; j <= y2; j++) {
346 Screen_blit(the_host_buffer + i, the_buffer + i, VideoMonitor.mode.x >> 3);
347 i += 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 = VideoMonitor.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 / VideoMonitor.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 * VideoMonitor.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, VideoMonitor.mode.x, height, 0);
366 else
367 XPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.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 int page = 0;
382 for (;;) {
383 const int first_page = find_next_page_set(page);
384 if (first_page >= mainBuffer.pageCount)
385 break;
386
387 page = find_next_page_clear(first_page);
388 PFLAG_CLEAR_RANGE(first_page, page);
389
390 // Make the dirty pages read-only again
391 const int32 offset = first_page << mainBuffer.pageBits;
392 const uint32 length = (page - first_page) << mainBuffer.pageBits;
393 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
394
395 // I am sure that y2 >= y1 and depth != 1
396 const int y1 = mainBuffer.pageInfo[first_page].top;
397 const int y2 = mainBuffer.pageInfo[page - 1].bottom;
398
399 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
400 const int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
401 int i, j;
402
403 // Check for first column from left and first column
404 // from right that have changed
405 int x1 = VideoMonitor.mode.x * bytes_per_pixel - 1;
406 for (j = y1; j <= y2; j++) {
407 uint8 * const p1 = &the_buffer[j * bytes_per_row];
408 uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
409 for (i = 0; i < x1; i++) {
410 if (p1[i] != p2[i]) {
411 x1 = i;
412 break;
413 }
414 }
415 }
416 x1 /= bytes_per_pixel;
417
418 int x2 = x1 * bytes_per_pixel;
419 for (j = y2; j >= y1; j--) {
420 uint8 * const p1 = &the_buffer[j * bytes_per_row];
421 uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
422 for (i = VideoMonitor.mode.x * bytes_per_pixel - 1; i > x2; i--) {
423 if (p1[i] != p2[i]) {
424 x2 = i;
425 break;
426 }
427 }
428 }
429 x2 /= bytes_per_pixel;
430
431 // Update the_host_buffer and copy of the_buffer
432 // There should be at least one pixel to copy
433 const int width = x2 - x1 + 1;
434 i = y1 * bytes_per_row + x1 * bytes_per_pixel;
435 for (j = y1; j <= y2; j++) {
436 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
437 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
438 i += bytes_per_row;
439 }
440 }
441 mainBuffer.dirty = false;
442 }
443 #endif
444
445 #endif /* ENABLE_VOSF */
446
447 #endif /* VIDEO_VOSF_H */