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