Mercurial > repos > blastem
comparison gst.c @ 451:b7c3b2d22858
Added support for saving savestates. Added gst savestate format test harness
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 26 Jul 2013 19:55:04 -0700 |
parents | |
children | 140af5509ce7 |
comparison
equal
deleted
inserted
replaced
448:e85a107e6ec0 | 451:b7c3b2d22858 |
---|---|
1 #include "gst.h" | |
2 #include <string.h> | |
3 | |
4 #define GST_68K_REGS 0x80 | |
5 #define GST_68K_REG_SIZE (0xDA-GST_68K_REGS) | |
6 #define GST_68K_PC_OFFSET (0xC8-GST_68K_REGS) | |
7 #define GST_68K_SR_OFFSET (0xD0-GST_68K_REGS) | |
8 #define GST_68K_USP_OFFSET (0xD2-GST_68K_REGS) | |
9 #define GST_68K_SSP_OFFSET (0xD6-GST_68K_REGS) | |
10 #define GST_68K_RAM 0x2478 | |
11 #define GST_Z80_REGS 0x404 | |
12 #define GST_Z80_REG_SIZE (0x440-GST_Z80_REGS) | |
13 #define GST_Z80_RAM 0x474 | |
14 #define GST_VDP_REGS 0xFA | |
15 #define GST_VDP_MEM 0x12478 | |
16 #define GST_YM_OFFSET 0x1E4 | |
17 #define GST_YM_SIZE (0x3E4-GST_YM_OFFSET) | |
18 | |
19 uint32_t read_le_32(uint8_t * data) | |
20 { | |
21 return data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; | |
22 } | |
23 | |
24 uint16_t read_le_16(uint8_t * data) | |
25 { | |
26 return data[1] << 8 | data[0]; | |
27 } | |
28 | |
29 uint16_t read_be_16(uint8_t * data) | |
30 { | |
31 return data[0] << 8 | data[1]; | |
32 } | |
33 | |
34 void write_le_32(uint8_t * dst, uint32_t val) | |
35 { | |
36 dst[0] = val; | |
37 dst[1] = val >> 8; | |
38 dst[2] = val >> 16; | |
39 dst[3] = val >> 24; | |
40 } | |
41 | |
42 void write_le_16(uint8_t * dst, uint16_t val) | |
43 { | |
44 dst[0] = val; | |
45 dst[1] = val >> 8; | |
46 } | |
47 | |
48 void write_be_32(uint8_t * dst, uint32_t val) | |
49 { | |
50 dst[0] = val >> 24; | |
51 dst[1] = val >> 16; | |
52 dst[2] = val >> 8; | |
53 dst[3] = val; | |
54 } | |
55 | |
56 void write_be_16(uint8_t * dst, uint16_t val) | |
57 { | |
58 dst[0] = val >> 8; | |
59 dst[1] = val; | |
60 } | |
61 | |
62 uint32_t m68k_load_gst(m68k_context * context, FILE * gstfile) | |
63 { | |
64 uint8_t buffer[4096]; | |
65 fseek(gstfile, GST_68K_REGS, SEEK_SET); | |
66 if (fread(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) { | |
67 fputs("Failed to read 68K registers from savestate\n", stderr); | |
68 return 0; | |
69 } | |
70 uint8_t * curpos = buffer; | |
71 for (int i = 0; i < 8; i++) { | |
72 context->dregs[i] = read_le_32(curpos); | |
73 curpos += sizeof(uint32_t); | |
74 } | |
75 for (int i = 0; i < 8; i++) { | |
76 context->aregs[i] = read_le_32(curpos); | |
77 curpos += sizeof(uint32_t); | |
78 } | |
79 uint32_t pc = read_le_32(buffer + GST_68K_PC_OFFSET); | |
80 uint16_t sr = read_le_16(buffer + GST_68K_SR_OFFSET); | |
81 context->status = sr >> 8; | |
82 for (int flag = 4; flag >= 0; flag--) { | |
83 context->flags[flag] = sr & 1; | |
84 sr >>= 1; | |
85 } | |
86 if (context->status & (1 << 5)) { | |
87 context->aregs[8] = read_le_32(buffer + GST_68K_USP_OFFSET); | |
88 } else { | |
89 context->aregs[8] = read_le_32(buffer + GST_68K_SSP_OFFSET); | |
90 } | |
91 fseek(gstfile, GST_68K_RAM, SEEK_SET); | |
92 for (int i = 0; i < (32*1024);) { | |
93 if (fread(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) { | |
94 fputs("Failed to read 68K RAM from savestate\n", stderr); | |
95 return 0; | |
96 } | |
97 for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) { | |
98 context->mem_pointers[1][i++] = read_be_16(curpos); | |
99 } | |
100 } | |
101 return pc; | |
102 } | |
103 | |
104 uint8_t m68k_save_gst(m68k_context * context, uint32_t pc, FILE * gstfile) | |
105 { | |
106 uint8_t buffer[4096]; | |
107 uint8_t * curpos = buffer; | |
108 for (int i = 0; i < 8; i++) { | |
109 write_le_32(curpos, context->dregs[i]); | |
110 curpos += sizeof(uint32_t); | |
111 } | |
112 for (int i = 0; i < 8; i++) { | |
113 write_le_32(curpos, context->aregs[i]); | |
114 curpos += sizeof(uint32_t); | |
115 } | |
116 write_le_32(buffer + GST_68K_PC_OFFSET, pc); | |
117 uint16_t sr = context->status << 3; | |
118 for (int flag = 4; flag >= 0; flag--) { | |
119 sr <<= 1; | |
120 sr |= context->flags[flag]; | |
121 } | |
122 write_le_16(buffer + GST_68K_SR_OFFSET, sr); | |
123 if (context->status & (1 << 5)) { | |
124 write_le_32(buffer + GST_68K_USP_OFFSET, context->aregs[8]); | |
125 write_le_32(buffer + GST_68K_SSP_OFFSET, context->aregs[7]); | |
126 } else { | |
127 write_le_32(buffer + GST_68K_USP_OFFSET, context->aregs[7]); | |
128 write_le_32(buffer + GST_68K_SSP_OFFSET, context->aregs[8]); | |
129 } | |
130 fseek(gstfile, GST_68K_REGS, SEEK_SET); | |
131 if (fwrite(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) { | |
132 fputs("Failed to write 68K registers to savestate\n", stderr); | |
133 return 0; | |
134 } | |
135 | |
136 fseek(gstfile, GST_68K_RAM, SEEK_SET); | |
137 for (int i = 0; i < (32*1024);) { | |
138 for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) { | |
139 write_be_16(curpos, context->mem_pointers[1][i++]); | |
140 } | |
141 if (fwrite(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) { | |
142 fputs("Failed to write 68K RAM to savestate\n", stderr); | |
143 return 0; | |
144 } | |
145 } | |
146 return 1; | |
147 } | |
148 | |
149 uint8_t z80_load_gst(z80_context * context, FILE * gstfile) | |
150 { | |
151 uint8_t regdata[GST_Z80_REG_SIZE]; | |
152 fseek(gstfile, GST_Z80_REGS, SEEK_SET); | |
153 if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { | |
154 fputs("Failed to read Z80 registers from savestate\n", stderr); | |
155 return 0; | |
156 } | |
157 uint8_t * curpos = regdata; | |
158 uint8_t f = *(curpos++); | |
159 context->flags[ZF_C] = f & 1; | |
160 f >>= 1; | |
161 context->flags[ZF_N] = f & 1; | |
162 f >>= 1; | |
163 context->flags[ZF_PV] = f & 1; | |
164 f >>= 2; | |
165 context->flags[ZF_H] = f & 1; | |
166 f >>= 2; | |
167 context->flags[ZF_Z] = f & 1; | |
168 f >>= 1; | |
169 context->flags[ZF_S] = f; | |
170 | |
171 context->regs[Z80_A] = *curpos; | |
172 curpos += 3; | |
173 for (int reg = Z80_C; reg <= Z80_IYH; reg++) { | |
174 context->regs[reg++] = *(curpos++); | |
175 context->regs[reg] = *curpos; | |
176 curpos += 3; | |
177 } | |
178 context->pc = read_le_16(curpos); | |
179 curpos += 4; | |
180 context->sp = read_le_16(curpos); | |
181 curpos += 4; | |
182 f = *(curpos++); | |
183 context->alt_flags[ZF_C] = f & 1; | |
184 f >>= 1; | |
185 context->alt_flags[ZF_N] = f & 1; | |
186 f >>= 1; | |
187 context->alt_flags[ZF_PV] = f & 1; | |
188 f >>= 2; | |
189 context->alt_flags[ZF_H] = f & 1; | |
190 f >>= 2; | |
191 context->alt_flags[ZF_Z] = f & 1; | |
192 f >>= 1; | |
193 context->alt_flags[ZF_S] = f; | |
194 context->alt_regs[Z80_A] = *curpos; | |
195 curpos += 3; | |
196 for (int reg = Z80_C; reg <= Z80_H; reg++) { | |
197 context->alt_regs[reg++] = *(curpos++); | |
198 context->alt_regs[reg] = *curpos; | |
199 curpos += 3; | |
200 } | |
201 context->regs[Z80_I] = *curpos; | |
202 curpos += 2; | |
203 context->iff1 = context->iff2 = *curpos; | |
204 curpos += 2; | |
205 reset = !*(curpos++); | |
206 busreq = *curpos; | |
207 curpos += 3; | |
208 uint32_t bank = read_le_32(curpos); | |
209 if (bank < 0x400000) { | |
210 context->mem_pointers[1] = context->mem_pointers[2] + bank; | |
211 } else { | |
212 context->mem_pointers[1] = NULL; | |
213 } | |
214 context->bank_reg = bank >> 15; | |
215 fseek(gstfile, GST_Z80_RAM, SEEK_SET); | |
216 if(fread(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) { | |
217 fputs("Failed to read Z80 RAM from savestate\n", stderr); | |
218 return 0; | |
219 } | |
220 return 1; | |
221 } | |
222 | |
223 uint8_t vdp_load_gst(vdp_context * context, FILE * state_file) | |
224 { | |
225 uint8_t tmp_buf[CRAM_SIZE*2]; | |
226 fseek(state_file, GST_VDP_REGS, SEEK_SET); | |
227 if (fread(context->regs, 1, VDP_REGS, state_file) != VDP_REGS) { | |
228 fputs("Failed to read VDP registers from savestate\n", stderr); | |
229 return 0; | |
230 } | |
231 context->double_res = (context->regs[REG_MODE_4] & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); | |
232 if (!context->double_res) { | |
233 context->framebuf = context->oddbuf; | |
234 } | |
235 latch_mode(context); | |
236 if (fread(tmp_buf, 1, sizeof(tmp_buf), state_file) != sizeof(tmp_buf)) { | |
237 fputs("Failed to read CRAM from savestate\n", stderr); | |
238 return 0; | |
239 } | |
240 for (int i = 0; i < CRAM_SIZE; i++) { | |
241 uint16_t value; | |
242 context->cram[i] = value = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; | |
243 context->colors[i] = color_map[value & 0xEEE]; | |
244 context->colors[i + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW]; | |
245 context->colors[i + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT]; | |
246 } | |
247 if (fread(tmp_buf, 2, VSRAM_SIZE, state_file) != VSRAM_SIZE) { | |
248 fputs("Failed to read VSRAM from savestate\n", stderr); | |
249 return 0; | |
250 } | |
251 for (int i = 0; i < VSRAM_SIZE; i++) { | |
252 context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; | |
253 } | |
254 fseek(state_file, GST_VDP_MEM, SEEK_SET); | |
255 if (fread(context->vdpmem, 1, VRAM_SIZE, state_file) != VRAM_SIZE) { | |
256 fputs("Failed to read VRAM from savestate\n", stderr); | |
257 return 0; | |
258 } | |
259 return 1; | |
260 } | |
261 | |
262 uint8_t vdp_save_gst(vdp_context * context, FILE * outfile) | |
263 { | |
264 uint8_t tmp_buf[CRAM_SIZE*2]; | |
265 fseek(outfile, GST_VDP_REGS, SEEK_SET); | |
266 if(fwrite(context->regs, 1, VDP_REGS, outfile) != VDP_REGS) { | |
267 fputs("Error writing VDP regs to savestate\n", stderr); | |
268 return 0; | |
269 } | |
270 for (int i = 0; i < CRAM_SIZE; i++) | |
271 { | |
272 tmp_buf[i*2] = context->cram[i]; | |
273 tmp_buf[i*2+1] = context->cram[i] >> 8; | |
274 } | |
275 if (fwrite(tmp_buf, 1, sizeof(tmp_buf), outfile) != sizeof(tmp_buf)) { | |
276 fputs("Error writing CRAM to savestate\n", stderr); | |
277 return 0; | |
278 } | |
279 for (int i = 0; i < VSRAM_SIZE; i++) | |
280 { | |
281 tmp_buf[i*2] = context->vsram[i]; | |
282 tmp_buf[i*2+1] = context->vsram[i] >> 8; | |
283 } | |
284 if (fwrite(tmp_buf, 2, VSRAM_SIZE, outfile) != VSRAM_SIZE) { | |
285 fputs("Error writing VSRAM to savestate\n", stderr); | |
286 return 0; | |
287 } | |
288 fseek(outfile, GST_VDP_MEM, SEEK_SET); | |
289 if (fwrite(context->vdpmem, 1, VRAM_SIZE, outfile) != VRAM_SIZE) { | |
290 fputs("Error writing VRAM to savestate\n", stderr); | |
291 return 0; | |
292 } | |
293 return 1; | |
294 } | |
295 | |
296 uint8_t z80_save_gst(z80_context * context, FILE * gstfile) | |
297 { | |
298 uint8_t regdata[GST_Z80_REG_SIZE]; | |
299 uint8_t * curpos = regdata; | |
300 memset(regdata, 0, sizeof(regdata)); | |
301 uint8_t f = context->flags[ZF_S]; | |
302 f <<= 1; | |
303 f |= context->flags[ZF_Z] ; | |
304 f <<= 2; | |
305 f |= context->flags[ZF_H]; | |
306 f <<= 2; | |
307 f |= context->flags[ZF_PV]; | |
308 f <<= 1; | |
309 f |= context->flags[ZF_N]; | |
310 f <<= 1; | |
311 f |= context->flags[ZF_C]; | |
312 *(curpos++) = f; | |
313 *curpos = context->regs[Z80_A]; | |
314 | |
315 curpos += 3; | |
316 for (int reg = Z80_C; reg <= Z80_IYH; reg++) { | |
317 *(curpos++) = context->regs[reg++]; | |
318 *curpos = context->regs[reg]; | |
319 curpos += 3; | |
320 } | |
321 write_le_16(curpos, context->pc); | |
322 curpos += 4; | |
323 write_le_16(curpos, context->sp); | |
324 curpos += 4; | |
325 f = context->alt_flags[ZF_S]; | |
326 f <<= 1; | |
327 f |= context->alt_flags[ZF_Z] ; | |
328 f <<= 2; | |
329 f |= context->alt_flags[ZF_H]; | |
330 f <<= 2; | |
331 f |= context->alt_flags[ZF_PV]; | |
332 f <<= 1; | |
333 f |= context->alt_flags[ZF_N]; | |
334 f <<= 1; | |
335 f |= context->alt_flags[ZF_C]; | |
336 *(curpos++) = f; | |
337 *curpos = context->alt_regs[Z80_A]; | |
338 curpos += 3; | |
339 for (int reg = Z80_C; reg <= Z80_H; reg++) { | |
340 *(curpos++) = context->alt_regs[reg++]; | |
341 *curpos = context->alt_regs[reg]; | |
342 curpos += 3; | |
343 } | |
344 *curpos = context->regs[Z80_I]; | |
345 curpos += 2; | |
346 *curpos = context->iff1; | |
347 curpos += 2; | |
348 *(curpos++) = !reset; | |
349 *curpos = busreq; | |
350 curpos += 3; | |
351 uint32_t bank = context->bank_reg << 15; | |
352 write_le_32(curpos, bank); | |
353 fseek(gstfile, GST_Z80_REGS, SEEK_SET); | |
354 if (fwrite(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { | |
355 return 0; | |
356 } | |
357 fseek(gstfile, GST_Z80_RAM, SEEK_SET); | |
358 if(fwrite(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) { | |
359 fputs("Failed to write Z80 RAM to savestate\n", stderr); | |
360 return 0; | |
361 } | |
362 return 1; | |
363 } | |
364 | |
365 uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile) | |
366 { | |
367 uint8_t regdata[GST_YM_SIZE]; | |
368 fseek(gstfile, GST_YM_OFFSET, SEEK_SET); | |
369 if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { | |
370 return 0; | |
371 } | |
372 for (int i = 0; i < sizeof(regdata); i++) { | |
373 if (i & 0x100) { | |
374 ym_address_write_part2(context, i & 0xFF); | |
375 } else { | |
376 ym_address_write_part1(context, i); | |
377 } | |
378 ym_data_write(context, regdata[i]); | |
379 } | |
380 return 1; | |
381 } | |
382 | |
383 uint8_t ym_save_gst(ym2612_context * context, FILE * gstfile) | |
384 { | |
385 uint8_t regdata[GST_YM_SIZE]; | |
386 for (int i = 0; i < sizeof(regdata); i++) { | |
387 if (i & 0x100) { | |
388 int reg = (i & 0xFF); | |
389 if (reg >= YM_PART2_START && reg < YM_REG_END) { | |
390 regdata[i] = context->part2_regs[reg-YM_PART2_START]; | |
391 } else { | |
392 regdata[i] = 0xFF; | |
393 } | |
394 } else { | |
395 if (i >= YM_PART1_START && i < YM_REG_END) { | |
396 regdata[i] = context->part1_regs[i-YM_PART1_START]; | |
397 } else { | |
398 regdata[i] = 0xFF; | |
399 } | |
400 } | |
401 } | |
402 fseek(gstfile, GST_YM_OFFSET, SEEK_SET); | |
403 if (fwrite(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { | |
404 return 0; | |
405 } | |
406 return 1; | |
407 } | |
408 | |
409 uint32_t load_gst(genesis_context * gen, char * fname) | |
410 { | |
411 FILE * gstfile = fopen(fname, "rb"); | |
412 if (!gstfile) { | |
413 fprintf(stderr, "Could not open file %s for reading\n", fname); | |
414 goto error; | |
415 } | |
416 char ident[5]; | |
417 if (fread(ident, 1, sizeof(ident), gstfile) != sizeof(ident)) { | |
418 fprintf(stderr, "Could not read ident code from %s\n", fname); | |
419 goto error_close; | |
420 } | |
421 if (memcmp(ident, "GST\x40\xE0", 5) != 0) { | |
422 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\\x40\\xE0.\n", fname, ident[0], ident[1], ident[2], ident[3], ident[4]); | |
423 goto error_close; | |
424 } | |
425 uint32_t pc = m68k_load_gst(gen->m68k, gstfile); | |
426 if (!pc) { | |
427 goto error_close; | |
428 } | |
429 if (!vdp_load_gst(gen->vdp, gstfile)) { | |
430 goto error_close; | |
431 } | |
432 if (!ym_load_gst(gen->ym, gstfile)) { | |
433 goto error_close; | |
434 } | |
435 if (!z80_load_gst(gen->z80, gstfile)) { | |
436 goto error_close; | |
437 } | |
438 gen->ports[0].control = 0x40; | |
439 gen->ports[1].control = 0x40; | |
440 fclose(gstfile); | |
441 return pc; | |
442 | |
443 error_close: | |
444 fclose(gstfile); | |
445 error: | |
446 return 0; | |
447 } | |
448 | |
449 uint8_t save_gst(genesis_context * gen, char *fname, uint32_t m68k_pc) | |
450 { | |
451 FILE * gstfile = fopen(fname, "wb"); | |
452 if (!gstfile) { | |
453 fprintf(stderr, "Could not open %s for writing\n", fname); | |
454 goto error; | |
455 } | |
456 if (fwrite("GST\x40\xE0", 1, 5, gstfile) != 5) { | |
457 fputs("Error writing signature to savestate\n", stderr); | |
458 goto error_close; | |
459 } | |
460 if (!m68k_save_gst(gen->m68k, m68k_pc, gstfile)) { | |
461 goto error_close; | |
462 } | |
463 if (!z80_save_gst(gen->z80, gstfile)) { | |
464 goto error_close; | |
465 } | |
466 if (!vdp_save_gst(gen->vdp, gstfile)) { | |
467 goto error_close; | |
468 } | |
469 if (!ym_save_gst(gen->ym, gstfile)) { | |
470 goto error_close; | |
471 } | |
472 return 1; | |
473 | |
474 error_close: | |
475 fclose(gstfile); | |
476 error: | |
477 return 0; | |
478 } |