Blame view

hw/tcx.c 17.3 KB
1
/*
bellard authored
2
 * QEMU TCX Frame buffer
3
 *
bellard authored
4
 * Copyright (c) 2003-2005 Fabrice Bellard
5
 *
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
pbrook authored
24
25
26
#include "hw.h"
#include "sun4m.h"
#include "console.h"
27
#include "pixel_ops.h"
28
29
30

#define MAXX 1024
#define MAXY 768
bellard authored
31
#define TCX_DAC_NREGS 16
32
33
34
#define TCX_THC_NREGS_8  0x081c
#define TCX_THC_NREGS_24 0x1000
#define TCX_TEC_NREGS    0x1000
35
36

typedef struct TCXState {
37
    target_phys_addr_t addr;
38
    DisplayState *ds;
bellard authored
39
    uint8_t *vram;
blueswir1 authored
40
41
42
    uint32_t *vram24, *cplane;
    ram_addr_t vram_offset, vram24_offset, cplane_offset;
    uint16_t width, height, depth;
bellard authored
43
    uint8_t r[256], g[256], b[256];
44
    uint32_t palette[256];
bellard authored
45
    uint8_t dac_index, dac_state;
46
47
} TCXState;
48
static void tcx_screen_dump(void *opaque, const char *filename);
blueswir1 authored
49
static void tcx24_screen_dump(void *opaque, const char *filename);
50
51
static void tcx_invalidate_display(void *opaque);
static void tcx24_invalidate_display(void *opaque);
52
53
54
55
56
57
58
59
60
61
62
static void update_palette_entries(TCXState *s, int start, int end)
{
    int i;
    for(i = start; i < end; i++) {
        switch(s->ds->depth) {
        default:
        case 8:
            s->palette[i] = rgb_to_pixel8(s->r[i], s->g[i], s->b[i]);
            break;
        case 15:
63
64
65
66
            if (s->ds->bgr)
                s->palette[i] = rgb_to_pixel15bgr(s->r[i], s->g[i], s->b[i]);
            else
                s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
67
68
            break;
        case 16:
69
70
71
72
            if (s->ds->bgr)
                s->palette[i] = rgb_to_pixel16bgr(s->r[i], s->g[i], s->b[i]);
            else
                s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
73
74
            break;
        case 32:
75
76
77
78
            if (s->ds->bgr)
                s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
            else
                s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
79
80
81
            break;
        }
    }
82
83
84
85
    if (s->depth == 24)
        tcx24_invalidate_display(s);
    else
        tcx_invalidate_display(s);
86
87
}
88
static void tcx_draw_line32(TCXState *s1, uint8_t *d,
blueswir1 authored
89
                            const uint8_t *s, int width)
90
{
bellard authored
91
92
    int x;
    uint8_t val;
93
    uint32_t *p = (uint32_t *)d;
bellard authored
94
95

    for(x = 0; x < width; x++) {
blueswir1 authored
96
        val = *s++;
97
        *p++ = s1->palette[val];
bellard authored
98
    }
99
100
}
101
static void tcx_draw_line16(TCXState *s1, uint8_t *d,
blueswir1 authored
102
                            const uint8_t *s, int width)
bellard authored
103
104
105
{
    int x;
    uint8_t val;
106
    uint16_t *p = (uint16_t *)d;
bellard authored
107
bellard authored
108
    for(x = 0; x < width; x++) {
blueswir1 authored
109
        val = *s++;
110
        *p++ = s1->palette[val];
bellard authored
111
112
113
    }
}
114
static void tcx_draw_line8(TCXState *s1, uint8_t *d,
blueswir1 authored
115
                           const uint8_t *s, int width)
116
{
bellard authored
117
118
119
120
    int x;
    uint8_t val;

    for(x = 0; x < width; x++) {
blueswir1 authored
121
        val = *s++;
122
        *d++ = s1->palette[val];
123
124
125
    }
}
blueswir1 authored
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
                                     const uint8_t *s, int width,
                                     const uint32_t *cplane,
                                     const uint32_t *s24)
{
    int x;
    uint8_t val;
    uint32_t *p = (uint32_t *)d;
    uint32_t dval;

    for(x = 0; x < width; x++, s++, s24++) {
        if ((bswap32(*cplane++) & 0xff000000) == 0x03000000) { // 24-bit direct
            dval = bswap32(*s24) & 0x00ffffff;
        } else {
            val = *s;
            dval = s1->palette[val];
        }
        *p++ = dval;
    }
}

static inline int check_dirty(TCXState *ts, ram_addr_t page, ram_addr_t page24,
                              ram_addr_t cpage)
{
    int ret;
    unsigned int off;

    ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
    for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
        ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
        ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
    }
    return ret;
}

static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
                               ram_addr_t page_max, ram_addr_t page24,
                              ram_addr_t cpage)
{
    cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
                                    VGA_DIRTY_FLAG);
    page_min -= ts->vram_offset;
    page_max -= ts->vram_offset;
    cpu_physical_memory_reset_dirty(page24 + page_min * 4,
                                    page24 + page_max * 4 + TARGET_PAGE_SIZE,
                                    VGA_DIRTY_FLAG);
    cpu_physical_memory_reset_dirty(cpage + page_min * 4,
                                    cpage + page_max * 4 + TARGET_PAGE_SIZE,
                                    VGA_DIRTY_FLAG);
}
bellard authored
177
178
/* Fixed line length 1024 allows us to do nice tricks not possible on
   VGA... */
