tcx.c 25.2 KB
Newer Older
1
/*
B
bellard 已提交
2
 * QEMU TCX Frame buffer
3
 *
B
bellard 已提交
4
 * Copyright (c) 2003-2005 Fabrice Bellard
5
 *
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
B
Blue Swirl 已提交
24

P
Peter Maydell 已提交
25
#include "qemu/osdep.h"
26
#include "qapi/error.h"
27
#include "qemu-common.h"
28 29
#include "ui/console.h"
#include "ui/pixel_ops.h"
30
#include "hw/loader.h"
31
#include "hw/sysbus.h"
32
#include "qemu/error-report.h"
33

34 35 36
#define TCX_ROM_FILE "QEMU,tcx.bin"
#define FCODE_MAX_ROM_SIZE 0x10000

37 38
#define MAXX 1024
#define MAXY 768
39 40 41
#define TCX_DAC_NREGS    16
#define TCX_THC_NREGS    0x1000
#define TCX_DHC_NREGS    0x4000
42
#define TCX_TEC_NREGS    0x1000
43 44 45 46 47 48 49 50 51 52
#define TCX_ALT_NREGS    0x8000
#define TCX_STIP_NREGS   0x800000
#define TCX_BLIT_NREGS   0x800000
#define TCX_RSTIP_NREGS  0x800000
#define TCX_RBLIT_NREGS  0x800000

#define TCX_THC_MISC     0x818
#define TCX_THC_CURSXY   0x8fc
#define TCX_THC_CURSMASK 0x900
#define TCX_THC_CURSBITS 0x980
53

A
Andreas Färber 已提交
54 55 56
#define TYPE_TCX "SUNW,tcx"
#define TCX(obj) OBJECT_CHECK(TCXState, (obj), TYPE_TCX)

57
typedef struct TCXState {
A
Andreas Färber 已提交
58 59
    SysBusDevice parent_obj;

60
    QemuConsole *con;
61
    qemu_irq irq;
B
bellard 已提交
62
    uint8_t *vram;
B
blueswir1 已提交
63
    uint32_t *vram24, *cplane;
64 65
    hwaddr prom_addr;
    MemoryRegion rom;
A
Avi Kivity 已提交
66 67 68
    MemoryRegion vram_mem;
    MemoryRegion vram_8bit;
    MemoryRegion vram_24bit;
69 70
    MemoryRegion stip;
    MemoryRegion blit;
A
Avi Kivity 已提交
71
    MemoryRegion vram_cplane;
72 73
    MemoryRegion rstip;
    MemoryRegion rblit;
A
Avi Kivity 已提交
74
    MemoryRegion tec;
75 76 77 78
    MemoryRegion dac;
    MemoryRegion thc;
    MemoryRegion dhc;
    MemoryRegion alt;
A
Avi Kivity 已提交
79
    MemoryRegion thc24;
80

A
Avi Kivity 已提交
81
    ram_addr_t vram24_offset, cplane_offset;
82
    uint32_t tmpblit;
G
Gerd Hoffmann 已提交
83
    uint32_t vram_size;
84 85
    uint32_t palette[260];
    uint8_t r[260], g[260], b[260];
86
    uint16_t width, height, depth;
B
bellard 已提交
87
    uint8_t dac_index, dac_state;
88 89 90 91 92
    uint32_t thcmisc;
    uint32_t cursmask[32];
    uint32_t cursbits[32];
    uint16_t cursx;
    uint16_t cursy;
93 94
} TCXState;

95
static void tcx_set_dirty(TCXState *s, ram_addr_t addr, int len)
B
Blue Swirl 已提交
96
{
97
    memory_region_set_dirty(&s->vram_mem, addr, len);
98 99 100 101 102 103 104

    if (s->depth == 24) {
        memory_region_set_dirty(&s->vram_mem, s->vram24_offset + addr * 4,
                                len * 4);
        memory_region_set_dirty(&s->vram_mem, s->cplane_offset + addr * 4,
                                len * 4);
    }
B
Blue Swirl 已提交
105 106
}

107 108
static int tcx_check_dirty(TCXState *s, DirtyBitmapSnapshot *snap,
                           ram_addr_t addr, int len)
B
Blue Swirl 已提交
109
{
110 111
    int ret;

112
    ret = memory_region_snapshot_get_dirty(&s->vram_mem, snap, addr, len);
113 114

    if (s->depth == 24) {
115 116 117 118
        ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
                                       s->vram24_offset + addr * 4, len * 4);
        ret |= memory_region_snapshot_get_dirty(&s->vram_mem, snap,
                                       s->cplane_offset + addr * 4, len * 4);
119 120
    }

121 122 123
    return ret;
}

B
bellard 已提交
124 125
static void update_palette_entries(TCXState *s, int start, int end)
{
126
    DisplaySurface *surface = qemu_console_surface(s->con);
B
bellard 已提交
127
    int i;
128 129

    for (i = start; i < end; i++) {
130 131 132 133
        if (is_surface_bgr(surface)) {
            s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
        } else {
            s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
B
bellard 已提交
134 135
        }
    }
136
    tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
B
bellard 已提交
137 138
}

139
static void tcx_draw_line32(TCXState *s1, uint8_t *d,
B
blueswir1 已提交
140
                            const uint8_t *s, int width)
141
{
B
bellard 已提交
142 143
    int x;
    uint8_t val;
T
ths 已提交
144
    uint32_t *p = (uint32_t *)d;
B
bellard 已提交
145

146
    for (x = 0; x < width; x++) {
B
blueswir1 已提交
147
        val = *s++;
T
ths 已提交
148
        *p++ = s1->palette[val];
B
bellard 已提交
149
    }
150 151
}

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
static void tcx_draw_cursor32(TCXState *s1, uint8_t *d,
                              int y, int width)
{
    int x, len;
    uint32_t mask, bits;
    uint32_t *p = (uint32_t *)d;

    y = y - s1->cursy;
    mask = s1->cursmask[y];
    bits = s1->cursbits[y];
    len = MIN(width - s1->cursx, 32);
    p = &p[s1->cursx];
    for (x = 0; x < len; x++) {
        if (mask & 0x80000000) {
            if (bits & 0x80000000) {
                *p = s1->palette[259];
            } else {
                *p = s1->palette[258];
            }
        }
        p++;
        mask <<= 1;
        bits <<= 1;
    }
}

B
blueswir1 已提交
178 179 180 181 182
/*
  XXX Could be much more optimal:
  * detect if line/page/whole screen is in 24 bit mode
  * if destination is also BGR, use memcpy
  */
