Statistics
| Branch: | Revision:

root / hw / tcx.c @ c78f7137

History | View | Annotate | Download (20.8 kB)

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-common.h"
26
#include "ui/console.h"
27
#include "ui/pixel_ops.h"
28
#include "hw/sysbus.h"
29
#include "hw/qdev-addr.h"
30

    
31
#define MAXX 1024
32
#define MAXY 768
33
#define TCX_DAC_NREGS 16
34
#define TCX_THC_NREGS_8  0x081c
35
#define TCX_THC_NREGS_24 0x1000
36
#define TCX_TEC_NREGS    0x1000
37

    
38
typedef struct TCXState {
39
    SysBusDevice busdev;
40
    hwaddr addr;
41
    QemuConsole *con;
42
    uint8_t *vram;
43
    uint32_t *vram24, *cplane;
44
    MemoryRegion vram_mem;
45
    MemoryRegion vram_8bit;
46
    MemoryRegion vram_24bit;
47
    MemoryRegion vram_cplane;
48
    MemoryRegion dac;
49
    MemoryRegion tec;
50
    MemoryRegion thc24;
51
    MemoryRegion thc8;
52
    ram_addr_t vram24_offset, cplane_offset;
53
    uint32_t vram_size;
54
    uint32_t palette[256];
55
    uint8_t r[256], g[256], b[256];
56
    uint16_t width, height, depth;
57
    uint8_t dac_index, dac_state;
58
} TCXState;
59

    
60
static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch,
61
                            Error **errp);
62
static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch,
63
                            Error **errp);
64

    
65
static void tcx_set_dirty(TCXState *s)
66
{
67
    memory_region_set_dirty(&s->vram_mem, 0, MAXX * MAXY);
68
}
69

    
70
static void tcx24_set_dirty(TCXState *s)
71
{
72
    memory_region_set_dirty(&s->vram_mem, s->vram24_offset, MAXX * MAXY * 4);
73
    memory_region_set_dirty(&s->vram_mem, s->cplane_offset, MAXX * MAXY * 4);
74
}
75

    
76
static void update_palette_entries(TCXState *s, int start, int end)
77
{
78
    DisplaySurface *surface = qemu_console_surface(s->con);
79
    int i;
80

    
81
    for (i = start; i < end; i++) {
82
        switch (surface_bits_per_pixel(surface)) {
83
        default:
84
        case 8:
85
            s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
86
            break;
87
        case 15:
88
            s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
89
            break;
90
        case 16:
91
            s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
92
            break;
93
        case 32:
94
            if (is_surface_bgr(surface)) {
95
                s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
96
            } else {
97
                s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
98
            }
99
            break;
100
        }
101
    }
102
    if (s->depth == 24) {
103
        tcx24_set_dirty(s);
104
    } else {
105
        tcx_set_dirty(s);
106
    }
107
}
108

    
109
static void tcx_draw_line32(TCXState *s1, uint8_t *d,
110
                            const uint8_t *s, int width)
111
{
112
    int x;
113
    uint8_t val;
114
    uint32_t *p = (uint32_t *)d;
115

    
116
    for(x = 0; x < width; x++) {
117
        val = *s++;
118
        *p++ = s1->palette[val];
119
    }
120
}
121

    
122
static void tcx_draw_line16(TCXState *s1, uint8_t *d,
123
                            const uint8_t *s, int width)
124
{
125
    int x;
126
    uint8_t val;
127
    uint16_t *p = (uint16_t *)d;
128

    
129
    for(x = 0; x < width; x++) {
130
        val = *s++;
131
        *p++ = s1->palette[val];
132
    }
133
}
134

    
135
static void tcx_draw_line8(TCXState *s1, uint8_t *d,
136
                           const uint8_t *s, int width)
137
{
138
    int x;
139
    uint8_t val;
140

    
141
    for(x = 0; x < width; x++) {
142
        val = *s++;
143
        *d++ = s1->palette[val];
144
    }
145
}
146

    
147
/*
148
  XXX Could be much more optimal:
149
  * detect if line/page/whole screen is in 24 bit mode
150
  * if destination is also BGR, use memcpy
151
  */
