# HG changeset patch # User Mike Pavone # Date 1372551308 25200 # Node ID 7e8e179116af58cdf800d93c9a9b8b50a689cefd # Parent 8e136187c0e00f3710382546501c091c418fcd3d Add support for loading GST format savestates diff -r 8e136187c0e0 -r 7e8e179116af blastem.c --- a/blastem.c Tue Jun 25 23:18:57 2013 -0700 +++ b/blastem.c Sat Jun 29 17:15:08 2013 -0700 @@ -1463,6 +1463,190 @@ 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 @@ 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 @@ 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 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 @@ 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 @@ } set_keybindings(); - init_run_cpu(&gen, debug, address_log); + init_run_cpu(&gen, debug, address_log, statefile); return 0; } diff -r 8e136187c0e0 -r 7e8e179116af m68k_to_x86.c --- a/m68k_to_x86.c Tue Jun 25 23:18:57 2013 -0700 +++ b/m68k_to_x86.c Sat Jun 29 17:15:08 2013 -0700 @@ -4127,7 +4127,7 @@ 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 -r 8e136187c0e0 -r 7e8e179116af stateview.c --- a/stateview.c Tue Jun 25 23:18:57 2013 -0700 +++ b/stateview.c Sat Jun 29 17:15:08 2013 -0700 @@ -36,7 +36,7 @@ } 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 -r 8e136187c0e0 -r 7e8e179116af vdp.c --- a/vdp.c Tue Jun 25 23:18:57 2013 -0700 +++ b/vdp.c Sat Jun 29 17:15:08 2013 -0700 @@ -1653,22 +1653,39 @@ #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 -r 8e136187c0e0 -r 7e8e179116af vdp.h --- a/vdp.h Tue Jun 25 23:18:57 2013 -0700 +++ b/vdp.h Sat Jun 29 17:15:08 2013 -0700 @@ -156,7 +156,7 @@ 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 -r 8e136187c0e0 -r 7e8e179116af ym2612.c --- a/ym2612.c Tue Jun 25 23:18:57 2013 -0700 +++ b/ym2612.c Sat Jun 29 17:15:08 2013 -0700 @@ -763,3 +763,24 @@ 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 -r 8e136187c0e0 -r 7e8e179116af ym2612.h --- a/ym2612.h Tue Jun 25 23:18:57 2013 -0700 +++ b/ym2612.h Sat Jun 29 17:15:08 2013 -0700 @@ -85,6 +85,7 @@ 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 -r 8e136187c0e0 -r 7e8e179116af z80_to_x86.c --- a/z80_to_x86.c Tue Jun 25 23:18:57 2013 -0700 +++ b/z80_to_x86.c Sat Jun 29 17:15:08 2013 -0700 @@ -1637,7 +1637,7 @@ 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 -r 8e136187c0e0 -r 7e8e179116af z80_to_x86.h --- a/z80_to_x86.h Tue Jun 25 23:18:57 2013 -0700 +++ b/z80_to_x86.h Sat Jun 29 17:15:08 2013 -0700 @@ -56,6 +56,7 @@ 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);