Переглянути джерело

32x and sms savestates. Core-independent z80 state. SS bugfixing/refactoring.

git-svn-id: file:///home/notaz/opt/svn/PicoDrive@868 be3aeb3a-fb24-0410-a615-afba39da0efa
notaz 14 роки тому
батько
коміт
b4db550e41
19 змінених файлів з 1113 додано та 845 видалено
  1. 27 14
      cpu/DrZ80/drz80.s
  2. 2 2
      cpu/cz80/cz80.c
  3. 30 0
      cpu/sh2/sh2.c
  4. 5 0
      cpu/sh2/sh2.h
  5. 20 6
      pico/32x/memory.c
  6. 0 1
      pico/32x/pwm.c
  7. 0 300
      pico/area.c
  8. 3 0
      pico/cart.c
  9. 0 321
      pico/cd/area.c
  10. 1 1
      pico/memory.c
  11. 4 4
      pico/memory_arm.s
  12. 4 4
      pico/pico.h
  13. 26 32
      pico/pico_int.h
  14. 79 5
      pico/sek.c
  15. 13 3
      pico/sms.c
  16. 679 0
      pico/state.c
  17. 203 122
      pico/z80if.c
  18. 2 7
      platform/common/common.mak
  19. 15 23
      platform/common/menu.c

+ 27 - 14
cpu/DrZ80/drz80.s

@@ -19,7 +19,6 @@
 
 .if DRZ80_XMAP
       .equ Z80_MEM_SHIFT, 13
-      ;@ note: stack is locked in single bank that z80sp_base points to
 .endif
 
 .if INTERRUPT_MODE
@@ -206,12 +205,30 @@ z80_xmap_rebase_pc:
     bxcc lr
 
 z80_bad_jump:
