Statistics
| Branch: | Revision:

root / hw / tcx.c @ c60e08d9

History | View | Annotate | Download (17.4 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 "hw.h"
25
#include "sun4m.h"
26
#include "console.h"
27
#include "pixel_ops.h"
28

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

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

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

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

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

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

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

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

    
115
static void tcx_draw_line8(TCXState *s1, uint8_t *d,
116
                           const uint8_t *s, int width)
117
{
118
    int x;
119
    uint8_t val;
120

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

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

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

    
148
static inline int check_dirty(ram_addr_t page, ram_addr_t page24,
149
                              ram_addr_t cpage)
150
{
151
    int ret;
152
    unsigned int off;
153

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

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

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

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

    
199
    switch (ts->ds->depth) {
200
    case 32:
201
        f = tcx_draw_line32;
202
        break;
203
    case 15:
204
    case 16:
205
        f = tcx_draw_line16;
206
        break;
207
    default:
208
    case 8:
209
        f = tcx_draw_line8;
210
        break;
211
    case 0:
212
        return;
213
    }
214

    
215
    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
216
        if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
217
            if (y_start < 0)
218
                y_start = y;
219
            if (page < page_min)
220
                page_min = page;
221
            if (page > page_max)
222
                page_max = page;
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
            f(ts, d, s, ts->width);
233
            d += dd;
234
            s += ds;
235
        } else {
236
            if (y_start >= 0) {
237
                /* flush to display */
238
                dpy_update(ts->ds, 0, y_start,
239
                           ts->width, y - y_start);
240
                y_start = -1;
241
            }
242
            d += dd * 4;
243
            s += ds * 4;
244
        }
245
    }
246
    if (y_start >= 0) {
247
        /* flush to display */
248
        dpy_update(ts->ds, 0, y_start,
249
                   ts->width, y - y_start);
250
    }
251
    /* reset modified pages */
252
    if (page_min <= page_max) {
253
        cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
254
                                        VGA_DIRTY_FLAG);
255
    }
256
}
257

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

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

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

    
334
static void tcx_invalidate_display(void *opaque)
335
{
336
    TCXState *s = opaque;
337
    int i;
338

    
339
    for (i = 0; i < MAXX*MAXY; i += TARGET_PAGE_SIZE) {
340
        cpu_physical_memory_set_dirty(s->vram_offset + i);
341
    }
342
}
343

    
344
static void tcx24_invalidate_display(void *opaque)
345
{
346
    TCXState *s = opaque;
347
    int i;
348

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

    
356
static void tcx_save(QEMUFile *f, void *opaque)
357
{
358
    TCXState *s = opaque;
359

    
360
    qemu_put_be16s(f, (uint16_t *)&s->height);
361
    qemu_put_be16s(f, (uint16_t *)&s->width);
362
    qemu_put_be16s(f, (uint16_t *)&s->depth);
363
    qemu_put_buffer(f, s->r, 256);
364
    qemu_put_buffer(f, s->g, 256);
365
    qemu_put_buffer(f, s->b, 256);
366
    qemu_put_8s(f, &s->dac_index);
367
    qemu_put_8s(f, &s->dac_state);
368
}
369

    
370
static int tcx_load(QEMUFile *f, void *opaque, int version_id)
371
{
372
    TCXState *s = opaque;
373
    uint32_t dummy;
374

    
375
    if (version_id != 3 && version_id != 4)
376
        return -EINVAL;
377

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

    
397
    return 0;
398
}
399

    
400
static void tcx_reset(void *opaque)
401
{
402
    TCXState *s = opaque;
403

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

    
417
static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
418
{
419
    return 0;
420
}
421

    
422
static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
423
{
424
    TCXState *s = opaque;
425
    uint32_t saddr;
426

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

    
460
static CPUReadMemoryFunc *tcx_dac_read[3] = {
461
    NULL,
462
    NULL,
463
    tcx_dac_readl,
464
};
465

    
466
static CPUWriteMemoryFunc *tcx_dac_write[3] = {
467
    NULL,
468
    NULL,
469
    tcx_dac_writel,
470
};
471

    
472
static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
473
{
474
    return 0;
475
}
476

    
477
static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
478
                             uint32_t val)
479
{
480
}
481

    
482
static CPUReadMemoryFunc *tcx_dummy_read[3] = {
483
    NULL,
484
    NULL,
485
    tcx_dummy_readl,
486
};
487

    
488
static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
489
    NULL,
490
    NULL,
491
    tcx_dummy_writel,
492
};
493

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

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

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

    
519
    io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s);
520
    cpu_register_physical_memory(addr + 0x00200000ULL, TCX_DAC_NREGS,
521
                                 io_memory);
522

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

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

    
555
    register_savevm("tcx", addr, 4, tcx_save, tcx_load, s);
556
    qemu_register_reset(tcx_reset, s);
557
    tcx_reset(s);
558
    qemu_console_resize(s->console, width, height);
559
}
560

    
561
static void tcx_screen_dump(void *opaque, const char *filename)
562
{
563
    TCXState *s = opaque;
564
    FILE *f;
565
    uint8_t *d, *d1, v;
566
    int y, x;
567

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

    
588
static void tcx24_screen_dump(void *opaque, const char *filename)
589
{
590
    TCXState *s = opaque;
591
    FILE *f;
592
    uint8_t *d, *d1, v;
593
    uint32_t *s24, *cptr, dval;
594
    int y, x;
595

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