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