-    ldr r0,[cpucontext,#z80_read8]
-    ldr r0,[r0]
-    str r0,[cpucontext,#z80pc_base]
+    stmfd sp!,{r3,r12,lr}
+    mov lr,pc
+    ldr pc,[cpucontext,#z80_rebasePC]
     mov z80pc,r0
-    bx lr
-.endif
+    ldmfd sp!,{r3,r12,pc}
+
+z80_xmap_rebase_sp:
+    ldr r1,[cpucontext,#z80_read8]
+    sub r2,r0,#1
+    mov r2,r2,lsl #16
+    mov r2,r2,lsr #(Z80_MEM_SHIFT+16)
+    ldr r1,[r1,r2,lsl #2]
+    movs r1,r1,lsl #1
+    strcc r1,[cpucontext,#z80sp_base]
+    addcc z80sp,r1,r0
+    bxcc lr
+
+    stmfd sp!,{r3,r12,lr}
+    mov lr,pc
+    ldr pc,[cpucontext,#z80_rebaseSP]
+    mov z80sp,r0
+    ldmfd sp!,{r3,r12,pc}
+ 
+.endif @ DRZ80_XMAP
 
 
 .macro fetch cycs
@@ -367,15 +384,13 @@ z80_bad_jump:
      str z80pc,[cpucontext,#z80pc_pointer]
 .endif
 .if DRZ80_XMAP
-    ;@ XXX: SP is locked to single back z80sp_base points to.
-    ldr r1,[cpucontext,#z80sp_base]
-    bic r0,r0,#0x7f<<Z80_MEM_SHIFT
-    add r0,r1,r0
+    bl z80_xmap_rebase_sp
 .else
 	stmfd sp!,{r3,r12}
 	mov lr,pc
 	ldr pc,[cpucontext,#z80_rebaseSP]		;@ external function must rebase sp
 	ldmfd sp!,{r3,r12}
+	mov z80sp,r0
 .endif
 .endm
 ;@----------------------------------------------------------------------------
@@ -4492,7 +4507,6 @@ opcode_3_1:
 .if FAST_Z80SP
 	orr r0,r0,r1, lsl #8
 	rebasesp
-	mov z80sp,r0
 .else
 	orr z80sp,r0,r1, lsl #8
 .endif
@@ -5567,7 +5581,6 @@ opcode_F_9:
 .if FAST_Z80SP
 	mov r0,z80hl, lsr #16
 	rebasesp
-	mov z80sp,r0
 .else
 	mov z80sp,z80hl, lsr #16
 .endif
@@ -7433,7 +7446,6 @@ opcode_DD_F9:
 .if FAST_Z80SP
 	ldrh r0,[z80xx,#2]
 	rebasesp
-	mov z80sp,r0
 .else
 	ldrh z80sp,[z80xx,#2]
 .endif
@@ -7771,8 +7783,9 @@ opcode_ED_7B:
 	readmem16
 .if FAST_Z80SP
 	rebasesp
-.endif
+.else
 	mov z80sp,r0
+.endif
 	fetch 20
 ;@LDI
 opcode_ED_A0:

+ 2 - 2
cpu/cz80/cz80.c

@@ -394,8 +394,8 @@ void Cz80_Set_Reg(cz80_struc *CPU, INT32 regnum, UINT32 val)
 	case CZ80_R:    zR = val; break;
 	case CZ80_I:    zI = val; break;
 	case CZ80_IM:   zIM = val; break;
-	case CZ80_IFF1: zIFF1 = val; break;
-	case CZ80_IFF2: zIFF2 = val; break;
+	case CZ80_IFF1: zIFF1 = val ? (1 << 2) : 0; break;
+	case CZ80_IFF2: zIFF2 = val ? (1 << 2) : 0; break;
 	case CZ80_HALT: CPU->HaltState = val; break;
 	case CZ80_IRQ:  CPU->IRQState = val; break;
 	default: break;

+ 30 - 0
cpu/sh2/sh2.c

@@ -1,4 +1,6 @@
 #include <string.h>
+#include <stddef.h>
+
 #include "sh2.h"
 #include "../debug.h"
 #include "compiler.h"
@@ -85,3 +87,31 @@ void sh2_internal_irq(SH2 *sh2, int level, int vector)
 	sh2->test_irq = 1;
 }
 
+#define SH2_REG_SIZE (offsetof(SH2, macl) + sizeof(sh2->macl))
+
+void sh2_pack(const SH2 *sh2, unsigned char *buff)
+{
+	unsigned int *p;
+
+	memcpy(buff, sh2, SH2_REG_SIZE);
+	p = (void *)(buff + SH2_REG_SIZE);
+
+	p[0] = sh2->pending_int_irq;
+	p[1] = sh2->pending_int_vector;
+	p[2] = sh2->cycles_aim;
+	p[3] = sh2->cycles_done;
+}
+
+void sh2_unpack(SH2 *sh2, const unsigned char *buff)
+{
+	unsigned int *p;
+
+	memcpy(sh2, buff, SH2_REG_SIZE);
+	p = (void *)(buff + SH2_REG_SIZE);
+
+	sh2->pending_int_irq = p[0];
+	sh2->pending_int_vector = p[1];
+	sh2->cycles_aim = p[2];
+	sh2->cycles_done = p[3];
+}
+

+ 5 - 0
cpu/sh2/sh2.h

@@ -64,9 +64,14 @@ void sh2_reset(SH2 *sh2);
 void sh2_irl_irq(SH2 *sh2, int level, int nested_call);
 void sh2_internal_irq(SH2 *sh2, int level, int vector);
 void sh2_do_irq(SH2 *sh2, int level, int vector);
+void sh2_pack(const SH2 *sh2, unsigned char *buff);
+void sh2_unpack(SH2 *sh2, const unsigned char *buff);
 
 void sh2_execute(SH2 *sh2, int cycles);
 
+// regs, pending_int*, cycles, reserved
+#define SH2_STATE_SIZE ((24 + 2 + 2 + 12) * 4)
+
 // pico memhandlers
 // XXX: move somewhere else
 unsigned int REGPARM(2) p32x_sh2_read8(unsigned int a, SH2 *sh2);

+ 20 - 6
pico/32x/memory.c

@@ -352,7 +352,7 @@ static void p32x_vdp_write8(u32 a, u32 d)
       Pico32x.pending_fb = d;
       // if we are blanking and FS bit is changing
       if (((r[0x0a/2] & P32XV_VBLK) || (r[0] & P32XV_Mx) == 0) && ((r[0x0a/2] ^ d) & P32XV_FS)) {
-        r[0x0a/2] ^= 1;
+        r[0x0a/2] ^= P32XV_FS;
 	Pico32xSwapDRAM(d ^ 1);
         elprintf(EL_32X, "VDP FS: %d", r[0x0a/2] & P32XV_FS);
       }
@@ -1329,7 +1329,7 @@ static void get_bios(void)
   // M68K ROM
   if (p32x_bios_g != NULL) {
     elprintf(EL_STATUS|EL_32X, "32x: using supplied 68k BIOS");
-    Byteswap(Pico32xMem->m68k_rom, p32x_bios_g, 0x100);
+    Byteswap(Pico32xMem->m68k_rom, p32x_bios_g, sizeof(Pico32xMem->m68k_rom));
   }
   else {
     // generate 68k ROM
@@ -1351,7 +1351,9 @@ static void get_bios(void)
 #endif
   }
   // fill remaining m68k_rom page with game ROM
-  memcpy(Pico32xMem->m68k_rom + 0x100, Pico.rom + 0x100, sizeof(Pico32xMem->m68k_rom) - 0x100);
+  memcpy(Pico32xMem->m68k_rom_bank + sizeof(Pico32xMem->m68k_rom),
+    Pico.rom + sizeof(Pico32xMem->m68k_rom),
+    sizeof(Pico32xMem->m68k_rom_bank) - sizeof(Pico32xMem->m68k_rom));
 
   // MSH2
   if (p32x_bios_m != NULL) {
@@ -1439,9 +1441,9 @@ void PicoMemSetup32x(void)
   // m68k_map_unmap(0x000000, 0x3fffff);
 
   // MD ROM area
-  rs = sizeof(Pico32xMem->m68k_rom);
-  cpu68k_map_set(m68k_read8_map,   0x000000, rs - 1, Pico32xMem->m68k_rom, 0);
-  cpu68k_map_set(m68k_read16_map,  0x000000, rs - 1, Pico32xMem->m68k_rom, 0);
+  rs = sizeof(Pico32xMem->m68k_rom_bank);
+  cpu68k_map_set(m68k_read8_map,   0x000000, rs - 1, Pico32xMem->m68k_rom_bank, 0);
+  cpu68k_map_set(m68k_read16_map,  0x000000, rs - 1, Pico32xMem->m68k_rom_bank, 0);
   cpu68k_map_set(m68k_write8_map,  0x000000, rs - 1, PicoWrite8_hint, 1); // TODO verify
   cpu68k_map_set(m68k_write16_map, 0x000000, rs - 1, PicoWrite16_hint, 1);
 
@@ -1538,4 +1540,16 @@ void PicoMemSetup32x(void)
 #endif
 }
 
+void Pico32xStateLoaded(void)
+{
+  bank_switch(Pico32x.regs[4 / 2]);
+  Pico32xSwapDRAM((Pico32x.vdp_regs[0x0a / 2] & P32XV_FS) ^ P32XV_FS);
+  p32x_poll_event(3, 0);
+  Pico32x.dirty_pal = 1;
+  memset(Pico32xMem->pwm, 0, sizeof(Pico32xMem->pwm));
+#ifdef DRC_SH2
+  sh2_drc_flush_all();
+#endif
+}
+
 // vim:shiftwidth=2:expandtab

+ 0 - 1
pico/32x/pwm.c

@@ -145,7 +145,6 @@ void p32x_pwm_write16(unsigned int a, unsigned int d)
 
 void p32x_pwm_update(int *buf32, int length, int stereo)
 {
-  extern int pwm_ptr;
   short *pwmb;
   int step;
   int p = 0;

+ 0 - 300
pico/area.c

@@ -1,300 +0,0 @@
-// This is part of Pico Library
-
-// (c) Copyright 2004 Dave, All rights reserved.
-// (c) Copyright 2006 notaz, All rights reserved.
-// Free for non-commercial use.
-
-// For commercial use, separate licencing terms must be obtained.
-
-
-#include "pico_int.h"
-#include <zlib/zlib.h>
-
-// ym2612
-#include "sound/ym2612.h"
-
-// sn76496
-extern int *sn76496_regs;
-
-struct PicoArea { void *data; int len; char *name; };
-
-// strange observation on Symbian OS 9.1, m600 organizer fw r3a06:
-// taking an address of fread or fwrite causes "application could't be started" error
-// on startup randomly depending on binary layout of executable file.
-
-arearw    *areaRead  = (arearw *) 0; // fread;  // read and write function pointers for
-arearw    *areaWrite = (arearw *) 0; // fwrite; // gzip save state ability
-areaeof   *areaEof   = (areaeof *) 0;
-areaseek  *areaSeek  = (areaseek *) 0;
-areaclose *areaClose = (areaclose *) 0;
-
-void (*PicoLoadStateHook)(void) = NULL;
-
-
-// Scan one variable and callback
-static int ScanVar(void *data,int len,char *name,void *PmovFile,int is_write)
-{
-  int ret = 0;
-  if (is_write)
-    ret = areaWrite(data,1,len,PmovFile);
-  else
-    ret = areaRead (data,1,len,PmovFile);
-  return (ret != len);
-}
-
-#define SCAN_VAR(x,y) ScanVar(&x,sizeof(x),y,PmovFile,is_write);
-#define SCANP(x)      ScanVar(&Pico.x,sizeof(Pico.x),#x,PmovFile,is_write);
-
-// Pack the cpu into a common format:
-PICO_INTERNAL void PicoAreaPackCpu(unsigned char *cpu, int is_sub)
-{
-  unsigned int pc=0;
-
-#if defined(EMU_C68K)
-  struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
-  memcpy(cpu,context->d,0x40);
-  pc=context->pc-context->membase;
-  *(unsigned int *)(cpu+0x44)=CycloneGetSr(context);
-  *(unsigned int *)(cpu+0x48)=context->osp;
-  cpu[0x4c] = context->irq;
-  cpu[0x4d] = context->state_flags & 1;
-#elif defined(EMU_M68K)
-  void *oldcontext = m68ki_cpu_p;
-  m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
-  memcpy(cpu,m68ki_cpu_p->dar,0x40);
-  pc=m68ki_cpu_p->pc;
-  *(unsigned int  *)(cpu+0x44)=m68k_get_reg(NULL, M68K_REG_SR);
-  *(unsigned int  *)(cpu+0x48)=m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET];
-  cpu[0x4c] = CPU_INT_LEVEL>>8;
-  cpu[0x4d] = CPU_STOPPED;
-  m68k_set_context(oldcontext);
-#elif defined(EMU_F68K)
-  M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
-  memcpy(cpu,context->dreg,0x40);
-  pc=context->pc;
-  *(unsigned int  *)(cpu+0x44)=context->sr;
-  *(unsigned int  *)(cpu+0x48)=context->asp;
-  cpu[0x4c] = context->interrupts[0];
-  cpu[0x4d] = (context->execinfo & FM68K_HALTED) ? 1 : 0;
-#endif
-
-  *(unsigned int *)(cpu+0x40)=pc;
-}
-
-PICO_INTERNAL void PicoAreaUnpackCpu(unsigned char *cpu, int is_sub)
-{
-#if defined(EMU_C68K)
-  struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
-  CycloneSetSr(context, *(unsigned int *)(cpu+0x44));
-  context->osp=*(unsigned int *)(cpu+0x48);
-  memcpy(context->d,cpu,0x40);
-  context->membase = 0;
-  context->pc = *(unsigned int *)(cpu+0x40);
-  CycloneUnpack(context, NULL); // rebase PC
-  context->irq = cpu[0x4c];
-  context->state_flags = 0;
-  if (cpu[0x4d])
-    context->state_flags |= 1;
-#elif defined(EMU_M68K)
-  void *oldcontext = m68ki_cpu_p;
-  m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
-  m68k_set_reg(M68K_REG_SR, *(unsigned int *)(cpu+0x44));
-  memcpy(m68ki_cpu_p->dar,cpu,0x40);
-  m68ki_cpu_p->pc=*(unsigned int *)(cpu+0x40);
-  m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET]=*(unsigned int *)(cpu+0x48);
-  CPU_INT_LEVEL = cpu[0x4c] << 8;
-  CPU_STOPPED = cpu[0x4d];
-  m68k_set_context(oldcontext);
-#elif defined(EMU_F68K)
-  M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
-  memcpy(context->dreg,cpu,0x40);
-  context->pc =*(unsigned int *)(cpu+0x40);
-  context->sr =*(unsigned int *)(cpu+0x44);
-  context->asp=*(unsigned int *)(cpu+0x48);
-  context->interrupts[0] = cpu[0x4c];
-  context->execinfo &= ~FM68K_HALTED;
-  if (cpu[0x4d]&1) context->execinfo |= FM68K_HALTED;
-#endif
-}
-
-// Scan the contents of the virtual machine's memory for saving or loading
-static int PicoAreaScan(int is_write, unsigned int ver, void *PmovFile)
-{
-  void *ym2612_regs;
-  unsigned char cpu[0x60];
-  unsigned char cpu_z80[0x60];
-  int ret;
-
-  memset(&cpu,0,sizeof(cpu));
-  memset(&cpu_z80,0,sizeof(cpu_z80));
-  Pico.m.scanline=0;
-
-  ym2612_regs = YM2612GetRegs();
-
-  // Scan all the memory areas:
-  SCANP(ram) SCANP(vram) SCANP(zram) SCANP(cram) SCANP(vsram)
-
-  // Pack, scan and unpack the cpu data:
-  if (is_write)
-    PicoAreaPackCpu(cpu, 0);
-  SCAN_VAR(cpu,"cpu")
-  if (!is_write)
-    PicoAreaUnpackCpu(cpu, 0);
-
-  SCAN_VAR(Pico.m    ,"misc")
-  SCAN_VAR(Pico.video,"video")
-
-  // no longer keeping eeprom data in sram_reg
-  if (!is_write && (Pico.m.sram_reg & 4))
-    Pico.m.sram_reg = SRR_MAPPED;
-
-  if (is_write)
-    z80_pack(cpu_z80);
-  ret = SCAN_VAR(cpu_z80,"cpu_z80")
-  // do not unpack if we fail to load z80 state
-  if (!is_write) {
-    if (ret) z80_reset();
-    else     z80_unpack(cpu_z80);
-  }
-
-  ScanVar(sn76496_regs, 28*4, "SN76496state", PmovFile, is_write);
-  if (is_write)
-    ym2612_pack_state();
-  ret = ScanVar(ym2612_regs, 0x200+4, "YM2612state", PmovFile, is_write); // regs + addr line
-  if (!is_write && !ret)
-    ym2612_unpack_state();
-
-  return 0;
-}
-
-// ---------------------------------------------------------------------------
-// Helper code to save/load to a file handle
-
-// XXX: error checking
-// Save or load the state from PmovFile:
-static int PmovState(int is_write, void *PmovFile)
-{
-  unsigned char head[32];
-
-  if ((PicoAHW & PAHW_MCD) || carthw_chunks != NULL)
-  {
-    if (is_write)
-      return PicoCdSaveState(PmovFile);
-    else {
-      int ret = PicoCdLoadState(PmovFile);
-      if (PicoLoadStateHook) PicoLoadStateHook();
-      return ret;
-    }
-  }
-
-  memset(head,0,sizeof(head));
-
-  // not really used..
-  memcpy(head,"Pico",4);
-  *(unsigned int *)(head+0x8)=0x0133;
-  *(unsigned int *)(head+0xc)=0x0021;
-
-  // Scan header:
-  if (is_write)
-    areaWrite(head,1,sizeof(head),PmovFile);
-  else
-    areaRead (head,1,sizeof(head),PmovFile);
-
-  // Scan memory areas:
-  PicoAreaScan(is_write, *(unsigned int *)(head+0x8), PmovFile);
-
-  if (!is_write && PicoLoadStateHook)
-    PicoLoadStateHook();
-
-  return 0;
-}
-
-static size_t gzRead2(void *p, size_t _size, size_t _n, void *file)
-{
-  return gzread(file, p, _n);
-}
-
-static size_t gzWrite2(void *p, size_t _size, size_t _n, void *file)
-{
-  return gzwrite(file, p, _n);
-}
-
-static void set_cbs(int gz)
-{
-  if (gz) {
-    areaRead  = gzRead2;
-    areaWrite = gzWrite2;
-    areaEof   = (areaeof *) gzeof;
-    areaSeek  = (areaseek *) gzseek;
-    areaClose = (areaclose *) gzclose;
-  } else {
-    areaRead  = (arearw *) fread;
-    areaWrite = (arearw *) fwrite;
-    areaEof   = (areaeof *) feof;
-    areaSeek  = (areaseek *) fseek;
-    areaClose = (areaclose *) fclose;
-  }
-}
-
-int PicoState(const char *fname, int is_save)
-{
-  void *afile = NULL;
-  int ret;
-
-  if (strcmp(fname + strlen(fname) - 3, ".gz") == 0)
-  {
-    if ( (afile = gzopen(fname, is_save ? "wb" : "rb")) ) {
-      set_cbs(1);
-      if (is_save)
-        gzsetparams(afile, 9, Z_DEFAULT_STRATEGY);
-    }
-  }
-  else
-  {
-    if ( (afile = fopen(fname, is_save ? "wb" : "rb")) ) {
-      set_cbs(0);
-    }
-  }
-
-  if (afile == NULL)
-    return -1;
-
-  ret = PmovState(is_save, afile);
-  areaClose(afile);
-  if (!is_save)
-    Pico.m.dirtyPal=1;
-
-  return ret;
-}
-
-int PicoStateLoadVDP(const char *fname)
-{
-  void *afile = NULL;
-  if (strcmp(fname + strlen(fname) - 3, ".gz") == 0)
-  {
-    if ( (afile = gzopen(fname, "rb")) )
-      set_cbs(1);
-  }
-  else
-  {
-    if ( (afile = fopen(fname, "rb")) )
-      set_cbs(0);
-  }
-  if (afile == NULL)
-    return -1;
-
-  if ((PicoAHW & PAHW_MCD) || carthw_chunks != NULL) {
-    PicoCdLoadStateGfx(afile);
-  } else {
-    areaSeek(afile, 0x10020, SEEK_SET);  // skip header and RAM in state file
-    areaRead(Pico.vram, 1, sizeof(Pico.vram), afile);
-    areaSeek(afile, 0x2000, SEEK_CUR);
-    areaRead(Pico.cram, 1, sizeof(Pico.cram), afile);
-    areaRead(Pico.vsram, 1, sizeof(Pico.vsram), afile);
-    areaSeek(afile, 0x221a0, SEEK_SET);
-    areaRead(&Pico.video, 1, sizeof(Pico.video), afile);
-  }
-  areaClose(afile);
-  return 0;
-}
-

+ 3 - 0
pico/cart.c

@@ -439,6 +439,9 @@ static unsigned char *PicoCartAlloc(int filesize, int is_sms)
     if (filesize > (1 << s))
       s++;
     rom_alloc_size = 1 << s;
+    // be sure we can cover all address space
+    if (rom_alloc_size < 0x10000)
+      rom_alloc_size = 0x10000;
   }
   else {
     // make alloc size at least sizeof(mcd_state),

+ 0 - 321
pico/cd/area.c

@@ -1,321 +0,0 @@
-// Savestate handling for emulated Sega/Mega CD machine.
-// (c) Copyright 2007, Grazvydas "notaz" Ignotas
-
-
-#include "../pico_int.h"
-
-// ym2612
-#include "../sound/ym2612.h"
-
-// sn76496
-extern int *sn76496_regs;
-
-carthw_state_chunk *carthw_chunks;
-void (*PicoStateProgressCB)(const char *str) = 0;
-
-
-typedef enum {
-	CHUNK_M68K = 1,
-	CHUNK_RAM,
-	CHUNK_VRAM,
-	CHUNK_ZRAM,
-	CHUNK_CRAM,	// 5
-	CHUNK_VSRAM,
-	CHUNK_MISC,
-	CHUNK_VIDEO,
-	CHUNK_Z80,
-	CHUNK_PSG,	// 10
-	CHUNK_FM,
-	// CD stuff
-	CHUNK_S68K,
-	CHUNK_PRG_RAM,
-	CHUNK_WORD_RAM,
-	CHUNK_PCM_RAM,	// 15
-	CHUNK_BRAM,
-	CHUNK_GA_REGS,
-	CHUNK_PCM,
-	CHUNK_CDC,
-	CHUNK_CDD,	// 20
-	CHUNK_SCD,
-	CHUNK_RC,
-	CHUNK_MISC_CD,
-	CHUNK_DEFAULT_COUNT
-	// CHUNK_CARTHW = 64,	// defined in PicoInt
-} chunk_name_e;
-
-
-static char *chunk_names[] = {
-	"INVALID!",
-	"Saving.. M68K state",
-	"Saving.. RAM",
-	"Saving.. VRAM",
-	"Saving.. ZRAM",
-	"Saving.. CRAM",	// 5
-	"Saving.. VSRAM",
-	"Saving.. emu state",
-	"Saving.. VIDEO",
-	"Saving.. Z80 state",
-	"Saving.. PSG",		// 10
-	"Saving.. FM",
-	// CD stuff
-	"Saving.. S68K state",
-	"Saving.. PRG_RAM",
-	"Saving.. WORD_RAM",
-	"Saving.. PCM_RAM",	// 15
-	"Saving.. BRAM",
-	"Saving.. GATE ARRAY regs",
-	"Saving.. PCM state",
-	"Saving.. CDC",
-	"Saving.. CDD",		// 20
-	"Saving.. SCD",
-	"Saving.. GFX chip",
-	"Saving.. MCD state",
-};
-
-
-static int write_chunk(chunk_name_e name, int len, void *data, void *file)
-{
-	size_t bwritten = 0;
-	bwritten += areaWrite(&name, 1, 1, file);
-	bwritten += areaWrite(&len, 1, 4, file);
-	bwritten += areaWrite(data, 1, len, file);
-
-	return (bwritten == len + 4 + 1);
-}
-
-
-#define CHECKED_WRITE(name,len,data) { \
-	if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) \
-		PicoStateProgressCB(chunk_names[name]); \
-	if (!write_chunk(name, len, data, file)) return 1; \
-}
-
-#define CHECKED_WRITE_BUFF(name,buff) { \
-	if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) \
-		PicoStateProgressCB(chunk_names[name]); \
-	if (!write_chunk(name, sizeof(buff), &buff, file)) return 1; \
-}
-
-PICO_INTERNAL int PicoCdSaveState(void *file)
-{
-	unsigned char buff[0x60];
-	void *ym2612_regs = YM2612GetRegs();
-	int ver = 0x0133; // not really used..
-
-	areaWrite("PicoSEXT", 1, 8, file);
-	areaWrite(&ver, 1, 4, file);
-
-	memset(buff, 0, sizeof(buff));
-	PicoAreaPackCpu(buff, 0);
-	CHECKED_WRITE_BUFF(CHUNK_M68K,  buff);
-	CHECKED_WRITE_BUFF(CHUNK_RAM,   Pico.ram);
-	CHECKED_WRITE_BUFF(CHUNK_VRAM,  Pico.vram);
-	CHECKED_WRITE_BUFF(CHUNK_ZRAM,  Pico.zram);
-	CHECKED_WRITE_BUFF(CHUNK_CRAM,  Pico.cram);
-	CHECKED_WRITE_BUFF(CHUNK_VSRAM, Pico.vsram);
-	CHECKED_WRITE_BUFF(CHUNK_MISC,  Pico.m);
-	CHECKED_WRITE_BUFF(CHUNK_VIDEO, Pico.video);
-
-	memset(buff, 0, sizeof(buff));
-	z80_pack(buff);
-	CHECKED_WRITE_BUFF(CHUNK_Z80, buff);
-	CHECKED_WRITE(CHUNK_PSG, 28*4, sn76496_regs);
-	ym2612_pack_state();
-	CHECKED_WRITE(CHUNK_FM, 0x200+4, ym2612_regs);
-
-	if (PicoAHW & PAHW_MCD)
-	{
-		memset(buff, 0, sizeof(buff));
-		PicoAreaPackCpu(buff, 1);
-		if (Pico_mcd->s68k_regs[3]&4) // 1M mode?
-			wram_1M_to_2M(Pico_mcd->word_ram2M);
-        	Pico_mcd->m.hint_vector = *(unsigned short *)(Pico_mcd->bios + 0x72);
-
-		CHECKED_WRITE_BUFF(CHUNK_S68K,     buff);
-		CHECKED_WRITE_BUFF(CHUNK_PRG_RAM,  Pico_mcd->prg_ram);
-		CHECKED_WRITE_BUFF(CHUNK_WORD_RAM, Pico_mcd->word_ram2M); // in 2M format
-		CHECKED_WRITE_BUFF(CHUNK_PCM_RAM,  Pico_mcd->pcm_ram);
-		CHECKED_WRITE_BUFF(CHUNK_BRAM,     Pico_mcd->bram);
-		CHECKED_WRITE_BUFF(CHUNK_GA_REGS,  Pico_mcd->s68k_regs); // GA regs, not CPU regs
-		CHECKED_WRITE_BUFF(CHUNK_PCM,      Pico_mcd->pcm);
-		CHECKED_WRITE_BUFF(CHUNK_CDD,      Pico_mcd->cdd);
-		CHECKED_WRITE_BUFF(CHUNK_CDC,      Pico_mcd->cdc);
-		CHECKED_WRITE_BUFF(CHUNK_SCD,      Pico_mcd->scd);
-		CHECKED_WRITE_BUFF(CHUNK_RC,       Pico_mcd->rot_comp);
-		CHECKED_WRITE_BUFF(CHUNK_MISC_CD,  Pico_mcd->m);
-
-		if (Pico_mcd->s68k_regs[3]&4) // convert back
-			wram_2M_to_1M(Pico_mcd->word_ram2M);
-	}
-
-	if (carthw_chunks != NULL)
-	{
-		carthw_state_chunk *chwc;
-		if (PicoStateProgressCB)
-			PicoStateProgressCB("Saving.. cart hw state");
-		for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++)
-			CHECKED_WRITE(chwc->chunk, chwc->size, chwc->ptr);
-	}
-
-	return 0;
-}
-
-static int g_read_offs = 0;
-
-#define R_ERROR_RETURN(error) \
-{ \
-	elprintf(EL_STATUS, "PicoCdLoadState @ %x: " error, g_read_offs); \
-	return 1; \
-}
-
-// when is eof really set?
-#define CHECKED_READ(len,data) \
-	if (areaRead(data, 1, len, file) != len) { \
-		if (len == 1 && areaEof(file)) goto readend; \
-		R_ERROR_RETURN("areaRead: premature EOF\n"); \
-		return 1; \
-	} \
-	g_read_offs += len;
-
-#define CHECKED_READ2(len2,data) \
-	if (len2 != len) { \
-		elprintf(EL_STATUS, "unexpected len %i, wanted %i (%s)", len, len2, #len2); \
-		if (len > len2) R_ERROR_RETURN("failed."); \
-		/* else read anyway and hope for the best.. */ \
-	} \
-	CHECKED_READ(len, data)
-
-#define CHECKED_READ_BUFF(buff) CHECKED_READ2(sizeof(buff), &buff);
-
-PICO_INTERNAL int PicoCdLoadState(void *file)
-{
-	unsigned char buff[0x60], buff_m68k[0x60], buff_s68k[0x60];
-	int ver, len;
-	void *ym2612_regs = YM2612GetRegs();
-
-	g_read_offs = 0;
-	CHECKED_READ(8, buff);
-	if (strncmp((char *)buff, "PicoSMCD", 8) && strncmp((char *)buff, "PicoSEXT", 8))
-		R_ERROR_RETURN("bad header");
-	CHECKED_READ(4, &ver);
-
-	while (!areaEof(file))
-	{
-		CHECKED_READ(1, buff);
-		CHECKED_READ(4, &len);
-		if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");
-		if (buff[0] > CHUNK_FM && buff[0] <= CHUNK_MISC_CD && !(PicoAHW & PAHW_MCD))
-			R_ERROR_RETURN("cd chunk in non CD state?");
-
-		switch (buff[0])
-		{
-			case CHUNK_M68K:
-				CHECKED_READ_BUFF(buff_m68k);
-				break;
-
-			case CHUNK_Z80:
-				CHECKED_READ_BUFF(buff);
-				z80_unpack(buff);
-				break;
-
-			case CHUNK_RAM:   CHECKED_READ_BUFF(Pico.ram); break;
-			case CHUNK_VRAM:  CHECKED_READ_BUFF(Pico.vram); break;
-			case CHUNK_ZRAM:  CHECKED_READ_BUFF(Pico.zram); break;
-			case CHUNK_CRAM:  CHECKED_READ_BUFF(Pico.cram); break;
-			case CHUNK_VSRAM: CHECKED_READ_BUFF(Pico.vsram); break;
-			case CHUNK_MISC:  CHECKED_READ_BUFF(Pico.m); break;
-			case CHUNK_VIDEO: CHECKED_READ_BUFF(Pico.video); break;
-			case CHUNK_PSG:   CHECKED_READ2(28*4, sn76496_regs); break;
-			case CHUNK_FM:
-				CHECKED_READ2(0x200+4, ym2612_regs);
-				ym2612_unpack_state();
-				break;
-
-			// cd stuff
-			case CHUNK_S68K:
-				CHECKED_READ_BUFF(buff_s68k);
-				break;
-
-			case CHUNK_PRG_RAM:	CHECKED_READ_BUFF(Pico_mcd->prg_ram); break;
-			case CHUNK_WORD_RAM:	CHECKED_READ_BUFF(Pico_mcd->word_ram2M); break;
-			case CHUNK_PCM_RAM:	CHECKED_READ_BUFF(Pico_mcd->pcm_ram); break;
-			case CHUNK_BRAM:	CHECKED_READ_BUFF(Pico_mcd->bram); break;
-			case CHUNK_GA_REGS:	CHECKED_READ_BUFF(Pico_mcd->s68k_regs); break;
-			case CHUNK_PCM:		CHECKED_READ_BUFF(Pico_mcd->pcm); break;
-			case CHUNK_CDD:		CHECKED_READ_BUFF(Pico_mcd->cdd); break;
-			case CHUNK_CDC:		CHECKED_READ_BUFF(Pico_mcd->cdc); break;
-			case CHUNK_SCD:		CHECKED_READ_BUFF(Pico_mcd->scd); break;
-			case CHUNK_RC:		CHECKED_READ_BUFF(Pico_mcd->rot_comp); break;
-			case CHUNK_MISC_CD:	CHECKED_READ_BUFF(Pico_mcd->m); break;
-
-			default:
-				if (carthw_chunks != NULL)
-				{
-					carthw_state_chunk *chwc;
-					for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++) {
-						if (chwc->chunk == buff[0]) {
-							CHECKED_READ2(chwc->size, chwc->ptr);
-							goto breakswitch;
-						}
-					}
-				}
-				elprintf(EL_STATUS, "PicoCdLoadState: skipping unknown chunk %i of size %i", buff[0], len);
-				areaSeek(file, len, SEEK_CUR);
-				break;
-		}
-		breakswitch:;
-	}
-
-readend:
-	if (PicoAHW & PAHW_MCD)
-	{
-		PicoMemStateLoaded();
-
-		if (!(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1))
-			cdda_start_play();
-
-		// must unpack after other CD stuff is loaded
-		PicoAreaUnpackCpu(buff_s68k, 1);
-	}
-	PicoAreaUnpackCpu(buff_m68k, 0);
-
-	return 0;
-}
-
-
-int PicoCdLoadStateGfx(void *file)
-{
-	int ver, len, found = 0;
-	char buff[8];
-
-	g_read_offs = 0;
-	CHECKED_READ(8, buff);
-	if (strncmp((char *)buff, "PicoSMCD", 8) && strncmp((char *)buff, "PicoSEXT", 8))
-		R_ERROR_RETURN("bad header");
-	CHECKED_READ(4, &ver);
-
-	while (!areaEof(file) && found < 4)
-	{
-		CHECKED_READ(1, buff);
-		CHECKED_READ(4, &len);
-		if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");
-		if (buff[0] > CHUNK_FM && buff[0] <= CHUNK_MISC_CD && !(PicoAHW & PAHW_MCD))
-			R_ERROR_RETURN("cd chunk in non CD state?");
-
-		switch (buff[0])
-		{
-			case CHUNK_VRAM:  CHECKED_READ_BUFF(Pico.vram);  found++; break;
-			case CHUNK_CRAM:  CHECKED_READ_BUFF(Pico.cram);  found++; break;
-			case CHUNK_VSRAM: CHECKED_READ_BUFF(Pico.vsram); found++; break;
-			case CHUNK_VIDEO: CHECKED_READ_BUFF(Pico.video); found++; break;
-			default:
-				areaSeek(file, len, SEEK_CUR);
-				break;
-		}
-	}
-
-readend:
-	return 0;
-}
-
-

+ 1 - 1
pico/memory.c

@@ -322,7 +322,7 @@ static u32 PicoRead8_sram(u32 a)
 static u32 PicoRead16_sram(u32 a)
 {
   u32 d;
-  if (SRam.end >= a && a >= SRam.start && (Pico.m.sram_reg & SRR_MAPPED))
+  if (SRam.start <= a && a <= SRam.end && (Pico.m.sram_reg & SRR_MAPPED))
   {
     if (SRam.flags & SRF_EEPROM)
       d = EEPROM_read();

+ 4 - 4
pico/memory_arm.s

@@ -27,7 +27,7 @@ PicoRead8_sram: @ u32 a, u32 d
     ldr     r3, =(Pico+0x22200)
     ldr     r1, [r2, #8]    @ SRam.end
     cmp     r0, r1
-    bge     m_read8_nosram
+    bgt     m_read8_nosram
     ldr     r1, [r2, #4]    @ SRam.start
     cmp     r0, r1
     blt     m_read8_nosram
@@ -58,8 +58,8 @@ m_read8_nosram:
 m_read8_eeprom:
     stmfd   sp!,{r0,lr}
     bl      EEPROM_read
-    ldmfd   sp!,{r0,lr}
-    tst     r0, #1
+    ldmfd   sp!,{r1,lr}
+    tst     r1, #1
     moveq   r0, r0, lsr #8
     bx      lr
 
@@ -126,7 +126,7 @@ PicoRead16_sram: @ u32 a, u32 d
     ldr     r3, =(Pico+0x22200)
     ldr     r1, [r2, #8]    @ SRam.end
     cmp     r0, r1
-    bge     m_read16_nosram
+    bgt     m_read16_nosram
     ldr     r1, [r2, #4]    @ SRam.start
     cmp     r0, r1
     blt     m_read16_nosram

+ 4 - 4
pico/pico.h

@@ -34,6 +34,7 @@ extern void cache_flush_d_inval_i(const void *start_addr, const void *end_addr);
 // attempt to alloc mem at specified address.
 // alloc anywhere else if that fails (callers should handle that)
 extern void *plat_mmap(unsigned long addr, size_t size);
+extern void *plat_mremap(void *ptr, size_t oldsize, size_t newsize);
 extern void  plat_munmap(void *ptr, size_t size);
 
 // this one should handle display mode changes
@@ -119,12 +120,11 @@ extern picohw_state PicoPicohw;
 
 // area.c
 int PicoState(const char *fname, int is_save);
-int PicoStateLoadVDP(const char *fname);
+int PicoStateLoadGfx(const char *fname);
+void *PicoTmpStateSave(void);
+void  PicoTmpStateRestore(void *data);
 extern void (*PicoStateProgressCB)(const char *str);
 
-// cd/area.c
-int  PicoCdLoadStateGfx(void *file);
-
 // cd/buffering.c
 void PicoCDBufferInit(void);
 void PicoCDBufferFree(void);

+ 26 - 32
pico/pico_int.h

@@ -178,14 +178,7 @@ extern int dbg_irq_level;
 
 // ----------------------- Z80 CPU -----------------------
 
-#if defined(_USE_MZ80)
-#include "../cpu/mz80/mz80.h"
-
-#define z80_run(cycles)    { mz80GetElapsedTicks(1); mz80_run(cycles) }
-#define z80_run_nr(cycles) mz80_run(cycles)
-#define z80_int()          mz80int(0)
-
-#elif defined(_USE_DRZ80)
+#if defined(_USE_DRZ80)
 #include "../cpu/DrZ80/drz80.h"
 
 extern struct DrZ80 drZ80;
@@ -215,6 +208,8 @@ extern struct DrZ80 drZ80;
 
 #endif
 
+#define Z80_STATE_SIZE 0x60
+
 extern int z80stopCycle;         /* in 68k cycles */
 extern int z80_cycle_cnt;        /* 'done' z80 cycles before z80_run() */
 extern int z80_cycle_aim;
@@ -305,6 +300,13 @@ struct PicoMisc
   unsigned int  frame_count;   // 1c for movies and idle det
 };
 
+struct PicoMS
+{
+  unsigned char carthw[0x10];
+  unsigned char io_ctl;
+  unsigned char pad[0x4f];
+};
+
 // some assembly stuff depend on these, do not touch!
 struct Pico
 {
@@ -314,9 +316,8 @@ struct Pico
     unsigned char  vramb[0x4000]; // VRAM in SMS mode
   };
   unsigned char zram[0x2000];  // 0x20000 Z80 ram
-  unsigned char ioports[0x10];
-  unsigned char sms_io_ctl;
-  unsigned char pad[0xef];     // unused
+  unsigned char ioports[0x10]; // XXX: fix asm and mv
+  unsigned char pad[0xf0];     // unused
   unsigned short cram[0x40];   // 0x22100
   unsigned short vsram[0x40];  // 0x22180
 
@@ -325,6 +326,7 @@ struct Pico
 
   struct PicoMisc m;
   struct PicoVideo video;
+  struct PicoMS ms;
 };
 
 // sram
@@ -487,6 +489,7 @@ struct Pico32x
   unsigned short dmac_fifo[DMAC_FIFO_LEN];
   unsigned int dmac_ptr;
   unsigned int pwm_irq_sample_cnt;
+  unsigned int reserved[9];
 };
 
 struct Pico32xMem
@@ -496,7 +499,10 @@ struct Pico32xMem
   unsigned short drcblk_ram[1 << (18 - SH2_DRCBLK_RAM_SHIFT)];
 #endif
   unsigned short dram[2][0x20000/2];    // AKA fb
-  unsigned char  m68k_rom[0x10000];     // 0x100; using M68K_BANK_SIZE
+  union {
+    unsigned char  m68k_rom[0x100];
+    unsigned char  m68k_rom_bank[0x10000]; // M68K_BANK_SIZE
+  };
   unsigned char  data_array[2][0x1000]; // cache in SH2s (can be used as RAM)
 #ifdef DRC_SH2
   unsigned short drcblk_da[2][1 << (12 - SH2_DRCBLK_DA_SHIFT)];
@@ -510,14 +516,8 @@ struct Pico32xMem
 };
 
 // area.c
-PICO_INTERNAL void PicoAreaPackCpu(unsigned char *cpu, int is_sub);
-PICO_INTERNAL void PicoAreaUnpackCpu(unsigned char *cpu, int is_sub);
 extern void (*PicoLoadStateHook)(void);
 
-// cd/area.c
-PICO_INTERNAL int PicoCdSaveState(void *file);
-PICO_INTERNAL int PicoCdLoadState(void *file);
-
 typedef struct {
 	int chunk;
 	int size;
@@ -526,19 +526,9 @@ typedef struct {
 extern carthw_state_chunk *carthw_chunks;
 #define CHUNK_CARTHW 64
 
-// area.c
-typedef size_t (arearw)(void *p, size_t _size, size_t _n, void *file);
-typedef size_t (areaeof)(void *file);
-typedef int    (areaseek)(void *file, long offset, int whence);
-typedef int    (areaclose)(void *file);
-extern arearw  *areaRead;  // external read and write function pointers for
-extern arearw  *areaWrite; // gzip save state ability
-extern areaeof *areaEof;
-extern areaseek *areaSeek;
-extern areaclose *areaClose;
-
 // cart.c
-void Byteswap(void *dst, const void *src, int len);
+extern int PicoCartResize(int newsize);
+extern void Byteswap(void *dst, const void *src, int len);
 extern void (*PicoCartMemSetup)(void);
 extern void (*PicoCartUnloadHook)(void);
 
@@ -612,6 +602,8 @@ PICO_INTERNAL void SekInit(void);
 PICO_INTERNAL int  SekReset(void);
 PICO_INTERNAL void SekState(int *data);
 PICO_INTERNAL void SekSetRealTAS(int use_real);
+PICO_INTERNAL void SekPackCpu(unsigned char *cpu, int is_sub);
+PICO_INTERNAL void SekUnpackCpu(const unsigned char *cpu, int is_sub);
 void SekStepM68k(void);
 void SekInitIdleDet(void);
 void SekFinishIdleDet(void);
@@ -671,8 +663,8 @@ unsigned int EEPROM_read(void);
 
 // z80 functionality wrappers
 PICO_INTERNAL void z80_init(void);
-PICO_INTERNAL void z80_pack(unsigned char *data);
-PICO_INTERNAL void z80_unpack(unsigned char *data);
+PICO_INTERNAL void z80_pack(void *data);
+PICO_INTERNAL int  z80_unpack(const void *data);
 PICO_INTERNAL void z80_reset(void);
 PICO_INTERNAL void z80_exit(void);
 
@@ -695,6 +687,7 @@ extern int PsndDacLine;
 void PicoPowerMS(void);
 void PicoResetMS(void);
 void PicoMemSetupMS(void);
+void PicoStateLoadedMS(void);
 void PicoFrameMS(void);
 void PicoFrameDrawOnlyMS(void);
 
@@ -717,6 +710,7 @@ void PicoWrite8_32x(unsigned int a, unsigned int d);
 void PicoWrite16_32x(unsigned int a, unsigned int d);
 void PicoMemSetup32x(void);
 void Pico32xSwapDRAM(int b);
+void Pico32xStateLoaded(void);
 void p32x_poll_event(int cpu_mask, int is_vdp);
 
 // 32x/draw.c

+ 79 - 5
pico/sek.c

@@ -50,12 +50,13 @@ static void SekResetAck(void)
 
 static int SekUnrecognizedOpcode()
 {
-  unsigned int pc, op;
+  unsigned int pc;
   pc = SekPc;
-  op = PicoCpuCM68k.read16(pc);
-  elprintf(EL_ANOMALY, "Unrecognized Opcode %04x @ %06x", op, pc);
-  // see if we are not executing trash
-  if (pc < 0x200 || (pc > Pico.romsize+4 && (pc&0xe00000)!=0xe00000)) {
+  elprintf(EL_ANOMALY, "Unrecognized Opcode @ %06x", pc);
+  // see if we are still in a mapped region
+  pc &= 0x00ffffff;
+  if (map_flag_set(m68k_read16_map[pc >> M68K_MEM_SHIFT])) {
+    elprintf(EL_STATUS|EL_ANOMALY, "m68k crash @%06x", pc);
     PicoCpuCM68k.cycles = 0;
     PicoCpuCM68k.state_flags |= 1;
     return 1;
@@ -184,6 +185,79 @@ PICO_INTERNAL void SekSetRealTAS(int use_real)
 #endif
 }
 
+// Pack the cpu into a common format:
+// XXX: rename
+PICO_INTERNAL void SekPackCpu(unsigned char *cpu, int is_sub)
+{
+  unsigned int pc=0;
+
+#if defined(EMU_C68K)
+  struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
+  memcpy(cpu,context->d,0x40);
+  pc=context->pc-context->membase;
+  *(unsigned int *)(cpu+0x44)=CycloneGetSr(context);
+  *(unsigned int *)(cpu+0x48)=context->osp;
+  cpu[0x4c] = context->irq;
+  cpu[0x4d] = context->state_flags & 1;
+#elif defined(EMU_M68K)
+  void *oldcontext = m68ki_cpu_p;
+  m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
+  memcpy(cpu,m68ki_cpu_p->dar,0x40);
+  pc=m68ki_cpu_p->pc;
+  *(unsigned int  *)(cpu+0x44)=m68k_get_reg(NULL, M68K_REG_SR);
+  *(unsigned int  *)(cpu+0x48)=m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET];
+  cpu[0x4c] = CPU_INT_LEVEL>>8;
+  cpu[0x4d] = CPU_STOPPED;
+  m68k_set_context(oldcontext);
+#elif defined(EMU_F68K)
+  M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
+  memcpy(cpu,context->dreg,0x40);
+  pc=context->pc;
+  *(unsigned int  *)(cpu+0x44)=context->sr;
+  *(unsigned int  *)(cpu+0x48)=context->asp;
+  cpu[0x4c] = context->interrupts[0];
+  cpu[0x4d] = (context->execinfo & FM68K_HALTED) ? 1 : 0;
+#endif
+
+  *(unsigned int *)(cpu+0x40)=pc;
+}
+
+PICO_INTERNAL void SekUnpackCpu(const unsigned char *cpu, int is_sub)
+{
+#if defined(EMU_C68K)
+  struct Cyclone *context = is_sub ? &PicoCpuCS68k : &PicoCpuCM68k;
+  CycloneSetSr(context, *(unsigned int *)(cpu+0x44));
+  context->osp=*(unsigned int *)(cpu+0x48);
+  memcpy(context->d,cpu,0x40);
+  context->membase = 0;
+  context->pc = *(unsigned int *)(cpu+0x40);
+  CycloneUnpack(context, NULL); // rebase PC
+  context->irq = cpu[0x4c];
+  context->state_flags = 0;
+  if (cpu[0x4d])
+    context->state_flags |= 1;
+#elif defined(EMU_M68K)
+  void *oldcontext = m68ki_cpu_p;
+  m68k_set_context(is_sub ? &PicoCpuMS68k : &PicoCpuMM68k);
+  m68k_set_reg(M68K_REG_SR, *(unsigned int *)(cpu+0x44));
+  memcpy(m68ki_cpu_p->dar,cpu,0x40);
+  m68ki_cpu_p->pc=*(unsigned int *)(cpu+0x40);
+  m68ki_cpu_p->sp[m68ki_cpu_p->s_flag^SFLAG_SET]=*(unsigned int *)(cpu+0x48);
+  CPU_INT_LEVEL = cpu[0x4c] << 8;
+  CPU_STOPPED = cpu[0x4d];
+  m68k_set_context(oldcontext);
+#elif defined(EMU_F68K)
+  M68K_CONTEXT *context = is_sub ? &PicoCpuFS68k : &PicoCpuFM68k;
+  memcpy(context->dreg,cpu,0x40);
+  context->pc =*(unsigned int *)(cpu+0x40);
+  context->sr =*(unsigned int *)(cpu+0x44);
+  context->asp=*(unsigned int *)(cpu+0x48);
+  context->interrupts[0] = cpu[0x4c];
+  context->execinfo &= ~FM68K_HALTED;
+  if (cpu[0x4d]&1) context->execinfo |= FM68K_HALTED;
+#endif
+}
+
 
 /* idle loop detection, not to be used in CD mode */
 #ifdef EMU_C68K

+ 13 - 3
pico/sms.c

@@ -103,7 +103,7 @@ static unsigned char z80_sms_in(unsigned short a)
       break;
 
     case 0xc1: /* I/O port B and miscellaneous */
-      d = (Pico.sms_io_ctl & 0x80) | ((Pico.sms_io_ctl << 1) & 0x40) | 0x30;
+      d = (Pico.ms.io_ctl & 0x80) | ((Pico.ms.io_ctl << 1) & 0x40) | 0x30;
       d |= ~(PicoPad[1] >> 2) & 0x0f;
       break;
   }
@@ -119,7 +119,7 @@ static void z80_sms_out(unsigned short a, unsigned char d)
   switch (a)
   {
     case 0x01:
-      Pico.sms_io_ctl = d;
+      Pico.ms.io_ctl = d;
       break;
 
     case 0x40:
@@ -167,6 +167,7 @@ static void write_bank(unsigned short a, unsigned char d)
 #endif
       break;
   }
+  Pico.ms.carthw[a & 0x0f] = d;
 }
 
 static void xwrite(unsigned int a, unsigned char d)
@@ -174,7 +175,7 @@ static void xwrite(unsigned int a, unsigned char d)
   elprintf(EL_IO, "z80 write [%04x] %02x", a, d);
   if (a >= 0xc000)
     Pico.zram[a & 0x1fff] = d;
-  if (a >= 0xfff0)
+  if (a >= 0xfff8)
     write_bank(a, d);
 }
 
@@ -203,6 +204,9 @@ void PicoPowerMS(void)
   tmp = 1 << s;
   bank_mask = (tmp - 1) >> 14;
 
+  Pico.ms.carthw[0x0e] = 1;
+  Pico.ms.carthw[0x0f] = 2;
+
   PicoReset();
 }
 
@@ -229,6 +233,12 @@ void PicoMemSetupMS(void)
 #endif
 }
 
+void PicoStateLoadedMS(void)
+{
+  write_bank(0xfffe, Pico.ms.carthw[0x0e]);
+  write_bank(0xffff, Pico.ms.carthw[0x0f]);
+}
+
 void PicoFrameMS(void)
 {
   struct PicoVideo *pv = &Pico.video;

+ 679 - 0
pico/state.c

@@ -0,0 +1,679 @@
+// (c) Copyright 2004 Dave, All rights reserved.
+// (c) Copyright 2006-2010 notaz, All rights reserved.
+// Free for non-commercial use.
+
+// For commercial use, separate licencing terms must be obtained.
+
+
+#include "pico_int.h"
+#include <zlib/zlib.h>
+
+#include "../cpu/sh2/sh2.h"
+#include "sound/ym2612.h"
+
+// sn76496
+extern int *sn76496_regs;
+
+typedef size_t (arearw)(void *p, size_t _size, size_t _n, void *file);
+typedef size_t (areaeof)(void *file);
+typedef int    (areaseek)(void *file, long offset, int whence);
+typedef int    (areaclose)(void *file);
+
+static arearw    *areaRead;
+static arearw    *areaWrite;
+static areaeof   *areaEof;
+static areaseek  *areaSeek;
+static areaclose *areaClose;
+
+carthw_state_chunk *carthw_chunks;
+void (*PicoStateProgressCB)(const char *str);
+void (*PicoLoadStateHook)(void);
+
+
+/* I/O functions */
+static size_t gzRead2(void *p, size_t _size, size_t _n, void *file)
+{
+  return gzread(file, p, _size * _n);
+}
+
+static size_t gzWrite2(void *p, size_t _size, size_t _n, void *file)
+{
+  return gzwrite(file, p, _size * _n);
+}
+
+static void set_cbs(int gz)
+{
+  if (gz) {
+    areaRead  = gzRead2;
+    areaWrite = gzWrite2;
+    areaEof   = (areaeof *) gzeof;
+    areaSeek  = (areaseek *) gzseek;
+    areaClose = (areaclose *) gzclose;
+  } else {
+    areaRead  = (arearw *) fread;
+    areaWrite = (arearw *) fwrite;
+    areaEof   = (areaeof *) feof;
+    areaSeek  = (areaseek *) fseek;
+    areaClose = (areaclose *) fclose;
+  }
+}
+
+static void *open_save_file(const char *fname, int is_save)
+{
+  int len = strlen(fname);
+  void *afile = NULL;
+
+  if (len > 3 && strcmp(fname + len - 3, ".gz") == 0)
+  {
+    if ( (afile = gzopen(fname, is_save ? "wb" : "rb")) ) {
+      set_cbs(1);
+      if (is_save)
+        gzsetparams(afile, 9, Z_DEFAULT_STRATEGY);
+    }
+  }
+  else
+  {
+    if ( (afile = fopen(fname, is_save ? "wb" : "rb")) ) {
+      set_cbs(0);
+    }
+  }
+
+  return afile;
+}
+
+// legacy savestate loading
+#define SCANP(f, x) areaRead(&Pico.x, sizeof(Pico.x), 1, f)
+
+static int state_load_legacy(void *file)
+{
+  unsigned char head[32];
+  unsigned char cpu[0x60];
+  unsigned char cpu_z80[Z80_STATE_SIZE];
+  void *ym2612_regs;
+  int ok;
+
+  memset(&cpu,0,sizeof(cpu));
+  memset(&cpu_z80,0,sizeof(cpu_z80));
+
+  memset(head, 0, sizeof(head));
+  areaRead(head, sizeof(head), 1, file);
+  if (strcmp((char *)head, "Pico") != 0)
+    return -1;
+
+  elprintf(EL_STATUS, "legacy savestate");
+
+  // Scan all the memory areas:
+  SCANP(file, ram);
+  SCANP(file, vram);
+  SCANP(file, zram);
+  SCANP(file, cram);
+  SCANP(file, vsram);
+
+  // Pack, scan and unpack the cpu data:
+  areaRead(cpu, sizeof(cpu), 1, file);
+  SekUnpackCpu(cpu, 0);
+
+  SCANP(file, m);
+  SCANP(file, video);
+
+  ok = areaRead(cpu_z80, sizeof(cpu_z80), 1, file) == sizeof(cpu_z80);
+  // do not unpack if we fail to load z80 state
+  if (!ok) z80_reset();
+  else     z80_unpack(cpu_z80);
+
+  ym2612_regs = YM2612GetRegs();
+  areaRead(sn76496_regs, 28*4, 1, file);
+  areaRead(ym2612_regs, 0x200+4, 1, file);
+  ym2612_unpack_state();
+
+  return 0;
+}
+
+// ---------------------------------------------------------------------------
+
+typedef enum {
+  CHUNK_M68K = 1,
+  CHUNK_RAM,
+  CHUNK_VRAM,
+  CHUNK_ZRAM,
+  CHUNK_CRAM,    // 5
+  CHUNK_VSRAM,
+  CHUNK_MISC,
+  CHUNK_VIDEO,
+  CHUNK_Z80,
+  CHUNK_PSG,     // 10
+  CHUNK_FM,
+  // CD stuff
+  CHUNK_S68K,
+  CHUNK_PRG_RAM,
+  CHUNK_WORD_RAM,
+  CHUNK_PCM_RAM, // 15
+  CHUNK_BRAM,
+  CHUNK_GA_REGS,
+  CHUNK_PCM,
+  CHUNK_CDC,
+  CHUNK_CDD,     // 20
+  CHUNK_SCD,
+  CHUNK_RC,
+  CHUNK_MISC_CD,
+  //
+  CHUNK_IOPORTS, // versions < 1.70 did not save that..
+  CHUNK_SMS,     // 25
+  // 32x
+  CHUNK_MSH2,
+  CHUNK_MSH2_DATA,
+  CHUNK_MSH2_PERI,
+  CHUNK_SSH2,
+  CHUNK_SSH2_DATA, // 30
+  CHUNK_SSH2_PERI,
+  CHUNK_32XSYS,
+  CHUNK_M68K_BIOS,
+  CHUNK_MSH2_BIOS,
+  CHUNK_SSH2_BIOS, // 35
+  CHUNK_SDRAM,
+  CHUNK_DRAM,
+  CHUNK_32XPAL,
+  //
+  CHUNK_DEFAULT_COUNT,
+  CHUNK_CARTHW_ = CHUNK_CARTHW,  // defined in PicoInt
+} chunk_name_e;
+
+static const char * const chunk_names[] = {
+  "INVALID!",
+  "M68K state",
+  "RAM",
+  "VRAM",
+  "ZRAM",
+  "CRAM",      // 5
+  "VSRAM",
+  "emu state",
+  "VIDEO",
+  "Z80 state",
+  "PSG",       // 10
+  "FM",
+  // CD stuff
+  "S68K state",
+  "PRG_RAM",
+  "WORD_RAM",
+  "PCM_RAM",   // 15
+  "BRAM",
+  "GATE ARRAY regs",
+  "PCM state",
+  "CDC",
+  "CDD",       // 20
+  "SCD",
+  "GFX chip",
+  "MCD state",
+  //
+  "IO",
+  "SMS state", // 25
+  // 32x
+  "MSH2",
+  "MSH2 data",
+  "MSH2 peri",
+  "SSH2",
+  "SSH2 data", // 30
+  "SSH2 peri",
+  "32X system regs",
+  "M68K BIOS",
+  "MSH2 BIOS",
+  "SSH2 BIOS", // 35
+  "SDRAM",
+  "DRAM",
+  "PAL",
+};
+
+static int write_chunk(chunk_name_e name, int len, void *data, void *file)
+{
+  size_t bwritten = 0;
+  bwritten += areaWrite(&name, 1, 1, file);
+  bwritten += areaWrite(&len, 1, 4, file);
+  bwritten += areaWrite(data, 1, len, file);
+
+  return (bwritten == len + 4 + 1);
+}
+
+#define CHECKED_WRITE(name,len,data) { \
+  if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) { \
+    strncpy(sbuff + 9, chunk_names[name], sizeof(sbuff) - 9); \
+    PicoStateProgressCB(sbuff); \
+  } \
+  if (!write_chunk(name, len, data, file)) return 1; \
+}
+
+#define CHECKED_WRITE_BUFF(name,buff) { \
+  if (PicoStateProgressCB && name < CHUNK_DEFAULT_COUNT) { \
+    strncpy(sbuff + 9, chunk_names[name], sizeof(sbuff) - 9); \
+    PicoStateProgressCB(sbuff); \
+  } \
+  if (!write_chunk(name, sizeof(buff), &buff, file)) return 1; \
+}
+
+static int state_save(void *file)
+{
+  char sbuff[32] = "Saving.. ";
+  unsigned char buff[0x60], buff_z80[Z80_STATE_SIZE];
+  void *ym2612_regs = YM2612GetRegs();
+  int ver = 0x0170; // not really used..
+
+  areaWrite("PicoSEXT", 1, 8, file);
+  areaWrite(&ver, 1, 4, file);
+
+  if (!(PicoAHW & PAHW_SMS)) {
+    memset(buff, 0, sizeof(buff));
+    SekPackCpu(buff, 0);
+    CHECKED_WRITE_BUFF(CHUNK_M68K,  buff);
+    CHECKED_WRITE_BUFF(CHUNK_RAM,   Pico.ram);
+    CHECKED_WRITE_BUFF(CHUNK_VSRAM, Pico.vsram);
+    CHECKED_WRITE_BUFF(CHUNK_IOPORTS, Pico.ioports);
+    ym2612_pack_state();
+    CHECKED_WRITE(CHUNK_FM, 0x200+4, ym2612_regs);
+  }
+  else {
+    CHECKED_WRITE_BUFF(CHUNK_SMS, Pico.ms);
+  }
+
+  CHECKED_WRITE_BUFF(CHUNK_VRAM,  Pico.vram);
+  CHECKED_WRITE_BUFF(CHUNK_ZRAM,  Pico.zram);
+  CHECKED_WRITE_BUFF(CHUNK_CRAM,  Pico.cram);
+  CHECKED_WRITE_BUFF(CHUNK_MISC,  Pico.m);
+  CHECKED_WRITE_BUFF(CHUNK_VIDEO, Pico.video);
+
+  z80_pack(buff_z80);
+  CHECKED_WRITE_BUFF(CHUNK_Z80, buff_z80);
+  CHECKED_WRITE(CHUNK_PSG, 28*4, sn76496_regs);
+
+  if (PicoAHW & PAHW_MCD)
+  {
+    memset(buff, 0, sizeof(buff));
+    SekPackCpu(buff, 1);
+    if (Pico_mcd->s68k_regs[3] & 4) // 1M mode?
+      wram_1M_to_2M(Pico_mcd->word_ram2M);
+    Pico_mcd->m.hint_vector = *(unsigned short *)(Pico_mcd->bios + 0x72);
+
+    CHECKED_WRITE_BUFF(CHUNK_S68K,     buff);
+    CHECKED_WRITE_BUFF(CHUNK_PRG_RAM,  Pico_mcd->prg_ram);
+    CHECKED_WRITE_BUFF(CHUNK_WORD_RAM, Pico_mcd->word_ram2M); // in 2M format
+    CHECKED_WRITE_BUFF(CHUNK_PCM_RAM,  Pico_mcd->pcm_ram);
+    CHECKED_WRITE_BUFF(CHUNK_BRAM,     Pico_mcd->bram);
+    CHECKED_WRITE_BUFF(CHUNK_GA_REGS,  Pico_mcd->s68k_regs); // GA regs, not CPU regs
+    CHECKED_WRITE_BUFF(CHUNK_PCM,      Pico_mcd->pcm);
+    CHECKED_WRITE_BUFF(CHUNK_CDD,      Pico_mcd->cdd);
+    CHECKED_WRITE_BUFF(CHUNK_CDC,      Pico_mcd->cdc);
+    CHECKED_WRITE_BUFF(CHUNK_SCD,      Pico_mcd->scd);
+    CHECKED_WRITE_BUFF(CHUNK_RC,       Pico_mcd->rot_comp);
+    CHECKED_WRITE_BUFF(CHUNK_MISC_CD,  Pico_mcd->m);
+
+    if (Pico_mcd->s68k_regs[3] & 4) // convert back
+      wram_2M_to_1M(Pico_mcd->word_ram2M);
+  }
+
+  if (PicoAHW & PAHW_32X)
+  {
+    unsigned char cpubuff[SH2_STATE_SIZE];
+
+    memset(cpubuff, 0, sizeof(cpubuff));
+
+    sh2_pack(&sh2s[0], cpubuff);
+    CHECKED_WRITE_BUFF(CHUNK_MSH2,      cpubuff);
+    CHECKED_WRITE_BUFF(CHUNK_MSH2_DATA, Pico32xMem->data_array[0]);
+    CHECKED_WRITE_BUFF(CHUNK_MSH2_PERI, Pico32xMem->sh2_peri_regs[0]);
+
+    sh2_pack(&sh2s[1], cpubuff);
+    CHECKED_WRITE_BUFF(CHUNK_SSH2,      cpubuff);
+    CHECKED_WRITE_BUFF(CHUNK_SSH2_DATA, Pico32xMem->data_array[1]);
+    CHECKED_WRITE_BUFF(CHUNK_SSH2_PERI, Pico32xMem->sh2_peri_regs[1]);
+
+    CHECKED_WRITE_BUFF(CHUNK_32XSYS,    Pico32x);
+    CHECKED_WRITE_BUFF(CHUNK_M68K_BIOS, Pico32xMem->m68k_rom);
+    CHECKED_WRITE_BUFF(CHUNK_MSH2_BIOS, Pico32xMem->sh2_rom_m);
+    CHECKED_WRITE_BUFF(CHUNK_SSH2_BIOS, Pico32xMem->sh2_rom_s);
+    CHECKED_WRITE_BUFF(CHUNK_SDRAM,     Pico32xMem->sdram);
+    CHECKED_WRITE_BUFF(CHUNK_DRAM,      Pico32xMem->dram);
+    CHECKED_WRITE_BUFF(CHUNK_32XPAL,    Pico32xMem->pal);
+  }
+
+  if (carthw_chunks != NULL)
+  {
+    carthw_state_chunk *chwc;
+    if (PicoStateProgressCB)
+      PicoStateProgressCB("Saving.. cart hw state");
+    for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++)
+      CHECKED_WRITE(chwc->chunk, chwc->size, chwc->ptr);
+  }
+
+  return 0;
+}
+
+static int g_read_offs = 0;
+
+#define R_ERROR_RETURN(error) \
+{ \
+  elprintf(EL_STATUS, "load_state @ %x: " error, g_read_offs); \
+  return 1; \
+}
+
+// when is eof really set?
+#define CHECKED_READ(len,data) { \
+  if (areaRead(data, 1, len, file) != len) { \
+    if (len == 1 && areaEof(file)) goto readend; \
+    R_ERROR_RETURN("areaRead: premature EOF\n"); \
+    return 1; \
+  } \
+  g_read_offs += len; \
+}
+
+#define CHECKED_READ2(len2,data) { \
+  if (len2 != len) { \
+    elprintf(EL_STATUS, "unexpected len %i, wanted %i (%s)", len, len2, #len2); \
+    if (len > len2) R_ERROR_RETURN("failed."); \
+    /* else read anyway and hope for the best.. */ \
+  } \
+  CHECKED_READ(len, data); \
+}
+
+#define CHECKED_READ_BUFF(buff) CHECKED_READ2(sizeof(buff), &buff);
+
+static int state_load(void *file)
+{
+  unsigned char buff_m68k[0x60], buff_s68k[0x60];
+  unsigned char buff_z80[Z80_STATE_SIZE];
+  unsigned char buff_sh2[SH2_STATE_SIZE];
+  unsigned char chunk;
+  void *ym2612_regs;
+  char header[8];
+  int ver, len;
+
+  g_read_offs = 0;
+  CHECKED_READ(8, header);
+  if (strncmp(header, "PicoSMCD", 8) && strncmp(header, "PicoSEXT", 8))
+    R_ERROR_RETURN("bad header");
+  CHECKED_READ(4, &ver);
+
+  while (!areaEof(file))
+  {
+    CHECKED_READ(1, &chunk);
+    CHECKED_READ(4, &len);
+    if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");
+    if (CHUNK_S68K <= chunk && chunk <= CHUNK_MISC_CD && !(PicoAHW & PAHW_MCD))
+      R_ERROR_RETURN("cd chunk in non CD state?");
+    if (CHUNK_MSH2 <= chunk && chunk <= CHUNK_32XPAL && !(PicoAHW & PAHW_32X))
+      R_ERROR_RETURN("32x chunk in non 32x state?");
+
+    switch (chunk)
+    {
+      case CHUNK_M68K:
+        CHECKED_READ_BUFF(buff_m68k);
+        break;
+
+      case CHUNK_Z80:
+        CHECKED_READ_BUFF(buff_z80);
+        z80_unpack(buff_z80);
+        break;
+
+      case CHUNK_RAM:     CHECKED_READ_BUFF(Pico.ram); break;
+      case CHUNK_VRAM:    CHECKED_READ_BUFF(Pico.vram); break;
+      case CHUNK_ZRAM:    CHECKED_READ_BUFF(Pico.zram); break;
+      case CHUNK_CRAM:    CHECKED_READ_BUFF(Pico.cram); break;
+      case CHUNK_VSRAM:   CHECKED_READ_BUFF(Pico.vsram); break;
+      case CHUNK_MISC:    CHECKED_READ_BUFF(Pico.m); break;
+      case CHUNK_VIDEO:   CHECKED_READ_BUFF(Pico.video); break;
+      case CHUNK_IOPORTS: CHECKED_READ_BUFF(Pico.ioports); break;
+      case CHUNK_PSG:     CHECKED_READ2(28*4, sn76496_regs); break;
+      case CHUNK_FM:
+        ym2612_regs = YM2612GetRegs();
+        CHECKED_READ2(0x200+4, ym2612_regs);
+        ym2612_unpack_state();
+        break;
+
+      case CHUNK_SMS:
+        CHECKED_READ_BUFF(Pico.ms);
+        break;
+
+      // cd stuff
+      case CHUNK_S68K:
+        CHECKED_READ_BUFF(buff_s68k);
+        break;
+
+      case CHUNK_PRG_RAM:  CHECKED_READ_BUFF(Pico_mcd->prg_ram); break;
+      case CHUNK_WORD_RAM: CHECKED_READ_BUFF(Pico_mcd->word_ram2M); break;
+      case CHUNK_PCM_RAM:  CHECKED_READ_BUFF(Pico_mcd->pcm_ram); break;
+      case CHUNK_BRAM:     CHECKED_READ_BUFF(Pico_mcd->bram); break;
+      case CHUNK_GA_REGS:  CHECKED_READ_BUFF(Pico_mcd->s68k_regs); break;
+      case CHUNK_PCM:      CHECKED_READ_BUFF(Pico_mcd->pcm); break;
+      case CHUNK_CDD:      CHECKED_READ_BUFF(Pico_mcd->cdd); break;
+      case CHUNK_CDC:      CHECKED_READ_BUFF(Pico_mcd->cdc); break;
+      case CHUNK_SCD:      CHECKED_READ_BUFF(Pico_mcd->scd); break;
+      case CHUNK_RC:       CHECKED_READ_BUFF(Pico_mcd->rot_comp); break;
+      case CHUNK_MISC_CD:  CHECKED_READ_BUFF(Pico_mcd->m); break;
+
+      // 32x stuff
+      case CHUNK_MSH2:
+        CHECKED_READ_BUFF(buff_sh2);
+        sh2_unpack(&sh2s[0], buff_sh2);
+        break;
+
+      case CHUNK_SSH2:
+        CHECKED_READ_BUFF(buff_sh2);
+        sh2_unpack(&sh2s[1], buff_sh2);
+        break;
+
+      case CHUNK_MSH2_DATA:   CHECKED_READ_BUFF(Pico32xMem->data_array[0]); break;
+      case CHUNK_MSH2_PERI:   CHECKED_READ_BUFF(Pico32xMem->sh2_peri_regs[0]); break;
+      case CHUNK_SSH2_DATA:   CHECKED_READ_BUFF(Pico32xMem->data_array[1]); break;
+      case CHUNK_SSH2_PERI:   CHECKED_READ_BUFF(Pico32xMem->sh2_peri_regs[1]); break;
+      case CHUNK_32XSYS:      CHECKED_READ_BUFF(Pico32x); break;
+      case CHUNK_M68K_BIOS:   CHECKED_READ_BUFF(Pico32xMem->m68k_rom); break;
+      case CHUNK_MSH2_BIOS:   CHECKED_READ_BUFF(Pico32xMem->sh2_rom_m); break;
+      case CHUNK_SSH2_BIOS:   CHECKED_READ_BUFF(Pico32xMem->sh2_rom_s); break;
+      case CHUNK_SDRAM:       CHECKED_READ_BUFF(Pico32xMem->sdram); break;
+      case CHUNK_DRAM:        CHECKED_READ_BUFF(Pico32xMem->dram); break;
+      case CHUNK_32XPAL:      CHECKED_READ_BUFF(Pico32xMem->pal); break;
+
+      default:
+        if (carthw_chunks != NULL)
+        {
+          carthw_state_chunk *chwc;
+          for (chwc = carthw_chunks; chwc->ptr != NULL; chwc++) {
+            if (chwc->chunk == chunk) {
+              CHECKED_READ2(chwc->size, chwc->ptr);
+              goto breakswitch;
+            }
+          }
+        }
+        elprintf(EL_STATUS, "load_state: skipping unknown chunk %i of size %i", chunk, len);
+        areaSeek(file, len, SEEK_CUR);
+        break;
+    }
+breakswitch:;
+  }
+
+readend:
+  if (PicoAHW & PAHW_SMS)
+    PicoStateLoadedMS();
+
+  if (PicoAHW & PAHW_MCD)
+  {
+    PicoMemStateLoaded();
+
+    if (!(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1))
+      cdda_start_play();
+
+    // must unpack after mem is set up
+    SekUnpackCpu(buff_s68k, 1);
+  }
+
+  if (!(PicoAHW & PAHW_SMS))
+    SekUnpackCpu(buff_m68k, 0);
+
+  if (PicoAHW & PAHW_32X)
+    Pico32xStateLoaded();
+
+  return 0;
+}
+
+static int state_load_gfx(void *file)
+{
+  int ver, len, found = 0, to_find = 4;
+  char buff[8];
+
+  if (PicoAHW & PAHW_32X)
+    to_find += 2;
+
+  g_read_offs = 0;
+  CHECKED_READ(8, buff);
+  if (strncmp((char *)buff, "PicoSMCD", 8) && strncmp((char *)buff, "PicoSEXT", 8))
+    R_ERROR_RETURN("bad header");
+  CHECKED_READ(4, &ver);
+
+  while (!areaEof(file) && found < to_find)
+  {
+    CHECKED_READ(1, buff);
+    CHECKED_READ(4, &len);
+    if (len < 0 || len > 1024*512) R_ERROR_RETURN("bad length");
+    if (buff[0] > CHUNK_FM && buff[0] <= CHUNK_MISC_CD && !(PicoAHW & PAHW_MCD))
+      R_ERROR_RETURN("cd chunk in non CD state?");
+
+    switch (buff[0])
+    {
+      case CHUNK_VRAM:  CHECKED_READ_BUFF(Pico.vram);  found++; break;
+      case CHUNK_CRAM:  CHECKED_READ_BUFF(Pico.cram);  found++; break;
+      case CHUNK_VSRAM: CHECKED_READ_BUFF(Pico.vsram); found++; break;
+      case CHUNK_VIDEO: CHECKED_READ_BUFF(Pico.video); found++; break;
+
+      case CHUNK_DRAM:
+        if (Pico32xMem != NULL)
+          CHECKED_READ_BUFF(Pico32xMem->dram);
+        break;
+
+      case CHUNK_32XPAL:
+        if (Pico32xMem != NULL)
+          CHECKED_READ_BUFF(Pico32xMem->pal);
+        Pico32x.dirty_pal = 1;
+        break;
+
+      case CHUNK_32XSYS:
+        CHECKED_READ_BUFF(Pico32x);
+        break;
+
+      default:
+        areaSeek(file, len, SEEK_CUR);
+        break;
+    }
+  }
+
+readend:
+  return 0;
+}
+
+int PicoState(const char *fname, int is_save)
+{
+  void *afile = NULL;
+  int ret;
+
+  afile = open_save_file(fname, is_save);
+  if (afile == NULL)
+    return -1;
+
+  if (is_save)
+    ret = state_save(afile);
+  else {
+    ret = state_load(afile);
+    if (ret != 0) {
+      areaSeek(afile, 0, SEEK_SET);
+      ret = state_load_legacy(afile);
+    }
+
+    if (PicoLoadStateHook != NULL)
+      PicoLoadStateHook();
+    Pico.m.dirtyPal = 1;
+  }
+
+  areaClose(afile);
+  return ret;
+}
+
+int PicoStateLoadGfx(const char *fname)
+{
+  void *afile;
+  int ret;
+
+  afile = open_save_file(fname, 0);
+  if (afile == NULL)
+    return -1;
+
+  ret = state_load_gfx(afile);
+  if (ret != 0) {
+    // assume legacy
+    areaSeek(afile, 0x10020, SEEK_SET);  // skip header and RAM
+    areaRead(Pico.vram, 1, sizeof(Pico.vram), afile);
+    areaSeek(afile, 0x2000, SEEK_CUR);
+    areaRead(Pico.cram, 1, sizeof(Pico.cram), afile);
+    areaRead(Pico.vsram, 1, sizeof(Pico.vsram), afile);
+    areaSeek(afile, 0x221a0, SEEK_SET);
+    areaRead(&Pico.video, 1, sizeof(Pico.video), afile);
+  }
+  areaClose(afile);
+  return 0;
+}
+
+// tmp state
+struct PicoTmp
+{
+  unsigned short vram[0x8000];
+  unsigned short cram[0x40];
+  unsigned short vsram[0x40];
+
+  //struct PicoMisc m;
+  struct PicoVideo video;
+
+  struct {
+    struct Pico32x p32x;
+    unsigned short dram[2][0x20000/2];
+    unsigned short pal[0x100];
+  } t32x;
+};
+
+// returns data ptr to free() or PicoTmpStateRestore()
+void *PicoTmpStateSave(void)
+{
+  // gfx only for now
+  struct PicoTmp *t = malloc(sizeof(*t));
+  if (t == NULL)
+    return NULL;
+
+  memcpy(t->vram, Pico.vram, sizeof(Pico.vram));
+  memcpy(t->cram, Pico.cram, sizeof(Pico.cram));
+  memcpy(t->vsram, Pico.vsram, sizeof(Pico.vsram));
+  memcpy(&t->video, &Pico.video, sizeof(Pico.video));
+
+  if (PicoAHW & PAHW_32X) {
+    memcpy(&t->t32x.p32x, &Pico32x, sizeof(Pico32x));
+    memcpy(t->t32x.dram, Pico32xMem->dram, sizeof(Pico32xMem->dram));
+    memcpy(t->t32x.pal, Pico32xMem->pal, sizeof(Pico32xMem->pal));
+  }
+
+  return t;
+}
+
+void PicoTmpStateRestore(void *data)
+{
+  struct PicoTmp *t = data;
+  if (t == NULL)
+    return;
+
+  memcpy(Pico.vram, t->vram, sizeof(Pico.vram));
+  memcpy(Pico.cram, t->cram, sizeof(Pico.cram));
+  memcpy(Pico.vsram, t->vsram, sizeof(Pico.vsram));
+  memcpy(&Pico.video, &t->video, sizeof(Pico.video));
+  Pico.m.dirtyPal = 1;
+
+  if (PicoAHW & PAHW_32X) {
+    memcpy(&Pico32x, &t->t32x.p32x, sizeof(Pico32x));
+    memcpy(Pico32xMem->dram, t->t32x.dram, sizeof(Pico32xMem->dram));
+    memcpy(Pico32xMem->pal, t->t32x.pal, sizeof(Pico32xMem->pal));
+    Pico32x.dirty_pal = 1;
+  }
+}
+
+// vim:shiftwidth=2:expandtab

+ 203 - 122
pico/z80if.c

@@ -5,72 +5,60 @@
 uptr z80_read_map [0x10000 >> Z80_MEM_SHIFT];
 uptr z80_write_map[0x10000 >> Z80_MEM_SHIFT];
 
-#ifdef _USE_MZ80
+#ifdef _USE_DRZ80
+struct DrZ80 drZ80;
 
-// memhandlers for mz80 core
-unsigned char mz80_read(UINT32 a,  struct MemoryReadByte *w)  { return z80_read(a); }
-void mz80_write(UINT32 a, UINT8 d, struct MemoryWriteByte *w) { z80_write(d, a); }
+static u32 drz80_sp_base;
 
-// structures for mz80 core
-static struct MemoryReadByte mz80_mem_read[]=
-{
-  {0x0000,0xffff,mz80_read},
-  {(UINT32) -1,(UINT32) -1,NULL}
-};
-static struct MemoryWriteByte mz80_mem_write[]=
+static void drz80_load_pcsp(u32 pc, u32 sp)
 {
-  {0x0000,0xffff,mz80_write},
-  {(UINT32) -1,(UINT32) -1,NULL}
-};
-static struct z80PortRead mz80_io_read[] ={
-  {(UINT16) -1,(UINT16) -1,NULL}
-};
-static struct z80PortWrite mz80_io_write[]={
-  {(UINT16) -1,(UINT16) -1,NULL}
-};
+  drZ80.Z80PC_BASE = z80_read_map[pc >> Z80_MEM_SHIFT];
+  if (drZ80.Z80PC_BASE & (1<<31)) {
+    elprintf(EL_STATUS|EL_ANOMALY, "load_pcsp: bad PC: %04x", pc);
+    drZ80.Z80PC_BASE = drZ80.Z80PC = z80_read_map[0];
+  } else {
+    drZ80.Z80PC_BASE <<= 1;
+    drZ80.Z80PC = drZ80.Z80PC_BASE + pc;
+  }
+  drZ80.Z80SP_BASE = z80_read_map[sp >> Z80_MEM_SHIFT];
+  if (drZ80.Z80SP_BASE & (1<<31)) {
+    elprintf(EL_STATUS|EL_ANOMALY, "load_pcsp: bad SP: %04x", sp);
+    drZ80.Z80SP_BASE = z80_read_map[0];
+    drZ80.Z80SP = drZ80.Z80SP_BASE + (1 << Z80_MEM_SHIFT);
+  } else {
+    drZ80.Z80SP_BASE <<= 1;
+    drZ80.Z80SP = drZ80.Z80SP_BASE + sp;
+  }
+}
 
