xref: /openbmc/qemu/hw/display/tcx.c (revision 66dcabea)
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 
25 #include "qemu/osdep.h"
26 #include "qapi/error.h"
27 #include "qemu-common.h"
28 #include "cpu.h" /* FIXME shouldn't use TARGET_PAGE_SIZE */
29 #include "ui/console.h"
30 #include "ui/pixel_ops.h"
31 #include "hw/loader.h"
32 #include "hw/sysbus.h"
33 #include "qemu/error-report.h"
34 
35 #define TCX_ROM_FILE "QEMU,tcx.bin"
36 #define FCODE_MAX_ROM_SIZE 0x10000
37 
38 #define MAXX 1024
39 #define MAXY 768
40 #define TCX_DAC_NREGS    16
41 #define TCX_THC_NREGS    0x1000
42 #define TCX_DHC_NREGS    0x4000
43 #define TCX_TEC_NREGS    0x1000
44 #define TCX_ALT_NREGS    0x8000
45 #define TCX_STIP_NREGS   0x800000
46 #define TCX_BLIT_NREGS   0x800000
47 #define TCX_RSTIP_NREGS  0x800000
48 #define TCX_RBLIT_NREGS  0x800000
49 
50 #define TCX_THC_MISC     0x818
51 #define TCX_THC_CURSXY   0x8fc
52 #define TCX_THC_CURSMASK 0x900
53 #define TCX_THC_CURSBITS 0x980
54 
55 #define TYPE_TCX "SUNW,tcx"
56 #define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX)
57 
58 typedef struct TCXState {
59     SysBusDevice parent_obj;
60 
61     QemuConsole *con;
62     qemu_irq irq;
63     uint8_t *vram;
64     uint32_t *vram24, *cplane;
65     hwaddr prom_addr;
66     MemoryRegion rom;
67     MemoryRegion vram_mem;
68     MemoryRegion vram_8bit;
69     MemoryRegion vram_24bit;
70     MemoryRegion stip;
71     MemoryRegion blit;
72     MemoryRegion vram_cplane;
73     MemoryRegion rstip;
74     MemoryRegion rblit;
75     MemoryRegion tec;
76     MemoryRegion dac;
77     MemoryRegion thc;
78     MemoryRegion dhc;
79     MemoryRegion alt;
80     MemoryRegion thc24;
81 
82     ram_addr_t vram24_offset, cplane_offset;
83     uint32_t tmpblit;
84     uint32_t vram_size;
85     uint32_t palette[260];
86     uint8_t r[260], g[260], b[260];
87     uint16_t width, height, depth;
88     uint8_t dac_index, dac_state;
89     uint32_t thcmisc;
90     uint32_t cursmask[32];
91     uint32_t cursbits[32];
92     uint16_t cursx;
93     uint16_t cursy;
94 } TCXState;
95 
96 static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len)
97 {
98     memory_region_set_dirty(&s->vram_mem, addr, len);
99 
100     if (s->depth == 24) {
101         memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
102                                 len * 4);
103         memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
104                                 len * 4);
105     }
106 }
107 
108 static int tcx_check_dirty(TCXState *s, ram_addr_t addr, int len)
109 {
110     int ret;
111 
112     ret = memory_region_get_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA);
113 
114     if (s->depth == 24) {
115         ret |= memory_region_get_dirty(&s->vram_mem,
116                                        s->vram24_offset + addr * 4, len * 4,
117                                        DIRTY_MEMORY_VGA);
118         ret |= memory_region_get_dirty(&s->vram_mem,
119                                        s->cplane_offset + addr * 4, len * 4,
120                                        DIRTY_MEMORY_VGA);
121     }
122 
123     return ret;
124 }
125 
126 static void tcx_reset_dirty(TCXState *s, ram_addr_t addr, int len)
127 {
128     memory_region_reset_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA);
129 
130     if (s->depth == 24) {
131         memory_region_reset_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
132                                   len * 4, DIRTY_MEMORY_VGA);
133         memory_region_reset_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
134                                   len * 4, DIRTY_MEMORY_VGA);
135     }
136 }
137 
138 static void update_palette_entries(TCXState *s, int start, int end)
139 {
140     DisplaySurface *surface = qemu_console_surface(s->con);
141     int i;
142 
143     for (i = start; i < end; i++) {
144         switch (surface_bits_per_pixel(surface)) {
145         default:
146         case 8:
147             s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
148             break;
149         case 15:
150             s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
151             break;
152         case 16:
153             s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
154             break;
155         case 32:
156             if (is_surface_bgr(surface)) {
157                 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
158             } else {
159                 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
160             }
161             break;
162         }
163     }
164     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
165 }
166 
167 static void tcx_draw_line32(TCXState *s1, uint8_t *d,
168                             const uint8_t *s, int width)
169 {
170     int x;
171     uint8_t val;
172     uint32_t *p = (uint32_t *)d;
173 
174     for (x = 0; x < width; x++) {
175         val = *s++;
176         *p++ = s1->palette[val];
177     }
178 }
179 
180 static void tcx_draw_line16(TCXState *s1, uint8_t *d,
181                             const uint8_t *s, int width)
182 {
183     int x;
184     uint8_t val;
185     uint16_t *p = (uint16_t *)d;
186 
187     for (x = 0; x < width; x++) {
188         val = *s++;
189         *p++ = s1->palette[val];
190     }
191 }
192 
193 static void tcx_draw_line8(TCXState *s1, uint8_t *d,
194                            const uint8_t *s, int width)
195 {
196     int x;
197     uint8_t val;
198 
199     for(x = 0; x < width; x++) {
200         val = *s++;
201         *d++ = s1->palette[val];
202     }
203 }
204 
205 static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
206                               int y, int width)
207 {
208     int x, len;
209     uint32_t mask, bits;
210     uint32_t *p = (uint32_t *)d;
211 
212     y = y - s1->cursy;
213     mask = s1->cursmask[y];
214     bits = s1->cursbits[y];
215     len = MIN(width - s1->cursx, 32);
216     p = &p[s1->cursx];
217     for (x = 0; x < len; x++) {
218         if (mask & 0x80000000) {
219             if (bits & 0x80000000) {
220                 *p = s1->palette[259];
221             } else {
222                 *p = s1->palette[258];
223             }
224         }
225         p++;
226         mask <<= 1;
227         bits <<= 1;
228     }
229 }
230 
231 static void tcx_draw_cursor16(TCXState *s1, uint8_t *d,
232                               int y, int width)
233 {
234     int x, len;
235     uint32_t mask, bits;
236     uint16_t *p = (uint16_t *)d;
237 
238     y = y - s1->cursy;
239     mask = s1->cursmask[y];
240     bits = s1->cursbits[y];
241     len = MIN(width - s1->cursx, 32);
242     p = &p[s1->cursx];
243     for (x = 0; x < len; x++) {
244         if (mask & 0x80000000) {
245             if (bits & 0x80000000) {
246                 *p = s1->palette[259];
247             } else {
248                 *p = s1->palette[258];
249             }
250         }
251         p++;
252         mask <<= 1;
253         bits <<= 1;
254     }
255 }
256 
257 static void tcx_draw_cursor8(TCXState *s1, uint8_t *d,
258                               int y, int width)
259 {
260     int x, len;
261     uint32_t mask, bits;
262 
263     y = y - s1->cursy;
264     mask = s1->cursmask[y];
265     bits = s1->cursbits[y];
266     len = MIN(width - s1->cursx, 32);
267     d = &d[s1->cursx];
268     for (x = 0; x < len; x++) {
269         if (mask & 0x80000000) {
270             if (bits & 0x80000000) {
271                 *d = s1->palette[259];
272             } else {
273                 *d = s1->palette[258];
274             }
275         }
276         d++;
277         mask <<= 1;
278         bits <<= 1;
279     }
280 }
281 
282 /*
283   XXX Could be much more optimal:
284   * detect if line/page/whole screen is in 24 bit mode
285   * if destination is also BGR, use memcpy
286   */
287 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
288                                      const uint8_t *s, int width,
289                                      const uint32_t *cplane,
290                                      const uint32_t *s24)
291 {
292     DisplaySurface *surface = qemu_console_surface(s1->con);
293     int x, bgr, r, g, b;
294     uint8_t val, *p8;
295     uint32_t *p = (uint32_t *)d;
296     uint32_t dval;
297     bgr = is_surface_bgr(surface);
298     for(x = 0; x < width; x++, s++, s24++) {
299         if (be32_to_cpu(*cplane) & 0x03000000) {
300             /* 24-bit direct, BGR order */
301             p8 = (uint8_t *)s24;
302             p8++;
303             b = *p8++;
304             g = *p8++;
305             r = *p8;
306             if (bgr)
307                 dval = rgb_to_pixel32bgr(r, g, b);
308             else
309                 dval = rgb_to_pixel32(r, g, b);
310         } else {
311             /* 8-bit pseudocolor */
312             val = *s;
313             dval = s1->palette[val];
314         }
315         *p++ = dval;
316         cplane++;
317     }
318 }
319 
320 /* Fixed line length 1024 allows us to do nice tricks not possible on
321    VGA... */
322 
323 static void tcx_update_display(void *opaque)
324 {
325     TCXState *ts = opaque;
326     DisplaySurface *surface = qemu_console_surface(ts->con);
327     ram_addr_t page, page_min, page_max;
328     int y, y_start, dd, ds;
329     uint8_t *d, *s;
330     void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
331     void (*fc)(TCXState *s1, uint8_t *dst, int y, int width);
332 
333     if (surface_bits_per_pixel(surface) == 0) {
334         return;
335     }
336 
337     page = 0;
338     y_start = -1;
339     page_min = -1;
340     page_max = 0;
341     d = surface_data(surface);
342     s = ts->vram;
343     dd = surface_stride(surface);
344     ds = 1024;
345 
346     switch (surface_bits_per_pixel(surface)) {
347     case 32:
348         f = tcx_draw_line32;
349         fc = tcx_draw_cursor32;
350         break;
351     case 15:
352     case 16:
353         f = tcx_draw_line16;
354         fc = tcx_draw_cursor16;
355         break;
356     default:
357     case 8:
358         f = tcx_draw_line8;
359         fc = tcx_draw_cursor8;
360         break;
361     case 0:
362         return;
363     }
364 
365     memory_region_sync_dirty_bitmap(&ts->vram_mem);
366     for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) {
367         if (tcx_check_dirty(ts, page, TARGET_PAGE_SIZE)) {
368             if (y_start < 0)
369                 y_start = y;
370             if (page < page_min)
371                 page_min = page;
372             if (page > page_max)
373                 page_max = page;
374 
375             f(ts, d, s, ts->width);
376             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
377                 fc(ts, d, y, ts->width);
378             }
379             d += dd;
380             s += ds;
381             y++;
382 
383             f(ts, d, s, ts->width);
384             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
385                 fc(ts, d, y, ts->width);
386             }
387             d += dd;
388             s += ds;
389             y++;
390 
391             f(ts, d, s, ts->width);
392             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
393                 fc(ts, d, y, ts->width);
394             }
395             d += dd;
396             s += ds;
397             y++;
398 
399             f(ts, d, s, ts->width);
400             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
401                 fc(ts, d, y, ts->width);
402             }
403             d += dd;
404             s += ds;
405             y++;
406         } else {
407             if (y_start >= 0) {
408                 /* flush to display */
409                 dpy_gfx_update(ts->con, 0, y_start,
410                                ts->width, y - y_start);
411                 y_start = -1;
412             }
413             d += dd * 4;
414             s += ds * 4;
415             y += 4;
416         }
417     }
418     if (y_start >= 0) {
419         /* flush to display */
420         dpy_gfx_update(ts->con, 0, y_start,
421                        ts->width, y - y_start);
422     }
423     /* reset modified pages */
424     if (page_max >= page_min) {
425         tcx_reset_dirty(ts, page_min, page_max - page_min);
426     }
427 }
428 
429 static void tcx24_update_display(void *opaque)
430 {
431     TCXState *ts = opaque;
432     DisplaySurface *surface = qemu_console_surface(ts->con);
433     ram_addr_t page, page_min, page_max;
434     int y, y_start, dd, ds;
435     uint8_t *d, *s;
436     uint32_t *cptr, *s24;
437 
438     if (surface_bits_per_pixel(surface) != 32) {
439             return;
440     }
441 
442     page = 0;
443     y_start = -1;
444     page_min = -1;
445     page_max = 0;
446     d = surface_data(surface);
447     s = ts->vram;
448     s24 = ts->vram24;
449     cptr = ts->cplane;
450     dd = surface_stride(surface);
451     ds = 1024;
452 
453     memory_region_sync_dirty_bitmap(&ts->vram_mem);
454     for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) {
455         if (tcx_check_dirty(ts, page, TARGET_PAGE_SIZE)) {
456             if (y_start < 0)
457                 y_start = y;
458             if (page < page_min)
459                 page_min = page;
460             if (page > page_max)
461                 page_max = page;
462             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
463             if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
464                 tcx_draw_cursor32(ts, d, y, ts->width);
465             }
466             d += dd;
467             s += ds;
468             cptr += ds;
469             s24 += ds;
470             y++;
471             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
472             if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
473                 tcx_draw_cursor32(ts, d, y, ts->width);
474             }
475             d += dd;
476             s += ds;
477             cptr += ds;
478             s24 += ds;
479             y++;
480             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
481             if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
482                 tcx_draw_cursor32(ts, d, y, ts->width);
483             }
484             d += dd;
485             s += ds;
486             cptr += ds;
487             s24 += ds;
488             y++;
489             tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
490             if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
491                 tcx_draw_cursor32(ts, d, y, ts->width);
492             }
493             d += dd;
494             s += ds;
495             cptr += ds;
496             s24 += ds;
497             y++;
498         } else {
499             if (y_start >= 0) {
500                 /* flush to display */
501                 dpy_gfx_update(ts->con, 0, y_start,
502                                ts->width, y - y_start);
503                 y_start = -1;
504             }
505             d += dd * 4;
506             s += ds * 4;
507             cptr += ds * 4;
508             s24 += ds * 4;
509             y += 4;
510         }
511     }
512     if (y_start >= 0) {
513         /* flush to display */
514         dpy_gfx_update(ts->con, 0, y_start,
515                        ts->width, y - y_start);
516     }
517     /* reset modified pages */
518     if (page_max >= page_min) {
519         tcx_reset_dirty(ts, page_min, page_max - page_min);
520     }
521 }
522 
523 static void tcx_invalidate_display(void *opaque)
524 {
525     TCXState *s = opaque;
526 
527     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
528     qemu_console_resize(s->con, s->width, s->height);
529 }
530 
531 static void tcx24_invalidate_display(void *opaque)
532 {
533     TCXState *s = opaque;
534 
535     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
536     qemu_console_resize(s->con, s->width, s->height);
537 }
538 
539 static int vmstate_tcx_post_load(void *opaque, int version_id)
540 {
541     TCXState *s = opaque;
542 
543     update_palette_entries(s, 0, 256);
544     tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
545     return 0;
546 }
547 
548 static const VMStateDescription vmstate_tcx = {
549     .name ="tcx",
550     .version_id = 4,
551     .minimum_version_id = 4,
552     .post_load = vmstate_tcx_post_load,
553     .fields = (VMStateField[]) {
554         VMSTATE_UINT16(height, TCXState),
555         VMSTATE_UINT16(width, TCXState),
556         VMSTATE_UINT16(depth, TCXState),
557         VMSTATE_BUFFER(r, TCXState),
558         VMSTATE_BUFFER(g, TCXState),
559         VMSTATE_BUFFER(b, TCXState),
560         VMSTATE_UINT8(dac_index, TCXState),
561         VMSTATE_UINT8(dac_state, TCXState),
562         VMSTATE_END_OF_LIST()
563     }
564 };
565 
566 static void tcx_reset(DeviceState *d)
567 {
568     TCXState *s = TCX(d);
569 
570     /* Initialize palette */
571     memset(s->r, 0, 260);
572     memset(s->g, 0, 260);
573     memset(s->b, 0, 260);
574     s->r[255] = s->g[255] = s->b[255] = 255;
575     s->r[256] = s->g[256] = s->b[256] = 255;
576     s->r[258] = s->g[258] = s->b[258] = 255;
577     update_palette_entries(s, 0, 260);
578     memset(s->vram, 0, MAXX*MAXY);
579     memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
580                               DIRTY_MEMORY_VGA);
581     s->dac_index = 0;
582     s->dac_state = 0;
583     s->cursx = 0xf000; /* Put cursor off screen */
584     s->cursy = 0xf000;
585 }
586 
587 static uint64_t tcx_dac_readl(void *opaque, hwaddr addr,
588                               unsigned size)
589 {
590     TCXState *s = opaque;
591     uint32_t val = 0;
592 
593     switch (s->dac_state) {
594     case 0:
595         val = s->r[s->dac_index] << 24;
596         s->dac_state++;
597         break;
598     case 1:
599         val = s->g[s->dac_index] << 24;
600         s->dac_state++;
601         break;
602     case 2:
603         val = s->b[s->dac_index] << 24;
604         s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
605     default:
606         s->dac_state = 0;
607         break;
608     }
609 
610     return val;
611 }
612 
613 static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
614                            unsigned size)
615 {
616     TCXState *s = opaque;
617     unsigned index;
618 
619     switch (addr) {
620     case 0: /* Address */
621         s->dac_index = val >> 24;
622         s->dac_state = 0;
623         break;
624     case 4:  /* Pixel colours */
625     case 12: /* Overlay (cursor) colours */
626         if (addr & 8) {
627             index = (s->dac_index & 3) + 256;
628         } else {
629             index = s->dac_index;
630         }
631         switch (s->dac_state) {
632         case 0:
633             s->r[index] = val >> 24;
634             update_palette_entries(s, index, index + 1);
635             s->dac_state++;
636             break;
637         case 1:
638             s->g[index] = val >> 24;
639             update_palette_entries(s, index, index + 1);
640             s->dac_state++;
641             break;
642         case 2:
643             s->b[index] = val >> 24;
644             update_palette_entries(s, index, index + 1);
645             s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
646         default:
647             s->dac_state = 0;
648             break;
649         }
650         break;
651     default: /* Control registers */
652         break;
653     }
654 }
655 
656 static const MemoryRegionOps tcx_dac_ops = {
657     .read = tcx_dac_readl,
658     .write = tcx_dac_writel,
659     .endianness = DEVICE_NATIVE_ENDIAN,
660     .valid = {
661         .min_access_size = 4,
662         .max_access_size = 4,
663     },
664 };
665 
666 static uint64_t tcx_stip_readl(void *opaque, hwaddr addr,
667                                unsigned size)
668 {
669     return 0;
670 }
671 
672 static void tcx_stip_writel(void *opaque, hwaddr addr,
673                             uint64_t val, unsigned size)
674 {
675     TCXState *s = opaque;
676     int i;
677     uint32_t col;
678 
679     if (!(addr & 4)) {
680         s->tmpblit = val;
681     } else {
682         addr = (addr >> 3) & 0xfffff;
683         col = cpu_to_be32(s->tmpblit);
684         if (s->depth == 24) {
685             for (i = 0; i < 32; i++)  {
686                 if (val & 0x80000000) {
687                     s->vram[addr + i] = s->tmpblit;
688                     s->vram24[addr + i] = col;
689                 }
690                 val <<= 1;
691             }
692         } else {
693             for (i = 0; i < 32; i++)  {
694                 if (val & 0x80000000) {
695                     s->vram[addr + i] = s->tmpblit;
696                 }
697                 val <<= 1;
698             }
699         }
700         memory_region_set_dirty(&s->vram_mem, addr, 32);
701     }
702 }
703 
704 static void tcx_rstip_writel(void *opaque, hwaddr addr,
705                              uint64_t val, unsigned size)
706 {
707     TCXState *s = opaque;
708     int i;
709     uint32_t col;
710 
711     if (!(addr & 4)) {
712         s->tmpblit = val;
713     } else {
714         addr = (addr >> 3) & 0xfffff;
715         col = cpu_to_be32(s->tmpblit);
716         if (s->depth == 24) {
717             for (i = 0; i < 32; i++) {
718                 if (val & 0x80000000) {
719                     s->vram[addr + i] = s->tmpblit;
720                     s->vram24[addr + i] = col;
721                     s->cplane[addr + i] = col;
722                 }
723                 val <<= 1;
724             }
725         } else {
726             for (i = 0; i < 32; i++)  {
727                 if (val & 0x80000000) {
728                     s->vram[addr + i] = s->tmpblit;
729                 }
730                 val <<= 1;
731             }
732         }
733         memory_region_set_dirty(&s->vram_mem, addr, 32);
734     }
735 }
736 
737 static const MemoryRegionOps tcx_stip_ops = {
738     .read = tcx_stip_readl,
739     .write = tcx_stip_writel,
740     .endianness = DEVICE_NATIVE_ENDIAN,
741     .valid = {
742         .min_access_size = 4,
743         .max_access_size = 4,
744     },
745 };
746 
747 static const MemoryRegionOps tcx_rstip_ops = {
748     .read = tcx_stip_readl,
749     .write = tcx_rstip_writel,
750     .endianness = DEVICE_NATIVE_ENDIAN,
751     .valid = {
752         .min_access_size = 4,
753         .max_access_size = 4,
754     },
755 };
756 
757 static uint64_t tcx_blit_readl(void *opaque, hwaddr addr,
758                                unsigned size)
759 {
760     return 0;
761 }
762 
763 static void tcx_blit_writel(void *opaque, hwaddr addr,
764                             uint64_t val, unsigned size)
765 {
766     TCXState *s = opaque;
767     uint32_t adsr, len;
768     int i;
769 
770     if (!(addr & 4)) {
771         s->tmpblit = val;
772     } else {
773         addr = (addr >> 3) & 0xfffff;
774         adsr = val & 0xffffff;
775         len = ((val >> 24) & 0x1f) + 1;
776         if (adsr == 0xffffff) {
777             memset(&s->vram[addr], s->tmpblit, len);
778             if (s->depth == 24) {
779                 val = s->tmpblit & 0xffffff;
780                 val = cpu_to_be32(val);
781                 for (i = 0; i < len; i++) {
782                     s->vram24[addr + i] = val;
783                 }
784             }
785         } else {
786             memcpy(&s->vram[addr], &s->vram[adsr], len);
787             if (s->depth == 24) {
788                 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
789             }
790         }
791         memory_region_set_dirty(&s->vram_mem, addr, len);
792     }
793 }
794 
795 static void tcx_rblit_writel(void *opaque, hwaddr addr,
796                          uint64_t val, unsigned size)
797 {
798     TCXState *s = opaque;
799     uint32_t adsr, len;
800     int i;
801 
802     if (!(addr & 4)) {
803         s->tmpblit = val;
804     } else {
805         addr = (addr >> 3) & 0xfffff;
806         adsr = val & 0xffffff;
807         len = ((val >> 24) & 0x1f) + 1;
808         if (adsr == 0xffffff) {
809             memset(&s->vram[addr], s->tmpblit, len);
810             if (s->depth == 24) {
811                 val = s->tmpblit & 0xffffff;
812                 val = cpu_to_be32(val);
813                 for (i = 0; i < len; i++) {
814                     s->vram24[addr + i] = val;
815                     s->cplane[addr + i] = val;
816                 }
817             }
818         } else {
819             memcpy(&s->vram[addr], &s->vram[adsr], len);
820             if (s->depth == 24) {
821                 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
822                 memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4);
823             }
824         }
825         memory_region_set_dirty(&s->vram_mem, addr, len);
826     }
827 }
828 
829 static const MemoryRegionOps tcx_blit_ops = {
830     .read = tcx_blit_readl,
831     .write = tcx_blit_writel,
832     .endianness = DEVICE_NATIVE_ENDIAN,
833     .valid = {
834         .min_access_size = 4,
835         .max_access_size = 4,
836     },
837 };
838 
839 static const MemoryRegionOps tcx_rblit_ops = {
840     .read = tcx_blit_readl,
841     .write = tcx_rblit_writel,
842     .endianness = DEVICE_NATIVE_ENDIAN,
843     .valid = {
844         .min_access_size = 4,
845         .max_access_size = 4,
846     },
847 };
848 
849 static void tcx_invalidate_cursor_position(TCXState *s)
850 {
851     int ymin, ymax, start, end;
852 
853     /* invalidate only near the cursor */
854     ymin = s->cursy;
855     if (ymin >= s->height) {
856         return;
857     }
858     ymax = MIN(s->height, ymin + 32);
859     start = ymin * 1024;
860     end   = ymax * 1024;
861 
862     memory_region_set_dirty(&s->vram_mem, start, end-start);
863 }
864 
865 static uint64_t tcx_thc_readl(void *opaque, hwaddr addr,
866                             unsigned size)
867 {
868     TCXState *s = opaque;
869     uint64_t val;
870 
871     if (addr == TCX_THC_MISC) {
872         val = s->thcmisc | 0x02000000;
873     } else {
874         val = 0;
875     }
876     return val;
877 }
878 
879 static void tcx_thc_writel(void *opaque, hwaddr addr,
880                          uint64_t val, unsigned size)
881 {
882     TCXState *s = opaque;
883 
884     if (addr == TCX_THC_CURSXY) {
885         tcx_invalidate_cursor_position(s);
886         s->cursx = val >> 16;
887         s->cursy = val;
888         tcx_invalidate_cursor_position(s);
889     } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) {
890         s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val;
891         tcx_invalidate_cursor_position(s);
892     } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) {
893         s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val;
894         tcx_invalidate_cursor_position(s);
895     } else if (addr == TCX_THC_MISC) {
896         s->thcmisc = val;
897     }
898 
899 }
900 
901 static const MemoryRegionOps tcx_thc_ops = {
902     .read = tcx_thc_readl,
903     .write = tcx_thc_writel,
904     .endianness = DEVICE_NATIVE_ENDIAN,
905     .valid = {
906         .min_access_size = 4,
907         .max_access_size = 4,
908     },
909 };
910 
911 static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr,
912                             unsigned size)
913 {
914     return 0;
915 }
916 
917 static void tcx_dummy_writel(void *opaque, hwaddr addr,
918                          uint64_t val, unsigned size)
919 {
920     return;
921 }
922 
923 static const MemoryRegionOps tcx_dummy_ops = {
924     .read = tcx_dummy_readl,
925     .write = tcx_dummy_writel,
926     .endianness = DEVICE_NATIVE_ENDIAN,
927     .valid = {
928         .min_access_size = 4,
929         .max_access_size = 4,
930     },
931 };
932 
933 static const GraphicHwOps tcx_ops = {
934     .invalidate = tcx_invalidate_display,
935     .gfx_update = tcx_update_display,
936 };
937 
938 static const GraphicHwOps tcx24_ops = {
939     .invalidate = tcx24_invalidate_display,
940     .gfx_update = tcx24_update_display,
941 };
942 
943 static void tcx_initfn(Object *obj)
944 {
945     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
946     TCXState *s = TCX(obj);
947 
948     memory_region_init_ram(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE,
949                            &error_fatal);
950     memory_region_set_readonly(&s->rom, true);
951     sysbus_init_mmio(sbd, &s->rom);
952 
953     /* 2/STIP : Stippler */
954     memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip",
955                           TCX_STIP_NREGS);
956     sysbus_init_mmio(sbd, &s->stip);
957 
958     /* 3/BLIT : Blitter */
959     memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit",
960                           TCX_BLIT_NREGS);
961     sysbus_init_mmio(sbd, &s->blit);
962 
963     /* 5/RSTIP : Raw Stippler */
964     memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip",
965                           TCX_RSTIP_NREGS);
966     sysbus_init_mmio(sbd, &s->rstip);
967 
968     /* 6/RBLIT : Raw Blitter */
969     memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit",
970                           TCX_RBLIT_NREGS);
971     sysbus_init_mmio(sbd, &s->rblit);
972 
973     /* 7/TEC : ??? */
974     memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec",
975                           TCX_TEC_NREGS);
976     sysbus_init_mmio(sbd, &s->tec);
977 
978     /* 8/CMAP : DAC */
979     memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac",
980                           TCX_DAC_NREGS);
981     sysbus_init_mmio(sbd, &s->dac);
982 
983     /* 9/THC : Cursor */
984     memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc",
985                           TCX_THC_NREGS);
986     sysbus_init_mmio(sbd, &s->thc);
987 
988     /* 11/DHC : ??? */
989     memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc",
990                           TCX_DHC_NREGS);
991     sysbus_init_mmio(sbd, &s->dhc);
992 
993     /* 12/ALT : ??? */
994     memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt",
995                           TCX_ALT_NREGS);
996     sysbus_init_mmio(sbd, &s->alt);
997 }
998 
999 static void tcx_realizefn(DeviceState *dev, Error **errp)
1000 {
1001     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
1002     TCXState *s = TCX(dev);
1003     ram_addr_t vram_offset = 0;
1004     int size, ret;
1005     uint8_t *vram_base;
1006     char *fcode_filename;
1007 
1008     memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram",
1009                            s->vram_size * (1 + 4 + 4), &error_fatal);
1010     vmstate_register_ram_global(&s->vram_mem);
1011     memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
1012     vram_base = memory_region_get_ram_ptr(&s->vram_mem);
1013 
1014     /* 10/ROM : FCode ROM */
1015     vmstate_register_ram_global(&s->rom);
1016     fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE);
1017     if (fcode_filename) {
1018         ret = load_image_targphys(fcode_filename, s->prom_addr,
1019                                   FCODE_MAX_ROM_SIZE);
1020         g_free(fcode_filename);
1021         if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
1022             error_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
1023         }
1024     }
1025 
1026     /* 0/DFB8 : 8-bit plane */
1027     s->vram = vram_base;
1028     size = s->vram_size;
1029     memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit",
1030                              &s->vram_mem, vram_offset, size);
1031     sysbus_init_mmio(sbd, &s->vram_8bit);
1032     vram_offset += size;
1033     vram_base += size;
1034 
1035     /* 1/DFB24 : 24bit plane */
1036     size = s->vram_size * 4;
1037     s->vram24 = (uint32_t *)vram_base;
1038     s->vram24_offset = vram_offset;
1039     memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit",
1040                              &s->vram_mem, vram_offset, size);
1041     sysbus_init_mmio(sbd, &s->vram_24bit);
1042     vram_offset += size;
1043     vram_base += size;
1044 
1045     /* 4/RDFB32 : Raw Framebuffer */
1046     size = s->vram_size * 4;
1047     s->cplane = (uint32_t *)vram_base;
1048     s->cplane_offset = vram_offset;
1049     memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane",
1050                              &s->vram_mem, vram_offset, size);
1051     sysbus_init_mmio(sbd, &s->vram_cplane);
1052 
1053     /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */
1054     if (s->depth == 8) {
1055         memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s,
1056                               "tcx.thc24", TCX_THC_NREGS);
1057         sysbus_init_mmio(sbd, &s->thc24);
1058     }
1059 
1060     sysbus_init_irq(sbd, &s->irq);
1061 
1062     if (s->depth == 8) {
1063         s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s);
1064     } else {
1065         s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s);
1066     }
1067     s->thcmisc = 0;
1068 
1069     qemu_console_resize(s->con, s->width, s->height);
1070 }
1071 
1072 static Property tcx_properties[] = {
1073     DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1),
1074     DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
1075     DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
1076     DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
1077     DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1),
1078     DEFINE_PROP_END_OF_LIST(),
1079 };
1080 
1081 static void tcx_class_init(ObjectClass *klass, void *data)
1082 {
1083     DeviceClass *dc = DEVICE_CLASS(klass);
1084 
1085     dc->realize = tcx_realizefn;
1086     dc->reset = tcx_reset;
1087     dc->vmsd = &vmstate_tcx;
1088     dc->props = tcx_properties;
1089 }
1090 
1091 static const TypeInfo tcx_info = {
1092     .name          = TYPE_TCX,
1093     .parent        = TYPE_SYS_BUS_DEVICE,
1094     .instance_size = sizeof(TCXState),
1095     .instance_init = tcx_initfn,
1096     .class_init    = tcx_class_init,
1097 };
1098 
1099 static void tcx_register_types(void)
1100 {
1101     type_register_static(&tcx_info);
1102 }
1103 
1104 type_init(tcx_register_types)
1105