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