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