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