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