B
blueswir1 已提交
183 184 185 186 187
static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
                                     const uint8_t *s, int width,
                                     const uint32_t *cplane,
                                     const uint32_t *s24)
{
188
    DisplaySurface *surface = qemu_console_surface(s1->con);
189
    int x, bgr, r, g, b;
B
blueswir1 已提交
190
    uint8_t val, *p8;
B
blueswir1 已提交
191 192
    uint32_t *p = (uint32_t *)d;
    uint32_t dval;
193
    bgr = is_surface_bgr(surface);
B
blueswir1 已提交
194
    for(x = 0; x < width; x++, s++, s24++) {
195 196
        if (be32_to_cpu(*cplane) & 0x03000000) {
            /* 24-bit direct, BGR order */
B
blueswir1 已提交
197 198 199 200
            p8 = (uint8_t *)s24;
            p8++;
            b = *p8++;
            g = *p8++;
201
            r = *p8;
202 203 204 205
            if (bgr)
                dval = rgb_to_pixel32bgr(r, g, b);
            else
                dval = rgb_to_pixel32(r, g, b);
B
blueswir1 已提交
206
        } else {
207
            /* 8-bit pseudocolor */
B
blueswir1 已提交
208 209 210 211
            val = *s;
            dval = s1->palette[val];
        }
        *p++ = dval;
212
        cplane++;
B
blueswir1 已提交
213 214 215
    }
}

B
bellard 已提交
216 217
/* Fixed line length 1024 allows us to do nice tricks not possible on
   VGA... */
218