152
static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
153
                                     const uint8_t *s, int width,
154
                                     const uint32_t *cplane,
155
                                     const uint32_t *s24)
156
{
157
    DisplaySurface *surface = qemu_console_surface(s1->con);
158
    int x, bgr, r, g, b;
159
    uint8_t val, *p8;
160
    uint32_t *p = (uint32_t *)d;
161
    uint32_t dval;
162

    
163
    bgr = is_surface_bgr(surface);
164
    for(x = 0; x < width; x++, s++, s24++) {
165
        if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
166
            // 24-bit direct, BGR order
167
            p8 = (uint8_t *)s24;
168
            p8++;
169
            b = *p8++;
170
            g = *p8++;
171
            r = *p8;
172
            if (bgr)
173
                dval = rgb_to_pixel32bgr(r, g, b);
174
            else
175
                dval = rgb_to_pixel32(r, g, b);
176
        } else {
177
            val = *s;
178
            dval = s1->palette[val];
179
        }
180
        *p++ = dval;
181
    }
182
}
183

    
184
static inline int check_dirty(TCXState *s, ram_addr_t page, ram_addr_t page24,
185
                              ram_addr_t cpage)
186
{
187
    int ret;
188

    
189
    ret = memory_region_get_dirty(&s->vram_mem, page, TARGET_PAGE_SIZE,
190
                                  DIRTY_MEMORY_VGA);
191
    ret |= memory_region_get_dirty(&s->vram_mem, page24, TARGET_PAGE_SIZE * 4,
192
                                   DIRTY_MEMORY_VGA);
193
    ret |= memory_region_get_dirty(&s->vram_mem, cpage, TARGET_PAGE_SIZE * 4,
194
                                   DIRTY_MEMORY_VGA);
195
    return ret;
196
}
197

    
198
static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
199
                               ram_addr_t page_max, ram_addr_t page24,
200
                              ram_addr_t cpage)
201
{
202
    memory_region_reset_dirty(&ts->vram_mem,
203
                              page_min, page_max + TARGET_PAGE_SIZE,
204
                              DIRTY_MEMORY_VGA);
205
    memory_region_reset_dirty(&ts->vram_mem,
206
                              page24 + page_min * 4,
207
                              page24 + page_max * 4 + TARGET_PAGE_SIZE,
208
                              DIRTY_MEMORY_VGA);
209
    memory_region_reset_dirty(&ts->vram_mem,
210
                              cpage + page_min * 4,
211
                              cpage + page_max * 4 + TARGET_PAGE_SIZE,
212
                              DIRTY_MEMORY_VGA);
213
}
214

    
215
/* Fixed line length 1024 allows us to do nice tricks not possible on
216
   VGA... */
217
static void tcx_update_display(void *opaque)
218
{
219
    TCXState *ts = opaque;
220
    DisplaySurface *surface = qemu_console_surface(ts->con);
221
    ram_addr_t page, page_min, page_max;
222
    int y, y_start, dd, ds;
223
    uint8_t *d, *s;
224
    void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
225

    
226
    if (surface_bits_per_pixel(surface) == 0) {
227
        return;
228
    }
229

    
230
    page = 0;
231
    y_start = -1;
232
    page_min = -1;
233
    page_max = 0;
234
    d = surface_data(surface);
235
    s = ts->vram;
236
    dd = surface_stride(surface);
237
    ds = 1024;
238

    
239
    switch (surface_bits_per_pixel(surface)) {
240
    case 32:
241
        f = tcx_draw_line32;
242
        break;
243
    case 15:
244
    case 16:
245
        f = tcx_draw_line16;
246
        break;
247
    default:
248
    case 8:
249
        f = tcx_draw_line8;
250
        break;
251
    case 0:
252
        return;
253
    }
254

    
255
    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
256
        if (memory_region_get_dirty(&ts->vram_mem, page, TARGET_PAGE_SIZE,
257
                                    DIRTY_MEMORY_VGA)) {
258
            if (y_start < 0)
259
                y_start = y;
260
            if (page < page_min)
261
                page_min = page;
262
            if (page > page_max)
263
                page_max = page;
264
            f(ts, d, s, ts->width);
265
            d += dd;
266
            s += ds;
267
            f(ts, d, s, ts->width);
268
            d += dd;
269
            s += ds;
270
            f(ts, d, s, ts->width);
271
            d += dd;
272
            s += ds;
273
            f(ts, d, s, ts->width);
274
            d += dd;
275
            s += ds;
276
        } else {
277
            if (y_start >= 0) {
278
                /* flush to display */
279
                dpy_gfx_update(ts->con, 0, y_start,
280
                               ts->width, y - y_start);
281
                y_start = -1;
282
            }
283
            d += dd * 4;
284
            s += ds * 4;
285
        }
286
    }
