ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.24
Committed: 2001-07-01T21:09:29Z (23 years, 3 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.23: +8 -5 lines
Log Message:
- video_set_palette() gets passed the number of used palette entries
- video_x.cpp supports 2- and 4-bit modes on truecolor screens

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_8BIT) {
341
342 // Update the_host_buffer and copy of the_buffer
343 const int src_bytes_per_row = VideoMonitor.mode.bytes_per_row;
344 const int dst_bytes_per_row = drv->img->bytes_per_line;
345 const int pixels_per_byte = VideoMonitor.mode.x / src_bytes_per_row;
346 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
347 for (j = y1; j <= y2; j++) {
348 Screen_blit(the_host_buffer + i2, the_buffer + i1, VideoMonitor.mode.x / pixels_per_byte);
349 i1 += src_bytes_per_row;
350 i2 += dst_bytes_per_row;
351 }
352
353 } else {
354
355 // Update the_host_buffer and copy of the_buffer
356 const int src_bytes_per_row = VideoMonitor.mode.bytes_per_row;
357 const int dst_bytes_per_row = drv->img->bytes_per_line;
358 const int bytes_per_pixel = src_bytes_per_row / VideoMonitor.mode.x;
359 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
360 for (j = y1; j <= y2; j++) {
361 Screen_blit(the_host_buffer + i2, the_buffer + i1, bytes_per_pixel * VideoMonitor.mode.x);
362 i1 += src_bytes_per_row;
363 i2 += dst_bytes_per_row;
364 }
365 }
366
367 if (drv->have_shm)
368 XShmPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height, 0);
369 else
370 XPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height);
371 }
372 mainBuffer.dirty = false;
373 }
374
375
376 /*
377 * Update display for DGA mode and VOSF
378 * (only in Real or Direct Addressing mode)
379 */
380
381 #if REAL_ADDRESSING || DIRECT_ADDRESSING
382 static inline void update_display_dga_vosf(void)
383 {
384 int page = 0;
385 for (;;) {
386 const int first_page = find_next_page_set(page);
387 if (first_page >= mainBuffer.pageCount)
388 break;
389
390 page = find_next_page_clear(first_page);
391 PFLAG_CLEAR_RANGE(first_page, page);
392
393 // Make the dirty pages read-only again
394 const int32 offset = first_page << mainBuffer.pageBits;
395 const uint32 length = (page - first_page) << mainBuffer.pageBits;
396 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
397
398 // I am sure that y2 >= y1 and depth != 1
399 const int y1 = mainBuffer.pageInfo[first_page].top;
400 const int y2 = mainBuffer.pageInfo[page - 1].bottom;
401
402 const int bytes_per_row = VideoMonitor.mode.bytes_per_row;
403 const int bytes_per_pixel = VideoMonitor.mode.bytes_per_row / VideoMonitor.mode.x;
404 int i, j;
405
406 // Check for first column from left and first column
407 // from right that have changed
408 int x1 = VideoMonitor.mode.x * bytes_per_pixel - 1;
409 for (j = y1; j <= y2; j++) {
410 uint8 * const p1 = &the_buffer[j * bytes_per_row];
411 uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
412 for (i = 0; i < x1; i++) {
413 if (p1[i] != p2[i]) {
414 x1 = i;
415 break;
416 }
417 }
418 }
419 x1 /= bytes_per_pixel;
420
421 int x2 = x1 * bytes_per_pixel;
422 for (j = y2; j >= y1; j--) {
423 uint8 * const p1 = &the_buffer[j * bytes_per_row];
424 uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
425 for (i = VideoMonitor.mode.x * bytes_per_pixel - 1; i > x2; i--) {
426 if (p1[i] != p2[i]) {
427 x2 = i;
428 break;
429 }
430 }
431 }
432 x2 /= bytes_per_pixel;
433
434 // Update the_host_buffer and copy of the_buffer
435 // There should be at least one pixel to copy
436 const int width = x2 - x1 + 1;
437 i = y1 * bytes_per_row + x1 * bytes_per_pixel;
438 for (j = y1; j <= y2; j++) {
439 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
440 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
441 i += bytes_per_row;
442 }
443 }
444 mainBuffer.dirty = false;
445 }
446 #endif
447
448 #endif /* ENABLE_VOSF */
449
450 #endif /* VIDEO_VOSF_H */