comparison sms.c @ 1433:c886c54d8cf1

Added save states to SMS emulation
author Michael Pavone <pavone@retrodev.com>
date Wed, 16 Aug 2017 20:06:28 -0700
parents b56c8c51ca5d
children da1dce39e846 2455662378ed
comparison
equal deleted inserted replaced
1432:5e7e6d9b79ff 1433:c886c54d8cf1
107 return (port_a & 0x40) | (port_b >> 2 & 0xF) | (port_b << 1 & 0x80) | 0x10; 107 return (port_a & 0x40) | (port_b >> 2 & 0xF) | (port_b << 1 & 0x80) | 0x10;
108 } 108 }
109 return 0xFF; 109 return 0xFF;
110 } 110 }
111 111
112 static void *mapper_write(uint32_t location, void *vcontext, uint8_t value) 112 static void update_mem_map(uint32_t location, sms_context *sms, uint8_t value)
113 { 113 {
114 z80_context *z80 = vcontext; 114 z80_context *z80 = sms->z80;
115 sms_context *sms = z80->system;
116 void *old_value; 115 void *old_value;
117 location &= 3;
118 sms->ram[0x1FFC + location] = value;
119 sms->bank_regs[location] = value;
120 if (location) { 116 if (location) {
121 uint32_t idx = location - 1; 117 uint32_t idx = location - 1;
122 old_value = z80->mem_pointers[idx]; 118 old_value = z80->mem_pointers[idx];
123 z80->mem_pointers[idx] = sms->rom + (value << 14 & (sms->rom_size-1)); 119 z80->mem_pointers[idx] = sms->rom + (value << 14 & (sms->rom_size-1));
124 if (old_value != z80->mem_pointers[idx]) { 120 if (old_value != z80->mem_pointers[idx]) {
137 if (old_value != z80->mem_pointers[2]) { 133 if (old_value != z80->mem_pointers[2]) {
138 //invalidate any code we translated for the relevant bank 134 //invalidate any code we translated for the relevant bank
139 z80_invalidate_code_range(z80, 0x8000, 0xC000); 135 z80_invalidate_code_range(z80, 0x8000, 0xC000);
140 } 136 }
141 } 137 }
138 }
139
140 static void *mapper_write(uint32_t location, void *vcontext, uint8_t value)
141 {
142 z80_context *z80 = vcontext;
143 sms_context *sms = z80->system;
144 location &= 3;
145 sms->ram[0x1FFC + location] = value;
146 sms->bank_regs[location] = value;
147 update_mem_map(location, sms, value);
142 return vcontext; 148 return vcontext;
143 } 149 }
144 150
145 static void *cart_ram_write(uint32_t location, void *vcontext, uint8_t value) 151 static void *cart_ram_write(uint32_t location, void *vcontext, uint8_t value)
146 { 152 {
187 context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100; 193 context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100;
188 194
189 psg_adjust_master_clock(context->psg, context->master_clock); 195 psg_adjust_master_clock(context->psg, context->master_clock);
190 } 196 }
191 197
198 void sms_serialize(sms_context *sms, serialize_buffer *buf)
199 {
200 start_section(buf, SECTION_Z80);
201 z80_serialize(sms->z80, buf);
202 end_section(buf);
203
204 start_section(buf, SECTION_VDP);
205 vdp_serialize(sms->vdp, buf);
206 end_section(buf);
207
208 start_section(buf, SECTION_PSG);
209 psg_serialize(sms->psg, buf);
210 end_section(buf);
211
212 start_section(buf, SECTION_SEGA_IO_1);
213 io_serialize(sms->io.ports, buf);
214 end_section(buf);
215
216 start_section(buf, SECTION_SEGA_IO_2);
217 io_serialize(sms->io.ports + 1, buf);
218 end_section(buf);
219
220 start_section(buf, SECTION_MAIN_RAM);
221 save_int8(buf, sizeof(sms->ram) / 1024);
222 save_buffer8(buf, sms->ram, sizeof(sms->ram));
223 end_section(buf);
224
225 start_section(buf, SECTION_MAPPER);
226 save_int8(buf, 1);//mapper type, 1 for Sega mapper
227 save_buffer8(buf, sms->bank_regs, sizeof(sms->bank_regs));
228 end_section(buf);
229
230 start_section(buf, SECTION_CART_RAM);
231 save_int8(buf, SMS_CART_RAM_SIZE / 1024);
232 save_buffer8(buf, sms->cart_ram, SMS_CART_RAM_SIZE);
233 end_section(buf);
234 }
235
236 static void ram_deserialize(deserialize_buffer *buf, void *vsms)
237 {
238 sms_context *sms = vsms;
239 uint32_t ram_size = load_int8(buf) * 1024;
240 if (ram_size > sizeof(sms->ram)) {
241 fatal_error("State has a RAM size of %d bytes", ram_size);
242 }
243 load_buffer8(buf, sms->ram, ram_size);
244 }
245
246 static void cart_ram_deserialize(deserialize_buffer *buf, void *vsms)
247 {
248 sms_context *sms = vsms;
249 uint32_t ram_size = load_int8(buf) * 1024;
250 if (ram_size > SMS_CART_RAM_SIZE) {
251 fatal_error("State has a cart RAM size of %d bytes", ram_size);
252 }
253 load_buffer8(buf, sms->cart_ram, ram_size);
254 }
255
256 static void mapper_deserialize(deserialize_buffer *buf, void *vsms)
257 {
258 sms_context *sms = vsms;
259 uint8_t mapper_type = load_int8(buf);
260 if (mapper_type != 1) {
261 warning("State contains an unrecognized mapper type %d, it may be from a newer version of BlastEm\n", mapper_type);
262 return;
263 }
264 for (int i = 0; i < sizeof(sms->bank_regs); i++)
265 {
266 sms->bank_regs[i] = load_int8(buf);
267 update_mem_map(i, sms, sms->bank_regs[i]);
268 }
269 }
270
271 void sms_deserialize(deserialize_buffer *buf, sms_context *sms)
272 {
273 register_section_handler(buf, (section_handler){.fun = z80_deserialize, .data = sms->z80}, SECTION_Z80);
274 register_section_handler(buf, (section_handler){.fun = vdp_deserialize, .data = sms->vdp}, SECTION_VDP);
275 register_section_handler(buf, (section_handler){.fun = psg_deserialize, .data = sms->psg}, SECTION_PSG);
276 register_section_handler(buf, (section_handler){.fun = io_deserialize, .data = sms->io.ports}, SECTION_SEGA_IO_1);
277 register_section_handler(buf, (section_handler){.fun = io_deserialize, .data = sms->io.ports + 1}, SECTION_SEGA_IO_2);
278 register_section_handler(buf, (section_handler){.fun = ram_deserialize, .data = sms}, SECTION_MAIN_RAM);
279 register_section_handler(buf, (section_handler){.fun = mapper_deserialize, .data = sms}, SECTION_MAPPER);
280 register_section_handler(buf, (section_handler){.fun = cart_ram_deserialize, .data = sms}, SECTION_CART_RAM);
281 //TODO: cart RAM
282 while (buf->cur_pos < buf->size)
283 {
284 load_section(buf);
285 }
286 z80_invalidate_code_range(sms->z80, 0xC000, 0x10000);
287 if (sms->bank_regs[0] & 8) {
288 //cart RAM is enabled, invalidate the region in case there is any code there
289 z80_invalidate_code_range(sms->z80, 0x8000, 0xC000);
290 }
291 }
292
293 static void save_state(sms_context *sms, uint8_t slot)
294 {
295 char *save_path;
296 if (slot == QUICK_SAVE_SLOT) {
297 save_path = save_state_path;
298 } else {
299 char slotname[] = "slot_0.state";
300 slotname[5] = '0' + slot;
301 char const *parts[] = {sms->header.save_dir, PATH_SEP, slotname};
302 save_path = alloc_concat_m(3, parts);
303 }
304 serialize_buffer state;
305 init_serialize(&state);
306 sms_serialize(sms, &state);
307 save_to_file(&state, save_path);
308 printf("Saved state to %s\n", save_path);
309 free(state.data);
310 }
311
312 static uint8_t load_state_path(sms_context *sms, char *path)
313 {
314 deserialize_buffer state;
315 uint8_t ret;
316 if ((ret = load_from_file(&state, path))) {
317 sms_deserialize(&state, sms);
318 free(state.data);
319 printf("Loaded %s\n", path);
320 }
321 return ret;
322 }
323
324 static uint8_t load_state(system_header *system, uint8_t slot)
325 {
326 sms_context *sms = (sms_context *)system;
327 char numslotname[] = "slot_0.state";
328 char *slotname;
329 if (slot == QUICK_SAVE_SLOT) {
330 slotname = "quicksave.state";
331 } else {
332 numslotname[5] = '0' + slot;
333 slotname = numslotname;
334 }
335 char const *parts[] = {sms->header.save_dir, PATH_SEP, slotname};
336 char *statepath = alloc_concat_m(3, parts);
337 uint8_t ret = load_state_path(sms, statepath);
338 free(statepath);
339 return ret;
340 }
341
192 static void run_sms(system_header *system) 342 static void run_sms(system_header *system)
193 { 343 {
194 render_disable_ym(); 344 render_disable_ym();
195 sms_context *sms = (sms_context *)system; 345 sms_context *sms = (sms_context *)system;
196 uint32_t target_cycle = sms->z80->current_cycle + 3420*16; 346 uint32_t target_cycle = sms->z80->current_cycle + 3420*16;
213 z80_clear_reset(sms->z80, sms->z80->current_cycle + 128*15); 363 z80_clear_reset(sms->z80, sms->z80->current_cycle + 128*15);
214 } 364 }
215 target_cycle = sms->z80->current_cycle; 365 target_cycle = sms->z80->current_cycle;
216 vdp_run_context(sms->vdp, target_cycle); 366 vdp_run_context(sms->vdp, target_cycle);
217 psg_run(sms->psg, target_cycle); 367 psg_run(sms->psg, target_cycle);
368
369 if (system->save_state) {
370 while (!sms->z80->pc) {
371 //advance Z80 to an instruction boundary
372 z80_run(sms->z80, sms->z80->current_cycle + 1);
373 }
374 save_state(sms, system->save_state - 1);
375 system->save_state = 0;
376 }
377
218 target_cycle += 3420*16; 378 target_cycle += 3420*16;
219 if (target_cycle > 0x10000000) { 379 if (target_cycle > 0x10000000) {
220 uint32_t adjust = sms->z80->current_cycle - 3420*262*2; 380 uint32_t adjust = sms->z80->current_cycle - 3420*262*2;
221 io_adjust_cycles(sms->io.ports, sms->z80->current_cycle, adjust); 381 io_adjust_cycles(sms->io.ports, sms->z80->current_cycle, adjust);
222 io_adjust_cycles(sms->io.ports+1, sms->z80->current_cycle, adjust); 382 io_adjust_cycles(sms->io.ports+1, sms->z80->current_cycle, adjust);
241 static void start_sms(system_header *system, char *statefile) 401 static void start_sms(system_header *system, char *statefile)
242 { 402 {
243 sms_context *sms = (sms_context *)system; 403 sms_context *sms = (sms_context *)system;
244 set_keybindings(&sms->io); 404 set_keybindings(&sms->io);
245 405
406 z80_assert_reset(sms->z80, 0);
407 z80_clear_reset(sms->z80, 128*15);
408
409 if (statefile) {
410 load_state_path(sms, statefile);
411 }
412
246 if (system->enter_debugger) { 413 if (system->enter_debugger) {
247 system->enter_debugger = 0; 414 system->enter_debugger = 0;
248 zinsert_breakpoint(sms->z80, 0, (uint8_t *)zdebugger); 415 zinsert_breakpoint(sms->z80, sms->z80->pc, (uint8_t *)zdebugger);
249 } 416 }
250
251 z80_assert_reset(sms->z80, 0);
252 z80_clear_reset(sms->z80, 128*15);
253 417
254 run_sms(system); 418 run_sms(system);
255 } 419 }
256 420
257 static void soft_reset(system_header *system) 421 static void soft_reset(system_header *system)
370 sms->header.set_speed_percent = set_speed_percent; 534 sms->header.set_speed_percent = set_speed_percent;
371 sms->header.start_context = start_sms; 535 sms->header.start_context = start_sms;
372 sms->header.resume_context = resume_sms; 536 sms->header.resume_context = resume_sms;
373 sms->header.load_save = load_save; 537 sms->header.load_save = load_save;
374 sms->header.persist_save = persist_save; 538 sms->header.persist_save = persist_save;
539 sms->header.load_state = load_state;
375 sms->header.free_context = free_sms; 540 sms->header.free_context = free_sms;
376 sms->header.get_open_bus_value = get_open_bus_value; 541 sms->header.get_open_bus_value = get_open_bus_value;
377 sms->header.request_exit = request_exit; 542 sms->header.request_exit = request_exit;
378 sms->header.soft_reset = soft_reset; 543 sms->header.soft_reset = soft_reset;
379 sms->header.inc_debug_mode = inc_debug_mode; 544 sms->header.inc_debug_mode = inc_debug_mode;