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