287
    if (y_start >= 0) {
288
        /* flush to display */
289
        dpy_gfx_update(ts->con, 0, y_start,
290
                       ts->width, y - y_start);
291
    }
292
    /* reset modified pages */
293
    if (page_max >= page_min) {
294
        memory_region_reset_dirty(&ts->vram_mem,
295
                                  page_min, page_max + TARGET_PAGE_SIZE,
296
                                  DIRTY_MEMORY_VGA);
297
    }
298
}
299

    
300
static void tcx24_update_display(void *opaque)
301
{
302
    TCXState *ts = opaque;
303
    DisplaySurface *surface = qemu_console_surface(ts->con);
304
    ram_addr_t page, page_min, page_max, cpage, page24;
305
    int y, y_start, dd, ds;
306
    uint8_t *d, *s;
307
    uint32_t *cptr, *s24;
308

    
309
    if (surface_bits_per_pixel(surface) != 32) {
310
            return;
311
    }
312

    
313
    page = 0;
314
    page24 = ts->vram24_offset;
315
    cpage = ts->cplane_offset;
316
    y_start = -1;
317
    page_min = -1;
318
    page_max = 0;
319
    d = surface_data(surface);
320
    s = ts->vram;
321
    s24 = ts->vram24;
322
    cptr = ts->cplane;
323
    dd = surface_stride(surface);
324
    ds = 1024;
325

    
326
    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
327
            page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
328
        if (check_dirty(ts, page, page24, cpage)) {
329
            if (y_start < 0)
330
                y_start = y;
331
            if (page < page_min)
332
                page_min = page;
333
            if (page > page_max)
334
                page_max = page;
335
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
336
            d += dd;
337
            s += ds;
338
            cptr += ds;
339
            s24 += ds;
340
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
341
            d += dd;
342
            s += ds;
343
            cptr += ds;
344
            s24 += ds;
345
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
346
            d += dd;
347
            s += ds;
348
            cptr += ds;
349
            s24 += ds;
350
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
351
            d += dd;
352
            s += ds;
353
            cptr += ds;
354
            s24 += ds;
355
        } else {
356
            if (y_start >= 0) {
357
                /* flush to display */
358
                dpy_gfx_update(ts->con, 0, y_start,
359
                               ts->width, y - y_start);
360
                y_start = -1;
361
            }
362
            d += dd * 4;
363
            s += ds * 4;
364
            cptr += ds * 4;
365
            s24 += ds * 4;
366
        }
367
    }
368
    if (y_start >= 0) {
369
        /* flush to display */
370
        dpy_gfx_update(ts->con, 0, y_start,
371
                       ts->width, y - y_start);
372
    }
373
    /* reset modified pages */
374
    if (page_max >= page_min) {
375
        reset_dirty(ts, page_min, page_max, page24, cpage);
376
    }