-int mz80_run(int cycles)
+// called only if internal xmap rebase fails
+static unsigned int dz80_rebase_pc(unsigned short pc)
 {
-  int ticks_pre = mz80GetElapsedTicks(0);
-  mz80exec(cycles);
-  return mz80GetElapsedTicks(0) - ticks_pre;
+  elprintf(EL_STATUS|EL_ANOMALY, "dz80_rebase_pc: fail on %04x", pc);
+  drZ80.Z80PC_BASE = z80_read_map[0] << 1;
+  return drZ80.Z80PC_BASE;
 }
 
-#endif
-
-#ifdef _USE_DRZ80
-struct DrZ80 drZ80;
+static unsigned int dz80_rebase_sp(unsigned short sp)
+{
+  elprintf(EL_STATUS|EL_ANOMALY, "dz80_rebase_sp: fail on %04x", sp);
+  drZ80.Z80SP_BASE = z80_read_map[drz80_sp_base >> Z80_MEM_SHIFT] << 1;
+  return drZ80.Z80SP_BASE + (1 << Z80_MEM_SHIFT) - 0x100;
+}
 #endif
 
 
-PICO_INTERNAL void z80_init(void)
+void z80_init(void)
 {
-#ifdef _USE_MZ80
-  struct mz80context z80;
-
-  // z80
-  mz80init();
-  // Modify the default context
-  mz80GetContext(&z80);
-
-  // point mz80 stuff
-  z80.z80Base=Pico.zram;
-  z80.z80MemRead=mz80_mem_read;
-  z80.z80MemWrite=mz80_mem_write;
-  z80.z80IoRead=mz80_io_read;
-  z80.z80IoWrite=mz80_io_write;
-
-  mz80SetContext(&z80);
-#endif
 #ifdef _USE_DRZ80
   memset(&drZ80, 0, sizeof(drZ80));
-  drZ80.z80_rebasePC=NULL; // unused, handled by xmap
-  drZ80.z80_rebaseSP=NULL;
-  drZ80.z80_read8   =(void *)z80_read_map;
-  drZ80.z80_read16  =NULL;
-  drZ80.z80_write8  =(void *)z80_write_map;
-  drZ80.z80_write16 =NULL;
-  drZ80.z80_irq_callback=NULL;
+  drZ80.z80_rebasePC = dz80_rebase_pc;
+  drZ80.z80_rebaseSP = dz80_rebase_sp;
+  drZ80.z80_read8    = (void *)z80_read_map;
+  drZ80.z80_read16   = NULL;
+  drZ80.z80_write8   = (void *)z80_write_map;
+  drZ80.z80_write16  = NULL;
+  drZ80.z80_irq_callback = NULL;
 #endif
 #ifdef _USE_CZ80
   memset(&CZ80, 0, sizeof(CZ80));
@@ -80,113 +68,206 @@ PICO_INTERNAL void z80_init(void)
 #endif
 }
 
