Statistics
| Branch: | Revision:

root / hw / tcx.c @ 5fafdf24

History | View | Annotate | Download (16.9 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
#include "vl.h"
25
#include "pixel_ops.h"
26

    
27
#define MAXX 1024
28
#define MAXY 768
29
#define TCX_DAC_NREGS 16
30
#define TCX_THC_NREGS_8  0x081c
31
#define TCX_THC_NREGS_24 0x1000
32
#define TCX_TEC_NREGS    0x1000
33

    
34
typedef struct TCXState {
35
    target_phys_addr_t addr;
36
    DisplayState *ds;
37
    uint8_t *vram;
38
    uint32_t *vram24, *cplane;
39
    ram_addr_t vram_offset, vram24_offset, cplane_offset;
40
    uint16_t width, height, depth;
41
    uint8_t r[256], g[256], b[256];
42
    uint32_t palette[256];
43
    uint8_t dac_index, dac_state;
44
} TCXState;
45

    
46
static void tcx_screen_dump(void *opaque, const char *filename);
47
static void tcx24_screen_dump(void *opaque, const char *filename);
48
static void tcx_invalidate_display(void *opaque);
49
static void tcx24_invalidate_display(void *opaque);
50

    
51
static void update_palette_entries(TCXState *s, int start, int end)
52
{
53
    int i;
54
    for(i = start; i < end; i++) {
55
        switch(s->ds->depth) {
56
        default:
57
        case 8:
58
            s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
59
            break;
60
        case 15:
61
            if (s->ds->bgr)
62
                s->palette[i] = rgb_to_pixel15bgr(s->r[i], s->g[i], s->b[i]);
63
            else
64
                s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
65
            break;
66
        case 16:
67
            if (s->ds->bgr)
68
                s->palette[i] = rgb_to_pixel16bgr(s->r[i], s->g[i], s->b[i]);
69
            else
70
                s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
71
            break;
72
        case 32:
73
            if (s->ds->bgr)
74
                s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
75
            else
76
                s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
77
            break;
78
        }
79
    }
80
    if (s->depth == 24)
81
        tcx24_invalidate_display(s);
82
    else
83
        tcx_invalidate_display(s);
84
}
85

    
86
static void tcx_draw_line32(TCXState *s1, uint8_t *d,
87
                            const uint8_t *s, int width)
88
{
89
    int x;
90
    uint8_t val;
91
    uint32_t *p = (uint32_t *)d;
92

    
93
    for(x = 0; x < width; x++) {
94
        val = *s++;
95
        *p++ = s1->palette[val];
96
    }
97
}
98

    
99
static void tcx_draw_line16(TCXState *s1, uint8_t *d,
100
                            const uint8_t *s, int width)
101
{
102
    int x;
103
    uint8_t val;
104
    uint16_t *p = (uint16_t *)d;
105

    
106
    for(x = 0; x < width; x++) {
107
        val = *s++;
108
        *p++ = s1->palette[val];
109
    }
110
}
111

    
112
static void tcx_draw_line8(TCXState *s1, uint8_t *d,
113
                           const uint8_t *s, int width)
114
{
115
    int x;
116
    uint8_t val;
117

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

    
124
static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
125
                                     const uint8_t *s, int width,
126
                                     const uint32_t *cplane,
127
                                     const uint32_t *s24)
128
{
129
    int x;
130
    uint8_t val;
131
    uint32_t *p = (uint32_t *)d;
132
    uint32_t dval;
133

    
134
    for(x = 0; x < width; x++, s++, s24++) {
135
        if ((bswap32(*cplane++) & 0xff000000) == 0x03000000) { // 24-bit direct
136
            dval = bswap32(*s24) & 0x00ffffff;
137
        } else {
138
            val = *s;
139
            dval = s1->palette[val];
140
        }
141
        *p++ = dval;
142
    }
143
}
144

    
145
static inline int check_dirty(TCXState *ts, ram_addr_t page, ram_addr_t page24,
146
                              ram_addr_t cpage)
147
{
148
    int ret;
149
    unsigned int off;
150

    
151
    ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
152
    for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
153
        ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
154
        ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
155
    }
156
    return ret;
157
}
158

    
159
static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
160
                               ram_addr_t page_max, ram_addr_t page24,
161
                              ram_addr_t cpage)
162
{
163
    cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
164
                                    VGA_DIRTY_FLAG);
165
    page_min -= ts->vram_offset;
166
    page_max -= ts->vram_offset;
167
    cpu_physical_memory_reset_dirty(page24 + page_min * 4,
168
                                    page24 + page_max * 4 + TARGET_PAGE_SIZE,
169
                                    VGA_DIRTY_FLAG);
170
    cpu_physical_memory_reset_dirty(cpage + page_min * 4,
171
                                    cpage + page_max * 4 + TARGET_PAGE_SIZE,
172
                                    VGA_DIRTY_FLAG);
