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