1 /* 2 * QEMU HP Artist Emulation 3 * 4 * Copyright (c) 2019 Sven Schnelle <svens@stackframe.org> 5 * 6 * This work is licensed under the terms of the GNU GPL, version 2 or later. 7 */ 8 9 #include "qemu/osdep.h" 10 #include "qemu-common.h" 11 #include "qemu/error-report.h" 12 #include "qemu/typedefs.h" 13 #include "qemu/log.h" 14 #include "qemu/module.h" 15 #include "qemu/units.h" 16 #include "qapi/error.h" 17 #include "hw/sysbus.h" 18 #include "hw/loader.h" 19 #include "hw/qdev-core.h" 20 #include "hw/qdev-properties.h" 21 #include "migration/vmstate.h" 22 #include "ui/console.h" 23 #include "trace.h" 24 #include "hw/display/framebuffer.h" 25 26 #define TYPE_ARTIST "artist" 27 #define ARTIST(obj) OBJECT_CHECK(ARTISTState, (obj), TYPE_ARTIST) 28 29 #ifdef HOST_WORDS_BIGENDIAN 30 #define ROP8OFF(_i) (3 - (_i)) 31 #else 32 #define ROP8OFF 33 #endif 34 35 struct vram_buffer { 36 MemoryRegion mr; 37 uint8_t *data; 38 int size; 39 int width; 40 int height; 41 }; 42 43 typedef struct ARTISTState { 44 SysBusDevice parent_obj; 45 46 QemuConsole *con; 47 MemoryRegion vram_mem; 48 MemoryRegion mem_as_root; 49 MemoryRegion reg; 50 MemoryRegionSection fbsection; 51 52 void *vram_int_mr; 53 AddressSpace as; 54 55 struct vram_buffer vram_buffer[16]; 56 57 uint16_t width; 58 uint16_t height; 59 uint16_t depth; 60 61 uint32_t fg_color; 62 uint32_t bg_color; 63 64 uint32_t vram_char_y; 65 uint32_t vram_bitmask; 66 67 uint32_t vram_start; 68 uint32_t vram_pos; 69 70 uint32_t vram_size; 71 72 uint32_t blockmove_source; 73 uint32_t blockmove_dest; 74 uint32_t blockmove_size; 75 76 uint32_t line_size; 77 uint32_t line_end; 78 uint32_t line_xy; 79 uint32_t line_pattern_start; 80 uint32_t line_pattern_skip; 81 82 uint32_t cursor_pos; 83 84 uint32_t cursor_height; 85 uint32_t cursor_width; 86 87 uint32_t plane_mask; 88 89 uint32_t reg_100080; 90 uint32_t reg_300200; 91 uint32_t reg_300208; 92 uint32_t reg_300218; 93 94 uint32_t cmap_bm_access; 95 uint32_t dst_bm_access; 96 uint32_t src_bm_access; 97 uint32_t control_plane; 98 uint32_t transfer_data; 99 uint32_t image_bitmap_op; 100 101 uint32_t font_write1; 102 uint32_t font_write2; 103 uint32_t font_write_pos_y; 104 105 int draw_line_pattern; 106 } ARTISTState; 107 108 typedef enum { 109 ARTIST_BUFFER_AP = 1, 110 ARTIST_BUFFER_OVERLAY = 2, 111 ARTIST_BUFFER_CURSOR1 = 6, 112 ARTIST_BUFFER_CURSOR2 = 7, 113 ARTIST_BUFFER_ATTRIBUTE = 13, 114 ARTIST_BUFFER_CMAP = 15, 115 } artist_buffer_t; 116 117 typedef enum { 118 VRAM_IDX = 0x1004a0, 119 VRAM_BITMASK = 0x1005a0, 120 VRAM_WRITE_INCR_X = 0x100600, 121 VRAM_WRITE_INCR_X2 = 0x100604, 122 VRAM_WRITE_INCR_Y = 0x100620, 123 VRAM_START = 0x100800, 124 BLOCK_MOVE_SIZE = 0x100804, 125 BLOCK_MOVE_SOURCE = 0x100808, 126 TRANSFER_DATA = 0x100820, 127 FONT_WRITE_INCR_Y = 0x1008a0, 128 VRAM_START_TRIGGER = 0x100a00, 129 VRAM_SIZE_TRIGGER = 0x100a04, 130 FONT_WRITE_START = 0x100aa0, 131 BLOCK_MOVE_DEST_TRIGGER = 0x100b00, 132 BLOCK_MOVE_SIZE_TRIGGER = 0x100b04, 133 LINE_XY = 0x100ccc, 134 PATTERN_LINE_START = 0x100ecc, 135 LINE_SIZE = 0x100e04, 136 LINE_END = 0x100e44, 137 CMAP_BM_ACCESS = 0x118000, 138 DST_BM_ACCESS = 0x118004, 139 SRC_BM_ACCESS = 0x118008, 140 CONTROL_PLANE = 0x11800c, 141 FG_COLOR = 0x118010, 142 BG_COLOR = 0x118014, 143 PLANE_MASK = 0x118018, 144 IMAGE_BITMAP_OP = 0x11801c, 145 CURSOR_POS = 0x300100, 146 CURSOR_CTRL = 0x300104, 147 } artist_reg_t; 148 149 typedef enum { 150 ARTIST_ROP_CLEAR = 0, 151 ARTIST_ROP_COPY = 3, 152 ARTIST_ROP_XOR = 6, 153 ARTIST_ROP_NOT_DST = 10, 154 ARTIST_ROP_SET = 15, 155 } artist_rop_t; 156 157 #define REG_NAME(_x) case _x: return " "#_x; 158 static const char *artist_reg_name(uint64_t addr) 159 { 160 switch ((artist_reg_t)addr) { 161 REG_NAME(VRAM_IDX); 162 REG_NAME(VRAM_BITMASK); 163 REG_NAME(VRAM_WRITE_INCR_X); 164 REG_NAME(VRAM_WRITE_INCR_X2); 165 REG_NAME(VRAM_WRITE_INCR_Y); 166 REG_NAME(VRAM_START); 167 REG_NAME(BLOCK_MOVE_SIZE); 168 REG_NAME(BLOCK_MOVE_SOURCE); 169 REG_NAME(FG_COLOR); 170 REG_NAME(BG_COLOR); 171 REG_NAME(PLANE_MASK); 172 REG_NAME(VRAM_START_TRIGGER); 173 REG_NAME(VRAM_SIZE_TRIGGER); 174 REG_NAME(BLOCK_MOVE_DEST_TRIGGER); 175 REG_NAME(BLOCK_MOVE_SIZE_TRIGGER); 176 REG_NAME(TRANSFER_DATA); 177 REG_NAME(CONTROL_PLANE); 178 REG_NAME(IMAGE_BITMAP_OP); 179 REG_NAME(CMAP_BM_ACCESS); 180 REG_NAME(DST_BM_ACCESS); 181 REG_NAME(SRC_BM_ACCESS); 182 REG_NAME(CURSOR_POS); 183 REG_NAME(CURSOR_CTRL); 184 REG_NAME(LINE_XY); 185 REG_NAME(PATTERN_LINE_START); 186 REG_NAME(LINE_SIZE); 187 REG_NAME(LINE_END); 188 REG_NAME(FONT_WRITE_INCR_Y); 189 REG_NAME(FONT_WRITE_START); 190 } 191 return ""; 192 } 193 #undef REG_NAME 194 195 static int16_t artist_get_x(uint32_t reg) 196 { 197 return reg >> 16; 198 } 199 200 static int16_t artist_get_y(uint32_t reg) 201 { 202 return reg & 0xffff; 203 } 204 205 static void artist_invalidate_lines(struct vram_buffer *buf, 206 int starty, int height) 207 { 208 int start = starty * buf->width; 209 int size = height * buf->width; 210 211 if (start + size <= buf->size) { 212 memory_region_set_dirty(&buf->mr, start, size); 213 } 214 } 215 216 static int vram_write_pix_per_transfer(ARTISTState *s) 217 { 218 if (s->cmap_bm_access) { 219 return 1 << ((s->cmap_bm_access >> 27) & 0x0f); 220 } else { 221 return 1 << ((s->dst_bm_access >> 27) & 0x0f); 222 } 223 } 224 225 static int vram_pixel_length(ARTISTState *s) 226 { 227 if (s->cmap_bm_access) { 228 return (s->cmap_bm_access >> 24) & 0x07; 229 } else { 230 return (s->dst_bm_access >> 24) & 0x07; 231 } 232 } 233 234 static int vram_write_bufidx(ARTISTState *s) 235 { 236 if (s->cmap_bm_access) { 237 return (s->cmap_bm_access >> 12) & 0x0f; 238 } else { 239 return (s->dst_bm_access >> 12) & 0x0f; 240 } 241 } 242 243 static int vram_read_bufidx(ARTISTState *s) 244 { 245 if (s->cmap_bm_access) { 246 return (s->cmap_bm_access >> 12) & 0x0f; 247 } else { 248 return (s->src_bm_access >> 12) & 0x0f; 249 } 250 } 251 252 static struct vram_buffer *vram_read_buffer(ARTISTState *s) 253 { 254 return &s->vram_buffer[vram_read_bufidx(s)]; 255 } 256 257 static struct vram_buffer *vram_write_buffer(ARTISTState *s) 258 { 259 return &s->vram_buffer[vram_write_bufidx(s)]; 260 } 261 262 static uint8_t artist_get_color(ARTISTState *s) 263 { 264 if (s->image_bitmap_op & 2) { 265 return s->fg_color; 266 } else { 267 return s->bg_color; 268 } 269 } 270 271 static artist_rop_t artist_get_op(ARTISTState *s) 272 { 273 return (s->image_bitmap_op >> 8) & 0xf; 274 } 275 276 static void artist_rop8(ARTISTState *s, uint8_t *dst, uint8_t val) 277 { 278 279 const artist_rop_t op = artist_get_op(s); 280 uint8_t plane_mask = s->plane_mask & 0xff; 281 282 switch (op) { 283 case ARTIST_ROP_CLEAR: 284 *dst &= ~plane_mask; 285 break; 286 287 case ARTIST_ROP_COPY: 288 *dst &= ~plane_mask; 289 *dst |= val & plane_mask; 290 break; 291 292 case ARTIST_ROP_XOR: 293 *dst ^= val & plane_mask; 294 break; 295 296 case ARTIST_ROP_NOT_DST: 297 *dst ^= plane_mask; 298 break; 299 300 case ARTIST_ROP_SET: 301 *dst |= plane_mask; 302 break; 303 304 default: 305 qemu_log_mask(LOG_UNIMP, "%s: unsupported rop %d\n", __func__, op); 306 break; 307 } 308 } 309 310 static void artist_get_cursor_pos(ARTISTState *s, int *x, int *y) 311 { 312 /* 313 * Don't know whether these magic offset values are configurable via 314 * some register. They are the same for all resolutions, so don't 315 * bother about it. 316 */ 317 318 *y = 0x47a - artist_get_y(s->cursor_pos); 319 *x = ((artist_get_x(s->cursor_pos) - 338) / 2); 320 321 if (*x > s->width) { 322 *x = 0; 323 } 324 325 if (*y > s->height) { 326 *y = 0; 327 } 328 } 329 330 static void artist_invalidate_cursor(ARTISTState *s) 331 { 332 int x, y; 333 artist_get_cursor_pos(s, &x, &y); 334 artist_invalidate_lines(&s->vram_buffer[ARTIST_BUFFER_AP], 335 y, s->cursor_height); 336 } 337 338 static void vram_bit_write(ARTISTState *s, int posx, int posy, bool incr_x, 339 int size, uint32_t data) 340 { 341 struct vram_buffer *buf; 342 uint32_t vram_bitmask = s->vram_bitmask; 343 int mask, i, pix_count, pix_length, offset, height, width; 344 uint8_t *data8, *p; 345 346 pix_count = vram_write_pix_per_transfer(s); 347 pix_length = vram_pixel_length(s); 348 349 buf = vram_write_buffer(s); 350 height = buf->height; 351 width = buf->width; 352 353 if (s->cmap_bm_access) { 354 offset = s->vram_pos; 355 } else { 356 offset = posy * width + posx; 357 } 358 359 if (!buf->size) { 360 qemu_log("write to non-existent buffer\n"); 361 return; 362 } 363 364 p = buf->data; 365 366 if (pix_count > size * 8) { 367 pix_count = size * 8; 368 } 369 370 if (posy * width + posx + pix_count > buf->size) { 371 qemu_log("write outside bounds: wants %dx%d, max size %dx%d\n", 372 posx, posy, width, height); 373 return; 374 } 375 376 377 switch (pix_length) { 378 case 0: 379 if (s->image_bitmap_op & 0x20000000) { 380 data &= vram_bitmask; 381 } 382 383 for (i = 0; i < pix_count; i++) { 384 artist_rop8(s, p + offset + pix_count - 1 - i, 385 (data & 1) ? (s->plane_mask >> 24) : 0); 386 data >>= 1; 387 } 388 memory_region_set_dirty(&buf->mr, offset, pix_count); 389 break; 390 391 case 3: 392 if (s->cmap_bm_access) { 393 *(uint32_t *)(p + offset) = data; 394 break; 395 } 396 data8 = (uint8_t *)&data; 397 398 for (i = 3; i >= 0; i--) { 399 if (!(s->image_bitmap_op & 0x20000000) || 400 s->vram_bitmask & (1 << (28 + i))) { 401 artist_rop8(s, p + offset + 3 - i, data8[ROP8OFF(i)]); 402 } 403 } 404 memory_region_set_dirty(&buf->mr, offset, 3); 405 break; 406 407 case 6: 408 switch (size) { 409 default: 410 case 4: 411 vram_bitmask = s->vram_bitmask; 412 break; 413 414 case 2: 415 vram_bitmask = s->vram_bitmask >> 16; 416 break; 417 418 case 1: 419 vram_bitmask = s->vram_bitmask >> 24; 420 break; 421 } 422 423 for (i = 0; i < pix_count; i++) { 424 mask = 1 << (pix_count - 1 - i); 425 426 if (!(s->image_bitmap_op & 0x20000000) || 427 (vram_bitmask & mask)) { 428 if (data & mask) { 429 artist_rop8(s, p + offset + i, s->fg_color); 430 } else { 431 if (!(s->image_bitmap_op & 0x10000002)) { 432 artist_rop8(s, p + offset + i, s->bg_color); 433 } 434 } 435 } 436 } 437 memory_region_set_dirty(&buf->mr, offset, pix_count); 438 break; 439 440 default: 441 qemu_log_mask(LOG_UNIMP, "%s: unknown pixel length %d\n", 442 __func__, pix_length); 443 break; 444 } 445 446 if (incr_x) { 447 if (s->cmap_bm_access) { 448 s->vram_pos += 4; 449 } else { 450 s->vram_pos += pix_count << 2; 451 } 452 } 453 454 if (vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR1 || 455 vram_write_bufidx(s) == ARTIST_BUFFER_CURSOR2) { 456 artist_invalidate_cursor(s); 457 } 458 } 459 460 static void block_move(ARTISTState *s, int source_x, int source_y, int dest_x, 461 int dest_y, int width, int height) 462 { 463 struct vram_buffer *buf; 464 int line, endline, lineincr, startcolumn, endcolumn, columnincr, column; 465 uint32_t dst, src; 466 467 trace_artist_block_move(source_x, source_y, dest_x, dest_y, width, height); 468 469 if (s->control_plane != 0) { 470 /* We don't support CONTROL_PLANE accesses */ 471 qemu_log_mask(LOG_UNIMP, "%s: CONTROL_PLANE: %08x\n", __func__, 472 s->control_plane); 473 return; 474 } 475 476 buf = &s->vram_buffer[ARTIST_BUFFER_AP]; 477 478 if (dest_y > source_y) { 479 /* move down */ 480 line = height - 1; 481 endline = -1; 482 lineincr = -1; 483 } else { 484 /* move up */ 485 line = 0; 486 endline = height; 487 lineincr = 1; 488 } 489 490 if (dest_x > source_x) { 491 /* move right */ 492 startcolumn = width - 1; 493 endcolumn = -1; 494 columnincr = -1; 495 } else { 496 /* move left */ 497 startcolumn = 0; 498 endcolumn = width; 499 columnincr = 1; 500 } 501 502 for ( ; line != endline; line += lineincr) { 503 src = source_x + ((line + source_y) * buf->width); 504 dst = dest_x + ((line + dest_y) * buf->width); 505 506 for (column = startcolumn; column != endcolumn; column += columnincr) { 507 if (dst + column > buf->size || src + column > buf->size) { 508 continue; 509 } 510 artist_rop8(s, buf->data + dst + column, buf->data[src + column]); 511 } 512 } 513 514 artist_invalidate_lines(buf, dest_y, height); 515 } 516 517 static void fill_window(ARTISTState *s, int startx, int starty, 518 int width, int height) 519 { 520 uint32_t offset; 521 uint8_t color = artist_get_color(s); 522 struct vram_buffer *buf; 523 int x, y; 524 525 trace_artist_fill_window(startx, starty, width, height, 526 s->image_bitmap_op, s->control_plane); 527 528 if (s->control_plane != 0) { 529 /* We don't support CONTROL_PLANE accesses */ 530 qemu_log_mask(LOG_UNIMP, "%s: CONTROL_PLANE: %08x\n", __func__, 531 s->control_plane); 532 return; 533 } 534 535 if (s->reg_100080 == 0x7d) { 536 /* 537 * Not sure what this register really does, but 538 * 0x7d seems to enable autoincremt of the Y axis 539 * by the current block move height. 540 */ 541 height = artist_get_y(s->blockmove_size); 542 s->vram_start += height; 543 } 544 545 buf = &s->vram_buffer[ARTIST_BUFFER_AP]; 546 547 for (y = starty; y < starty + height; y++) { 548 offset = y * s->width; 549 550 for (x = startx; x < startx + width; x++) { 551 artist_rop8(s, buf->data + offset + x, color); 552 } 553 } 554 artist_invalidate_lines(buf, starty, height); 555 } 556 557 static void draw_line(ARTISTState *s, int x1, int y1, int x2, int y2, 558 bool update_start, int skip_pix, int max_pix) 559 { 560 struct vram_buffer *buf; 561 uint8_t color; 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(DEVICE(dev), 0, &artist_ops, s); 1357 qemu_console_resize(s->con, s->width, s->height); 1358 } 1359 1360 static int vmstate_artist_post_load(void *opaque, int version_id) 1361 { 1362 artist_invalidate(opaque); 1363 return 0; 1364 } 1365 1366 static const VMStateDescription vmstate_artist = { 1367 .name = "artist", 1368 .version_id = 1, 1369 .minimum_version_id = 1, 1370 .post_load = vmstate_artist_post_load, 1371 .fields = (VMStateField[]) { 1372 VMSTATE_UINT16(height, ARTISTState), 1373 VMSTATE_UINT16(width, ARTISTState), 1374 VMSTATE_UINT16(depth, ARTISTState), 1375 VMSTATE_UINT32(fg_color, ARTISTState), 1376 VMSTATE_UINT32(bg_color, ARTISTState), 1377 VMSTATE_UINT32(vram_char_y, ARTISTState), 1378 VMSTATE_UINT32(vram_bitmask, ARTISTState), 1379 VMSTATE_UINT32(vram_start, ARTISTState), 1380 VMSTATE_UINT32(vram_pos, ARTISTState), 1381 VMSTATE_UINT32(vram_size, ARTISTState), 1382 VMSTATE_UINT32(blockmove_source, ARTISTState), 1383 VMSTATE_UINT32(blockmove_dest, ARTISTState), 1384 VMSTATE_UINT32(blockmove_size, ARTISTState), 1385 VMSTATE_UINT32(line_size, ARTISTState), 1386 VMSTATE_UINT32(line_end, ARTISTState), 1387 VMSTATE_UINT32(line_xy, ARTISTState), 1388 VMSTATE_UINT32(cursor_pos, ARTISTState), 1389 VMSTATE_UINT32(cursor_height, ARTISTState), 1390 VMSTATE_UINT32(cursor_width, ARTISTState), 1391 VMSTATE_UINT32(plane_mask, ARTISTState), 1392 VMSTATE_UINT32(reg_100080, ARTISTState), 1393 VMSTATE_UINT32(reg_300200, ARTISTState), 1394 VMSTATE_UINT32(reg_300208, ARTISTState), 1395 VMSTATE_UINT32(reg_300218, ARTISTState), 1396 VMSTATE_UINT32(cmap_bm_access, ARTISTState), 1397 VMSTATE_UINT32(dst_bm_access, ARTISTState), 1398 VMSTATE_UINT32(src_bm_access, ARTISTState), 1399 VMSTATE_UINT32(control_plane, ARTISTState), 1400 VMSTATE_UINT32(transfer_data, ARTISTState), 1401 VMSTATE_UINT32(image_bitmap_op, ARTISTState), 1402 VMSTATE_UINT32(font_write1, ARTISTState), 1403 VMSTATE_UINT32(font_write2, ARTISTState), 1404 VMSTATE_UINT32(font_write_pos_y, ARTISTState), 1405 VMSTATE_END_OF_LIST() 1406 } 1407 }; 1408 1409 static Property artist_properties[] = { 1410 DEFINE_PROP_UINT16("width", ARTISTState, width, 1280), 1411 DEFINE_PROP_UINT16("height", ARTISTState, height, 1024), 1412 DEFINE_PROP_UINT16("depth", ARTISTState, depth, 8), 1413 DEFINE_PROP_END_OF_LIST(), 1414 }; 1415 1416 static void artist_reset(DeviceState *qdev) 1417 { 1418 } 1419 1420 static void artist_class_init(ObjectClass *klass, void *data) 1421 { 1422 DeviceClass *dc = DEVICE_CLASS(klass); 1423 1424 dc->realize = artist_realizefn; 1425 dc->vmsd = &vmstate_artist; 1426 dc->reset = artist_reset; 1427 device_class_set_props(dc, artist_properties); 1428 } 1429 1430 static const TypeInfo artist_info = { 1431 .name = TYPE_ARTIST, 1432 .parent = TYPE_SYS_BUS_DEVICE, 1433 .instance_size = sizeof(ARTISTState), 1434 .instance_init = artist_initfn, 1435 .class_init = artist_class_init, 1436 }; 1437 1438 static void artist_register_types(void) 1439 { 1440 type_register_static(&artist_info); 1441 } 1442 1443 type_init(artist_register_types) 1444