173
}
174

    
175
/* Fixed line length 1024 allows us to do nice tricks not possible on
176
   VGA... */
177
static void tcx_update_display(void *opaque)
178
{
179
    TCXState *ts = opaque;
180
    ram_addr_t page, page_min, page_max;
181
    int y, y_start, dd, ds;
182
    uint8_t *d, *s;
183
    void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
184

    
185
    if (ts->ds->depth == 0)
186
        return;
187
    page = ts->vram_offset;
188
    y_start = -1;
189
    page_min = 0xffffffff;
190
    page_max = 0;
191
    d = ts->ds->data;
192
    s = ts->vram;
193
    dd = ts->ds->linesize;
194
    ds = 1024;
195

    
196
    switch (ts->ds->depth) {
197
    case 32:
198
        f = tcx_draw_line32;
199
        break;
200
    case 15:
201
    case 16:
202
        f = tcx_draw_line16;
203
        break;
204
    default:
205
    case 8:
206
        f = tcx_draw_line8;
207
        break;
208
    case 0:
209
        return;
210
    }
211
   
212
    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
213
        if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
214
            if (y_start < 0)
215
                y_start = y;
216
            if (page < page_min)
217
                page_min = page;
218
            if (page > page_max)
219
                page_max = page;
220
            f(ts, d, s, ts->width);
221
            d += dd;
222
            s += ds;
223
            f(ts, d, s, ts->width);
224
            d += dd;
225
            s += ds;
226
            f(ts, d, s, ts->width);
227
            d += dd;
228
            s += ds;
229
            f(ts, d, s, ts->width);
230
            d += dd;
231
            s += ds;
232
        } else {
233
            if (y_start >= 0) {
234
                /* flush to display */
235
                dpy_update(ts->ds, 0, y_start,
236
                           ts->width, y - y_start);
237
                y_start = -1;
238
            }
239
            d += dd * 4;
240
            s += ds * 4;
241
        }
242
    }
243
    if (y_start >= 0) {
244
        /* flush to display */
245
        dpy_update(ts->ds, 0, y_start,
246
                   ts->width, y - y_start);
247
    }
248
    /* reset modified pages */
249
    if (page_min <= page_max) {
250
        cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
251
                                        VGA_DIRTY_FLAG);
252
    }
253
}
254

    
255
static void tcx24_update_display(void *opaque)
256
{
257
    TCXState *ts = opaque;
258
    ram_addr_t page, page_min, page_max, cpage, page24;
259
    int y, y_start, dd, ds;
260
    uint8_t *d, *s;
261
    uint32_t *cptr, *s24;
262

    
263
    if (ts->ds->depth != 32)
264
            return;
265
    page = ts->vram_offset;
266
    page24 = ts->vram24_offset;
267
    cpage = ts->cplane_offset;
268
    y_start = -1;
269
    page_min = 0xffffffff;
270
    page_max = 0;
271
    d = ts->ds->data;
272
    s = ts->vram;
273
    s24 = ts->vram24;
274
    cptr = ts->cplane;
275
    dd = ts->ds->linesize;
276
    ds = 1024;
277

    
278
    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
279
            page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
280
        if (check_dirty(ts, page, page24, cpage)) {
281
            if (y_start < 0)
282
                y_start = y;
283
            if (page < page_min)
284
                page_min = page;
285
            if (page > page_max)
286
                page_max = page;
287
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
288
            d += dd;
289
            s += ds;
290
            cptr += ds;
291
            s24 += ds;
292
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
293
            d += dd;
294
            s += ds;
295
            cptr += ds;
296
            s24 += ds;
297
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
298
            d += dd;
299
            s += ds;
300
            cptr += ds;
301
            s24 += ds;
302
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
303
            d += dd;
304
            s += ds;
305
            cptr += ds;
306
            s24 += ds;
307
        } else {
308
            if (y_start >= 0) {
309
                /* flush to display */
310
                dpy_update(ts->ds, 0, y_start,
311
                           ts->width, y - y_start);
312
                y_start = -1;
313
            }
314
            d += dd * 4;
315
            s += ds * 4;
316
            cptr += ds * 4;
317
            s24 += ds * 4;
318
        }
319
    }
320
    if (y_start >= 0) {
321
        /* flush to display */
322
        dpy_update(ts->ds, 0, y_start,
323
                   ts->width, y - y_start);
324
    }
325
    /* reset modified pages */
326
    if (page_min <= page_max) {
327
        reset_dirty(ts, page_min, page_max, page24, cpage);
328
    }
