meson: convert hw/vfio
[qemu.git] / hw / display / artist.c
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)