377
}
378

    
379
static void tcx_invalidate_display(void *opaque)
380
{
381
    TCXState *s = opaque;
382

    
383
    tcx_set_dirty(s);
384
    qemu_console_resize(s->con, s->width, s->height);
385
}
386

    
387
static void tcx24_invalidate_display(void *opaque)
388
{
389
    TCXState *s = opaque;
390

    
391
    tcx_set_dirty(s);
392
    tcx24_set_dirty(s);
393
    qemu_console_resize(s->con, s->width, s->height);
394
}
395

    
396
static int vmstate_tcx_post_load(void *opaque, int version_id)
397
{
398
    TCXState *s = opaque;
399

    
400
    update_palette_entries(s, 0, 256);
401
    if (s->depth == 24) {
402
        tcx24_set_dirty(s);
403
    } else {
404
        tcx_set_dirty(s);
405
    }
406

    
407
    return 0;
408
}
409

    
410
static const VMStateDescription vmstate_tcx = {
411
    .name ="tcx",
412
    .version_id = 4,
413
    .minimum_version_id = 4,
414
    .minimum_version_id_old = 4,
415
    .post_load = vmstate_tcx_post_load,
416
    .fields      = (VMStateField []) {
417
        VMSTATE_UINT16(height, TCXState),
418
        VMSTATE_UINT16(width, TCXState),
419
        VMSTATE_UINT16(depth, TCXState),
420
        VMSTATE_BUFFER(r, TCXState),
421
        VMSTATE_BUFFER(g, TCXState),
422
        VMSTATE_BUFFER(b, TCXState),
423
        VMSTATE_UINT8(dac_index, TCXState),
424
        VMSTATE_UINT8(dac_state, TCXState),
425
        VMSTATE_END_OF_LIST()
426
    }
427
};
428

    
429
static void tcx_reset(DeviceState *d)
430
{
431
    TCXState *s = container_of(d, TCXState, busdev.qdev);
432

    
433
    /* Initialize palette */
434
    memset(s->r, 0, 256);
435
    memset(s->g, 0, 256);
436
    memset(s->b, 0, 256);
437
    s->r[255] = s->g[255] = s->b[255] = 255;
438
    update_palette_entries(s, 0, 256);
439
    memset(s->vram, 0, MAXX*MAXY);
440
    memory_region_reset_dirty(&s->vram_mem, 0, MAXX * MAXY * (1 + 4 + 4),
441
                              DIRTY_MEMORY_VGA);
442
    s->dac_index = 0;
443
    s->dac_state = 0;
444
}
445

    
446
static uint64_t tcx_dac_readl(void *opaque, hwaddr addr,
447
                              unsigned size)
448
{
449
    return 0;
450
}
451

    
452
static void tcx_dac_writel(void *opaque, hwaddr addr, uint64_t val,
453
                           unsigned size)
454
{
455
    TCXState *s = opaque;
456

    
457
    switch (addr) {
458
    case 0:
459
        s->dac_index = val >> 24;
460
        s->dac_state = 0;
461
        break;
462
    case 4:
463
        switch (s->dac_state) {
464
        case 0:
465
            s->r[s->dac_index] = val >> 24;
466
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
467
            s->dac_state++;
468
            break;
469
        case 1:
470
            s->g[s->dac_index] = val >> 24;
471
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
472
            s->dac_state++;
473
            break;
474
        case 2:
475
            s->b[s->dac_index] = val >> 24;
476
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
477
            s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
478
        default:
479
            s->dac_state = 0;
480
            break;
481
        }
482
        break;
483
    default:
484
        break;
485
    }
486
}
487

    
488
static const MemoryRegionOps tcx_dac_ops = {
489
    .read = tcx_dac_readl,
490
    .write = tcx_dac_writel,
491
    .endianness = DEVICE_NATIVE_ENDIAN,
492
    .valid = {
493
        .min_access_size = 4,
494
        .max_access_size = 4,
495
    },
496
};
497

    
498
static uint64_t dummy_readl(void *opaque, hwaddr addr,
499
                            unsigned size)
500
{
501
    return 0;
502
}
503

    
504
static void dummy_writel(void *opaque, hwaddr addr,
505
                         uint64_t val, unsigned size)
