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    default:
 414        s->dac_state = 0;
 415        break;
 416    }
 417
 418    return val;
 419}
 420
 421static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
 422                           unsigned size)
 423{
 424    TCXState *s = opaque;
 425    unsigned index;
 426
 427    switch (addr) {
 428    case 0: /* Address */
 429        s->dac_index = val >> 24;
 430        s->dac_state = 0;
 431        break;
 432    case 4:  /* Pixel colours */
 433    case 12: /* Overlay (cursor) colours */
 434        if (addr & 8) {
 435            index = (s->dac_index & 3) + 256;
 436        } else {
 437            index = s->dac_index;
 438        }
 439        switch (s->dac_state) {
 440        case 0:
 441            s->r[index] = val >> 24;
 442            update_palette_entries(s, index, index + 1);
 443            s->dac_state++;
 444            break;
 445        case 1:
 446            s->g[index] = val >> 24;
 447            update_palette_entries(s, index, index + 1);
 448            s->dac_state++;
 449            break;
 450        case 2:
 451            s->b[index] = val >> 24;
 452            update_palette_entries(s, index, index + 1);
 453            s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
 454        default:
 455            s->dac_state = 0;
 456            break;
 457        }
 458        break;
 459    default: /* Control registers */
 460        break;
 461    }
 462}
 463
 464static const MemoryRegionOps tcx_dac_ops = {
 465    .read = tcx_dac_readl,
 466    .write = tcx_dac_writel,
 467    .endianness = DEVICE_NATIVE_ENDIAN,
 468    .valid = {
 469        .min_access_size = 4,
 470        .max_access_size = 4,
 471    },
 472};
 473
 474static uint64_t tcx_stip_readl(void *opaque, hwaddr addr,
 475                               unsigned size)
 476{
 477    return 0;
 478}
 479
 480static void tcx_stip_writel(void *opaque, hwaddr addr,
 481                            uint64_t val, unsigned size)
 482{
 483    TCXState *s = opaque;
 484    int i;
 485    uint32_t col;
 486
 487    if (!(addr & 4)) {
 488        s->tmpblit = val;
 489    } else {
 490        addr = (addr >> 3) & 0xfffff;
 491        col = cpu_to_be32(s->tmpblit);
 492        if (s->depth == 24) {
 493            for (i = 0; i < 32; i++)  {
 494                if (val & 0x80000000) {
 495                    s->vram[addr + i] = s->tmpblit;
 496                    s->vram24[addr + i] = col;
 497                }
 498                val <<= 1;
 499            }
 500        } else {
 501            for (i = 0; i < 32; i++)  {
 502                if (val & 0x80000000) {
 503                    s->vram[addr + i] = s->tmpblit;
 504                }
 505                val <<= 1;
 506            }
 507        }
 508        tcx_set_dirty(s, addr, 32);
 509    }
 510}
 511
 512static void tcx_rstip_writel(void *opaque, hwaddr addr,
 513                             uint64_t val, unsigned size)
 514{
 515    TCXState *s = opaque;
 516    int i;
 517    uint32_t col;
 518
 519    if (!(addr & 4)) {
 520        s->tmpblit = val;
 521    } else {
 522        addr = (addr >> 3) & 0xfffff;
 523        col = cpu_to_be32(s->tmpblit);
 524        if (s->depth == 24) {
 525            for (i = 0; i < 32; i++) {
 526                if (val & 0x80000000) {
 527                    s->vram[addr + i] = s->tmpblit;
 528                    s->vram24[addr + i] = col;
 529                    s->cplane[addr + i] = col;
 530                }
 531                val <<= 1;
 532            }
 533        } else {
 534            for (i = 0; i < 32; i++)  {
 535                if (val & 0x80000000) {
 536                    s->vram[addr + i] = s->tmpblit;
 537                }
 538                val <<= 1;
 539            }
 540        }
 541        tcx_set_dirty(s, addr, 32);
 542    }
 543}
 544
 545static const MemoryRegionOps tcx_stip_ops = {
 546    .read = tcx_stip_readl,
 547    .write = tcx_stip_writel,
 548    .endianness = DEVICE_NATIVE_ENDIAN,
 549    .valid = {
 550        .min_access_size = 4,
 551        .max_access_size = 4,
 552    },
 553};
 554
 555static const MemoryRegionOps tcx_rstip_ops = {
 556    .read = tcx_stip_readl,
 557    .write = tcx_rstip_writel,
 558    .endianness = DEVICE_NATIVE_ENDIAN,
 559    .valid = {
 560        .min_access_size = 4,
 561        .max_access_size = 4,
 562    },
 563};
 564
 565static uint64_t tcx_blit_readl(void *opaque, hwaddr addr,
 566                               unsigned size)
 567{
 568    return 0;
 569}
 570
 571static void tcx_blit_writel(void *opaque, hwaddr addr,
 572                            uint64_t val, unsigned size)
 573{
 574    TCXState *s = opaque;
 575    uint32_t adsr, len;
 576    int i;
 577
 578    if (!(addr & 4)) {
 579        s->tmpblit = val;
 580    } else {
 581        addr = (addr >> 3) & 0xfffff;
 582        adsr = val & 0xffffff;
 583        len = ((val >> 24) & 0x1f) + 1;
 584        if (adsr == 0xffffff) {
 585            memset(&s->vram[addr], s->tmpblit, len);
 586            if (s->depth == 24) {
 587                val = s->tmpblit & 0xffffff;
 588                val = cpu_to_be32(val);
 589                for (i = 0; i < len; i++) {
 590                    s->vram24[addr + i] = val;
 591                }
 592            }
 593        } else {
 594            memcpy(&s->vram[addr], &s->vram[adsr], len);
 595            if (s->depth == 24) {
 596                memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
 597            }
 598        }
 599        tcx_set_dirty(s, addr, len);
 600    }
 601}
 602
 603static void tcx_rblit_writel(void *opaque, hwaddr addr,
 604                         uint64_t val, unsigned size)
 605{
 606    TCXState *s = opaque;
 607    uint32_t adsr, len;
 608    int i;
 609
 610    if (!(addr & 4)) {
 611        s->tmpblit = val;
 612    } else {
 613        addr = (addr >> 3) & 0xfffff;
 614        adsr = val & 0xffffff;
 615        len = ((val >> 24) & 0x1f) + 1;
 616        if (adsr == 0xffffff) {
 617            memset(&s->vram[addr], s->tmpblit, len);
 618            if (s->depth == 24) {
 619                val = s->tmpblit & 0xffffff;
 620                val = cpu_to_be32(val);
 621                for (i = 0; i < len; i++) {
 622                    s->vram24[addr + i] = val;
 623                    s->cplane[addr + i] = val;
 624                }
 625            }
 626        } else {
 627            memcpy(&s->vram[addr], &s->vram[adsr], len);
 628            if (s->depth == 24) {
 629                memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
 630                memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4);
 631            }
 632        }
 633        tcx_set_dirty(s, addr, len);
 634    }
 635}
 636
 637static const MemoryRegionOps tcx_blit_ops = {
 638    .read = tcx_blit_readl,
 639    .write = tcx_blit_writel,
 640    .endianness = DEVICE_NATIVE_ENDIAN,
 641    .valid = {
 642        .min_access_size = 4,
 643        .max_access_size = 4,
 644    },
 645};
 646
 647static const MemoryRegionOps tcx_rblit_ops = {
 648    .read = tcx_blit_readl,
 649    .write = tcx_rblit_writel,
 650    .endianness = DEVICE_NATIVE_ENDIAN,
 651    .valid = {
 652        .min_access_size = 4,
 653        .max_access_size = 4,
 654    },
 655};
 656
 657static void tcx_invalidate_cursor_position(TCXState *s)
 658{
 659    int ymin, ymax, start, end;
 660
 661    /* invalidate only near the cursor */
 662    ymin = s->cursy;
 663    if (ymin >= s->height) {
 664        return;
 665    }
 666    ymax = MIN(s->height, ymin + 32);
 667    start = ymin * 1024;
 668    end   = ymax * 1024;
 669
 670    tcx_set_dirty(s, start, end - start);
 671}
 672
 673static uint64_t tcx_thc_readl(void *opaque, hwaddr addr,
 674                            unsigned size)
 675{
 676    TCXState *s = opaque;
 677    uint64_t val;
 678
 679    if (addr == TCX_THC_MISC) {
 680        val = s->thcmisc | 0x02000000;
 681    } else {
 682        val = 0;
 683    }
 684    return val;
 685}
 686
 687static void tcx_thc_writel(void *opaque, hwaddr addr,
 688                         uint64_t val, unsigned size)
 689{
 690    TCXState *s = opaque;
 691
 692    if (addr == TCX_THC_CURSXY) {
 693        tcx_invalidate_cursor_position(s);
 694        s->cursx = val >> 16;
 695        s->cursy = val;
 696        tcx_invalidate_cursor_position(s);
 697    } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) {
 698        s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val;
 699        tcx_invalidate_cursor_position(s);
 700    } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) {
 701        s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val;
 702        tcx_invalidate_cursor_position(s);
 703    } else if (addr == TCX_THC_MISC) {
 704        s->thcmisc = val;
 705    }
 706
 707}
 708
 709static const MemoryRegionOps tcx_thc_ops = {
 710    .read = tcx_thc_readl,
 711    .write = tcx_thc_writel,
 712    .endianness = DEVICE_NATIVE_ENDIAN,
 713    .valid = {
 714        .min_access_size = 4,
 715        .max_access_size = 4,
 716    },
 717};
 718
 719static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr,
 720                            unsigned size)
 721{
 722    return 0;
 723}
 724
 725static void tcx_dummy_writel(void *opaque, hwaddr addr,
 726                         uint64_t val, unsigned size)
 727{
 728    return;
 729}
 730
 731static const MemoryRegionOps tcx_dummy_ops = {
 732    .read = tcx_dummy_readl,
 733    .write = tcx_dummy_writel,
 734    .endianness = DEVICE_NATIVE_ENDIAN,
 735    .valid = {
 736        .min_access_size = 4,
 737        .max_access_size = 4,
 738    },
 739};
 740
 741static const GraphicHwOps tcx_ops = {
 742    .invalidate = tcx_invalidate_display,
 743    .gfx_update = tcx_update_display,
 744};
 745
 746static const GraphicHwOps tcx24_ops = {
 747    .invalidate = tcx24_invalidate_display,
 748    .gfx_update = tcx24_update_display,
 749};
 750
 751static void tcx_initfn(Object *obj)
 752{
 753    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 754    TCXState *s = TCX(obj);
 755
 756    memory_region_init_ram_nomigrate(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE,
 757                           &error_fatal);
 758    memory_region_set_readonly(&s->rom, true);
 759    sysbus_init_mmio(sbd, &s->rom);
 760
 761    /* 2/STIP : Stippler */
 762    memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip",
 763                          TCX_STIP_NREGS);
 764    sysbus_init_mmio(sbd, &s->stip);
 765
 766    /* 3/BLIT : Blitter */
 767    memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit",
 768                          TCX_BLIT_NREGS);
 769    sysbus_init_mmio(sbd, &s->blit);
 770
 771    /* 5/RSTIP : Raw Stippler */
 772    memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip",
 773                          TCX_RSTIP_NREGS);
 774    sysbus_init_mmio(sbd, &s->rstip);
 775
 776    /* 6/RBLIT : Raw Blitter */
 777    memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit",
 778                          TCX_RBLIT_NREGS);
 779    sysbus_init_mmio(sbd, &s->rblit);
 780
 781    /* 7/TEC : ??? */
 782    memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec",
 783                          TCX_TEC_NREGS);
 784    sysbus_init_mmio(sbd, &s->tec);
 785
 786    /* 8/CMAP : DAC */
 787    memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac",
 788                          TCX_DAC_NREGS);
 789    sysbus_init_mmio(sbd, &s->dac);
 790
 791    /* 9/THC : Cursor */
 792    memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc",
 793                          TCX_THC_NREGS);
 794    sysbus_init_mmio(sbd, &s->thc);
 795
 796    /* 11/DHC : ??? */
 797    memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc",
 798                          TCX_DHC_NREGS);
 799    sysbus_init_mmio(sbd, &s->dhc);
 800
 801    /* 12/ALT : ??? */
 802    memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt",
 803                          TCX_ALT_NREGS);
 804    sysbus_init_mmio(sbd, &s->alt);
 805}
 806
 807static void tcx_realizefn(DeviceState *dev, Error **errp)
 808{
 809    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 810    TCXState *s = TCX(dev);
 811    ram_addr_t vram_offset = 0;
 812    int size, ret;
 813    uint8_t *vram_base;
 814    char *fcode_filename;
 815
 816    memory_region_init_ram_nomigrate(&s->vram_mem, OBJECT(s), "tcx.vram",
 817                           s->vram_size * (1 + 4 + 4), &error_fatal);
 818    vmstate_register_ram_global(&s->vram_mem);
 819    memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
 820    vram_base = memory_region_get_ram_ptr(&s->vram_mem);
 821
 822    /* 10/ROM : FCode ROM */
 823    vmstate_register_ram_global(&s->rom);
 824    fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE);
 825    if (fcode_filename) {
 826        ret = load_image_mr(fcode_filename, &s->rom);
 827        g_free(fcode_filename);
 828        if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
 829            warn_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
 830        }
 831    }
 832
 833    /* 0/DFB8 : 8-bit plane */
 834    s->vram = vram_base;
 835    size = s->vram_size;
 836    memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit",
 837                             &s->vram_mem, vram_offset, size);
 838    sysbus_init_mmio(sbd, &s->vram_8bit);
 839    vram_offset += size;
 840    vram_base += size;
 841
 842    /* 1/DFB24 : 24bit plane */
 843    size = s->vram_size * 4;
 844    s->vram24 = (uint32_t *)vram_base;
 845    s->vram24_offset = vram_offset;
 846    memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit",
 847                             &s->vram_mem, vram_offset, size);
 848    sysbus_init_mmio(sbd, &s->vram_24bit);
 849    vram_offset += size;
 850    vram_base += size;
 851
 852    /* 4/RDFB32 : Raw Framebuffer */
 853    size = s->vram_size * 4;
 854    s->cplane = (uint32_t *)vram_base;
 855    s->cplane_offset = vram_offset;
 856    memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane",
 857                             &s->vram_mem, vram_offset, size);
 858    sysbus_init_mmio(sbd, &s->vram_cplane);
 859
 860    /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */
 861    if (s->depth == 8) {
 862        memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s,
 863                              "tcx.thc24", TCX_THC_NREGS);
 864        sysbus_init_mmio(sbd, &s->thc24);
 865    }
 866
 867    sysbus_init_irq(sbd, &s->irq);
 868
 869    if (s->depth == 8) {
 870        s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s);
 871    } else {
 872        s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s);
 873    }
 874    s->thcmisc = 0;
 875
 876    qemu_console_resize(s->con, s->width, s->height);
 877}
 878
 879static Property tcx_properties[] = {
 880    DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1),
 881    DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
 882    DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
 883    DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
 884    DEFINE_PROP_END_OF_LIST(),
 885};
 886
 887static void tcx_class_init(ObjectClass *klass, void *data)
 888{
 889    DeviceClass *dc = DEVICE_CLASS(klass);
 890
 891    dc->realize = tcx_realizefn;
 892    dc->reset = tcx_reset;
 893    dc->vmsd = &vmstate_tcx;
 894    dc->props = tcx_properties;
 895}
 896
 897static const TypeInfo tcx_info = {
 898    .name          = TYPE_TCX,
 899    .parent        = TYPE_SYS_BUS_DEVICE,
 900    .instance_size = sizeof(TCXState),
 901    .instance_init = tcx_initfn,
 902    .class_init    = tcx_class_init,
 903};
 904
 905static void tcx_register_types(void)
 906{
 907    type_register_static(&tcx_info);
 908}
 909
 910type_init(tcx_register_types)
 911