P
pbrook 已提交
219
static void tcx_update_display(void *opaque)
220
{
B
bellard 已提交
221
    TCXState *ts = opaque;
222
    DisplaySurface *surface = qemu_console_surface(ts->con);
223 224
    ram_addr_t page;
    DirtyBitmapSnapshot *snap = NULL;
225
    int y, y_start, dd, ds;
B
bellard 已提交
226 227
    uint8_t *d, *s;

228
    if (surface_bits_per_pixel(surface) != 32) {
B
blueswir1 已提交
229
        return;
230 231
    }

A
Avi Kivity 已提交
232
    page = 0;
B
bellard 已提交
233
    y_start = -1;
234
    d = surface_data(surface);
B
bellard 已提交
235
    s = ts->vram;
236
    dd = surface_stride(surface);
B
bellard 已提交
237 238
    ds = 1024;

239
    memory_region_sync_dirty_bitmap(&ts->vram_mem);
240 241 242 243
    snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
                                             memory_region_size(&ts->vram_mem),
                                             DIRTY_MEMORY_VGA);

244
    for (y = 0; y < ts->height; y++, page += ds) {
245
        if (tcx_check_dirty(ts, snap, page, ds)) {
B
blueswir1 已提交
246
            if (y_start < 0)
B
bellard 已提交
247
                y_start = y;
248

249
            tcx_draw_line32(ts, d, s, ts->width);
250
            if (y >= ts->cursy && y < ts->cursy + 32 && ts->cursx < ts->width) {
251
                tcx_draw_cursor32(ts, d, y, ts->width);
252
            }
B
blueswir1 已提交
253
        } else {
B
bellard 已提交
254 255
            if (y_start >= 0) {
                /* flush to display */
256
                dpy_gfx_update(ts->con, 0, y_start,
257
                               ts->width, y - y_start);
B
bellard 已提交
258 259
                y_start = -1;
            }
B
blueswir1 已提交
260
        }
261 262
        s += ds;
        d += dd;
B
bellard 已提交
263 264
    }
    if (y_start >= 0) {
B
blueswir1 已提交
265
        /* flush to display */
266
        dpy_gfx_update(ts->con, 0, y_start,
267
                       ts->width, y - y_start);
B
bellard 已提交
268
    }
269
    g_free(snap);
270 271
}

B
blueswir1 已提交
272 273 274
static void tcx24_update_display(void *opaque)
{
    TCXState *ts = opaque;
275
    DisplaySurface *surface = qemu_console_surface(ts->con);
276 277
    ram_addr_t page;
    DirtyBitmapSnapshot *snap = NULL;
B
blueswir1 已提交
278 279 280 281
    int y, y_start, dd, ds;
    uint8_t *d, *s;
    uint32_t *cptr, *s24;

282
    if (surface_bits_per_pixel(surface) != 32) {
B
blueswir1 已提交
283
            return;
284 285
    }

A
Avi Kivity 已提交
286
    page = 0;
B
blueswir1 已提交
287
    y_start = -1;
288
    d = surface_data(surface);
B
blueswir1 已提交
289 290 291
    s = ts->vram;
    s24 = ts->vram24;
    cptr = ts->cplane;
292
    dd = surface_stride(surface);
B
blueswir1 已提交
293 294
    ds = 1024;

295
    memory_region_sync_dirty_bitmap(&ts->vram_mem);
296 297 298 299
    snap = memory_region_snapshot_and_clear_dirty(&ts->vram_mem, 0x0,
                                             memory_region_size(&ts->vram_mem),
                                             DIRTY_MEMORY_VGA);

300
    for (y = 0; y < ts->height; y++, page += ds) {
301
        if (tcx_check_dirty(ts, snap, page, ds)) {
B
blueswir1 已提交
302 303
            if (y_start < 0)
                y_start = y;
304

B
blueswir1 已提交
305
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
306 307 308
            if (y >= ts->cursy && y < ts->cursy+32 && ts->cursx < ts->width) {
                tcx_draw_cursor32(ts, d, y, ts->width);
            }
B
blueswir1 已提交
309 310 311
        } else {
            if (y_start >= 0) {
                /* flush to display */
312
                dpy_gfx_update(ts->con, 0, y_start,
313
                               ts->width, y - y_start);
B
blueswir1 已提交
314 315 316
                y_start = -1;
            }
        }
317 318 319 320
        d += dd;
        s += ds;
        cptr += ds;
        s24 += ds;
B
blueswir1 已提交
321 322 323
    }
    if (y_start >= 0) {
        /* flush to display */
324
        dpy_gfx_update(ts->con, 0, y_start,
325
                       ts->width, y - y_start);
B
blueswir1 已提交
326
    }
327
    g_free(snap);
B
blueswir1 已提交
328 329
}

