Statistics
| Branch: | Revision:

root / hw / tcx.c @ 97e7df27

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

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

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

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

    
50
/* XXX: unify with vga draw line functions */
51
static inline unsigned int rgb_to_pixel8(unsigned int r, unsigned int g, unsigned b)
52
{
53
    return ((r >> 5) << 5) | ((g >> 5) << 2) | (b >> 6);
54
}
55

    
56
static inline unsigned int rgb_to_pixel15(unsigned int r, unsigned int g, unsigned b)
57
{
58
    return ((r >> 3) << 10) | ((g >> 3) << 5) | (b >> 3);
59
}
60

    
61
static inline unsigned int rgb_to_pixel16(unsigned int r, unsigned int g, unsigned b)
62
{
63
    return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
64
}
65

    
66
static inline unsigned int rgb_to_pixel32(unsigned int r, unsigned int g, unsigned b)
67
{
68
    return (r << 16) | (g << 8) | b;
69
}
70

    
71
static void update_palette_entries(TCXState *s, int start, int end)
72
{
73
    int i;
74
    for(i = start; i < end; i++) {
75
        switch(s->ds->depth) {
76
        default:
77
        case 8:
78
            s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
79
            break;
80
        case 15:
81
            s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
82
            break;
83
        case 16:
84
            s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
85
            break;
86
        case 32:
87
            s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
88
            break;
89
        }
90
    }
91
    if (s->depth == 24)
92
        tcx24_invalidate_display(s);
93
    else
94
        tcx_invalidate_display(s);
95
}
96

    
97
static void tcx_draw_line32(TCXState *s1, uint8_t *d, 
98
                            const uint8_t *s, int width)
99
{
100
    int x;
101
    uint8_t val;
102
    uint32_t *p = (uint32_t *)d;
103

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

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

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

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

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

    
135
static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
136
                                     const uint8_t *s, int width,
137
                                     const uint32_t *cplane,
138
                                     const uint32_t *s24)
139
{
140
    int x;
141
    uint8_t val;
142
    uint32_t *p = (uint32_t *)d;
143
    uint32_t dval;
144

    
145
    for(x = 0; x < width; x++, s++, s24++) {
146
        if ((bswap32(*cplane++) & 0xff000000) == 0x03000000) { // 24-bit direct
147
            dval = bswap32(*s24) & 0x00ffffff;
148
        } else {
149
            val = *s;
150
            dval = s1->palette[val];
151
        }
152
        *p++ = dval;
153
    }
154
}
155

    
156
static inline int check_dirty(TCXState *ts, ram_addr_t page, ram_addr_t page24,
157
                              ram_addr_t cpage)
158
{
159
    int ret;
160
    unsigned int off;
161

    
162
    ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
163
    for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
164
        ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
165
        ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
166
    }
167
    return ret;
168
}
169

    
170
static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
171
                               ram_addr_t page_max, ram_addr_t page24,
172
                              ram_addr_t cpage)
173
{
174
    cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
175
                                    VGA_DIRTY_FLAG);
176
    page_min -= ts->vram_offset;
177
    page_max -= ts->vram_offset;
178
    cpu_physical_memory_reset_dirty(page24 + page_min * 4,
179
                                    page24 + page_max * 4 + TARGET_PAGE_SIZE,
180
                                    VGA_DIRTY_FLAG);
181
    cpu_physical_memory_reset_dirty(cpage + page_min * 4,
182
                                    cpage + page_max * 4 + TARGET_PAGE_SIZE,
183
                                    VGA_DIRTY_FLAG);
184
}
185

    
186
/* Fixed line length 1024 allows us to do nice tricks not possible on
187
   VGA... */