179
static void tcx_update_display(void *opaque)
180
{
bellard authored
181
    TCXState *ts = opaque;
182
183
    ram_addr_t page, page_min, page_max;
    int y, y_start, dd, ds;
bellard authored
184
    uint8_t *d, *s;
185
    void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
bellard authored
186
187

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

    switch (ts->ds->depth) {
    case 32:
blueswir1 authored
200
201
        f = tcx_draw_line32;
        break;
202
203
    case 15:
    case 16:
blueswir1 authored
204
205
        f = tcx_draw_line16;
        break;
bellard authored
206
207
    default:
    case 8:
blueswir1 authored
208
209
        f = tcx_draw_line8;
        break;
bellard authored
210
    case 0:
blueswir1 authored
211
        return;
bellard authored
212
    }
213
bellard authored
214
    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
blueswir1 authored
215
216
        if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
            if (y_start < 0)
bellard authored
217
218
219
220
221
                y_start = y;
            if (page < page_min)
                page_min = page;
            if (page > page_max)
                page_max = page;
blueswir1 authored
222
223
224
225
226
227
228
229
230
231
232
233
234
            f(ts, d, s, ts->width);
            d += dd;
            s += ds;
            f(ts, d, s, ts->width);
            d += dd;
            s += ds;
            f(ts, d, s, ts->width);
            d += dd;
            s += ds;
            f(ts, d, s, ts->width);
            d += dd;
            s += ds;
        } else {
bellard authored
235
236
            if (y_start >= 0) {
                /* flush to display */
237
                dpy_update(ts->ds, 0, y_start,
bellard authored
238
                           ts->width, y - y_start);
bellard authored
239
240
                y_start = -1;
            }
blueswir1 authored
241
242
243
            d += dd * 4;
            s += ds * 4;
        }
bellard authored
244
245
    }
    if (y_start >= 0) {
blueswir1 authored
246
247
248
        /* flush to display */
        dpy_update(ts->ds, 0, y_start,
                   ts->width, y - y_start);
bellard authored
249
250
    }
    /* reset modified pages */
251
    if (page_min <= page_max) {
bellard authored
252
253
        cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
                                        VGA_DIRTY_FLAG);
bellard authored
254
    }
