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