1 // (c) Copyright 2004 Dave, All rights reserved.
\r
2 // (c) Copyright 2006-2010 notaz, All rights reserved.
\r
3 // Free for non-commercial use.
\r
5 // For commercial use, separate licencing terms must be obtained.
\r
8 #include "pico_int.h"
\r
9 #include <zlib/zlib.h>
\r
11 #include "../cpu/sh2/sh2.h"
\r
12 #include "sound/ym2612.h"
\r
15 extern int *sn76496_regs;
\r
17 typedef size_t (arearw)(void *p, size_t _size, size_t _n, void *file);
\r
18 typedef size_t (areaeof)(void *file);
\r
19 typedef int (areaseek)(void *file, long offset, int whence);
\r
20 typedef int (areaclose)(void *file);
\r
22 static arearw *areaRead;
\r
23 static arearw *areaWrite;
\r
24 static areaeof *areaEof;
\r
25 static areaseek *areaSeek;
\r
26 static areaclose *areaClose;
\r
28 carthw_state_chunk *carthw_chunks;
\r
29 void (*PicoStateProgressCB)(const char *str);
\r
30 void (*PicoLoadStateHook)(void);
\r
34 static size_t gzRead2(void *p, size_t _size, size_t _n, void *file)
\r
36 return gzread(file, p, _size * _n);
\r
39 static size_t gzWrite2(void *p, size_t _size, size_t _n, void *file)
\r
41 return gzwrite(file, p, _size * _n);
\r
44 static void set_cbs(int gz)
\r
48 areaWrite = gzWrite2;
\r
49 areaEof = (areaeof *) gzeof;
\r
50 areaSeek = (areaseek *) gzseek;
\r
51 areaClose = (areaclose *) gzclose;
\r
53 areaRead = (arearw *) fread;
\r
54 areaWrite = (arearw *) fwrite;
\r
55 areaEof = (areaeof *) feof;
\r
56 areaSeek = (areaseek *) fseek;
\r
57 areaClose = (areaclose *) fclose;
\r
61 static void *open_save_file(const char *fname, int is_save)
\r
63 int len = strlen(fname);
\r
66 if (len > 3 && strcmp(fname + len - 3, ".gz") == 0)
\r
68 if ( (afile = gzopen(fname, is_save ? "wb" : "rb")) ) {
\r
71 gzsetparams(afile, 9, Z_DEFAULT_STRATEGY);
\r
76 if ( (afile = fopen(fname, is_save ? "wb" : "rb")) ) {
\r
84 // legacy savestate loading
\r
85 #define SCANP(f, x) areaRead(&Pico.x, sizeof(Pico.x), 1, f)
\r
87 static int state_load_legacy(void *file)
\r
89 unsigned char head[32];
\r
90 unsigned char cpu[0x60];
\r
91 unsigned char cpu_z80[Z80_STATE_SIZE];
\r
95 memset(&cpu,0,sizeof(cpu));
\r
96 memset(&cpu_z80,0,sizeof(cpu_z80));
\r
98 memset(head, 0, sizeof(head));
\r
99 areaRead(head, sizeof(head), 1, file);
\r
100 if (strcmp((char *)head, "Pico") != 0)
\r
103 elprintf(EL_STATUS, "legacy savestate");
\r
105 // Scan all the memory areas:
\r
110 SCANP(file, vsram);
\r
112 // Pack, scan and unpack the cpu data:
\r
113 areaRead(cpu, sizeof(cpu), 1, file);
\r
114 SekUnpackCpu(cpu, 0);
\r
117 SCANP(file, video);
\r
119 ok = areaRead(cpu_z80, sizeof(cpu_z80), 1, file) == sizeof(cpu_z80);
\r
120 // do not unpack if we fail to load z80 state
\r
121 if (!ok) z80_reset();
\r
122 else z80_unpack(cpu_z80);
\r
124 ym2612_regs = YM2612GetRegs();
\r
125 areaRead(sn76496_regs, 28*4, 1, file);
\r
126 areaRead(ym2612_regs, 0x200+4, 1, file);
\r
127 ym2612_unpack_state();
\r
132 // ---------------------------------------------------------------------------
\r
150 CHUNK_PCM_RAM, // 15
\r
160 CHUNK_IOPORTS, // versions < 1.70 did not save that..
\r
167 CHUNK_SSH2_DATA, // 30
\r
172 CHUNK_SSH2_BIOS, // 35
\r
177 CHUNK_DEFAULT_COUNT,
\r
178 CHUNK_CARTHW_ = CHUNK_CARTHW, // defined in PicoInt
\r
181 static const char * const chunk_names[] = {
\r
226 static int write_chunk(chunk_name_e name, int len, void *data, void *file)
\r
228 size_t bwritten = 0;
\r
229 bwritten += areaWrite(&name, 1, 1, file);
\r
230 bwritten += areaWrite(&len, 1, 4, file);
\r
231 bwritten += areaWrite(data, 1, len, file);
\r
233 return (bwritten == len + 4 + 1);
\r
236 #define CHECKED_WRITE(name,len,data) { \
\r
237 if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) { \
\r
238 strncpy(sbuff + 9, chunk_names[name], sizeof(sbuff) - 9); \
\r
239 PicoStateProgressCB(sbuff); \
\r
241 if (!write_chunk(name, len, data, file)) return 1; \
\r
244 #define CHECKED_WRITE_BUFF(name,buff) { \
\r
245 if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) { \
\r
246 strncpy(sbuff + 9, chunk_names[name], sizeof(sbuff) - 9); \
\r
247 PicoStateProgressCB(sbuff); \
\r
249 if (!write_chunk(name, sizeof(buff), &buff, file)) return 1; \
\r
252 static int state_save(void *file)
\r
254 char sbuff[32] = "Saving.. ";
\r
255 unsigned char buff[0x60], buff_z80[Z80_STATE_SIZE];
\r
256 void *ym2612_regs = YM2612GetRegs();
\r
257 int ver = 0x0170; // not really used..
\r
259 areaWrite("PicoSEXT", 1, 8, file);
\r
260 areaWrite(&ver, 1, 4, file);
\r
262 if (!(PicoAHW & PAHW_SMS)) {
\r
263 memset(buff, 0, sizeof(buff));
\r
264 SekPackCpu(buff, 0);
\r
265 CHECKED_WRITE_BUFF(CHUNK_M68K, buff);
\r
266 CHECKED_WRITE_BUFF(CHUNK_RAM, Pico.ram);
\r
267 CHECKED_WRITE_BUFF(CHUNK_VSRAM, Pico.vsram);
\r
268 CHECKED_WRITE_BUFF(CHUNK_IOPORTS, Pico.ioports);
\r
269 ym2612_pack_state();
\r
270 CHECKED_WRITE(CHUNK_FM, 0x200+4, ym2612_regs);
\r
273 CHECKED_WRITE_BUFF(CHUNK_SMS, Pico.ms);
\r
276 CHECKED_WRITE_BUFF(CHUNK_VRAM, Pico.vram);
\r
277 CHECKED_WRITE_BUFF(CHUNK_ZRAM, Pico.zram);
\r
278 CHECKED_WRITE_BUFF(CHUNK_CRAM, Pico.cram);
\r
279 CHECKED_WRITE_BUFF(CHUNK_MISC, Pico.m);
\r
280 CHECKED_WRITE_BUFF(CHUNK_VIDEO, Pico.video);
\r
282 z80_pack(buff_z80);
\r
283 CHECKED_WRITE_BUFF(CHUNK_Z80, buff_z80);
\r
284 CHECKED_WRITE(CHUNK_PSG, 28*4, sn76496_regs);
\r
286 if (PicoAHW & PAHW_MCD)
\r
288 memset(buff, 0, sizeof(buff));
\r
289 SekPackCpu(buff, 1);
\r
290 if (Pico_mcd->s68k_regs[3] & 4) // 1M mode?
\r
291 wram_1M_to_2M(Pico_mcd->word_ram2M);
\r
292 Pico_mcd->m.hint_vector = *(unsigned short *)(Pico_mcd->bios + 0x72);
\r
294 CHECKED_WRITE_BUFF(CHUNK_S68K, buff);
\r
295 CHECKED_WRITE_BUFF(CHUNK_PRG_RAM, Pico_mcd->prg_ram);
\r
296 CHECKED_WRITE_BUFF(CHUNK_WORD_RAM, Pico_mcd->word_ram2M); // in 2M format
\r
297 CHECKED_WRITE_BUFF(CHUNK_PCM_RAM, Pico_mcd->pcm_ram);
\r
298 CHECKED_WRITE_BUFF(CHUNK_BRAM, Pico_mcd->bram);
\r
299 CHECKED_WRITE_BUFF(CHUNK_GA_REGS, Pico_mcd->s68k_regs); // GA regs, not CPU regs
\r
300 CHECKED_WRITE_BUFF(CHUNK_PCM, Pico_mcd->pcm);
\r
301 CHECKED_WRITE_BUFF(CHUNK_CDD, Pico_mcd->cdd);
\r
302 CHECKED_WRITE_BUFF(CHUNK_CDC, Pico_mcd->cdc);
\r
303 CHECKED_WRITE_BUFF(CHUNK_SCD, Pico_mcd->scd);
\r
304 CHECKED_WRITE_BUFF(CHUNK_RC, Pico_mcd->rot_comp);
\r
305 CHECKED_WRITE_BUFF(CHUNK_MISC_CD, Pico_mcd->m);
\r
307 if (Pico_mcd->s68k_regs[3] & 4) // convert back
\r
308 wram_2M_to_1M(Pico_mcd->word_ram2M);
\r
311 if (PicoAHW & PAHW_32X)
\r
313 unsigned char cpubuff[SH2_STATE_SIZE];
\r
315 memset(cpubuff, 0, sizeof(cpubuff));
\r
317 sh2_pack(&sh2s[0], cpubuff);
\r
318 CHECKED_WRITE_BUFF(CHUNK_MSH2, cpubuff);
\r
319 CHECKED_WRITE_BUFF(CHUNK_MSH2_DATA, Pico32xMem->data_array[0]);
\r
320 CHECKED_WRITE_BUFF(CHUNK_MSH2_PERI, Pico32xMem->sh2_peri_regs[0]);
\r
322 sh2_pack(&sh2s[1], cpubuff);
\r
323 CHECKED_WRITE_BUFF(CHUNK_SSH2, cpubuff);
\r
324 CHECKED_WRITE_BUFF(CHUNK_SSH2_DATA, Pico32xMem->data_array[1]);
\r
325 CHECKED_WRITE_BUFF(CHUNK_SSH2_PERI, Pico32xMem->sh2_peri_regs[1]);
\r
327 CHECKED_WRITE_BUFF(CHUNK_32XSYS, Pico32x);
\r
328 CHECKED_WRITE_BUFF(CHUNK_M68K_BIOS, Pico32xMem->m68k_rom);
\r
329 CHECKED_WRITE_BUFF(CHUNK_MSH2_BIOS, Pico32xMem->sh2_rom_m);
\r
330 CHECKED_WRITE_BUFF(CHUNK_SSH2_BIOS, Pico32xMem->sh2_rom_s);
\r
331 CHECKED_WRITE_BUFF(CHUNK_SDRAM, Pico32xMem->sdram);
\r
332 CHECKED_WRITE_BUFF(CHUNK_DRAM, Pico32xMem->dram);
\r
333 CHECKED_WRITE_BUFF(CHUNK_32XPAL, Pico32xMem->pal);
\r
336 if (carthw_chunks != NULL)
\r
338 carthw_state_chunk *chwc;
\r
339 if (PicoStateProgressCB)
\r
340 PicoStateProgressCB("Saving.. cart hw state");
\r
341 for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++)
\r
342 CHECKED_WRITE(chwc->chunk, chwc->size, chwc->ptr);
\r
348 static int g_read_offs = 0;
\r
350 #define R_ERROR_RETURN(error) \
\r
352 elprintf(EL_STATUS, "load_state @ %x: " error, g_read_offs); \
\r
356 // when is eof really set?
\r
357 #define CHECKED_READ(len,data) { \
\r
358 if (areaRead(data, 1, len, file) != len) { \
\r
359 if (len == 1 && areaEof(file)) goto readend; \
\r
360 R_ERROR_RETURN("areaRead: premature EOF\n"); \
\r
363 g_read_offs += len; \
\r
366 #define CHECKED_READ2(len2,data) { \
\r
367 if (len2 != len) { \
\r
368 elprintf(EL_STATUS, "unexpected len %i, wanted %i (%s)", len, len2, #len2); \
\r
369 if (len > len2) R_ERROR_RETURN("failed."); \
\r
370 /* else read anyway and hope for the best.. */ \
\r
372 CHECKED_READ(len, data); \
\r
375 #define CHECKED_READ_BUFF(buff) CHECKED_READ2(sizeof(buff), &buff);
\r
377 static int state_load(void *file)
\r
379 unsigned char buff_m68k[0x60], buff_s68k[0x60];
\r
380 unsigned char buff_z80[Z80_STATE_SIZE];
\r
381 unsigned char buff_sh2[SH2_STATE_SIZE];
\r
382 unsigned char chunk;
\r
387 memset(buff_m68k, 0, sizeof(buff_m68k));
\r
388 memset(buff_s68k, 0, sizeof(buff_s68k));
\r
389 memset(buff_z80, 0, sizeof(buff_z80));
\r
392 CHECKED_READ(8, header);
\r
393 if (strncmp(header, "PicoSMCD", 8) && strncmp(header, "PicoSEXT", 8))
\r
394 R_ERROR_RETURN("bad header");
\r
395 CHECKED_READ(4, &ver);
\r
397 while (!areaEof(file))
\r
399 CHECKED_READ(1, &chunk);
\r
400 CHECKED_READ(4, &len);
\r
401 if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");
\r
402 if (CHUNK_S68K <= chunk && chunk <= CHUNK_MISC_CD && !(PicoAHW & PAHW_MCD))
\r
403 R_ERROR_RETURN("cd chunk in non CD state?");
\r
404 if (CHUNK_MSH2 <= chunk && chunk <= CHUNK_32XPAL && !(PicoAHW & PAHW_32X))
\r
405 R_ERROR_RETURN("32x chunk in non 32x state?");
\r
410 CHECKED_READ_BUFF(buff_m68k);
\r
414 CHECKED_READ_BUFF(buff_z80);
\r
417 case CHUNK_RAM: CHECKED_READ_BUFF(Pico.ram); break;
\r
418 case CHUNK_VRAM: CHECKED_READ_BUFF(Pico.vram); break;
\r
419 case CHUNK_ZRAM: CHECKED_READ_BUFF(Pico.zram); break;
\r
420 case CHUNK_CRAM: CHECKED_READ_BUFF(Pico.cram); break;
\r
421 case CHUNK_VSRAM: CHECKED_READ_BUFF(Pico.vsram); break;
\r
422 case CHUNK_MISC: CHECKED_READ_BUFF(Pico.m); break;
\r
423 case CHUNK_VIDEO: CHECKED_READ_BUFF(Pico.video); break;
\r
424 case CHUNK_IOPORTS: CHECKED_READ_BUFF(Pico.ioports); break;
\r
425 case CHUNK_PSG: CHECKED_READ2(28*4, sn76496_regs); break;
\r
427 ym2612_regs = YM2612GetRegs();
\r
428 CHECKED_READ2(0x200+4, ym2612_regs);
\r
429 ym2612_unpack_state();
\r
433 CHECKED_READ_BUFF(Pico.ms);
\r
438 CHECKED_READ_BUFF(buff_s68k);
\r
441 case CHUNK_PRG_RAM: CHECKED_READ_BUFF(Pico_mcd->prg_ram); break;
\r
442 case CHUNK_WORD_RAM: CHECKED_READ_BUFF(Pico_mcd->word_ram2M); break;
\r
443 case CHUNK_PCM_RAM: CHECKED_READ_BUFF(Pico_mcd->pcm_ram); break;
\r
444 case CHUNK_BRAM: CHECKED_READ_BUFF(Pico_mcd->bram); break;
\r
445 case CHUNK_GA_REGS: CHECKED_READ_BUFF(Pico_mcd->s68k_regs); break;
\r
446 case CHUNK_PCM: CHECKED_READ_BUFF(Pico_mcd->pcm); break;
\r
447 case CHUNK_CDD: CHECKED_READ_BUFF(Pico_mcd->cdd); break;
\r
448 case CHUNK_CDC: CHECKED_READ_BUFF(Pico_mcd->cdc); break;
\r
449 case CHUNK_SCD: CHECKED_READ_BUFF(Pico_mcd->scd); break;
\r
450 case CHUNK_RC: CHECKED_READ_BUFF(Pico_mcd->rot_comp); break;
\r
451 case CHUNK_MISC_CD: CHECKED_READ_BUFF(Pico_mcd->m); break;
\r
455 CHECKED_READ_BUFF(buff_sh2);
\r
456 sh2_unpack(&sh2s[0], buff_sh2);
\r
460 CHECKED_READ_BUFF(buff_sh2);
\r
461 sh2_unpack(&sh2s[1], buff_sh2);
\r
464 case CHUNK_MSH2_DATA: CHECKED_READ_BUFF(Pico32xMem->data_array[0]); break;
\r
465 case CHUNK_MSH2_PERI: CHECKED_READ_BUFF(Pico32xMem->sh2_peri_regs[0]); break;
\r
466 case CHUNK_SSH2_DATA: CHECKED_READ_BUFF(Pico32xMem->data_array[1]); break;
\r
467 case CHUNK_SSH2_PERI: CHECKED_READ_BUFF(Pico32xMem->sh2_peri_regs[1]); break;
\r
468 case CHUNK_32XSYS: CHECKED_READ_BUFF(Pico32x); break;
\r
469 case CHUNK_M68K_BIOS: CHECKED_READ_BUFF(Pico32xMem->m68k_rom); break;
\r
470 case CHUNK_MSH2_BIOS: CHECKED_READ_BUFF(Pico32xMem->sh2_rom_m); break;
\r
471 case CHUNK_SSH2_BIOS: CHECKED_READ_BUFF(Pico32xMem->sh2_rom_s); break;
\r
472 case CHUNK_SDRAM: CHECKED_READ_BUFF(Pico32xMem->sdram); break;
\r
473 case CHUNK_DRAM: CHECKED_READ_BUFF(Pico32xMem->dram); break;
\r
474 case CHUNK_32XPAL: CHECKED_READ_BUFF(Pico32xMem->pal); break;
\r
477 if (carthw_chunks != NULL)
\r
479 carthw_state_chunk *chwc;
\r
480 for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++) {
\r
481 if (chwc->chunk == chunk) {
\r
482 CHECKED_READ2(chwc->size, chwc->ptr);
\r
487 elprintf(EL_STATUS, "load_state: skipping unknown chunk %i of size %i", chunk, len);
\r
488 areaSeek(file, len, SEEK_CUR);
\r
495 if (PicoAHW & PAHW_SMS)
\r
496 PicoStateLoadedMS();
\r
498 if (PicoAHW & PAHW_MCD)
\r
500 PicoMemStateLoaded();
\r
502 if (!(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1))
\r
505 SekUnpackCpu(buff_s68k, 1);
\r
508 // must unpack 68k and z80 after banks are set up
\r
509 if (!(PicoAHW & PAHW_SMS))
\r
510 SekUnpackCpu(buff_m68k, 0);
\r
512 z80_unpack(buff_z80);
\r
514 if (PicoAHW & PAHW_32X)
\r
515 Pico32xStateLoaded();
\r
520 static int state_load_gfx(void *file)
\r
522 int ver, len, found = 0, to_find = 4;
\r
525 if (PicoAHW & PAHW_32X)
\r
529 CHECKED_READ(8, buff);
\r
530 if (strncmp((char *)buff, "PicoSMCD", 8) && strncmp((char *)buff, "PicoSEXT", 8))
\r
531 R_ERROR_RETURN("bad header");
\r
532 CHECKED_READ(4, &ver);
\r
534 while (!areaEof(file) && found < to_find)
\r
536 CHECKED_READ(1, buff);
\r
537 CHECKED_READ(4, &len);
\r
538 if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");
\r
539 if (buff[0] > CHUNK_FM && buff[0] <= CHUNK_MISC_CD && !(PicoAHW & PAHW_MCD))
\r
540 R_ERROR_RETURN("cd chunk in non CD state?");
\r
544 case CHUNK_VRAM: CHECKED_READ_BUFF(Pico.vram); found++; break;
\r
545 case CHUNK_CRAM: CHECKED_READ_BUFF(Pico.cram); found++; break;
\r
546 case CHUNK_VSRAM: CHECKED_READ_BUFF(Pico.vsram); found++; break;
\r
547 case CHUNK_VIDEO: CHECKED_READ_BUFF(Pico.video); found++; break;
\r
550 if (Pico32xMem != NULL)
\r
551 CHECKED_READ_BUFF(Pico32xMem->dram);
\r
555 if (Pico32xMem != NULL)
\r
556 CHECKED_READ_BUFF(Pico32xMem->pal);
\r
557 Pico32x.dirty_pal = 1;
\r
561 CHECKED_READ_BUFF(Pico32x);
\r
565 areaSeek(file, len, SEEK_CUR);
\r
574 int PicoState(const char *fname, int is_save)
\r
576 void *afile = NULL;
\r
579 afile = open_save_file(fname, is_save);
\r
584 ret = state_save(afile);
\r
586 ret = state_load(afile);
\r
588 areaSeek(afile, 0, SEEK_SET);
\r
589 ret = state_load_legacy(afile);
\r
592 if (PicoLoadStateHook != NULL)
\r
593 PicoLoadStateHook();
\r
594 Pico.m.dirtyPal = 1;
\r
601 int PicoStateLoadGfx(const char *fname)
\r
606 afile = open_save_file(fname, 0);
\r
610 ret = state_load_gfx(afile);
\r
613 areaSeek(afile, 0x10020, SEEK_SET); // skip header and RAM
\r
614 areaRead(Pico.vram, 1, sizeof(Pico.vram), afile);
\r
615 areaSeek(afile, 0x2000, SEEK_CUR);
\r
616 areaRead(Pico.cram, 1, sizeof(Pico.cram), afile);
\r
617 areaRead(Pico.vsram, 1, sizeof(Pico.vsram), afile);
\r
618 areaSeek(afile, 0x221a0, SEEK_SET);
\r
619 areaRead(&Pico.video, 1, sizeof(Pico.video), afile);
\r
628 unsigned short vram[0x8000];
\r
629 unsigned short cram[0x40];
\r
630 unsigned short vsram[0x40];
\r
632 //struct PicoMisc m;
\r
633 struct PicoVideo video;
\r
636 struct Pico32x p32x;
\r
637 unsigned short dram[2][0x20000/2];
\r
638 unsigned short pal[0x100];
\r
642 // returns data ptr to free() or PicoTmpStateRestore()
\r
643 void *PicoTmpStateSave(void)
\r
645 // gfx only for now
\r
646 struct PicoTmp *t = malloc(sizeof(*t));
\r
650 memcpy(t->vram, Pico.vram, sizeof(Pico.vram));
\r
651 memcpy(t->cram, Pico.cram, sizeof(Pico.cram));
\r
652 memcpy(t->vsram, Pico.vsram, sizeof(Pico.vsram));
\r
653 memcpy(&t->video, &Pico.video, sizeof(Pico.video));
\r
655 if (PicoAHW & PAHW_32X) {
\r
656 memcpy(&t->t32x.p32x, &Pico32x, sizeof(Pico32x));
\r
657 memcpy(t->t32x.dram, Pico32xMem->dram, sizeof(Pico32xMem->dram));
\r
658 memcpy(t->t32x.pal, Pico32xMem->pal, sizeof(Pico32xMem->pal));
\r
664 void PicoTmpStateRestore(void *data)
\r
666 struct PicoTmp *t = data;
\r
670 memcpy(Pico.vram, t->vram, sizeof(Pico.vram));
\r
671 memcpy(Pico.cram, t->cram, sizeof(Pico.cram));
\r
672 memcpy(Pico.vsram, t->vsram, sizeof(Pico.vsram));
\r
673 memcpy(&Pico.video, &t->video, sizeof(Pico.video));
\r
674 Pico.m.dirtyPal = 1;
\r
676 if (PicoAHW & PAHW_32X) {
\r
677 memcpy(&Pico32x, &t->t32x.p32x, sizeof(Pico32x));
\r
678 memcpy(Pico32xMem->dram, t->t32x.dram, sizeof(Pico32xMem->dram));
\r
679 memcpy(Pico32xMem->pal, t->t32x.pal, sizeof(Pico32xMem->pal));
\r
680 Pico32x.dirty_pal = 1;
\r
684 // vim:shiftwidth=2:expandtab
\r