xref: /openbmc/qemu/hw/display/artist.c (revision 65a117da)
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 "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;
562     int dx, dy, t, e, x, y, incy, diago, horiz;
563     bool c1;
564     uint8_t *p;
565 
566     trace_artist_draw_line(x1, y1, x2, y2);
567 
568     if (update_start) {
569         s->vram_start = (x2 << 16) | y2;
570     }
571 
572     if (x2 > x1) {
573         dx = x2 - x1;
574     } else {
575         dx = x1 - x2;
576     }
577     if (y2 > y1) {
578         dy = y2 - y1;
579     } else {
580         dy = y1 - y2;
581     }
582     if (!dx || !dy) {
583         return;
584     }
585 
586     c1 = false;
587     if (dy > dx) {
588         t = y2;
589         y2 = x2;
590         x2 = t;
591 
592         t = y1;
593         y1 = x1;
594         x1 = t;
595 
596         t = dx;
597         dx = dy;
598         dy = t;
599 
600         c1 = true;
601     }
602 
603     if (x1 > x2) {
604         t = y2;
605         y2 = y1;
606         y1 = t;
607 
608         t = x1;
609         x1 = x2;
610         x2 = t;
611     }
612 
613     horiz = dy << 1;
614     diago = (dy - dx) << 1;
615     e = (dy << 1) - dx;
616 
617     if (y1 <= y2) {
618         incy = 1;
619     } else {
620         incy = -1;
621     }
622     x = x1;
623     y = y1;
624     color = artist_get_color(s);
625     buf = &s->vram_buffer[ARTIST_BUFFER_AP];
626 
627     do {
628         if (c1) {
629             p = buf->data + x * s->width + y;
630         } else {
631             p = buf->data + y * s->width + x;
632         }
633 
634         if (skip_pix > 0) {
635             skip_pix--;
636         } else {
637             artist_rop8(s, p, color);
638         }
639 
640         if (e > 0) {
641             artist_invalidate_lines(buf, y, 1);
642             y  += incy;
643             e  += diago;
644         } else {
645             e += horiz;
646         }
647         x++;
648     } while (x <= x2 && (max_pix == -1 || --max_pix > 0));
649 }
650 
651 static void draw_line_pattern_start(ARTISTState *s)
652 {
653 
654     int startx = artist_get_x(s->vram_start);
655     int starty = artist_get_y(s->vram_start);
656     int endx = artist_get_x(s->blockmove_size);
657     int endy = artist_get_y(s->blockmove_size);
658     int pstart = s->line_pattern_start >> 16;
659 
660     draw_line(s, startx, starty, endx, endy, false, -1, pstart);
661     s->line_pattern_skip = pstart;
662 }
663 
664 static void draw_line_pattern_next(ARTISTState *s)
665 {
666 
667     int startx = artist_get_x(s->vram_start);
668     int starty = artist_get_y(s->vram_start);
669     int endx = artist_get_x(s->blockmove_size);
670     int endy = artist_get_y(s->blockmove_size);
671     int line_xy = s->line_xy >> 16;
672 
673     draw_line(s, startx, starty, endx, endy, false, s->line_pattern_skip,
674               s->line_pattern_skip + line_xy);
675     s->line_pattern_skip += line_xy;
676     s->image_bitmap_op ^= 2;
677 }
678 
679 static void draw_line_size(ARTISTState *s, bool update_start)
680 {
681 
682     int startx = artist_get_x(s->vram_start);
683     int starty = artist_get_y(s->vram_start);
684     int endx = artist_get_x(s->line_size);
685     int endy = artist_get_y(s->line_size);
686 
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     draw_line(s, startx, starty, endx, endy, false, -1, -1);
738 }
739 
740 static void draw_line_end(ARTISTState *s, bool update_start)
741 {
742 
743     int startx = artist_get_x(s->vram_start);
744     int starty = artist_get_y(s->vram_start);
745     int endx = artist_get_x(s->line_end);
746     int endy = artist_get_y(s->line_end);
747 
748     draw_line(s, startx, starty, endx, endy, update_start, -1, -1);
749 }
750 
751 static void font_write16(ARTISTState *s, uint16_t val)
752 {
753     struct vram_buffer *buf;
754     uint32_t color = (s->image_bitmap_op & 2) ? s->fg_color : s->bg_color;
755     uint16_t mask;
756     int i;
757 
758     int startx = artist_get_x(s->vram_start);
759     int starty = artist_get_y(s->vram_start) + s->font_write_pos_y;
760     int offset = starty * s->width + startx;
761 
762     buf = &s->vram_buffer[ARTIST_BUFFER_AP];
763 
764     if (offset + 16 > buf->size) {
765         return;
766     }
767 
768     for (i = 0; i < 16; i++) {
769         mask = 1 << (15 - i);
770         if (val & mask) {
771             artist_rop8(s, buf->data + offset + i, color);
772         } else {
773             if (!(s->image_bitmap_op & 0x20000000)) {
774                 artist_rop8(s, buf->data + offset + i, s->bg_color);
775             }
776         }
777     }
778     artist_invalidate_lines(buf, starty, 1);
779 }
780 
781 static void font_write(ARTISTState *s, uint32_t val)
782 {
783     font_write16(s, val >> 16);
784     if (++s->font_write_pos_y == artist_get_y(s->blockmove_size)) {
785         s->vram_start += (s->blockmove_size & 0xffff0000);
786         return;
787     }
788 
789     font_write16(s, val & 0xffff);
790     if (++s->font_write_pos_y == artist_get_y(s->blockmove_size)) {
791         s->vram_start += (s->blockmove_size & 0xffff0000);
792         return;
793     }
794 }
795 
796 static void combine_write_reg(hwaddr addr, uint64_t val, int size, void *out)
797 {
798     /*
799      * FIXME: is there a qemu helper for this?
800      */
801 
802 #ifndef HOST_WORDS_BIGENDIAN
803     addr ^= 3;
804 #endif
805 
806     switch (size) {
807     case 1:
808         *(uint8_t *)(out + (addr & 3)) = val;
809         break;
810 
811     case 2:
812         *(uint16_t *)(out + (addr & 2)) = val;
813         break;
814 
815     case 4:
816         *(uint32_t *)out = val;
817         break;
818 
819     default:
820         qemu_log_mask(LOG_UNIMP, "unsupported write size: %d\n", size);
821     }
822 }
823 
824 static void artist_reg_write(void *opaque, hwaddr addr, uint64_t val,
825                              unsigned size)
826 {
827     ARTISTState *s = opaque;
828     int posx, posy;
829     int width, height;
830 
831     trace_artist_reg_write(size, addr, artist_reg_name(addr & ~3ULL), val);
832 
833     switch (addr & ~3ULL) {
834     case 0x100080:
835         combine_write_reg(addr, val, size, &s->reg_100080);
836         break;
837 
838     case FG_COLOR:
839         combine_write_reg(addr, val, size, &s->fg_color);
840         break;
841 
842     case BG_COLOR:
843         combine_write_reg(addr, val, size, &s->bg_color);
844         break;
845 
846     case VRAM_BITMASK:
847         combine_write_reg(addr, val, size, &s->vram_bitmask);
848         break;
849 
850     case VRAM_WRITE_INCR_Y:
851         posx = (s->vram_pos >> 2) & 0x7ff;
852         posy = (s->vram_pos >> 13) & 0x3ff;
853         vram_bit_write(s, posx, posy + s->vram_char_y++, false, size, val);
854         break;
855 
856     case VRAM_WRITE_INCR_X:
857     case VRAM_WRITE_INCR_X2:
858         posx = (s->vram_pos >> 2) & 0x7ff;
859         posy = (s->vram_pos >> 13) & 0x3ff;
860         vram_bit_write(s, posx, posy + s->vram_char_y, true, size, val);
861         break;
862 
863     case VRAM_IDX:
864         combine_write_reg(addr, val, size, &s->vram_pos);
865         s->vram_char_y = 0;
866         s->draw_line_pattern = 0;
867         break;
868 
869     case VRAM_START:
870         combine_write_reg(addr, val, size, &s->vram_start);
871         s->draw_line_pattern = 0;
872         break;
873 
874     case VRAM_START_TRIGGER:
875         combine_write_reg(addr, val, size, &s->vram_start);
876         fill_window(s, artist_get_x(s->vram_start),
877                     artist_get_y(s->vram_start),
878                     artist_get_x(s->blockmove_size),
879                     artist_get_y(s->blockmove_size));
880         break;
881 
882     case VRAM_SIZE_TRIGGER:
883         combine_write_reg(addr, val, size, &s->vram_size);
884 
885         if (size == 2 && !(addr & 2)) {
886             height = artist_get_y(s->blockmove_size);
887         } else {
888             height = artist_get_y(s->vram_size);
889         }
890 
891         if (size == 2 && (addr & 2)) {
892             width = artist_get_x(s->blockmove_size);
893         } else {
894             width = artist_get_x(s->vram_size);
895         }
896 
897         fill_window(s, artist_get_x(s->vram_start),
898                     artist_get_y(s->vram_start),
899                     width, height);
900         break;
901 
902     case LINE_XY:
903         combine_write_reg(addr, val, size, &s->line_xy);
904         if (s->draw_line_pattern) {
905             draw_line_pattern_next(s);
906         } else {
907             draw_line_xy(s, true);
908         }
909         break;
910 
911     case PATTERN_LINE_START:
912         combine_write_reg(addr, val, size, &s->line_pattern_start);
913         s->draw_line_pattern = 1;
914         draw_line_pattern_start(s);
915         break;
916 
917     case LINE_SIZE:
918         combine_write_reg(addr, val, size, &s->line_size);
919         draw_line_size(s, true);
920         break;
921 
922     case LINE_END:
923         combine_write_reg(addr, val, size, &s->line_end);
924         draw_line_end(s, true);
925         break;
926 
927     case BLOCK_MOVE_SIZE:
928         combine_write_reg(addr, val, size, &s->blockmove_size);
929         break;
930 
931     case BLOCK_MOVE_SOURCE:
932         combine_write_reg(addr, val, size, &s->blockmove_source);
933         break;
934 
935     case BLOCK_MOVE_DEST_TRIGGER:
936         combine_write_reg(addr, val, size, &s->blockmove_dest);
937 
938         block_move(s, artist_get_x(s->blockmove_source),
939                    artist_get_y(s->blockmove_source),
940                    artist_get_x(s->blockmove_dest),
941                    artist_get_y(s->blockmove_dest),
942                    artist_get_x(s->blockmove_size),
943                    artist_get_y(s->blockmove_size));
944         break;
945 
946     case BLOCK_MOVE_SIZE_TRIGGER:
947         combine_write_reg(addr, val, size, &s->blockmove_size);
948 
949         block_move(s,
950                    artist_get_x(s->blockmove_source),
951                    artist_get_y(s->blockmove_source),
952                    artist_get_x(s->vram_start),
953                    artist_get_y(s->vram_start),
954                    artist_get_x(s->blockmove_size),
955                    artist_get_y(s->blockmove_size));
956         break;
957 
958     case PLANE_MASK:
959         combine_write_reg(addr, val, size, &s->plane_mask);
960         break;
961 
962     case CMAP_BM_ACCESS:
963         combine_write_reg(addr, val, size, &s->cmap_bm_access);
964         break;
965 
966     case DST_BM_ACCESS:
967         combine_write_reg(addr, val, size, &s->dst_bm_access);
968         s->cmap_bm_access = 0;
969         break;
970 
971     case SRC_BM_ACCESS:
972         combine_write_reg(addr, val, size, &s->src_bm_access);
973         s->cmap_bm_access = 0;
974         break;
975 
976     case CONTROL_PLANE:
977         combine_write_reg(addr, val, size, &s->control_plane);
978         break;
979 
980     case TRANSFER_DATA:
981         combine_write_reg(addr, val, size, &s->transfer_data);
982         break;
983 
984     case 0x300200:
985         combine_write_reg(addr, val, size, &s->reg_300200);
986         break;
987 
988     case 0x300208:
989         combine_write_reg(addr, val, size, &s->reg_300208);
990         break;
991 
992     case 0x300218:
993         combine_write_reg(addr, val, size, &s->reg_300218);
994         break;
995 
996     case CURSOR_POS:
997         artist_invalidate_cursor(s);
998         combine_write_reg(addr, val, size, &s->cursor_pos);
999         artist_invalidate_cursor(s);
1000         break;
1001 
1002     case CURSOR_CTRL:
1003         break;
1004 
1005     case IMAGE_BITMAP_OP:
1006         combine_write_reg(addr, val, size, &s->image_bitmap_op);
1007         break;
1008 
1009     case FONT_WRITE_INCR_Y:
1010         combine_write_reg(addr, val, size, &s->font_write1);
1011         font_write(s, s->font_write1);
1012         break;
1013 
1014     case FONT_WRITE_START:
1015         combine_write_reg(addr, val, size, &s->font_write2);
1016         s->font_write_pos_y = 0;
1017         font_write(s, s->font_write2);
1018         break;
1019 
1020     case 300104:
1021         break;
1022 
1023     default:
1024         qemu_log_mask(LOG_UNIMP, "%s: unknown register: reg=%08" HWADDR_PRIx
1025                       " val=%08" PRIx64 " size=%d\n",
1026                       __func__, addr, val, size);
1027         break;
1028     }
1029 }
1030 
1031 static uint64_t combine_read_reg(hwaddr addr, int size, void *in)
1032 {
1033     /*
1034      * FIXME: is there a qemu helper for this?
1035      */
1036 
1037 #ifndef HOST_WORDS_BIGENDIAN
1038     addr ^= 3;
1039 #endif
1040 
1041     switch (size) {
1042     case 1:
1043         return *(uint8_t *)(in + (addr & 3));
1044 
1045     case 2:
1046         return *(uint16_t *)(in + (addr & 2));
1047 
1048     case 4:
1049         return *(uint32_t *)in;
1050 
1051     default:
1052         qemu_log_mask(LOG_UNIMP, "unsupported read size: %d\n", size);
1053         return 0;
1054     }
1055 }
1056 
1057 static uint64_t artist_reg_read(void *opaque, hwaddr addr, unsigned size)
1058 {
1059     ARTISTState *s = opaque;
1060     uint32_t val = 0;
1061 
1062     switch (addr & ~3ULL) {
1063         /* Unknown status registers */
1064     case 0:
1065         break;
1066 
1067     case 0x211110:
1068         val = (s->width << 16) | s->height;
1069         if (s->depth == 1) {
1070             val |= 1 << 31;
1071         }
1072         break;
1073 
1074     case 0x100000:
1075     case 0x300000:
1076     case 0x300004:
1077     case 0x300308:
1078     case 0x380000:
1079         break;
1080 
1081     case 0x300008:
1082     case 0x380008:
1083         /*
1084          * FIFO ready flag. we're not emulating the FIFOs
1085          * so we're always ready
1086          */
1087         val = 0x10;
1088         break;
1089 
1090     case 0x300200:
1091         val = s->reg_300200;
1092         break;
1093 
1094     case 0x300208:
1095         val = s->reg_300208;
1096         break;
1097 
1098     case 0x300218:
1099         val = s->reg_300218;
1100         break;
1101 
1102     case 0x30023c:
1103         val = 0xac4ffdac;
1104         break;
1105 
1106     case 0x380004:
1107         /* 0x02000000 Buserror */
1108         val = 0x6dc20006;
1109         break;
1110 
1111     default:
1112         qemu_log_mask(LOG_UNIMP, "%s: unknown register: %08" HWADDR_PRIx
1113                       " size %d\n", __func__, addr, size);
1114         break;
1115     }
1116     val = combine_read_reg(addr, size, &val);
1117     trace_artist_reg_read(size, addr, artist_reg_name(addr & ~3ULL), val);
1118     return val;
1119 }
1120 
1121 static void artist_vram_write(void *opaque, hwaddr addr, uint64_t val,
1122                               unsigned size)
1123 {
1124     ARTISTState *s = opaque;
1125     struct vram_buffer *buf;
1126     int posy = (addr >> 11) & 0x3ff;
1127     int posx = addr & 0x7ff;
1128     uint32_t offset;
1129     trace_artist_vram_write(size, addr, val);
1130 
1131     if (s->cmap_bm_access) {
1132         buf = &s->vram_buffer[ARTIST_BUFFER_CMAP];
1133         if (addr + 3 < buf->size) {
1134             *(uint32_t *)(buf->data + addr) = val;
1135         }
1136         return;
1137     }
1138 
1139     buf = vram_write_buffer(s);
1140     if (!buf->size) {
1141         return;
1142     }
1143 
1144     if (posy > buf->height || posx > buf->width) {
1145         return;
1146     }
1147 
1148     offset = posy * buf->width + posx;
1149     switch (size) {
1150     case 4:
1151         *(uint32_t *)(buf->data + offset) = be32_to_cpu(val);
1152         memory_region_set_dirty(&buf->mr, offset, 4);
1153         break;
1154     case 2:
1155         *(uint16_t *)(buf->data + offset) = be16_to_cpu(val);
1156         memory_region_set_dirty(&buf->mr, offset, 2);
1157         break;
1158     case 1:
1159         *(uint8_t *)(buf->data + offset) = val;
1160         memory_region_set_dirty(&buf->mr, offset, 1);
1161         break;
1162     default:
1163         break;
1164     }
1165 }
1166 
1167 static uint64_t artist_vram_read(void *opaque, hwaddr addr, unsigned size)
1168 {
1169     ARTISTState *s = opaque;
1170     struct vram_buffer *buf;
1171     uint64_t val;
1172     int posy, posx;
1173 
1174     if (s->cmap_bm_access) {
1175         buf = &s->vram_buffer[ARTIST_BUFFER_CMAP];
1176         val = *(uint32_t *)(buf->data + addr);
1177         trace_artist_vram_read(size, addr, 0, 0, val);
1178         return 0;
1179     }
1180 
1181     buf = vram_read_buffer(s);
1182     if (!buf->size) {
1183         return 0;
1184     }
1185 
1186     posy = (addr >> 13) & 0x3ff;
1187     posx = (addr >> 2) & 0x7ff;
1188 
1189     if (posy > buf->height || posx > buf->width) {
1190         return 0;
1191     }
1192 
1193     val = cpu_to_be32(*(uint32_t *)(buf->data + posy * buf->width + posx));
1194     trace_artist_vram_read(size, addr, posx, posy, val);
1195     return val;
1196 }
1197 
1198 static const MemoryRegionOps artist_reg_ops = {
1199     .read = artist_reg_read,
1200     .write = artist_reg_write,
1201     .endianness = DEVICE_NATIVE_ENDIAN,
1202     .valid = {
1203         .min_access_size = 1,
1204         .max_access_size = 4,
1205     },
1206 };
1207 
1208 static const MemoryRegionOps artist_vram_ops = {
1209     .read = artist_vram_read,
1210     .write = artist_vram_write,
1211     .endianness = DEVICE_NATIVE_ENDIAN,
1212     .valid = {
1213         .min_access_size = 1,
1214         .max_access_size = 4,
1215     },
1216 };
1217 
1218 static void artist_draw_cursor(ARTISTState *s)
1219 {
1220     DisplaySurface *surface = qemu_console_surface(s->con);
1221     uint32_t *data = (uint32_t *)surface_data(surface);
1222     struct vram_buffer *cursor0, *cursor1 , *buf;
1223     int cx, cy, cursor_pos_x, cursor_pos_y;
1224 
1225     cursor0 = &s->vram_buffer[ARTIST_BUFFER_CURSOR1];
1226     cursor1 = &s->vram_buffer[ARTIST_BUFFER_CURSOR2];
1227     buf = &s->vram_buffer[ARTIST_BUFFER_AP];
1228 
1229     artist_get_cursor_pos(s, &cursor_pos_x, &cursor_pos_y);
1230 
1231     for (cy = 0; cy < s->cursor_height; cy++) {
1232 
1233         for (cx = 0; cx < s->cursor_width; cx++) {
1234 
1235             if (cursor_pos_y + cy < 0 ||
1236                 cursor_pos_x + cx < 0 ||
1237                 cursor_pos_y + cy > buf->height - 1 ||
1238                 cursor_pos_x + cx > buf->width) {
1239                 continue;
1240             }
1241 
1242             int dstoffset = (cursor_pos_y + cy) * s->width +
1243                 (cursor_pos_x + cx);
1244 
1245             if (cursor0->data[cy * cursor0->width + cx]) {
1246                 data[dstoffset] = 0;
1247             } else {
1248                 if (cursor1->data[cy * cursor1->width + cx]) {
1249                     data[dstoffset] = 0xffffff;
1250                 }
1251             }
1252         }
1253     }
1254 }
1255 
1256 static void artist_draw_line(void *opaque, uint8_t *d, const uint8_t *src,
1257                              int width, int pitch)
1258 {
1259     ARTISTState *s = ARTIST(opaque);
1260     uint32_t *cmap, *data = (uint32_t *)d;
1261     int x;
1262 
1263     cmap = (uint32_t *)(s->vram_buffer[ARTIST_BUFFER_CMAP].data + 0x400);
1264 
1265     for (x = 0; x < s->width; x++) {
1266         *data++ = cmap[*src++];
1267     }
1268 }
1269 
1270 static void artist_update_display(void *opaque)
1271 {
1272     ARTISTState *s = opaque;
1273     DisplaySurface *surface = qemu_console_surface(s->con);
1274     int first = 0, last;
1275 
1276 
1277     framebuffer_update_display(surface, &s->fbsection, s->width, s->height,
1278                                s->width, s->width * 4, 0, 0, artist_draw_line,
1279                                s, &first, &last);
1280 
1281     artist_draw_cursor(s);
1282 
1283     dpy_gfx_update(s->con, 0, 0, s->width, s->height);
1284 }
1285 
1286 static void artist_invalidate(void *opaque)
1287 {
1288     ARTISTState *s = ARTIST(opaque);
1289     struct vram_buffer *buf = &s->vram_buffer[ARTIST_BUFFER_AP];
1290     memory_region_set_dirty(&buf->mr, 0, buf->size);
1291 }
1292 
1293 static const GraphicHwOps artist_ops = {
1294     .invalidate  = artist_invalidate,
1295     .gfx_update = artist_update_display,
1296 };
1297 
1298 static void artist_initfn(Object *obj)
1299 {
1300     SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1301     ARTISTState *s = ARTIST(obj);
1302 
1303     memory_region_init_io(&s->reg, obj, &artist_reg_ops, s, "artist.reg",
1304                           4 * MiB);
1305     memory_region_init_io(&s->vram_mem, obj, &artist_vram_ops, s, "artist.vram",
1306                           8 * MiB);
1307     sysbus_init_mmio(sbd, &s->reg);
1308     sysbus_init_mmio(sbd, &s->vram_mem);
1309 }
1310 
1311 static void artist_create_buffer(ARTISTState *s, const char *name,
1312                                  hwaddr *offset, unsigned int idx,
1313                                  int width, int height)
1314 {
1315     struct vram_buffer *buf = s->vram_buffer + idx;
1316 
1317     memory_region_init_ram(&buf->mr, NULL, name, width * height,
1318                            &error_fatal);
1319     memory_region_add_subregion_overlap(&s->mem_as_root, *offset, &buf->mr, 0);
1320 
1321     buf->data = memory_region_get_ram_ptr(&buf->mr);
1322     buf->size = height * width;
1323     buf->width = width;
1324     buf->height = height;
1325 
1326     *offset += buf->size;
1327 }
1328 
1329 static void artist_realizefn(DeviceState *dev, Error **errp)
1330 {
1331     ARTISTState *s = ARTIST(dev);
1332     struct vram_buffer *buf;
1333     hwaddr offset = 0;
1334 
1335     memory_region_init(&s->mem_as_root, OBJECT(dev), "artist", ~0ull);
1336     address_space_init(&s->as, &s->mem_as_root, "artist");
1337 
1338     artist_create_buffer(s, "cmap", &offset, ARTIST_BUFFER_CMAP, 2048, 4);
1339     artist_create_buffer(s, "ap", &offset, ARTIST_BUFFER_AP,
1340                          s->width, s->height);
1341     artist_create_buffer(s, "cursor1", &offset, ARTIST_BUFFER_CURSOR1, 64, 64);
1342     artist_create_buffer(s, "cursor2", &offset, ARTIST_BUFFER_CURSOR2, 64, 64);
1343     artist_create_buffer(s, "attribute", &offset, ARTIST_BUFFER_ATTRIBUTE,
1344                          64, 64);
1345 
1346     buf = &s->vram_buffer[ARTIST_BUFFER_AP];
1347     framebuffer_update_memory_section(&s->fbsection, &buf->mr, 0,
1348                                       buf->width, buf->height);
1349     /*
1350      * no idea whether the cursor is fixed size or not, so assume 32x32 which
1351      * seems sufficient for HP-UX X11.
1352      */
1353     s->cursor_height = 32;
1354     s->cursor_width = 32;
1355 
1356     s->con = graphic_console_init(dev, 0, &artist_ops, s);
1357     qemu_console_resize(s->con, s->width, s->height);
1358 }
1359 
1360 static int vmstate_artist_post_load(void *opaque, int version_id)
1361 {
1362     artist_invalidate(opaque);
1363     return 0;
1364 }
1365 
1366 static const VMStateDescription vmstate_artist = {
1367     .name = "artist",
1368     .version_id = 1,
1369     .minimum_version_id = 1,
1370     .post_load = vmstate_artist_post_load,
1371     .fields = (VMStateField[]) {
1372         VMSTATE_UINT16(height, ARTISTState),
1373         VMSTATE_UINT16(width, ARTISTState),
1374         VMSTATE_UINT16(depth, ARTISTState),
1375         VMSTATE_UINT32(fg_color, ARTISTState),
1376         VMSTATE_UINT32(bg_color, ARTISTState),
1377         VMSTATE_UINT32(vram_char_y, ARTISTState),
1378         VMSTATE_UINT32(vram_bitmask, ARTISTState),
1379         VMSTATE_UINT32(vram_start, ARTISTState),
1380         VMSTATE_UINT32(vram_pos, ARTISTState),
1381         VMSTATE_UINT32(vram_size, ARTISTState),
1382         VMSTATE_UINT32(blockmove_source, ARTISTState),
1383         VMSTATE_UINT32(blockmove_dest, ARTISTState),
1384         VMSTATE_UINT32(blockmove_size, ARTISTState),
1385         VMSTATE_UINT32(line_size, ARTISTState),
1386         VMSTATE_UINT32(line_end, ARTISTState),
1387         VMSTATE_UINT32(line_xy, ARTISTState),
1388         VMSTATE_UINT32(cursor_pos, ARTISTState),
1389         VMSTATE_UINT32(cursor_height, ARTISTState),
1390         VMSTATE_UINT32(cursor_width, ARTISTState),
1391         VMSTATE_UINT32(plane_mask, ARTISTState),
1392         VMSTATE_UINT32(reg_100080, ARTISTState),
1393         VMSTATE_UINT32(reg_300200, ARTISTState),
1394         VMSTATE_UINT32(reg_300208, ARTISTState),
1395         VMSTATE_UINT32(reg_300218, ARTISTState),
1396         VMSTATE_UINT32(cmap_bm_access, ARTISTState),
1397         VMSTATE_UINT32(dst_bm_access, ARTISTState),
1398         VMSTATE_UINT32(src_bm_access, ARTISTState),
1399         VMSTATE_UINT32(control_plane, ARTISTState),
1400         VMSTATE_UINT32(transfer_data, ARTISTState),
1401         VMSTATE_UINT32(image_bitmap_op, ARTISTState),
1402         VMSTATE_UINT32(font_write1, ARTISTState),
1403         VMSTATE_UINT32(font_write2, ARTISTState),
1404         VMSTATE_UINT32(font_write_pos_y, ARTISTState),
1405         VMSTATE_END_OF_LIST()
1406     }
1407 };
1408 
1409 static Property artist_properties[] = {
1410     DEFINE_PROP_UINT16("width",        ARTISTState, width, 1280),
1411     DEFINE_PROP_UINT16("height",       ARTISTState, height, 1024),
1412     DEFINE_PROP_UINT16("depth",        ARTISTState, depth, 8),
1413     DEFINE_PROP_END_OF_LIST(),
1414 };
1415 
1416 static void artist_reset(DeviceState *qdev)
1417 {
1418 }
1419 
1420 static void artist_class_init(ObjectClass *klass, void *data)
1421 {
1422     DeviceClass *dc = DEVICE_CLASS(klass);
1423 
1424     dc->realize = artist_realizefn;
1425     dc->vmsd = &vmstate_artist;
1426     dc->reset = artist_reset;
1427     device_class_set_props(dc, artist_properties);
1428 }
1429 
1430 static const TypeInfo artist_info = {
1431     .name          = TYPE_ARTIST,
1432     .parent        = TYPE_SYS_BUS_DEVICE,
1433     .instance_size = sizeof(ARTISTState),
1434     .instance_init = artist_initfn,
1435     .class_init    = artist_class_init,
1436 };
1437 
1438 static void artist_register_types(void)
1439 {
1440     type_register_static(&artist_info);
1441 }
1442 
1443 type_init(artist_register_types)
1444