1 /* 2 * QEMU TCX Frame buffer 3 * 4 * Copyright (c) 2003-2005 Fabrice Bellard 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to deal 8 * in the Software without restriction, including without limitation the rights 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 * copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 * THE SOFTWARE. 23 */ 24 25 #include "qemu/osdep.h" 26 #include "qapi/error.h" 27 #include "qemu-common.h" 28 #include "ui/console.h" 29 #include "ui/pixel_ops.h" 30 #include "hw/loader.h" 31 #include "hw/sysbus.h" 32 #include "qemu/error-report.h" 33 34 #define TCX_ROM_FILE "QEMU,tcx.bin" 35 #define FCODE_MAX_ROM_SIZE 0x10000 36 37 #define MAXX 1024 38 #define MAXY 768 39 #define TCX_DAC_NREGS 16 40 #define TCX_THC_NREGS 0x1000 41 #define TCX_DHC_NREGS 0x4000 42 #define TCX_TEC_NREGS 0x1000 43 #define TCX_ALT_NREGS 0x8000 44 #define TCX_STIP_NREGS 0x800000 45 #define TCX_BLIT_NREGS 0x800000 46 #define TCX_RSTIP_NREGS 0x800000 47 #define TCX_RBLIT_NREGS 0x800000 48 49 #define TCX_THC_MISC 0x818 50 #define TCX_THC_CURSXY 0x8fc 51 #define TCX_THC_CURSMASK 0x900 52 #define TCX_THC_CURSBITS 0x980 53 54 #define TYPE_TCX "SUNW,tcx" 55 #define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX) 56 57 typedef struct TCXState { 58 SysBusDevice parent_obj; 59 60 QemuConsole *con; 61 qemu_irq irq; 62 uint8_t *vram; 63 uint32_t *vram24, *cplane; 64 hwaddr prom_addr; 65 MemoryRegion rom; 66 MemoryRegion vram_mem; 67 MemoryRegion vram_8bit; 68 MemoryRegion vram_24bit; 69 MemoryRegion stip; 70 MemoryRegion blit; 71 MemoryRegion vram_cplane; 72 MemoryRegion rstip; 73 MemoryRegion rblit; 74 MemoryRegion tec; 75 MemoryRegion dac; 76 MemoryRegion thc; 77 MemoryRegion dhc; 78 MemoryRegion alt; 79 MemoryRegion thc24; 80 81 ram_addr_t vram24_offset, cplane_offset; 82 uint32_t tmpblit; 83 uint32_t vram_size; 84 uint32_t palette[260]; 85 uint8_t r[260], g[260], b[260]; 86 uint16_t width, height, depth; 87 uint8_t dac_index, dac_state; 88 uint32_t thcmisc; 89 uint32_t cursmask[32]; 90 uint32_t cursbits[32]; 91 uint16_t cursx; 92 uint16_t cursy; 93 } TCXState; 94 95 static void tcx_set_dirty(TCXState *s) 96 { 97 memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY); 98 } 99 100 static inline int tcx24_check_dirty(TCXState *s, ram_addr_t page, 101 ram_addr_t page24, ram_addr_t cpage) 102 { 103 int ret; 104 105 ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE, 106 DIRTY_MEMORY_VGA); 107 ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4, 108 DIRTY_MEMORY_VGA); 109 ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4, 110 DIRTY_MEMORY_VGA); 111 return ret; 112 } 113 114 static inline void tcx24_reset_dirty(TCXState *ts, ram_addr_t page_min, 115 ram_addr_t page_max, ram_addr_t page24, 116 ram_addr_t cpage) 117 { 118 memory_region_reset_dirty(&ts->vram_mem, 119 page_min, 120 (page_max - page_min) + TARGET_PAGE_SIZE, 121 DIRTY_MEMORY_VGA); 122 memory_region_reset_dirty(&ts->vram_mem, 123 page24 + page_min * 4, 124 (page_max - page_min) * 4 + TARGET_PAGE_SIZE, 125 DIRTY_MEMORY_VGA); 126 memory_region_reset_dirty(&ts->vram_mem, 127 cpage + page_min * 4, 128 (page_max - page_min) * 4 + TARGET_PAGE_SIZE, 129 DIRTY_MEMORY_VGA); 130 } 131 132 static void update_palette_entries(TCXState *s, int start, int end) 133 { 134 DisplaySurface *surface = qemu_console_surface(s->con); 135 int i; 136 137 for (i = start; i < end; i++) { 138 switch (surface_bits_per_pixel(surface)) { 139 default: 140 case 8: 141 s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); 142 break; 143 case 15: 144 s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]); 145 break; 146 case 16: 147 s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); 148 break; 149 case 32: 150 if (is_surface_bgr(surface)) { 151 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); 152 } else { 153 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); 154 } 155 break; 156 } 157 } 158 tcx_set_dirty(s); 159 } 160 161 static void tcx_draw_line32(TCXState *s1, uint8_t *d, 162 const uint8_t *s, int width) 163 { 164 int x; 165 uint8_t val; 166 uint32_t *p = (uint32_t *)d; 167 168 for (x = 0; x < width; x++) { 169 val = *s++; 170 *p++ = s1->palette[val]; 171 } 172 } 173 174 static void tcx_draw_line16(TCXState *s1, uint8_t *d, 175 const uint8_t *s, int width) 176 { 177 int x; 178 uint8_t val; 179 uint16_t *p = (uint16_t *)d; 180 181 for (x = 0; x < width; x++) { 182 val = *s++; 183 *p++ = s1->palette[val]; 184 } 185 } 186 187 static void tcx_draw_line8(TCXState *s1, uint8_t *d, 188 const uint8_t *s, int width) 189 { 190 int x; 191 uint8_t val; 192 193 for(x = 0; x < width; x++) { 194 val = *s++; 195 *d++ = s1->palette[val]; 196 } 197 } 198 199 static void tcx_draw_cursor32(TCXState *s1, uint8_t *d, 200 int y, int width) 201 { 202 int x, len; 203 uint32_t mask, bits; 204 uint32_t *p = (uint32_t *)d; 205 206 y = y - s1->cursy; 207 mask = s1->cursmask[y]; 208 bits = s1->cursbits[y]; 209 len = MIN(width - s1->cursx, 32); 210 p = &p[s1->cursx]; 211 for (x = 0; x < len; x++) { 212 if (mask & 0x80000000) { 213 if (bits & 0x80000000) { 214 *p = s1->palette[259]; 215 } else { 216 *p = s1->palette[258]; 217 } 218 } 219 p++; 220 mask <<= 1; 221 bits <<= 1; 222 } 223 } 224 225 static void tcx_draw_cursor16(TCXState *s1, uint8_t *d, 226 int y, int width) 227 { 228 int x, len; 229 uint32_t mask, bits; 230 uint16_t *p = (uint16_t *)d; 231 232 y = y - s1->cursy; 233 mask = s1->cursmask[y]; 234 bits = s1->cursbits[y]; 235 len = MIN(width - s1->cursx, 32); 236 p = &p[s1->cursx]; 237 for (x = 0; x < len; x++) { 238 if (mask & 0x80000000) { 239 if (bits & 0x80000000) { 240 *p = s1->palette[259]; 241 } else { 242 *p = s1->palette[258]; 243 } 244 } 245 p++; 246 mask <<= 1; 247 bits <<= 1; 248 } 249 } 250 251 static void tcx_draw_cursor8(TCXState *s1, uint8_t *d, 252 int y, int width) 253 { 254 int x, len; 255 uint32_t mask, bits; 256 257 y = y - s1->cursy; 258 mask = s1->cursmask[y]; 259 bits = s1->cursbits[y]; 260 len = MIN(width - s1->cursx, 32); 261 d = &d[s1->cursx]; 262 for (x = 0; x < len; x++) { 263 if (mask & 0x80000000) { 264 if (bits & 0x80000000) { 265 *d = s1->palette[259]; 266 } else { 267 *d = s1->palette[258]; 268 } 269 } 270 d++; 271 mask <<= 1; 272 bits <<= 1; 273 } 274 } 275 276 /* 277 XXX Could be much more optimal: 278 * detect if line/page/whole screen is in 24 bit mode 279 * if destination is also BGR, use memcpy 280 */ 281 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, 282 const uint8_t *s, int width, 283 const uint32_t *cplane, 284 const uint32_t *s24) 285 { 286 DisplaySurface *surface = qemu_console_surface(s1->con); 287 int x, bgr, r, g, b; 288 uint8_t val, *p8; 289 uint32_t *p = (uint32_t *)d; 290 uint32_t dval; 291 bgr = is_surface_bgr(surface); 292 for(x = 0; x < width; x++, s++, s24++) { 293 if (be32_to_cpu(*cplane) & 0x03000000) { 294 /* 24-bit direct, BGR order */ 295 p8 = (uint8_t *)s24; 296 p8++; 297 b = *p8++; 298 g = *p8++; 299 r = *p8; 300 if (bgr) 301 dval = rgb_to_pixel32bgr(r, g, b); 302 else 303 dval = rgb_to_pixel32(r, g, b); 304 } else { 305 /* 8-bit pseudocolor */ 306 val = *s; 307 dval = s1->palette[val]; 308 } 309 *p++ = dval; 310 cplane++; 311 } 312 } 313 314 /* Fixed line length 1024 allows us to do nice tricks not possible on 315 VGA... */ 316 317 static void tcx_update_display(void *opaque) 318 { 319 TCXState *ts = opaque; 320 DisplaySurface *surface = qemu_console_surface(ts->con); 321 ram_addr_t page, page_min, page_max; 322 int y, y_start, dd, ds; 323 uint8_t *d, *s; 324 void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); 325 void (*fc)(TCXState *s1, uint8_t *dst, int y, int width); 326 327 if (surface_bits_per_pixel(surface) == 0) { 328 return; 329 } 330 331 page = 0; 332 y_start = -1; 333 page_min = -1; 334 page_max = 0; 335 d = surface_data(surface); 336 s = ts->vram; 337 dd = surface_stride(surface); 338 ds = 1024; 339 340 switch (surface_bits_per_pixel(surface)) { 341 case 32: 342 f = tcx_draw_line32; 343 fc = tcx_draw_cursor32; 344 break; 345 case 15: 346 case 16: 347 f = tcx_draw_line16; 348 fc = tcx_draw_cursor16; 349 break; 350 default: 351 case 8: 352 f = tcx_draw_line8; 353 fc = tcx_draw_cursor8; 354 break; 355 case 0: 356 return; 357 } 358 359 memory_region_sync_dirty_bitmap(&ts->vram_mem); 360 for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) { 361 if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE, 362 DIRTY_MEMORY_VGA)) { 363 if (y_start < 0) 364 y_start = y; 365 if (page < page_min) 366 page_min = page; 367 if (page > page_max) 368 page_max = page; 369 370 f(ts, d, s, ts->width); 371 if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { 372 fc(ts, d, y, ts->width); 373 } 374 d += dd; 375 s += ds; 376 y++; 377 378 f(ts, d, s, ts->width); 379 if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { 380 fc(ts, d, y, ts->width); 381 } 382 d += dd; 383 s += ds; 384 y++; 385 386 f(ts, d, s, ts->width); 387 if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { 388 fc(ts, d, y, ts->width); 389 } 390 d += dd; 391 s += ds; 392 y++; 393 394 f(ts, d, s, ts->width); 395 if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { 396 fc(ts, d, y, ts->width); 397 } 398 d += dd; 399 s += ds; 400 y++; 401 } else { 402 if (y_start >= 0) { 403 /* flush to display */ 404 dpy_gfx_update(ts->con, 0, y_start, 405 ts->width, y - y_start); 406 y_start = -1; 407 } 408 d += dd * 4; 409 s += ds * 4; 410 y += 4; 411 } 412 } 413 if (y_start >= 0) { 414 /* flush to display */ 415 dpy_gfx_update(ts->con, 0, y_start, 416 ts->width, y - y_start); 417 } 418 /* reset modified pages */ 419 if (page_max >= page_min) { 420 memory_region_reset_dirty(&ts->vram_mem, 421 page_min, 422 (page_max - page_min) + TARGET_PAGE_SIZE, 423 DIRTY_MEMORY_VGA); 424 } 425 } 426 427 static void tcx24_update_display(void *opaque) 428 { 429 TCXState *ts = opaque; 430 DisplaySurface *surface = qemu_console_surface(ts->con); 431 ram_addr_t page, page_min, page_max, cpage, page24; 432 int y, y_start, dd, ds; 433 uint8_t *d, *s; 434 uint32_t *cptr, *s24; 435 436 if (surface_bits_per_pixel(surface) != 32) { 437 return; 438 } 439 440 page = 0; 441 page24 = ts->vram24_offset; 442 cpage = ts->cplane_offset; 443 y_start = -1; 444 page_min = -1; 445 page_max = 0; 446 d = surface_data(surface); 447 s = ts->vram; 448 s24 = ts->vram24; 449 cptr = ts->cplane; 450 dd = surface_stride(surface); 451 ds = 1024; 452 453 memory_region_sync_dirty_bitmap(&ts->vram_mem); 454 for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE, 455 page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) { 456 if (tcx24_check_dirty(ts, page, page24, cpage)) { 457 if (y_start < 0) 458 y_start = y; 459 if (page < page_min) 460 page_min = page; 461 if (page > page_max) 462 page_max = page; 463 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 464 if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { 465 tcx_draw_cursor32(ts, d, y, ts->width); 466 } 467 d += dd; 468 s += ds; 469 cptr += ds; 470 s24 += ds; 471 y++; 472 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 473 if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { 474 tcx_draw_cursor32(ts, d, y, ts->width); 475 } 476 d += dd; 477 s += ds; 478 cptr += ds; 479 s24 += ds; 480 y++; 481 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 482 if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { 483 tcx_draw_cursor32(ts, d, y, ts->width); 484 } 485 d += dd; 486 s += ds; 487 cptr += ds; 488 s24 += ds; 489 y++; 490 tcx24_draw_line32(ts, d, s, ts->width, cptr, s24); 491 if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) { 492 tcx_draw_cursor32(ts, d, y, ts->width); 493 } 494 d += dd; 495 s += ds; 496 cptr += ds; 497 s24 += ds; 498 y++; 499 } else { 500 if (y_start >= 0) { 501 /* flush to display */ 502 dpy_gfx_update(ts->con, 0, y_start, 503 ts->width, y - y_start); 504 y_start = -1; 505 } 506 d += dd * 4; 507 s += ds * 4; 508 cptr += ds * 4; 509 s24 += ds * 4; 510 y += 4; 511 } 512 } 513 if (y_start >= 0) { 514 /* flush to display */ 515 dpy_gfx_update(ts->con, 0, y_start, 516 ts->width, y - y_start); 517 } 518 /* reset modified pages */ 519 if (page_max >= page_min) { 520 tcx24_reset_dirty(ts, page_min, page_max, page24, cpage); 521 } 522 } 523 524 static void tcx_invalidate_display(void *opaque) 525 { 526 TCXState *s = opaque; 527 528 tcx_set_dirty(s); 529 qemu_console_resize(s->con, s->width, s->height); 530 } 531 532 static void tcx24_invalidate_display(void *opaque) 533 { 534 TCXState *s = opaque; 535 536 tcx_set_dirty(s); 537 qemu_console_resize(s->con, s->width, s->height); 538 } 539 540 static int vmstate_tcx_post_load(void *opaque, int version_id) 541 { 542 TCXState *s = opaque; 543 544 update_palette_entries(s, 0, 256); 545 tcx_set_dirty(s); 546 return 0; 547 } 548 549 static const VMStateDescription vmstate_tcx = { 550 .name ="tcx", 551 .version_id = 4, 552 .minimum_version_id = 4, 553 .post_load = vmstate_tcx_post_load, 554 .fields = (VMStateField[]) { 555 VMSTATE_UINT16(height, TCXState), 556 VMSTATE_UINT16(width, TCXState), 557 VMSTATE_UINT16(depth, TCXState), 558 VMSTATE_BUFFER(r, TCXState), 559 VMSTATE_BUFFER(g, TCXState), 560 VMSTATE_BUFFER(b, TCXState), 561 VMSTATE_UINT8(dac_index, TCXState), 562 VMSTATE_UINT8(dac_state, TCXState), 563 VMSTATE_END_OF_LIST() 564 } 565 }; 566 567 static void tcx_reset(DeviceState *d) 568 { 569 TCXState *s = TCX(d); 570 571 /* Initialize palette */ 572 memset(s->r, 0, 260); 573 memset(s->g, 0, 260); 574 memset(s->b, 0, 260); 575 s->r[255] = s->g[255] = s->b[255] = 255; 576 s->r[256] = s->g[256] = s->b[256] = 255; 577 s->r[258] = s->g[258] = s->b[258] = 255; 578 update_palette_entries(s, 0, 260); 579 memset(s->vram, 0, MAXX*MAXY); 580 memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4), 581 DIRTY_MEMORY_VGA); 582 s->dac_index = 0; 583 s->dac_state = 0; 584 s->cursx = 0xf000; /* Put cursor off screen */ 585 s->cursy = 0xf000; 586 } 587 588 static uint64_t tcx_dac_readl(void *opaque, hwaddr addr, 589 unsigned size) 590 { 591 TCXState *s = opaque; 592 uint32_t val = 0; 593 594 switch (s->dac_state) { 595 case 0: 596 val = s->r[s->dac_index] << 24; 597 s->dac_state++; 598 break; 599 case 1: 600 val = s->g[s->dac_index] << 24; 601 s->dac_state++; 602 break; 603 case 2: 604 val = s->b[s->dac_index] << 24; 605 s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */ 606 default: 607 s->dac_state = 0; 608 break; 609 } 610 611 return val; 612 } 613 614 static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val, 615 unsigned size) 616 { 617 TCXState *s = opaque; 618 unsigned index; 619 620 switch (addr) { 621 case 0: /* Address */ 622 s->dac_index = val >> 24; 623 s->dac_state = 0; 624 break; 625 case 4: /* Pixel colours */ 626 case 12: /* Overlay (cursor) colours */ 627 if (addr & 8) { 628 index = (s->dac_index & 3) + 256; 629 } else { 630 index = s->dac_index; 631 } 632 switch (s->dac_state) { 633 case 0: 634 s->r[index] = val >> 24; 635 update_palette_entries(s, index, index + 1); 636 s->dac_state++; 637 break; 638 case 1: 639 s->g[index] = val >> 24; 640 update_palette_entries(s, index, index + 1); 641 s->dac_state++; 642 break; 643 case 2: 644 s->b[index] = val >> 24; 645 update_palette_entries(s, index, index + 1); 646 s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */ 647 default: 648 s->dac_state = 0; 649 break; 650 } 651 break; 652 default: /* Control registers */ 653 break; 654 } 655 } 656 657 static const MemoryRegionOps tcx_dac_ops = { 658 .read = tcx_dac_readl, 659 .write = tcx_dac_writel, 660 .endianness = DEVICE_NATIVE_ENDIAN, 661 .valid = { 662 .min_access_size = 4, 663 .max_access_size = 4, 664 }, 665 }; 666 667 static uint64_t tcx_stip_readl(void *opaque, hwaddr addr, 668 unsigned size) 669 { 670 return 0; 671 } 672 673 static void tcx_stip_writel(void *opaque, hwaddr addr, 674 uint64_t val, unsigned size) 675 { 676 TCXState *s = opaque; 677 int i; 678 uint32_t col; 679 680 if (!(addr & 4)) { 681 s->tmpblit = val; 682 } else { 683 addr = (addr >> 3) & 0xfffff; 684 col = cpu_to_be32(s->tmpblit); 685 if (s->depth == 24) { 686 for (i = 0; i < 32; i++) { 687 if (val & 0x80000000) { 688 s->vram[addr + i] = s->tmpblit; 689 s->vram24[addr + i] = col; 690 } 691 val <<= 1; 692 } 693 } else { 694 for (i = 0; i < 32; i++) { 695 if (val & 0x80000000) { 696 s->vram[addr + i] = s->tmpblit; 697 } 698 val <<= 1; 699 } 700 } 701 memory_region_set_dirty(&s->vram_mem, addr, 32); 702 } 703 } 704 705 static void tcx_rstip_writel(void *opaque, hwaddr addr, 706 uint64_t val, unsigned size) 707 { 708 TCXState *s = opaque; 709 int i; 710 uint32_t col; 711 712 if (!(addr & 4)) { 713 s->tmpblit = val; 714 } else { 715 addr = (addr >> 3) & 0xfffff; 716 col = cpu_to_be32(s->tmpblit); 717 if (s->depth == 24) { 718 for (i = 0; i < 32; i++) { 719 if (val & 0x80000000) { 720 s->vram[addr + i] = s->tmpblit; 721 s->vram24[addr + i] = col; 722 s->cplane[addr + i] = col; 723 } 724 val <<= 1; 725 } 726 } else { 727 for (i = 0; i < 32; i++) { 728 if (val & 0x80000000) { 729 s->vram[addr + i] = s->tmpblit; 730 } 731 val <<= 1; 732 } 733 } 734 memory_region_set_dirty(&s->vram_mem, addr, 32); 735 } 736 } 737 738 static const MemoryRegionOps tcx_stip_ops = { 739 .read = tcx_stip_readl, 740 .write = tcx_stip_writel, 741 .endianness = DEVICE_NATIVE_ENDIAN, 742 .valid = { 743 .min_access_size = 4, 744 .max_access_size = 4, 745 }, 746 }; 747 748 static const MemoryRegionOps tcx_rstip_ops = { 749 .read = tcx_stip_readl, 750 .write = tcx_rstip_writel, 751 .endianness = DEVICE_NATIVE_ENDIAN, 752 .valid = { 753 .min_access_size = 4, 754 .max_access_size = 4, 755 }, 756 }; 757 758 static uint64_t tcx_blit_readl(void *opaque, hwaddr addr, 759 unsigned size) 760 { 761 return 0; 762 } 763 764 static void tcx_blit_writel(void *opaque, hwaddr addr, 765 uint64_t val, unsigned size) 766 { 767 TCXState *s = opaque; 768 uint32_t adsr, len; 769 int i; 770 771 if (!(addr & 4)) { 772 s->tmpblit = val; 773 } else { 774 addr = (addr >> 3) & 0xfffff; 775 adsr = val & 0xffffff; 776 len = ((val >> 24) & 0x1f) + 1; 777 if (adsr == 0xffffff) { 778 memset(&s->vram[addr], s->tmpblit, len); 779 if (s->depth == 24) { 780 val = s->tmpblit & 0xffffff; 781 val = cpu_to_be32(val); 782 for (i = 0; i < len; i++) { 783 s->vram24[addr + i] = val; 784 } 785 } 786 } else { 787 memcpy(&s->vram[addr], &s->vram[adsr], len); 788 if (s->depth == 24) { 789 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); 790 } 791 } 792 memory_region_set_dirty(&s->vram_mem, addr, len); 793 } 794 } 795 796 static void tcx_rblit_writel(void *opaque, hwaddr addr, 797 uint64_t val, unsigned size) 798 { 799 TCXState *s = opaque; 800 uint32_t adsr, len; 801 int i; 802 803 if (!(addr & 4)) { 804 s->tmpblit = val; 805 } else { 806 addr = (addr >> 3) & 0xfffff; 807 adsr = val & 0xffffff; 808 len = ((val >> 24) & 0x1f) + 1; 809 if (adsr == 0xffffff) { 810 memset(&s->vram[addr], s->tmpblit, len); 811 if (s->depth == 24) { 812 val = s->tmpblit & 0xffffff; 813 val = cpu_to_be32(val); 814 for (i = 0; i < len; i++) { 815 s->vram24[addr + i] = val; 816 s->cplane[addr + i] = val; 817 } 818 } 819 } else { 820 memcpy(&s->vram[addr], &s->vram[adsr], len); 821 if (s->depth == 24) { 822 memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4); 823 memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4); 824 } 825 } 826 memory_region_set_dirty(&s->vram_mem, addr, len); 827 } 828 } 829 830 static const MemoryRegionOps tcx_blit_ops = { 831 .read = tcx_blit_readl, 832 .write = tcx_blit_writel, 833 .endianness = DEVICE_NATIVE_ENDIAN, 834 .valid = { 835 .min_access_size = 4, 836 .max_access_size = 4, 837 }, 838 }; 839 840 static const MemoryRegionOps tcx_rblit_ops = { 841 .read = tcx_blit_readl, 842 .write = tcx_rblit_writel, 843 .endianness = DEVICE_NATIVE_ENDIAN, 844 .valid = { 845 .min_access_size = 4, 846 .max_access_size = 4, 847 }, 848 }; 849 850 static void tcx_invalidate_cursor_position(TCXState *s) 851 { 852 int ymin, ymax, start, end; 853 854 /* invalidate only near the cursor */ 855 ymin = s->cursy; 856 if (ymin >= s->height) { 857 return; 858 } 859 ymax = MIN(s->height, ymin + 32); 860 start = ymin * 1024; 861 end = ymax * 1024; 862 863 memory_region_set_dirty(&s->vram_mem, start, end-start); 864 } 865 866 static uint64_t tcx_thc_readl(void *opaque, hwaddr addr, 867 unsigned size) 868 { 869 TCXState *s = opaque; 870 uint64_t val; 871 872 if (addr == TCX_THC_MISC) { 873 val = s->thcmisc | 0x02000000; 874 } else { 875 val = 0; 876 } 877 return val; 878 } 879 880 static void tcx_thc_writel(void *opaque, hwaddr addr, 881 uint64_t val, unsigned size) 882 { 883 TCXState *s = opaque; 884 885 if (addr == TCX_THC_CURSXY) { 886 tcx_invalidate_cursor_position(s); 887 s->cursx = val >> 16; 888 s->cursy = val; 889 tcx_invalidate_cursor_position(s); 890 } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) { 891 s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val; 892 tcx_invalidate_cursor_position(s); 893 } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) { 894 s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val; 895 tcx_invalidate_cursor_position(s); 896 } else if (addr == TCX_THC_MISC) { 897 s->thcmisc = val; 898 } 899 900 } 901 902 static const MemoryRegionOps tcx_thc_ops = { 903 .read = tcx_thc_readl, 904 .write = tcx_thc_writel, 905 .endianness = DEVICE_NATIVE_ENDIAN, 906 .valid = { 907 .min_access_size = 4, 908 .max_access_size = 4, 909 }, 910 }; 911 912 static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr, 913 unsigned size) 914 { 915 return 0; 916 } 917 918 static void tcx_dummy_writel(void *opaque, hwaddr addr, 919 uint64_t val, unsigned size) 920 { 921 return; 922 } 923 924 static const MemoryRegionOps tcx_dummy_ops = { 925 .read = tcx_dummy_readl, 926 .write = tcx_dummy_writel, 927 .endianness = DEVICE_NATIVE_ENDIAN, 928 .valid = { 929 .min_access_size = 4, 930 .max_access_size = 4, 931 }, 932 }; 933 934 static const GraphicHwOps tcx_ops = { 935 .invalidate = tcx_invalidate_display, 936 .gfx_update = tcx_update_display, 937 }; 938 939 static const GraphicHwOps tcx24_ops = { 940 .invalidate = tcx24_invalidate_display, 941 .gfx_update = tcx24_update_display, 942 }; 943 944 static void tcx_initfn(Object *obj) 945 { 946 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 947 TCXState *s = TCX(obj); 948 949 memory_region_init_ram(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE, 950 &error_fatal); 951 memory_region_set_readonly(&s->rom, true); 952 sysbus_init_mmio(sbd, &s->rom); 953 954 /* 2/STIP : Stippler */ 955 memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip", 956 TCX_STIP_NREGS); 957 sysbus_init_mmio(sbd, &s->stip); 958 959 /* 3/BLIT : Blitter */ 960 memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit", 961 TCX_BLIT_NREGS); 962 sysbus_init_mmio(sbd, &s->blit); 963 964 /* 5/RSTIP : Raw Stippler */ 965 memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip", 966 TCX_RSTIP_NREGS); 967 sysbus_init_mmio(sbd, &s->rstip); 968 969 /* 6/RBLIT : Raw Blitter */ 970 memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit", 971 TCX_RBLIT_NREGS); 972 sysbus_init_mmio(sbd, &s->rblit); 973 974 /* 7/TEC : ??? */ 975 memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec", 976 TCX_TEC_NREGS); 977 sysbus_init_mmio(sbd, &s->tec); 978 979 /* 8/CMAP : DAC */ 980 memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac", 981 TCX_DAC_NREGS); 982 sysbus_init_mmio(sbd, &s->dac); 983 984 /* 9/THC : Cursor */ 985 memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc", 986 TCX_THC_NREGS); 987 sysbus_init_mmio(sbd, &s->thc); 988 989 /* 11/DHC : ??? */ 990 memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc", 991 TCX_DHC_NREGS); 992 sysbus_init_mmio(sbd, &s->dhc); 993 994 /* 12/ALT : ??? */ 995 memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt", 996 TCX_ALT_NREGS); 997 sysbus_init_mmio(sbd, &s->alt); 998 } 999 1000 static void tcx_realizefn(DeviceState *dev, Error **errp) 1001 { 1002 SysBusDevice *sbd = SYS_BUS_DEVICE(dev); 1003 TCXState *s = TCX(dev); 1004 ram_addr_t vram_offset = 0; 1005 int size, ret; 1006 uint8_t *vram_base; 1007 char *fcode_filename; 1008 1009 memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram", 1010 s->vram_size * (1 + 4 + 4), &error_fatal); 1011 vmstate_register_ram_global(&s->vram_mem); 1012 memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA); 1013 vram_base = memory_region_get_ram_ptr(&s->vram_mem); 1014 1015 /* 10/ROM : FCode ROM */ 1016 vmstate_register_ram_global(&s->rom); 1017 fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE); 1018 if (fcode_filename) { 1019 ret = load_image_targphys(fcode_filename, s->prom_addr, 1020 FCODE_MAX_ROM_SIZE); 1021 g_free(fcode_filename); 1022 if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { 1023 error_report("tcx: could not load prom '%s'", TCX_ROM_FILE); 1024 } 1025 } 1026 1027 /* 0/DFB8 : 8-bit plane */ 1028 s->vram = vram_base; 1029 size = s->vram_size; 1030 memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit", 1031 &s->vram_mem, vram_offset, size); 1032 sysbus_init_mmio(sbd, &s->vram_8bit); 1033 vram_offset += size; 1034 vram_base += size; 1035 1036 /* 1/DFB24 : 24bit plane */ 1037 size = s->vram_size * 4; 1038 s->vram24 = (uint32_t *)vram_base; 1039 s->vram24_offset = vram_offset; 1040 memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit", 1041 &s->vram_mem, vram_offset, size); 1042 sysbus_init_mmio(sbd, &s->vram_24bit); 1043 vram_offset += size; 1044 vram_base += size; 1045 1046 /* 4/RDFB32 : Raw Framebuffer */ 1047 size = s->vram_size * 4; 1048 s->cplane = (uint32_t *)vram_base; 1049 s->cplane_offset = vram_offset; 1050 memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane", 1051 &s->vram_mem, vram_offset, size); 1052 sysbus_init_mmio(sbd, &s->vram_cplane); 1053 1054 /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */ 1055 if (s->depth == 8) { 1056 memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s, 1057 "tcx.thc24", TCX_THC_NREGS); 1058 sysbus_init_mmio(sbd, &s->thc24); 1059 } 1060 1061 sysbus_init_irq(sbd, &s->irq); 1062 1063 if (s->depth == 8) { 1064 s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s); 1065 } else { 1066 s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s); 1067 } 1068 s->thcmisc = 0; 1069 1070 qemu_console_resize(s->con, s->width, s->height); 1071 } 1072 1073 static Property tcx_properties[] = { 1074 DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1), 1075 DEFINE_PROP_UINT16("width", TCXState, width, -1), 1076 DEFINE_PROP_UINT16("height", TCXState, height, -1), 1077 DEFINE_PROP_UINT16("depth", TCXState, depth, -1), 1078 DEFINE_PROP_UINT64("prom_addr", TCXState, prom_addr, -1), 1079 DEFINE_PROP_END_OF_LIST(), 1080 }; 1081 1082 static void tcx_class_init(ObjectClass *klass, void *data) 1083 { 1084 DeviceClass *dc = DEVICE_CLASS(klass); 1085 1086 dc->realize = tcx_realizefn; 1087 dc->reset = tcx_reset; 1088 dc->vmsd = &vmstate_tcx; 1089 dc->props = tcx_properties; 1090 } 1091 1092 static const TypeInfo tcx_info = { 1093 .name = TYPE_TCX, 1094 .parent = TYPE_SYS_BUS_DEVICE, 1095 .instance_size = sizeof(TCXState), 1096 .instance_init = tcx_initfn, 1097 .class_init = tcx_class_init, 1098 }; 1099 1100 static void tcx_register_types(void) 1101 { 1102 type_register_static(&tcx_info); 1103 } 1104 1105 type_init(tcx_register_types) 1106