P
pbrook 已提交
330
static void tcx_invalidate_display(void *opaque)
331
{
B
bellard 已提交
332 333
    TCXState *s = opaque;

334
    tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
335
    qemu_console_resize(s->con, s->width, s->height);
336 337
}

B
blueswir1 已提交
338 339 340 341
static void tcx24_invalidate_display(void *opaque)
{
    TCXState *s = opaque;

342
    tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
343
    qemu_console_resize(s->con, s->width, s->height);
B
blueswir1 已提交
344 345
}

346
static int vmstate_tcx_post_load(void *opaque, int version_id)
347 348
{
    TCXState *s = opaque;
349

B
bellard 已提交
350
    update_palette_entries(s, 0, 256);
351
    tcx_set_dirty(s, 0, memory_region_size(&s->vram_mem));
B
bellard 已提交
352
    return 0;
353 354
}

B
Blue Swirl 已提交
355 356 357 358
static const VMStateDescription vmstate_tcx = {
    .name ="tcx",
    .version_id = 4,
    .minimum_version_id = 4,
359
    .post_load = vmstate_tcx_post_load,
360
    .fields = (VMStateField[]) {
B
Blue Swirl 已提交
361 362 363 364 365 366 367 368 369 370 371 372
        VMSTATE_UINT16(height, TCXState),
        VMSTATE_UINT16(width, TCXState),
        VMSTATE_UINT16(depth, TCXState),
        VMSTATE_BUFFER(r, TCXState),
        VMSTATE_BUFFER(g, TCXState),
        VMSTATE_BUFFER(b, TCXState),
        VMSTATE_UINT8(dac_index, TCXState),
        VMSTATE_UINT8(dac_state, TCXState),
        VMSTATE_END_OF_LIST()
    }
};

373
static void tcx_reset(DeviceState *d)
374
{
A
Andreas Färber 已提交
375
    TCXState *s = TCX(d);
B
bellard 已提交
376 377

    /* Initialize palette */
378 379 380
    memset(s->r, 0, 260);
    memset(s->g, 0, 260);
    memset(s->b, 0, 260);
B
bellard 已提交
381
    s->r[255] = s->g[255] = s->b[255] = 255;
382 383 384
    s->r[256] = s->g[256] = s->b[256] = 255;
    s->r[258] = s->g[258] = s->b[258] = 255;
    update_palette_entries(s, 0, 260);
B
bellard 已提交
385
    memset(s->vram, 0, MAXX*MAXY);
A
Avi Kivity 已提交
386 387
    memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
                              DIRTY_MEMORY_VGA);
B
bellard 已提交
388 389
    s->dac_index = 0;
    s->dac_state = 0;
390 391
    s->cursx = 0xf000; /* Put cursor off screen */
    s->cursy = 0xf000;
B
bellard 已提交
392 393
}

A
Avi Kivity 已提交
394
static uint64_t tcx_dac_readl(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
395
                              unsigned size)
B
bellard 已提交
396
{
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
    TCXState *s = opaque;
    uint32_t val = 0;

    switch (s->dac_state) {
    case 0:
        val = s->r[s->dac_index] << 24;
        s->dac_state++;
        break;
    case 1:
        val = s->g[s->dac_index] << 24;
        s->dac_state++;
        break;
    case 2:
        val = s->b[s->dac_index] << 24;
        s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
    default:
        s->dac_state = 0;
        break;
    }

    return val;
B
bellard 已提交
418 419
}

A
Avi Kivity 已提交
420
static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
A
Avi Kivity 已提交
421
                           unsigned size)
