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