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