B
bellard 已提交
422 423
{
    TCXState *s = opaque;
424
    unsigned index;
B
bellard 已提交
425

B
blueswir1 已提交
426
    switch (addr) {
427
    case 0: /* Address */
B
blueswir1 已提交
428 429 430
        s->dac_index = val >> 24;
        s->dac_state = 0;
        break;
431 432 433 434 435 436 437
    case 4:  /* Pixel colours */
    case 12: /* Overlay (cursor) colours */
        if (addr & 8) {
            index = (s->dac_index & 3) + 256;
        } else {
            index = s->dac_index;
        }
B
blueswir1 已提交
438 439
        switch (s->dac_state) {
        case 0:
440 441
            s->r[index] = val >> 24;
            update_palette_entries(s, index, index + 1);
B
blueswir1 已提交
442 443 444
            s->dac_state++;
            break;
        case 1:
445 446
            s->g[index] = val >> 24;
            update_palette_entries(s, index, index + 1);
B
blueswir1 已提交
447 448 449
            s->dac_state++;
            break;
        case 2:
450 451 452
            s->b[index] = val >> 24;
            update_palette_entries(s, index, index + 1);
            s->dac_index = (s->dac_index + 1) & 0xff; /* Index autoincrement */
B
blueswir1 已提交
453 454 455 456 457
        default:
            s->dac_state = 0;
            break;
        }
        break;
458
    default: /* Control registers */
B
blueswir1 已提交
459
        break;
B
bellard 已提交
460
    }
461 462
}

A
Avi Kivity 已提交
463 464 465 466 467 468 469 470
static const MemoryRegionOps tcx_dac_ops = {
    .read = tcx_dac_readl,
    .write = tcx_dac_writel,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
B
bellard 已提交
471 472
};

473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
static uint64_t tcx_stip_readl(void *opaque, hwaddr addr,
                               unsigned size)
{
    return 0;
}

static void tcx_stip_writel(void *opaque, hwaddr addr,
                            uint64_t val, unsigned size)
{
    TCXState *s = opaque;
    int i;
    uint32_t col;

    if (!(addr & 4)) {
        s->tmpblit = val;
    } else {
        addr = (addr >> 3) & 0xfffff;
        col = cpu_to_be32(s->tmpblit);
        if (s->depth == 24) {
            for (i = 0; i < 32; i++)  {
                if (val & 0x80000000) {
                    s->vram[addr + i] = s->tmpblit;
                    s->vram24[addr + i] = col;
                }
                val <<= 1;
            }
        } else {
            for (i = 0; i < 32; i++)  {
                if (val & 0x80000000) {
                    s->vram[addr + i] = s->tmpblit;
                }
                val <<= 1;
            }
        }
507
        tcx_set_dirty(s, addr, 32);
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539
    }
}

static void tcx_rstip_writel(void *opaque, hwaddr addr,
                             uint64_t val, unsigned size)
{
    TCXState *s = opaque;
    int i;
    uint32_t col;

    if (!(addr & 4)) {
        s->tmpblit = val;
    } else {
        addr = (addr >> 3) & 0xfffff;
        col = cpu_to_be32(s->tmpblit);
        if (s->depth == 24) {
            for (i = 0; i < 32; i++) {
                if (val & 0x80000000) {
                    s->vram[addr + i] = s->tmpblit;
                    s->vram24[addr + i] = col;
                    s->cplane[addr + i] = col;
                }
                val <<= 1;
            }
        } else {
            for (i = 0; i < 32; i++)  {
                if (val & 0x80000000) {
                    s->vram[addr + i] = s->tmpblit;
                }
                val <<= 1;
            }
        }
540
        tcx_set_dirty(s, addr, 32);
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597
    }
}

static const MemoryRegionOps tcx_stip_ops = {
    .read = tcx_stip_readl,
    .write = tcx_stip_writel,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
};

static const MemoryRegionOps tcx_rstip_ops = {
    .read = tcx_stip_readl,
    .write = tcx_rstip_writel,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
};

static uint64_t tcx_blit_readl(void *opaque, hwaddr addr,
                               unsigned size)
{
    return 0;
}