506
{
507
}
508

    
509
static const MemoryRegionOps dummy_ops = {
510
    .read = dummy_readl,
511
    .write = dummy_writel,
512
    .endianness = DEVICE_NATIVE_ENDIAN,
513
    .valid = {
514
        .min_access_size = 4,
515
        .max_access_size = 4,
516
    },
517
};
518

    
519
static int tcx_init1(SysBusDevice *dev)
520
{
521
    TCXState *s = FROM_SYSBUS(TCXState, dev);
522
    ram_addr_t vram_offset = 0;
523
    int size;
524
    uint8_t *vram_base;
525

    
526
    memory_region_init_ram(&s->vram_mem, "tcx.vram",
527
                           s->vram_size * (1 + 4 + 4));
528
    vmstate_register_ram_global(&s->vram_mem);
529
    vram_base = memory_region_get_ram_ptr(&s->vram_mem);
530

    
531
    /* 8-bit plane */
532
    s->vram = vram_base;
533
    size = s->vram_size;
534
    memory_region_init_alias(&s->vram_8bit, "tcx.vram.8bit",
535
                             &s->vram_mem, vram_offset, size);
536
    sysbus_init_mmio(dev, &s->vram_8bit);
537
    vram_offset += size;
538
    vram_base += size;
539

    
540
    /* DAC */
541
    memory_region_init_io(&s->dac, &tcx_dac_ops, s, "tcx.dac", TCX_DAC_NREGS);
542
    sysbus_init_mmio(dev, &s->dac);
543

    
544
    /* TEC (dummy) */
545
    memory_region_init_io(&s->tec, &dummy_ops, s, "tcx.tec", TCX_TEC_NREGS);
546
    sysbus_init_mmio(dev, &s->tec);
547
    /* THC: NetBSD writes here even with 8-bit display: dummy */
548
    memory_region_init_io(&s->thc24, &dummy_ops, s, "tcx.thc24",
549
                          TCX_THC_NREGS_24);
550
    sysbus_init_mmio(dev, &s->thc24);
551

    
552
    if (s->depth == 24) {
553
        /* 24-bit plane */
554
        size = s->vram_size * 4;
555
        s->vram24 = (uint32_t *)vram_base;
556
        s->vram24_offset = vram_offset;
557
        memory_region_init_alias(&s->vram_24bit, "tcx.vram.24bit",
558
                                 &s->vram_mem, vram_offset, size);
559
        sysbus_init_mmio(dev, &s->vram_24bit);
560
        vram_offset += size;
561
        vram_base += size;
562

    
563
        /* Control plane */
564
        size = s->vram_size * 4;
565
        s->cplane = (uint32_t *)vram_base;
566
        s->cplane_offset = vram_offset;
567
        memory_region_init_alias(&s->vram_cplane, "tcx.vram.cplane",
568
                                 &s->vram_mem, vram_offset, size);
569
        sysbus_init_mmio(dev, &s->vram_cplane);
570

    
571
        s->con = graphic_console_init(tcx24_update_display,
572
                                      tcx24_invalidate_display,
573
                                      tcx24_screen_dump, NULL, s);
574
    } else {
575
        /* THC 8 bit (dummy) */
576
        memory_region_init_io(&s->thc8, &dummy_ops, s, "tcx.thc8",
577
                              TCX_THC_NREGS_8);
578
        sysbus_init_mmio(dev, &s->thc8);
579

    
580
        s->con = graphic_console_init(tcx_update_display,
581
                                      tcx_invalidate_display,
582
                                      tcx_screen_dump, NULL, s);
583
    }
584

    
585
    qemu_console_resize(s->con, s->width, s->height);
586
    return 0;
587
}
588

    
589
static void tcx_screen_dump(void *opaque, const char *filename, bool cswitch,
590
                            Error **errp)
