1 /*
2  *  Copyright (C) 2003-2018  Anders Gavare.  All rights reserved.
3  *
4  *  Redistribution and use in source and binary forms, with or without
5  *  modification, are permitted provided that the following conditions are met:
6  *
7  *  1. Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  *  2. Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *  3. The name of the author may not be used to endorse or promote products
13  *     derived from this software without specific prior written permission.
14  *
15  *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  *  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  *  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  *  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  *  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  *  SUCH DAMAGE.
26  *
27  *
28  *  COMMENT: Generic framebuffer device
29  *
30  *	DECstation VFB01 monochrome framebuffer, 1024x864
31  *	DECstation VFB02 8-bit color framebuffer, 1024x864
32  *	DECstation Maxine, 1024x768 8-bit color
33  *	Playstation 2 (24-bit color)
34  *	Generic (any resolution, several bit depths possible, useful for
35  *		testmachines)
36  *
37  *
38  *  TODO:  This should actually be independent of X11, but that
39  *         might be too hard to do right now.
40  *
41  *  TODO:  playstation 2 pixels are stored in another format, actually
42  */
43 
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include "console.h"
49 #include "cpu.h"
50 #include "devices.h"
51 #include "machine.h"
52 #include "memory.h"
53 #include "misc.h"
54 #include "x11.h"
55 
56 #ifdef WITH_X11
57 #include <X11/Xlib.h>
58 #include <X11/Xos.h>
59 #include <X11/Xutil.h>
60 #endif
61 
62 
63 #define	FB_TICK_SHIFT		19
64 
65 
66 /*  #define FB_DEBUG  */
67 
68 /*
69  *  set_grayscale_palette():
70  *
71  *  Fill d->rgb_palette with grayscale values. ncolors should
72  *  be something like 2, 4, 16, or 256.
73  */
set_grayscale_palette(struct vfb_data * d,int ncolors)74 void set_grayscale_palette(struct vfb_data *d, int ncolors)
75 {
76 	int i, gray;
77 
78 	for (i=0; i<256; i++) {
79 		gray = 255*i/(ncolors-1);
80 		d->rgb_palette[i*3 + 0] = gray;
81 		d->rgb_palette[i*3 + 1] = gray;
82 		d->rgb_palette[i*3 + 2] = gray;
83 	}
84 }
85 
86 
87 /*
88  *  set_blackwhite_palette():
89  *
90  *  Set color 0 = black, all others to white.
91  */
set_blackwhite_palette(struct vfb_data * d,int ncolors)92 void set_blackwhite_palette(struct vfb_data *d, int ncolors)
93 {
94 	int i, gray;
95 
96 	for (i=0; i<256; i++) {
97 		gray = i==0? 0 : 255;
98 		d->rgb_palette[i*3 + 0] = gray;
99 		d->rgb_palette[i*3 + 1] = gray;
100 		d->rgb_palette[i*3 + 2] = gray;
101 	}
102 }
103 
104 
set_title(struct vfb_data * d)105 static void set_title(struct vfb_data *d)
106 {
107 	snprintf(d->title, sizeof(d->title),"GXemul: %ix%ix%i %s framebuffer",
108 	    d->visible_xsize, d->visible_ysize, d->bit_depth, d->name);
109 	d->title[sizeof(d->title)-1] = '\0';
110 }
111 
112 
113 /*
114  *  dev_fb_resize():
115  *
116  *  Resize a framebuffer window. (This functionality is probably a bit buggy,
117  *  because I didn't think of including it from the start.)
118  *
119  *  SUPER-IMPORTANT: Anyone who resizes a framebuffer by calling this function
120  *  must also clear all dyntrans address translations manually, in all cpus
121  *  which might have access to the framebuffer!
122  */
dev_fb_resize(struct vfb_data * d,int new_xsize,int new_ysize)123 void dev_fb_resize(struct vfb_data *d, int new_xsize, int new_ysize)
124 {
125 	unsigned char *new_framebuffer;
126 	int y, new_bytes_per_line;
127 	size_t size;
128 
129 	if (d == NULL) {
130 		fatal("dev_fb_resize(): d == NULL\n");
131 		return;
132 	}
133 
134 	if (new_xsize < 10 || new_ysize < 10) {
135 		fatal("dev_fb_resize(): size too small.\n");
136 		exit(1);
137 	}
138 
139 	new_bytes_per_line = new_xsize * d->bit_depth / 8;
140 	size = new_ysize * new_bytes_per_line;
141 
142 	CHECK_ALLOCATION(new_framebuffer = (unsigned char *) malloc(size));
143 
144 	/*  Copy the old framebuffer to the new:  */
145 	if (d->framebuffer != NULL) {
146 		for (y=0; y<new_ysize; y++) {
147 			size_t fromofs = d->bytes_per_line * y;
148 			size_t toofs = new_bytes_per_line * y;
149 			size_t len_to_copy = d->bytes_per_line <
150 			    new_bytes_per_line? d->bytes_per_line
151 			    : new_bytes_per_line;
152 			memset(new_framebuffer + toofs, 0, new_bytes_per_line);
153 			if (y < d->x11_ysize)
154 				memmove(new_framebuffer + toofs,
155 				    d->framebuffer + fromofs, len_to_copy);
156 		}
157 
158 		free(d->framebuffer);
159 	}
160 
161 	d->framebuffer = new_framebuffer;
162 	d->framebuffer_size = size;
163 
164 	if (new_xsize > d->xsize || new_ysize > d->ysize) {
165 		d->update_x1 = d->update_y1 = 0;
166 		d->update_x2 = new_xsize - 1;
167 		d->update_y2 = new_ysize - 1;
168 	}
169 
170 	d->bytes_per_line = new_bytes_per_line;
171 	d->xsize = d->visible_xsize = new_xsize;
172 	d->ysize = d->visible_ysize = new_ysize;
173 
174 	d->x11_xsize = d->xsize / d->vfb_scaledown;
175 	d->x11_ysize = d->ysize / d->vfb_scaledown;
176 
177 	memory_device_update_data(d->memory, d, d->framebuffer);
178 
179 	set_title(d);
180 
181 #ifdef WITH_X11
182 	if (d->fb_window != NULL) {
183 		x11_fb_resize(d->fb_window, d->x11_xsize, d->x11_ysize);
184 		x11_set_standard_properties(d->fb_window, d->title);
185 	}
186 #endif
187 }
188 
189 
190 /*
191  *  dev_fb_setcursor():
192  */
dev_fb_setcursor(struct vfb_data * d,int cursor_x,int cursor_y,int on,int cursor_xsize,int cursor_ysize)193 void dev_fb_setcursor(struct vfb_data *d, int cursor_x, int cursor_y, int on,
194 	int cursor_xsize, int cursor_ysize)
195 {
196 	if (cursor_x < 0)
197 		cursor_x = 0;
198 	if (cursor_y < 0)
199 		cursor_y = 0;
200 	if (cursor_x + cursor_xsize >= d->xsize)
201 		cursor_x = d->xsize - cursor_xsize;
202 	if (cursor_y + cursor_ysize >= d->ysize)
203 		cursor_y = d->ysize - cursor_ysize;
204 
205 #ifdef WITH_X11
206 	if (d->fb_window != NULL) {
207 		d->fb_window->cursor_x      = cursor_x;
208 		d->fb_window->cursor_y      = cursor_y;
209 		d->fb_window->cursor_on     = on;
210 		d->fb_window->cursor_xsize  = cursor_xsize;
211 		d->fb_window->cursor_ysize  = cursor_ysize;
212 	}
213 #endif
214 
215 	/*  debug("dev_fb_setcursor(%i,%i, size %i,%i, on=%i)\n",
216 	    cursor_x, cursor_y, cursor_xsize, cursor_ysize, on);  */
217 }
218 
219 
220 /*
221  *  framebuffer_blockcopyfill():
222  *
223  *  This function should be used by devices that are capable of doing
224  *  block copy/fill.
225  *
226  *  If fillflag is non-zero, then fill_[rgb] should contain the color
227  *  with which to fill. (In 8-bit mode, only fill_r is used.)
228  *
229  *  If fillflag is zero, copy mode is used, and from_[xy] should contain
230  *  the offset on the framebuffer where we should copy from.
231  *
232  *  NOTE:  Overlapping copies are undefined!
233  */
framebuffer_blockcopyfill(struct vfb_data * d,int fillflag,int fill_r,int fill_g,int fill_b,int x1,int y1,int x2,int y2,int from_x,int from_y)234 void framebuffer_blockcopyfill(struct vfb_data *d, int fillflag, int fill_r,
235 	int fill_g, int fill_b, int x1, int y1, int x2, int y2,
236 	int from_x, int from_y)
237 {
238 	int x, y;
239 	size_t from_ofs, dest_ofs, linelen;
240 
241 	if (fillflag)
242 		debug("framebuffer_blockcopyfill(FILL, %i,%i, %i,%i, "
243 		    "color %i,%i,%i)\n", x1,y1, x2,y2, fill_r, fill_g, fill_b);
244 	else
245 		debug("framebuffer_blockcopyfill(COPY, %i,%i, %i,%i, from "
246 		    "%i,%i)\n", x1,y1, x2,y2, from_x,from_y);
247 
248 	/*  Clip x:  */
249 	if (x1 < 0)		x1 = 0;
250 	if (x1 >= d->xsize)	x1 = d->xsize-1;
251 	if (x2 < 0)		x2 = 0;
252 	if (x2 >= d->xsize)	x2 = d->xsize-1;
253 
254 	dest_ofs = d->bytes_per_line * y1 + (d->bit_depth/8) * x1;
255 	linelen = (x2-x1 + 1) * (d->bit_depth/8);
256 	/*  NOTE: linelen is nr of bytes, not pixels  */
257 
258 	if (fillflag) {
259 		for (y=y1; y<=y2; y++) {
260 			if (y>=0 && y<d->ysize) {
261 				unsigned char *buf =
262 				    d->framebuffer + dest_ofs;
263 
264 				if (d->bit_depth == 24) {
265 					for (x=0; x<(ssize_t)linelen && x <
266 					    (int) sizeof(buf); x += 3) {
267 						buf[x] = fill_r;
268 						buf[x+1] = fill_g;
269 						buf[x+2] = fill_b;
270 					}
271 				} else if (d->bit_depth == 8) {
272 					memset(buf, fill_r, linelen);
273 				} else {
274 					fatal("Unimplemented bit-depth (%i)"
275 					    " for fb fill\n", d->bit_depth);
276 					exit(1);
277 				}
278 			}
279 
280 			dest_ofs += d->bytes_per_line;
281 		}
282 	} else {
283 		from_ofs = d->bytes_per_line * from_y +
284 		    (d->bit_depth/8) * from_x;
285 		for (y=y1; y<=y2; y++) {
286 			if (y >= 0 && y < d->ysize) {
287 				if (from_y >= 0 && from_y < d->ysize)
288 					memmove(d->framebuffer + dest_ofs,
289 					    d->framebuffer + from_ofs, linelen);
290 				else
291 					memset(d->framebuffer + dest_ofs,
292 					    0, linelen);
293 			}
294 			from_y ++;
295 			from_ofs += d->bytes_per_line;
296 			dest_ofs += d->bytes_per_line;
297 		}
298 	}
299 
300 	if (x1 < d->update_x1 || d->update_x1 == -1)	d->update_x1 = x1;
301 	if (x1 > d->update_x2 || d->update_x2 == -1)	d->update_x2 = x1;
302 	if (x2 < d->update_x1 || d->update_x1 == -1)	d->update_x1 = x2;
303 	if (x2 > d->update_x2 || d->update_x2 == -1)	d->update_x2 = x2;
304 
305 	if (y1 < d->update_y1 || d->update_y1 == -1)	d->update_y1 = y1;
306 	if (y1 > d->update_y2 || d->update_y2 == -1)	d->update_y2 = y1;
307 	if (y2 < d->update_y1 || d->update_y1 == -1)	d->update_y1 = y2;
308 	if (y2 > d->update_y2 || d->update_y2 == -1)	d->update_y2 = y2;
309 }
310 
311 
312 #ifdef WITH_X11
313 
314 #define	REDRAW	redraw_fallback
315 #include "fb_include.cc"
316 #undef REDRAW
317 
318 #define FB_24
319 #define REDRAW	redraw_24
320 #include "fb_include.cc"
321 #undef REDRAW
322 #undef FB_24
323 #define FB_16
324 #define REDRAW	redraw_16
325 #include "fb_include.cc"
326 #undef FB_16
327 #undef REDRAW
328 #define FB_15
329 #define REDRAW	redraw_15
330 #include "fb_include.cc"
331 #undef REDRAW
332 #undef FB_15
333 
334 #define FB_BO
335 #define FB_24
336 #define REDRAW	redraw_24_bo
337 #include "fb_include.cc"
338 #undef REDRAW
339 #undef FB_24
340 #define FB_16
341 #define REDRAW	redraw_16_bo
342 #include "fb_include.cc"
343 #undef FB_16
344 #undef REDRAW
345 #define FB_15
346 #define REDRAW	redraw_15_bo
347 #include "fb_include.cc"
348 #undef REDRAW
349 #undef FB_15
350 #undef FB_BO
351 
352 #define FB_SCALEDOWN
353 
354 #define	REDRAW	redraw_fallback_sd
355 #include "fb_include.cc"
356 #undef REDRAW
357 
358 #define FB_24
359 #define REDRAW	redraw_24_sd
360 #include "fb_include.cc"
361 #undef REDRAW
362 #undef FB_24
363 #define FB_16
364 #define REDRAW	redraw_16_sd
365 #include "fb_include.cc"
366 #undef FB_16
367 #undef REDRAW
368 #define FB_15
369 #define REDRAW	redraw_15_sd
370 #include "fb_include.cc"
371 #undef REDRAW
372 #undef FB_15
373 
374 #define FB_BO
375 #define FB_24
376 #define REDRAW	redraw_24_bo_sd
377 #include "fb_include.cc"
378 #undef REDRAW
379 #undef FB_24
380 #define FB_16
381 #define REDRAW	redraw_16_bo_sd
382 #include "fb_include.cc"
383 #undef FB_16
384 #undef REDRAW
385 #define FB_15
386 #define REDRAW	redraw_15_bo_sd
387 #include "fb_include.cc"
388 #undef REDRAW
389 #undef FB_15
390 #undef FB_BO
391 
392 void (*redraw[2 * 4 * 2])(struct vfb_data *, int, int) = {
393 	redraw_fallback, redraw_fallback,
394 	redraw_15, redraw_15_bo,
395 	redraw_16, redraw_16_bo,
396 	redraw_24, redraw_24_bo,
397 	redraw_fallback_sd, redraw_fallback_sd,
398 	redraw_15_sd, redraw_15_bo_sd,
399 	redraw_16_sd, redraw_16_bo_sd,
400 	redraw_24_sd, redraw_24_bo_sd  };
401 
402 #endif	/*  WITH_X11  */
403 
404 
DEVICE_TICK(fb)405 DEVICE_TICK(fb)
406 {
407 	struct vfb_data *d = (struct vfb_data *) extra;
408 #ifdef WITH_X11
409 	int need_to_flush_x11 = 0;
410 	int need_to_redraw_cursor = 0;
411 #endif
412 
413 	if (!cpu->machine->x11_md.in_use)
414 		return;
415 
416 	do {
417 		uint64_t high, low = (uint64_t)(int64_t) -1;
418 		int x, y;
419 
420 		memory_device_dyntrans_access(cpu, cpu->mem,
421 		    extra, &low, &high);
422 		if ((int64_t)low == -1)
423 			break;
424 
425 		/*  printf("low=%016llx high=%016llx\n",
426 		    (long long)low, (long long)high);  */
427 
428 		x = (low % d->bytes_per_line) * 8 / d->bit_depth;
429 		y = low / d->bytes_per_line;
430 		if (x < d->update_x1 || d->update_x1 == -1)
431 			d->update_x1 = x;
432 		if (x > d->update_x2 || d->update_x2 == -1)
433 			d->update_x2 = x;
434 		if (y < d->update_y1 || d->update_y1 == -1)
435 			d->update_y1 = y;
436 		if (y > d->update_y2 || d->update_y2 == -1)
437 			d->update_y2 = y;
438 
439 		x = ((low+7) % d->bytes_per_line) * 8 / d->bit_depth;
440 		y = (low+7) / d->bytes_per_line;
441 		if (x < d->update_x1 || d->update_x1 == -1)
442 			d->update_x1 = x;
443 		if (x > d->update_x2 || d->update_x2 == -1)
444 			d->update_x2 = x;
445 		if (y < d->update_y1 || d->update_y1 == -1)
446 			d->update_y1 = y;
447 		if (y > d->update_y2 || d->update_y2 == -1)
448 			d->update_y2 = y;
449 
450 		x = (high % d->bytes_per_line) * 8 / d->bit_depth;
451 		y = high / d->bytes_per_line;
452 		if (x < d->update_x1 || d->update_x1 == -1)
453 			d->update_x1 = x;
454 		if (x > d->update_x2 || d->update_x2 == -1)
455 			d->update_x2 = x;
456 		if (y < d->update_y1 || d->update_y1 == -1)
457 			d->update_y1 = y;
458 		if (y > d->update_y2 || d->update_y2 == -1)
459 			d->update_y2 = y;
460 
461 		x = ((high+7) % d->bytes_per_line) * 8 / d->bit_depth;
462 		y = (high+7) / d->bytes_per_line;
463 		if (x < d->update_x1 || d->update_x1 == -1)
464 			d->update_x1 = x;
465 		if (x > d->update_x2 || d->update_x2 == -1)
466 			d->update_x2 = x;
467 		if (y < d->update_y1 || d->update_y1 == -1)
468 			d->update_y1 = y;
469 		if (y > d->update_y2 || d->update_y2 == -1)
470 			d->update_y2 = y;
471 
472 		/*
473 		 *  An update covering more than one line will automatically
474 		 *  force an update of all the affected lines:
475 		 */
476 		if (d->update_y1 != d->update_y2) {
477 			d->update_x1 = 0;
478 			d->update_x2 = d->xsize-1;
479 		}
480 	} while (0);
481 
482 #ifdef WITH_X11
483 	/*  Do we need to redraw the cursor?  */
484 	if (d->fb_window->cursor_on != d->fb_window->OLD_cursor_on ||
485 	    d->fb_window->cursor_x != d->fb_window->OLD_cursor_x ||
486 	    d->fb_window->cursor_y != d->fb_window->OLD_cursor_y ||
487 	    d->fb_window->cursor_xsize != d->fb_window->OLD_cursor_xsize ||
488 	    d->fb_window->cursor_ysize != d->fb_window->OLD_cursor_ysize)
489 		need_to_redraw_cursor = 1;
490 
491 	if (d->update_x2 != -1) {
492 		if (((d->update_x1 >= d->fb_window->OLD_cursor_x &&
493 		      d->update_x1 < (d->fb_window->OLD_cursor_x +
494 		      d->fb_window->OLD_cursor_xsize)) ||
495 		     (d->update_x2 >= d->fb_window->OLD_cursor_x &&
496 		      d->update_x2 < (d->fb_window->OLD_cursor_x +
497 		      d->fb_window->OLD_cursor_xsize)) ||
498 		     (d->update_x1 <  d->fb_window->OLD_cursor_x &&
499 		      d->update_x2 >= (d->fb_window->OLD_cursor_x +
500 		      d->fb_window->OLD_cursor_xsize)) ) &&
501 		   ( (d->update_y1 >= d->fb_window->OLD_cursor_y &&
502 		      d->update_y1 < (d->fb_window->OLD_cursor_y +
503 		      d->fb_window->OLD_cursor_ysize)) ||
504 		     (d->update_y2 >= d->fb_window->OLD_cursor_y &&
505 		      d->update_y2 < (d->fb_window->OLD_cursor_y +
506 		      d->fb_window->OLD_cursor_ysize)) ||
507 		     (d->update_y1 <  d->fb_window->OLD_cursor_y &&
508 		      d->update_y2 >= (d->fb_window->OLD_cursor_y +
509 		     d->fb_window->OLD_cursor_ysize)) ) )
510 			need_to_redraw_cursor = 1;
511 	}
512 
513 	if (need_to_redraw_cursor) {
514 		/*  Remove old cursor, if any:  */
515 		if (d->fb_window->OLD_cursor_on) {
516 			XPutImage(d->fb_window->x11_display,
517 			    d->fb_window->x11_fb_window,
518 			    d->fb_window->x11_fb_gc, d->fb_window->fb_ximage,
519 			    d->fb_window->OLD_cursor_x/d->vfb_scaledown,
520 			    d->fb_window->OLD_cursor_y/d->vfb_scaledown,
521 			    d->fb_window->OLD_cursor_x/d->vfb_scaledown,
522 			    d->fb_window->OLD_cursor_y/d->vfb_scaledown,
523 			    d->fb_window->OLD_cursor_xsize/d->vfb_scaledown + 1,
524 			    d->fb_window->OLD_cursor_ysize/d->vfb_scaledown +1);
525 		}
526 	}
527 #endif
528 
529 	if (d->update_x2 != -1) {
530 #ifdef WITH_X11
531 		int y;
532 #endif
533 		int addr, addr2, q = d->vfb_scaledown;
534 
535 		if (d->update_x1 >= d->visible_xsize)
536 			d->update_x1 = d->visible_xsize - 1;
537 		if (d->update_x2 >= d->visible_xsize)
538 			d->update_x2 = d->visible_xsize - 1;
539 		if (d->update_y1 >= d->visible_ysize)
540 			d->update_y1 = d->visible_ysize - 1;
541 		if (d->update_y2 >= d->visible_ysize)
542 			d->update_y2 = d->visible_ysize - 1;
543 
544 		/*  Without these, we might miss the rightmost/bottom pixel:  */
545 		d->update_x2 += (q - 1);
546 		d->update_y2 += (q - 1);
547 
548 		d->update_x1 = d->update_x1 / q * q;
549 		d->update_x2 = d->update_x2 / q * q;
550 		d->update_y1 = d->update_y1 / q * q;
551 		d->update_y2 = d->update_y2 / q * q;
552 
553 		addr  = d->update_y1 * d->bytes_per_line +
554 		    d->update_x1 * d->bit_depth / 8;
555 		addr2 = d->update_y1 * d->bytes_per_line +
556 		    d->update_x2 * d->bit_depth / 8;
557 
558 #ifdef WITH_X11
559 		for (y=d->update_y1; y<=d->update_y2; y+=q) {
560 			d->redraw_func(d, addr, addr2 - addr);
561 			addr  += d->bytes_per_line * q;
562 			addr2 += d->bytes_per_line * q;
563 		}
564 
565 		XPutImage(d->fb_window->x11_display, d->fb_window->
566 		    x11_fb_window, d->fb_window->x11_fb_gc, d->fb_window->
567 		    fb_ximage, d->update_x1/d->vfb_scaledown, d->update_y1/
568 		    d->vfb_scaledown, d->update_x1/d->vfb_scaledown,
569 		    d->update_y1/d->vfb_scaledown,
570 		    (d->update_x2 - d->update_x1)/d->vfb_scaledown + 1,
571 		    (d->update_y2 - d->update_y1)/d->vfb_scaledown + 1);
572 
573 		need_to_flush_x11 = 1;
574 #endif
575 
576 		d->update_x1 = d->update_y1 = 99999;
577 		d->update_x2 = d->update_y2 = -1;
578 	}
579 
580 #ifdef WITH_X11
581 	if (need_to_redraw_cursor) {
582 		/*  Paint new cursor:  */
583 		if (d->fb_window->cursor_on) {
584 			x11_redraw_cursor(cpu->machine,
585 			    d->fb_window->fb_number);
586 			d->fb_window->OLD_cursor_on = d->fb_window->cursor_on;
587 			d->fb_window->OLD_cursor_x = d->fb_window->cursor_x;
588 			d->fb_window->OLD_cursor_y = d->fb_window->cursor_y;
589 			d->fb_window->OLD_cursor_xsize = d->fb_window->
590 			    cursor_xsize;
591 			d->fb_window->OLD_cursor_ysize = d->fb_window->
592 			    cursor_ysize;
593 			need_to_flush_x11 = 1;
594 		}
595 	}
596 #endif
597 
598 #ifdef WITH_X11
599 	if (need_to_flush_x11)
600 		XFlush(d->fb_window->x11_display);
601 #endif
602 
603 #if 0
604 
605 This is a hack used to produce raw ppm image dumps, which can then be
606 used to make movies, e.g. http://www.youtube.com/watch?v=Afh1ECLWac8
607 
608 {
609 	static struct timeval tv_last;
610 	static bool first = true;
611 
612 	struct timeval tv;
613 	gettimeofday(&tv, NULL);
614 
615 	if (first)
616 	{
617 		tv_last = tv;
618 		first = false;
619 	}
620 	else
621 	{
622 		double diff = (tv.tv_sec - tv_last.tv_sec) + (tv.tv_usec - tv_last.tv_usec) / 1000000.0;
623 		if (diff > 0.2)
624 		{
625 			static int outputNr = 0;
626 			outputNr ++;
627 			tv_last = tv;
628 
629 			char name[50];
630 			snprintf(name, sizeof(name), "gxemul-%06i.ppm", outputNr);
631 
632 			FILE *f = fopen(name, "w");
633 			if (f == NULL)
634 			{
635 				perror(name);
636 			}
637 			else
638 			{
639 				const int xsize = 640;
640 				const int ysize = 480;
641 				fprintf(f, "P6\n%i %i\n255\n", xsize, ysize);
642 				unsigned char buf[xsize*ysize*3];
643 				memset(buf, 0, xsize*ysize*3);
644 
645 				// Calculate scaledown:
646 				int scaledown = 1;
647 
648 				while (d->visible_xsize / scaledown > xsize ||
649 					d->visible_ysize / scaledown > ysize)
650 				{
651 					scaledown ++;
652 				}
653 
654 				// printf("scaledown = %i\n", scaledown);
655 
656 				int xofs = (xsize - d->visible_xsize / scaledown) / 2;
657 				int yofs = (ysize - d->visible_ysize / scaledown) / 2;
658 
659 				for (int y = 0; y < d->visible_ysize / scaledown; ++y)
660 				    for (int x = 0; x < d->visible_xsize / scaledown; ++x)
661 				    {
662 				    	int r = 0, g = 0, b = 0, n = 0;
663 				    	for (int suby = 0; suby < scaledown; ++suby)
664 				    	    for (int subx = 0; subx < scaledown; ++subx)
665 				    	    {
666 				    	    	++n;
667 				    	    	int rx = x * scaledown + subx;
668 				    	    	int ry = y * scaledown + suby;
669 				    	    	int i = (d->xsize * ry + rx) * d->bit_depth / 8;
670 
671 #if 0
672 						r += d->framebuffer[i+0];
673 						g += d->framebuffer[i+1];
674 						b += d->framebuffer[i+2];
675 #else
676 						int col = d->framebuffer[i];
677 						r += d->rgb_palette[col*3 + 0];
678 						g += d->rgb_palette[col*3 + 1];
679 						b += d->rgb_palette[col*3 + 2];
680 #endif
681 				    	    }
682 
683 					r /= n; g /= n; b /= n;
684 					int j = (y + yofs) * xsize + x + xofs;
685 					buf[j*3+0] = r;
686 					buf[j*3+1] = g;
687 					buf[j*3+2] = b;
688 				    }
689 
690 				fwrite(buf, 1, xsize*ysize*3, f);
691 				fclose(f);
692 			}
693 		}
694 	}
695 
696 }
697 
698 #endif
699 
700 
701 }
702 
703 
DEVICE_ACCESS(fb)704 DEVICE_ACCESS(fb)
705 {
706 	struct vfb_data *d = (struct vfb_data *) extra;
707 	size_t i;
708 
709 #ifdef FB_DEBUG
710 	if (writeflag == MEM_WRITE) { if (data[0]) {
711 		fatal("[ dev_fb: write  to addr=%08lx, data = ",
712 		    (long)relative_addr);
713 		for (i=0; i<len; i++)
714 			fatal("%02x ", data[i]);
715 		fatal("]\n");
716 	} else {
717 		fatal("[ dev_fb: read from addr=%08lx, data = ",
718 		    (long)relative_addr);
719 		for (i=0; i<len; i++)
720 			fatal("%02x ", d->framebuffer[relative_addr + i]);
721 		fatal("]\n");
722 	}
723 #endif
724 
725 	if (relative_addr >= d->framebuffer_size)
726 		return 0;
727 
728 	/*  See if a write actually modifies the framebuffer contents:  */
729 	if (writeflag == MEM_WRITE) {
730 		for (i=0; i<len; i++) {
731 			if (data[i] != d->framebuffer[relative_addr + i])
732 				break;
733 
734 			/*  If all bytes are equal to what is already stored
735 			    in the framebuffer, then simply return:  */
736 			if (i == len-1)
737 				return 1;
738 		}
739 	}
740 
741 	/*
742 	 *  If the framebuffer is modified, then we should keep a track
743 	 *  of which area(s) we modify, so that the display isn't updated
744 	 *  unnecessarily.
745 	 */
746 	if (writeflag == MEM_WRITE && cpu->machine->x11_md.in_use) {
747 		int x, y, x2,y2;
748 
749 		x = (relative_addr % d->bytes_per_line) * 8 / d->bit_depth;
750 		y = relative_addr / d->bytes_per_line;
751 		x2 = ((relative_addr + len) % d->bytes_per_line)
752 		    * 8 / d->bit_depth;
753 		y2 = (relative_addr + len) / d->bytes_per_line;
754 
755 		if (x < d->update_x1 || d->update_x1 == -1)
756 			d->update_x1 = x;
757 		if (x > d->update_x2 || d->update_x2 == -1)
758 			d->update_x2 = x;
759 
760 		if (y < d->update_y1 || d->update_y1 == -1)
761 			d->update_y1 = y;
762 		if (y > d->update_y2 || d->update_y2 == -1)
763 			d->update_y2 = y;
764 
765 		if (x2 < d->update_x1 || d->update_x1 == -1)
766 			d->update_x1 = x2;
767 		if (x2 > d->update_x2 || d->update_x2 == -1)
768 			d->update_x2 = x2;
769 
770 		if (y2 < d->update_y1 || d->update_y1 == -1)
771 			d->update_y1 = y2;
772 		if (y2 > d->update_y2 || d->update_y2 == -1)
773 			d->update_y2 = y2;
774 
775 		/*
776 		 *  An update covering more than one line will automatically
777 		 *  force an update of all the affected lines:
778 		 */
779 		if (y != y2) {
780 			d->update_x1 = 0;
781 			d->update_x2 = d->xsize-1;
782 		}
783 	}
784 
785 	/*
786 	 *  Read from/write to the framebuffer:
787 	 *  (TODO: take the color_plane_mask into account)
788 	 *
789 	 *  Calling memcpy() is probably overkill, as it usually is just one
790 	 *  or a few bytes that are read/written at a time.
791 	 */
792 	if (writeflag == MEM_WRITE) {
793 		if (len > 8)
794 			memcpy(d->framebuffer + relative_addr, data, len);
795 		else {
796 			for (i=0; i<len; i++)
797 				d->framebuffer[relative_addr + i] = data[i];
798 		}
799 	} else {
800 		if (len > 8)
801 			memcpy(data, d->framebuffer + relative_addr, len);
802 		else {
803 			for (i=0; i<len; i++)
804 				data[i] = d->framebuffer[relative_addr + i];
805 		}
806 	}
807 
808 	return 1;
809 }
810 
811 
812 /*
813  *  dev_fb_init():
814  *
815  *  This function is big and ugly, but the point is to initialize a framebuffer
816  *  device. :-)
817  *
818  *  visible_xsize and visible_ysize are the sizes of the visible display area.
819  *  xsize and ysize tell how much memory is actually allocated (for example
820  *  visible_xsize could be 640, but xsize could be 1024, for better alignment).
821  *
822  *  vfb_type is useful for selecting special features.
823  *
824  *  type = VFB_GENERIC is the most useful type, especially when bit_depth = 24.
825  *
826  *  VFB_DEC_VFB01, _VFB02, and VFB_DEC_MAXINE are DECstation specific.
827  *
828  *  VFB_HPC is like generic, but the color encoding is done as on HPCmips
829  *  and Dreamcast.
830  *
831  *  If bit_depth = -15 (note the minus sign), then a special hack is used for
832  *  the Playstation Portable's 5-bit R, 5-bit G, 5-bit B.
833  */
834 struct vfb_data *dev_fb_init(struct machine *machine, struct memory *mem,
835 	uint64_t baseaddr, int vfb_type, int visible_xsize, int visible_ysize,
836 	int xsize, int ysize, int bit_depth, const char *name)
837 {
838 	struct vfb_data *d;
839 	size_t size, nlen;
840 	int flags;
841 	int reverse_start = 0;
842 	char *name2;
843 
844 	CHECK_ALLOCATION(d = (struct vfb_data *) malloc(sizeof(struct vfb_data)));
845 	memset(d, 0, sizeof(struct vfb_data));
846 
847 	if (vfb_type & VFB_REVERSE_START) {
848 		vfb_type &= ~VFB_REVERSE_START;
849 		reverse_start = 1;
850 	}
851 
852 	d->memory = mem;
853 	d->vfb_type = vfb_type;
854 
855 	/*  Defaults:  */
856 	d->xsize = xsize;  d->visible_xsize = visible_xsize;
857 	d->ysize = ysize;  d->visible_ysize = visible_ysize;
858 
859 	d->bit_depth = bit_depth;
860 
861 	if (bit_depth == 15) {
862 		d->color32k = 1;
863 		bit_depth = d->bit_depth = 16;
864 	} else if (bit_depth == -15) {
865 		d->psp_15bit = 1;
866 		bit_depth = d->bit_depth = 16;
867 	}
868 
869 	/*  Specific types:  */
870 	switch (vfb_type) {
871 	case VFB_DEC_VFB01:
872 		/*  DECstation VFB01 (monochrome)  */
873 		d->xsize = 2048;  d->visible_xsize = 1024;
874 		d->ysize = 1024;  d->visible_ysize = 864;
875 		d->bit_depth = 1;
876 		break;
877 	case VFB_DEC_VFB02:
878 		/*  DECstation VFB02 (color)  */
879 		d->xsize = 1024;  d->visible_xsize = 1024;
880 		d->ysize = 1024;  d->visible_ysize = 864;
881 		d->bit_depth = 8;
882 		break;
883 	case VFB_DEC_MAXINE:
884 		/*  DECstation Maxine (1024x768x8)  */
885 		d->xsize = 1024; d->visible_xsize = d->xsize;
886 		d->ysize = 768;  d->visible_ysize = d->ysize;
887 		d->bit_depth = 8;
888 		break;
889 	case VFB_PLAYSTATION2:
890 		/*  Playstation 2  */
891 		d->xsize = xsize;  d->visible_xsize = d->xsize;
892 		d->ysize = ysize;  d->visible_ysize = d->ysize;
893 		d->bit_depth = 24;
894 		break;
895 	}
896 
897 	if (d->bit_depth == 2 || d->bit_depth == 4)
898 		set_grayscale_palette(d, 1 << d->bit_depth);
899 	else if (d->bit_depth == 8 || d->bit_depth == 1)
900 		set_blackwhite_palette(d, 1 << d->bit_depth);
901 
902 	d->vfb_scaledown = machine->x11_md.scaledown;
903 
904 	d->bytes_per_line = d->xsize * d->bit_depth / 8;
905 	size = d->ysize * d->bytes_per_line;
906 
907 	CHECK_ALLOCATION(d->framebuffer = (unsigned char *) malloc(size));
908 
909 	/*  Clear the framebuffer (all black pixels):  */
910 	d->framebuffer_size = size;
911 	memset(d->framebuffer, reverse_start? 255 : 0, size);
912 
913 	d->x11_xsize = d->visible_xsize / d->vfb_scaledown;
914 	d->x11_ysize = d->visible_ysize / d->vfb_scaledown;
915 
916 	/*  Only "update" from the start if we need to fill with white.  */
917 	/*  (The Ximage will be black from the start anyway.)  */
918 	if (reverse_start) {
919 		d->update_x1 = d->update_y1 = 0;
920 		d->update_x2 = d->xsize - 1;
921 		d->update_y2 = d->ysize - 1;
922 	} else {
923 		d->update_x1 = d->update_y1 = 99999;
924 		d->update_x2 = d->update_y2 = -1;
925 	}
926 
927 	CHECK_ALLOCATION(d->name = strdup(name));
928 	set_title(d);
929 
930 #ifdef WITH_X11
931 	if (machine->x11_md.in_use) {
932 		int i = 0;
933 		d->fb_window = x11_fb_init(d->x11_xsize, d->x11_ysize,
934 		    d->title, machine->x11_md.scaledown, machine);
935 		switch (d->fb_window->x11_screen_depth) {
936 		case 15: i = 2; break;
937 		case 16: i = 4; break;
938 		case 24: i = 6; break;
939 		}
940 		if (d->fb_window->fb_ximage->byte_order)
941 			i ++;
942 		if (d->vfb_scaledown > 1)
943 			i += 8;
944 		d->redraw_func = redraw[i];
945 	} else
946 #endif
947 		d->fb_window = NULL;
948 
949 	nlen = strlen(name) + 10;
950 	CHECK_ALLOCATION(name2 = (char *) malloc(nlen));
951 
952 	snprintf(name2, nlen, "fb [%s]", name);
953 
954 	flags = DM_DEFAULT;
955 	if ((baseaddr & 0xfff) == 0)
956 		flags = DM_DYNTRANS_OK | DM_DYNTRANS_WRITE_OK;
957 
958 	flags |= DM_READS_HAVE_NO_SIDE_EFFECTS;
959 
960 	memory_device_register(mem, name2, baseaddr, size, dev_fb_access,
961 	    d, flags, d->framebuffer);
962 
963 	machine_add_tickfunction(machine, dev_fb_tick, d, FB_TICK_SHIFT);
964 
965 	return d;
966 }
967 
968