Use full 36-bit physical address space on SS10
[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     target_phys_addr_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->vram);
363     qemu_put_be32s(f, (uint32_t *)&s->vram24);
364     qemu_put_be32s(f, (uint32_t *)&s->cplane);
365     qemu_put_be16s(f, (uint16_t *)&s->height);
366     qemu_put_be16s(f, (uint16_t *)&s->width);
367     qemu_put_be16s(f, (uint16_t *)&s->depth);
368     qemu_put_buffer(f, s->r, 256);
369     qemu_put_buffer(f, s->g, 256);
370     qemu_put_buffer(f, s->b, 256);
371     qemu_put_8s(f, &s->dac_index);
372     qemu_put_8s(f, &s->dac_state);
373 }
374
375 static int tcx_load(QEMUFile *f, void *opaque, int version_id)
376 {
377     TCXState *s = opaque;
378     
379     if (version_id != 3)
380         return -EINVAL;
381
382     qemu_get_be32s(f, (uint32_t *)&s->vram);
383     qemu_get_be32s(f, (uint32_t *)&s->vram24);
384     qemu_get_be32s(f, (uint32_t *)&s->cplane);
385     qemu_get_be16s(f, (uint16_t *)&s->height);
386     qemu_get_be16s(f, (uint16_t *)&s->width);
387     qemu_get_be16s(f, (uint16_t *)&s->depth);
388     qemu_get_buffer(f, s->r, 256);
389     qemu_get_buffer(f, s->g, 256);
390     qemu_get_buffer(f, s->b, 256);
391     qemu_get_8s(f, &s->dac_index);
392     qemu_get_8s(f, &s->dac_state);
393     update_palette_entries(s, 0, 256);
394     tcx_invalidate_display(s);
395
396     return 0;
397 }
398
399 static void tcx_reset(void *opaque)
400 {
401     TCXState *s = opaque;
402
403     /* Initialize palette */
404     memset(s->r, 0, 256);
405     memset(s->g, 0, 256);
406     memset(s->b, 0, 256);
407     s->r[255] = s->g[255] = s->b[255] = 255;
408     update_palette_entries(s, 0, 256);
409     memset(s->vram, 0, MAXX*MAXY);
410     cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
411                                     MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
412     s->dac_index = 0;
413     s->dac_state = 0;
414 }
415
416 static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
417 {
418     return 0;
419 }
420
421 static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
422 {
423     TCXState *s = opaque;
424     uint32_t saddr;
425
426     saddr = (addr & (TCX_DAC_NREGS - 1)) >> 2;
427     switch (saddr) {
428     case 0:
429         s->dac_index = val >> 24;
430         s->dac_state = 0;
431         break;
432     case 1:
433         switch (s->dac_state) {
434         case 0:
435             s->r[s->dac_index] = val >> 24;
436             update_palette_entries(s, s->dac_index, s->dac_index + 1);
437             s->dac_state++;
438             break;
439         case 1:
440             s->g[s->dac_index] = val >> 24;
441             update_palette_entries(s, s->dac_index, s->dac_index + 1);
442             s->dac_state++;
443             break;
444         case 2:
445             s->b[s->dac_index] = val >> 24;
446             update_palette_entries(s, s->dac_index, s->dac_index + 1);
447             s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
448         default:
449             s->dac_state = 0;
450             break;
451         }
452         break;
453     default:
454         break;
455     }
456     return;
457 }
458
459 static CPUReadMemoryFunc *tcx_dac_read[3] = {
460     tcx_dac_readl,
461     tcx_dac_readl,
462     tcx_dac_readl,
463 };
464
465 static CPUWriteMemoryFunc *tcx_dac_write[3] = {
466     tcx_dac_writel,
467     tcx_dac_writel,
468     tcx_dac_writel,
469 };
470
471 static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
472 {
473     return 0;
474 }
475
476 static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
477                              uint32_t val)
478 {
479 }
480
481 static CPUReadMemoryFunc *tcx_dummy_read[3] = {
482     tcx_dummy_readl,
483     tcx_dummy_readl,
484     tcx_dummy_readl,
485 };
486
487 static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
488     tcx_dummy_writel,
489     tcx_dummy_writel,
490     tcx_dummy_writel,
491 };
492
493 void tcx_init(DisplayState *ds, target_phys_addr_t addr, uint8_t *vram_base,
494               unsigned long vram_offset, int vram_size, int width, int height,
495               int depth)
496 {
497     TCXState *s;
498     int io_memory, dummy_memory;
499     int size;
500
501     s = qemu_mallocz(sizeof(TCXState));
502     if (!s)
503         return;
504     s->ds = ds;
505     s->addr = addr;
506     s->vram_offset = vram_offset;
507     s->width = width;
508     s->height = height;
509     s->depth = depth;
510
511     // 8-bit plane
512     s->vram = vram_base;
513     size = vram_size;
514     cpu_register_physical_memory(addr + 0x00800000ULL, size, vram_offset);
515     vram_offset += size;
516     vram_base += size;
517
518     io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s);
519     cpu_register_physical_memory(addr + 0x00200000ULL, TCX_DAC_NREGS, io_memory);
520
521     dummy_memory = cpu_register_io_memory(0, tcx_dummy_read, tcx_dummy_write,
522                                           s);
523     cpu_register_physical_memory(addr + 0x00700000ULL, TCX_TEC_NREGS,
524                                  dummy_memory);
525     if (depth == 24) {
526         // 24-bit plane
527         size = vram_size * 4;
528         s->vram24 = (uint32_t *)vram_base;
529         s->vram24_offset = vram_offset;
530         cpu_register_physical_memory(addr + 0x02000000ULL, size, vram_offset);
531         vram_offset += size;
532         vram_base += size;
533
534         // Control plane
535         size = vram_size * 4;
536         s->cplane = (uint32_t *)vram_base;
537         s->cplane_offset = vram_offset;
538         cpu_register_physical_memory(addr + 0x0a000000ULL, size, vram_offset);
539         graphic_console_init(s->ds, tcx24_update_display,
540                              tcx24_invalidate_display, tcx24_screen_dump, s);
541     } else {
542         cpu_register_physical_memory(addr + 0x00300000ULL, TCX_THC_NREGS_8,
543                                      dummy_memory);
544         graphic_console_init(s->ds, tcx_update_display, tcx_invalidate_display,
545                              tcx_screen_dump, s);
546     }
547     // NetBSD writes here even with 8-bit display
548     cpu_register_physical_memory(addr + 0x00301000ULL, TCX_THC_NREGS_24,
549                                  dummy_memory);
550
551     register_savevm("tcx", addr, 3, tcx_save, tcx_load, s);
552     qemu_register_reset(tcx_reset, s);
553     tcx_reset(s);
554     dpy_resize(s->ds, width, height);
555 }
556
557 static void tcx_screen_dump(void *opaque, const char *filename)
558 {
559     TCXState *s = opaque;
560     FILE *f;
561     uint8_t *d, *d1, v;
562     int y, x;
563
564     f = fopen(filename, "wb");
565     if (!f)
566         return;
567     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
568     d1 = s->vram;
569     for(y = 0; y < s->height; y++) {
570         d = d1;
571         for(x = 0; x < s->width; x++) {
572             v = *d;
573             fputc(s->r[v], f);
574             fputc(s->g[v], f);
575             fputc(s->b[v], f);
576             d++;
577         }
578         d1 += MAXX;
579     }
580     fclose(f);
581     return;
582 }
583
584 static void tcx24_screen_dump(void *opaque, const char *filename)
585 {
586     TCXState *s = opaque;
587     FILE *f;
588     uint8_t *d, *d1, v;
589     uint32_t *s24, *cptr, dval;
590     int y, x;
591
592     f = fopen(filename, "wb");
593     if (!f)
594         return;
595     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
596     d1 = s->vram;
597     s24 = s->vram24;
598     cptr = s->cplane;
599     for(y = 0; y < s->height; y++) {
600         d = d1;
601         for(x = 0; x < s->width; x++, d++, s24++) {
602             if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
603                 dval = *s24 & 0x00ffffff;
604                 fputc((dval >> 16) & 0xff, f);
605                 fputc((dval >> 8) & 0xff, f);
606                 fputc(dval & 0xff, f);
607             } else {
608                 v = *d;
609                 fputc(s->r[v], f);
610                 fputc(s->g[v], f);
611                 fputc(s->b[v], f);
612             }
613         }
614         d1 += MAXX;
615     }
616     fclose(f);
617     return;
618 }