-PICO_INTERNAL void z80_reset(void)
+void z80_reset(void)
 {
-#ifdef _USE_MZ80
-  mz80reset();
-#endif
 #ifdef _USE_DRZ80
   memset(&drZ80, 0, 0x54);
   drZ80.Z80F  = (1<<2);  // set ZFlag
   drZ80.Z80F2 = (1<<2);  // set ZFlag
   drZ80.Z80IX = 0xFFFF << 16;
   drZ80.Z80IY = 0xFFFF << 16;
+  drZ80.Z80I = 0;
   drZ80.Z80IM = 0; // 1?
   drZ80.z80irqvector = 0xff0000; // RST 38h
   drZ80.Z80PC_BASE = drZ80.Z80PC = z80_read_map[0] << 1;
   // drZ80 is locked in single bank
-  drZ80.Z80SP_BASE = ((PicoAHW & PAHW_SMS) ?
-    z80_read_map[0xc000 >> Z80_MEM_SHIFT] : z80_read_map[0]) << 1;
-//  drZ80.Z80SP = drZ80.z80_rebaseSP(0x2000); // 0xf000 ?
+  drz80_sp_base = (PicoAHW & PAHW_SMS) ? 0xc000 : 0x0000;
+  drZ80.Z80SP_BASE = z80_read_map[drz80_sp_base >> Z80_MEM_SHIFT] << 1;
+  if (PicoAHW & PAHW_SMS)
+    drZ80.Z80SP = drZ80.Z80SP_BASE + 0xdff0; // simulate BIOS
 #endif
 #ifdef _USE_CZ80
   Cz80_Reset(&CZ80);
   Cz80_Set_Reg(&CZ80, CZ80_IX, 0xffff);
   Cz80_Set_Reg(&CZ80, CZ80_IY, 0xffff);
-  Cz80_Set_Reg(&CZ80, CZ80_SP, 0x2000);
-#endif
-}
-
-// XXX TODO: should better use universal z80 save format
-PICO_INTERNAL void z80_pack(unsigned char *data)
-{
-#if defined(_USE_MZ80)
-  struct mz80context mz80;
-  *(int *)data = 0x00005A6D; // "mZ"
-  mz80GetContext(&mz80);
-  memcpy(data+4, &mz80.z80clockticks, sizeof(mz80)-5*4); // don't save base&memhandlers
-#elif defined(_USE_DRZ80)
-  *(int *)data = 0x015A7244; // "DrZ" v1
-//  drZ80.Z80PC = drZ80.z80_rebasePC(drZ80.Z80PC-drZ80.Z80PC_BASE);
-//  drZ80.Z80SP = drZ80.z80_rebaseSP(drZ80.Z80SP-drZ80.Z80SP_BASE);
-  memcpy(data+4, &drZ80, 0x54);
-#elif defined(_USE_CZ80)
-  *(int *)data = 0x00007a43; // "Cz"
-  *(int *)(data+4) = Cz80_Get_Reg(&CZ80, CZ80_PC);
-  memcpy(data+8, &CZ80, offsetof(cz80_struc, BasePC));
+  if (PicoAHW & PAHW_SMS)
+    Cz80_Set_Reg(&CZ80, CZ80_SP, 0xdff0);
 #endif
 }
 