255
256
}
blueswir1 authored
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
static void tcx24_update_display(void *opaque)
{
    TCXState *ts = opaque;
    ram_addr_t page, page_min, page_max, cpage, page24;
    int y, y_start, dd, ds;
    uint8_t *d, *s;
    uint32_t *cptr, *s24;

    if (ts->ds->depth != 32)
            return;
    page = ts->vram_offset;
    page24 = ts->vram24_offset;
    cpage = ts->cplane_offset;
    y_start = -1;
    page_min = 0xffffffff;
    page_max = 0;
    d = ts->ds->data;
    s = ts->vram;
    s24 = ts->vram24;
    cptr = ts->cplane;
    dd = ts->ds->linesize;
    ds = 1024;

    for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
            page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
        if (check_dirty(ts, page, page24, cpage)) {
            if (y_start < 0)
                y_start = y;
            if (page < page_min)
                page_min = page;
            if (page > page_max)
                page_max = page;
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
            d += dd;
            s += ds;
            cptr += ds;
            s24 += ds;
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
            d += dd;
            s += ds;
            cptr += ds;
            s24 += ds;
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
            d += dd;
            s += ds;
            cptr += ds;
            s24 += ds;
            tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
            d += dd;
            s += ds;
            cptr += ds;
            s24 += ds;
        } else {
            if (y_start >= 0) {
                /* flush to display */
                dpy_update(ts->ds, 0, y_start,
                           ts->width, y - y_start);
                y_start = -1;
            }
            d += dd * 4;
            s += ds * 4;
            cptr += ds * 4;
            s24 += ds * 4;
        }
    }
    if (y_start >= 0) {
        /* flush to display */
        dpy_update(ts->ds, 0, y_start,
                   ts->width, y - y_start);
    }
    /* reset modified pages */
    if (page_min <= page_max) {
        reset_dirty(ts, page_min, page_max, page24, cpage);
    }
}
333
static void tcx_invalidate_display(void *opaque)
334
{
bellard authored
335
336
337
338
    TCXState *s = opaque;
    int i;

    for (i = 0; i < MAXX*MAXY; i += TARGET_PAGE_SIZE) {
blueswir1 authored
339
        cpu_physical_memory_set_dirty(s->vram_offset + i);
bellard authored
340
    }
341
342
}
blueswir1 authored
343
344
345
346
347
348
349
350
351
352
353
354
static void tcx24_invalidate_display(void *opaque)
{
    TCXState *s = opaque;
    int i;

    tcx_invalidate_display(s);
    for (i = 0; i < MAXX*MAXY * 4; i += TARGET_PAGE_SIZE) {
        cpu_physical_memory_set_dirty(s->vram24_offset + i);
        cpu_physical_memory_set_dirty(s->cplane_offset + i);
    }
}
bellard authored
355
static void tcx_save(QEMUFile *f, void *opaque)
356
357
{
    TCXState *s = opaque;
358
bellard authored
359
360
    qemu_put_be16s(f, (uint16_t *)&s->height);
    qemu_put_be16s(f, (uint16_t *)&s->width);
blueswir1 authored
361
    qemu_put_be16s(f, (uint16_t *)&s->depth);
bellard authored
362
363
364
    qemu_put_buffer(f, s->r, 256);
    qemu_put_buffer(f, s->g, 256);
    qemu_put_buffer(f, s->b, 256);
bellard authored
365
366
    qemu_put_8s(f, &s->dac_index);
    qemu_put_8s(f, &s->dac_state);
367
368
}
bellard authored
369
static int tcx_load(QEMUFile *f, void *opaque, int version_id)
370
{
bellard authored
371
    TCXState *s = opaque;
blueswir1 authored
372
373
374
    uint32_t dummy;

    if (version_id != 3 && version_id != 4)
bellard authored
375
376
        return -EINVAL;
blueswir1 authored
377
378
379
380
381
    if (version_id == 3) {
        qemu_get_be32s(f, (uint32_t *)&dummy);
        qemu_get_be32s(f, (uint32_t *)&dummy);
        qemu_get_be32s(f, (uint32_t *)&dummy);
    }
bellard authored
382
383
    qemu_get_be16s(f, (uint16_t *)&s->height);
    qemu_get_be16s(f, (uint16_t *)&s->width);
blueswir1 authored
384
    qemu_get_be16s(f, (uint16_t *)&s->depth);
bellard authored
385
386
387
    qemu_get_buffer(f, s->r, 256);
    qemu_get_buffer(f, s->g, 256);
    qemu_get_buffer(f, s->b, 256);
bellard authored
388
389
    qemu_get_8s(f, &s->dac_index);
    qemu_get_8s(f, &s->dac_state);
390
    update_palette_entries(s, 0, 256);
391
392
393
394
    if (s->depth == 24)
        tcx24_invalidate_display(s);
    else
        tcx_invalidate_display(s);
395
bellard authored
396
    return 0;
397
398
}
bellard authored
399
static void tcx_reset(void *opaque)
400
{
bellard authored
401
402
403
404
405
406
407
    TCXState *s = opaque;

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

static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
{
    return 0;
}

static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
{
    TCXState *s = opaque;
    uint32_t saddr;

    saddr = (addr & (TCX_DAC_NREGS - 1)) >> 2;
    switch (saddr) {
    case 0:
blueswir1 authored
429
430
431
        s->dac_index = val >> 24;
        s->dac_state = 0;
        break;
bellard authored
432
    case 1:
blueswir1 authored
433
434
435
        switch (s->dac_state) {
        case 0:
            s->r[s->dac_index] = val >> 24;
436
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
blueswir1 authored
437
438
439
440
            s->dac_state++;
            break;
        case 1:
            s->g[s->dac_index] = val >> 24;
441
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
blueswir1 authored
442
443
444
445
            s->dac_state++;
            break;
        case 2:
            s->b[s->dac_index] = val >> 24;
446
            update_palette_entries(s, s->dac_index, s->dac_index + 1);
blueswir1 authored
447
            s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
blueswir1 authored
448
449
450
451
452
        default:
            s->dac_state = 0;
            break;
        }
        break;
bellard authored
453
    default:
blueswir1 authored
454
        break;
bellard authored
455
456
    }
    return;
457
458
}
bellard authored
459
460
461
462
463
464
465
466
467
468
469
470
static CPUReadMemoryFunc *tcx_dac_read[3] = {
    tcx_dac_readl,
    tcx_dac_readl,
    tcx_dac_readl,
};

static CPUWriteMemoryFunc *tcx_dac_write[3] = {
    tcx_dac_writel,
    tcx_dac_writel,
    tcx_dac_writel,
};
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
{
    return 0;
}

static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
                             uint32_t val)
{
}

