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