Statistics
| Branch: | Revision:

root / hw / tcx.c @ 688ea2eb

History | View | Annotate | Download (17.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 "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
/*
128
  XXX Could be much more optimal:
129
  * detect if line/page/whole screen is in 24 bit mode
130
  * if destination is also BGR, use memcpy
131
  */
132
static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
133
                                     const uint8_t *s, int width,
134
                                     const uint32_t *cplane,
135
                                     const uint32_t *s24)
136
{
137
    int x, bgr, r, g, b;
138
    uint8_t val, *p8;
139
    uint32_t *p = (uint32_t *)d;
140
    uint32_t dval;
141

    
142
    bgr = s1->ds->bgr;
143
    for(x = 0; x < width; x++, s++, s24++) {
144
        if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
145
            // 24-bit direct, BGR order
146
            p8 = (uint8_t *)s24;
147
            p8++;
148
            b = *p8++;
149
            g = *p8++;
150
            r = *p8++;
151
            if (bgr)
152
                dval = rgb_to_pixel32bgr(r, g, b);
153
            else
154
                dval = rgb_to_pixel32(r, g, b);
155
        } else {
156
            val = *s;
157
            dval = s1->palette[val];
158
        }
159
        *p++ = dval;
160
    }
161
}
162

    
163
static inline int check_dirty(ram_addr_t page, ram_addr_t page24,
164
                              ram_addr_t cpage)
165
{
166
    int ret;
167
    unsigned int off;
168

    
169
    ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
170
    for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
171
        ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
172
        ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
173
    }
174
    return ret;
175
}
176

    
177
static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
178
                               ram_addr_t page_max, ram_addr_t page24,
179
                              ram_addr_t cpage)
180
{
181
    cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
182
                                    VGA_DIRTY_FLAG);
183
    page_min -= ts->vram_offset;
184
    page_max -= ts->vram_offset;
185
    cpu_physical_memory_reset_dirty(page24 + page_min * 4,
186
                                    page24 + page_max * 4 + TARGET_PAGE_SIZE,
187
                                    VGA_DIRTY_FLAG);
188
    cpu_physical_memory_reset_dirty(cpage + page_min * 4,
189
                                    cpage + page_max * 4 + TARGET_PAGE_SIZE,
190
                                    VGA_DIRTY_FLAG);
191
}
192

    
193
/* Fixed line length 1024 allows us to do nice tricks not possible on
194
   VGA... */
195
static void tcx_update_display(void *opaque)
196
{
197
    TCXState *ts = opaque;
198
    ram_addr_t page, page_min, page_max;
199
    int y, y_start, dd, ds;
200
    uint8_t *d, *s;
201
    void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
202

    
203
    if (ts->ds->depth == 0)
204
        return;
205
    page = ts->vram_offset;
206
    y_start = -1;
207
    page_min = 0xffffffff;
208
    page_max = 0;
209
    d = ts->ds->data;
210
    s = ts->vram;
211
    dd = ts->ds->linesize;
212
    ds = 1024;
213

    
214
    switch (ts->ds->depth) {
215
    case 32:
216
        f = tcx_draw_line32;
217
        break;
218
    case 15:
219
    case 16:
220
        f = tcx_draw_line16;
221
        break;
222
    default:
223
    case 8:
224
        f = tcx_draw_line8;
225
        break;
226
    case 0:
227
        return;
228
    }
229

    
230
    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
231
        if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
232
            if (y_start < 0)
233
                y_start = y;
234
            if (page < page_min)
235
                page_min = page;
236
            if (page > page_max)
237
                page_max = page;
238
            f(ts, d, s, ts->width);
239
            d += dd;
240
            s += ds;
241
            f(ts, d, s, ts->width);
242
            d += dd;
243
            s += ds;
244
            f(ts, d, s, ts->width);
245
            d += dd;
246
            s += ds;
247
            f(ts, d, s, ts->width);
248
            d += dd;
249
            s += ds;
250
        } else {
251
            if (y_start >= 0) {
252
                /* flush to display */
253
                dpy_update(ts->ds, 0, y_start,
254
                           ts->width, y - y_start);
255
                y_start = -1;
256
            }
257
            d += dd * 4;
258
            s += ds * 4;
259
        }
260
    }
261
    if (y_start >= 0) {
262
        /* flush to display */
263
        dpy_update(ts->ds, 0, y_start,
264
                   ts->width, y - y_start);
265
    }
266
    /* reset modified pages */
267
    if (page_min <= page_max) {
268
        cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
269
                                        VGA_DIRTY_FLAG);
