|
|
1.1 root 1: /*
2: * QEMU TCX Frame buffer
1.1.1.4 ! root 3: *
1.1 root 4: * Copyright (c) 2003-2005 Fabrice Bellard
1.1.1.4 ! root 5: *
1.1 root 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: */
1.1.1.4 ! root 24: #include "hw.h"
! 25: #include "sun4m.h"
! 26: #include "console.h"
! 27: #include "pixel_ops.h"
1.1 root 28:
29: #define MAXX 1024
30: #define MAXY 768
31: #define TCX_DAC_NREGS 16
1.1.1.4 ! root 32: #define TCX_THC_NREGS_8 0x081c
! 33: #define TCX_THC_NREGS_24 0x1000
! 34: #define TCX_TEC_NREGS 0x1000
1.1 root 35:
36: typedef struct TCXState {
1.1.1.4 ! root 37: target_phys_addr_t addr;
1.1 root 38: DisplayState *ds;
39: uint8_t *vram;
1.1.1.4 ! root 40: uint32_t *vram24, *cplane;
! 41: ram_addr_t vram_offset, vram24_offset, cplane_offset;
! 42: uint16_t width, height, depth;
1.1 root 43: uint8_t r[256], g[256], b[256];
1.1.1.3 root 44: uint32_t palette[256];
1.1 root 45: uint8_t dac_index, dac_state;
46: } TCXState;
47:
1.1.1.2 root 48: static void tcx_screen_dump(void *opaque, const char *filename);
1.1.1.4 ! root 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);
1.1.1.3 root 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(s->ds->depth) {
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:
1.1.1.4 ! root 63: if (s->ds->bgr)
! 64: s->palette[i] = rgb_to_pixel15bgr(s->r[i], s->g[i], s->b[i]);
! 65: else
! 66: s->palette[i] = rgb_to_pixel15(s->r[i], s->g[i], s->b[i]);
1.1.1.3 root 67: break;
68: case 16:
1.1.1.4 ! root 69: if (s->ds->bgr)
! 70: s->palette[i] = rgb_to_pixel16bgr(s->r[i], s->g[i], s->b[i]);
! 71: else
! 72: s->palette[i] = rgb_to_pixel16(s->r[i], s->g[i], s->b[i]);
1.1.1.3 root 73: break;
74: case 32:
1.1.1.4 ! root 75: if (s->ds->bgr)
! 76: s->palette[i] = rgb_to_pixel32bgr(s->r[i], s->g[i], s->b[i]);
! 77: else
! 78: s->palette[i] = rgb_to_pixel32(s->r[i], s->g[i], s->b[i]);
1.1.1.3 root 79: break;
80: }
81: }
1.1.1.4 ! root 82: if (s->depth == 24)
! 83: tcx24_invalidate_display(s);
! 84: else
! 85: tcx_invalidate_display(s);
1.1.1.3 root 86: }
87:
1.1.1.4 ! root 88: static void tcx_draw_line32(TCXState *s1, uint8_t *d,
! 89: const uint8_t *s, int width)
1.1 root 90: {
91: int x;
92: uint8_t val;
1.1.1.3 root 93: uint32_t *p = (uint32_t *)d;
1.1 root 94:
95: for(x = 0; x < width; x++) {
1.1.1.4 ! root 96: val = *s++;
1.1.1.3 root 97: *p++ = s1->palette[val];
1.1 root 98: }
99: }
100:
1.1.1.4 ! root 101: static void tcx_draw_line16(TCXState *s1, uint8_t *d,
! 102: const uint8_t *s, int width)
1.1 root 103: {
104: int x;
105: uint8_t val;
1.1.1.3 root 106: uint16_t *p = (uint16_t *)d;
1.1 root 107:
108: for(x = 0; x < width; x++) {
1.1.1.4 ! root 109: val = *s++;
1.1.1.3 root 110: *p++ = s1->palette[val];
1.1 root 111: }
112: }
113:
1.1.1.4 ! root 114: static void tcx_draw_line8(TCXState *s1, uint8_t *d,
! 115: const uint8_t *s, int width)
1.1 root 116: {
117: int x;
118: uint8_t val;
119:
120: for(x = 0; x < width; x++) {
1.1.1.4 ! root 121: val = *s++;
1.1.1.3 root 122: *d++ = s1->palette[val];
1.1 root 123: }
124: }
125:
1.1.1.4 ! root 126: static inline void tcx24_draw_line32(TCXState *s1, uint8_t *d,
! 127: const uint8_t *s, int width,
! 128: const uint32_t *cplane,
! 129: const uint32_t *s24)
! 130: {
! 131: int x;
! 132: uint8_t val;
! 133: uint32_t *p = (uint32_t *)d;
! 134: uint32_t dval;
! 135:
! 136: for(x = 0; x < width; x++, s++, s24++) {
! 137: if ((bswap32(*cplane++) & 0xff000000) == 0x03000000) { // 24-bit direct
! 138: dval = bswap32(*s24) & 0x00ffffff;
! 139: } else {
! 140: val = *s;
! 141: dval = s1->palette[val];
! 142: }
! 143: *p++ = dval;
! 144: }
! 145: }
! 146:
! 147: static inline int check_dirty(TCXState *ts, ram_addr_t page, ram_addr_t page24,
! 148: ram_addr_t cpage)
! 149: {
! 150: int ret;
! 151: unsigned int off;
! 152:
! 153: ret = cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG);
! 154: for (off = 0; off < TARGET_PAGE_SIZE * 4; off += TARGET_PAGE_SIZE) {
! 155: ret |= cpu_physical_memory_get_dirty(page24 + off, VGA_DIRTY_FLAG);
! 156: ret |= cpu_physical_memory_get_dirty(cpage + off, VGA_DIRTY_FLAG);
! 157: }
! 158: return ret;
! 159: }
! 160:
! 161: static inline void reset_dirty(TCXState *ts, ram_addr_t page_min,
! 162: ram_addr_t page_max, ram_addr_t page24,
! 163: ram_addr_t cpage)
! 164: {
! 165: cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
! 166: VGA_DIRTY_FLAG);
! 167: page_min -= ts->vram_offset;
! 168: page_max -= ts->vram_offset;
! 169: cpu_physical_memory_reset_dirty(page24 + page_min * 4,
! 170: page24 + page_max * 4 + TARGET_PAGE_SIZE,
! 171: VGA_DIRTY_FLAG);
! 172: cpu_physical_memory_reset_dirty(cpage + page_min * 4,
! 173: cpage + page_max * 4 + TARGET_PAGE_SIZE,
! 174: VGA_DIRTY_FLAG);
! 175: }
! 176:
1.1 root 177: /* Fixed line length 1024 allows us to do nice tricks not possible on
178: VGA... */
1.1.1.2 root 179: static void tcx_update_display(void *opaque)
1.1 root 180: {
181: TCXState *ts = opaque;
1.1.1.3 root 182: ram_addr_t page, page_min, page_max;
183: int y, y_start, dd, ds;
1.1 root 184: uint8_t *d, *s;
1.1.1.4 ! root 185: void (*f)(TCXState *s1, uint8_t *dst, const uint8_t *src, int width);
1.1 root 186:
187: if (ts->ds->depth == 0)
1.1.1.4 ! root 188: return;
1.1 root 189: page = ts->vram_offset;
190: y_start = -1;
1.1.1.3 root 191: page_min = 0xffffffff;
192: page_max = 0;
1.1 root 193: d = ts->ds->data;
194: s = ts->vram;
195: dd = ts->ds->linesize;
196: ds = 1024;
197:
198: switch (ts->ds->depth) {
199: case 32:
1.1.1.4 ! root 200: f = tcx_draw_line32;
! 201: break;
1.1.1.3 root 202: case 15:
203: case 16:
1.1.1.4 ! root 204: f = tcx_draw_line16;
! 205: break;
1.1 root 206: default:
207: case 8:
1.1.1.4 ! root 208: f = tcx_draw_line8;
! 209: break;
1.1 root 210: case 0:
1.1.1.4 ! root 211: return;
1.1 root 212: }
1.1.1.4 ! root 213:
1.1 root 214: for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE) {
1.1.1.4 ! root 215: if (cpu_physical_memory_get_dirty(page, VGA_DIRTY_FLAG)) {
! 216: if (y_start < 0)
1.1 root 217: y_start = y;
218: if (page < page_min)
219: page_min = page;
220: if (page > page_max)
221: page_max = page;
1.1.1.4 ! root 222: f(ts, d, s, ts->width);
! 223: d += dd;
! 224: s += ds;
! 225: f(ts, d, s, ts->width);
! 226: d += dd;
! 227: s += ds;
! 228: f(ts, d, s, ts->width);
! 229: d += dd;
! 230: s += ds;
! 231: f(ts, d, s, ts->width);
! 232: d += dd;
! 233: s += ds;
! 234: } else {
1.1 root 235: if (y_start >= 0) {
236: /* flush to display */
1.1.1.4 ! root 237: dpy_update(ts->ds, 0, y_start,
1.1 root 238: ts->width, y - y_start);
239: y_start = -1;
240: }
1.1.1.4 ! root 241: d += dd * 4;
! 242: s += ds * 4;
! 243: }
1.1 root 244: }
245: if (y_start >= 0) {
1.1.1.4 ! root 246: /* flush to display */
! 247: dpy_update(ts->ds, 0, y_start,
! 248: ts->width, y - y_start);
1.1 root 249: }
250: /* reset modified pages */
1.1.1.3 root 251: if (page_min <= page_max) {
1.1 root 252: cpu_physical_memory_reset_dirty(page_min, page_max + TARGET_PAGE_SIZE,
253: VGA_DIRTY_FLAG);
254: }
255: }
256:
1.1.1.4 ! root 257: static void tcx24_update_display(void *opaque)
! 258: {
! 259: TCXState *ts = opaque;
! 260: ram_addr_t page, page_min, page_max, cpage, page24;
! 261: int y, y_start, dd, ds;
! 262: uint8_t *d, *s;
! 263: uint32_t *cptr, *s24;
! 264:
! 265: if (ts->ds->depth != 32)
! 266: return;
! 267: page = ts->vram_offset;
! 268: page24 = ts->vram24_offset;
! 269: cpage = ts->cplane_offset;
! 270: y_start = -1;
! 271: page_min = 0xffffffff;
! 272: page_max = 0;
! 273: d = ts->ds->data;
! 274: s = ts->vram;
! 275: s24 = ts->vram24;
! 276: cptr = ts->cplane;
! 277: dd = ts->ds->linesize;
! 278: ds = 1024;
! 279:
! 280: for(y = 0; y < ts->height; y += 4, page += TARGET_PAGE_SIZE,
! 281: page24 += TARGET_PAGE_SIZE, cpage += TARGET_PAGE_SIZE) {
! 282: if (check_dirty(ts, page, page24, cpage)) {
! 283: if (y_start < 0)
! 284: y_start = y;
! 285: if (page < page_min)
! 286: page_min = page;
! 287: if (page > page_max)
! 288: page_max = page;
! 289: tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
! 290: d += dd;
! 291: s += ds;
! 292: cptr += ds;
! 293: s24 += ds;
! 294: tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
! 295: d += dd;
! 296: s += ds;
! 297: cptr += ds;
! 298: s24 += ds;
! 299: tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
! 300: d += dd;
! 301: s += ds;
! 302: cptr += ds;
! 303: s24 += ds;
! 304: tcx24_draw_line32(ts, d, s, ts->width, cptr, s24);
! 305: d += dd;
! 306: s += ds;
! 307: cptr += ds;
! 308: s24 += ds;
! 309: } else {
! 310: if (y_start >= 0) {
! 311: /* flush to display */
! 312: dpy_update(ts->ds, 0, y_start,
! 313: ts->width, y - y_start);
! 314: y_start = -1;
! 315: }
! 316: d += dd * 4;
! 317: s += ds * 4;
! 318: cptr += ds * 4;
! 319: s24 += ds * 4;
! 320: }
! 321: }
! 322: if (y_start >= 0) {
! 323: /* flush to display */
! 324: dpy_update(ts->ds, 0, y_start,
! 325: ts->width, y - y_start);
! 326: }
! 327: /* reset modified pages */
! 328: if (page_min <= page_max) {
! 329: reset_dirty(ts, page_min, page_max, page24, cpage);
! 330: }
! 331: }
! 332:
1.1.1.2 root 333: static void tcx_invalidate_display(void *opaque)
1.1 root 334: {
335: TCXState *s = opaque;
336: int i;
337:
338: for (i = 0; i < MAXX*MAXY; i += TARGET_PAGE_SIZE) {
1.1.1.4 ! root 339: cpu_physical_memory_set_dirty(s->vram_offset + i);
! 340: }
! 341: }
! 342:
! 343: static void tcx24_invalidate_display(void *opaque)
! 344: {
! 345: TCXState *s = opaque;
! 346: int i;
! 347:
! 348: tcx_invalidate_display(s);
! 349: for (i = 0; i < MAXX*MAXY * 4; i += TARGET_PAGE_SIZE) {
! 350: cpu_physical_memory_set_dirty(s->vram24_offset + i);
! 351: cpu_physical_memory_set_dirty(s->cplane_offset + i);
1.1 root 352: }
353: }
354:
355: static void tcx_save(QEMUFile *f, void *opaque)
356: {
357: TCXState *s = opaque;
1.1.1.4 ! root 358:
1.1 root 359: qemu_put_be16s(f, (uint16_t *)&s->height);
360: qemu_put_be16s(f, (uint16_t *)&s->width);
1.1.1.4 ! root 361: qemu_put_be16s(f, (uint16_t *)&s->depth);
1.1 root 362: qemu_put_buffer(f, s->r, 256);
363: qemu_put_buffer(f, s->g, 256);
364: qemu_put_buffer(f, s->b, 256);
365: qemu_put_8s(f, &s->dac_index);
366: qemu_put_8s(f, &s->dac_state);
367: }
368:
369: static int tcx_load(QEMUFile *f, void *opaque, int version_id)
370: {
371: TCXState *s = opaque;
1.1.1.4 ! root 372: uint32_t dummy;
! 373:
! 374: if (version_id != 3 && version_id != 4)
1.1 root 375: return -EINVAL;
376:
1.1.1.4 ! root 377: if (version_id == 3) {
! 378: qemu_get_be32s(f, (uint32_t *)&dummy);
! 379: qemu_get_be32s(f, (uint32_t *)&dummy);
! 380: qemu_get_be32s(f, (uint32_t *)&dummy);
! 381: }
1.1 root 382: qemu_get_be16s(f, (uint16_t *)&s->height);
383: qemu_get_be16s(f, (uint16_t *)&s->width);
1.1.1.4 ! root 384: qemu_get_be16s(f, (uint16_t *)&s->depth);
1.1 root 385: qemu_get_buffer(f, s->r, 256);
386: qemu_get_buffer(f, s->g, 256);
387: qemu_get_buffer(f, s->b, 256);
388: qemu_get_8s(f, &s->dac_index);
389: qemu_get_8s(f, &s->dac_state);
1.1.1.3 root 390: update_palette_entries(s, 0, 256);
1.1.1.4 ! root 391: if (s->depth == 24)
! 392: tcx24_invalidate_display(s);
! 393: else
! 394: tcx_invalidate_display(s);
! 395:
1.1 root 396: return 0;
397: }
398:
399: static void tcx_reset(void *opaque)
400: {
401: TCXState *s = opaque;
402:
403: /* Initialize palette */
404: memset(s->r, 0, 256);
405: memset(s->g, 0, 256);
406: memset(s->b, 0, 256);
407: s->r[255] = s->g[255] = s->b[255] = 255;
1.1.1.3 root 408: update_palette_entries(s, 0, 256);
1.1 root 409: memset(s->vram, 0, MAXX*MAXY);
1.1.1.4 ! root 410: cpu_physical_memory_reset_dirty(s->vram_offset, s->vram_offset +
! 411: MAXX * MAXY * (1 + 4 + 4), VGA_DIRTY_FLAG);
1.1 root 412: s->dac_index = 0;
413: s->dac_state = 0;
414: }
415:
416: static uint32_t tcx_dac_readl(void *opaque, target_phys_addr_t addr)
417: {
418: return 0;
419: }
420:
421: static void tcx_dac_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
422: {
423: TCXState *s = opaque;
424: uint32_t saddr;
425:
426: saddr = (addr & (TCX_DAC_NREGS - 1)) >> 2;
427: switch (saddr) {
428: case 0:
1.1.1.4 ! root 429: s->dac_index = val >> 24;
! 430: s->dac_state = 0;
! 431: break;
1.1 root 432: case 1:
1.1.1.4 ! root 433: switch (s->dac_state) {
! 434: case 0:
! 435: s->r[s->dac_index] = val >> 24;
1.1.1.3 root 436: update_palette_entries(s, s->dac_index, s->dac_index + 1);
1.1.1.4 ! root 437: s->dac_state++;
! 438: break;
! 439: case 1:
! 440: s->g[s->dac_index] = val >> 24;
1.1.1.3 root 441: update_palette_entries(s, s->dac_index, s->dac_index + 1);
1.1.1.4 ! root 442: s->dac_state++;
! 443: break;
! 444: case 2:
! 445: s->b[s->dac_index] = val >> 24;
1.1.1.3 root 446: update_palette_entries(s, s->dac_index, s->dac_index + 1);
1.1.1.4 ! root 447: s->dac_index = (s->dac_index + 1) & 255; // Index autoincrement
! 448: default:
! 449: s->dac_state = 0;
! 450: break;
! 451: }
! 452: break;
1.1 root 453: default:
1.1.1.4 ! root 454: break;
1.1 root 455: }
456: return;
457: }
458:
459: static CPUReadMemoryFunc *tcx_dac_read[3] = {
1.1.1.4 ! root 460: NULL,
! 461: NULL,
1.1 root 462: tcx_dac_readl,
463: };
464:
465: static CPUWriteMemoryFunc *tcx_dac_write[3] = {
1.1.1.4 ! root 466: NULL,
! 467: NULL,
1.1 root 468: tcx_dac_writel,
469: };
470:
1.1.1.4 ! root 471: static uint32_t tcx_dummy_readl(void *opaque, target_phys_addr_t addr)
! 472: {
! 473: return 0;
! 474: }
! 475:
! 476: static void tcx_dummy_writel(void *opaque, target_phys_addr_t addr,
! 477: uint32_t val)
! 478: {
! 479: }
! 480:
! 481: static CPUReadMemoryFunc *tcx_dummy_read[3] = {
! 482: NULL,
! 483: NULL,
! 484: tcx_dummy_readl,
! 485: };
! 486:
! 487: static CPUWriteMemoryFunc *tcx_dummy_write[3] = {
! 488: NULL,
! 489: NULL,
! 490: tcx_dummy_writel,
! 491: };
! 492:
! 493: void tcx_init(DisplayState *ds, target_phys_addr_t addr, uint8_t *vram_base,
! 494: unsigned long vram_offset, int vram_size, int width, int height,
! 495: int depth)
1.1 root 496: {
497: TCXState *s;
1.1.1.4 ! root 498: int io_memory, dummy_memory;
! 499: int size;
1.1 root 500:
501: s = qemu_mallocz(sizeof(TCXState));
502: if (!s)
1.1.1.2 root 503: return;
1.1 root 504: s->ds = ds;
505: s->addr = addr;
506: s->vram_offset = vram_offset;
507: s->width = width;
508: s->height = height;
1.1.1.4 ! root 509: s->depth = depth;
! 510:
! 511: // 8-bit plane
! 512: s->vram = vram_base;
! 513: size = vram_size;
! 514: cpu_register_physical_memory(addr + 0x00800000ULL, size, vram_offset);
! 515: vram_offset += size;
! 516: vram_base += size;
1.1 root 517:
518: io_memory = cpu_register_io_memory(0, tcx_dac_read, tcx_dac_write, s);
1.1.1.4 ! root 519: cpu_register_physical_memory(addr + 0x00200000ULL, TCX_DAC_NREGS, io_memory);
1.1 root 520:
1.1.1.4 ! root 521: dummy_memory = cpu_register_io_memory(0, tcx_dummy_read, tcx_dummy_write,
! 522: s);
! 523: cpu_register_physical_memory(addr + 0x00700000ULL, TCX_TEC_NREGS,
! 524: dummy_memory);
! 525: if (depth == 24) {
! 526: // 24-bit plane
! 527: size = vram_size * 4;
! 528: s->vram24 = (uint32_t *)vram_base;
! 529: s->vram24_offset = vram_offset;
! 530: cpu_register_physical_memory(addr + 0x02000000ULL, size, vram_offset);
! 531: vram_offset += size;
! 532: vram_base += size;
! 533:
! 534: // Control plane
! 535: size = vram_size * 4;
! 536: s->cplane = (uint32_t *)vram_base;
! 537: s->cplane_offset = vram_offset;
! 538: cpu_register_physical_memory(addr + 0x0a000000ULL, size, vram_offset);
! 539: graphic_console_init(s->ds, tcx24_update_display,
! 540: tcx24_invalidate_display, tcx24_screen_dump, s);
! 541: } else {
! 542: cpu_register_physical_memory(addr + 0x00300000ULL, TCX_THC_NREGS_8,
! 543: dummy_memory);
! 544: graphic_console_init(s->ds, tcx_update_display, tcx_invalidate_display,
! 545: tcx_screen_dump, s);
! 546: }
! 547: // NetBSD writes here even with 8-bit display
! 548: cpu_register_physical_memory(addr + 0x00301000ULL, TCX_THC_NREGS_24,
! 549: dummy_memory);
! 550:
! 551: register_savevm("tcx", addr, 4, tcx_save, tcx_load, s);
1.1 root 552: qemu_register_reset(tcx_reset, s);
553: tcx_reset(s);
554: dpy_resize(s->ds, width, height);
555: }
556:
1.1.1.2 root 557: static void tcx_screen_dump(void *opaque, const char *filename)
1.1 root 558: {
559: TCXState *s = opaque;
560: FILE *f;
561: uint8_t *d, *d1, v;
562: int y, x;
563:
564: f = fopen(filename, "wb");
565: if (!f)
566: return;
567: fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
568: d1 = s->vram;
569: for(y = 0; y < s->height; y++) {
570: d = d1;
571: for(x = 0; x < s->width; x++) {
572: v = *d;
573: fputc(s->r[v], f);
574: fputc(s->g[v], f);
575: fputc(s->b[v], f);
576: d++;
577: }
578: d1 += MAXX;
579: }
580: fclose(f);
581: return;
582: }
583:
1.1.1.4 ! root 584: static void tcx24_screen_dump(void *opaque, const char *filename)
! 585: {
! 586: TCXState *s = opaque;
! 587: FILE *f;
! 588: uint8_t *d, *d1, v;
! 589: uint32_t *s24, *cptr, dval;
! 590: int y, x;
1.1 root 591:
1.1.1.4 ! root 592: f = fopen(filename, "wb");
! 593: if (!f)
! 594: return;
! 595: fprintf(f, "P6\n%d %d\n%d\n", s->width, s->height, 255);
! 596: d1 = s->vram;
! 597: s24 = s->vram24;
! 598: cptr = s->cplane;
! 599: for(y = 0; y < s->height; y++) {
! 600: d = d1;
! 601: for(x = 0; x < s->width; x++, d++, s24++) {
! 602: if ((*cptr++ & 0xff000000) == 0x03000000) { // 24-bit direct
! 603: dval = *s24 & 0x00ffffff;
! 604: fputc((dval >> 16) & 0xff, f);
! 605: fputc((dval >> 8) & 0xff, f);
! 606: fputc(dval & 0xff, f);
! 607: } else {
! 608: v = *d;
! 609: fputc(s->r[v], f);
! 610: fputc(s->g[v], f);
! 611: fputc(s->b[v], f);
! 612: }
! 613: }
! 614: d1 += MAXX;
! 615: }
! 616: fclose(f);
! 617: return;
! 618: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.