find -type f | xargs sed -i 's/[\t ]$//g' # on most files
[qemu] / hw / tcx.c
1 /*
2  * QEMU TCX Frame buffer
3  *
4  * Copyright (c) 2003-2005 Fabrice Bellard
5  *
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:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
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.
23  */
24 #include "vl.h"
25 #include "pixel_ops.h"
26
27 #define MAXX 1024
28 #define MAXY 768
29 #define TCX_DAC_NREGS 16
30 #define TCX_THC_NREGS_8  0x081c
31 #define TCX_THC_NREGS_24 0x1000
32 #define TCX_TEC_NREGS    0x1000
33
34 typedef struct TCXState {
35     target_phys_addr_t addr;
36     DisplayState *ds;
37     uint8_t *vram;
38     uint32_t *vram24, *cplane;
39     ram_addr_t vram_offset, vram24_offset, cplane_offset;
40     uint16_t width, height, depth;
41     uint8_t r[256], g[256], b[256];
42     uint32_t palette[256];
43     uint8_t dac_index, dac_state;
44 } TCXState;
45
46 static void tcx_screen_dump(void *opaque, const char *filename);
47 static void tcx24_screen_dump(void *opaque, const char *filename);
48 static void tcx_invalidate_display(void *opaque);
49 static void tcx24_invalidate_display(void *opaque);
50
51 static void update_palette_entries(TCXState *s, int start, int end)
52 {
53     int i;
54     for(i = start; i < end; i++) {
55         switch(s->ds->depth) {
56         default:
57         case 8:
58             s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
59             break;
60         case 15:
61             if (s->ds->bgr)
62                 s->palette[i] = rgb_to_pixel15bgr(s->r[i], s->g[i], s->b[i]);
63             else
64                 s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
65             break;
66         case 16:
67             if (s->ds->bgr)
68                 s->palette[i] = rgb_to_pixel16bgr(s->r[i], s->g[i], s->b[i]);
69             else
70                 s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
71             break;
72         case 32:
73             if (s->ds->bgr)
74                 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
75             else
76                 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
77             break;
78         }
79     }
80     if (s->depth == 24)
81         tcx24_invalidate_display(s);
82     else
83         tcx_invalidate_display(s);
84 }
85
86 static void tcx_draw_line32(TCXState *s1, uint8_t *d,
87                             const uint8_t *s, int width)
88 {
89     int x;
90     uint8_t val;
91     uint32_t *p = (uint32_t *)d;
92
93     for(x = 0; x < width; x++) {
94         val = *s++;
95         *p++ = s1->palette[val];
96     }
97 }
98
99 static void tcx_draw_line16(TCXState *s1, uint8_t *d,
100                             const uint8_t *s, int width)
101 {
102     int x;
103     uint8_t val;
104     uint16_t *p = (uint16_t *)d;
105
106     for(x = 0; x < width; x++) {
107         val = *s++;
108         *p++ = s1->palette[val];
109     }
110 }
111
112 static void tcx_draw_line8(TCXState *s1, uint8_t *d,
113                            const uint8_t *s, int width)
114 {
115     int x;
116     uint8_t val;
117
118     for(x = 0; x < width; x++) {
119         val = *s++;
120         *d++ = s1->palette[val];
121     }
122 }
123
124 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
125                                      const uint8_t *s, int width,
126                                      const uint32_t *cplane,
127                                      const uint32_t *s24)
128 {
129     int x;
130     uint8_t val;
131     uint32_t *p = (uint32_t *)d;
132     uint32_t dval;
133
134     for(x = 0; x < width; x++, s++, s24++) {
135         if ((bswap32(*cplane++) & 0xff000000) == 0x03000000) { // 24-bit direct
136             dval = bswap32(*s24) & 0x00ffffff;
137         } else {
138             val = *s;
139             dval = s1->palette[val];
140         }
141         *p++ = dval;
142     }
143 }
144
145 static inline int check_dirty(TCXState *ts, ram_addr_t page, ram_addr_t page24,
146                               ram_addr_t cpage)
147 {
148     int ret;
149     unsigned int off;
150
151     ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
152     for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
153         ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
154         ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
155     }
156     return ret;
157 }
158
159 static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
160                                ram_addr_t page_max, ram_addr_t page24,
161                               ram_addr_t cpage)
162 {
163     cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
164                                     VGA_DIRTY_FLAG);
165     page_min -= ts->vram_offset;
166     page_max -= ts->vram_offset;
167     cpu_physical_memory_reset_dirty(page24 + page_min * 4,
168                                     page24 + page_max * 4 + TARGET_PAGE_SIZE,
169                                     VGA_DIRTY_FLAG);
170     cpu_physical_memory_reset_dirty(cpage + page_min * 4,
171                                     cpage + page_max * 4 + TARGET_PAGE_SIZE,
172                                     VGA_DIRTY_FLAG);
173 }
174
175 /* Fixed line length 1024 allows us to do nice tricks not possible on
176    VGA... */
177 static void tcx_update_display(void *opaque)
178 {
179     TCXState *ts = opaque;
180     ram_addr_t page, page_min, page_max;
181     int y, y_start, dd, ds;
182     uint8_t *d, *s;
183     void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
184
185     if (ts->ds->depth == 0)
186         return;
187     page = ts->vram_offset;
188     y_start = -1;
189     page_min = 0xffffffff;
190     page_max = 0;
191     d = ts->ds->data;
192     s = ts->vram;
193     dd = ts->ds->linesize;
194     ds = 1024;
195
196     switch (ts->ds->depth) {
197     case 32:
198         f = tcx_draw_line32;
199         break;
200     case 15:
201     case 16:
202         f = tcx_draw_line16;
203         break;
204     default:
205     case 8:
206         f = tcx_draw_line8;
207         break;
208     case 0:
209         return;
210     }
211    
212     for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
213         if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
214             if (y_start < 0)
215                 y_start = y;
216             if (page < page_min)
217                 page_min = page;
218             if (page > page_max)
219                 page_max = page;
220             f(ts, d, s, ts->width);
221             d += dd;
222             s += ds;
223             f(ts, d, s, ts->width);
224             d += dd;
225             s += ds;
226             f(ts, d, s, ts->width);
227             d += dd;
228             s += ds;
229             f(ts, d, s, ts->width);
230             d += dd;
231             s += ds;
232         } else {
233             if (y_start >= 0) {
234                 /* flush to display */
235                 dpy_update(ts->ds, 0, y_start,
236                            ts->width, y - y_start);
237                 y_start = -1;
238             }
239             d += dd * 4;
240             s += ds * 4;
241         }
242     }
243     if (y_start >= 0) {
244         /* flush to display */
245         dpy_update(ts->ds, 0, y_start,
246                    ts->width, y - y_start);
247     }
248     /* reset modified pages */
249     if (page_min <= page_max) {
250         cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
251                                         VGA_DIRTY_FLAG);
252     }
253 }
254
255 static void tcx24_update_display(void *opaque)
256 {
257     TCXState *ts = opaque;
258     ram_addr_t page, page_min, page_max, cpage, page24;
259     int y, y_start, dd, ds;
260     uint8_t *d, *s;
261     uint32_t *cptr, *s24;
262
263     if (ts->ds->depth != 32)
264             return;
265     page = ts->vram_offset;
266     page24 = ts->vram24_offset;
267     cpage = ts->cplane_offset;
268     y_start = -1;
269     page_min = 0xffffffff;
270     page_max = 0;
271     d = ts->ds->data;
272     s = ts->vram;
273     s24 = ts->vram24;
274     cptr = ts->cplane;
275     dd = ts->ds->linesize;
276     ds = 1024;
277
278     for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
279             page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
280         if (check_dirty(ts, page, page24, cpage)) {
281             if (y_start < 0)
282                 y_start = y;
283             if (page < page_min)
284                 page_min = page;
285             if (page > page_max)
286                 page_max = page;
287             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
288             d += dd;
289             s += ds;
290             cptr += ds;
291             s24 += ds;
292             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
293             d += dd;
294             s += ds;
295             cptr += ds;
296             s24 += ds;
297             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
298             d += dd;
299             s += ds;
300             cptr += ds;
301             s24 += ds;
302             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
303             d += dd;
304             s += ds;
305             cptr += ds;
306             s24 += ds;
307         } else {
308             if (y_start >= 0) {
309                 /* flush to display */
310                 dpy_update(ts->ds, 0, y_start,
311                            ts->width, y - y_start);
312                 y_start = -1;
313             }
314             d += dd * 4;
315             s += ds * 4;
316             cptr += ds * 4;
317             s24 += ds * 4;
318         }
319     }
320     if (y_start >= 0) {
321         /* flush to display */
322         dpy_update(ts->ds, 0, y_start,
323                    ts->width, y - y_start);
324     }
325     /* reset modified pages */
326     if (page_min <= page_max) {
327         reset_dirty(ts, page_min, page_max, page24, cpage);
328     }
329 }
330
331 static void tcx_invalidate_display(void *opaque)
332 {
333     TCXState *s = opaque;
334     int i;
335
336     for (i = 0; i < MAXX*MAXY; i += TARGET_PAGE_SIZE) {
337         cpu_physical_memory_set_dirty(s->vram_offset + i);
338     }
339 }
340
341 static void tcx24_invalidate_display(void *opaque)
342 {
343     TCXState *s = opaque;
344     int i;
345
346     tcx_invalidate_display(s);
347     for (i = 0; i < MAXX*MAXY * 4; i += TARGET_PAGE_SIZE) {
348         cpu_physical_memory_set_dirty(s->vram24_offset + i);
349         cpu_physical_memory_set_dirty(s->cplane_offset + i);
350     }
351 }
352
353 static void tcx_save(QEMUFile *f, void *opaque)
354 {
355     TCXState *s = opaque;
356    
357     qemu_put_be16s(f, (uint16_t *)&s->height);
358     qemu_put_be16s(f, (uint16_t *)&s->width);
359     qemu_put_be16s(f, (uint16_t *)&s->depth);
360     qemu_put_buffer(f, s->r, 256);
361     qemu_put_buffer(f, s->g, 256);
362     qemu_put_buffer(f, s->b, 256);
363     qemu_put_8s(f, &s->dac_index);
364     qemu_put_8s(f, &s->dac_state);
365 }
366
367 static int tcx_load(QEMUFile *f, void *opaque, int version_id)
368 {
369     TCXState *s = opaque;
370     uint32_t dummy;
371
372     if (version_id != 3 && version_id != 4)
373         return -EINVAL;
374
375     if (version_id == 3) {
376         qemu_get_be32s(f, (uint32_t *)&dummy);
377         qemu_get_be32s(f, (uint32_t *)&dummy);
378         qemu_get_be32s(f, (uint32_t *)&dummy);
379     }
380     qemu_get_be16s(f, (uint16_t *)&s->height);
381     qemu_get_be16s(f, (uint16_t *)&s->width);
382     qemu_get_be16s(f, (uint16_t *)&s->depth);
383     qemu_get_buffer(f, s->r, 256);
384     qemu_get_buffer(f, s->g, 256);
385     qemu_get_buffer(f, s->b, 256);
386     qemu_get_8s(f, &s->dac_index);
387     qemu_get_8s(f, &s->dac_state);
388     update_palette_entries(s, 0, 256);
389     if (s->depth == 24)
390         tcx24_invalidate_display(s);
391     else
392         tcx_invalidate_display(s);
393
394     return 0;
395 }
396
397 static void tcx_reset(void *opaque)
398 {
399     TCXState *s = opaque;
400
401     /* Initialize palette */
402     memset(s->r, 0, 256);
403     memset(s->g, 0, 256);
404     memset(s->b, 0, 256);
405     s->r[255] = s->g[255] = s->b[255] = 255;
406     update_palette_entries(s, 0, 256);
407     memset(s->vram, 0, MAXX*MAXY);
408     cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
409                                     MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
410     s->dac_index = 0;
411     s->dac_state = 0;
412 }
413
414 static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
415 {
416     return 0;
417 }
418
419 static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
420 {
421     TCXState *s = opaque;
422     uint32_t saddr;
423
424     saddr = (addr & (TCX_DAC_NREGS - 1)) >> 2;
425     switch (saddr) {
426     case 0:
427         s->dac_index = val >> 24;
428         s->dac_state = 0;
429         break;
430     case 1:
431         switch (s->dac_state) {
432         case 0:
433             s->r[s->dac_index] = val >> 24;
434             update_palette_entries(s, s->dac_index, s->dac_index + 1);
435             s->dac_state++;
436             break;
437         case 1:
438             s->g[s->dac_index] = val >> 24;
439             update_palette_entries(s, s->dac_index, s->dac_index + 1);
440             s->dac_state++;
441             break;
442         case 2:
443             s->b[s->dac_index] = val >> 24;
444             update_palette_entries(s, s->dac_index, s->dac_index + 1);
445             s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
446         default:
447             s->dac_state = 0;
448             break;
449         }
450         break;
451     default:
452         break;
453     }
454     return;
455 }
456
457 static CPUReadMemoryFunc *tcx_dac_read[3] = {
458     tcx_dac_readl,
459     tcx_dac_readl,
460     tcx_dac_readl,
461 };
462
463 static CPUWriteMemoryFunc *tcx_dac_write[3] = {
464     tcx_dac_writel,
465     tcx_dac_writel,
466     tcx_dac_writel,
467 };
468
469 static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
470 {
471     return 0;
472 }
473
474 static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
475                              uint32_t val)
476 {
477 }
478
479 static CPUReadMemoryFunc *tcx_dummy_read[3] = {
480     tcx_dummy_readl,
481     tcx_dummy_readl,
482     tcx_dummy_readl,
483 };
484
485 static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
486     tcx_dummy_writel,
487     tcx_dummy_writel,
488     tcx_dummy_writel,
489 };
490
491 void tcx_init(DisplayState *ds, target_phys_addr_t addr, uint8_t *vram_base,
492               unsigned long vram_offset, int vram_size, int width, int height,
493               int depth)
494 {
495     TCXState *s;
496     int io_memory, dummy_memory;
497     int size;
498
499     s = qemu_mallocz(sizeof(TCXState));
500     if (!s)
501         return;
502     s->ds = ds;
503     s->addr = addr;
504     s->vram_offset = vram_offset;
505     s->width = width;
506     s->height = height;
507     s->depth = depth;
508
509     // 8-bit plane
510     s->vram = vram_base;
511     size = vram_size;
512     cpu_register_physical_memory(addr + 0x00800000ULL, size, vram_offset);
513     vram_offset += size;
514     vram_base += size;
515
516     io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s);
517     cpu_register_physical_memory(addr + 0x00200000ULL, TCX_DAC_NREGS, io_memory);
518
519     dummy_memory = cpu_register_io_memory(0, tcx_dummy_read, tcx_dummy_write,
520                                           s);
521     cpu_register_physical_memory(addr + 0x00700000ULL, TCX_TEC_NREGS,
522                                  dummy_memory);
523     if (depth == 24) {
524         // 24-bit plane
525         size = vram_size * 4;
526         s->vram24 = (uint32_t *)vram_base;
527         s->vram24_offset = vram_offset;
528         cpu_register_physical_memory(addr + 0x02000000ULL, size, vram_offset);
529         vram_offset += size;
530         vram_base += size;
531
532         // Control plane
533         size = vram_size * 4;
534         s->cplane = (uint32_t *)vram_base;
535         s->cplane_offset = vram_offset;
536         cpu_register_physical_memory(addr + 0x0a000000ULL, size, vram_offset);
537         graphic_console_init(s->ds, tcx24_update_display,
538                              tcx24_invalidate_display, tcx24_screen_dump, s);
539     } else {
540         cpu_register_physical_memory(addr + 0x00300000ULL, TCX_THC_NREGS_8,
541                                      dummy_memory);
542         graphic_console_init(s->ds, tcx_update_display, tcx_invalidate_display,
543                              tcx_screen_dump, s);
544     }
545     // NetBSD writes here even with 8-bit display
546     cpu_register_physical_memory(addr + 0x00301000ULL, TCX_THC_NREGS_24,
547                                  dummy_memory);
548
549     register_savevm("tcx", addr, 4, tcx_save, tcx_load, s);
550     qemu_register_reset(tcx_reset, s);
551     tcx_reset(s);
552     dpy_resize(s->ds, width, height);
553 }
554
555 static void tcx_screen_dump(void *opaque, const char *filename)
556 {
557     TCXState *s = opaque;
558     FILE *f;
559     uint8_t *d, *d1, v;
560     int y, x;
561
562     f = fopen(filename, "wb");
563     if (!f)
564         return;
565     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
566     d1 = s->vram;
567     for(y = 0; y < s->height; y++) {
568         d = d1;
569         for(x = 0; x < s->width; x++) {
570             v = *d;
571             fputc(s->r[v], f);
572             fputc(s->g[v], f);
573             fputc(s->b[v], f);
574             d++;
575         }
576         d1 += MAXX;
577     }
578     fclose(f);
579     return;
580 }
581
582 static void tcx24_screen_dump(void *opaque, const char *filename)
583 {
584     TCXState *s = opaque;
585     FILE *f;
586     uint8_t *d, *d1, v;
587     uint32_t *s24, *cptr, dval;
588     int y, x;
589
590     f = fopen(filename, "wb");
591     if (!f)
592         return;
593     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
594     d1 = s->vram;
595     s24 = s->vram24;
596     cptr = s->cplane;
597     for(y = 0; y < s->height; y++) {
598         d = d1;
599         for(x = 0; x < s->width; x++, d++, s24++) {
600             if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
601                 dval = *s24 & 0x00ffffff;
602                 fputc((dval >> 16) & 0xff, f);
603                 fputc((dval >> 8) & 0xff, f);
604                 fputc(dval & 0xff, f);
605             } else {
606                 v = *d;
607                 fputc(s->r[v], f);
608                 fputc(s->g[v], f);
609                 fputc(s->b[v], f);
610             }
611         }
612         d1 += MAXX;
613     }
614     fclose(f);
615     return;
616 }