188
static void tcx_update_display(void *opaque)
189
{
190
    TCXState *ts = opaque;
191
    ram_addr_t page, page_min, page_max;
192
    int y, y_start, dd, ds;
193
    uint8_t *d, *s;
194
    void (*f)(TCXState *s1, uint8_t *d, const uint8_t *s, int width);
195

    
196
    if (ts->ds->depth == 0)
197
        return;
198
    page = ts->vram_offset;
199
    y_start = -1;
200
    page_min = 0xffffffff;
201
    page_max = 0;
202
    d = ts->ds->data;
203
    s = ts->vram;
204
    dd = ts->ds->linesize;
205
    ds = 1024;
206

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

    
266
static void tcx24_update_display(void *opaque)
267
{
268
    TCXState *ts = opaque;
269
    ram_addr_t page, page_min, page_max, cpage, page24;
270
    int y, y_start, dd, ds;
271
    uint8_t *d, *s;
272
    uint32_t *cptr, *s24;
273

    
274
    if (ts->ds->depth != 32)
275
            return;
276
    page = ts->vram_offset;
277
    page24 = ts->vram24_offset;
278
    cpage = ts->cplane_offset;
279
    y_start = -1;
280
    page_min = 0xffffffff;
281
    page_max = 0;
282
    d = ts->ds->data;
283
    s = ts->vram;
284
    s24 = ts->vram24;
285
    cptr = ts->cplane;
286
    dd = ts->ds->linesize;
287
    ds = 1024;
288

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

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

    
347
    for (i = 0; i < MAXX*MAXY; i += TARGET_PAGE_SIZE) {
348
        cpu_physical_memory_set_dirty(s->vram_offset + i);
349
    }
350
}
351

    
352
static void tcx24_invalidate_display(void *opaque)
353
{
354
    TCXState *s = opaque;
355
    int i;
356

    
357
    tcx_invalidate_display(s);
358
    for (i = 0; i < MAXX*MAXY * 4; i += TARGET_PAGE_SIZE) {
359
        cpu_physical_memory_set_dirty(s->vram24_offset + i);
360
        cpu_physical_memory_set_dirty(s->cplane_offset + i);
361
    }
362
}
363

    
364
static void tcx_save(QEMUFile *f, void *opaque)
365
{
366
    TCXState *s = opaque;
367
    
368
    qemu_put_be32s(f, (uint32_t *)&s->vram);
369
    qemu_put_be32s(f, (uint32_t *)&s->vram24);
370
    qemu_put_be32s(f, (uint32_t *)&s->cplane);
371
    qemu_put_be16s(f, (uint16_t *)&s->height);
372
    qemu_put_be16s(f, (uint16_t *)&s->width);
373
    qemu_put_be16s(f, (uint16_t *)&s->depth);
374
    qemu_put_buffer(f, s->r, 256);
375
    qemu_put_buffer(f, s->g, 256);
376
    qemu_put_buffer(f, s->b, 256);
377
    qemu_put_8s(f, &s->dac_index);
378
    qemu_put_8s(f, &s->dac_state);
379
}
380

    
381
static int tcx_load(QEMUFile *f, void *opaque, int version_id)
382
{
383
    TCXState *s = opaque;
384
    
385
    if (version_id != 3)
386
        return -EINVAL;
387

    
388
    qemu_get_be32s(f, (uint32_t *)&s->vram);
389
    qemu_get_be32s(f, (uint32_t *)&s->vram24);
390
    qemu_get_be32s(f, (uint32_t *)&s->cplane);
391
    qemu_get_be16s(f, (uint16_t *)&s->height);
392
    qemu_get_be16s(f, (uint16_t *)&s->width);
393
    qemu_get_be16s(f, (uint16_t *)&s->depth);
394
    qemu_get_buffer(f, s->r, 256);
395
    qemu_get_buffer(f, s->g, 256);
396
    qemu_get_buffer(f, s->b, 256);
397
    qemu_get_8s(f, &s->dac_index);
398
    qemu_get_8s(f, &s->dac_state);
399
    update_palette_entries(s, 0, 256);
400
    if (s->depth == 24)
401
        tcx24_invalidate_display(s);
402
    else
403
        tcx_invalidate_display(s);
404

    
405
    return 0;
406
}
407

    
408
static void tcx_reset(void *opaque)
409
{
410
    TCXState *s = opaque;
411

    
412
    /* Initialize palette */
413
    memset(s->r, 0, 256);
414
    memset(s->g, 0, 256);
415
    memset(s->b, 0, 256);
416
    s->r[255] = s->g[255] = s->b[255] = 255;
417
    update_palette_entries(s, 0, 256);
418
    memset(s->vram, 0, MAXX*MAXY);
419
    cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
420
                                    MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
421
    s->dac_index = 0;
422
    s->dac_state = 0;
423
}
424

    
425
static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
426
{
427
    return 0;
428
}
429

    
430
static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
431
{
432
    TCXState *s = opaque;
433
    uint32_t saddr;
434

    
435
    saddr = (addr & (TCX_DAC_NREGS - 1)) >> 2;
436
    switch (saddr) {
437
    case 0:
438
        s->dac_index = val >> 24;
439
        s->dac_state = 0;
440
        break;
441
    case 1:
442
        switch (s->dac_state) {
443
        case 0:
444
            s->r[s->dac_index] = val >> 24;
445
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
446
            s->dac_state++;
447
            break;
448
        case 1:
449
            s->g[s->dac_index] = val >> 24;
450
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
451
            s->dac_state++;
452
            break;
453
        case 2:
454
            s->b[s->dac_index] = val >> 24;
455
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
456
            s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
457
        default:
458
            s->dac_state = 0;
459
            break;
460
        }
461
        break;
462
    default:
463
        break;
464
    }
465
    return;
466
}
467

    
468
static CPUReadMemoryFunc *tcx_dac_read[3] = {
469
    tcx_dac_readl,
470
    tcx_dac_readl,
471
    tcx_dac_readl,
472
};
473

    
474
static CPUWriteMemoryFunc *tcx_dac_write[3] = {
475
    tcx_dac_writel,
476
    tcx_dac_writel,
477
    tcx_dac_writel,
478
};
479

    
480
static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
481
{
482
    return 0;
483
}
484

    
485
static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
486
                             uint32_t val)
