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