static CPUReadMemoryFunc *tcx_dummy_read[3] = {
    tcx_dummy_readl,
    tcx_dummy_readl,
    tcx_dummy_readl,
};

static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
    tcx_dummy_writel,
    tcx_dummy_writel,
    tcx_dummy_writel,
};
493
void tcx_init(DisplayState *ds, target_phys_addr_t addr, uint8_t *vram_base,
blueswir1 authored
494
495
              unsigned long vram_offset, int vram_size, int width, int height,
              int depth)
496
497
{
    TCXState *s;
498
    int io_memory, dummy_memory;
blueswir1 authored
499
    int size;
500
501
502

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

    // 8-bit plane
    s->vram = vram_base;
    size = vram_size;
514
    cpu_register_physical_memory(addr + 0x00800000ULL, size, vram_offset);
blueswir1 authored
515
516
    vram_offset += size;
    vram_base += size;
bellard authored
517
bellard authored
518
    io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s);
519
    cpu_register_physical_memory(addr + 0x00200000ULL, TCX_DAC_NREGS, io_memory);
blueswir1 authored
520
521
522
    dummy_memory = cpu_register_io_memory(0, tcx_dummy_read, tcx_dummy_write,
                                          s);
523
    cpu_register_physical_memory(addr + 0x00700000ULL, TCX_TEC_NREGS,
524
                                 dummy_memory);
