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