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, ram_addr_t addr, int len) 97 { 98 memory_region_set_dirty(&s->vram_mem, addr, len); 99 100 if (s->depth == 24) { 101 memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4, 102 len * 4); 103 memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4, 104 len * 4); 105 } 106 } 107 108 static int tcx_check_dirty(TCXState *s, ram_addr_t addr, int len) 109 { 110 int ret; 111 112 ret = memory_region_get_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA); 113 114 if (s->depth == 24) { 115 ret |= memory_region_get_dirty(&s->vram_mem, 116 s->vram24_offset + addr * 4, len * 4, 117 DIRTY_MEMORY_VGA); 118 ret |= memory_region_get_dirty(&s->vram_mem, 119 s->cplane_offset + addr * 4, len * 4, 120 DIRTY_MEMORY_VGA); 121 } 122 123 return ret; 124 } 125 126 static void tcx_reset_dirty(TCXState *s, ram_addr_t addr, int len) 127 { 128 memory_region_reset_dirty(&s->vram_mem, addr, len, DIRTY_MEMORY_VGA); 129 130 if (s->depth == 24) { 131 memory_region_reset_dirty(&s->vram_mem, s->vram24_offset + addr * 4, 132 len * 4, DIRTY_MEMORY_VGA); 133 memory_region_reset_dirty(&s->vram_mem, s->cplane_offset + addr * 4, 134 len * 4, DIRTY_MEMORY_VGA); 135 } 136 } 137 138 static void update_palette_entries(TCXState *s, int start, int end) 139 { 140 DisplaySurface *surface = qemu_console_surface(s->con); 141 int i; 142 143 for (i = start; i < end; i++) { 144 switch (surface_bits_per_pixel(surface)) { 145 default: 146 case 8: 147 s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]); 148 break; 149 case 15: 150 s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]); 151 break; 152 case 16: 153 s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]); 154 break; 155 case 32: 156 if (is_surface_bgr(surface)) { 157 s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]); 158 } else { 159 s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]); 160 } 161 break; 162 } 163 } 164 tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); 165 } 166 167 static void tcx_draw_line32(TCXState *s1, uint8_t *d, 168 const uint8_t *s, int width) 169 { 170 int x; 171 uint8_t val; 172 uint32_t *p = (uint32_t *)d; 173 174 for (x = 0; x < width; x++) { 175 val = *s++; 176 *p++ = s1->palette[val]; 177 } 178 } 179 180 static void tcx_draw_line16(TCXState *s1, uint8_t *d, 181 const uint8_t *s, int width) 182 { 183 int x; 184 uint8_t val; 185 uint16_t *p = (uint16_t *)d; 186 187 for (x = 0; x < width; x++) { 188 val = *s++; 189 *p++ = s1->palette[val]; 190 } 191 } 192 193 static void tcx_draw_line8(TCXState *s1, uint8_t *d, 194 const uint8_t *s, int width) 195 { 196 int x; 197 uint8_t val; 198 199 for(x = 0; x < width; x++) { 200 val = *s++; 201 *d++ = s1->palette[val]; 202 } 203 } 204 205 static void tcx_draw_cursor32(TCXState *s1, uint8_t *d, 206 int y, int width) 207 { 208 int x, len; 209 uint32_t mask, bits; 210 uint32_t *p = (uint32_t *)d; 211 212 y = y - s1->cursy; 213 mask = s1->cursmask[y]; 214 bits = s1->cursbits[y]; 215 len = MIN(width - s1->cursx, 32); 216 p = &p[s1->cursx]; 217 for (x = 0; x < len; x++) { 218 if (mask & 0x80000000) { 219 if (bits & 0x80000000) { 220 *p = s1->palette[259]; 221 } else { 222 *p = s1->palette[258]; 223 } 224 } 225 p++; 226 mask <<= 1; 227 bits <<= 1; 228 } 229 } 230 231 static void tcx_draw_cursor16(TCXState *s1, uint8_t *d, 232 int y, int width) 233 { 234 int x, len; 235 uint32_t mask, bits; 236 uint16_t *p = (uint16_t *)d; 237 238 y = y - s1->cursy; 239 mask = s1->cursmask[y]; 240 bits = s1->cursbits[y]; 241 len = MIN(width - s1->cursx, 32); 242 p = &p[s1->cursx]; 243 for (x = 0; x < len; x++) { 244 if (mask & 0x80000000) { 245 if (bits & 0x80000000) { 246 *p = s1->palette[259]; 247 } else { 248 *p = s1->palette[258]; 249 } 250 } 251 p++; 252 mask <<= 1; 253 bits <<= 1; 254 } 255 } 256 257 static void tcx_draw_cursor8(TCXState *s1, uint8_t *d, 258 int y, int width) 259 { 260 int x, len; 261 uint32_t mask, bits; 262 263 y = y - s1->cursy; 264 mask = s1->cursmask[y]; 265 bits = s1->cursbits[y]; 266 len = MIN(width - s1->cursx, 32); 267 d = &d[s1->cursx]; 268 for (x = 0; x < len; x++) { 269 if (mask & 0x80000000) { 270 if (bits & 0x80000000) { 271 *d = s1->palette[259]; 272 } else { 273 *d = s1->palette[258]; 274 } 275 } 276 d++; 277 mask <<= 1; 278 bits <<= 1; 279 } 280 } 281 282 /* 283 XXX Could be much more optimal: 284 * detect if line/page/whole screen is in 24 bit mode 285 * if destination is also BGR, use memcpy 286 */ 287 static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d, 288 const uint8_t *s, int width, 289 const uint32_t *cplane, 290 const uint32_t *s24) 291 { 292 DisplaySurface *surface = qemu_console_surface(s1->con); 293 int x, bgr, r, g, b; 294 uint8_t val, *p8; 295 uint32_t *p = (uint32_t *)d; 296 uint32_t dval; 297 bgr = is_surface_bgr(surface); 298 for(x = 0; x < width; x++, s++, s24++) { 299 if (be32_to_cpu(*cplane) & 0x03000000) { 300 /* 24-bit direct, BGR order */ 301 p8 = (uint8_t *)s24; 302 p8++; 303 b = *p8++; 304 g = *p8++; 305 r = *p8; 306 if (bgr) 307 dval = rgb_to_pixel32bgr(r, g, b); 308 else 309 dval = rgb_to_pixel32(r, g, b); 310 } else { 311 /* 8-bit pseudocolor */ 312 val = *s; 313 dval = s1->palette[val]; 314 } 315 *p++ = dval; 316 cplane++; 317 } 318 } 319 320 /* Fixed line length 1024 allows us to do nice tricks not possible on 321 VGA... */ 322 323 static void tcx_update_display(void *opaque) 324 { 325 TCXState *ts = opaque; 326 DisplaySurface *surface = qemu_console_surface(ts->con); 327 ram_addr_t page, page_min, page_max; 328 int y, y_start, dd, ds; 329 uint8_t *d, *s; 330 void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width); 331 void (*fc)(TCXState *s1, uint8_t *dst, int y, int width); 332 333 if (surface_bits_per_pixel(surface) == 0) { 334 return; 335 } 336 337 page = 0; 338 y_start = -1; 339 page_min = -1; 340 page_max = 0; 341 d = surface_data(surface); 342 s = ts->vram; 343 dd = surface_stride(surface); 344 ds = 1024; 345 346 switch (surface_bits_per_pixel(surface)) { 347 case 32: 348 f = tcx_draw_line32; 349 fc = tcx_draw_cursor32; 350 break; 351 case 15: 352 case 16: 353 f = tcx_draw_line16; 354 fc = tcx_draw_cursor16; 355 break; 356 default: 357 case 8: 358 f = tcx_draw_line8; 359 fc = tcx_draw_cursor8; 360 break; 361 case 0: 362 return; 363 } 364 365 memory_region_sync_dirty_bitmap(&ts->vram_mem); 366 for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) { 367 if (tcx_check_dirty(ts, page, TARGET_PAGE_SIZE)) { 368 if (y_start < 0) 369 y_start = y; 370 if (page < page_min) 371 page_min = page; 372 if (page > page_max) 373 page_max = page; 374 375 f(ts, d, s, ts->width); 376 if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { 377 fc(ts, d, y, ts->width); 378 } 379 d += dd; 380 s += ds; 381 y++; 382 383 f(ts, d, s, ts->width); 384 if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { 385 fc(ts, d, y, ts->width); 386 } 387 d += dd; 388 s += ds; 389 y++; 390 391 f(ts, d, s, ts->width); 392 if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { 393 fc(ts, d, y, ts->width); 394 } 395 d += dd; 396 s += ds; 397 y++; 398 399 f(ts, d, s, ts->width); 400 if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) { 401 fc(ts, d, y, ts->width); 402 } 403 d += dd; 404 s += ds; 405 y++; 406 } else { 407 if (y_start >= 0) { 408 /* flush to display */ 409 dpy_gfx_update(ts->con, 0, y_start, 410 ts->width, y - y_start); 411 y_start = -1; 412 } 413 d += dd * 4; 414 s += ds * 4; 415 y += 4; 416 } 417 } 418 if (y_start >= 0) { 419 /* flush to display */ 420 dpy_gfx_update(ts->con, 0, y_start, 421 ts->width, y - y_start); 422 } 423 /* reset modified pages */ 424 if (page_max >= page_min) { 425 tcx_reset_dirty(ts, page_min, page_max - page_min); 426 } 427 } 428 429 static void tcx24_update_display(void *opaque) 430 { 431 TCXState *ts = opaque; 432 DisplaySurface *surface = qemu_console_surface(ts->con); 433 ram_addr_t page, page_min, page_max; 434 int y, y_start, dd, ds; 435 uint8_t *d, *s; 436 uint32_t *cptr, *s24; 437 438 if (surface_bits_per_pixel(surface) != 32) { 439 return; 440 } 441 442 page = 0; 443 y_start = -1; 444 page_min = -1; 445 page_max = 0; 446 d = surface_data(surface); 447 s = ts->vram; 448 s24 = ts->vram24; 449 cptr = ts->cplane; 450 dd = surface_stride(surface); 451 ds = 1024; 452 453 memory_region_sync_dirty_bitmap(&ts->vram_mem); 454 for (y = 0; y < ts->height; page += TARGET_PAGE_SIZE) { 455 if (tcx_check_dirty(ts, page, TARGET_PAGE_SIZE)) { 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 tcx_reset_dirty(ts, page_min, page_max - page_min); 520 } 521 } 522 523 static void tcx_invalidate_display(void *opaque) 524 { 525 TCXState *s = opaque; 526 527 tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem)); 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, 0, memory_region_size(&s->vram_mem)); 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, 0, memory_region_size(&s->vram_mem)); 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