Statistics
| Branch: | Revision:

root / hw / tcx.c @ 1eed09cb

History | View | Annotate | Download (17.5 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
    uint8_t *vram;
40
    uint32_t *vram24, *cplane;
41
    ram_addr_t vram_offset, vram24_offset, cplane_offset;
42
    uint16_t width, height, depth;
43
    uint8_t r[256], g[256], b[256];
44
    uint32_t palette[256];
45
    uint8_t dac_index, dac_state;
46
} TCXState;
47

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

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

    
82
static void tcx_draw_line32(TCXState *s1, uint8_t *d,
83
                            const uint8_t *s, int width)
84
{
85
    int x;
86
    uint8_t val;
87
    uint32_t *p = (uint32_t *)d;
88

    
89
    for(x = 0; x < width; x++) {
90
        val = *s++;
91
        *p++ = s1->palette[val];
92
    }
93
}
94

    
95
static void tcx_draw_line16(TCXState *s1, uint8_t *d,
96
                            const uint8_t *s, int width)
97
{
98
    int x;
99
    uint8_t val;
100
    uint16_t *p = (uint16_t *)d;
101

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

    
108
static void tcx_draw_line8(TCXState *s1, uint8_t *d,
109
                           const uint8_t *s, int width)
110
{
111
    int x;
112
    uint8_t val;
113

    
114
    for(x = 0; x < width; x++) {
115
        val = *s++;
116
        *d++ = s1->palette[val];
117
    }
118
}
119

    
120
/*
121
  XXX Could be much more optimal:
122
  * detect if line/page/whole screen is in 24 bit mode
123
  * if destination is also BGR, use memcpy
124
  */
125
static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
126
                                     const uint8_t *s, int width,
127
                                     const uint32_t *cplane,
128
                                     const uint32_t *s24)
129
{
130
    int x, bgr, r, g, b;
131
    uint8_t val, *p8;
132
    uint32_t *p = (uint32_t *)d;
133
    uint32_t dval;
134

    
135
    bgr = is_surface_bgr(s1->ds->surface);
136
    for(x = 0; x < width; x++, s++, s24++) {
137
        if ((be32_to_cpu(*cplane++) & 0xff000000) == 0x03000000) {
138
            // 24-bit direct, BGR order
139
            p8 = (uint8_t *)s24;
140
            p8++;
141
            b = *p8++;
142
            g = *p8++;
143
            r = *p8++;
144
            if (bgr)
145
                dval = rgb_to_pixel32bgr(r, g, b);
146
            else
147
                dval = rgb_to_pixel32(r, g, b);
148
        } else {
149
            val = *s;
150
            dval = s1->palette[val];
151
        }
152
        *p++ = dval;
153
    }
154
}
155

    
156
static inline int check_dirty(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 *dst, const uint8_t *src, int width);
195

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

    
207
    switch (ds_get_bits_per_pixel(ts->ds)) {
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_max >= page_min) {
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 (ds_get_bits_per_pixel(ts->ds) != 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 = -1;
281
    page_max = 0;
282
    d = ds_get_data(ts->ds);
283
    s = ts->vram;
284
    s24 = ts->vram24;
285
    cptr = ts->cplane;
286
    dd = ds_get_linesize(ts->ds);
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(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_max >= page_min) {
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_be16s(f, &s->height);
369
    qemu_put_be16s(f, &s->width);
370
    qemu_put_be16s(f, &s->depth);
371
    qemu_put_buffer(f, s->r, 256);
372
    qemu_put_buffer(f, s->g, 256);
373
    qemu_put_buffer(f, s->b, 256);
374
    qemu_put_8s(f, &s->dac_index);
375
    qemu_put_8s(f, &s->dac_state);
376
}
377

    
378
static int tcx_load(QEMUFile *f, void *opaque, int version_id)
379
{
380
    TCXState *s = opaque;
381
    uint32_t dummy;
382

    
383
    if (version_id != 3 && version_id != 4)
384
        return -EINVAL;
385

    
386
    if (version_id == 3) {
387
        qemu_get_be32s(f, &dummy);
388
        qemu_get_be32s(f, &dummy);
389
        qemu_get_be32s(f, &dummy);
390
    }
391
    qemu_get_be16s(f, &s->height);
392
    qemu_get_be16s(f, &s->width);
393
    qemu_get_be16s(f, &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

    
434
    switch (addr) {
435
    case 0:
436
        s->dac_index = val >> 24;
437
        s->dac_state = 0;
438
        break;
439
    case 4:
440
        switch (s->dac_state) {
441
        case 0:
442
            s->r[s->dac_index] = val >> 24;
443
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
444
            s->dac_state++;
445
            break;
446
        case 1:
447
            s->g[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 2:
452
            s->b[s->dac_index] = val >> 24;
453
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
454
            s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
455
        default:
456
            s->dac_state = 0;
457
            break;
458
        }
459
        break;
460
    default:
461
        break;
462
    }
463
    return;
464
}
465

    
466
static CPUReadMemoryFunc *tcx_dac_read[3] = {
467
    NULL,
468
    NULL,
469
    tcx_dac_readl,
470
};
471

    
472
static CPUWriteMemoryFunc *tcx_dac_write[3] = {
473
    NULL,
474
    NULL,
475
    tcx_dac_writel,
476
};
477

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

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

    
488
static CPUReadMemoryFunc *tcx_dummy_read[3] = {
489
    NULL,
490
    NULL,
491
    tcx_dummy_readl,
492
};
493

    
494
static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
495
    NULL,
496
    NULL,
497
    tcx_dummy_writel,
498
};
499

    
500
void tcx_init(target_phys_addr_t addr, int vram_size, int width, int height,
501
              int depth)
502
{
503
    TCXState *s;
504
    int io_memory, dummy_memory;
505
    ram_addr_t vram_offset;
506
    int size;
507
    uint8_t *vram_base;
508

    
509
    vram_offset = qemu_ram_alloc(vram_size * (1 + 4 + 4));
510
    vram_base = qemu_get_ram_ptr(vram_offset);
511

    
512
    s = qemu_mallocz(sizeof(TCXState));
513
    s->addr = addr;
514
    s->vram_offset = vram_offset;
515
    s->width = width;
516
    s->height = height;
517
    s->depth = depth;
518

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

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

    
530
    dummy_memory = cpu_register_io_memory(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
        s->ds = graphic_console_init(tcx24_update_display,
549
                                     tcx24_invalidate_display,
550
                                     tcx24_screen_dump, NULL, s);
551
    } else {
552
        cpu_register_physical_memory(addr + 0x00300000ULL, TCX_THC_NREGS_8,
553
                                     dummy_memory);
554
        s->ds = graphic_console_init(tcx_update_display,
555
                                     tcx_invalidate_display,
556
                                     tcx_screen_dump, NULL, s);
557
    }
558
    // NetBSD writes here even with 8-bit display
559
    cpu_register_physical_memory(addr + 0x00301000ULL, TCX_THC_NREGS_24,
560
                                 dummy_memory);
561

    
562
    register_savevm("tcx", addr, 4, tcx_save, tcx_load, s);
563
    qemu_register_reset(tcx_reset, 0, s);
564
    tcx_reset(s);
565
    qemu_console_resize(s->ds, width, height);
566
}
567

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

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

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

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