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