static void tcx_blit_writel(void *opaque, hwaddr addr,
                            uint64_t val, unsigned size)
{
    TCXState *s = opaque;
    uint32_t adsr, len;
    int i;

    if (!(addr & 4)) {
        s->tmpblit = val;
    } else {
        addr = (addr >> 3) & 0xfffff;
        adsr = val & 0xffffff;
        len = ((val >> 24) & 0x1f) + 1;
        if (adsr == 0xffffff) {
            memset(&s->vram[addr], s->tmpblit, len);
            if (s->depth == 24) {
                val = s->tmpblit & 0xffffff;
                val = cpu_to_be32(val);
                for (i = 0; i < len; i++) {
                    s->vram24[addr + i] = val;
                }
            }
        } else {
            memcpy(&s->vram[addr], &s->vram[adsr], len);
            if (s->depth == 24) {
                memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
            }
        }
598
        tcx_set_dirty(s, addr, len);
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
    }
}

static void tcx_rblit_writel(void *opaque, hwaddr addr,
                         uint64_t val, unsigned size)
{
    TCXState *s = opaque;
    uint32_t adsr, len;
    int i;

    if (!(addr & 4)) {
        s->tmpblit = val;
    } else {
        addr = (addr >> 3) & 0xfffff;
        adsr = val & 0xffffff;
        len = ((val >> 24) & 0x1f) + 1;
        if (adsr == 0xffffff) {
            memset(&s->vram[addr], s->tmpblit, len);
            if (s->depth == 24) {
                val = s->tmpblit & 0xffffff;
                val = cpu_to_be32(val);
                for (i = 0; i < len; i++) {
                    s->vram24[addr + i] = val;
                    s->cplane[addr + i] = val;
                }
            }
        } else {
            memcpy(&s->vram[addr], &s->vram[adsr], len);
            if (s->depth == 24) {
                memcpy(&s->vram24[addr], &s->vram24[adsr], len * 4);
                memcpy(&s->cplane[addr], &s->cplane[adsr], len * 4);
            }
        }
632
        tcx_set_dirty(s, addr, len);
633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
    }
}

static const MemoryRegionOps tcx_blit_ops = {
    .read = tcx_blit_readl,
    .write = tcx_blit_writel,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
};

static const MemoryRegionOps tcx_rblit_ops = {
    .read = tcx_blit_readl,
    .write = tcx_rblit_writel,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
};

static void tcx_invalidate_cursor_position(TCXState *s)
{
    int ymin, ymax, start, end;

    /* invalidate only near the cursor */
    ymin = s->cursy;
    if (ymin >= s->height) {
        return;
    }
    ymax = MIN(s->height, ymin + 32);
    start = ymin * 1024;
    end   = ymax * 1024;

669
    tcx_set_dirty(s, start, end - start);
670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
}

static uint64_t tcx_thc_readl(void *opaque, hwaddr addr,
                            unsigned size)
{
    TCXState *s = opaque;
    uint64_t val;

    if (addr == TCX_THC_MISC) {
        val = s->thcmisc | 0x02000000;
    } else {
        val = 0;
    }
    return val;
}

static void tcx_thc_writel(void *opaque, hwaddr addr,
                         uint64_t val, unsigned size)
{
    TCXState *s = opaque;

    if (addr == TCX_THC_CURSXY) {
        tcx_invalidate_cursor_position(s);
        s->cursx = val >> 16;
        s->cursy = val;
        tcx_invalidate_cursor_position(s);
    } else if (addr >= TCX_THC_CURSMASK && addr < TCX_THC_CURSMASK + 128) {
        s->cursmask[(addr - TCX_THC_CURSMASK) >> 2] = val;
        tcx_invalidate_cursor_position(s);
    } else if (addr >= TCX_THC_CURSBITS && addr < TCX_THC_CURSBITS + 128) {
        s->cursbits[(addr - TCX_THC_CURSBITS) >> 2] = val;
        tcx_invalidate_cursor_position(s);
    } else if (addr == TCX_THC_MISC) {
        s->thcmisc = val;
    }

}

static const MemoryRegionOps tcx_thc_ops = {
    .read = tcx_thc_readl,
    .write = tcx_thc_writel,
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
};

static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
719
                            unsigned size)
720 721 722 723
{
    return 0;
}

724
static void tcx_dummy_writel(void *opaque, hwaddr addr,
A
Avi Kivity 已提交
725
                         uint64_t val, unsigned size)
726
{
727
    return;
728 729
}