591
{
592
    TCXState *s = opaque;
593
    FILE *f;
594
    uint8_t *d, *d1, v;
595
    int ret, y, x;
596

    
597
    f = fopen(filename, "wb");
598
    if (!f) {
599
        error_setg(errp, "failed to open file '%s': %s", filename,
600
                   strerror(errno));
601
        return;
602
    }
603
    ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
604
    if (ret < 0) {
605
        goto write_err;
606
    }
607
    d1 = s->vram;
608
    for(y = 0; y < s->height; y++) {
609
        d = d1;
610
        for(x = 0; x < s->width; x++) {
611
            v = *d;
612
            ret = fputc(s->r[v], f);
613
            if (ret == EOF) {
614
                goto write_err;
615
            }
616
            ret = fputc(s->g[v], f);
617
            if (ret == EOF) {
618
                goto write_err;
619
            }
620
            ret = fputc(s->b[v], f);
621
            if (ret == EOF) {
622
                goto write_err;
623
            }
624
            d++;
625
        }
626
        d1 += MAXX;
627
    }
628

    
629
out:
630
    fclose(f);
631
    return;
632

    
633
write_err:
634
    error_setg(errp, "failed to write to file '%s': %s", filename,
635
               strerror(errno));
636
    unlink(filename);
637
    goto out;
638
}
639

    
640
static void tcx24_screen_dump(void *opaque, const char *filename, bool cswitch,
641
                              Error **errp)
642
{
643
    TCXState *s = opaque;
644
    FILE *f;
645
    uint8_t *d, *d1, v;
646
    uint32_t *s24, *cptr, dval;
647
    int ret, y, x;
648

    
649
    f = fopen(filename, "wb");
650
    if (!f) {
651
        error_setg(errp, "failed to open file '%s': %s", filename,
652
                   strerror(errno));
653
        return;
654
    }
655
    ret = fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
656
    if (ret < 0) {
657
        goto write_err;
658
    }
659
    d1 = s->vram;
660
    s24 = s->vram24;
661
    cptr = s->cplane;
662
    for(y = 0; y < s->height; y++) {
663
        d = d1;
664
        for(x = 0; x < s->width; x++, d++, s24++) {
665
            if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
666
                dval = *s24 & 0x00ffffff;
667
                ret = fputc((dval >> 16) & 0xff, f);
668
                if (ret == EOF) {
669
                    goto write_err;
670
                }
671
                ret = fputc((dval >> 8) & 0xff, f);
672
                if (ret == EOF) {
673
                    goto write_err;
674
                }
675
                ret = fputc(dval & 0xff, f);
676
                if (ret == EOF) {
677
                    goto write_err;
678
                }
679
            } else {
680
                v = *d;
681
                ret = fputc(s->r[v], f);
682
                if (ret == EOF) {
683
                    goto write_err;
684
                }
685
                ret = fputc(s->g[v], f);
686
                if (ret == EOF) {
687
                    goto write_err;
688
                }
689
                ret = fputc(s->b[v], f);
690
                if (ret == EOF) {
691
                    goto write_err;
692
                }
693
            }
694
        }
695
        d1 += MAXX;
696
    }
697

    
698
out:
699
    fclose(f);
700
    return;
701

    
702
write_err:
703
    error_setg(errp, "failed to write to file '%s': %s", filename,
704
               strerror(errno));
705
    unlink(filename);
706
    goto out;
707
}
708

    
709
static Property tcx_properties[] = {
710
    DEFINE_PROP_TADDR("addr",      TCXState, addr,      -1),
711
    DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
712
    DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
713
    DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
714
    DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
715
    DEFINE_PROP_END_OF_LIST(),
716
};
717

    
718
static void tcx_class_init(ObjectClass *klass, void *data)
719
{
720
    DeviceClass *dc = DEVICE_CLASS(klass);
721
    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
722

    
723
    k->init = tcx_init1;
724
    dc->reset = tcx_reset;
725
    dc->vmsd = &vmstate_tcx;
726
    dc->props = tcx_properties;
727
}
728

    
729
static const TypeInfo tcx_info = {
730
    .name          = "SUNW,tcx",
731
    .parent        = TYPE_SYS_BUS_DEVICE,
732
    .instance_size = sizeof(TCXState),
733
    .class_init    = tcx_class_init,
734
};
735

    
736
static void tcx_register_types(void)
737
{
738
    type_register_static(&tcx_info);
739
}
740

    
741
type_init(tcx_register_types)