-PICO_INTERNAL void z80_unpack(unsigned char *data)
+/* save state stuff */
+static int z80_unpack_legacy(const void *data)
 {
-#if defined(_USE_MZ80)
-  if (*(int *)data == 0x00005A6D) { // "mZ" save?
-    struct mz80context mz80;
-    mz80GetContext(&mz80);
-    memcpy(&mz80.z80clockticks, data+4, sizeof(mz80)-5*4);
-    mz80SetContext(&mz80);
-  } else {
-    z80_reset();
-    z80_int();
-  }
-#elif defined(_USE_DRZ80)
+#if defined(_USE_DRZ80)
   if (*(int *)data == 0x015A7244) { // "DrZ" v1 save?
-    int pc, sp;
+    u32 pc, sp;
     memcpy(&drZ80, data+4, 0x54);
     pc = (drZ80.Z80PC - drZ80.Z80PC_BASE) & 0xffff;
     sp = (drZ80.Z80SP - drZ80.Z80SP_BASE) & 0xffff;
     // update bases
-    drZ80.Z80PC_BASE = z80_read_map[pc >> Z80_MEM_SHIFT];
-    if (drZ80.Z80PC & (1<<31)) {
-      elprintf(EL_STATUS|EL_ANOMALY, "bad PC in z80 save: %04x", pc);
-      drZ80.Z80PC_BASE = drZ80.Z80PC = z80_read_map[0];
-    } else {
-      drZ80.Z80PC_BASE <<= 1;
-      drZ80.Z80PC = drZ80.Z80PC_BASE + pc;
-    }
-    drZ80.Z80SP_BASE = z80_read_map[sp >> Z80_MEM_SHIFT];
-    if (drZ80.Z80SP & (1<<31)) {
-      elprintf(EL_STATUS|EL_ANOMALY, "bad SP in z80 save: %04x", sp);
-      drZ80.Z80SP_BASE = z80_read_map[0];
-      drZ80.Z80SP = drZ80.Z80SP_BASE + (1 << Z80_MEM_SHIFT);
-    } else {
-      drZ80.Z80SP_BASE <<= 1;
-      drZ80.Z80SP = drZ80.Z80SP_BASE + sp;
-    }
-  } else {
-    z80_reset();
-    drZ80.Z80IM = 1;
-    z80_int(); // try to goto int handler, maybe we won't execute trash there?
+    drz80_load_pcsp(pc, sp);
+    return 0;
   }
 #elif defined(_USE_CZ80)
   if (*(int *)data == 0x00007a43) { // "Cz" save?
     memcpy(&CZ80, data+8, offsetof(cz80_struc, BasePC));
     Cz80_Set_Reg(&CZ80, CZ80_PC, *(int *)(data+4));
-  } else {
-    z80_reset();
-    z80_int();
+    return 0;
   }
 #endif
+  return -1;
 }
 