730 731 732
static const MemoryRegionOps tcx_dummy_ops = {
    .read = tcx_dummy_readl,
    .write = tcx_dummy_writel,
A
Avi Kivity 已提交
733 734 735 736 737
    .endianness = DEVICE_NATIVE_ENDIAN,
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
738 739
};

G
Gerd Hoffmann 已提交
740 741 742 743 744 745 746 747 748 749
static const GraphicHwOps tcx_ops = {
    .invalidate = tcx_invalidate_display,
    .gfx_update = tcx_update_display,
};

static const GraphicHwOps tcx24_ops = {
    .invalidate = tcx24_invalidate_display,
    .gfx_update = tcx24_update_display,
};

750 751 752 753 754
static void tcx_initfn(Object *obj)
{
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    TCXState *s = TCX(obj);

755
    memory_region_init_ram(&s->rom, obj, "tcx.prom", FCODE_MAX_ROM_SIZE,
756
                           &error_fatal);
757 758 759
    memory_region_set_readonly(&s->rom, true);
    sysbus_init_mmio(sbd, &s->rom);

760
    /* 2/STIP : Stippler */
761
    memory_region_init_io(&s->stip, obj, &tcx_stip_ops, s, "tcx.stip",
762 763 764 765
                          TCX_STIP_NREGS);
    sysbus_init_mmio(sbd, &s->stip);

    /* 3/BLIT : Blitter */
766
    memory_region_init_io(&s->blit, obj, &tcx_blit_ops, s, "tcx.blit",
767 768 769 770
                          TCX_BLIT_NREGS);
    sysbus_init_mmio(sbd, &s->blit);

    /* 5/RSTIP : Raw Stippler */
771
    memory_region_init_io(&s->rstip, obj, &tcx_rstip_ops, s, "tcx.rstip",
772 773 774 775
                          TCX_RSTIP_NREGS);
    sysbus_init_mmio(sbd, &s->rstip);

    /* 6/RBLIT : Raw Blitter */
776
    memory_region_init_io(&s->rblit, obj, &tcx_rblit_ops, s, "tcx.rblit",
777 778 779 780
                          TCX_RBLIT_NREGS);
    sysbus_init_mmio(sbd, &s->rblit);

    /* 7/TEC : ??? */
781 782
    memory_region_init_io(&s->tec, obj, &tcx_dummy_ops, s, "tcx.tec",
                          TCX_TEC_NREGS);
783 784 785
    sysbus_init_mmio(sbd, &s->tec);

    /* 8/CMAP : DAC */
786 787
    memory_region_init_io(&s->dac, obj, &tcx_dac_ops, s, "tcx.dac",
                          TCX_DAC_NREGS);
788 789
    sysbus_init_mmio(sbd, &s->dac);

790
    /* 9/THC : Cursor */
791
    memory_region_init_io(&s->thc, obj, &tcx_thc_ops, s, "tcx.thc",
792 793
                          TCX_THC_NREGS);
    sysbus_init_mmio(sbd, &s->thc);
794

795
    /* 11/DHC : ??? */
796
    memory_region_init_io(&s->dhc, obj, &tcx_dummy_ops, s, "tcx.dhc",
797 798 799 800
                          TCX_DHC_NREGS);
    sysbus_init_mmio(sbd, &s->dhc);

    /* 12/ALT : ??? */
801
    memory_region_init_io(&s->alt, obj, &tcx_dummy_ops, s, "tcx.alt",
802 803
                          TCX_ALT_NREGS);
    sysbus_init_mmio(sbd, &s->alt);
804 805
}

806
static void tcx_realizefn(DeviceState *dev, Error **errp)
B
Blue Swirl 已提交
807
{
808
    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
A
Andreas Färber 已提交
809
    TCXState *s = TCX(dev);
A
Avi Kivity 已提交
810
    ram_addr_t vram_offset = 0;
811
    int size, ret;
812
    uint8_t *vram_base;
813
    char *fcode_filename;
814

815
    memory_region_init_ram(&s->vram_mem, OBJECT(s), "tcx.vram",
816
                           s->vram_size * (1 + 4 + 4), &error_fatal);
817
    vmstate_register_ram_global(&s->vram_mem);
818
    memory_region_set_log(&s->vram_mem, true, DIRTY_MEMORY_VGA);
A
Avi Kivity 已提交
819
    vram_base = memory_region_get_ram_ptr(&s->vram_mem);
B
blueswir1 已提交
820

821
    /* 10/ROM : FCode ROM */
822 823 824
    vmstate_register_ram_global(&s->rom);
    fcode_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, TCX_ROM_FILE);
    if (fcode_filename) {
825
        ret = load_image_mr(fcode_filename, &s->rom);
S
Shannon Zhao 已提交
826
        g_free(fcode_filename);
827
        if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) {
828
            error_report("tcx: could not load prom '%s'", TCX_ROM_FILE);
829 830 831
        }
    }