blueswir1 authored
525
526
527
528
529
    if (depth == 24) {
        // 24-bit plane
        size = vram_size * 4;
        s->vram24 = (uint32_t *)vram_base;
        s->vram24_offset = vram_offset;
530
        cpu_register_physical_memory(addr + 0x02000000ULL, size, vram_offset);
blueswir1 authored
531
532
533
534
535
536
537
        vram_offset += size;
        vram_base += size;

        // Control plane
        size = vram_size * 4;
        s->cplane = (uint32_t *)vram_base;
        s->cplane_offset = vram_offset;
538
        cpu_register_physical_memory(addr + 0x0a000000ULL, size, vram_offset);
539
540
        graphic_console_init(s->ds, tcx24_update_display,
                             tcx24_invalidate_display, tcx24_screen_dump, s);
blueswir1 authored
541
    } else {
542
        cpu_register_physical_memory(addr + 0x00300000ULL, TCX_THC_NREGS_8,
543
                                     dummy_memory);
blueswir1 authored
544
545
546
        graphic_console_init(s->ds, tcx_update_display, tcx_invalidate_display,
                             tcx_screen_dump, s);
    }
547
    // NetBSD writes here even with 8-bit display
548
    cpu_register_physical_memory(addr + 0x00301000ULL, TCX_THC_NREGS_24,
549
                                 dummy_memory);
bellard authored
550
blueswir1 authored
551
    register_savevm("tcx", addr, 4, tcx_save, tcx_load, s);
bellard authored
552
553
    qemu_register_reset(tcx_reset, s);
    tcx_reset(s);
bellard authored
554
    dpy_resize(s->ds, width, height);
555
556
}
557
static void tcx_screen_dump(void *opaque, const char *filename)
bellard authored
558
{
bellard authored
559
    TCXState *s = opaque;
bellard authored
560
    FILE *f;
bellard authored
561
    uint8_t *d, *d1, v;
bellard authored
562
563
564
565
    int y, x;

    f = fopen(filename, "wb");
    if (!f)
bellard authored
566
        return;
bellard authored
567
568
569
    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
    d1 = s->vram;
    for(y = 0; y < s->height; y++) {
bellard authored
570
        d = d1;
bellard authored
571
        for(x = 0; x < s->width; x++) {
bellard authored
572
            v = *d;
bellard authored
573
574
575
            fputc(s->r[v], f);
            fputc(s->g[v], f);
            fputc(s->b[v], f);
bellard authored
576
577
            d++;
        }
bellard authored
578
        d1 += MAXX;
bellard authored
579
580
581
582
583
    }
    fclose(f);
    return;
}
blueswir1 authored
584
585
586
587
588
589
590
static void tcx24_screen_dump(void *opaque, const char *filename)
{
    TCXState *s = opaque;
    FILE *f;
    uint8_t *d, *d1, v;
    uint32_t *s24, *cptr, dval;
    int y, x;
bellard authored
591
blueswir1 authored
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
    f = fopen(filename, "wb");
    if (!f)
        return;
    fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
    d1 = s->vram;
    s24 = s->vram24;
    cptr = s->cplane;
    for(y = 0; y < s->height; y++) {
        d = d1;
        for(x = 0; x < s->width; x++, d++, s24++) {
            if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
                dval = *s24 & 0x00ffffff;
                fputc((dval >> 16) & 0xff, f);
                fputc((dval >> 8) & 0xff, f);
                fputc(dval & 0xff, f);
            } else {
                v = *d;
                fputc(s->r[v], f);
                fputc(s->g[v], f);
                fputc(s->b[v], f);
            }
        }
        d1 += MAXX;
    }
    fclose(f);
    return;
}