xref: /openbmc/qemu/hw/display/artist.c (revision 19f70347)
1 /*
2  * QEMU HP Artist Emulation
3  *
4  * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or later.
7  */
8 
9 #include "qemu/osdep.h"
10 #include "qemu-common.h"
11 #include "qemu/error-report.h"
12 #include "qemu/typedefs.h"
13 #include "qemu/log.h"
14 #include "qemu/module.h"
15 #include "qemu/units.h"
16 #include "qapi/error.h"
17 #include "hw/sysbus.h"
18 #include "hw/loader.h"
19 #include "hw/qdev-core.h"
20 #include "hw/qdev-properties.h"
21 #include "migration/vmstate.h"
22 #include "ui/console.h"
23 #include "trace.h"
24 #include "hw/display/framebuffer.h"
25 
26 #define TYPE_ARTIST "artist"
27 #define ARTIST(obj) OBJECT_CHECK(ARTISTState, (obj), TYPE_ARTIST)
28 
29 #ifdef HOST_WORDS_BIGENDIAN
30 #define ROP8OFF(_i) (3 - (_i))
31 #else
32 #define ROP8OFF
33 #endif
34 
35 struct vram_buffer {
36     MemoryRegion mr;
37     uint8_t *data;
38     int size;
39     int width;
40     int height;
41 };
42 
43 typedef struct ARTISTState {
44     SysBusDevice parent_obj;
45 
46     QemuConsole *con;
47     MemoryRegion vram_mem;
48     MemoryRegion mem_as_root;
49     MemoryRegion reg;
50     MemoryRegionSection fbsection;
51 
52     void *vram_int_mr;
53     AddressSpace as;
54 
55     struct vram_buffer vram_buffer[16];
56 
57     uint16_t width;
58     uint16_t height;
59     uint16_t depth;
60 
61     uint32_t fg_color;
62     uint32_t bg_color;
63 
64     uint32_t vram_char_y;
65     uint32_t vram_bitmask;
66 
67     uint32_t vram_start;
68     uint32_t vram_pos;
69 
70     uint32_t vram_size;
71 
72     uint32_t blockmove_source;
73     uint32_t blockmove_dest;
74     uint32_t blockmove_size;
75 
76     uint32_t line_size;
77     uint32_t line_end;
78     uint32_t line_xy;
79     uint32_t line_pattern_start;
80     uint32_t line_pattern_skip;
81 
82     uint32_t cursor_pos;
83 
84     uint32_t cursor_height;
85     uint32_t cursor_width;
86 
87     uint32_t plane_mask;
88 
89     uint32_t reg_100080;
90     uint32_t reg_300200;
91     uint32_t reg_300208;
92     uint32_t reg_300218;
93 
94     uint32_t cmap_bm_access;
95     uint32_t dst_bm_access;
96     uint32_t src_bm_access;
97     uint32_t control_plane;
98     uint32_t transfer_data;
99     uint32_t image_bitmap_op;
100 
101     uint32_t font_write1;
102     uint32_t font_write2;
103     uint32_t font_write_pos_y;
104 
105     int draw_line_pattern;
106 } ARTISTState;
107 
108 typedef enum {
109     ARTIST_BUFFER_AP = 1,
110     ARTIST_BUFFER_OVERLAY = 2,
111     ARTIST_BUFFER_CURSOR1 = 6,
112     ARTIST_BUFFER_CURSOR2 = 7,
113     ARTIST_BUFFER_ATTRIBUTE = 13,
114     ARTIST_BUFFER_CMAP = 15,
115 } artist_buffer_t;
116 
117 typedef enum {
118     VRAM_IDX = 0x1004a0,
119     VRAM_BITMASK = 0x1005a0,
120     VRAM_WRITE_INCR_X = 0x100600,
121     VRAM_WRITE_INCR_X2 = 0x100604,
122     VRAM_WRITE_INCR_Y = 0x100620,
123     VRAM_START = 0x100800,
124     BLOCK_MOVE_SIZE = 0x100804,
125     BLOCK_MOVE_SOURCE = 0x100808,
126     TRANSFER_DATA = 0x100820,
127     FONT_WRITE_INCR_Y = 0x1008a0,
128     VRAM_START_TRIGGER = 0x100a00,
129     VRAM_SIZE_TRIGGER = 0x100a04,
130     FONT_WRITE_START = 0x100aa0,
131     BLOCK_MOVE_DEST_TRIGGER = 0x100b00,
132     BLOCK_MOVE_SIZE_TRIGGER = 0x100b04,
133     LINE_XY = 0x100ccc,
134     PATTERN_LINE_START = 0x100ecc,
135     LINE_SIZE = 0x100e04,
136     LINE_END = 0x100e44,
137     CMAP_BM_ACCESS = 0x118000,
138     DST_BM_ACCESS = 0x118004,
139     SRC_BM_ACCESS = 0x118008,
140     CONTROL_PLANE = 0x11800c,
141     FG_COLOR = 0x118010,
142     BG_COLOR = 0x118014,
143     PLANE_MASK = 0x118018,
144     IMAGE_BITMAP_OP = 0x11801c,
145     CURSOR_POS = 0x300100,
146     CURSOR_CTRL = 0x300104,
147 } artist_reg_t;
148 
149 typedef enum {
150     ARTIST_ROP_CLEAR = 0,
151     ARTIST_ROP_COPY = 3,
152     ARTIST_ROP_XOR = 6,
153     ARTIST_ROP_NOT_DST = 10,
154     ARTIST_ROP_SET = 15,
155 } artist_rop_t;
156 
157 #define REG_NAME(_x) case _x: return " "#_x;
158 static const char *artist_reg_name(uint64_t addr)
159 {
160     switch ((artist_reg_t)addr) {
161     REG_NAME(VRAM_IDX);
162     REG_NAME(VRAM_BITMASK);
163     REG_NAME(VRAM_WRITE_INCR_X);
164     REG_NAME(VRAM_WRITE_INCR_X2);
165     REG_NAME(VRAM_WRITE_INCR_Y);
166     REG_NAME(VRAM_START);
167     REG_NAME(BLOCK_MOVE_SIZE);
168     REG_NAME(BLOCK_MOVE_SOURCE);
169     REG_NAME(FG_COLOR);
170     REG_NAME(BG_COLOR);
171     REG_NAME(PLANE_MASK);
172     REG_NAME(VRAM_START_TRIGGER);
173     REG_NAME(VRAM_SIZE_TRIGGER);
174     REG_NAME(BLOCK_MOVE_DEST_TRIGGER);
175     REG_NAME(BLOCK_MOVE_SIZE_TRIGGER);
176     REG_NAME(TRANSFER_DATA);
177     REG_NAME(CONTROL_PLANE);
178     REG_NAME(IMAGE_BITMAP_OP);
179     REG_NAME(CMAP_BM_ACCESS);
180     REG_NAME(DST_BM_ACCESS);
181     REG_NAME(SRC_BM_ACCESS);
182     REG_NAME(CURSOR_POS);
183     REG_NAME(CURSOR_CTRL);
184     REG_NAME(LINE_XY);
185     REG_NAME(PATTERN_LINE_START);
186     REG_NAME(LINE_SIZE);
187     REG_NAME(LINE_END);
188     REG_NAME(FONT_WRITE_INCR_Y);
189     REG_NAME(FONT_WRITE_START);
190     }
191     return "";
192 }
193 #undef REG_NAME
194 
195 static int16_t artist_get_x(uint32_t reg)
196 {
197     return reg >> 16;
198 }
199 
200 static int16_t artist_get_y(uint32_t reg)
201 {
202     return reg & 0xffff;
203 }
204 
205 static void artist_invalidate_lines(struct vram_buffer *buf,
206                                     int starty, int height)
207 {
208     int start = starty * buf->width;
209     int size = height * buf->width;
210 
211     if (start + size <= buf->size) {
212         memory_region_set_dirty(&buf->mr, start, size);
213     }
214 }
215 
216 static int vram_write_pix_per_transfer(ARTISTState *s)
217 {
218     if (s->cmap_bm_access) {
219         return 1 << ((s->cmap_bm_access >> 27) & 0x0f);
220     } else {
221         return 1 << ((s->dst_bm_access >> 27) & 0x0f);
222     }
223 }
224 
225 static int vram_pixel_length(ARTISTState *s)
226 {
227     if (s->cmap_bm_access) {
228         return (s->cmap_bm_access >> 24) & 0x07;
229     } else {
230         return (s->dst_bm_access >> 24) & 0x07;
231     }
232 }
233 
234 static int vram_write_bufidx(ARTISTState *s)
235 {
236     if (s->cmap_bm_access) {
237         return (s->cmap_bm_access >> 12) & 0x0f;
238     } else {
239         return (s->dst_bm_access >> 12) & 0x0f;
240     }
241 }
242 
243 static int vram_read_bufidx(ARTISTState *s)
244 {
245     if (s->cmap_bm_access) {
246         return (s->cmap_bm_access >> 12) & 0x0f;
247     } else {
248         return (s->src_bm_access >> 12) & 0x0f;
249     }
250 }
251 
252 static struct vram_buffer *vram_read_buffer(ARTISTState *s)
253 {
254     return &s->vram_buffer[vram_read_bufidx(s)];
255 }
256 
257 static struct vram_buffer *vram_write_buffer(ARTISTState *s)
258 {
259     return &s->vram_buffer[vram_write_bufidx(s)];
260 }
261 
262 static uint8_t artist_get_color(ARTISTState *s)
263 {
264     if (s->image_bitmap_op & 2) {
265         return s->fg_color;
266     } else {
267         return s->bg_color;
268     }
269 }
270 
271 static artist_rop_t artist_get_op(ARTISTState *s)
272 {
273     return (s->image_bitmap_op >> 8) & 0xf;
274 }
275 
276 static void artist_rop8(ARTISTState *s, uint8_t *dst, uint8_t val)
277 {
278 
279     const artist_rop_t op = artist_get_op(s);
280     uint8_t plane_mask = s->plane_mask & 0xff;
281 
282     switch (op) {
283     case ARTIST_ROP_CLEAR:
284         *dst &= ~plane_mask;
285         break;
286 
287     case ARTIST_ROP_COPY:
288         *dst &= ~plane_mask;
289         *dst |= val & plane_mask;
290         break;
291 
292     case ARTIST_ROP_XOR:
293         *dst ^= val & plane_mask;
294         break;
295 
296     case ARTIST_ROP_NOT_DST:
297         *dst ^= plane_mask;
298         break;
299 
300     case ARTIST_ROP_SET:
301         *dst |= plane_mask;
302         break;
303 
304     default:
305         qemu_log_mask(LOG_UNIMP, "%s: unsupported rop %d\n", __func__, op);
306         break;
307     }
308 }
309 
310 static void artist_get_cursor_pos(ARTISTState *s, int *x, int *y)
311 {
312     /*
313      * Don't know whether these magic offset values are configurable via
314      * some register. They are the same for all resolutions, so don't
315      * bother about it.
316      */
317 
318     *y = 0x47a - artist_get_y(s->cursor_pos);
319     *x = ((artist_get_x(s->cursor_pos) - 338) / 2);
320 
321     if (*x > s->width) {
322         *x = 0;
323     }
324 
325     if (*y > s->height) {
326         *y = 0;
327     }
328 }
329 
330 static void artist_invalidate_cursor(ARTISTState *s)
331 {
332     int x, y;
333     artist_get_cursor_pos(s, &x, &y);
334     artist_invalidate_lines(&s->vram_buffer[ARTIST_BUFFER_AP],
335                             y, s->cursor_height);
336 }
337 
338 static void vram_bit_write(ARTISTState *s, int posx, int posy, bool incr_x,
339                            int size, uint32_t data)
340 {
341     struct vram_buffer *buf;
342     uint32_t vram_bitmask = s->vram_bitmask;
343     int mask, i, pix_count, pix_length, offset, height, width;
344     uint8_t *data8, *p;
345 
346     pix_count = vram_write_pix_per_transfer(s);
347     pix_length = vram_pixel_length(s);
348 
349     buf = vram_write_buffer(s);
350     height = buf->height;
351     width = buf->width;
352 
353     if (s->cmap_bm_access) {
354         offset = s->vram_pos;
355     } else {
356         offset = posy * width + posx;
357     }
358 
359     if (!buf->size) {
360         qemu_log("write to non-existent buffer\n");
361         return;
362     }
363 
364     p = buf->data;
365 
366     if (pix_count > size * 8) {
367         pix_count = size * 8;
368     }
369 
370     if (posy * width + posx + pix_count > buf->size) {
371         qemu_log("write outside bounds: wants %dx%d, max size %dx%d\n",
372                  posx, posy, width, height);
373         return;
374     }
375 
376 
377     switch (pix_length) {
378     case 0:
379         if (s->image_bitmap_op & 0x20000000) {
380             data &= vram_bitmask;
381         }
382 
383         for (i = 0; i < pix_count; i++) {
384             artist_rop8(s, p + offset + pix_count - 1 - i,
385                         (data & 1) ? (s->plane_mask >> 24) : 0);
386             data >>= 1;
387         }
388         memory_region_set_dirty(&buf->mr, offset, pix_count);
389         break;
390 
391     case 3:
392         if (s->cmap_bm_access) {
393             *(uint32_t *)(p + offset) = data;
394             break;
395         }
396         data8 = (uint8_t *)&data;
397 
398         for (i = 3; i >= 0; i--) {
399             if (!(s->image_bitmap_op & 0x20000000) ||
400                 s->vram_bitmask & (1 << (28 + i))) {
401                 artist_rop8(s, p + offset + 3 - i, data8[ROP8OFF(i)]);
402             }
403         }
404         memory_region_set_dirty(&buf->mr, offset, 3);
405         break;
406 
407     case 6:
408         switch (size) {
409         default:
410         case 4:
411             vram_bitmask = s->vram_bitmask;
412             break;
413 
414         case 2:
415             vram_bitmask = s->vram_bitmask >> 16;
416             break;
417 
418         case 1:
419             vram_bitmask = s->vram_bitmask >> 24;
420             break;
421         }
422 
423         for (i = 0; i < pix_count; i++) {
424             mask = 1 << (pix_count - 1 - i);
425 
426             if (!(s->image_bitmap_op & 0x20000000) ||
427                 (vram_bitmask & mask)) {
428                 if (data & mask) {
429                     artist_rop8(s, p + offset + i, s->fg_color);
430                 } else {
431                     if (!(s->image_bitmap_op & 0x10000002)) {
432                         artist_rop8(s, p + offset + i, s->bg_color);
433                     }
434                 }
435             }
436         }
437         memory_region_set_dirty(&buf->mr, offset, pix_count);
438         break;
439 
440     default:
441         qemu_log_mask(LOG_UNIMP, "%s: unknown pixel length %d\n",
442                       __func__, pix_length);
443         break;
444     }
445 
446     if (incr_x) {
447         if (s->cmap_bm_access) {
448             s->vram_pos += 4;
449         } else {
450             s->vram_pos += pix_count << 2;
451         }
452     }
453 
454     if (vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR1 ||
455         vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR2) {
456         artist_invalidate_cursor(s);
457     }
458 }
459 
460 static void block_move(ARTISTState *s, int source_x, int source_y, int dest_x,
461                        int dest_y, int width, int height)
462 {
463     struct vram_buffer *buf;
464     int line, endline, lineincr, startcolumn, endcolumn, columnincr, column;
465     uint32_t dst, src;
466 
467     trace_artist_block_move(source_x, source_y, dest_x, dest_y, width, height);
468 
469     if (s->control_plane != 0) {
470         /* We don't support CONTROL_PLANE accesses */
471         qemu_log_mask(LOG_UNIMP, "%s: CONTROL_PLANE: %08x\n", __func__,
472                       s->control_plane);
473         return;
474     }
475 
476     buf = &s->vram_buffer[ARTIST_BUFFER_AP];
477 
478     if (dest_y > source_y) {
479         /* move down */
480         line = height - 1;
481         endline = -1;
482         lineincr = -1;
483     } else {
484         /* move up */
485         line = 0;
486         endline = height;
487         lineincr = 1;
488     }
489 
490     if (dest_x > source_x) {
491         /* move right */
492         startcolumn = width - 1;
493         endcolumn = -1;
494         columnincr = -1;
495     } else {
496         /* move left */
497         startcolumn = 0;
498         endcolumn = width;
499         columnincr = 1;
500     }
501 
502     for ( ; line != endline; line += lineincr) {
503         src = source_x + ((line + source_y) * buf->width);
504         dst = dest_x + ((line + dest_y) * buf->width);
505 
506         for (column = startcolumn; column != endcolumn; column += columnincr) {
507             if (dst + column > buf->size || src + column > buf->size) {
508                 continue;
509             }
510             artist_rop8(s, buf->data + dst + column, buf->data[src + column]);
511         }
512     }
513 
514     artist_invalidate_lines(buf, dest_y, height);
515 }
516 
517 static void fill_window(ARTISTState *s, int startx, int starty,
518                         int width, int height)
519 {
520     uint32_t offset;
521     uint8_t color = artist_get_color(s);
522     struct vram_buffer *buf;
523     int x, y;
524 
525     trace_artist_fill_window(startx, starty, width, height,
526                              s->image_bitmap_op, s->control_plane);
527 
528     if (s->control_plane != 0) {
529         /* We don't support CONTROL_PLANE accesses */
530         qemu_log_mask(LOG_UNIMP, "%s: CONTROL_PLANE: %08x\n", __func__,
531                       s->control_plane);
532         return;
533     }
534 
535     if (s->reg_100080 == 0x7d) {
536         /*
537          * Not sure what this register really does, but
538          * 0x7d seems to enable autoincremt of the Y axis
539          * by the current block move height.
540          */
541         height = artist_get_y(s->blockmove_size);
542         s->vram_start += height;
543     }
544 
545     buf = &s->vram_buffer[ARTIST_BUFFER_AP];
546 
547     for (y = starty; y < starty + height; y++) {
548         offset = y * s->width;
549 
550         for (x = startx; x < startx + width; x++) {
551             artist_rop8(s, buf->data + offset + x, color);
552         }
553     }
554     artist_invalidate_lines(buf, starty, height);
555 }
556 
557 static void draw_line(ARTISTState *s, int x1, int y1, int x2, int y2,
558                       bool update_start, int skip_pix, int max_pix)
559 {
560     struct vram_buffer *buf;
561     uint8_t color = artist_get_color(s);
562     int dx, dy, t, e, x, y, incy, diago, horiz;
563     bool c1;
564     uint8_t *p;
565 
566 
567     if (update_start) {
568         s->vram_start = (x2 << 16) | y2;
569     }
570 
571     buf = &s->vram_buffer[ARTIST_BUFFER_AP];
572 
573     c1 = false;
574     incy = 1;
575 
576     if (x2 > x1) {
577         dx = x2 - x1;
578     } else {
579         dx = x1 - x2;
580     }
581     if (y2 > y1) {
582         dy = y2 - y1;
583     } else {
584         dy = y1 - y2;
585     }
586     if (dy > dx) {
587         t = y2;
588         y2 = x2;
589         x2 = t;
590 
591         t = y1;
592         y1 = x1;
593         x1 = t;
594 
595         t = dx;
596         dx = dy;
597         dy = t;
598 
599         c1 = true;
600     }
601 
602     if (x1 > x2) {
603         t = y2;
604         y2 = y1;
605         y1 = t;
606 
607         t = x1;
608         x1 = x2;
609         x2 = t;
610     }
611 
612     horiz = dy << 1;
613     diago = (dy - dx) << 1;
614     e = (dy << 1) - dx;
615 
616     if (y1 <= y2) {
617         incy = 1;
618     } else {
619         incy = -1;
620     }
621     x = x1;
622     y = y1;
623 
624     do {
625         if (c1) {
626             p = buf->data + x * s->width + y;
627         } else {
628             p = buf->data + y * s->width + x;
629         }
630 
631         if (skip_pix > 0) {
632             skip_pix--;
633         } else {
634             artist_rop8(s, p, color);
635         }
636 
637         if (e > 0) {
638             artist_invalidate_lines(buf, y, 1);
639             y  += incy;
640             e  += diago;
641         } else {
642             e += horiz;
643         }
644         x++;
645     } while (x <= x2 && (max_pix == -1 || --max_pix > 0));
646 }
647 
648 static void draw_line_pattern_start(ARTISTState *s)
649 {
650 
651     int startx = artist_get_x(s->vram_start);
652     int starty = artist_get_y(s->vram_start);
653     int endx = artist_get_x(s->blockmove_size);
654     int endy = artist_get_y(s->blockmove_size);
655     int pstart = s->line_pattern_start >> 16;
656 
657     trace_artist_draw_line(startx, starty, endx, endy);
658     draw_line(s, startx, starty, endx, endy, false, -1, pstart);
659     s->line_pattern_skip = pstart;
660 }
661 
662 static void draw_line_pattern_next(ARTISTState *s)
663 {
664 
665     int startx = artist_get_x(s->vram_start);
666     int starty = artist_get_y(s->vram_start);
667     int endx = artist_get_x(s->blockmove_size);
668     int endy = artist_get_y(s->blockmove_size);
669     int line_xy = s->line_xy >> 16;
670 
671     trace_artist_draw_line(startx, starty, endx, endy);
672     draw_line(s, startx, starty, endx, endy, false, s->line_pattern_skip,
673               s->line_pattern_skip + line_xy);
674     s->line_pattern_skip += line_xy;
675     s->image_bitmap_op ^= 2;
676 }
677 
678 static void draw_line_size(ARTISTState *s, bool update_start)
679 {
680 
681     int startx = artist_get_x(s->vram_start);
682     int starty = artist_get_y(s->vram_start);
683     int endx = artist_get_x(s->line_size);
684     int endy = artist_get_y(s->line_size);
685 
686     trace_artist_draw_line(startx, starty, endx, endy);
687     draw_line(s, startx, starty, endx, endy, update_start, -1, -1);
688 }
689 
690 static void draw_line_xy(ARTISTState *s, bool update_start)
691 {
692 
693     int startx = artist_get_x(s->vram_start);
694     int starty = artist_get_y(s->vram_start);
695     int sizex = artist_get_x(s->blockmove_size);
696     int sizey = artist_get_y(s->blockmove_size);
697     int linexy = s->line_xy >> 16;
698     int endx, endy;
699 
700     endx = startx;
701     endy = starty;
702 
703     if (sizex > 0) {
704         endx = startx + linexy;
705     }
706 
707     if (sizex < 0) {
708         endx = startx;
709         startx -= linexy;
710     }
711 
712     if (sizey > 0) {
713         endy = starty + linexy;
714     }
715 
716     if (sizey < 0) {
717         endy = starty;
718         starty -= linexy;
719     }
720 
721     if (startx < 0) {
722         startx = 0;
723     }
724 
725     if (endx < 0) {
726         endx = 0;
727     }
728 
729     if (starty < 0) {
730         starty = 0;
731     }
732 
733     if (endy < 0) {
734         endy = 0;
735     }
736 
737 
738     if (endx < 0) {
739         return;
740     }
741 
742     if (endy < 0) {
743         return;
744     }
745 
746     trace_artist_draw_line(startx, starty, endx, endy);
747     draw_line(s, startx, starty, endx, endy, false, -1, -1);
748 }
749 
750 static void draw_line_end(ARTISTState *s, bool update_start)
751 {
752 
753     int startx = artist_get_x(s->vram_start);
754     int starty = artist_get_y(s->vram_start);
755     int endx = artist_get_x(s->line_end);
756     int endy = artist_get_y(s->line_end);
757 
758     trace_artist_draw_line(startx, starty, endx, endy);
759     draw_line(s, startx, starty, endx, endy, update_start, -1, -1);
760 }
761 
762 static void font_write16(ARTISTState *s, uint16_t val)
763 {
764     struct vram_buffer *buf;
765     uint32_t color = (s->image_bitmap_op & 2) ? s->fg_color : s->bg_color;
766     uint16_t mask;
767     int i;
768 
769     int startx = artist_get_x(s->vram_start);
770     int starty = artist_get_y(s->vram_start) + s->font_write_pos_y;
771     int offset = starty * s->width + startx;
772 
773     buf = &s->vram_buffer[ARTIST_BUFFER_AP];
774 
775     if (offset + 16 > buf->size) {
776         return;
777     }
778 
779     for (i = 0; i < 16; i++) {
780         mask = 1 << (15 - i);
781         if (val & mask) {
782             artist_rop8(s, buf->data + offset + i, color);
783         } else {
784             if (!(s->image_bitmap_op & 0x20000000)) {
785                 artist_rop8(s, buf->data + offset + i, s->bg_color);
786             }
787         }
788     }
789     artist_invalidate_lines(buf, starty, 1);
790 }
791 
792 static void font_write(ARTISTState *s, uint32_t val)
793 {
794     font_write16(s, val >> 16);
795     if (++s->font_write_pos_y == artist_get_y(s->blockmove_size)) {
796         s->vram_start += (s->blockmove_size & 0xffff0000);
797         return;
798     }
799 
800     font_write16(s, val & 0xffff);
801     if (++s->font_write_pos_y == artist_get_y(s->blockmove_size)) {
802         s->vram_start += (s->blockmove_size & 0xffff0000);
803         return;
804     }
805 }
806 
807 static void combine_write_reg(hwaddr addr, uint64_t val, int size, void *out)
808 {
809     /*
810      * FIXME: is there a qemu helper for this?
811      */
812 
813 #ifndef HOST_WORDS_BIGENDIAN
814     addr ^= 3;
815 #endif
816 
817     switch (size) {
818     case 1:
819         *(uint8_t *)(out + (addr & 3)) = val;
820         break;
821 
822     case 2:
823         *(uint16_t *)(out + (addr & 2)) = val;
824         break;
825 
826     case 4:
827         *(uint32_t *)out = val;
828         break;
829 
830     default:
831         qemu_log_mask(LOG_UNIMP, "unsupported write size: %d\n", size);
832     }
833 }
834 
835 static void artist_reg_write(void *opaque, hwaddr addr, uint64_t val,
836                              unsigned size)
837 {
838     ARTISTState *s = opaque;
839     int posx, posy;
840     int width, height;
841 
842     trace_artist_reg_write(size, addr, artist_reg_name(addr & ~3ULL), val);
843 
844     switch (addr & ~3ULL) {
845     case 0x100080:
846         combine_write_reg(addr, val, size, &s->reg_100080);
847         break;
848 
849     case FG_COLOR:
850         combine_write_reg(addr, val, size, &s->fg_color);
851         break;
852 
853     case BG_COLOR:
854         combine_write_reg(addr, val, size, &s->bg_color);
855         break;
856 
857     case VRAM_BITMASK:
858         combine_write_reg(addr, val, size, &s->vram_bitmask);
859         break;
860 
861     case VRAM_WRITE_INCR_Y:
862         posx = (s->vram_pos >> 2) & 0x7ff;
863         posy = (s->vram_pos >> 13) & 0x3ff;
864         vram_bit_write(s, posx, posy + s->vram_char_y++, false, size, val);
865         break;
866 
867     case VRAM_WRITE_INCR_X:
868     case VRAM_WRITE_INCR_X2:
869         posx = (s->vram_pos >> 2) & 0x7ff;
870         posy = (s->vram_pos >> 13) & 0x3ff;
871         vram_bit_write(s, posx, posy + s->vram_char_y, true, size, val);
872         break;
873 
874     case VRAM_IDX:
875         combine_write_reg(addr, val, size, &s->vram_pos);
876         s->vram_char_y = 0;
877         s->draw_line_pattern = 0;
878         break;
879 
880     case VRAM_START:
881         combine_write_reg(addr, val, size, &s->vram_start);
882         s->draw_line_pattern = 0;
883         break;
884 
885     case VRAM_START_TRIGGER:
886         combine_write_reg(addr, val, size, &s->vram_start);
887         fill_window(s, artist_get_x(s->vram_start),
888                     artist_get_y(s->vram_start),
889                     artist_get_x(s->blockmove_size),
890                     artist_get_y(s->blockmove_size));
891         break;
892 
893     case VRAM_SIZE_TRIGGER:
894         combine_write_reg(addr, val, size, &s->vram_size);
895 
896         if (size == 2 && !(addr & 2)) {
897             height = artist_get_y(s->blockmove_size);
898         } else {
899             height = artist_get_y(s->vram_size);
900         }
901 
902         if (size == 2 && (addr & 2)) {
903             width = artist_get_x(s->blockmove_size);
904         } else {
905             width = artist_get_x(s->vram_size);
906         }
907 
908         fill_window(s, artist_get_x(s->vram_start),
909                     artist_get_y(s->vram_start),
910                     width, height);
911         break;
912 
913     case LINE_XY:
914         combine_write_reg(addr, val, size, &s->line_xy);
915         if (s->draw_line_pattern) {
916             draw_line_pattern_next(s);
917         } else {
918             draw_line_xy(s, true);
919         }
920         break;
921 
922     case PATTERN_LINE_START:
923         combine_write_reg(addr, val, size, &s->line_pattern_start);
924         s->draw_line_pattern = 1;
925         draw_line_pattern_start(s);
926         break;
927 
928     case LINE_SIZE:
929         combine_write_reg(addr, val, size, &s->line_size);
930         draw_line_size(s, true);
931         break;
932 
933     case LINE_END:
934         combine_write_reg(addr, val, size, &s->line_end);
935         draw_line_end(s, true);
936         break;
937 
938     case BLOCK_MOVE_SIZE:
939         combine_write_reg(addr, val, size, &s->blockmove_size);
940         break;
941 
942     case BLOCK_MOVE_SOURCE:
943         combine_write_reg(addr, val, size, &s->blockmove_source);
944         break;
945 
946     case BLOCK_MOVE_DEST_TRIGGER:
947         combine_write_reg(addr, val, size, &s->blockmove_dest);
948 
949         block_move(s, artist_get_x(s->blockmove_source),
950                    artist_get_y(s->blockmove_source),
951                    artist_get_x(s->blockmove_dest),
952                    artist_get_y(s->blockmove_dest),
953                    artist_get_x(s->blockmove_size),
954                    artist_get_y(s->blockmove_size));
955         break;
956 
957     case BLOCK_MOVE_SIZE_TRIGGER:
958         combine_write_reg(addr, val, size, &s->blockmove_size);
959 
960         block_move(s,
961                    artist_get_x(s->blockmove_source),
962                    artist_get_y(s->blockmove_source),
963                    artist_get_x(s->vram_start),
964                    artist_get_y(s->vram_start),
965                    artist_get_x(s->blockmove_size),
966                    artist_get_y(s->blockmove_size));
967         break;
968 
969     case PLANE_MASK:
970         combine_write_reg(addr, val, size, &s->plane_mask);
971         break;
972 
973     case CMAP_BM_ACCESS:
974         combine_write_reg(addr, val, size, &s->cmap_bm_access);
975         break;
976 
977     case DST_BM_ACCESS:
978         combine_write_reg(addr, val, size, &s->dst_bm_access);
979         s->cmap_bm_access = 0;
980         break;
981 
982     case SRC_BM_ACCESS:
983         combine_write_reg(addr, val, size, &s->src_bm_access);
984         s->cmap_bm_access = 0;
985         break;
986 
987     case CONTROL_PLANE:
988         combine_write_reg(addr, val, size, &s->control_plane);
989         break;
990 
991     case TRANSFER_DATA:
992         combine_write_reg(addr, val, size, &s->transfer_data);
993         break;
994 
995     case 0x300200:
996         combine_write_reg(addr, val, size, &s->reg_300200);
997         break;
998 
999     case 0x300208:
1000         combine_write_reg(addr, val, size, &s->reg_300208);
1001         break;
1002 
1003     case 0x300218:
1004         combine_write_reg(addr, val, size, &s->reg_300218);
1005         break;
1006 
1007     case CURSOR_POS:
1008         artist_invalidate_cursor(s);
1009         combine_write_reg(addr, val, size, &s->cursor_pos);
1010         artist_invalidate_cursor(s);
1011         break;
1012 
1013     case CURSOR_CTRL:
1014         break;
1015 
1016     case IMAGE_BITMAP_OP:
1017         combine_write_reg(addr, val, size, &s->image_bitmap_op);
1018         break;
1019 
1020     case FONT_WRITE_INCR_Y:
1021         combine_write_reg(addr, val, size, &s->font_write1);
1022         font_write(s, s->font_write1);
1023         break;
1024 
1025     case FONT_WRITE_START:
1026         combine_write_reg(addr, val, size, &s->font_write2);
1027         s->font_write_pos_y = 0;
1028         font_write(s, s->font_write2);
1029         break;
1030 
1031     case 300104:
1032         break;
1033 
1034     default:
1035         qemu_log_mask(LOG_UNIMP, "%s: unknown register: reg=%08" HWADDR_PRIx
1036                       " val=%08" PRIx64 " size=%d\n",
1037                       __func__, addr, val, size);
1038         break;
1039     }
1040 }
1041 
1042 static uint64_t combine_read_reg(hwaddr addr, int size, void *in)
1043 {
1044     /*
1045      * FIXME: is there a qemu helper for this?
1046      */
1047 
1048 #ifndef HOST_WORDS_BIGENDIAN
1049     addr ^= 3;
1050 #endif
1051 
1052     switch (size) {
1053     case 1:
1054         return *(uint8_t *)(in + (addr & 3));
1055 
1056     case 2:
1057         return *(uint16_t *)(in + (addr & 2));
1058 
1059     case 4:
1060         return *(uint32_t *)in;
1061 
1062     default:
1063         qemu_log_mask(LOG_UNIMP, "unsupported read size: %d\n", size);
1064         return 0;
1065     }
1066 }
1067 
1068 static uint64_t artist_reg_read(void *opaque, hwaddr addr, unsigned size)
1069 {
1070     ARTISTState *s = opaque;
1071     uint32_t val = 0;
1072 
1073     switch (addr & ~3ULL) {
1074         /* Unknown status registers */
1075     case 0:
1076         break;
1077 
1078     case 0x211110:
1079         val = (s->width << 16) | s->height;
1080         if (s->depth == 1) {
1081             val |= 1 << 31;
1082         }
1083         break;
1084 
1085     case 0x100000:
1086     case 0x300000:
1087     case 0x300004:
1088     case 0x300308:
1089     case 0x380000:
1090         break;
1091 
1092     case 0x300008:
1093     case 0x380008:
1094         /*
1095          * FIFO ready flag. we're not emulating the FIFOs
1096          * so we're always ready
1097          */
1098         val = 0x10;
1099         break;
1100 
1101     case 0x300200:
1102         val = s->reg_300200;
1103         break;
1104 
1105     case 0x300208:
1106         val = s->reg_300208;
1107         break;
1108 
1109     case 0x300218:
1110         val = s->reg_300218;
1111         break;
1112 
1113     case 0x30023c:
1114         val = 0xac4ffdac;
1115         break;
1116 
1117     case 0x380004:
1118         /* 0x02000000 Buserror */
1119         val = 0x6dc20006;
1120         break;
1121 
1122     default:
1123         qemu_log_mask(LOG_UNIMP, "%s: unknown register: %08" HWADDR_PRIx
1124                       " size %d\n", __func__, addr, size);
1125         break;
1126     }
1127     val = combine_read_reg(addr, size, &val);
1128     trace_artist_reg_read(size, addr, artist_reg_name(addr & ~3ULL), val);
1129     return val;
1130 }
1131 
1132 static void artist_vram_write(void *opaque, hwaddr addr, uint64_t val,
1133                               unsigned size)
1134 {
1135     ARTISTState *s = opaque;
1136     struct vram_buffer *buf;
1137     int posy = (addr >> 11) & 0x3ff;
1138     int posx = addr & 0x7ff;
1139     uint32_t offset;
1140     trace_artist_vram_write(size, addr, val);
1141 
1142     if (s->cmap_bm_access) {
1143         buf = &s->vram_buffer[ARTIST_BUFFER_CMAP];
1144         if (addr + 3 < buf->size) {
1145             *(uint32_t *)(buf->data + addr) = val;
1146         }
1147         return;
1148     }
1149 
1150     buf = vram_write_buffer(s);
1151     if (!buf->size) {
1152         return;
1153     }
1154 
1155     if (posy > buf->height || posx > buf->width) {
1156         return;
1157     }
1158 
1159     offset = posy * buf->width + posx;
1160     switch (size) {
1161     case 4:
1162         *(uint32_t *)(buf->data + offset) = be32_to_cpu(val);
1163         memory_region_set_dirty(&buf->mr, offset, 4);
1164         break;
1165     case 2:
1166         *(uint16_t *)(buf->data + offset) = be16_to_cpu(val);
1167         memory_region_set_dirty(&buf->mr, offset, 2);
1168         break;
1169     case 1:
1170         *(uint8_t *)(buf->data + offset) = val;
1171         memory_region_set_dirty(&buf->mr, offset, 1);
1172         break;
1173     default:
1174         break;
1175     }
1176 }
1177 
1178 static uint64_t artist_vram_read(void *opaque, hwaddr addr, unsigned size)
1179 {
1180     ARTISTState *s = opaque;
1181     struct vram_buffer *buf;
1182     uint64_t val;
1183     int posy, posx;
1184 
1185     if (s->cmap_bm_access) {
1186         buf = &s->vram_buffer[ARTIST_BUFFER_CMAP];
1187         val = *(uint32_t *)(buf->data + addr);
1188         trace_artist_vram_read(size, addr, 0, 0, val);
1189         return 0;
1190     }
1191 
1192     buf = vram_read_buffer(s);
1193     if (!buf->size) {
1194         return 0;
1195     }
1196 
1197     posy = (addr >> 13) & 0x3ff;
1198     posx = (addr >> 2) & 0x7ff;
1199 
1200     if (posy > buf->height || posx > buf->width) {
1201         return 0;
1202     }
1203 
1204     val = cpu_to_be32(*(uint32_t *)(buf->data + posy * buf->width + posx));
1205     trace_artist_vram_read(size, addr, posx, posy, val);
1206     return val;
1207 }
1208 
1209 static const MemoryRegionOps artist_reg_ops = {
1210     .read = artist_reg_read,
1211     .write = artist_reg_write,
1212     .endianness = DEVICE_NATIVE_ENDIAN,
1213     .valid = {
1214         .min_access_size = 1,
1215         .max_access_size = 4,
1216     },
1217 };
1218 
1219 static const MemoryRegionOps artist_vram_ops = {
1220     .read = artist_vram_read,
1221     .write = artist_vram_write,
1222     .endianness = DEVICE_NATIVE_ENDIAN,
1223     .valid = {
1224         .min_access_size = 1,
1225         .max_access_size = 4,
1226     },
1227 };
1228 
1229 static void artist_draw_cursor(ARTISTState *s)
1230 {
1231     DisplaySurface *surface = qemu_console_surface(s->con);
1232     uint32_t *data = (uint32_t *)surface_data(surface);
1233     struct vram_buffer *cursor0, *cursor1 , *buf;
1234     int cx, cy, cursor_pos_x, cursor_pos_y;
1235 
1236     cursor0 = &s->vram_buffer[ARTIST_BUFFER_CURSOR1];
1237     cursor1 = &s->vram_buffer[ARTIST_BUFFER_CURSOR2];
1238     buf = &s->vram_buffer[ARTIST_BUFFER_AP];
1239 
1240     artist_get_cursor_pos(s, &cursor_pos_x, &cursor_pos_y);
1241 
1242     for (cy = 0; cy < s->cursor_height; cy++) {
1243 
1244         for (cx = 0; cx < s->cursor_width; cx++) {
1245 
1246             if (cursor_pos_y + cy < 0 ||
1247                 cursor_pos_x + cx < 0 ||
1248                 cursor_pos_y + cy > buf->height - 1 ||
1249                 cursor_pos_x + cx > buf->width) {
1250                 continue;
1251             }
1252 
1253             int dstoffset = (cursor_pos_y + cy) * s->width +
1254                 (cursor_pos_x + cx);
1255 
1256             if (cursor0->data[cy * cursor0->width + cx]) {
1257                 data[dstoffset] = 0;
1258             } else {
1259                 if (cursor1->data[cy * cursor1->width + cx]) {
1260                     data[dstoffset] = 0xffffff;
1261                 }
1262             }
1263         }
1264     }
1265 }
1266 
1267 static void artist_draw_line(void *opaque, uint8_t *d, const uint8_t *src,
1268                              int width, int pitch)
1269 {
1270     ARTISTState *s = ARTIST(opaque);
1271     uint32_t *cmap, *data = (uint32_t *)d;
1272     int x;
1273 
1274     cmap = (uint32_t *)(s->vram_buffer[ARTIST_BUFFER_CMAP].data + 0x400);
1275 
1276     for (x = 0; x < s->width; x++) {
1277         *data++ = cmap[*src++];
1278     }
1279 }
1280 
1281 static void artist_update_display(void *opaque)
1282 {
1283     ARTISTState *s = opaque;
1284     DisplaySurface *surface = qemu_console_surface(s->con);
1285     int first = 0, last;
1286 
1287 
1288     framebuffer_update_display(surface, &s->fbsection, s->width, s->height,
1289                                s->width, s->width * 4, 0, 0, artist_draw_line,
1290                                s, &first, &last);
1291 
1292     artist_draw_cursor(s);
1293 
1294     dpy_gfx_update(s->con, 0, 0, s->width, s->height);
1295 }
1296 
1297 static void artist_invalidate(void *opaque)
1298 {
1299     ARTISTState *s = ARTIST(opaque);
1300     struct vram_buffer *buf = &s->vram_buffer[ARTIST_BUFFER_AP];
1301     memory_region_set_dirty(&buf->mr, 0, buf->size);
1302 }
1303 
1304 static const GraphicHwOps artist_ops = {
1305     .invalidate  = artist_invalidate,
1306     .gfx_update = artist_update_display,
1307 };
1308 
1309 static void artist_initfn(Object *obj)
1310 {
1311     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1312     ARTISTState *s = ARTIST(obj);
1313 
1314     memory_region_init_io(&s->reg, obj, &artist_reg_ops, s, "artist.reg",
1315                           4 * MiB);
1316     memory_region_init_io(&s->vram_mem, obj, &artist_vram_ops, s, "artist.vram",
1317                           8 * MiB);
1318     sysbus_init_mmio(sbd, &s->reg);
1319     sysbus_init_mmio(sbd, &s->vram_mem);
1320 }
1321 
1322 static void artist_create_buffer(ARTISTState *s, const char *name,
1323                                  hwaddr *offset, unsigned int idx,
1324                                  int width, int height)
1325 {
1326     struct vram_buffer *buf = s->vram_buffer + idx;
1327 
1328     memory_region_init_ram(&buf->mr, NULL, name, width * height,
1329                            &error_fatal);
1330     memory_region_add_subregion_overlap(&s->mem_as_root, *offset, &buf->mr, 0);
1331 
1332     buf->data = memory_region_get_ram_ptr(&buf->mr);
1333     buf->size = height * width;
1334     buf->width = width;
1335     buf->height = height;
1336 
1337     *offset += buf->size;
1338 }
1339 
1340 static void artist_realizefn(DeviceState *dev, Error **errp)
1341 {
1342     ARTISTState *s = ARTIST(dev);
1343     struct vram_buffer *buf;
1344     hwaddr offset = 0;
1345 
1346     memory_region_init(&s->mem_as_root, OBJECT(dev), "artist", ~0ull);
1347     address_space_init(&s->as, &s->mem_as_root, "artist");
1348 
1349     artist_create_buffer(s, "cmap", &offset, ARTIST_BUFFER_CMAP, 2048, 4);
1350     artist_create_buffer(s, "ap", &offset, ARTIST_BUFFER_AP,
1351                          s->width, s->height);
1352     artist_create_buffer(s, "cursor1", &offset, ARTIST_BUFFER_CURSOR1, 64, 64);
1353     artist_create_buffer(s, "cursor2", &offset, ARTIST_BUFFER_CURSOR2, 64, 64);
1354     artist_create_buffer(s, "attribute", &offset, ARTIST_BUFFER_ATTRIBUTE,
1355                          64, 64);
1356 
1357     buf = &s->vram_buffer[ARTIST_BUFFER_AP];
1358     framebuffer_update_memory_section(&s->fbsection, &buf->mr, 0,
1359                                       buf->width, buf->height);
1360     /*
1361      * no idea whether the cursor is fixed size or not, so assume 32x32 which
1362      * seems sufficient for HP-UX X11.
1363      */
1364     s->cursor_height = 32;
1365     s->cursor_width = 32;
1366 
1367     s->con = graphic_console_init(DEVICE(dev), 0, &artist_ops, s);
1368     qemu_console_resize(s->con, s->width, s->height);
1369 }
1370 
1371 static int vmstate_artist_post_load(void *opaque, int version_id)
1372 {
1373     artist_invalidate(opaque);
1374     return 0;
1375 }
1376 
1377 static const VMStateDescription vmstate_artist = {
1378     .name = "artist",
1379     .version_id = 1,
1380     .minimum_version_id = 1,
1381     .post_load = vmstate_artist_post_load,
1382     .fields = (VMStateField[]) {
1383         VMSTATE_UINT16(height, ARTISTState),
1384         VMSTATE_UINT16(width, ARTISTState),
1385         VMSTATE_UINT16(depth, ARTISTState),
1386         VMSTATE_UINT32(fg_color, ARTISTState),
1387         VMSTATE_UINT32(bg_color, ARTISTState),
1388         VMSTATE_UINT32(vram_char_y, ARTISTState),
1389         VMSTATE_UINT32(vram_bitmask, ARTISTState),
1390         VMSTATE_UINT32(vram_start, ARTISTState),
1391         VMSTATE_UINT32(vram_pos, ARTISTState),
1392         VMSTATE_UINT32(vram_size, ARTISTState),
1393         VMSTATE_UINT32(blockmove_source, ARTISTState),
1394         VMSTATE_UINT32(blockmove_dest, ARTISTState),
1395         VMSTATE_UINT32(blockmove_size, ARTISTState),
1396         VMSTATE_UINT32(line_size, ARTISTState),
1397         VMSTATE_UINT32(line_end, ARTISTState),
1398         VMSTATE_UINT32(line_xy, ARTISTState),
1399         VMSTATE_UINT32(cursor_pos, ARTISTState),
1400         VMSTATE_UINT32(cursor_height, ARTISTState),
1401         VMSTATE_UINT32(cursor_width, ARTISTState),
1402         VMSTATE_UINT32(plane_mask, ARTISTState),
1403         VMSTATE_UINT32(reg_100080, ARTISTState),
1404         VMSTATE_UINT32(reg_300200, ARTISTState),
1405         VMSTATE_UINT32(reg_300208, ARTISTState),
1406         VMSTATE_UINT32(reg_300218, ARTISTState),
1407         VMSTATE_UINT32(cmap_bm_access, ARTISTState),
1408         VMSTATE_UINT32(dst_bm_access, ARTISTState),
1409         VMSTATE_UINT32(src_bm_access, ARTISTState),
1410         VMSTATE_UINT32(control_plane, ARTISTState),
1411         VMSTATE_UINT32(transfer_data, ARTISTState),
1412         VMSTATE_UINT32(image_bitmap_op, ARTISTState),
1413         VMSTATE_UINT32(font_write1, ARTISTState),
1414         VMSTATE_UINT32(font_write2, ARTISTState),
1415         VMSTATE_UINT32(font_write_pos_y, ARTISTState),
1416         VMSTATE_END_OF_LIST()
1417     }
1418 };
1419 
1420 static Property artist_properties[] = {
1421     DEFINE_PROP_UINT16("width",        ARTISTState, width, 1280),
1422     DEFINE_PROP_UINT16("height",       ARTISTState, height, 1024),
1423     DEFINE_PROP_UINT16("depth",        ARTISTState, depth, 8),
1424     DEFINE_PROP_END_OF_LIST(),
1425 };
1426 
1427 static void artist_reset(DeviceState *qdev)
1428 {
1429 }
1430 
1431 static void artist_class_init(ObjectClass *klass, void *data)
1432 {
1433     DeviceClass *dc = DEVICE_CLASS(klass);
1434 
1435     dc->realize = artist_realizefn;
1436     dc->vmsd = &vmstate_artist;
1437     dc->reset = artist_reset;
1438     device_class_set_props(dc, artist_properties);
1439 }
1440 
1441 static const TypeInfo artist_info = {
1442     .name          = TYPE_ARTIST,
1443     .parent        = TYPE_SYS_BUS_DEVICE,
1444     .instance_size = sizeof(ARTISTState),
1445     .instance_init = artist_initfn,
1446     .class_init    = artist_class_init,
1447 };
1448 
1449 static void artist_register_types(void)
1450 {
1451     type_register_static(&artist_info);
1452 }
1453 
1454 type_init(artist_register_types)
1455