Statistics
| Branch: | Revision:

root / hw / tcx.c @ ff753bb9

History | View | Annotate | Download (17.7 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 "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

    
37
typedef 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

    
51
static void tcx_screen_dump(void *opaque, const char *filename);
52
static void tcx24_screen_dump(void *opaque, const char *filename);
53

    
54
static 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

    
63
static 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

    
73
static 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

    
103
static 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

    
116
static 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

    
129
static 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
  */
146
static 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

    
177
static 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

    
191
static 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... */
209
static 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

    
287
static 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

    
363
static 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

    
371
static 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

    
380
static 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

    
394
static 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

    
413
static 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

    
430
static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
431
{
432
    return 0;
433
}
434

    
435
static 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

    
471
static CPUReadMemoryFunc * const tcx_dac_read[3] = {
472
    NULL,
473
    NULL,
474
    tcx_dac_readl,
475
};
476

    
477
static CPUWriteMemoryFunc * const tcx_dac_write[3] = {
478
    NULL,
479
    NULL,
480
    tcx_dac_writel,
481
};
482

    
483
static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
484
{
485
    return 0;
486
}
487

    
488
static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
489
                             uint32_t val)
490
{
491
}
492

    
493
static CPUReadMemoryFunc * const tcx_dummy_read[3] = {
494
    NULL,
495
    NULL,
496
    tcx_dummy_readl,
497
};
498

    
499
static CPUWriteMemoryFunc * const tcx_dummy_write[3] = {
500
    NULL,
501
    NULL,
502
    tcx_dummy_writel,
503
};
504

    
505
static 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
    sysbus_init_mmio(dev, TCX_DAC_NREGS, io_memory);
527

    
528
    /* TEC (dummy) */
529
    dummy_memory = cpu_register_io_memory(tcx_dummy_read, tcx_dummy_write,
530
                                          s);
531
    sysbus_init_mmio(dev, TCX_TEC_NREGS, dummy_memory);
532
    /* THC: NetBSD writes here even with 8-bit display: dummy */
533
    sysbus_init_mmio(dev, TCX_THC_NREGS_24, dummy_memory);
534

    
535
    if (s->depth == 24) {
536
        /* 24-bit plane */
537
        size = s->vram_size * 4;
538
        s->vram24 = (uint32_t *)vram_base;
539
        s->vram24_offset = vram_offset;
540
        sysbus_init_mmio(dev, size, vram_offset);
541
        vram_offset += size;
542
        vram_base += size;
543

    
544
        /* Control plane */
545
        size = s->vram_size * 4;
546
        s->cplane = (uint32_t *)vram_base;
547
        s->cplane_offset = vram_offset;
548
        sysbus_init_mmio(dev, size, vram_offset);
549

    
550
        s->ds = graphic_console_init(tcx24_update_display,
551
                                     tcx24_invalidate_display,
552
                                     tcx24_screen_dump, NULL, s);
553
    } else {
554
        /* THC 8 bit (dummy) */
555
        sysbus_init_mmio(dev, TCX_THC_NREGS_8, dummy_memory);
556

    
557
        s->ds = graphic_console_init(tcx_update_display,
558
                                     tcx_invalidate_display,
559
                                     tcx_screen_dump, NULL, s);
560
    }
561

    
562
    qemu_console_resize(s->ds, s->width, s->height);
563
    return 0;
564
}
565

    
566
static void tcx_screen_dump(void *opaque, const char *filename)
567
{
568
    TCXState *s = opaque;
569
    FILE *f;
570
    uint8_t *d, *d1, v;
571
    int y, x;
572

    
573
    f = fopen(filename, "wb");
574
    if (!f)
575
        return;
576
    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
577
    d1 = s->vram;
578
    for(y = 0; y < s->height; y++) {
579
        d = d1;
580
        for(x = 0; x < s->width; x++) {
581
            v = *d;
582
            fputc(s->r[v], f);
583
            fputc(s->g[v], f);
584
            fputc(s->b[v], f);
585
            d++;
586
        }
587
        d1 += MAXX;
588
    }
589
    fclose(f);
590
    return;
591
}
592

    
593
static void tcx24_screen_dump(void *opaque, const char *filename)
594
{
595
    TCXState *s = opaque;
596
    FILE *f;
597
    uint8_t *d, *d1, v;
598
    uint32_t *s24, *cptr, dval;
599
    int y, x;
600

    
601
    f = fopen(filename, "wb");
602
    if (!f)
603
        return;
604
    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
605
    d1 = s->vram;
606
    s24 = s->vram24;
607
    cptr = s->cplane;
608
    for(y = 0; y < s->height; y++) {
609
        d = d1;
610
        for(x = 0; x < s->width; x++, d++, s24++) {
611
            if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
612
                dval = *s24 & 0x00ffffff;
613
                fputc((dval >> 16) & 0xff, f);
614
                fputc((dval >> 8) & 0xff, f);
615
                fputc(dval & 0xff, f);
616
            } else {
617
                v = *d;
618
                fputc(s->r[v], f);
619
                fputc(s->g[v], f);
620
                fputc(s->b[v], f);
621
            }
622
        }
623
        d1 += MAXX;
624
    }
625
    fclose(f);
626
    return;
627
}
628

    
629
static SysBusDeviceInfo tcx_info = {
630
    .init = tcx_init1,
631
    .qdev.name  = "SUNW,tcx",
632
    .qdev.size  = sizeof(TCXState),
633
    .qdev.reset = tcx_reset,
634
    .qdev.vmsd  = &vmstate_tcx,
635
    .qdev.props = (Property[]) {
636
        DEFINE_PROP_TADDR("addr",      TCXState, addr,      -1),
637
        DEFINE_PROP_HEX32("vram_size", TCXState, vram_size, -1),
638
        DEFINE_PROP_UINT16("width",    TCXState, width,     -1),
639
        DEFINE_PROP_UINT16("height",   TCXState, height,    -1),
640
        DEFINE_PROP_UINT16("depth",    TCXState, depth,     -1),
641
        DEFINE_PROP_END_OF_LIST(),
642
    }
643
};
644

    
645
static void tcx_register_devices(void)
646
{
647
    sysbus_register_withprop(&tcx_info);
648
}
649

    
650
device_init(tcx_register_devices)