ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.22
Committed: 2001-07-01T14:38:03Z (23 years, 5 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.21: +1 -1 lines
Log Message:
- sony.cpp/disk.cpp/cdrom.cpp use vector<> of drive_info objects instead of
  linked list
- color depth switching updates slot ROM
- video_x.cpp always supports 1-bit window modes
- timer_create()/clock_gettime() are pulled from librt if present

File Contents

# User Rev Content
1 gbeauche 1.1 /*
2     * video_vosf.h - Video/graphics emulation, video on SEGV signals support
3     *
4 gbeauche 1.13 * Basilisk II (C) 1997-2001 Christian Bauer
5 gbeauche 1.1 *
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 cebix 1.19 #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 gbeauche 1.20 static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
38     static uint32 the_buffer_size; // Size of allocated the_buffer
39 cebix 1.19
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 gbeauche 1.20 // 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 cebix 1.19
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 gbeauche 1.20 // Round up frame buffer base to page boundary
175 cebix 1.19 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 cebix 1.21 if ((mainBuffer.dirtyPages == NULL) || (mainBuffer.pageInfo == NULL))
198 cebix 1.19 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 gbeauche 1.20 // See "How can we deal with array overrun conditions ?" hereunder for further details
205 cebix 1.19 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 gbeauche 1.1 /*
235 gbeauche 1.20 * Screen fault handler
236 gbeauche 1.1 */
237    
238 gbeauche 1.16 static bool screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
239 gbeauche 1.1 {
240 gbeauche 1.16 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 gbeauche 1.11 /* Someone attempted to write to the frame buffer. Make it writeable
244 gbeauche 1.20 * now so that the data could actually be written to. It will be made
245 gbeauche 1.11 * 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 gbeauche 1.17 caddr_t page_ad = (caddr_t)(addr & -mainBuffer.pageSize);
250 gbeauche 1.11 LOCK_VOSF;
251     PFLAG_SET(page);
252 gbeauche 1.17 vm_protect((char *)page_ad, mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
253 gbeauche 1.13 mainBuffer.dirty = true;
254 gbeauche 1.11 UNLOCK_VOSF;
255 gbeauche 1.16 return true;
256 gbeauche 1.1 }
257    
258 gbeauche 1.11 /* 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 gbeauche 1.16 if (fault_instruction != SIGSEGV_INVALID_PC)
261     fprintf(stderr, " [IP=0x%08X]", fault_instruction);
262 gbeauche 1.11 fprintf(stderr, "\n");
263 cebix 1.19 #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 gbeauche 1.16 return false;
274 gbeauche 1.1 }
275    
276 gbeauche 1.20
277 gbeauche 1.1 /*
278     * Update display for Windowed mode and VOSF
279     */
280    
281 gbeauche 1.13 // From video_blit.cpp
282     extern void (*Screen_blit)(uint8 * dest, const uint8 * source, uint32 length);
283 cebix 1.22 extern bool Screen_blitter_init(XVisualInfo * visual_info, bool native_byte_order, video_depth mac_depth);
284 gbeauche 1.13
285 gbeauche 1.12 /* 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 gbeauche 1.13 The update routines must determine which pages have to be blitted to the
300 gbeauche 1.12 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 gbeauche 1.13 There are two cases to check:
305 gbeauche 1.12
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 cebix 1.21 static inline void update_display_window_vosf(driver_window *drv)
319 gbeauche 1.1 {
320     int page = 0;
321     for (;;) {
322 gbeauche 1.11 const int first_page = find_next_page_set(page);
323     if (first_page >= mainBuffer.pageCount)
324 gbeauche 1.1 break;
325 gbeauche 1.11
326     page = find_next_page_clear(first_page);
327     PFLAG_CLEAR_RANGE(first_page, page);
328 cebix 1.7
329 gbeauche 1.1 // 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 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
333 gbeauche 1.1
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 cebix 1.18 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 cebix 1.15 int i = y1 * bytes_per_row, j;
342 gbeauche 1.1
343 cebix 1.19 if (VideoMonitor.mode.depth == VDEPTH_1BIT) {
344 cebix 1.6
345     // Update the_host_buffer and copy of the_buffer
346     for (j = y1; j <= y2; j++) {
347 cebix 1.18 Screen_blit(the_host_buffer + i, the_buffer + i, VideoMonitor.mode.x >> 3);
348 cebix 1.6 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 cebix 1.18 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * VideoMonitor.mode.x);
356 cebix 1.6 i += bytes_per_row;
357     }
358 gbeauche 1.1 }
359 cebix 1.15
360 cebix 1.21 if (drv->have_shm)
361     XShmPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height, 0);
362 gbeauche 1.1 else
363 cebix 1.21 XPutImage(x_display, drv->w, drv->gc, drv->img, 0, y1, 0, y1, VideoMonitor.mode.x, height);
364 gbeauche 1.1 }
365 gbeauche 1.13 mainBuffer.dirty = false;
366 gbeauche 1.1 }
367    
368    
369     /*
370     * Update display for DGA mode and VOSF
371 gbeauche 1.20 * (only in Real or Direct Addressing mode)
372 gbeauche 1.1 */
373    
374     #if REAL_ADDRESSING || DIRECT_ADDRESSING
375     static inline void update_display_dga_vosf(void)
376     {
377     int page = 0;
378     for (;;) {
379 gbeauche 1.11 const int first_page = find_next_page_set(page);
380     if (first_page >= mainBuffer.pageCount)
381 gbeauche 1.1 break;
382 gbeauche 1.11
383     page = find_next_page_clear(first_page);
384     PFLAG_CLEAR_RANGE(first_page, page);
385    
386 gbeauche 1.1 // 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 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
390 gbeauche 1.1
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 cebix 1.18 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 gbeauche 1.1 int i, j;
398    
399     // Check for first column from left and first column
400     // from right that have changed
401 cebix 1.18 int x1 = VideoMonitor.mode.x * bytes_per_pixel - 1;
402 gbeauche 1.1 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 cebix 1.18 for (i = VideoMonitor.mode.x * bytes_per_pixel - 1; i > x2; i--) {
419 gbeauche 1.1 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 gbeauche 1.13 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
433 gbeauche 1.1 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
434     i += bytes_per_row;
435     }
436     }
437 gbeauche 1.13 mainBuffer.dirty = false;
438 gbeauche 1.1 }
439     #endif
440    
441     #endif /* ENABLE_VOSF */
442    
443     #endif /* VIDEO_VOSF_H */