Mercurial > repos > blastem
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; |