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