329
}
330

    
331
static void tcx_invalidate_display(void *opaque)
332
{
333
    TCXState *s = opaque;
334
    int i;
335

    
336
    for (i = 0; i < MAXX*MAXY; i += TARGET_PAGE_SIZE) {
337
        cpu_physical_memory_set_dirty(s->vram_offset + i);
338
    }
339
}
340

    
341
static void tcx24_invalidate_display(void *opaque)
342
{
343
    TCXState *s = opaque;
344
    int i;
345

    
346
    tcx_invalidate_display(s);
347
    for (i = 0; i < MAXX*MAXY * 4; i += TARGET_PAGE_SIZE) {
348
        cpu_physical_memory_set_dirty(s->vram24_offset + i);
349
        cpu_physical_memory_set_dirty(s->cplane_offset + i);
350
    }
351
}
352

    
353
static void tcx_save(QEMUFile *f, void *opaque)
354
{
355
    TCXState *s = opaque;
356
   
357
    qemu_put_be16s(f, (uint16_t *)&s->height);
358
    qemu_put_be16s(f, (uint16_t *)&s->width);
359
    qemu_put_be16s(f, (uint16_t *)&s->depth);
360
    qemu_put_buffer(f, s->r, 256);
361
    qemu_put_buffer(f, s->g, 256);
362
    qemu_put_buffer(f, s->b, 256);
363
    qemu_put_8s(f, &s->dac_index);
364
    qemu_put_8s(f, &s->dac_state);
365
}
366

    
367
static int tcx_load(QEMUFile *f, void *opaque, int version_id)
368
{
369
    TCXState *s = opaque;
370
    uint32_t dummy;
371

    
372
    if (version_id != 3 && version_id != 4)
373
        return -EINVAL;
374

    
375
    if (version_id == 3) {
376
        qemu_get_be32s(f, (uint32_t *)&dummy);
377
        qemu_get_be32s(f, (uint32_t *)&dummy);
378
        qemu_get_be32s(f, (uint32_t *)&dummy);
379
    }
380
    qemu_get_be16s(f, (uint16_t *)&s->height);
381
    qemu_get_be16s(f, (uint16_t *)&s->width);
382
    qemu_get_be16s(f, (uint16_t *)&s->depth);
383
    qemu_get_buffer(f, s->r, 256);
384
    qemu_get_buffer(f, s->g, 256);
385
    qemu_get_buffer(f, s->b, 256);
386
    qemu_get_8s(f, &s->dac_index);
387
    qemu_get_8s(f, &s->dac_state);
388
    update_palette_entries(s, 0, 256);
389
    if (s->depth == 24)
390
        tcx24_invalidate_display(s);
391
    else
392
        tcx_invalidate_display(s);
393

    
394
    return 0;
395
}
396

    
397
static void tcx_reset(void *opaque)
398
{
399
    TCXState *s = opaque;
400

    
401
    /* Initialize palette */
402
    memset(s->r, 0, 256);
403
    memset(s->g, 0, 256);
404
    memset(s->b, 0, 256);
405
    s->r[255] = s->g[255] = s->b[255] = 255;
406
    update_palette_entries(s, 0, 256);
407
    memset(s->vram, 0, MAXX*MAXY);
408
    cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
409
                                    MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
410
    s->dac_index = 0;
411
    s->dac_state = 0;
412
}
413

    
414
static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
415
{
416
    return 0;
417
}
418

    
419
static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
420
{
421
    TCXState *s = opaque;
422
    uint32_t saddr;
423

    
424
    saddr = (addr & (TCX_DAC_NREGS - 1)) >> 2;
425
    switch (saddr) {
426
    case 0:
427
        s->dac_index = val >> 24;
428
        s->dac_state = 0;
429
        break;
430
    case 1:
431
        switch (s->dac_state) {
432
        case 0:
433
            s->r[s->dac_index] = val >> 24;
434
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
435
            s->dac_state++;
436
            break;
437
        case 1:
438
            s->g[s->dac_index] = val >> 24;
439
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
440
            s->dac_state++;
441
            break;
442
        case 2:
443
            s->b[s->dac_index] = val >> 24;
444
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
445
            s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
446
        default:
447
            s->dac_state = 0;
448
            break;
449
        }
450
        break;
451
    default:
452
        break;
453
    }
454
    return;
455
}
456

    
457
static CPUReadMemoryFunc *tcx_dac_read[3] = {
458
    tcx_dac_readl,
459
    tcx_dac_readl,
460
    tcx_dac_readl,
461
};
462

    
463
static CPUWriteMemoryFunc *tcx_dac_write[3] = {
464
    tcx_dac_writel,
465
    tcx_dac_writel,
466
    tcx_dac_writel,
467
};
468

    
469
static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
470
{
471
    return 0;
472
}
473

    
474
static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
475
                             uint32_t val)
476
{
477
}
478

    
479
static CPUReadMemoryFunc *tcx_dummy_read[3] = {
480
    tcx_dummy_readl,
481
    tcx_dummy_readl,
482
    tcx_dummy_readl,
483
};
484

    
485
static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
486
    tcx_dummy_writel,