270
    }
271
}
272

    
273
static void tcx24_update_display(void *opaque)
274
{
275
    TCXState *ts = opaque;
276
    ram_addr_t page, page_min, page_max, cpage, page24;
277
    int y, y_start, dd, ds;
278
    uint8_t *d, *s;
279
    uint32_t *cptr, *s24;
280

    
281
    if (ts->ds->depth != 32)
282
            return;
283
    page = ts->vram_offset;
284
    page24 = ts->vram24_offset;
285
    cpage = ts->cplane_offset;
286
    y_start = -1;
287
    page_min = 0xffffffff;
288
    page_max = 0;
289
    d = ts->ds->data;
290
    s = ts->vram;
291
    s24 = ts->vram24;
292
    cptr = ts->cplane;
293
    dd = ts->ds->linesize;
294
    ds = 1024;
295

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

    
349
static void tcx_invalidate_display(void *opaque)
350
{
351
    TCXState *s = opaque;
352
    int i;
353

    
354
    for (i = 0; i < MAXX*MAXY; i += TARGET_PAGE_SIZE) {
355
        cpu_physical_memory_set_dirty(s->vram_offset + i);
356
    }
357
}
358

    
359
static void tcx24_invalidate_display(void *opaque)
360
{
361
    TCXState *s = opaque;
362
    int i;
363

    
364
    tcx_invalidate_display(s);
365
    for (i = 0; i < MAXX*MAXY * 4; i += TARGET_PAGE_SIZE) {
366
        cpu_physical_memory_set_dirty(s->vram24_offset + i);
367
        cpu_physical_memory_set_dirty(s->cplane_offset + i);
368
    }
369
}
370

    
371
static void tcx_save(QEMUFile *f, void *opaque)
372
{
373
    TCXState *s = opaque;
374

    
375
    qemu_put_be16s(f, (uint16_t *)&s->height);
376
    qemu_put_be16s(f, (uint16_t *)&s->width);
377
    qemu_put_be16s(f, (uint16_t *)&s->depth);
378
    qemu_put_buffer(f, s->r, 256);
379
    qemu_put_buffer(f, s->g, 256);
380
    qemu_put_buffer(f, s->b, 256);
381
    qemu_put_8s(f, &s->dac_index);
382
    qemu_put_8s(f, &s->dac_state);
383
}
384

    
385
static int tcx_load(QEMUFile *f, void *opaque, int version_id)
386
{
387
    TCXState *s = opaque;
388
    uint32_t dummy;
389

    
390
    if (version_id != 3 && version_id != 4)
391
        return -EINVAL;
392

    
393
    if (version_id == 3) {
394
        qemu_get_be32s(f, (uint32_t *)&dummy);
395
        qemu_get_be32s(f, (uint32_t *)&dummy);
396
        qemu_get_be32s(f, (uint32_t *)&dummy);
397
    }
398
    qemu_get_be16s(f, (uint16_t *)&s->height);
399
    qemu_get_be16s(f, (uint16_t *)&s->width);
400
    qemu_get_be16s(f, (uint16_t *)&s->depth);
401
    qemu_get_buffer(f, s->r, 256);
402
    qemu_get_buffer(f, s->g, 256);
403
    qemu_get_buffer(f, s->b, 256);
404
    qemu_get_8s(f, &s->dac_index);
405
    qemu_get_8s(f, &s->dac_state);
406
    update_palette_entries(s, 0, 256);
407
    if (s->depth == 24)
408
        tcx24_invalidate_display(s);
409
    else
410
        tcx_invalidate_display(s);
411

    
412
    return 0;
413
}
414

    
415
static void tcx_reset(void *opaque)
416
{
417
    TCXState *s = opaque;
418

    
419
    /* Initialize palette */
420
    memset(s->r, 0, 256);
421
    memset(s->g, 0, 256);
422
    memset(s->b, 0, 256);
423
    s->r[255] = s->g[255] = s->b[255] = 255;
424
    update_palette_entries(s, 0, 256);
425
    memset(s->vram, 0, MAXX*MAXY);
426
    cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
427
                                    MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
428
    s->dac_index = 0;
429
    s->dac_state = 0;
430
}
431

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

    
437
static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
438
{
439
    TCXState *s = opaque;
440
    uint32_t saddr;
441

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

    
475
static CPUReadMemoryFunc *tcx_dac_read[3] = {
476
    NULL,
477
    NULL,
478
    tcx_dac_readl,
479
};
480

    
481
static CPUWriteMemoryFunc *tcx_dac_write[3] = {
482
    NULL,
483
    NULL,
484
    tcx_dac_writel,
485
};
486

    
487
static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
488
{
489
    return 0;
490
}
491

    
492
static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
493
                             uint32_t val)
494
{
495
}
496

    
497
static CPUReadMemoryFunc *tcx_dummy_read[3] = {
498
    NULL,
499
    NULL,
500
    tcx_dummy_readl,
501
};
502

    
503
static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
504
    NULL,
505
    NULL,
506
    tcx_dummy_writel,
507
};
508

    
509
void tcx_init(DisplayState *ds, target_phys_addr_t addr, uint8_t *vram_base,
510
              unsigned long vram_offset, int vram_size, int width, int height,
511
              int depth)
