Sparc32: fix SDL zooming with TCX
[qemu-kvm/fedora.git] / hw / tcx.c
blob76acae809da6e1dfe7ab489363efe3b202934928
1 /*
2 * QEMU TCX Frame buffer
4 * Copyright (c) 2003-2005 Fabrice Bellard
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
25 #include "sun4m.h"
26 #include "console.h"
27 #include "pixel_ops.h"
28 #include "sysbus.h"
30 #define MAXX 1024
31 #define MAXY 768
32 #define TCX_DAC_NREGS 16
33 #define TCX_THC_NREGS_8 0x081c
34 #define TCX_THC_NREGS_24 0x1000
35 #define TCX_TEC_NREGS 0x1000
37 typedef struct TCXState {
38 SysBusDevice busdev;
39 target_phys_addr_t addr;
40 DisplayState *ds;
41 uint8_t *vram;
42 uint32_t *vram24, *cplane;
43 ram_addr_t vram_offset, vram24_offset, cplane_offset;
44 uint16_t width, height, depth;
45 uint8_t r[256], g[256], b[256];
46 uint32_t palette[256];
47 uint8_t dac_index, dac_state;
48 } TCXState;
50 static void tcx_screen_dump(void *opaque, const char *filename);
51 static void tcx24_screen_dump(void *opaque, const char *filename);
53 static void tcx_set_dirty(TCXState *s)
55 unsigned int i;
57 for (i = 0; i < MAXX * MAXY; i += TARGET_PAGE_SIZE) {
58 cpu_physical_memory_set_dirty(s->vram_offset + i);
62 static void tcx24_set_dirty(TCXState *s)
64 unsigned int i;
66 for (i = 0; i < MAXX * MAXY * 4; i += TARGET_PAGE_SIZE) {
67 cpu_physical_memory_set_dirty(s->vram24_offset + i);
68 cpu_physical_memory_set_dirty(s->cplane_offset + i);
72 static void update_palette_entries(TCXState *s, int start, int end)
74 int i;
75 for(i = start; i < end; i++) {
76 switch(ds_get_bits_per_pixel(s->ds)) {
77 default:
78 case 8:
79 s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
80 break;
81 case 15:
82 s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
83 break;
84 case 16:
85 s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
86 break;
87 case 32:
88 if (is_surface_bgr(s->ds->surface))
89 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
90 else
91 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
92 break;
95 if (s->depth == 24) {
96 tcx24_set_dirty(s);
97 } else {
98 tcx_set_dirty(s);
102 static void tcx_draw_line32(TCXState *s1, uint8_t *d,
103 const uint8_t *s, int width)
105 int x;
106 uint8_t val;
107 uint32_t *p = (uint32_t *)d;
109 for(x = 0; x < width; x++) {
110 val = *s++;
111 *p++ = s1->palette[val];
115 static void tcx_draw_line16(TCXState *s1, uint8_t *d,
116 const uint8_t *s, int width)
118 int x;
119 uint8_t val;
120 uint16_t *p = (uint16_t *)d;
122 for(x = 0; x < width; x++) {
123 val = *s++;
124 *p++ = s1->palette[val];
128 static void tcx_draw_line8(TCXState *s1, uint8_t *d,
129 const uint8_t *s, int width)
131 int x;
132 uint8_t val;
134 for(x = 0; x < width; x++) {
135 val = *s++;
136 *d++ = s1->palette[val];
141 XXX Could be much more optimal:
142 * detect if line/page/whole screen is in 24 bit mode
143 * if destination is also BGR, use memcpy
145 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
146 const uint8_t *s, int width,
147 const uint32_t *cplane,
148 const uint32_t *s24)
150 int x, bgr, r, g, b;
151 uint8_t val, *p8;
152 uint32_t *p = (uint32_t *)d;
153 uint32_t dval;
155 bgr = is_surface_bgr(s1->ds->surface);
156 for(x = 0; x < width; x++, s++, s24++) {
157 if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
158 // 24-bit direct, BGR order
159 p8 = (uint8_t *)s24;
160 p8++;
161 b = *p8++;
162 g = *p8++;
163 r = *p8++;
164 if (bgr)
165 dval = rgb_to_pixel32bgr(r, g, b);
166 else
167 dval = rgb_to_pixel32(r, g, b);
168 } else {
169 val = *s;
170 dval = s1->palette[val];
172 *p++ = dval;
176 static inline int check_dirty(ram_addr_t page, ram_addr_t page24,
177 ram_addr_t cpage)
179 int ret;
180 unsigned int off;
182 ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
183 for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
184 ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
185 ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
187 return ret;
190 static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
191 ram_addr_t page_max, ram_addr_t page24,
192 ram_addr_t cpage)
194 cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
195 VGA_DIRTY_FLAG);
196 page_min -= ts->vram_offset;
197 page_max -= ts->vram_offset;
198 cpu_physical_memory_reset_dirty(page24 + page_min * 4,
199 page24 + page_max * 4 + TARGET_PAGE_SIZE,
200 VGA_DIRTY_FLAG);
201 cpu_physical_memory_reset_dirty(cpage + page_min * 4,
202 cpage + page_max * 4 + TARGET_PAGE_SIZE,
203 VGA_DIRTY_FLAG);
206 /* Fixed line length 1024 allows us to do nice tricks not possible on
207 VGA... */
208 static void tcx_update_display(void *opaque)
210 TCXState *ts = opaque;
211 ram_addr_t page, page_min, page_max;
212 int y, y_start, dd, ds;
213 uint8_t *d, *s;
214 void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
216 if (ds_get_bits_per_pixel(ts->ds) == 0)
217 return;
218 page = ts->vram_offset;
219 y_start = -1;
220 page_min = -1;
221 page_max = 0;
222 d = ds_get_data(ts->ds);
223 s = ts->vram;
224 dd = ds_get_linesize(ts->ds);
225 ds = 1024;
227 switch (ds_get_bits_per_pixel(ts->ds)) {
228 case 32:
229 f = tcx_draw_line32;
230 break;
231 case 15:
232 case 16:
233 f = tcx_draw_line16;
234 break;
235 default:
236 case 8:
237 f = tcx_draw_line8;
238 break;
239 case 0:
240 return;
243 for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
244 if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
245 if (y_start < 0)
246 y_start = y;
247 if (page < page_min)
248 page_min = page;
249 if (page > page_max)
250 page_max = page;
251 f(ts, d, s, ts->width);
252 d += dd;
253 s += ds;
254 f(ts, d, s, ts->width);
255 d += dd;
256 s += ds;
257 f(ts, d, s, ts->width);
258 d += dd;
259 s += ds;
260 f(ts, d, s, ts->width);
261 d += dd;
262 s += ds;
263 } else {
264 if (y_start >= 0) {
265 /* flush to display */
266 dpy_update(ts->ds, 0, y_start,
267 ts->width, y - y_start);
268 y_start = -1;
270 d += dd * 4;
271 s += ds * 4;
274 if (y_start >= 0) {
275 /* flush to display */
276 dpy_update(ts->ds, 0, y_start,
277 ts->width, y - y_start);
279 /* reset modified pages */
280 if (page_max >= page_min) {
281 cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
282 VGA_DIRTY_FLAG);
286 static void tcx24_update_display(void *opaque)
288 TCXState *ts = opaque;
289 ram_addr_t page, page_min, page_max, cpage, page24;
290 int y, y_start, dd, ds;
291 uint8_t *d, *s;
292 uint32_t *cptr, *s24;
294 if (ds_get_bits_per_pixel(ts->ds) != 32)
295 return;
296 page = ts->vram_offset;
297 page24 = ts->vram24_offset;
298 cpage = ts->cplane_offset;
299 y_start = -1;
300 page_min = -1;
301 page_max = 0;
302 d = ds_get_data(ts->ds);
303 s = ts->vram;
304 s24 = ts->vram24;
305 cptr = ts->cplane;
306 dd = ds_get_linesize(ts->ds);
307 ds = 1024;
309 for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
310 page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
311 if (check_dirty(page, page24, cpage)) {
312 if (y_start < 0)
313 y_start = y;
314 if (page < page_min)
315 page_min = page;
316 if (page > page_max)
317 page_max = page;
318 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
319 d += dd;
320 s += ds;
321 cptr += ds;
322 s24 += ds;
323 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
324 d += dd;
325 s += ds;
326 cptr += ds;
327 s24 += ds;
328 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
329 d += dd;
330 s += ds;
331 cptr += ds;
332 s24 += ds;
333 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
334 d += dd;
335 s += ds;
336 cptr += ds;
337 s24 += ds;
338 } else {
339 if (y_start >= 0) {
340 /* flush to display */
341 dpy_update(ts->ds, 0, y_start,
342 ts->width, y - y_start);
343 y_start = -1;
345 d += dd * 4;
346 s += ds * 4;
347 cptr += ds * 4;
348 s24 += ds * 4;
351 if (y_start >= 0) {
352 /* flush to display */
353 dpy_update(ts->ds, 0, y_start,
354 ts->width, y - y_start);
356 /* reset modified pages */
357 if (page_max >= page_min) {
358 reset_dirty(ts, page_min, page_max, page24, cpage);
362 static void tcx_invalidate_display(void *opaque)
364 TCXState *s = opaque;
366 tcx_set_dirty(s);
367 qemu_console_resize(s->ds, s->width, s->height);
370 static void tcx24_invalidate_display(void *opaque)
372 TCXState *s = opaque;
374 tcx_set_dirty(s);
375 tcx24_set_dirty(s);
376 qemu_console_resize(s->ds, s->width, s->height);
379 static void tcx_save(QEMUFile *f, void *opaque)
381 TCXState *s = opaque;
383 qemu_put_be16s(f, &s->height);
384 qemu_put_be16s(f, &s->width);
385 qemu_put_be16s(f, &s->depth);
386 qemu_put_buffer(f, s->r, 256);
387 qemu_put_buffer(f, s->g, 256);
388 qemu_put_buffer(f, s->b, 256);
389 qemu_put_8s(f, &s->dac_index);
390 qemu_put_8s(f, &s->dac_state);
393 static int tcx_load(QEMUFile *f, void *opaque, int version_id)
395 TCXState *s = opaque;
396 uint32_t dummy;
398 if (version_id != 3 && version_id != 4)
399 return -EINVAL;
401 if (version_id == 3) {
402 qemu_get_be32s(f, &dummy);
403 qemu_get_be32s(f, &dummy);
404 qemu_get_be32s(f, &dummy);
406 qemu_get_be16s(f, &s->height);
407 qemu_get_be16s(f, &s->width);
408 qemu_get_be16s(f, &s->depth);
409 qemu_get_buffer(f, s->r, 256);
410 qemu_get_buffer(f, s->g, 256);
411 qemu_get_buffer(f, s->b, 256);
412 qemu_get_8s(f, &s->dac_index);
413 qemu_get_8s(f, &s->dac_state);
414 update_palette_entries(s, 0, 256);
415 if (s->depth == 24) {
416 tcx24_set_dirty(s);
417 } else {
418 tcx_set_dirty(s);
421 return 0;
424 static void tcx_reset(void *opaque)
426 TCXState *s = opaque;
428 /* Initialize palette */
429 memset(s->r, 0, 256);
430 memset(s->g, 0, 256);
431 memset(s->b, 0, 256);
432 s->r[255] = s->g[255] = s->b[255] = 255;
433 update_palette_entries(s, 0, 256);
434 memset(s->vram, 0, MAXX*MAXY);
435 cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
436 MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
437 s->dac_index = 0;
438 s->dac_state = 0;
441 static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
443 return 0;
446 static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
448 TCXState *s = opaque;
450 switch (addr) {
451 case 0:
452 s->dac_index = val >> 24;
453 s->dac_state = 0;
454 break;
455 case 4:
456 switch (s->dac_state) {
457 case 0:
458 s->r[s->dac_index] = val >> 24;
459 update_palette_entries(s, s->dac_index, s->dac_index + 1);
460 s->dac_state++;
461 break;
462 case 1:
463 s->g[s->dac_index] = val >> 24;
464 update_palette_entries(s, s->dac_index, s->dac_index + 1);
465 s->dac_state++;
466 break;
467 case 2:
468 s->b[s->dac_index] = val >> 24;
469 update_palette_entries(s, s->dac_index, s->dac_index + 1);
470 s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
471 default:
472 s->dac_state = 0;
473 break;
475 break;
476 default:
477 break;
479 return;
482 static CPUReadMemoryFunc *tcx_dac_read[3] = {
483 NULL,
484 NULL,
485 tcx_dac_readl,
488 static CPUWriteMemoryFunc *tcx_dac_write[3] = {
489 NULL,
490 NULL,
491 tcx_dac_writel,
494 static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
496 return 0;
499 static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
500 uint32_t val)
504 static CPUReadMemoryFunc *tcx_dummy_read[3] = {
505 NULL,
506 NULL,
507 tcx_dummy_readl,
510 static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
511 NULL,
512 NULL,
513 tcx_dummy_writel,
516 void tcx_init(target_phys_addr_t addr, int vram_size, int width, int height,
517 int depth)
519 DeviceState *dev;
520 SysBusDevice *s;
522 dev = qdev_create(NULL, "SUNW,tcx");
523 qdev_set_prop_int(dev, "addr", addr);
524 qdev_set_prop_int(dev, "vram_size", vram_size);
525 qdev_set_prop_int(dev, "width", width);
526 qdev_set_prop_int(dev, "height", height);
527 qdev_set_prop_int(dev, "depth", depth);
528 qdev_init(dev);
529 s = sysbus_from_qdev(dev);
530 /* 8-bit plane */
531 sysbus_mmio_map(s, 0, addr + 0x00800000ULL);
532 /* DAC */
533 sysbus_mmio_map(s, 1, addr + 0x00200000ULL);
534 /* TEC (dummy) */
535 sysbus_mmio_map(s, 2, addr + 0x00700000ULL);
536 /* THC 24 bit: NetBSD writes here even with 8-bit display: dummy */
537 sysbus_mmio_map(s, 3, addr + 0x00301000ULL);
538 if (depth == 24) {
539 /* 24-bit plane */
540 sysbus_mmio_map(s, 4, addr + 0x02000000ULL);
541 /* Control plane */
542 sysbus_mmio_map(s, 5, addr + 0x0a000000ULL);
543 } else {
544 /* THC 8 bit (dummy) */
545 sysbus_mmio_map(s, 4, addr + 0x00300000ULL);
549 static void tcx_init1(SysBusDevice *dev)
551 TCXState *s = FROM_SYSBUS(TCXState, dev);
552 int io_memory, dummy_memory;
553 ram_addr_t vram_offset;
554 int size, vram_size;
555 uint8_t *vram_base;
557 vram_size = qdev_get_prop_int(&dev->qdev, "vram_size", -1);
559 vram_offset = qemu_ram_alloc(vram_size * (1 + 4 + 4));
560 vram_base = qemu_get_ram_ptr(vram_offset);
561 s->addr = qdev_get_prop_int(&dev->qdev, "addr", -1);
562 s->vram_offset = vram_offset;
563 s->width = qdev_get_prop_int(&dev->qdev, "width", -1);
564 s->height = qdev_get_prop_int(&dev->qdev, "height", -1);
565 s->depth = qdev_get_prop_int(&dev->qdev, "depth", -1);
567 /* 8-bit plane */
568 s->vram = vram_base;
569 size = vram_size;
570 sysbus_init_mmio(dev, size, s->vram_offset);
571 vram_offset += size;
572 vram_base += size;
574 /* DAC */
575 io_memory = cpu_register_io_memory(tcx_dac_read, tcx_dac_write, s);
576 sysbus_init_mmio(dev, TCX_DAC_NREGS, io_memory);
578 /* TEC (dummy) */
579 dummy_memory = cpu_register_io_memory(tcx_dummy_read, tcx_dummy_write,
581 sysbus_init_mmio(dev, TCX_TEC_NREGS, dummy_memory);
582 /* THC: NetBSD writes here even with 8-bit display: dummy */
583 sysbus_init_mmio(dev, TCX_THC_NREGS_24, dummy_memory);
585 if (s->depth == 24) {
586 /* 24-bit plane */
587 size = vram_size * 4;
588 s->vram24 = (uint32_t *)vram_base;
589 s->vram24_offset = vram_offset;
590 sysbus_init_mmio(dev, size, vram_offset);
591 vram_offset += size;
592 vram_base += size;
594 /* Control plane */
595 size = vram_size * 4;
596 s->cplane = (uint32_t *)vram_base;
597 s->cplane_offset = vram_offset;
598 sysbus_init_mmio(dev, size, vram_offset);
600 s->ds = graphic_console_init(tcx24_update_display,
601 tcx24_invalidate_display,
602 tcx24_screen_dump, NULL, s);
603 } else {
604 /* THC 8 bit (dummy) */
605 sysbus_init_mmio(dev, TCX_THC_NREGS_8, dummy_memory);
607 s->ds = graphic_console_init(tcx_update_display,
608 tcx_invalidate_display,
609 tcx_screen_dump, NULL, s);
612 register_savevm("tcx", -1, 4, tcx_save, tcx_load, s);
613 qemu_register_reset(tcx_reset, s);
614 tcx_reset(s);
615 qemu_console_resize(s->ds, s->width, s->height);
618 static void tcx_screen_dump(void *opaque, const char *filename)
620 TCXState *s = opaque;
621 FILE *f;
622 uint8_t *d, *d1, v;
623 int y, x;
625 f = fopen(filename, "wb");
626 if (!f)
627 return;
628 fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
629 d1 = s->vram;
630 for(y = 0; y < s->height; y++) {
631 d = d1;
632 for(x = 0; x < s->width; x++) {
633 v = *d;
634 fputc(s->r[v], f);
635 fputc(s->g[v], f);
636 fputc(s->b[v], f);
637 d++;
639 d1 += MAXX;
641 fclose(f);
642 return;
645 static void tcx24_screen_dump(void *opaque, const char *filename)
647 TCXState *s = opaque;
648 FILE *f;
649 uint8_t *d, *d1, v;
650 uint32_t *s24, *cptr, dval;
651 int y, x;
653 f = fopen(filename, "wb");
654 if (!f)
655 return;
656 fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
657 d1 = s->vram;
658 s24 = s->vram24;
659 cptr = s->cplane;
660 for(y = 0; y < s->height; y++) {
661 d = d1;
662 for(x = 0; x < s->width; x++, d++, s24++) {
663 if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
664 dval = *s24 & 0x00ffffff;
665 fputc((dval >> 16) & 0xff, f);
666 fputc((dval >> 8) & 0xff, f);
667 fputc(dval & 0xff, f);
668 } else {
669 v = *d;
670 fputc(s->r[v], f);
671 fputc(s->g[v], f);
672 fputc(s->b[v], f);
675 d1 += MAXX;
677 fclose(f);
678 return;
681 static void tcx_register_devices(void)
683 sysbus_register_dev("SUNW,tcx", sizeof(TCXState), tcx_init1);
686 device_init(tcx_register_devices)