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, 9 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

# 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 /*
28 * Page-aligned memory allocation
29 */
30
31 // Align on page boundaries
32 static uintptr align_on_page_boundary(uintptr size)
33 {
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 const uintptr INVALID_PC = (uintptr)-1;
52
53 static inline void do_handle_screen_fault(uintptr addr, uintptr pc = INVALID_PC)
54 {
55 /* 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 mainBuffer.dirty = true;
66 UNLOCK_VOSF;
67 return;
68 }
69
70 /* 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 }
78
79 #if defined(HAVE_SIGINFO_T)
80
81 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 do_handle_screen_fault((uintptr)sip->si_addr);
85 }
86
87 #elif defined(HAVE_SIGCONTEXT_SUBTERFUGE)
88
89 # 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 do_handle_screen_fault((uintptr)scs.cr2, (uintptr)scs.eip);
94 }
95
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 # 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 # 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 vosf_sa.sa_flags = SA_SIGINFO;
147 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 #if !EMULATED_68K && defined(__NetBSD__)
156 sigaddset(&vosf_sa.sa_mask, SIGALRM);
157 vosf_sa.sa_flags = SA_ONSTACK;
158 #else
159 vosf_sa.sa_flags = 0;
160 #endif
161 return (sigaction(SIGSEGV, &vosf_sa, NULL) == 0);
162 }
163 #endif
164
165
166 /*
167 * Update display for Windowed mode and VOSF
168 */
169
170 // 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 /* 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 The update routines must determine which pages have to be blitted to the
189 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 There are two cases to check:
194
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 static inline void update_display_window_vosf(void)
208 {
209 int page = 0;
210 for (;;) {
211 const int first_page = find_next_page_set(page);
212 if (first_page >= mainBuffer.pageCount)
213 break;
214
215 page = find_next_page_clear(first_page);
216 PFLAG_CLEAR_RANGE(first_page, page);
217
218 // 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 int i = y1 * bytes_per_row, j;
231
232 if (depth == 1) {
233
234 // Update the_host_buffer and copy of the_buffer
235 for (j = y1; j <= y2; j++) {
236 Screen_blit(the_host_buffer + i, the_buffer + i, VideoMonitor.x >> 3);
237 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 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * VideoMonitor.x);
245 i += bytes_per_row;
246 }
247 }
248
249 if (have_shm)
250 XShmPutImage(x_display, the_win, the_gc, img, 0, y1, 0, y1, VideoMonitor.x, height, 0);
251 else
252 XPutImage(x_display, the_win, the_gc, img, 0, y1, 0, y1, VideoMonitor.x, height);
253 }
254
255 mainBuffer.dirty = false;
256 }
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 const int first_page = find_next_page_set(page);
270 if (first_page >= mainBuffer.pageCount)
271 break;
272
273 page = find_next_page_clear(first_page);
274 PFLAG_CLEAR_RANGE(first_page, page);
275
276 // 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 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
323 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
324 i += bytes_per_row;
325 }
326 }
327 mainBuffer.dirty = false;
328 }
329 #endif
330
331 #endif /* ENABLE_VOSF */
332
333 #endif /* VIDEO_VOSF_H */