-PICO_INTERNAL void z80_exit(void)
+struct z80sr_main {
+  u8 a, f;
+  u8 b, c;
+  u8 d, e;
+  u8 h, l;
+};
+
+struct z80_state {
+  char magic[4];
+  // regs
+  struct z80sr_main m; // main regs
+  struct z80sr_main a; // alt (') regs
+  u8  i, r;
+  u16 ix, iy;
+  u16 sp;
+  u16 pc;
+  // other
+  u8 halted;
+  u8 iff1, iff2;
+  u8 im;            // irq mode
+  u8 irq_pending;   // irq line level, 1 if active
+  u8 irq_vector[3]; // up to 3 byte vector for irq mode0 handling
+  u8 reserved[8];
+};
+
+void z80_pack(void *data)
 {
-#if defined(_USE_MZ80)
-  mz80shutdown();
+  struct z80_state *s = data;
+  memset(data, 0, Z80_STATE_SIZE);
+  strcpy(s->magic, "Z80");
+#if defined(_USE_DRZ80)
+  #define DRR8(n)   (drZ80.Z80##n >> 24)
+  #define DRR16(n)  (drZ80.Z80##n >> 16)
+  #define DRR16H(n) (drZ80.Z80##n >> 24)
+  #define DRR16L(n) ((drZ80.Z80##n >> 16) & 0xff)
+  s->m.a = DRR8(A);     s->m.f = DRR8(F);
+  s->m.b = DRR16H(BC);  s->m.c = DRR16L(BC);
+  s->m.d = DRR16H(DE);  s->m.e = DRR16L(DE);
+  s->m.h = DRR16H(HL);  s->m.l = DRR16L(HL);
+  s->a.a = DRR8(A2);    s->a.f = DRR8(F2);
+  s->a.b = DRR16H(BC2); s->a.c = DRR16L(BC2);
+  s->a.d = DRR16H(DE2); s->a.e = DRR16L(DE2);
+  s->a.h = DRR16H(HL2); s->a.l = DRR16L(HL2);
+  s->i = DRR8(I);       s->r = drZ80.spare;
+  s->ix = DRR16(IX);    s->iy = DRR16(IY);
+  s->sp = drZ80.Z80SP - drZ80.Z80SP_BASE;
+  s->pc = drZ80.Z80PC - drZ80.Z80PC_BASE;
+  s->halted = !!(drZ80.Z80IF & 4);
+  s->iff1 = !!(drZ80.Z80IF & 1);
+  s->iff2 = !!(drZ80.Z80IF & 2);
+  s->im = drZ80.Z80IM;
+  s->irq_pending = !!drZ80.Z80_IRQ;
+  s->irq_vector[0] = drZ80.z80irqvector >> 16;
+  s->irq_vector[1] = drZ80.z80irqvector >> 8;
+  s->irq_vector[2] = drZ80.z80irqvector;
+#elif defined(_USE_CZ80)
+  {
+    const cz80_struc *CPU = &CZ80;
+    s->m.a = zA;  s->m.f = zF;
+    s->m.b = zB;  s->m.c = zC;
+    s->m.d = zD;  s->m.e = zE;
+    s->m.h = zH;  s->m.l = zL;
+    s->a.a = zA2; s->a.f = zF2;
+    s->a.b = CZ80.BC2.B.H; s->a.c = CZ80.BC2.B.L;
+    s->a.d = CZ80.DE2.B.H; s->a.e = CZ80.DE2.B.L;
+    s->a.h = CZ80.HL2.B.H; s->a.l = CZ80.HL2.B.L;
+    s->i  = zI;   s->r  = zR;
+    s->ix = zIX;  s->iy = zIY;
+    s->sp = Cz80_Get_Reg(&CZ80, CZ80_SP);
+    s->pc = Cz80_Get_Reg(&CZ80, CZ80_PC);
+    s->halted = !!Cz80_Get_Reg(&CZ80, CZ80_HALT);
+    s->iff1 = !!zIFF1;
+    s->iff2 = !!zIFF2;
+    s->im = zIM;
+    s->irq_pending = (Cz80_Get_Reg(&CZ80, CZ80_IRQ) == HOLD_LINE);
+    s->irq_vector[0] = 0xff;
+  }
 #endif
 }
 