487
{
488
}
489

    
490
static CPUReadMemoryFunc *tcx_dummy_read[3] = {
491
    tcx_dummy_readl,
492
    tcx_dummy_readl,
493
    tcx_dummy_readl,
494
};
495

    
496
static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
497
    tcx_dummy_writel,
498
    tcx_dummy_writel,
499
    tcx_dummy_writel,
500
};
501

    
502
void tcx_init(DisplayState *ds, target_phys_addr_t addr, uint8_t *vram_base,
503
              unsigned long vram_offset, int vram_size, int width, int height,
504
              int depth)
505
{
506
    TCXState *s;
507
    int io_memory, dummy_memory;
508
    int size;
509

    
510
    s = qemu_mallocz(sizeof(TCXState));
511
    if (!s)
512
        return;
513
    s->ds = ds;
514
    s->addr = addr;
515
    s->vram_offset = vram_offset;
516
    s->width = width;
517
    s->height = height;
518
    s->depth = depth;
519

    
520
    // 8-bit plane
521
    s->vram = vram_base;
522
    size = vram_size;
523
    cpu_register_physical_memory(addr + 0x00800000ULL, size, vram_offset);
524
    vram_offset += size;
525
    vram_base += size;
526

    
527
    io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s);
528
    cpu_register_physical_memory(addr + 0x00200000ULL, TCX_DAC_NREGS, io_memory);
529

    
530
    dummy_memory = cpu_register_io_memory(0, tcx_dummy_read, tcx_dummy_write,
531
                                          s);
532
    cpu_register_physical_memory(addr + 0x00700000ULL, TCX_TEC_NREGS,
533
                                 dummy_memory);
534
    if (depth == 24) {
535
        // 24-bit plane
536
        size = vram_size * 4;
537
        s->vram24 = (uint32_t *)vram_base;
538
        s->vram24_offset = vram_offset;
539
        cpu_register_physical_memory(addr + 0x02000000ULL, size, vram_offset);
540
        vram_offset += size;
541
        vram_base += size;
542

    
543
        // Control plane
544
        size = vram_size * 4;
545
        s->cplane = (uint32_t *)vram_base;
546
        s->cplane_offset = vram_offset;
547
        cpu_register_physical_memory(addr + 0x0a000000ULL, size, vram_offset);
548
        graphic_console_init(s->ds, tcx24_update_display,
549
                             tcx24_invalidate_display, tcx24_screen_dump, s);
550
    } else {
551
        cpu_register_physical_memory(addr + 0x00300000ULL, TCX_THC_NREGS_8,
552
                                     dummy_memory);
553
        graphic_console_init(s->ds, tcx_update_display, tcx_invalidate_display,
554
                             tcx_screen_dump, s);
555
    }
556
    // NetBSD writes here even with 8-bit display
557
    cpu_register_physical_memory(addr + 0x00301000ULL, TCX_THC_NREGS_24,
558
                                 dummy_memory);
559

    
560
    register_savevm("tcx", addr, 3, tcx_save, tcx_load, s);
561
    qemu_register_reset(tcx_reset, s);
562
    tcx_reset(s);
563
    dpy_resize(s->ds, width, height);
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
}