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