symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/tcx.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     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(ds_get_bits_per_pixel(s->ds)) {
       
    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 (ds_get_bits_per_pixel(ts->ds) == 0)
       
   204         return;
       
   205     page = ts->vram_offset;
       
   206     y_start = -1;
       
   207     page_min = 0xffffffff;
       
   208     page_max = 0;
       
   209     d = ds_get_data(ts->ds);
       
   210     s = ts->vram;
       
   211     dd = ds_get_linesize(ts->ds);
       
   212     ds = 1024;
       
   213 
       
   214     switch (ds_get_bits_per_pixel(ts->ds)) {
       
   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 (ds_get_bits_per_pixel(ts->ds) != 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 = ds_get_data(ts->ds);
       
   290     s = ts->vram;
       
   291     s24 = ts->vram24;
       
   292     cptr = ts->cplane;
       
   293     dd = ds_get_linesize(ts->ds);
       
   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, &s->height);
       
   376     qemu_put_be16s(f, &s->width);
       
   377     qemu_put_be16s(f, &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, &dummy);
       
   395         qemu_get_be32s(f, &dummy);
       
   396         qemu_get_be32s(f, &dummy);
       
   397     }
       
   398     qemu_get_be16s(f, &s->height);
       
   399     qemu_get_be16s(f, &s->width);
       
   400     qemu_get_be16s(f, &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 
       
   441     switch (addr) {
       
   442     case 0:
       
   443         s->dac_index = val >> 24;
       
   444         s->dac_state = 0;
       
   445         break;
       
   446     case 4:
       
   447         switch (s->dac_state) {
       
   448         case 0:
       
   449             s->r[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 1:
       
   454             s->g[s->dac_index] = val >> 24;
       
   455             update_palette_entries(s, s->dac_index, s->dac_index + 1);
       
   456             s->dac_state++;
       
   457             break;
       
   458         case 2:
       
   459             s->b[s->dac_index] = val >> 24;
       
   460             update_palette_entries(s, s->dac_index, s->dac_index + 1);
       
   461             s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
       
   462         default:
       
   463             s->dac_state = 0;
       
   464             break;
       
   465         }
       
   466         break;
       
   467     default:
       
   468         break;
       
   469     }
       
   470     return;
       
   471 }
       
   472 
       
   473 static CPUReadMemoryFunc *tcx_dac_read[3] = {
       
   474     NULL,
       
   475     NULL,
       
   476     tcx_dac_readl,
       
   477 };
       
   478 
       
   479 static CPUWriteMemoryFunc *tcx_dac_write[3] = {
       
   480     NULL,
       
   481     NULL,
       
   482     tcx_dac_writel,
       
   483 };
       
   484 
       
   485 static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
       
   486 {
       
   487     return 0;
       
   488 }
       
   489 
       
   490 static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
       
   491                              uint32_t val)
       
   492 {
       
   493 }
       
   494 
       
   495 static CPUReadMemoryFunc *tcx_dummy_read[3] = {
       
   496     NULL,
       
   497     NULL,
       
   498     tcx_dummy_readl,
       
   499 };
       
   500 
       
   501 static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
       
   502     NULL,
       
   503     NULL,
       
   504     tcx_dummy_writel,
       
   505 };
       
   506 
       
   507 void tcx_init(DisplayState *ds, target_phys_addr_t addr, uint8_t *vram_base,
       
   508               unsigned long vram_offset, int vram_size, int width, int height,
       
   509               int depth)
       
   510 {
       
   511     TCXState *s;
       
   512     int io_memory, dummy_memory;
       
   513     int size;
       
   514 
       
   515     s = qemu_mallocz(sizeof(TCXState));
       
   516     if (!s)
       
   517         return;
       
   518     s->ds = ds;
       
   519     s->addr = addr;
       
   520     s->vram_offset = vram_offset;
       
   521     s->width = width;
       
   522     s->height = height;
       
   523     s->depth = depth;
       
   524 
       
   525     // 8-bit plane
       
   526     s->vram = vram_base;
       
   527     size = vram_size;
       
   528     cpu_register_physical_memory(addr + 0x00800000ULL, size, vram_offset);
       
   529     vram_offset += size;
       
   530     vram_base += size;
       
   531 
       
   532     io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s);
       
   533     cpu_register_physical_memory(addr + 0x00200000ULL, TCX_DAC_NREGS,
       
   534                                  io_memory);
       
   535 
       
   536     dummy_memory = cpu_register_io_memory(0, tcx_dummy_read, tcx_dummy_write,
       
   537                                           s);
       
   538     cpu_register_physical_memory(addr + 0x00700000ULL, TCX_TEC_NREGS,
       
   539                                  dummy_memory);
       
   540     if (depth == 24) {
       
   541         // 24-bit plane
       
   542         size = vram_size * 4;
       
   543         s->vram24 = (uint32_t *)vram_base;
       
   544         s->vram24_offset = vram_offset;
       
   545         cpu_register_physical_memory(addr + 0x02000000ULL, size, vram_offset);
       
   546         vram_offset += size;
       
   547         vram_base += size;
       
   548 
       
   549         // Control plane
       
   550         size = vram_size * 4;
       
   551         s->cplane = (uint32_t *)vram_base;
       
   552         s->cplane_offset = vram_offset;
       
   553         cpu_register_physical_memory(addr + 0x0a000000ULL, size, vram_offset);
       
   554         s->console = graphic_console_init(s->ds, tcx24_update_display,
       
   555                                           tcx24_invalidate_display,
       
   556                                           tcx24_screen_dump, NULL, s);
       
   557     } else {
       
   558         cpu_register_physical_memory(addr + 0x00300000ULL, TCX_THC_NREGS_8,
       
   559                                      dummy_memory);
       
   560         s->console = graphic_console_init(s->ds, tcx_update_display,
       
   561                                           tcx_invalidate_display,
       
   562                                           tcx_screen_dump, NULL, s);
       
   563     }
       
   564     // NetBSD writes here even with 8-bit display
       
   565     cpu_register_physical_memory(addr + 0x00301000ULL, TCX_THC_NREGS_24,
       
   566                                  dummy_memory);
       
   567 
       
   568     register_savevm("tcx", addr, 4, tcx_save, tcx_load, s);
       
   569     qemu_register_reset(tcx_reset, s);
       
   570     tcx_reset(s);
       
   571     qemu_console_resize(s->console, width, height);
       
   572 }
       
   573 
       
   574 static void tcx_screen_dump(void *opaque, const char *filename)
       
   575 {
       
   576     TCXState *s = opaque;
       
   577     FILE *f;
       
   578     uint8_t *d, *d1, v;
       
   579     int y, x;
       
   580 
       
   581     f = fopen(filename, "wb");
       
   582     if (!f)
       
   583         return;
       
   584     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
       
   585     d1 = s->vram;
       
   586     for(y = 0; y < s->height; y++) {
       
   587         d = d1;
       
   588         for(x = 0; x < s->width; x++) {
       
   589             v = *d;
       
   590             fputc(s->r[v], f);
       
   591             fputc(s->g[v], f);
       
   592             fputc(s->b[v], f);
       
   593             d++;
       
   594         }
       
   595         d1 += MAXX;
       
   596     }
       
   597     fclose(f);
       
   598     return;
       
   599 }
       
   600 
       
   601 static void tcx24_screen_dump(void *opaque, const char *filename)
       
   602 {
       
   603     TCXState *s = opaque;
       
   604     FILE *f;
       
   605     uint8_t *d, *d1, v;
       
   606     uint32_t *s24, *cptr, dval;
       
   607     int y, x;
       
   608 
       
   609     f = fopen(filename, "wb");
       
   610     if (!f)
       
   611         return;
       
   612     fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
       
   613     d1 = s->vram;
       
   614     s24 = s->vram24;
       
   615     cptr = s->cplane;
       
   616     for(y = 0; y < s->height; y++) {
       
   617         d = d1;
       
   618         for(x = 0; x < s->width; x++, d++, s24++) {
       
   619             if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
       
   620                 dval = *s24 & 0x00ffffff;
       
   621                 fputc((dval >> 16) & 0xff, f);
       
   622                 fputc((dval >> 8) & 0xff, f);
       
   623                 fputc(dval & 0xff, f);
       
   624             } else {
       
   625                 v = *d;
       
   626                 fputc(s->r[v], f);
       
   627                 fputc(s->g[v], f);
       
   628                 fputc(s->b[v], f);
       
   629             }
       
   630         }
       
   631         d1 += MAXX;
       
   632     }
       
   633     fclose(f);
       
   634     return;
       
   635 }