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