xref: /openbmc/qemu/hw/display/tcx.c (revision 9884abee)
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 "qemu-common.h"
27 #include "ui/console.h"
28 #include "ui/pixel_ops.h"
29 #include "hw/loader.h"
30 #include "hw/sysbus.h"
31 #include "qemu/error-report.h"
32 
33 #define TCX_ROM_FILE "QEMU,tcx.bin"
34 #define FCODE_MAX_ROM_SIZE 0x10000
35 
36 #define MAXX 1024
37 #define MAXY 768
38 #define TCX_DAC_NREGS    16
39 #define TCX_THC_NREGS    0x1000
40 #define TCX_DHC_NREGS    0x4000
41 #define TCX_TEC_NREGS    0x1000
42 #define TCX_ALT_NREGS    0x8000
43 #define TCX_STIP_NREGS   0x800000
44 #define TCX_BLIT_NREGS   0x800000
45 #define TCX_RSTIP_NREGS  0x800000
46 #define TCX_RBLIT_NREGS  0x800000
47 
48 #define TCX_THC_MISC     0x818
49 #define TCX_THC_CURSXY   0x8fc
50 #define TCX_THC_CURSMASK 0x900
51 #define TCX_THC_CURSBITS 0x980
52 
53 #define TYPE_TCX "SUNW,tcx"
54 #define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX)
55 
56 typedef struct TCXState {
57     SysBusDevice parent_obj;
58 
59     QemuConsole *con;
60     qemu_irq irq;
61     uint8_t *vram;
62     uint32_t *vram24, *cplane;
63     hwaddr prom_addr;
64     MemoryRegion rom;
65     MemoryRegion vram_mem;
66     MemoryRegion vram_8bit;
67     MemoryRegion vram_24bit;
68     MemoryRegion stip;
69     MemoryRegion blit;
70     MemoryRegion vram_cplane;
71     MemoryRegion rstip;
72     MemoryRegion rblit;
73     MemoryRegion tec;
74     MemoryRegion dac;
75     MemoryRegion thc;
76     MemoryRegion dhc;
77     MemoryRegion alt;
78     MemoryRegion thc24;
79 
80     ram_addr_t vram24_offset, cplane_offset;
81     uint32_t tmpblit;
82     uint32_t vram_size;
83     uint32_t palette[260];
84     uint8_t r[260], g[260], b[260];
85     uint16_t width, height, depth;
86     uint8_t dac_index, dac_state;
87     uint32_t thcmisc;
88     uint32_t cursmask[32];
89     uint32_t cursbits[32];
90     uint16_t cursx;
91     uint16_t cursy;
92 } TCXState;
93 
94 static void tcx_set_dirty(TCXState *s)
95 {
96     memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY);
97 }
98 
99 static inline int tcx24_check_dirty(TCXState *s, ram_addr_t page,
100                                     ram_addr_t page24, ram_addr_t cpage)
101 {
102     int ret;
103 
104     ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE,
105                                   DIRTY_MEMORY_VGA);
106     ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4,
107                                    DIRTY_MEMORY_VGA);
108     ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4,
109                                    DIRTY_MEMORY_VGA);
110     return ret;
111 }
112 
113 static inline void tcx24_reset_dirty(TCXState *ts, ram_addr_t page_min,
114                                ram_addr_t page_max, ram_addr_t page24,
115                               ram_addr_t cpage)
116 {
117     memory_region_reset_dirty(&ts->vram_mem,
118                               page_min,
119                               (page_max - page_min) + TARGET_PAGE_SIZE,
120                               DIRTY_MEMORY_VGA);
121     memory_region_reset_dirty(&ts->vram_mem,
122                               page24 + page_min * 4,
123                               (page_max - page_min) * 4 + TARGET_PAGE_SIZE,
124                               DIRTY_MEMORY_VGA);
125     memory_region_reset_dirty(&ts->vram_mem,
126                               cpage + page_min * 4,
127                               (page_max - page_min) * 4 + TARGET_PAGE_SIZE,
128                               DIRTY_MEMORY_VGA);
129 }
130 
131 static void update_palette_entries(TCXState *s, int start, int end)
132 {
133     DisplaySurface *surface = qemu_console_surface(s->con);
134     int i;
135 
136     for (i = start; i < end; i++) {
137         switch (surface_bits_per_pixel(surface)) {
138         default:
139         case 8:
140             s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
141             break;
142         case 15:
143             s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
144             break;
145         case 16:
146             s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
147             break;
148         case 32:
149             if (is_surface_bgr(surface)) {
150                 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
151             } else {
152                 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
153             }
154             break;
155         }
156     }
157     tcx_set_dirty(s);
158 }
159 
160 static void tcx_draw_line32(TCXState *s1, uint8_t *d,
161                             const uint8_t *s, int width)
162 {
163     int x;
164     uint8_t val;
165     uint32_t *p = (uint32_t *)d;
166 
167     for (x = 0; x < width; x++) {
168         val = *s++;
169         *p++ = s1->palette[val];
170     }
171 }
172 
173 static void tcx_draw_line16(TCXState *s1, uint8_t *d,
174                             const uint8_t *s, int width)
175 {
176     int x;
177     uint8_t val;
178     uint16_t *p = (uint16_t *)d;
179 
180     for (x = 0; x < width; x++) {
181         val = *s++;
182         *p++ = s1->palette[val];
183     }
184 }
185 
186 static void tcx_draw_line8(TCXState *s1, uint8_t *d,
187                            const uint8_t *s, int width)
188 {
189     int x;
190     uint8_t val;
191 
192     for(x = 0; x < width; x++) {
193         val = *s++;
194         *d++ = s1->palette[val];
195     }
196 }
197 
198 static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
199                               int y, int width)
200 {
201     int x, len;
202     uint32_t mask, bits;
203     uint32_t *p = (uint32_t *)d;
204 
205     y = y - s1->cursy;
206     mask = s1->cursmask[y];
207     bits = s1->cursbits[y];
208     len = MIN(width - s1->cursx, 32);
209     p = &p[s1->cursx];
210     for (x = 0; x < len; x++) {
211         if (mask & 0x80000000) {
212             if (bits & 0x80000000) {
213                 *p = s1->palette[259];
214             } else {
215                 *p = s1->palette[258];
216             }
217         }
218         p++;
219         mask <<= 1;
220         bits <<= 1;
221     }
222 }
223 
224 static void tcx_draw_cursor16(TCXState *s1, uint8_t *d,
225                               int y, int width)
226 {
227     int x, len;
228     uint32_t mask, bits;
229     uint16_t *p = (uint16_t *)d;
230 
231     y = y - s1->cursy;
232     mask = s1->cursmask[y];
233     bits = s1->cursbits[y];
234     len = MIN(width - s1->cursx, 32);
235     p = &p[s1->cursx];
236     for (x = 0; x < len; x++) {
237         if (mask & 0x80000000) {
238             if (bits & 0x80000000) {
239                 *p = s1->palette[259];
240             } else {
241                 *p = s1->palette[258];
242             }
243         }
244         p++;
245         mask <<= 1;
246         bits <<= 1;
247     }
248 }
249 
250 static void tcx_draw_cursor8(TCXState *s1, uint8_t *d,
251                               int y, int width)
252 {
253     int x, len;
254     uint32_t mask, bits;
255 
256     y = y - s1->cursy;
257     mask = s1->cursmask[y];
258     bits = s1->cursbits[y];
259     len = MIN(width - s1->cursx, 32);
260     d = &d[s1->cursx];
261     for (x = 0; x < len; x++) {
262         if (mask & 0x80000000) {
263             if (bits & 0x80000000) {
264                 *d = s1->palette[259];
265             } else {
266                 *d = s1->palette[258];
267             }
268         }
269         d++;
270         mask <<= 1;
271         bits <<= 1;
272     }
273 }
274 
275 /*
276   XXX Could be much more optimal:
277   * detect if line/page/whole screen is in 24 bit mode
278   * if destination is also BGR, use memcpy
279   */
280 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
281                                      const uint8_t *s, int width,
282                                      const uint32_t *cplane,
283                                      const uint32_t *s24)
284 {
285     DisplaySurface *surface = qemu_console_surface(s1->con);
286     int x, bgr, r, g, b;
287     uint8_t val, *p8;
288     uint32_t *p = (uint32_t *)d;
289     uint32_t dval;
290     bgr = is_surface_bgr(surface);
291     for(x = 0; x < width; x++, s++, s24++) {
292         if (be32_to_cpu(*cplane) & 0x03000000) {
293             /* 24-bit direct, BGR order */
294             p8 = (uint8_t *)s24;
295             p8++;
296             b = *p8++;
297             g = *p8++;
298             r = *p8;
299             if (bgr)
300                 dval = rgb_to_pixel32bgr(r, g, b);
301             else
302                 dval = rgb_to_pixel32(r, g, b);
303         } else {
304             /* 8-bit pseudocolor */
305             val = *s;
306             dval = s1->palette[val];
307         }
308         *p++ = dval;
309         cplane++;
310     }
311 }
312 
313 /* Fixed line length 1024 allows us to do nice tricks not possible on
314    VGA... */
315 
316 static void tcx_update_display(void *opaque)
317 {
318     TCXState *ts = opaque;
319     DisplaySurface *surface = qemu_console_surface(ts->con);
320     ram_addr_t page, page_min, page_max;
321     int y, y_start, dd, ds;
322     uint8_t *d, *s;
323     void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
324     void (*fc)(TCXState *s1, uint8_t *dst, int y, int width);
325 
326     if (surface_bits_per_pixel(surface) == 0) {
327         return;
328     }
329 
330     page = 0;
331     y_start = -1;
332     page_min = -1;
333     page_max = 0;
334     d = surface_data(surface);
335     s = ts->vram;
336     dd = surface_stride(surface);
337     ds = 1024;
338 
339     switch (surface_bits_per_pixel(surface)) {
340     case 32:
341         f = tcx_draw_line32;
342         fc = tcx_draw_cursor32;
343         break;
344     case 15:
345     case 16:
346         f = tcx_draw_line16;
347         fc = tcx_draw_cursor16;
348         break;
349     default:
350     case 8:
351         f = tcx_draw_line8;
352         fc = tcx_draw_cursor8;
353         break;
354     case 0:
355         return;
356     }
357 
358     memory_region_sync_dirty_bitmap(&ts->vram_mem);
359     for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) {
360         if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE,
361                                     DIRTY_MEMORY_VGA)) {
362             if (y_start < 0)
363                 y_start = y;
364             if (page < page_min)
365                 page_min = page;
366             if (page > page_max)
367                 page_max = page;
368 
369             f(ts, d, s, ts->width);
370             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
371                 fc(ts, d, y, ts->width);
372             }
373             d += dd;
374             s += ds;
375             y++;
376 
377             f(ts, d, s, ts->width);
378             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
379                 fc(ts, d, y, ts->width);
380             }
381             d += dd;
382             s += ds;
383             y++;
384 
385             f(ts, d, s, ts->width);
386             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
387                 fc(ts, d, y, ts->width);
388             }
389             d += dd;
390             s += ds;
391             y++;
392 
393             f(ts, d, s, ts->width);
394             if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
395                 fc(ts, d, y, ts->width);
396             }
397             d += dd;
398             s += ds;
399             y++;
400         } else {
401             if (y_start >= 0) {
402                 /* flush to display */
403                 dpy_gfx_update(ts->con, 0, y_start,
404                                ts->width, y - y_start);
405                 y_start = -1;
406             }
407             d += dd * 4;
408             s += ds * 4;
409             y += 4;
410         }
411     }
412     if (y_start >= 0) {
413         /* flush to display */
414         dpy_gfx_update(ts->con, 0, y_start,
415                        ts->width, y - y_start);
416     }
417     /* reset modified pages */
418     if (page_max >= page_min) {
419         memory_region_reset_dirty(&ts->vram_mem,
420                                   page_min,
421                                   (page_max - page_min) + TARGET_PAGE_SIZE,
422                                   DIRTY_MEMORY_VGA);
423     }
424 }
425 
426 static void tcx24_update_display(void *opaque)
427 {
428     TCXState *ts = opaque;
429     DisplaySurface *surface = qemu_console_surface(ts->con);
430     ram_addr_t page, page_min, page_max, cpage, page24;
431     int y, y_start, dd, ds;
432     uint8_t *d, *s;
433     uint32_t *cptr, *s24;
434 
435     if (surface_bits_per_pixel(surface) != 32) {
436             return;
437     }
438 
439     page = 0;
440     page24 = ts->vram24_offset;
441     cpage = ts->cplane_offset;
442     y_start = -1;
443     page_min = -1;
444     page_max = 0;
445     d = surface_data(surface);
446     s = ts->vram;
447     s24 = ts->vram24;
448     cptr = ts->cplane;
449     dd = surface_stride(surface);
450     ds = 1024;
451 
452     memory_region_sync_dirty_bitmap(&ts->vram_mem);
453     for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE,
454             page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
455         if (tcx24_check_dirty(ts, page, page24, cpage)) {
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         tcx24_reset_dirty(ts, page_min, page_max, page24, cpage);
520     }
521 }
522 
523 static void tcx_invalidate_display(void *opaque)
524 {
525     TCXState *s = opaque;
526 
527     tcx_set_dirty(s);
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);
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);
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