-PICO_INTERNAL void z80_debug(char *dstr)
+int z80_unpack(const void *data)
+{
+  const struct z80_state *s = data;
+  if (strcmp(s->magic, "Z80") != 0) {
+    if (z80_unpack_legacy(data) != 0)
+      goto fail;
+    elprintf(EL_STATUS, "legacy z80 state");
+    return 0;
+  }
+
+#if defined(_USE_DRZ80)
+  #define DRW8(n, v)       drZ80.Z80##n = (u32)(v) << 24
+  #define DRW16(n, v)      drZ80.Z80##n = (u32)(v) << 16
+  #define DRW16HL(n, h, l) drZ80.Z80##n = ((u32)(h) << 24) | ((u32)(l) << 16)
+  DRW8(A, s->m.a);  DRW8(F, s->m.f);
+  DRW16HL(BC, s->m.b, s->m.c);
+  DRW16HL(DE, s->m.d, s->m.e);
+  DRW16HL(HL, s->m.h, s->m.l);
+  DRW8(A2, s->a.a); DRW8(F2, s->a.f);
+  DRW16HL(BC2, s->a.b, s->a.c);
+  DRW16HL(DE2, s->a.d, s->a.e);
+  DRW16HL(HL2, s->a.h, s->a.l);
+  DRW8(I, s->i);    drZ80.spare = s->r;
+  DRW16(IX, s->ix); DRW16(IY, s->iy);
+  drz80_load_pcsp(s->pc, s->sp);
+  drZ80.Z80IF = 0;
+  if (s->halted) drZ80.Z80IF |= 4;
+  if (s->iff1)   drZ80.Z80IF |= 1;
+  if (s->iff2)   drZ80.Z80IF |= 2;
+  drZ80.Z80IM = s->im;
+  drZ80.Z80_IRQ = s->irq_pending;
+  drZ80.z80irqvector = ((u32)s->irq_vector[0] << 16) |
+    ((u32)s->irq_vector[1] << 8) | s->irq_vector[2];
+  return 0;
+#elif defined(_USE_CZ80)
+  {
+    cz80_struc *CPU = &CZ80;
+    zA  = s->m.a; zF  = s->m.f;
+    zB  = s->m.b; zC  = s->m.c;
+    zD  = s->m.d; zE  = s->m.e;
+    zH  = s->m.h; zL  = s->m.l;
+    zA2 = s->a.a; zF2 = s->a.f;
+    CZ80.BC2.B.H = s->a.b; CZ80.BC2.B.L = s->a.c;
+    CZ80.DE2.B.H = s->a.d; CZ80.DE2.B.L = s->a.e;
+    CZ80.HL2.B.H = s->a.h; CZ80.HL2.B.L = s->a.l;
+    zI  = s->i;   zR  = s->r;
+    zIX = s->ix;  zIY = s->iy;
+    Cz80_Set_Reg(&CZ80, CZ80_SP, s->sp);
+    Cz80_Set_Reg(&CZ80, CZ80_PC, s->pc);
+    Cz80_Set_Reg(&CZ80, CZ80_HALT, s->halted);
+    Cz80_Set_Reg(&CZ80, CZ80_IFF1, s->iff1);
+    Cz80_Set_Reg(&CZ80, CZ80_IFF2, s->iff2);
+    zIM = s->im;
+    Cz80_Set_Reg(&CZ80, CZ80_IRQ, s->irq_pending ? HOLD_LINE : CLEAR_LINE);
+    return 0;
+  }
+#endif
+
+fail:
+  elprintf(EL_STATUS|EL_ANOMALY, "z80_unpack failed");
+  z80_reset();
+  z80_int();
+  return -1;
+}
+
+void z80_exit(void)
+{
+}
+
+void z80_debug(char *dstr)
 {
 #if defined(_USE_DRZ80)
   sprintf(dstr, "Z80 state: PC: %04x SP: %04x\n", drZ80.Z80PC-drZ80.Z80PC_BASE, drZ80.Z80SP-drZ80.Z80SP_BASE);

+ 2 - 7
platform/common/common.mak

@@ -25,13 +25,13 @@ endif
 
 # === Pico core ===
 # Pico
-OBJS += pico/area.o pico/cart.o pico/memory.o pico/pico.o pico/sek.o pico/z80if.o \
+OBJS += pico/state.o pico/cart.o pico/memory.o pico/pico.o pico/sek.o pico/z80if.o \
 	pico/videoport.o pico/draw2.o pico/draw.o pico/mode4.o pico/sms.o \
 	pico/misc.o pico/eeprom.o pico/patch.o pico/debug.o
 # CD
 OBJS += pico/cd/pico.o pico/cd/memory.o pico/cd/sek.o pico/cd/LC89510.o \
 	pico/cd/cd_sys.o pico/cd/cd_file.o pico/cd/cue.o pico/cd/gfx_cd.o \
-	pico/cd/area.o pico/cd/misc.o pico/cd/pcm.o pico/cd/buffering.o
+	pico/cd/misc.o pico/cd/pcm.o pico/cd/buffering.o
 # 32X
 OBJS += pico/32x/32x.o pico/32x/memory.o pico/32x/draw.o pico/32x/pwm.o
 # Pico
@@ -62,11 +62,6 @@ OBJS += cpu/fame/famec.o
 endif
 
 # --- Z80 ---
-ifeq "$(use_mz80)" "1"
-DEFINES += _USE_MZ80
-OBJS += cpu/mz80/mz80.o
-endif
-#
 ifeq "$(use_drz80)" "1"
 DEFINES += _USE_DRZ80
 OBJS += cpu/DrZ80/drz80.o

+ 15 - 23
platform/common/menu.c

@@ -19,7 +19,7 @@
 #include <version.h>
 #include <revision.h>
 
-#include <pico/pico_int.h>
+#include <pico/pico.h>
 #include <pico/patch.h>
 
 static char static_buff[64];
@@ -1039,34 +1039,22 @@ static void state_check_slots(void)
 
 static void draw_savestate_bg(int slot)
 {
-	struct PicoVideo tmp_pv;
-	unsigned short tmp_cram[0x40];
-	unsigned short tmp_vsram[0x40];
-	void *tmp_vram;
 	const char *fname;
+	void *tmp_state;
 
 	fname = emu_get_save_fname(1, 0, slot);
-	if (!fname) return;
-
-	tmp_vram = malloc(sizeof(Pico.vram));
-	if (tmp_vram == NULL) return;
+	if (!fname)
+		return;
 
-	memcpy(tmp_vram, Pico.vram, sizeof(Pico.vram));
-	memcpy(tmp_cram, Pico.cram, sizeof(Pico.cram));
-	memcpy(tmp_vsram, Pico.vsram, sizeof(Pico.vsram));
-	memcpy(&tmp_pv, &Pico.video, sizeof(Pico.video));
+	tmp_state = PicoTmpStateSave();
 
-	PicoStateLoadVDP(fname);
+	PicoStateLoadGfx(fname);
 
 	/* do a frame and fetch menu bg */
 	pemu_forced_frame(POPT_EN_SOFTSCALE);
 	menu_enter(1);
 
-	memcpy(Pico.vram, tmp_vram, sizeof(Pico.vram));
-	memcpy(Pico.cram, tmp_cram, sizeof(Pico.cram));
-	memcpy(Pico.vsram, tmp_vsram, sizeof(Pico.vsram));
-	memcpy(&Pico.video, &tmp_pv,  sizeof(Pico.video));
-	free(tmp_vram);
+	PicoTmpStateRestore(tmp_state);
 }
 
 static void draw_savestate_menu(int menu_sel, int is_loading)
@@ -1105,6 +1093,7 @@ static int menu_loop_savestate(int is_loading)
 	static int menu_sel = 10;
 	int menu_sel_max = 10;
 	unsigned long inp = 0;
+	int ret = 0;
 
 	state_check_slots();
 
@@ -1134,15 +1123,18 @@ static int menu_loop_savestate(int is_loading)
 				state_slot = menu_sel;
 				if (emu_save_load_game(is_loading, 0)) {
 					me_update_msg(is_loading ? "Load failed" : "Save failed");
-					return 0;
+					break;
 				}
-				return 1;
+				ret = 1;
+				break;
 			}
-			return 0;
+			break;
 		}
 		if (inp & PBTN_MBACK)
-			return 0;
+			break;
 	}
+
+	return ret;
 }
 
 // -------------- key config --------------