512
{
513
    TCXState *s;
514
    int io_memory, dummy_memory;
515
    int size;
516

    
517
    s = qemu_mallocz(sizeof(TCXState));
518
    if (!s)
519
        return;
520
    s->ds = ds;
521
    s->addr = addr;
522
    s->vram_offset = vram_offset;
523
    s->width = width;
524
    s->height = height;
525
    s->depth = depth;
526

    
527
    // 8-bit plane
528
    s->vram = vram_base;
529
    size = vram_size;
530
    cpu_register_physical_memory(addr + 0x00800000ULL, size, vram_offset);
531
    vram_offset += size;
532
    vram_base += size;
533

    
534
    io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s);
535
    cpu_register_physical_memory(addr + 0x00200000ULL, TCX_DAC_NREGS,
536
                                 io_memory);
537

    
538
    dummy_memory = cpu_register_io_memory(0, tcx_dummy_read, tcx_dummy_write,
539
                                          s);
540
    cpu_register_physical_memory(addr + 0x00700000ULL, TCX_TEC_NREGS,
541
                                 dummy_memory);
542
    if (depth == 24) {
543
        // 24-bit plane
544
        size = vram_size * 4;
545
        s->vram24 = (uint32_t *)vram_base;
546
        s->vram24_offset = vram_offset;
547
        cpu_register_physical_memory(addr + 0x02000000ULL, size, vram_offset);
548
        vram_offset += size;
549
        vram_base += size;
550

    
551
        // Control plane
552
        size = vram_size * 4;
553
        s->cplane = (uint32_t *)vram_base;
554
        s->cplane_offset = vram_offset;
555
        cpu_register_physical_memory(addr + 0x0a000000ULL, size, vram_offset);
556
        s->console = graphic_console_init(s->ds, tcx24_update_display,
557
                                          tcx24_invalidate_display,
558
                                          tcx24_screen_dump, NULL, s);
559
    } else {
560
        cpu_register_physical_memory(addr + 0x00300000ULL, TCX_THC_NREGS_8,
561
                                     dummy_memory);
562
        s->console = graphic_console_init(s->ds, tcx_update_display,
563
                                          tcx_invalidate_display,
564
                                          tcx_screen_dump, NULL, s);
565
    }
566
    // NetBSD writes here even with 8-bit display
567
    cpu_register_physical_memory(addr + 0x00301000ULL, TCX_THC_NREGS_24,
568
                                 dummy_memory);
569

    
570
    register_savevm("tcx", addr, 4, tcx_save, tcx_load, s);
571
    qemu_register_reset(tcx_reset, s);
572
    tcx_reset(s);
573
    qemu_console_resize(s->console, width, height);
574
}
575

    
576
static void tcx_screen_dump(void *opaque, const char *filename)
577
{
578
    TCXState *s = opaque;
579
    FILE *f;
580
    uint8_t *d, *d1, v;
581
    int y, x;
582

    
583
    f = fopen(filename, "wb");
584
    if (!f)
585
        return;
586
    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
587
    d1 = s->vram;
588
    for(y = 0; y < s->height; y++) {
589
        d = d1;
590
        for(x = 0; x < s->width; x++) {
591
            v = *d;
592
            fputc(s->r[v], f);
593
            fputc(s->g[v], f);
594
            fputc(s->b[v], f);
595
            d++;
596
        }
597
        d1 += MAXX;
598
    }
599
    fclose(f);
600
    return;
601
}
602

    
603
static void tcx24_screen_dump(void *opaque, const char *filename)
604
{
605
    TCXState *s = opaque;
606
    FILE *f;
607
    uint8_t *d, *d1, v;
608
    uint32_t *s24, *cptr, dval;
609
    int y, x;
610

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