From b4db550e41b2aa277f570d7bff890c8e8ee1831f Mon Sep 17 00:00:00 2001 From: notaz Date: Wed, 27 Jan 2010 16:30:41 +0000 Subject: [PATCH] 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 --- cpu/DrZ80/drz80.s | 41 ++- cpu/cz80/cz80.c | 4 +- cpu/sh2/sh2.c | 30 ++ cpu/sh2/sh2.h | 5 + pico/32x/memory.c | 26 +- pico/32x/pwm.c | 1 - pico/area.c | 300 ---------------- pico/cart.c | 3 + pico/cd/area.c | 321 ------------------ pico/memory.c | 2 +- pico/memory_arm.s | 8 +- pico/pico.h | 8 +- pico/pico_int.h | 58 ++-- pico/sek.c | 84 ++++- pico/sms.c | 16 +- pico/state.c | 679 +++++++++++++++++++++++++++++++++++++ pico/z80if.c | 325 +++++++++++------- platform/common/common.mak | 9 +- platform/common/menu.c | 38 +-- 19 files changed, 1113 insertions(+), 845 deletions(-) delete mode 100644 pico/area.c delete mode 100644 pico/cd/area.c create mode 100644 pico/state.c diff --git a/cpu/DrZ80/drz80.s b/cpu/DrZ80/drz80.s index 499c856..edb29df 100644 --- a/cpu/DrZ80/drz80.s +++ b/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<HaltState = val; break; case CZ80_IRQ: CPU->IRQState = val; break; default: break; diff --git a/cpu/sh2/sh2.c b/cpu/sh2/sh2.c index 80c458f..0e6be67 100644 --- a/cpu/sh2/sh2.c +++ b/cpu/sh2/sh2.c @@ -1,4 +1,6 @@ #include +#include + #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]; +} + diff --git a/cpu/sh2/sh2.h b/cpu/sh2/sh2.h index a69a159..e15259d 100644 --- a/cpu/sh2/sh2.h +++ b/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); diff --git a/pico/32x/memory.c b/pico/32x/memory.c index ee33caa..c720547 100644 --- a/pico/32x/memory.c +++ b/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 diff --git a/pico/32x/pwm.c b/pico/32x/pwm.c index 8e36589..defbf0d 100644 --- a/pico/32x/pwm.c +++ b/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; diff --git a/pico/area.c b/pico/area.c deleted file mode 100644 index 23e00da..0000000 --- a/pico/area.c +++ /dev/null @@ -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 - -// 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; -} - diff --git a/pico/cart.c b/pico/cart.c index fa283c8..8ac330a 100644 --- a/pico/cart.c +++ b/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), diff --git a/pico/cd/area.c b/pico/cd/area.c deleted file mode 100644 index 645993b..0000000 --- a/pico/cd/area.c +++ /dev/null @@ -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; -} - - diff --git a/pico/memory.c b/pico/memory.c index d0b1697..00e5016 100644 --- a/pico/memory.c +++ b/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(); diff --git a/pico/memory_arm.s b/pico/memory_arm.s index dde8342..972b918 100644 --- a/pico/memory_arm.s +++ b/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 diff --git a/pico/pico.h b/pico/pico.h index c614aa9..870444a 100644 --- a/pico/pico.h +++ b/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); diff --git a/pico/pico_int.h b/pico/pico_int.h index 3ee4ea1..74d1da2 100644 --- a/pico/pico_int.h +++ b/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 diff --git a/pico/sek.c b/pico/sek.c index fe381f2..bc44c47 100644 --- a/pico/sek.c +++ b/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 diff --git a/pico/sms.c b/pico/sms.c index e94e731..cc75bff 100644 --- a/pico/sms.c +++ b/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; diff --git a/pico/state.c b/pico/state.c new file mode 100644 index 0000000..60a6f72 --- /dev/null +++ b/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 + +#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 diff --git a/pico/z80if.c b/pico/z80if.c index 454938b..10c438a 100644 --- a/pico/z80if.c +++ b/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); diff --git a/platform/common/common.mak b/platform/common/common.mak index 061771e..2a240b0 100644 --- a/platform/common/common.mak +++ b/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 diff --git a/platform/common/menu.c b/platform/common/menu.c index 34d5b49..5b0474a 100644 --- a/platform/common/menu.c +++ b/platform/common/menu.c @@ -19,7 +19,7 @@ #include #include -#include +#include #include 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 -------------- -- 2.39.2