487
    tcx_dummy_writel,
488
    tcx_dummy_writel,
489
};
490

    
491
void tcx_init(DisplayState *ds, target_phys_addr_t addr, uint8_t *vram_base,
492
              unsigned long vram_offset, int vram_size, int width, int height,
493
              int depth)
494
{
495
    TCXState *s;
496
    int io_memory, dummy_memory;
497
    int size;
498

    
499
    s = qemu_mallocz(sizeof(TCXState));
500
    if (!s)
501
        return;
502
    s->ds = ds;
503
    s->addr = addr;
504
    s->vram_offset = vram_offset;
505
    s->width = width;
506
    s->height = height;
507
    s->depth = depth;
508

    
509
    // 8-bit plane
510
    s->vram = vram_base;
511
    size = vram_size;
512
    cpu_register_physical_memory(addr + 0x00800000ULL, size, vram_offset);
513
    vram_offset += size;
514
    vram_base += size;
515

    
516
    io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s);
517
    cpu_register_physical_memory(addr + 0x00200000ULL, TCX_DAC_NREGS, io_memory);
518

    
519
    dummy_memory = cpu_register_io_memory(0, tcx_dummy_read, tcx_dummy_write,
520
                                          s);
521
    cpu_register_physical_memory(addr + 0x00700000ULL, TCX_TEC_NREGS,
522
                                 dummy_memory);
523
    if (depth == 24) {
524
        // 24-bit plane
525
        size = vram_size * 4;
526
        s->vram24 = (uint32_t *)vram_base;
527
        s->vram24_offset = vram_offset;
528
        cpu_register_physical_memory(addr + 0x02000000ULL, size, vram_offset);
529
        vram_offset += size;
530
        vram_base += size;
531

    
532
        // Control plane
533
        size = vram_size * 4;
534
        s->cplane = (uint32_t *)vram_base;
535
        s->cplane_offset = vram_offset;
536
        cpu_register_physical_memory(addr + 0x0a000000ULL, size, vram_offset);
537
        graphic_console_init(s->ds, tcx24_update_display,
538
                             tcx24_invalidate_display, tcx24_screen_dump, s);
539
    } else {
540
        cpu_register_physical_memory(addr + 0x00300000ULL, TCX_THC_NREGS_8,
541
                                     dummy_memory);
542
        graphic_console_init(s->ds, tcx_update_display, tcx_invalidate_display,
543
                             tcx_screen_dump, s);
544
    }
545
    // NetBSD writes here even with 8-bit display
546
    cpu_register_physical_memory(addr + 0x00301000ULL, TCX_THC_NREGS_24,
547
                                 dummy_memory);
548

    
549
    register_savevm("tcx", addr, 4, tcx_save, tcx_load, s);
550
    qemu_register_reset(tcx_reset, s);
551
    tcx_reset(s);
552
    dpy_resize(s->ds, width, height);
553
}
554

    
555
static void tcx_screen_dump(void *opaque, const char *filename)
556
{
557
    TCXState *s = opaque;
558
    FILE *f;
559
    uint8_t *d, *d1, v;
560
    int y, x;
561

    
562
    f = fopen(filename, "wb");
563
    if (!f)
564
        return;
565
    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
566
    d1 = s->vram;
567
    for(y = 0; y < s->height; y++) {
568
        d = d1;
569
        for(x = 0; x < s->width; x++) {
570
            v = *d;
571
            fputc(s->r[v], f);
572
            fputc(s->g[v], f);
573
            fputc(s->b[v], f);
574
            d++;
575
        }
576
        d1 += MAXX;
577
    }
578
    fclose(f);
579
    return;
580
}
581

    
582
static void tcx24_screen_dump(void *opaque, const char *filename)
583
{
584
    TCXState *s = opaque;
585
    FILE *f;
586
    uint8_t *d, *d1, v;
587
    uint32_t *s24, *cptr, dval;
588
    int y, x;
589

    
590
    f = fopen(filename, "wb");
591
    if (!f)
592
        return;
593
    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
594
    d1 = s->vram;
595
    s24 = s->vram24;
596
    cptr = s->cplane;
597
    for(y = 0; y < s->height; y++) {
598
        d = d1;
599
        for(x = 0; x < s->width; x++, d++, s24++) {
600
            if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
601
                dval = *s24 & 0x00ffffff;
602
                fputc((dval >> 16) & 0xff, f);
603
                fputc((dval >> 8) & 0xff, f);
604
                fputc(dval & 0xff, f);
605
            } else {
606
                v = *d;
607
                fputc(s->r[v], f);
608
                fputc(s->g[v], f);
609
                fputc(s->b[v], f);
610
            }
611
        }
612
        d1 += MAXX;
613
    }
614
    fclose(f);
615
    return;
616
}