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