qemu/hw/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 "console.h"
  26#include "pixel_ops.h"
  27#include "sysbus.h"
  28#include "qdev-addr.h"
  29
  30#define MAXX 1024
  31#define MAXY 768
  32#define TCX_DAC_NREGS 16
  33#define TCX_THC_NREGS_8  0x081c
  34#define TCX_THC_NREGS_24 0x1000
  35#define TCX_TEC_NREGS    0x1000
  36
  37typedef struct TCXState {
  38    SysBusDevice busdev;
  39    target_phys_addr_t addr;
  40    DisplayState *ds;
  41    uint8_t *vram;
  42    uint32_t *vram24, *cplane;
  43    ram_addr_t vram_offset, vram24_offset, cplane_offset;
  44    uint32_t vram_size;
  45    uint16_t width, height, depth;
  46    uint8_t r[256], g[256], b[256];
  47    uint32_t palette[256];
  48    uint8_t dac_index, dac_state;
  49} TCXState;
  50
  51static void tcx_screen_dump(void *opaque, const char *filename);
  52static void tcx24_screen_dump(void *opaque, const char *filename);
  53
  54static void tcx_set_dirty(TCXState *s)
  55{
  56    unsigned int i;
  57
  58    for (i = 0; i < MAXX * MAXY; i += TARGET_PAGE_SIZE) {
  59        cpu_physical_memory_set_dirty(s->vram_offset + i);
  60    }
  61}
  62
  63static void tcx24_set_dirty(TCXState *s)
  64{
  65    unsigned int i;
  66
  67    for (i = 0; i < MAXX * MAXY * 4; i += TARGET_PAGE_SIZE) {
  68        cpu_physical_memory_set_dirty(s->vram24_offset + i);
  69        cpu_physical_memory_set_dirty(s->cplane_offset + i);
  70    }
  71}
  72
  73static void update_palette_entries(TCXState *s, int start, int end)
  74{
  75    int i;
  76    for(i = start; i < end; i++) {
  77        switch(ds_get_bits_per_pixel(s->ds)) {
  78        default:
  79        case 8:
  80            s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
  81            break;
  82        case 15:
  83            s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
  84            break;
  85        case 16:
  86            s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
  87            break;
  88        case 32:
  89            if (is_surface_bgr(s->ds->surface))
  90                s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
  91            else
  92                s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
  93            break;
  94        }
  95    }
  96    if (s->depth == 24) {
  97        tcx24_set_dirty(s);
  98    } else {
  99        tcx_set_dirty(s);
 100    }
 101}
 102
 103static void tcx_draw_line32(TCXState *s1, uint8_t *d,
 104                            const uint8_t *s, int width)
 105{
 106    int x;
 107    uint8_t val;
 108    uint32_t *p = (uint32_t *)d;
 109
 110    for(x = 0; x < width; x++) {
 111        val = *s++;
 112        *p++ = s1->palette[val];
 113    }
 114}
 115
 116static void tcx_draw_line16(TCXState *s1, uint8_t *d,
 117                            const uint8_t *s, int width)
 118{
 119    int x;
 120    uint8_t val;
 121    uint16_t *p = (uint16_t *)d;
 122
 123    for(x = 0; x < width; x++) {
 124        val = *s++;
 125        *p++ = s1->palette[val];
 126    }
 127}
 128
 129static void tcx_draw_line8(TCXState *s1, uint8_t *d,
 130                           const uint8_t *s, int width)
 131{
 132    int x;
 133    uint8_t val;
 134
 135    for(x = 0; x < width; x++) {
 136        val = *s++;
 137        *d++ = s1->palette[val];
 138    }
 139}
 140
 141/*
 142  XXX Could be much more optimal:
 143  * detect if line/page/whole screen is in 24 bit mode
 144  * if destination is also BGR, use memcpy
 145  */
 146static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
 147                                     const uint8_t *s, int width,
 148                                     const uint32_t *cplane,
 149                                     const uint32_t *s24)
 150{
 151    int x, bgr, r, g, b;
 152    uint8_t val, *p8;
 153    uint32_t *p = (uint32_t *)d;
 154    uint32_t dval;
 155
 156    bgr = is_surface_bgr(s1->ds->surface);
 157    for(x = 0; x < width; x++, s++, s24++) {
 158        if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
 159            // 24-bit direct, BGR order
 160            p8 = (uint8_t *)s24;
 161            p8++;
 162            b = *p8++;
 163            g = *p8++;
 164            r = *p8;
 165            if (bgr)
 166                dval = rgb_to_pixel32bgr(r, g, b);
 167            else
 168                dval = rgb_to_pixel32(r, g, b);
 169        } else {
 170            val = *s;
 171            dval = s1->palette[val];
 172        }
 173        *p++ = dval;
 174    }
 175}
 176
 177static inline int check_dirty(ram_addr_t page, ram_addr_t page24,
 178                              ram_addr_t cpage)
 179{
 180    int ret;
 181    unsigned int off;
 182
 183    ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
 184    for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
 185        ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
 186        ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
 187    }
 188    return ret;
 189}
 190
 191static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
 192                               ram_addr_t page_max, ram_addr_t page24,
 193                              ram_addr_t cpage)
 194{
 195    cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
 196                                    VGA_DIRTY_FLAG);
 197    page_min -= ts->vram_offset;
 198    page_max -= ts->vram_offset;
 199    cpu_physical_memory_reset_dirty(page24 + page_min * 4,
 200                                    page24 + page_max * 4 + TARGET_PAGE_SIZE,
 201                                    VGA_DIRTY_FLAG);
 202    cpu_physical_memory_reset_dirty(cpage + page_min * 4,
 203                                    cpage + page_max * 4 + TARGET_PAGE_SIZE,
 204                                    VGA_DIRTY_FLAG);
 205}
 206
 207/* Fixed line length 1024 allows us to do nice tricks not possible on
 208   VGA... */
 209static void tcx_update_display(void *opaque)
 210{
 211    TCXState *ts = opaque;
 212    ram_addr_t page, page_min, page_max;
 213    int y, y_start, dd, ds;
 214    uint8_t *d, *s;
 215    void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
 216
 217    if (ds_get_bits_per_pixel(ts->ds) == 0)
 218        return;
 219    page = ts->vram_offset;
 220    y_start = -1;
 221    page_min = -1;
 222    page_max = 0;
 223    d = ds_get_data(ts->ds);
 224    s = ts->vram;
 225    dd = ds_get_linesize(ts->ds);
 226    ds = 1024;
 227
 228    switch (ds_get_bits_per_pixel(ts->ds)) {
 229    case 32:
 230        f = tcx_draw_line32;
 231        break;
 232    case 15:
 233    case 16:
 234        f = tcx_draw_line16;
 235        break;
 236    default:
 237    case 8:
 238        f = tcx_draw_line8;
 239        break;
 240    case 0:
 241        return;
 242    }
 243
 244    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
 245        if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
 246            if (y_start < 0)
 247                y_start = y;
 248            if (page < page_min)
 249                page_min = page;
 250            if (page > page_max)
 251                page_max = page;
 252            f(ts, d, s, ts->width);
 253            d += dd;
 254            s += ds;
 255            f(ts, d, s, ts->width);
 256            d += dd;
 257            s += ds;
 258            f(ts, d, s, ts->width);
 259            d += dd;
 260            s += ds;
 261            f(ts, d, s, ts->width);
 262            d += dd;
 263            s += ds;
 264        } else {
 265            if (y_start >= 0) {
 266                /* flush to display */
 267                dpy_update(ts->ds, 0, y_start,
 268                           ts->width, y - y_start);
 269                y_start = -1;
 270            }
 271            d += dd * 4;
 272            s += ds * 4;
 273        }
 274    }
 275    if (y_start >= 0) {
 276        /* flush to display */
 277        dpy_update(ts->ds, 0, y_start,
 278                   ts->width, y - y_start);
 279    }
 280    /* reset modified pages */
 281    if (page_max >= page_min) {
 282        cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
 283                                        VGA_DIRTY_FLAG);
 284    }
 285}
 286
 287static void tcx24_update_display(void *opaque)
 288{
 289    TCXState *ts = opaque;
 290    ram_addr_t page, page_min, page_max, cpage, page24;
 291    int y, y_start, dd, ds;
 292    uint8_t *d, *s;
 293    uint32_t *cptr, *s24;
 294
 295    if (ds_get_bits_per_pixel(ts->ds) != 32)
 296            return;
 297    page = ts->vram_offset;
 298    page24 = ts->vram24_offset;
 299    cpage = ts->cplane_offset;
 300    y_start = -1;
 301    page_min = -1;
 302    page_max = 0;
 303    d = ds_get_data(ts->ds);
 304    s = ts->vram;
 305    s24 = ts->vram24;
 306    cptr = ts->cplane;
 307    dd = ds_get_linesize(ts->ds);
 308    ds = 1024;
 309
 310    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
 311            page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
 312        if (check_dirty(page, page24, cpage)) {
 313            if (y_start < 0)
 314                y_start = y;
 315            if (page < page_min)
 316                page_min = page;
 317            if (page > page_max)
 318                page_max = page;
 319            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
 320            d += dd;
 321            s += ds;
 322            cptr += ds;
 323            s24 += ds;
 324            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
 325            d += dd;
 326            s += ds;
 327            cptr += ds;
 328            s24 += ds;
 329            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
 330            d += dd;
 331            s += ds;
 332            cptr += ds;
 333            s24 += ds;
 334            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
 335            d += dd;
 336            s += ds;
 337            cptr += ds;
 338            s24 += ds;
 339        } else {
 340            if (y_start >= 0) {
 341                /* flush to display */
 342                dpy_update(ts->ds, 0, y_start,
 343                           ts->width, y - y_start);
 344                y_start = -1;
 345            }
 346            d += dd * 4;
 347            s += ds * 4;
 348            cptr += ds * 4;
 349            s24 += ds * 4;
 350        }
 351    }
 352    if (y_start >= 0) {
 353        /* flush to display */
 354        dpy_update(ts->ds, 0, y_start,
 355                   ts->width, y - y_start);
 356    }
 357    /* reset modified pages */
 358    if (page_max >= page_min) {
 359        reset_dirty(ts, page_min, page_max, page24, cpage);
 360    }
 361}
 362
 363static void tcx_invalidate_display(void *opaque)
 364{
 365    TCXState *s = opaque;
 366
 367    tcx_set_dirty(s);
 368    qemu_console_resize(s->ds, s->width, s->height);
 369}
 370
 371static void tcx24_invalidate_display(void *opaque)
 372{
 373    TCXState *s = opaque;
 374
 375    tcx_set_dirty(s);
 376    tcx24_set_dirty(s);
 377    qemu_console_resize(s->ds, s->width, s->height);
 378}
 379
 380static int vmstate_tcx_post_load(void *opaque, int version_id)
 381{
 382    TCXState *s = opaque;
 383
 384    update_palette_entries(s, 0, 256);
 385    if (s->depth == 24) {
 386        tcx24_set_dirty(s);
 387    } else {
 388        tcx_set_dirty(s);
 389    }
 390
 391    return 0;
 392}
 393
 394static const VMStateDescription vmstate_tcx = {
 395    .name ="tcx",
 396    .version_id = 4,
 397    .minimum_version_id = 4,
 398    .minimum_version_id_old = 4,
 399    .post_load = vmstate_tcx_post_load,
 400    .fields      = (VMStateField []) {
 401        VMSTATE_UINT16(height, TCXState),
 402        VMSTATE_UINT16(width, TCXState),
 403        VMSTATE_UINT16(depth, TCXState),
 404        VMSTATE_BUFFER(r, TCXState),
 405        VMSTATE_BUFFER(g, TCXState),
 406        VMSTATE_BUFFER(b, TCXState),
 407        VMSTATE_UINT8(dac_index, TCXState),
 408        VMSTATE_UINT8(dac_state, TCXState),
 409        VMSTATE_END_OF_LIST()
 410    }
 411};
 412
 413static void tcx_reset(DeviceState *d)
 414{
 415    TCXState *s = container_of(d, TCXState, busdev.qdev);
 416
 417    /* Initialize palette */
 418    memset(s->r, 0, 256);
 419    memset(s->g, 0, 256);
 420    memset(s->b, 0, 256);
 421    s->r[255] = s->g[255] = s->b[255] = 255;
 422    update_palette_entries(s, 0, 256);
 423    memset(s->vram, 0, MAXX*MAXY);
 424    cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
 425                                    MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
 426    s->dac_index = 0;
 427    s->dac_state = 0;
 428}
 429
 430static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
 431{
 432    return 0;
 433}
 434
 435static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
 436{
 437    TCXState *s = opaque;
 438
 439    switch (addr) {
 440    case 0:
 441        s->dac_index = val >> 24;
 442        s->dac_state = 0;
 443        break;
 444    case 4:
 445        switch (s->dac_state) {
 446        case 0:
 447            s->r[s->dac_index] = val >> 24;
 448            update_palette_entries(s, s->dac_index, s->dac_index + 1);
 449            s->dac_state++;
 450            break;
 451        case 1:
 452            s->g[s->dac_index] = val >> 24;
 453            update_palette_entries(s, s->dac_index, s->dac_index + 1);
 454            s->dac_state++;
 455            break;
 456        case 2:
 457            s->b[s->dac_index] = val >> 24;
 458            update_palette_entries(s, s->dac_index, s->dac_index + 1);
 459            s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
 460        default:
 461            s->dac_state = 0;
 462            break;
 463        }
 464        break;
 465    default:
 466        break;
 467    }
 468    return;
 469}
 470
 471static CPUReadMemoryFunc * const tcx_dac_read[3] = {
 472    NULL,
 473    NULL,
 474    tcx_dac_readl,
 475};
 476
 477static CPUWriteMemoryFunc * const tcx_dac_write[3] = {
 478    NULL,
 479    NULL,
 480    tcx_dac_writel,
 481};
 482
 483static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
 484{
 485    return 0;
 486}
 487
 488static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
 489                             uint32_t val)
 490{
 491}
 492
 493static CPUReadMemoryFunc * const tcx_dummy_read[3] = {
 494    NULL,
 495    NULL,
 496    tcx_dummy_readl,
 497};
 498
 499static CPUWriteMemoryFunc * const tcx_dummy_write[3] = {
 500    NULL,
 501    NULL,
 502    tcx_dummy_writel,
 503};
 504
 505static int tcx_init1(SysBusDevice *dev)
 506{
 507    TCXState *s = FROM_SYSBUS(TCXState, dev);
 508    int io_memory, dummy_memory;
 509    ram_addr_t vram_offset;
 510    int size;
 511    uint8_t *vram_base;
 512
 513    vram_offset = qemu_ram_alloc(NULL, "tcx.vram", s->vram_size * (1 + 4 + 4));
 514    vram_base = qemu_get_ram_ptr(vram_offset);
 515    s->vram_offset = vram_offset;
 516
 517    /* 8-bit plane */
 518    s->vram = vram_base;
 519    size = s->vram_size;
 520    sysbus_init_mmio(dev, size, s->vram_offset);
 521    vram_offset += size;
 522    vram_base += size;
 523
 524    /* DAC */
 525    io_memory = cpu_register_io_memory(tcx_dac_read, tcx_dac_write, s,
 526                                       DEVICE_NATIVE_ENDIAN);
 527    sysbus_init_mmio(dev, TCX_DAC_NREGS, io_memory);
 528
 529    /* TEC (dummy) */
 530    dummy_memory = cpu_register_io_memory(tcx_dummy_read, tcx_dummy_write,
 531                                          s, DEVICE_NATIVE_ENDIAN);
 532    sysbus_init_mmio(dev, TCX_TEC_NREGS, dummy_memory);
 533    /* THC: NetBSD writes here even with 8-bit display: dummy */
 534    sysbus_init_mmio(dev, TCX_THC_NREGS_24, dummy_memory);
 535
 536    if (s->depth == 24) {
 537        /* 24-bit plane */
 538        size = s->vram_size * 4;
 539        s->vram24 = (uint32_t *)vram_base;
 540        s->vram24_offset = vram_offset;
 541        sysbus_init_mmio(dev, size, vram_offset);
 542        vram_offset += size;
 543        vram_base += size;
 544
 545        /* Control plane */
 546        size = s->vram_size * 4;
 547        s->cplane = (uint32_t *)vram_base;
 548        s->cplane_offset = vram_offset;
 549        sysbus_init_mmio(dev, size, vram_offset);
 550
 551        s->ds = graphic_console_init(tcx24_update_display,
 552                                     tcx24_invalidate_display,
 553                                     tcx24_screen_dump, NULL, s);
 554    } else {
 555        /* THC 8 bit (dummy) */
 556        sysbus_init_mmio(dev, TCX_THC_NREGS_8, dummy_memory);
 557
 558        s->ds = graphic_console_init(tcx_update_display,
 559                                     tcx_invalidate_display,
 560                                     tcx_screen_dump, NULL, s);
 561    }
 562
 563    qemu_console_resize(s->ds, s->width, s->height);
 564    return 0;
 565}
 566
 567static void tcx_screen_dump(void *opaque, const char *filename)
 568{
 569    TCXState *s = opaque;
 570    FILE *f;
 571    uint8_t *d, *d1, v;
 572    int y, x;
 573
 574    f = fopen(filename, "wb");
 575    if (!f)
 576        return;
 577    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
 578    d1 = s->vram;
 579    for(y = 0; y < s->height; y++) {
 580        d = d1;
 581        for(x = 0; x < s->width; x++) {
 582            v = *d;
 583            fputc(s->r[v], f);
 584            fputc(s->g[v], f);
 585            fputc(s->b[v], f);
 586            d++;
 587        }
 588        d1 += MAXX;
 589    }
 590    fclose(f);
 591    return;
 592}
 593
 594static void tcx24_screen_dump(void *opaque, const char *filename)
 595{
 596    TCXState *s = opaque;
 597    FILE *f;
 598    uint8_t *d, *d1, v;
 599    uint32_t *s24, *cptr, dval;
 600    int y, x;
 601
 602    f = fopen(filename, "wb");
 603    if (!f)
 604        return;
 605    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
 606    d1 = s->vram;
 607    s24 = s->vram24;
 608    cptr = s->cplane;
 609    for(y = 0; y < s->height; y++) {
 610        d = d1;
 611        for(x = 0; x < s->width; x++, d++, s24++) {
 612            if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
 613                dval = *s24 & 0x00ffffff;
 614                fputc((dval >> 16) & 0xff, f);
 615                fputc((dval >> 8) & 0xff, f);
 616                fputc(dval & 0xff, f);
 617            } else {
 618                v = *d;
 619                fputc(s->r[v], f);
 620                fputc(s->g[v], f);
 621                fputc(s->b[v], f);
 622            }
 623        }
 624        d1 += MAXX;
 625    }
 626    fclose(f);
 627    return;
 628}
 629
 630static SysBusDeviceInfo tcx_info = {
 631    .init = tcx_init1,
 632    .qdev.name  = "SUNW,tcx",
 633    .qdev.size  = sizeof(TCXState),
 634    .qdev.reset = tcx_reset,
 635    .qdev.vmsd  = &vmstate_tcx,
 636    .qdev.props = (Property[]) {
 637        DEFINE_PROP_TADDR("addr",      TCXState, addr,      -1),
 638        DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
 639        DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
 640        DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
 641        DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
 642        DEFINE_PROP_END_OF_LIST(),
 643    }
 644};
 645
 646static void tcx_register_devices(void)
 647{
 648    sysbus_register_withprop(&tcx_info);
 649}
 650
 651device_init(tcx_register_devices)
 652