hppa: Add emulation of Artist graphics
[qemu/ar7.git] / hw / display / artist.c
blob65be9e3554b7ada0dc48be294b79f690a367ec75
1 /*
2 * QEMU HP Artist Emulation
4 * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 */
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"
26 #define TYPE_ARTIST "artist"
27 #define ARTIST(obj) OBJECT_CHECK(ARTISTState, (obj), TYPE_ARTIST)
29 #ifdef HOST_WORDS_BIGENDIAN
30 #define ROP8OFF(_i) (3 - (_i))
31 #else
32 #define ROP8OFF
33 #endif
35 struct vram_buffer {
36 MemoryRegion mr;
37 uint8_t *data;
38 int size;
39 int width;
40 int height;
43 typedef struct ARTISTState {
44 SysBusDevice parent_obj;
46 QemuConsole *con;
47 MemoryRegion vram_mem;
48 MemoryRegion mem_as_root;
49 MemoryRegion reg;
50 MemoryRegionSection fbsection;
52 void *vram_int_mr;
53 AddressSpace as;
55 struct vram_buffer vram_buffer[16];
57 uint16_t width;
58 uint16_t height;
59 uint16_t depth;
61 uint32_t fg_color;
62 uint32_t bg_color;
64 uint32_t vram_char_y;
65 uint32_t vram_bitmask;
67 uint32_t vram_start;
68 uint32_t vram_pos;
70 uint32_t vram_size;
72 uint32_t blockmove_source;
73 uint32_t blockmove_dest;
74 uint32_t blockmove_size;
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;
82 uint32_t cursor_pos;
84 uint32_t cursor_height;
85 uint32_t cursor_width;
87 uint32_t plane_mask;
89 uint32_t reg_100080;
90 uint32_t reg_300200;
91 uint32_t reg_300208;
92 uint32_t reg_300218;
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;
101 uint32_t font_write1;
102 uint32_t font_write2;
103 uint32_t font_write_pos_y;
105 int draw_line_pattern;
106 } ARTISTState;
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;
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;
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;
157 #define REG_NAME(_x) case _x: return " "#_x;
158 static const char *artist_reg_name(uint64_t addr)
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);
191 return "";
193 #undef REG_NAME
195 static int16_t artist_get_x(uint32_t reg)
197 return reg >> 16;
200 static int16_t artist_get_y(uint32_t reg)
202 return reg & 0xffff;
205 static void artist_invalidate_lines(struct vram_buffer *buf,
206 int starty, int height)
208 int start = starty * buf->width;
209 int size = height * buf->width;
211 if (start + size <= buf->size) {
212 memory_region_set_dirty(&buf->mr, start, size);
216 static int vram_write_pix_per_transfer(ARTISTState *s)
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);
225 static int vram_pixel_length(ARTISTState *s)
227 if (s->cmap_bm_access) {
228 return (s->cmap_bm_access >> 24) & 0x07;
229 } else {
230 return (s->dst_bm_access >> 24) & 0x07;
234 static int vram_write_bufidx(ARTISTState *s)
236 if (s->cmap_bm_access) {
237 return (s->cmap_bm_access >> 12) & 0x0f;
238 } else {
239 return (s->dst_bm_access >> 12) & 0x0f;
243 static int vram_read_bufidx(ARTISTState *s)
245 if (s->cmap_bm_access) {
246 return (s->cmap_bm_access >> 12) & 0x0f;
247 } else {
248 return (s->src_bm_access >> 12) & 0x0f;
252 static struct vram_buffer *vram_read_buffer(ARTISTState *s)
254 return &s->vram_buffer[vram_read_bufidx(s)];
257 static struct vram_buffer *vram_write_buffer(ARTISTState *s)
259 return &s->vram_buffer[vram_write_bufidx(s)];
262 static uint8_t artist_get_color(ARTISTState *s)
264 if (s->image_bitmap_op & 2) {
265 return s->fg_color;
266 } else {
267 return s->bg_color;
271 static artist_rop_t artist_get_op(ARTISTState *s)
273 return (s->image_bitmap_op >> 8) & 0xf;
276 static void artist_rop8(ARTISTState *s, uint8_t *dst, uint8_t val)
279 const artist_rop_t op = artist_get_op(s);
280 uint8_t plane_mask = s->plane_mask & 0xff;
282 switch (op) {
283 case ARTIST_ROP_CLEAR:
284 *dst &= ~plane_mask;
285 break;
287 case ARTIST_ROP_COPY:
288 *dst &= ~plane_mask;
289 *dst |= val & plane_mask;
290 break;
292 case ARTIST_ROP_XOR:
293 *dst ^= val & plane_mask;
294 break;
296 case ARTIST_ROP_NOT_DST:
297 *dst ^= plane_mask;
298 break;
300 case ARTIST_ROP_SET:
301 *dst |= plane_mask;
302 break;
304 default:
305 qemu_log_mask(LOG_UNIMP, "%s: unsupported rop %d\n", __func__, op);
306 break;
310 static void artist_get_cursor_pos(ARTISTState *s, int *x, int *y)
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.
318 *y = 0x47a - artist_get_y(s->cursor_pos);
319 *x = ((artist_get_x(s->cursor_pos) - 338) / 2);
321 if (*x > s->width) {
322 *x = 0;
325 if (*y > s->height) {
326 *y = 0;
330 static void artist_invalidate_cursor(ARTISTState *s)
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);
338 static void vram_bit_write(ARTISTState *s, int posx, int posy, bool incr_x,
339 int size, uint32_t data)
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;
346 pix_count = vram_write_pix_per_transfer(s);
347 pix_length = vram_pixel_length(s);
349 buf = vram_write_buffer(s);
350 height = buf->height;
351 width = buf->width;
353 if (s->cmap_bm_access) {
354 offset = s->vram_pos;
355 } else {
356 offset = posy * width + posx;
359 if (!buf->size) {
360 qemu_log("write to non-existent buffer\n");
361 return;
364 p = buf->data;
366 if (pix_count > size * 8) {
367 pix_count = size * 8;
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;
377 switch (pix_length) {
378 case 0:
379 if (s->image_bitmap_op & 0x20000000) {
380 data &= vram_bitmask;
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;
388 memory_region_set_dirty(&buf->mr, offset, pix_count);
389 break;
391 case 3:
392 if (s->cmap_bm_access) {
393 *(uint32_t *)(p + offset) = data;
394 break;
396 data8 = (uint8_t *)&data;
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)]);
404 memory_region_set_dirty(&buf->mr, offset, 3);
405 break;
407 case 6:
408 switch (size) {
409 default:
410 case 4:
411 vram_bitmask = s->vram_bitmask;
412 break;
414 case 2:
415 vram_bitmask = s->vram_bitmask >> 16;
416 break;
418 case 1:
419 vram_bitmask = s->vram_bitmask >> 24;
420 break;
423 for (i = 0; i < pix_count; i++) {
424 mask = 1 << (pix_count - 1 - i);
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);
437 memory_region_set_dirty(&buf->mr, offset, pix_count);
438 break;
440 default:
441 qemu_log_mask(LOG_UNIMP, "%s: unknown pixel length %d\n",
442 __func__, pix_length);
443 break;
446 if (incr_x) {
447 if (s->cmap_bm_access) {
448 s->vram_pos += 4;
449 } else {
450 s->vram_pos += pix_count << 2;
454 if (vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR1 ||
455 vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR2) {
456 artist_invalidate_cursor(s);
460 static void block_move(ARTISTState *s, int source_x, int source_y, int dest_x,
461 int dest_y, int width, int height)
463 struct vram_buffer *buf;
464 int line, endline, lineincr, startcolumn, endcolumn, columnincr, column;
465 uint32_t dst, src;
467 trace_artist_block_move(source_x, source_y, dest_x, dest_y, width, height);
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;
476 buf = &s->vram_buffer[ARTIST_BUFFER_AP];
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;
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;
502 for ( ; line != endline; line += lineincr) {
503 src = source_x + ((line + source_y) * buf->width);
504 dst = dest_x + ((line + dest_y) * buf->width);
506 for (column = startcolumn; column != endcolumn; column += columnincr) {
507 if (dst + column > buf->size || src + column > buf->size) {
508 continue;
510 artist_rop8(s, buf->data + dst + column, buf->data[src + column]);
514 artist_invalidate_lines(buf, dest_y, height);
517 static void fill_window(ARTISTState *s, int startx, int starty,
518 int width, int height)
520 uint32_t offset;
521 uint8_t color = artist_get_color(s);
522 struct vram_buffer *buf;
523 int x, y;
525 trace_artist_fill_window(startx, starty, width, height,
526 s->image_bitmap_op, s->control_plane);
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;
535 if (s->reg_100080 == 0x7d) {
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.
541 height = artist_get_y(s->blockmove_size);
542 s->vram_start += height;
545 buf = &s->vram_buffer[ARTIST_BUFFER_AP];
547 for (y = starty; y < starty + height; y++) {
548 offset = y * s->width;
550 for (x = startx; x < startx + width; x++) {
551 artist_rop8(s, buf->data + offset + x, color);
554 artist_invalidate_lines(buf, starty, height);
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)
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;
567 if (update_start) {
568 s->vram_start = (x2 << 16) | y2;
571 buf = &s->vram_buffer[ARTIST_BUFFER_AP];
573 c1 = false;
574 incy = 1;
576 if (x2 > x1) {
577 dx = x2 - x1;
578 } else {
579 dx = x1 - x2;
581 if (y2 > y1) {
582 dy = y2 - y1;
583 } else {
584 dy = y1 - y2;
586 if (dy > dx) {
587 t = y2;
588 y2 = x2;
589 x2 = t;
591 t = y1;
592 y1 = x1;
593 x1 = t;
595 t = dx;
596 dx = dy;
597 dy = t;
599 c1 = true;
602 if (x1 > x2) {
603 t = y2;
604 y2 = y1;
605 y1 = t;
607 t = x1;
608 x1 = x2;
609 x2 = t;
612 horiz = dy << 1;
613 diago = (dy - dx) << 1;
614 e = (dy << 1) - dx;
616 if (y1 <= y2) {
617 incy = 1;
618 } else {
619 incy = -1;
621 x = x1;
622 y = y1;
624 do {
625 if (c1) {
626 p = buf->data + x * s->width + y;
627 } else {
628 p = buf->data + y * s->width + x;
631 if (skip_pix > 0) {
632 skip_pix--;
633 } else {
634 artist_rop8(s, p, color);
637 if (e > 0) {
638 artist_invalidate_lines(buf, y, 1);
639 y += incy;
640 e += diago;
641 } else {
642 e += horiz;
644 x++;
645 } while (x <= x2 && (max_pix == -1 || --max_pix > 0));
648 static void draw_line_pattern_start(ARTISTState *s)
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;
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;
662 static void draw_line_pattern_next(ARTISTState *s)
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;
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;
678 static void draw_line_size(ARTISTState *s, bool update_start)
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);
686 trace_artist_draw_line(startx, starty, endx, endy);
687 draw_line(s, startx, starty, endx, endy, update_start, -1, -1);
690 static void draw_line_xy(ARTISTState *s, bool update_start)
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;
700 endx = startx;
701 endy = starty;
703 if (sizex > 0) {
704 endx = startx + linexy;
707 if (sizex < 0) {
708 endx = startx;
709 startx -= linexy;
712 if (sizey > 0) {
713 endy = starty + linexy;
716 if (sizey < 0) {
717 endy = starty;
718 starty -= linexy;
721 if (startx < 0) {
722 startx = 0;
725 if (endx < 0) {
726 endx = 0;
729 if (starty < 0) {
730 starty = 0;
733 if (endy < 0) {
734 endy = 0;
738 if (endx < 0) {
739 return;
742 if (endy < 0) {
743 return;
746 trace_artist_draw_line(startx, starty, endx, endy);
747 draw_line(s, startx, starty, endx, endy, false, -1, -1);
750 static void draw_line_end(ARTISTState *s, bool update_start)
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);
758 trace_artist_draw_line(startx, starty, endx, endy);
759 draw_line(s, startx, starty, endx, endy, update_start, -1, -1);
762 static void font_write16(ARTISTState *s, uint16_t val)
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;
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;
773 buf = &s->vram_buffer[ARTIST_BUFFER_AP];
775 if (offset + 16 > buf->size) {
776 return;
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);
789 artist_invalidate_lines(buf, starty, 1);
792 static void font_write(ARTISTState *s, uint32_t val)
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;
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;
807 static void combine_write_reg(hwaddr addr, uint64_t val, int size, void *out)
810 * FIXME: is there a qemu helper for this?
813 #ifndef HOST_WORDS_BIGENDIAN
814 addr ^= 3;
815 #endif
817 switch (size) {
818 case 1:
819 *(uint8_t *)(out + (addr & 3)) = val;
820 break;
822 case 2:
823 *(uint16_t *)(out + (addr & 2)) = val;
824 break;
826 case 4:
827 *(uint32_t *)out = val;
828 break;
830 default:
831 qemu_log_mask(LOG_UNIMP, "unsupported write size: %d\n", size);
835 static void artist_reg_write(void *opaque, hwaddr addr, uint64_t val,
836 unsigned size)
838 ARTISTState *s = opaque;
839 int posx, posy;
840 int width, height;
842 trace_artist_reg_write(size, addr, artist_reg_name(addr & ~3ULL), val);
844 switch (addr & ~3ULL) {
845 case 0x100080:
846 combine_write_reg(addr, val, size, &s->reg_100080);
847 break;
849 case FG_COLOR:
850 combine_write_reg(addr, val, size, &s->fg_color);
851 break;
853 case BG_COLOR:
854 combine_write_reg(addr, val, size, &s->bg_color);
855 break;
857 case VRAM_BITMASK:
858 combine_write_reg(addr, val, size, &s->vram_bitmask);
859 break;
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;
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;
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;
880 case VRAM_START:
881 combine_write_reg(addr, val, size, &s->vram_start);
882 s->draw_line_pattern = 0;
883 break;
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;
893 case VRAM_SIZE_TRIGGER:
894 combine_write_reg(addr, val, size, &s->vram_size);
896 if (size == 2 && !(addr & 2)) {
897 height = artist_get_y(s->blockmove_size);
898 } else {
899 height = artist_get_y(s->vram_size);
902 if (size == 2 && (addr & 2)) {
903 width = artist_get_x(s->blockmove_size);
904 } else {
905 width = artist_get_x(s->vram_size);
908 fill_window(s, artist_get_x(s->vram_start),
909 artist_get_y(s->vram_start),
910 width, height);
911 break;
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);
920 break;
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;
928 case LINE_SIZE:
929 combine_write_reg(addr, val, size, &s->line_size);
930 draw_line_size(s, true);
931 break;
933 case LINE_END:
934 combine_write_reg(addr, val, size, &s->line_end);
935 draw_line_end(s, true);
936 break;
938 case BLOCK_MOVE_SIZE:
939 combine_write_reg(addr, val, size, &s->blockmove_size);
940 break;
942 case BLOCK_MOVE_SOURCE:
943 combine_write_reg(addr, val, size, &s->blockmove_source);
944 break;
946 case BLOCK_MOVE_DEST_TRIGGER:
947 combine_write_reg(addr, val, size, &s->blockmove_dest);
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;
957 case BLOCK_MOVE_SIZE_TRIGGER:
958 combine_write_reg(addr, val, size, &s->blockmove_size);
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;
969 case PLANE_MASK:
970 combine_write_reg(addr, val, size, &s->plane_mask);
971 break;
973 case CMAP_BM_ACCESS:
974 combine_write_reg(addr, val, size, &s->cmap_bm_access);
975 break;
977 case DST_BM_ACCESS:
978 combine_write_reg(addr, val, size, &s->dst_bm_access);
979 s->cmap_bm_access = 0;
980 break;
982 case SRC_BM_ACCESS:
983 combine_write_reg(addr, val, size, &s->src_bm_access);
984 s->cmap_bm_access = 0;
985 break;
987 case CONTROL_PLANE:
988 combine_write_reg(addr, val, size, &s->control_plane);
989 break;
991 case TRANSFER_DATA:
992 combine_write_reg(addr, val, size, &s->transfer_data);
993 break;
995 case 0x300200:
996 combine_write_reg(addr, val, size, &s->reg_300200);
997 break;
999 case 0x300208:
1000 combine_write_reg(addr, val, size, &s->reg_300208);
1001 break;
1003 case 0x300218:
1004 combine_write_reg(addr, val, size, &s->reg_300218);
1005 break;
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;
1013 case CURSOR_CTRL:
1014 break;
1016 case IMAGE_BITMAP_OP:
1017 combine_write_reg(addr, val, size, &s->image_bitmap_op);
1018 break;
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;
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;
1031 case 300104:
1032 break;
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;
1042 static uint64_t combine_read_reg(hwaddr addr, int size, void *in)
1045 * FIXME: is there a qemu helper for this?
1048 #ifndef HOST_WORDS_BIGENDIAN
1049 addr ^= 3;
1050 #endif
1052 switch (size) {
1053 case 1:
1054 return *(uint8_t *)(in + (addr & 3));
1056 case 2:
1057 return *(uint16_t *)(in + (addr & 2));
1059 case 4:
1060 return *(uint32_t *)in;
1062 default:
1063 qemu_log_mask(LOG_UNIMP, "unsupported read size: %d\n", size);
1064 return 0;
1068 static uint64_t artist_reg_read(void *opaque, hwaddr addr, unsigned size)
1070 ARTISTState *s = opaque;
1071 uint32_t val = 0;
1073 switch (addr & ~3ULL) {
1074 /* Unknown status registers */
1075 case 0:
1076 break;
1078 case 0x211110:
1079 val = (s->width << 16) | s->height;
1080 if (s->depth == 1) {
1081 val |= 1 << 31;
1083 break;
1085 case 0x100000:
1086 case 0x300000:
1087 case 0x300004:
1088 case 0x300308:
1089 case 0x380000:
1090 break;
1092 case 0x300008:
1093 case 0x380008:
1095 * FIFO ready flag. we're not emulating the FIFOs
1096 * so we're always ready
1098 val = 0x10;
1099 break;
1101 case 0x300200:
1102 val = s->reg_300200;
1103 break;
1105 case 0x300208:
1106 val = s->reg_300208;
1107 break;
1109 case 0x300218:
1110 val = s->reg_300218;
1111 break;
1113 case 0x30023c:
1114 val = 0xac4ffdac;
1115 break;
1117 case 0x380004:
1118 /* 0x02000000 Buserror */
1119 val = 0x6dc20006;
1120 break;
1122 default:
1123 qemu_log_mask(LOG_UNIMP, "%s: unknown register: %08" HWADDR_PRIx
1124 " size %d\n", __func__, addr, size);
1125 break;
1127 val = combine_read_reg(addr, size, &val);
1128 trace_artist_reg_read(size, addr, artist_reg_name(addr & ~3ULL), val);
1129 return val;
1132 static void artist_vram_write(void *opaque, hwaddr addr, uint64_t val,
1133 unsigned size)
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);
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;
1147 return;
1150 buf = vram_write_buffer(s);
1151 if (!buf->size) {
1152 return;
1155 if (posy > buf->height || posx > buf->width) {
1156 return;
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;
1178 static uint64_t artist_vram_read(void *opaque, hwaddr addr, unsigned size)
1180 ARTISTState *s = opaque;
1181 struct vram_buffer *buf;
1182 uint64_t val;
1183 int posy, posx;
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;
1192 buf = vram_read_buffer(s);
1193 if (!buf->size) {
1194 return 0;
1197 posy = (addr >> 13) & 0x3ff;
1198 posx = (addr >> 2) & 0x7ff;
1200 if (posy > buf->height || posx > buf->width) {
1201 return 0;
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;
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,
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,
1229 static void artist_draw_cursor(ARTISTState *s)
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;
1236 cursor0 = &s->vram_buffer[ARTIST_BUFFER_CURSOR1];
1237 cursor1 = &s->vram_buffer[ARTIST_BUFFER_CURSOR2];
1238 buf = &s->vram_buffer[ARTIST_BUFFER_AP];
1240 artist_get_cursor_pos(s, &cursor_pos_x, &cursor_pos_y);
1242 for (cy = 0; cy < s->cursor_height; cy++) {
1244 for (cx = 0; cx < s->cursor_width; cx++) {
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;
1253 int dstoffset = (cursor_pos_y + cy) * s->width +
1254 (cursor_pos_x + cx);
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;
1267 static void artist_draw_line(void *opaque, uint8_t *d, const uint8_t *src,
1268 int width, int pitch)
1270 ARTISTState *s = ARTIST(opaque);
1271 uint32_t *cmap, *data = (uint32_t *)d;
1272 int x;
1274 cmap = (uint32_t *)(s->vram_buffer[ARTIST_BUFFER_CMAP].data + 0x400);
1276 for (x = 0; x < s->width; x++) {
1277 *data++ = cmap[*src++];
1281 static void artist_update_display(void *opaque)
1283 ARTISTState *s = opaque;
1284 DisplaySurface *surface = qemu_console_surface(s->con);
1285 int first = 0, last;
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);
1292 artist_draw_cursor(s);
1294 dpy_gfx_update(s->con, 0, 0, s->width, s->height);
1297 static void artist_invalidate(void *opaque)
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);
1304 static const GraphicHwOps artist_ops = {
1305 .invalidate = artist_invalidate,
1306 .gfx_update = artist_update_display,
1309 static void artist_initfn(Object *obj)
1311 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
1312 ARTISTState *s = ARTIST(obj);
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);
1322 static void artist_create_buffer(ARTISTState *s, const char *name,
1323 hwaddr *offset, unsigned int idx,
1324 int width, int height)
1326 struct vram_buffer *buf = s->vram_buffer + idx;
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);
1332 buf->data = memory_region_get_ram_ptr(&buf->mr);
1333 buf->size = height * width;
1334 buf->width = width;
1335 buf->height = height;
1337 *offset += buf->size;
1340 static void artist_realizefn(DeviceState *dev, Error **errp)
1342 ARTISTState *s = ARTIST(dev);
1343 struct vram_buffer *buf;
1344 hwaddr offset = 0;
1346 memory_region_init(&s->mem_as_root, OBJECT(dev), "artist", ~0ull);
1347 address_space_init(&s->as, &s->mem_as_root, "artist");
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);
1357 buf = &s->vram_buffer[ARTIST_BUFFER_AP];
1358 framebuffer_update_memory_section(&s->fbsection, &buf->mr, 0,
1359 buf->width, buf->height);
1361 * no idea whether the cursor is fixed size or not, so assume 32x32 which
1362 * seems sufficient for HP-UX X11.
1364 s->cursor_height = 32;
1365 s->cursor_width = 32;
1367 s->con = graphic_console_init(DEVICE(dev), 0, &artist_ops, s);
1368 qemu_console_resize(s->con, s->width, s->height);
1371 static int vmstate_artist_post_load(void *opaque, int version_id)
1373 artist_invalidate(opaque);
1374 return 0;
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()
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(),
1427 static void artist_reset(DeviceState *qdev)
1431 static void artist_class_init(ObjectClass *klass, void *data)
1433 DeviceClass *dc = DEVICE_CLASS(klass);
1435 dc->realize = artist_realizefn;
1436 dc->vmsd = &vmstate_artist;
1437 dc->reset = artist_reset;
1438 device_class_set_props(dc, artist_properties);
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,
1449 static void artist_register_types(void)
1451 type_register_static(&artist_info);
1454 type_init(artist_register_types)