summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Pavone <pavone@retrodev.com>2013-06-29 17:15:08 -0700
committerMike Pavone <pavone@retrodev.com>2013-06-29 17:15:08 -0700
commit48718d1f339e263fb52a35a7a5230d32491b78b8 (patch)
tree16eb423306ce41301c188ffa4ed8193b438e334b
parent6183aae8b8ff8deb39d5e177896ef62bc72df7c0 (diff)
Add support for loading GST format savestates
-rw-r--r--blastem.c214
-rw-r--r--m68k_to_x86.c2
-rw-r--r--stateview.c2
-rw-r--r--vdp.c27
-rw-r--r--vdp.h2
-rw-r--r--ym2612.c21
-rw-r--r--ym2612.h1
-rw-r--r--z80_to_x86.c2
-rw-r--r--z80_to_x86.h1
9 files changed, 258 insertions, 14 deletions
diff --git a/blastem.c b/blastem.c
index 757f5d9..91c7de9 100644
--- a/blastem.c
+++ b/blastem.c
@@ -1463,6 +1463,190 @@ m68k_context * debugger(m68k_context * context, uint32_t address)
return context;
}
+#define GST_68K_REGS 0x80
+#define GST_68K_REG_SIZE (0xDA-GST_68K_REGS)
+#define GST_68K_PC_OFFSET (0xC8-GST_68K_REGS)
+#define GST_68K_SR_OFFSET (0xD0-GST_68K_REGS)
+#define GST_68K_USP_OFFSET (0xD2-GST_68K_REGS)
+#define GST_68K_SSP_OFFSET (0xD6-GST_68K_REGS)
+#define GST_68K_RAM 0x2478
+#define GST_Z80_REGS 0x404
+#define GST_Z80_REG_SIZE (0x440-GST_Z80_REGS)
+#define GST_Z80_RAM 0x474
+
+uint32_t read_le_32(uint8_t * data)
+{
+ return data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
+}
+
+uint16_t read_le_16(uint8_t * data)
+{
+ return data[1] << 8 | data[0];
+}
+
+uint16_t read_be_16(uint8_t * data)
+{
+ return data[0] << 8 | data[1];
+}
+
+uint32_t m68k_load_gst(m68k_context * context, FILE * gstfile)
+{
+ uint8_t buffer[4096];
+ fseek(gstfile, GST_68K_REGS, SEEK_SET);
+ if (fread(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) {
+ fputs("Failed to read 68K registers from savestate\n", stderr);
+ return 0;
+ }
+ uint8_t * curpos = buffer;
+ for (int i = 0; i < 8; i++) {
+ context->dregs[i] = read_le_32(curpos);
+ curpos += sizeof(uint32_t);
+ }
+ for (int i = 0; i < 8; i++) {
+ context->aregs[i] = read_le_32(curpos);
+ curpos += sizeof(uint32_t);
+ }
+ uint32_t pc = read_le_32(buffer + GST_68K_PC_OFFSET);
+ uint16_t sr = read_le_16(buffer + GST_68K_SR_OFFSET);
+ context->status = sr >> 8;
+ for (int flag = 4; flag >= 0; flag--) {
+ context->flags[flag] = sr & 1;
+ sr >>= 1;
+ }
+ if (context->status & (1 << 5)) {
+ context->aregs[8] = read_le_32(buffer + GST_68K_USP_OFFSET);
+ } else {
+ context->aregs[8] = read_le_32(buffer + GST_68K_SSP_OFFSET);
+ }
+ fseek(gstfile, GST_68K_RAM, SEEK_SET);
+ for (int i = 0; i < (32*1024);) {
+ if (fread(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) {
+ fputs("Failed to read 68K RAM from savestate\n", stderr);
+ return 0;
+ }
+ for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) {
+ context->mem_pointers[1][i++] = read_be_16(curpos);
+ }
+ }
+ return pc;
+}
+
+uint8_t z80_load_gst(z80_context * context, FILE * gstfile)
+{
+ uint8_t regdata[GST_Z80_REG_SIZE];
+ fseek(gstfile, GST_Z80_REGS, SEEK_SET);
+ if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) {
+ fputs("Failed to read Z80 registers from savestate\n", stderr);
+ return 0;
+ }
+ uint8_t * curpos = regdata;
+ uint8_t f = *(curpos++);
+ context->flags[ZF_C] = f & 1;
+ f >>= 1;
+ context->flags[ZF_N] = f & 1;
+ f >>= 1;
+ context->flags[ZF_PV] = f & 1;
+ f >>= 2;
+ context->flags[ZF_H] = f & 1;
+ f >>= 2;
+ context->flags[ZF_Z] = f & 1;
+ f >>= 1;
+ context->flags[ZF_S] = f;
+
+ context->regs[Z80_A] = *curpos;
+ curpos += 3;
+ for (int reg = Z80_C; reg <= Z80_IYH; reg++) {
+ context->regs[reg++] = *(curpos++);
+ context->regs[reg] = *curpos;
+ curpos += 3;
+ }
+ uint16_t pc = read_le_16(curpos);
+ curpos += 4;
+ context->sp = read_le_16(curpos);
+ curpos += 4;
+ f = *(curpos++);
+ context->alt_flags[ZF_C] = f & 1;
+ f >>= 1;
+ context->alt_flags[ZF_N] = f & 1;
+ f >>= 1;
+ context->alt_flags[ZF_PV] = f & 1;
+ f >>= 2;
+ context->alt_flags[ZF_H] = f & 1;
+ f >>= 2;
+ context->alt_flags[ZF_Z] = f & 1;
+ f >>= 1;
+ context->alt_flags[ZF_S] = f;
+ context->alt_regs[Z80_A] = *curpos;
+ curpos += 3;
+ for (int reg = Z80_C; reg <= Z80_H; reg++) {
+ context->alt_regs[reg++] = *(curpos++);
+ context->alt_regs[reg] = *curpos;
+ curpos += 3;
+ }
+ context->regs[Z80_I] = *curpos;
+ curpos += 2;
+ context->iff1 = context->iff2 = *curpos;
+ curpos += 2;
+ reset = !*(curpos++);
+ busreq = *curpos;
+ curpos += 3;
+ uint32_t bank = read_le_32(curpos);
+ if (bank < 0x400000) {
+ context->mem_pointers[1] = context->mem_pointers[2] + bank;
+ } else {
+ context->mem_pointers[1] = NULL;
+ }
+ context->bank_reg = bank >> 15;
+ fseek(gstfile, GST_Z80_RAM, SEEK_SET);
+ if(fread(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) {
+ fputs("Failed to read Z80 RAM from savestate\n", stderr);
+ return 0;
+ }
+ context->native_pc = z80_get_native_address_trans(context, pc);
+ return 1;
+}
+
+uint32_t load_gst(genesis_context * gen, char * fname)
+{
+ FILE * gstfile = fopen(fname, "rb");
+ if (!gstfile) {
+ fprintf(stderr, "Could not open file %s for reading\n", fname);
+ goto error;
+ }
+ char ident[5];
+ if (fread(ident, 1, sizeof(ident), gstfile) != sizeof(ident)) {
+ fprintf(stderr, "Could not read ident code from %s\n", fname);
+ goto error_close;
+ }
+ if (memcmp(ident, "GST\xE0\x40", 3) != 0) {
+ fprintf(stderr, "%s doesn't appear to be a GST savestate. The ident code is %c%c%c\\x%X\\x%X instead of GST\\xE0\\x40.\n", fname, ident[0], ident[1], ident[2], ident[3], ident[4]);
+ goto error_close;
+ }
+ uint32_t pc = m68k_load_gst(gen->m68k, gstfile);
+ if (!pc) {
+ goto error_close;
+ }
+ if (!vdp_load_gst(gen->vdp, gstfile)) {
+ goto error_close;
+ }
+ if (!ym_load_gst(gen->ym, gstfile)) {
+ goto error_close;
+ }
+ if (!z80_load_gst(gen->z80, gstfile)) {
+ goto error_close;
+ }
+ gen->ports[0].control = 0x40;
+ gen->ports[1].control = 0x40;
+ adjust_int_cycle(gen->m68k, gen->vdp);
+ fclose(gstfile);
+ return pc;
+
+error_close:
+ fclose(gstfile);
+error:
+ return 0;
+}
+
#define ROM_END 0x1A4
#define RAM_ID 0x1B0
#define RAM_FLAGS 0x1B2
@@ -1502,7 +1686,7 @@ void save_sram()
printf("Saved SRAM to %s\n", sram_filename);
}
-void init_run_cpu(genesis_context * gen, int debug, FILE * address_log)
+void init_run_cpu(genesis_context * gen, int debug, FILE * address_log, char * statefile)
{
m68k_context context;
x86_68k_options opts;
@@ -1616,10 +1800,21 @@ void init_run_cpu(genesis_context * gen, int debug, FILE * address_log)
uint32_t address;
address = cart[2] << 16 | cart[3];
translate_m68k_stream(address, &context);
- if (debug) {
- insert_breakpoint(&context, address, (uint8_t *)debugger);
+ if (statefile) {
+ uint32_t pc = load_gst(gen, statefile);
+ if (!pc) {
+ exit(1);
+ }
+ if (debug) {
+ insert_breakpoint(&context, pc, (uint8_t *)debugger);
+ }
+ start_68k_context(&context, pc);
+ } else {
+ if (debug) {
+ insert_breakpoint(&context, address, (uint8_t *)debugger);
+ }
+ m68k_reset(&context);
}
- m68k_reset(&context);
}
char title[64];
@@ -1689,6 +1884,7 @@ int main(int argc, char ** argv)
int debug = 0;
int ym_log = 0;
FILE *address_log = NULL;
+ char * statefile;
for (int i = 2; i < argc; i++) {
if (argv[i][0] == '-') {
switch(argv[i][1]) {
@@ -1732,6 +1928,14 @@ int main(int argc, char ** argv)
return 1;
}
break;
+ case 's':
+ i++;
+ if (i >= argc) {
+ fputs("-s must be followed by a savestate filename\n", stderr);
+ return 1;
+ }
+ statefile = argv[i];
+ break;
case 'y':
ym_log = 1;
break;
@@ -1801,6 +2005,6 @@ int main(int argc, char ** argv)
}
set_keybindings();
- init_run_cpu(&gen, debug, address_log);
+ init_run_cpu(&gen, debug, address_log, statefile);
return 0;
}
diff --git a/m68k_to_x86.c b/m68k_to_x86.c
index b35494e..6ebc96d 100644
--- a/m68k_to_x86.c
+++ b/m68k_to_x86.c
@@ -4127,7 +4127,7 @@ void remove_breakpoint(m68k_context * context, uint32_t address)
void start_68k_context(m68k_context * context, uint32_t address)
{
- uint8_t * addr = get_native_address(context->native_code_map, address);
+ uint8_t * addr = get_native_address_trans(context, address);
m68k_start_context(addr, context);
}
diff --git a/stateview.c b/stateview.c
index db19cfa..10120e9 100644
--- a/stateview.c
+++ b/stateview.c
@@ -36,7 +36,7 @@ int main(int argc, char ** argv)
}
vdp_context context;
init_vdp_context(&context);
- vdp_load_savestate(&context, state_file);
+ vdp_load_gst(&context, state_file);
vdp_run_to_vblank(&context);
vdp_print_sprite_table(&context);
printf("Display %s\n", (context.regs[REG_MODE_2] & DISPLAY_ENABLE) ? "enabled" : "disabled");
diff --git a/vdp.c b/vdp.c
index fc340a6..0ccdd26 100644
--- a/vdp.c
+++ b/vdp.c
@@ -1653,22 +1653,39 @@ void vdp_int_ack(vdp_context * context, uint16_t int_num)
#define GST_VDP_REGS 0xFA
#define GST_VDP_MEM 0x12478
-void vdp_load_savestate(vdp_context * context, FILE * state_file)
+uint8_t vdp_load_gst(vdp_context * context, FILE * state_file)
{
uint8_t tmp_buf[CRAM_SIZE*2];
fseek(state_file, GST_VDP_REGS, SEEK_SET);
- fread(context->regs, 1, VDP_REGS, state_file);
+ if (fread(context->regs, 1, VDP_REGS, state_file) != VDP_REGS) {
+ fputs("Failed to read VDP registers from savestate\n", stderr);
+ return 0;
+ }
+ context->double_res = (context->regs[REG_MODE_4] & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
+ if (!context->double_res) {
+ context->framebuf = context->oddbuf;
+ }
latch_mode(context);
- fread(tmp_buf, 1, sizeof(tmp_buf), state_file);
+ if (fread(tmp_buf, 1, sizeof(tmp_buf), state_file) != sizeof(tmp_buf)) {
+ fputs("Failed to read CRAM from savestate\n", stderr);
+ return 0;
+ }
for (int i = 0; i < CRAM_SIZE; i++) {
context->cram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2];
}
- fread(tmp_buf, 2, VSRAM_SIZE, state_file);
+ if (fread(tmp_buf, 2, VSRAM_SIZE, state_file) != VSRAM_SIZE) {
+ fputs("Failed to read VSRAM from savestate\n", stderr);
+ return 0;
+ }
for (int i = 0; i < VSRAM_SIZE; i++) {
context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2];
}
fseek(state_file, GST_VDP_MEM, SEEK_SET);
- fread(context->vdpmem, 1, VRAM_SIZE, state_file);
+ if (fread(context->vdpmem, 1, VRAM_SIZE, state_file) != VRAM_SIZE) {
+ fputs("Failed to read VRAM from savestate\n", stderr);
+ return 0;
+ }
+ return 1;
}
void vdp_save_state(vdp_context * context, FILE * outfile)
diff --git a/vdp.h b/vdp.h
index 2f17d6e..d547397 100644
--- a/vdp.h
+++ b/vdp.h
@@ -156,7 +156,7 @@ void vdp_run_context(vdp_context * context, uint32_t target_cycles);
uint32_t vdp_run_to_vblank(vdp_context * context);
//runs until the target cycle is reached or the current DMA operation has completed, whicever comes first
void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles);
-void vdp_load_savestate(vdp_context * context, FILE * state_file);
+uint8_t vdp_load_gst(vdp_context * context, FILE * state_file);
void vdp_save_state(vdp_context * context, FILE * outfile);
int vdp_control_port_write(vdp_context * context, uint16_t value);
int vdp_data_port_write(vdp_context * context, uint16_t value);
diff --git a/ym2612.c b/ym2612.c
index c2952bd..facba3a 100644
--- a/ym2612.c
+++ b/ym2612.c
@@ -763,3 +763,24 @@ uint8_t ym_read_status(ym2612_context * context)
return context->status;
}
+#define GST_YM_OFFSET 0x1E4
+#define GST_YM_SIZE (0x3E4-GST_YM_OFFSET)
+
+uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile)
+{
+ uint8_t regdata[GST_YM_SIZE];
+ fseek(gstfile, GST_YM_OFFSET, SEEK_SET);
+ if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) {
+ return 0;
+ }
+ for (int i = 0; i < sizeof(regdata); i++) {
+ if (i & 0x100) {
+ ym_address_write_part2(context, i & 0xFF);
+ } else {
+ ym_address_write_part1(context, i);
+ }
+ ym_data_write(context, regdata[i]);
+ }
+ return 1;
+}
+
diff --git a/ym2612.h b/ym2612.h
index e905f44..b0e3526 100644
--- a/ym2612.h
+++ b/ym2612.h
@@ -85,6 +85,7 @@ void ym_address_write_part1(ym2612_context * context, uint8_t address);
void ym_address_write_part2(ym2612_context * context, uint8_t address);
void ym_data_write(ym2612_context * context, uint8_t value);
uint8_t ym_read_status(ym2612_context * context);
+uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile);
#endif //YM2612_H_
diff --git a/z80_to_x86.c b/z80_to_x86.c
index 6ac701d..7f0ccf3 100644
--- a/z80_to_x86.c
+++ b/z80_to_x86.c
@@ -1637,7 +1637,7 @@ uint8_t * translate_z80inst(z80inst * inst, uint8_t * dst, z80_context * context
default: {
char disbuf[80];
z80_disasm(inst, disbuf, address);
- fprintf(stderr, "unimplemented instruction: %s\n", disbuf);
+ fprintf(stderr, "unimplemented instruction: %s at %X\n", disbuf, address);
FILE * f = fopen("zram.bin", "wb");
fwrite(context->mem_pointers[0], 1, 8 * 1024, f);
fclose(f);
diff --git a/z80_to_x86.h b/z80_to_x86.h
index 5bcc3a8..9f70803 100644
--- a/z80_to_x86.h
+++ b/z80_to_x86.h
@@ -56,6 +56,7 @@ void translate_z80_stream(z80_context * context, uint32_t address);
void init_x86_z80_opts(x86_z80_options * options);
void init_z80_context(z80_context * context, x86_z80_options * options);
uint8_t * z80_get_native_address(z80_context * context, uint32_t address);
+uint8_t * z80_get_native_address_trans(z80_context * context, uint32_t address);
z80_context * z80_handle_code_write(uint32_t address, z80_context * context);
void z80_run(z80_context * context);
void z80_reset(z80_context * context);