ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.15
Committed: 2001-03-06T18:41:12Z (23 years, 4 months ago) by cebix
Content type: text/plain
Branch: MAIN
Changes since 1.14: +7 -65 lines
Log Message:
added patches from Brian J. Johnson (better VOSF performance and responsiveness)

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     /*
28     * Page-aligned memory allocation
29     */
30    
31     // Align on page boundaries
32 gbeauche 1.4 static uintptr align_on_page_boundary(uintptr size)
33 gbeauche 1.1 {
34     const uint32 page_size = getpagesize();
35     const uint32 page_mask = page_size - 1;
36     return (size + page_mask) & ~page_mask;
37     }
38    
39     // Allocate memory on page boundary
40     static void * allocate_framebuffer(uint32 size, uint8 * hint = 0)
41     {
42     // Remind that the system can allocate at 0x00000000...
43     return mmap((caddr_t)hint, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, zero_fd, 0);
44     }
45    
46    
47     /*
48     * Screen fault handler
49     */
50    
51 gbeauche 1.11 const uintptr INVALID_PC = (uintptr)-1;
52    
53     static inline void do_handle_screen_fault(uintptr addr, uintptr pc = INVALID_PC)
54 gbeauche 1.1 {
55 gbeauche 1.11 /* Someone attempted to write to the frame buffer. Make it writeable
56     * now so that the data could actually be written. It will be made
57     * read-only back in one of the screen update_*() functions.
58     */
59     if ((addr >= mainBuffer.memStart) && (addr < mainBuffer.memEnd)) {
60     const int page = (addr - mainBuffer.memStart) >> mainBuffer.pageBits;
61     caddr_t page_ad = (caddr_t)(addr & ~(mainBuffer.pageSize - 1));
62     LOCK_VOSF;
63     PFLAG_SET(page);
64     mprotect(page_ad, mainBuffer.pageSize, PROT_READ | PROT_WRITE);
65 gbeauche 1.13 mainBuffer.dirty = true;
66 gbeauche 1.11 UNLOCK_VOSF;
67     return;
68 gbeauche 1.1 }
69    
70 gbeauche 1.11 /* Otherwise, we don't know how to handle the fault, let it crash */
71     fprintf(stderr, "do_handle_screen_fault: unhandled address 0x%08X", addr);
72     if (pc != INVALID_PC)
73     fprintf(stderr, " [IP=0x%08X]", pc);
74     fprintf(stderr, "\n");
75    
76     signal(SIGSEGV, SIG_DFL);
77 gbeauche 1.1 }
78    
79     #if defined(HAVE_SIGINFO_T)
80 cebix 1.6
81 gbeauche 1.1 static void Screen_fault_handler(int, siginfo_t * sip, void *)
82     {
83     D(bug("Screen_fault_handler: ADDR=0x%08X\n", sip->si_addr));
84 gbeauche 1.4 do_handle_screen_fault((uintptr)sip->si_addr);
85 gbeauche 1.1 }
86 cebix 1.6
87 gbeauche 1.1 #elif defined(HAVE_SIGCONTEXT_SUBTERFUGE)
88 cebix 1.6
89 gbeauche 1.1 # if defined(__i386__) && defined(__linux__)
90     static void Screen_fault_handler(int, struct sigcontext scs)
91     {
92     D(bug("Screen_fault_handler: ADDR=0x%08X from IP=0x%08X\n", scs.cr2, scs.eip));
93 gbeauche 1.11 do_handle_screen_fault((uintptr)scs.cr2, (uintptr)scs.eip);
94 gbeauche 1.1 }
95 cebix 1.6
96     # elif defined(__m68k__) && defined(__NetBSD__)
97    
98     # include <m68k/frame.h>
99     static void Screen_fault_handler(int, int code, struct sigcontext *scp)
100     {
101     D(bug("Screen_fault_handler: ADDR=0x%08X\n", code));
102     struct sigstate {
103     int ss_flags;
104     struct frame ss_frame;
105     };
106     struct sigstate *state = (struct sigstate *)scp->sc_ap;
107     uintptr fault_addr;
108     switch (state->ss_frame.f_format) {
109     case 7: // 68040 access error
110     // "code" is sometimes unreliable (i.e. contains NULL or a bogus address), reason unknown
111     fault_addr = state->ss_frame.f_fmt7.f_fa;
112     break;
113     default:
114     fault_addr = (uintptr)code;
115     break;
116     }
117     do_handle_screen_fault(fault_addr);
118     }
119    
120 cebix 1.14 # elif defined(__powerpc__) && defined(__linux__)
121    
122     static void Screen_fault_handler(int, struct sigcontext_struct *scs)
123     {
124     D(bug("Screen_fault_handler: ADDR=0x%08X from IP=0x%08X\n", scs->regs->dar, scs->regs->nip));
125     do_handle_screen_fault((uintptr)scs->regs->dar, (uintptr)scs->regs->nip);
126     }
127    
128 gbeauche 1.1 # else
129     # error "No suitable subterfuge for Video on SEGV signals"
130     # endif
131     #else
132     # error "Can't do Video on SEGV signals"
133     #endif
134    
135    
136     /*
137     * Screen fault handler initialization
138     */
139    
140     #if defined(HAVE_SIGINFO_T)
141     static bool Screen_fault_handler_init()
142     {
143     // Setup SIGSEGV handler to process writes to frame buffer
144     sigemptyset(&vosf_sa.sa_mask);
145     vosf_sa.sa_sigaction = Screen_fault_handler;
146 cebix 1.5 vosf_sa.sa_flags = SA_SIGINFO;
147 gbeauche 1.1 return (sigaction(SIGSEGV, &vosf_sa, NULL) == 0);
148     }
149     #elif defined(HAVE_SIGCONTEXT_SUBTERFUGE)
150     static bool Screen_fault_handler_init()
151     {
152     // Setup SIGSEGV handler to process writes to frame buffer
153     sigemptyset(&vosf_sa.sa_mask);
154     vosf_sa.sa_handler = (void (*)(int)) Screen_fault_handler;
155 cebix 1.7 #if !EMULATED_68K && defined(__NetBSD__)
156     sigaddset(&vosf_sa.sa_mask, SIGALRM);
157     vosf_sa.sa_flags = SA_ONSTACK;
158     #else
159 gbeauche 1.1 vosf_sa.sa_flags = 0;
160 cebix 1.7 #endif
161 gbeauche 1.1 return (sigaction(SIGSEGV, &vosf_sa, NULL) == 0);
162     }
163     #endif
164    
165    
166     /*
167     * Update display for Windowed mode and VOSF
168     */
169    
170 gbeauche 1.13 // From video_blit.cpp
171     extern void (*Screen_blit)(uint8 * dest, const uint8 * source, uint32 length);
172     extern bool Screen_blitter_init(XVisualInfo * visual_info, bool native_byte_order);
173    
174 gbeauche 1.12 /* How can we deal with array overrun conditions ?
175    
176     The state of the framebuffer pages that have been touched are maintained
177     in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
178    
179     Terminology
180    
181     "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
182     "CLEAR Page Guard" refers to the page following the Last Page but is always
183     in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
184     Page Guard but is always in the SET state.
185    
186     Rough process
187    
188 gbeauche 1.13 The update routines must determine which pages have to be blitted to the
189 gbeauche 1.12 screen. This job consists in finding the first_page that was touched.
190     i.e. find the next page that is SET. Then, finding how many pages were
191     touched starting from first_page. i.e. find the next page that is CLEAR.
192    
193 gbeauche 1.13 There are two cases to check:
194 gbeauche 1.12
195     - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
196     but it is beyond the valid pageCount value. Therefore, we exit from the
197     update routine.
198    
199     - Last Page is SET: first_page equals (pageCount - 1) and
200     find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
201     page to the screen. On the next iteration, page equals pageCount and
202     find_next_page_set() will reach the SET Page Guard. We still safely exit
203     from the update routine because the SET Page Guard position is greater
204     than pageCount.
205     */
206    
207 gbeauche 1.1 static inline void update_display_window_vosf(void)
208     {
209     int page = 0;
210     for (;;) {
211 gbeauche 1.11 const int first_page = find_next_page_set(page);
212     if (first_page >= mainBuffer.pageCount)
213 gbeauche 1.1 break;
214 gbeauche 1.11
215     page = find_next_page_clear(first_page);
216     PFLAG_CLEAR_RANGE(first_page, page);
217 cebix 1.7
218 gbeauche 1.1 // Make the dirty pages read-only again
219     const int32 offset = first_page << mainBuffer.pageBits;
220     const uint32 length = (page - first_page) << mainBuffer.pageBits;
221     mprotect((caddr_t)(mainBuffer.memStart + offset), length, PROT_READ);
222    
223     // There is at least one line to update
224     const int y1 = mainBuffer.pageInfo[first_page].top;
225     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
226     const int height = y2 - y1 + 1;
227    
228     const int bytes_per_row = VideoMonitor.bytes_per_row;
229     const int bytes_per_pixel = VideoMonitor.bytes_per_row / VideoMonitor.x;
230 cebix 1.15 int i = y1 * bytes_per_row, j;
231 gbeauche 1.1
232 cebix 1.6 if (depth == 1) {
233    
234     // Update the_host_buffer and copy of the_buffer
235     for (j = y1; j <= y2; j++) {
236 cebix 1.15 Screen_blit(the_host_buffer + i, the_buffer + i, VideoMonitor.x >> 3);
237 cebix 1.6 i += bytes_per_row;
238     }
239    
240     } else {
241    
242     // Update the_host_buffer and copy of the_buffer
243     for (j = y1; j <= y2; j++) {
244 cebix 1.15 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * VideoMonitor.x);
245 cebix 1.6 i += bytes_per_row;
246     }
247 gbeauche 1.1 }
248 cebix 1.15
249 gbeauche 1.1 if (have_shm)
250 cebix 1.15 XShmPutImage(x_display, the_win, the_gc, img, 0, y1, 0, y1, VideoMonitor.x, height, 0);
251 gbeauche 1.1 else
252 cebix 1.15 XPutImage(x_display, the_win, the_gc, img, 0, y1, 0, y1, VideoMonitor.x, height);
253 gbeauche 1.1 }
254 cebix 1.15
255 gbeauche 1.13 mainBuffer.dirty = false;
256 gbeauche 1.1 }
257    
258    
259     /*
260     * Update display for DGA mode and VOSF
261     * (only in Direct Addressing mode)
262     */
263    
264     #if REAL_ADDRESSING || DIRECT_ADDRESSING
265     static inline void update_display_dga_vosf(void)
266     {
267     int page = 0;
268     for (;;) {
269 gbeauche 1.11 const int first_page = find_next_page_set(page);
270     if (first_page >= mainBuffer.pageCount)
271 gbeauche 1.1 break;
272 gbeauche 1.11
273     page = find_next_page_clear(first_page);
274     PFLAG_CLEAR_RANGE(first_page, page);
275    
276 gbeauche 1.1 // Make the dirty pages read-only again
277     const int32 offset = first_page << mainBuffer.pageBits;
278     const uint32 length = (page - first_page) << mainBuffer.pageBits;
279     mprotect((caddr_t)(mainBuffer.memStart + offset), length, PROT_READ);
280    
281     // I am sure that y2 >= y1 and depth != 1
282     const int y1 = mainBuffer.pageInfo[first_page].top;
283     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
284    
285     const int bytes_per_row = VideoMonitor.bytes_per_row;
286     const int bytes_per_pixel = VideoMonitor.bytes_per_row / VideoMonitor.x;
287     int i, j;
288    
289     // Check for first column from left and first column
290     // from right that have changed
291     int x1 = VideoMonitor.x * bytes_per_pixel - 1;
292     for (j = y1; j <= y2; j++) {
293     uint8 * const p1 = &the_buffer[j * bytes_per_row];
294     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
295     for (i = 0; i < x1; i++) {
296     if (p1[i] != p2[i]) {
297     x1 = i;
298     break;
299     }
300     }
301     }
302     x1 /= bytes_per_pixel;
303    
304     int x2 = x1 * bytes_per_pixel;
305     for (j = y2; j >= y1; j--) {
306     uint8 * const p1 = &the_buffer[j * bytes_per_row];
307     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
308     for (i = VideoMonitor.x * bytes_per_pixel - 1; i > x2; i--) {
309     if (p1[i] != p2[i]) {
310     x2 = i;
311     break;
312     }
313     }
314     }
315     x2 /= bytes_per_pixel;
316    
317     // Update the_host_buffer and copy of the_buffer
318     // There should be at least one pixel to copy
319     const int width = x2 - x1 + 1;
320     i = y1 * bytes_per_row + x1 * bytes_per_pixel;
321     for (j = y1; j <= y2; j++) {
322 gbeauche 1.13 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
323 gbeauche 1.1 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
324     i += bytes_per_row;
325     }
326     }
327 gbeauche 1.13 mainBuffer.dirty = false;
328 gbeauche 1.1 }
329     #endif
330    
331     #endif /* ENABLE_VOSF */
332    
333     #endif /* VIDEO_VOSF_H */