ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.21
Committed: 2001-06-30T17:21:54Z (23 years, 5 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.20: +5 -5 lines
Log Message:
- experimental gamma table support
- restructured video_x.cpp: uses classes for display types

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);
284
285 /* How can we deal with array overrun conditions ?
286
287 The state of the framebuffer pages that have been touched are maintained
288 in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
289
290 Terminology
291
292 "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
293 "CLEAR Page Guard" refers to the page following the Last Page but is always
294 in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
295 Page Guard but is always in the SET state.
296
297 Rough process
298
299 The update routines must determine which pages have to be blitted to the
300 screen. This job consists in finding the first_page that was touched.
301 i.e. find the next page that is SET. Then, finding how many pages were
302 touched starting from first_page. i.e. find the next page that is CLEAR.
303
304 There are two cases to check:
305
306 - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
307 but it is beyond the valid pageCount value. Therefore, we exit from the
308 update routine.
309
310 - Last Page is SET: first_page equals (pageCount - 1) and
311 find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
312 page to the screen. On the next iteration, page equals pageCount and
313 find_next_page_set() will reach the SET Page Guard. We still safely exit
314 from the update routine because the SET Page Guard position is greater
315 than pageCount.
316 */
317
318 static inline void update_display_window_vosf(driver_window *drv)
319 {
320 int page = 0;
321 for (;;) {
322 const int first_page = find_next_page_set(page);
323 if (first_page >= mainBuffer.pageCount)
324 break;
325
326 page = find_next_page_clear(first_page);
327 PFLAG_CLEAR_RANGE(first_page, page);
328
329 // Make the dirty pages read-only again
330 const int32 offset = first_page << mainBuffer.pageBits;
331 const uint32 length = (page - first_page) << mainBuffer.pageBits;
332 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
333
334 // There is at least one line to update
335 const int y1 = mainBuffer.pageInfo[first_page].top;
336 const int y2 = mainBuffer.pageInfo[page - 1].bottom;
337 const int height = y2 - y1 + 1;
338
339 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
340 const int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
341 int i = y1 * bytes_per_row, j;
342
343 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
344
345 // Update the_host_buffer and copy of the_buffer
346 for (j = y1; j <= y2; j++) {
347 Screen_blit(the_host_buffer + i, the_buffer + i, VideoMonitor.mode.x >> 3);
348 i += bytes_per_row;
349 }
350
351 } else {
352
353 // Update the_host_buffer and copy of the_buffer
354 for (j = y1; j <= y2; j++) {
355 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * VideoMonitor.mode.x);
356 i += bytes_per_row;
357 }
358 }
359
360 if (drv->have_shm)
361 XShmPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height, 0);
362 else
363 XPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height);
364 }
365 mainBuffer.dirty = false;
366 }
367
368
369 /*
370 * Update display for DGA mode and VOSF
371 * (only in Real or Direct Addressing mode)
372 */
373
374 #if REAL_ADDRESSING || DIRECT_ADDRESSING
375 static inline void update_display_dga_vosf(void)
376 {
377 int page = 0;
378 for (;;) {
379 const int first_page = find_next_page_set(page);
380 if (first_page >= mainBuffer.pageCount)
381 break;
382
383 page = find_next_page_clear(first_page);
384 PFLAG_CLEAR_RANGE(first_page, page);
385
386 // Make the dirty pages read-only again
387 const int32 offset = first_page << mainBuffer.pageBits;
388 const uint32 length = (page - first_page) << mainBuffer.pageBits;
389 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
390
391 // I am sure that y2 >= y1 and depth != 1
392 const int y1 = mainBuffer.pageInfo[first_page].top;
393 const int y2 = mainBuffer.pageInfo[page - 1].bottom;
394
395 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
396 const int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
397 int i, j;
398
399 // Check for first column from left and first column
400 // from right that have changed
401 int x1 = VideoMonitor.mode.x * bytes_per_pixel - 1;
402 for (j = y1; j <= y2; j++) {
403 uint8 * const p1 = &the_buffer[j * bytes_per_row];
404 uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
405 for (i = 0; i < x1; i++) {
406 if (p1[i] != p2[i]) {
407 x1 = i;
408 break;
409 }
410 }
411 }
412 x1 /= bytes_per_pixel;
413
414 int x2 = x1 * bytes_per_pixel;
415 for (j = y2; j >= y1; j--) {
416 uint8 * const p1 = &the_buffer[j * bytes_per_row];
417 uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
418 for (i = VideoMonitor.mode.x * bytes_per_pixel - 1; i > x2; i--) {
419 if (p1[i] != p2[i]) {
420 x2 = i;
421 break;
422 }
423 }
424 }
425 x2 /= bytes_per_pixel;
426
427 // Update the_host_buffer and copy of the_buffer
428 // There should be at least one pixel to copy
429 const int width = x2 - x1 + 1;
430 i = y1 * bytes_per_row + x1 * bytes_per_pixel;
431 for (j = y1; j <= y2; j++) {
432 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
433 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
434 i += bytes_per_row;
435 }
436 }
437 mainBuffer.dirty = false;
438 }
439 #endif
440
441 #endif /* ENABLE_VOSF */
442
443 #endif /* VIDEO_VOSF_H */