832
    /* 0/DFB8 : 8-bit plane */
B
blueswir1 已提交
833
    s->vram = vram_base;
G
Gerd Hoffmann 已提交
834
    size = s->vram_size;
835
    memory_region_init_alias(&s->vram_8bit, OBJECT(s), "tcx.vram.8bit",
A
Avi Kivity 已提交
836
                             &s->vram_mem, vram_offset, size);
837
    sysbus_init_mmio(sbd, &s->vram_8bit);
B
blueswir1 已提交
838 839
    vram_offset += size;
    vram_base += size;
B
bellard 已提交
840

841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
    /* 1/DFB24 : 24bit plane */
    size = s->vram_size * 4;
    s->vram24 = (uint32_t *)vram_base;
    s->vram24_offset = vram_offset;
    memory_region_init_alias(&s->vram_24bit, OBJECT(s), "tcx.vram.24bit",
                             &s->vram_mem, vram_offset, size);
    sysbus_init_mmio(sbd, &s->vram_24bit);
    vram_offset += size;
    vram_base += size;

    /* 4/RDFB32 : Raw Framebuffer */
    size = s->vram_size * 4;
    s->cplane = (uint32_t *)vram_base;
    s->cplane_offset = vram_offset;
    memory_region_init_alias(&s->vram_cplane, OBJECT(s), "tcx.vram.cplane",
                             &s->vram_mem, vram_offset, size);
    sysbus_init_mmio(sbd, &s->vram_cplane);
B
Blue Swirl 已提交
858

859 860 861 862 863 864 865 866
    /* 9/THC24bits : NetBSD writes here even with 8-bit display: dummy */
    if (s->depth == 8) {
        memory_region_init_io(&s->thc24, OBJECT(s), &tcx_dummy_ops, s,
                              "tcx.thc24", TCX_THC_NREGS);
        sysbus_init_mmio(sbd, &s->thc24);
    }

    sysbus_init_irq(sbd, &s->irq);
B
Blue Swirl 已提交
867

868
    if (s->depth == 8) {
869
        s->con = graphic_console_init(DEVICE(dev), 0, &tcx_ops, s);
870 871
    } else {
        s->con = graphic_console_init(DEVICE(dev), 0, &tcx24_ops, s);
B
blueswir1 已提交
872
    }
873
    s->thcmisc = 0;
B
bellard 已提交
874

875
    qemu_console_resize(s->con, s->width, s->height);
876 877
}

878
static Property tcx_properties[] = {
879
    DEFINE_PROP_UINT32("vram_size", TCXState, vram_size, -1),
880 881 882 883 884 885 886 887
    DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
    DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
    DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
    DEFINE_PROP_END_OF_LIST(),
};

static void tcx_class_init(ObjectClass *klass, void *data)
{
888
    DeviceClass *dc = DEVICE_CLASS(klass);
889

890
    dc->realize = tcx_realizefn;
891 892 893
    dc->reset = tcx_reset;
    dc->vmsd = &vmstate_tcx;
    dc->props = tcx_properties;
894 895
}

896
static const TypeInfo tcx_info = {
A
Andreas Färber 已提交
897
    .name          = TYPE_TCX,
898 899
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(TCXState),
900
    .instance_init = tcx_initfn,
901
    .class_init    = tcx_class_init,
G
Gerd Hoffmann 已提交
902 903
};

A
Andreas Färber 已提交
904
static void tcx_register_types(void)
B
Blue Swirl 已提交
905
{
906
    type_register_static(&tcx_info);
B
Blue Swirl 已提交
907 908
}

A
Andreas Färber 已提交
909
type_init(tcx_register_types)