main
sms.c
1#include <string.h>
2#include <stdlib.h>
3#include <stddef.h>
4#include "sms.h"
5#include "blastem.h"
6#include "render.h"
7#include "util.h"
8#include "debug.h"
9#include "saves.h"
10#include "bindings.h"
11
12#define Z80_CYCLE current_cycle
13#define Z80_OPTS options
14
15static void *memory_io_write(uint32_t location, void *vcontext, uint8_t value)
16{
17 z80_context *z80 = vcontext;
18 sms_context *sms = z80->system;
19 if (location & 1) {
20 uint8_t fuzzy_ctrl_0 = sms->io.ports[0].control, fuzzy_ctrl_1 = sms->io.ports[1].control;
21 io_control_write(sms->io.ports, (~value) << 5 & 0x60, z80->Z80_CYCLE);
22 fuzzy_ctrl_0 |= sms->io.ports[0].control;
23 io_control_write(sms->io.ports+1, (~value) << 3 & 0x60, z80->Z80_CYCLE);
24 fuzzy_ctrl_1 |= sms->io.ports[1].control;
25 if (
26 (fuzzy_ctrl_0 & 0x40 & (sms->io.ports[0].output ^ (value << 1)) & (value << 1))
27 || (fuzzy_ctrl_0 & 0x40 & (sms->io.ports[1].output ^ (value >> 1)) & (value >> 1))
28 ) {
29 //TH is an output and it went from 0 -> 1
30 vdp_run_context(sms->vdp, z80->Z80_CYCLE);
31 vdp_latch_hv(sms->vdp);
32 }
33 io_data_write(sms->io.ports, value << 1, z80->Z80_CYCLE);
34 io_data_write(sms->io.ports + 1, value >> 1, z80->Z80_CYCLE);
35 } else {
36 //TODO: memory control write
37 }
38 return vcontext;
39}
40
41static uint8_t hv_read(uint32_t location, void *vcontext)
42{
43 z80_context *z80 = vcontext;
44 sms_context *sms = z80->system;
45 vdp_run_context(sms->vdp, z80->Z80_CYCLE);
46 uint16_t hv = vdp_hv_counter_read(sms->vdp);
47 if (location & 1) {
48 return hv;
49 } else {
50 return hv >> 8;
51 }
52}
53
54static void *sms_psg_write(uint32_t location, void *vcontext, uint8_t value)
55{
56 z80_context *z80 = vcontext;
57 sms_context *sms = z80->system;
58 psg_run(sms->psg, z80->Z80_CYCLE);
59 psg_write(sms->psg, value);
60 return vcontext;
61}
62
63static void update_interrupts(sms_context *sms)
64{
65 uint32_t vint = vdp_next_vint(sms->vdp);
66 uint32_t hint = vdp_next_hint(sms->vdp);
67 sms->z80->int_pulse_start = vint < hint ? vint : hint;
68}
69
70static uint8_t vdp_read(uint32_t location, void *vcontext)
71{
72 z80_context *z80 = vcontext;
73 sms_context *sms = z80->system;
74 vdp_run_context(sms->vdp, z80->Z80_CYCLE);
75 if (location & 1) {
76 uint8_t ret = vdp_control_port_read(sms->vdp);
77 sms->vdp->flags2 &= ~(FLAG2_VINT_PENDING|FLAG2_HINT_PENDING);
78 update_interrupts(sms);
79 return ret;
80 } else {
81 return vdp_data_port_read_pbc(sms->vdp);
82 }
83}
84
85static void *vdp_write(uint32_t location, void *vcontext, uint8_t value)
86{
87 z80_context *z80 = vcontext;
88 sms_context *sms = z80->system;
89 if (location & 1) {
90 vdp_run_context_full(sms->vdp, z80->Z80_CYCLE);
91 vdp_control_port_write_pbc(sms->vdp, value);
92 update_interrupts(sms);
93 } else {
94 vdp_run_context(sms->vdp, z80->Z80_CYCLE);
95 vdp_data_port_write_pbc(sms->vdp, value);
96 }
97 return vcontext;
98}
99
100static uint8_t io_read(uint32_t location, void *vcontext)
101{
102 z80_context *z80 = vcontext;
103 sms_context *sms = z80->system;
104 if (location == 0xC0 || location == 0xDC) {
105 uint8_t port_a = io_data_read(sms->io.ports, z80->Z80_CYCLE);
106 uint8_t port_b = io_data_read(sms->io.ports+1, z80->Z80_CYCLE);
107 return (port_a & 0x3F) | (port_b << 6);
108 }
109 if (location == 0xC1 || location == 0xDD) {
110 uint8_t port_a = io_data_read(sms->io.ports, z80->Z80_CYCLE);
111 uint8_t port_b = io_data_read(sms->io.ports+1, z80->Z80_CYCLE);
112 return (port_a & 0x40) | (port_b >> 2 & 0xF) | (port_b << 1 & 0x80) | 0x10;
113 }
114 return 0xFF;
115}
116
117static void update_mem_map(uint32_t location, sms_context *sms, uint8_t value)
118{
119 z80_context *z80 = sms->z80;
120 void *old_value;
121 if (location) {
122 uint32_t idx = location - 1;
123 old_value = z80->mem_pointers[idx];
124 z80->mem_pointers[idx] = sms->rom + (value << 14 & (sms->rom_size-1));
125 if (old_value != z80->mem_pointers[idx]) {
126 //invalidate any code we translated for the relevant bank
127 z80_invalidate_code_range(z80, idx ? idx * 0x4000 : 0x400, idx * 0x4000 + 0x4000);
128 }
129 } else {
130 old_value = z80->mem_pointers[2];
131 if (value & 8) {
132 //cartridge RAM is enabled
133 z80->mem_pointers[2] = sms->cart_ram + (value & 4 ? (SMS_CART_RAM_SIZE/2) : 0);
134 } else {
135 //cartridge RAM is disabled
136 z80->mem_pointers[2] = sms->rom + (sms->bank_regs[3] << 14 & (sms->rom_size-1));
137 }
138 if (old_value != z80->mem_pointers[2]) {
139 //invalidate any code we translated for the relevant bank
140 z80_invalidate_code_range(z80, 0x8000, 0xC000);
141 }
142 }
143}
144
145static void *mapper_write(uint32_t location, void *vcontext, uint8_t value)
146{
147 z80_context *z80 = vcontext;
148 sms_context *sms = z80->system;
149 location &= 3;
150 sms->ram[0x1FFC + location] = value;
151 sms->bank_regs[location] = value;
152 update_mem_map(location, sms, value);
153 return vcontext;
154}
155
156static void *cart_ram_write(uint32_t location, void *vcontext, uint8_t value)
157{
158 z80_context *z80 = vcontext;
159 sms_context *sms = z80->system;
160 if (sms->bank_regs[0] & 8) {
161 //cartridge RAM is enabled
162 location &= 0x3FFF;
163 z80->mem_pointers[2][location] = value;
164 z80_handle_code_write(0x8000 + location, z80);
165 }
166 return vcontext;
167}
168
169uint8_t debug_commands(system_header *system, char *input_buf)
170{
171 sms_context *sms = (sms_context *)system;
172 switch(input_buf[0])
173 {
174 case 'v':
175 if (input_buf[1] == 'r') {
176 vdp_print_reg_explain(sms->vdp);
177 } else if (input_buf[1] == 's') {
178 vdp_print_sprite_table(sms->vdp);
179 } else {
180 return 0;
181 }
182 break;
183 }
184 return 1;
185}
186
187static memmap_chunk io_map[] = {
188 {0x00, 0x40, 0xFF, 0, 0, 0, NULL, NULL, NULL, NULL, memory_io_write},
189 {0x40, 0x80, 0xFF, 0, 0, 0, NULL, NULL, NULL, hv_read, sms_psg_write},
190 {0x80, 0xC0, 0xFF, 0, 0, 0, NULL, NULL, NULL, vdp_read, vdp_write},
191 {0xC0, 0x100,0xFF, 0, 0, 0, NULL, NULL, NULL, io_read, NULL}
192};
193
194static void set_speed_percent(system_header * system, uint32_t percent)
195{
196 sms_context *context = (sms_context *)system;
197 uint32_t old_clock = context->master_clock;
198 context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100;
199
200 psg_adjust_master_clock(context->psg, context->master_clock);
201}
202
203void sms_serialize(sms_context *sms, serialize_buffer *buf)
204{
205 start_section(buf, SECTION_Z80);
206 z80_serialize(sms->z80, buf);
207 end_section(buf);
208
209 start_section(buf, SECTION_VDP);
210 vdp_serialize(sms->vdp, buf);
211 end_section(buf);
212
213 start_section(buf, SECTION_PSG);
214 psg_serialize(sms->psg, buf);
215 end_section(buf);
216
217 start_section(buf, SECTION_SEGA_IO_1);
218 io_serialize(sms->io.ports, buf);
219 end_section(buf);
220
221 start_section(buf, SECTION_SEGA_IO_2);
222 io_serialize(sms->io.ports + 1, buf);
223 end_section(buf);
224
225 start_section(buf, SECTION_MAIN_RAM);
226 save_int8(buf, sizeof(sms->ram) / 1024);
227 save_buffer8(buf, sms->ram, sizeof(sms->ram));
228 end_section(buf);
229
230 start_section(buf, SECTION_MAPPER);
231 save_int8(buf, 1);//mapper type, 1 for Sega mapper
232 save_buffer8(buf, sms->bank_regs, sizeof(sms->bank_regs));
233 end_section(buf);
234
235 start_section(buf, SECTION_CART_RAM);
236 save_int8(buf, SMS_CART_RAM_SIZE / 1024);
237 save_buffer8(buf, sms->cart_ram, SMS_CART_RAM_SIZE);
238 end_section(buf);
239}
240
241static uint8_t *serialize(system_header *sys, size_t *size_out)
242{
243 sms_context *sms = (sms_context *)sys;
244 serialize_buffer state;
245 init_serialize(&state);
246 sms_serialize(sms, &state);
247 if (size_out) {
248 *size_out = state.size;
249 }
250 return state.data;
251}
252
253static void ram_deserialize(deserialize_buffer *buf, void *vsms)
254{
255 sms_context *sms = vsms;
256 uint32_t ram_size = load_int8(buf) * 1024;
257 if (ram_size > sizeof(sms->ram)) {
258 fatal_error("State has a RAM size of %d bytes", ram_size);
259 }
260 load_buffer8(buf, sms->ram, ram_size);
261}
262
263static void cart_ram_deserialize(deserialize_buffer *buf, void *vsms)
264{
265 sms_context *sms = vsms;
266 uint32_t ram_size = load_int8(buf) * 1024;
267 if (ram_size > SMS_CART_RAM_SIZE) {
268 fatal_error("State has a cart RAM size of %d bytes", ram_size);
269 }
270 load_buffer8(buf, sms->cart_ram, ram_size);
271}
272
273static void mapper_deserialize(deserialize_buffer *buf, void *vsms)
274{
275 sms_context *sms = vsms;
276 uint8_t mapper_type = load_int8(buf);
277 if (mapper_type != 1) {
278 warning("State contains an unrecognized mapper type %d, it may be from a newer version of BlastEm\n", mapper_type);
279 return;
280 }
281 for (int i = 0; i < sizeof(sms->bank_regs); i++)
282 {
283 sms->bank_regs[i] = load_int8(buf);
284 update_mem_map(i, sms, sms->bank_regs[i]);
285 }
286}
287
288void sms_deserialize(deserialize_buffer *buf, sms_context *sms)
289{
290 register_section_handler(buf, (section_handler){.fun = z80_deserialize, .data = sms->z80}, SECTION_Z80);
291 register_section_handler(buf, (section_handler){.fun = vdp_deserialize, .data = sms->vdp}, SECTION_VDP);
292 register_section_handler(buf, (section_handler){.fun = psg_deserialize, .data = sms->psg}, SECTION_PSG);
293 register_section_handler(buf, (section_handler){.fun = io_deserialize, .data = sms->io.ports}, SECTION_SEGA_IO_1);
294 register_section_handler(buf, (section_handler){.fun = io_deserialize, .data = sms->io.ports + 1}, SECTION_SEGA_IO_2);
295 register_section_handler(buf, (section_handler){.fun = ram_deserialize, .data = sms}, SECTION_MAIN_RAM);
296 register_section_handler(buf, (section_handler){.fun = mapper_deserialize, .data = sms}, SECTION_MAPPER);
297 register_section_handler(buf, (section_handler){.fun = cart_ram_deserialize, .data = sms}, SECTION_CART_RAM);
298 //TODO: cart RAM
299 while (buf->cur_pos < buf->size)
300 {
301 load_section(buf);
302 }
303 z80_invalidate_code_range(sms->z80, 0xC000, 0x10000);
304 if (sms->bank_regs[0] & 8) {
305 //cart RAM is enabled, invalidate the region in case there is any code there
306 z80_invalidate_code_range(sms->z80, 0x8000, 0xC000);
307 }
308 free(buf->handlers);
309 buf->handlers = NULL;
310}
311
312static void deserialize(system_header *sys, uint8_t *data, size_t size)
313{
314 sms_context *sms = (sms_context *)sys;
315 deserialize_buffer buffer;
316 init_deserialize(&buffer, data, size);
317 sms_deserialize(&buffer, sms);
318}
319
320static void save_state(sms_context *sms, uint8_t slot)
321{
322 char *save_path = get_slot_name(&sms->header, slot, "state");
323 serialize_buffer state;
324 init_serialize(&state);
325 sms_serialize(sms, &state);
326 save_to_file(&state, save_path);
327 printf("Saved state to %s\n", save_path);
328 free(save_path);
329 free(state.data);
330}
331
332static uint8_t load_state_path(sms_context *sms, char *path)
333{
334 deserialize_buffer state;
335 uint8_t ret;
336 if ((ret = load_from_file(&state, path))) {
337 sms_deserialize(&state, sms);
338 free(state.data);
339 printf("Loaded %s\n", path);
340 }
341 return ret;
342}
343
344static uint8_t load_state(system_header *system, uint8_t slot)
345{
346 sms_context *sms = (sms_context *)system;
347 char *statepath = get_slot_name(system, slot, "state");
348 uint8_t ret;
349 if (!sms->z80->native_pc) {
350 ret = get_modification_time(statepath) != 0;
351 if (ret) {
352 system->delayed_load_slot = slot + 1;
353 }
354 goto done;
355
356 }
357 ret = load_state_path(sms, statepath);
358done:
359 free(statepath);
360 return ret;
361}
362
363static void run_sms(system_header *system)
364{
365 sms_context *sms = (sms_context *)system;
366 uint32_t target_cycle = sms->z80->Z80_CYCLE + 3420*16;
367 //TODO: PAL support
368 render_set_video_standard(VID_NTSC);
369 while (!sms->should_return)
370 {
371 if (system->delayed_load_slot) {
372 load_state(system, system->delayed_load_slot - 1);
373 system->delayed_load_slot = 0;
374
375 }
376 if (system->enter_debugger && sms->z80->pc) {
377 system->enter_debugger = 0;
378 zdebugger(sms->z80, sms->z80->pc);
379 }
380 if (sms->z80->nmi_start == CYCLE_NEVER) {
381 uint32_t nmi = vdp_next_nmi(sms->vdp);
382 if (nmi != CYCLE_NEVER) {
383 z80_assert_nmi(sms->z80, nmi);
384 }
385 }
386 z80_run(sms->z80, target_cycle);
387 if (sms->z80->reset) {
388 z80_clear_reset(sms->z80, sms->z80->Z80_CYCLE + 128*15);
389 }
390 target_cycle = sms->z80->Z80_CYCLE;
391 vdp_run_context(sms->vdp, target_cycle);
392 psg_run(sms->psg, target_cycle);
393
394 if (system->save_state) {
395 while (!sms->z80->pc) {
396 //advance Z80 to an instruction boundary
397 z80_run(sms->z80, sms->z80->Z80_CYCLE + 1);
398 }
399 save_state(sms, system->save_state - 1);
400 system->save_state = 0;
401 }
402
403 target_cycle += 3420*16;
404 if (target_cycle > 0x10000000) {
405 uint32_t adjust = sms->z80->Z80_CYCLE - 3420*262*2;
406 io_adjust_cycles(sms->io.ports, sms->z80->Z80_CYCLE, adjust);
407 io_adjust_cycles(sms->io.ports+1, sms->z80->Z80_CYCLE, adjust);
408 z80_adjust_cycles(sms->z80, adjust);
409 vdp_adjust_cycles(sms->vdp, adjust);
410 sms->psg->cycles -= adjust;
411 target_cycle -= adjust;
412 }
413 }
414 bindings_release_capture();
415 vdp_release_framebuffer(sms->vdp);
416 render_pause_source(sms->psg->audio);
417 sms->should_return = 0;
418}
419
420static void resume_sms(system_header *system)
421{
422 sms_context *sms = (sms_context *)system;
423 bindings_reacquire_capture();
424 vdp_reacquire_framebuffer(sms->vdp);
425 render_resume_source(sms->psg->audio);
426 run_sms(system);
427}
428
429static void start_sms(system_header *system, char *statefile)
430{
431 sms_context *sms = (sms_context *)system;
432
433 z80_assert_reset(sms->z80, 0);
434 z80_clear_reset(sms->z80, 128*15);
435
436 if (statefile) {
437 load_state_path(sms, statefile);
438 }
439
440 if (system->enter_debugger) {
441 system->enter_debugger = 0;
442 zinsert_breakpoint(sms->z80, sms->z80->pc, (uint8_t *)zdebugger);
443 }
444
445 run_sms(system);
446}
447
448static void soft_reset(system_header *system)
449{
450 sms_context *sms = (sms_context *)system;
451 z80_assert_reset(sms->z80, sms->z80->Z80_CYCLE);
452 sms->z80->target_cycle = sms->z80->sync_cycle = sms->z80->Z80_CYCLE;
453}
454
455static void free_sms(system_header *system)
456{
457 sms_context *sms = (sms_context *)system;
458 vdp_free(sms->vdp);
459 z80_options_free(sms->z80->Z80_OPTS);
460 free(sms->z80);
461 psg_free(sms->psg);
462 free(sms);
463}
464
465static uint16_t get_open_bus_value(system_header *system)
466{
467 return 0xFFFF;
468}
469
470static void request_exit(system_header *system)
471{
472 sms_context *sms = (sms_context *)system;
473 sms->should_return = 1;
474 sms->z80->target_cycle = sms->z80->sync_cycle = sms->z80->Z80_CYCLE;
475}
476
477static void inc_debug_mode(system_header *system)
478{
479 sms_context *sms = (sms_context *)system;
480 vdp_inc_debug_mode(sms->vdp);
481}
482
483static void load_save(system_header *system)
484{
485 //TODO: Implement me
486}
487
488static void persist_save(system_header *system)
489{
490 //TODO: Implement me
491}
492
493static void gamepad_down(system_header *system, uint8_t gamepad_num, uint8_t button)
494{
495 sms_context *sms = (sms_context *)system;
496 if (gamepad_num == GAMEPAD_MAIN_UNIT) {
497 if (button == MAIN_UNIT_PAUSE) {
498 vdp_pbc_pause(sms->vdp);
499 }
500 } else {
501 io_gamepad_down(&sms->io, gamepad_num, button);
502 }
503}
504
505static void gamepad_up(system_header *system, uint8_t gamepad_num, uint8_t button)
506{
507 sms_context *sms = (sms_context *)system;
508 io_gamepad_up(&sms->io, gamepad_num, button);
509}
510
511static void mouse_down(system_header *system, uint8_t mouse_num, uint8_t button)
512{
513 sms_context *sms = (sms_context *)system;
514 io_mouse_down(&sms->io, mouse_num, button);
515}
516
517static void mouse_up(system_header *system, uint8_t mouse_num, uint8_t button)
518{
519 sms_context *sms = (sms_context *)system;
520 io_mouse_up(&sms->io, mouse_num, button);
521}
522
523static void mouse_motion_absolute(system_header *system, uint8_t mouse_num, uint16_t x, uint16_t y)
524{
525 sms_context *sms = (sms_context *)system;
526 io_mouse_motion_absolute(&sms->io, mouse_num, x, y);
527}
528
529static void mouse_motion_relative(system_header *system, uint8_t mouse_num, int32_t x, int32_t y)
530{
531 sms_context *sms = (sms_context *)system;
532 io_mouse_motion_relative(&sms->io, mouse_num, x, y);
533}
534
535static void keyboard_down(system_header *system, uint8_t scancode)
536{
537 sms_context *sms = (sms_context *)system;
538 io_keyboard_down(&sms->io, scancode);
539}
540
541static void keyboard_up(system_header *system, uint8_t scancode)
542{
543 sms_context *sms = (sms_context *)system;
544 io_keyboard_up(&sms->io, scancode);
545}
546
547static void set_gain_config(sms_context *sms)
548{
549 char *config_gain;
550 config_gain = tern_find_path(config, "audio\0psg_gain\0", TVAL_PTR).ptrval;
551 render_audio_source_gaindb(sms->psg->audio, config_gain ? atof(config_gain) : 0.0f);
552}
553
554static void config_updated(system_header *system)
555{
556 sms_context *sms = (sms_context *)system;
557 setup_io_devices(config, &system->info, &sms->io);
558}
559
560
561sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region)
562{
563 sms_context *sms = calloc(1, sizeof(sms_context));
564 uint32_t rom_size = nearest_pow2(media->size);
565 memmap_chunk memory_map[6];
566 if (media->size > 0xC000) {
567 sms->header.info.map_chunks = 6;
568 uint8_t *ram_reg_overlap = sms->ram + sizeof(sms->ram) - 4;
569 memory_map[0] = (memmap_chunk){0x0000, 0x0400, 0xFFFF, 0, 0, MMAP_READ, media->buffer, NULL, NULL, NULL, NULL};
570 memory_map[1] = (memmap_chunk){0x0400, 0x4000, 0xFFFF, 0, 0, MMAP_READ|MMAP_PTR_IDX|MMAP_CODE, NULL, NULL, NULL, NULL, NULL};
571 memory_map[2] = (memmap_chunk){0x4000, 0x8000, 0x3FFF, 0, 1, MMAP_READ|MMAP_PTR_IDX|MMAP_CODE, NULL, NULL, NULL, NULL, NULL};
572 memory_map[3] = (memmap_chunk){0x8000, 0xC000, 0x3FFF, 0, 2, MMAP_READ|MMAP_PTR_IDX|MMAP_CODE, NULL, NULL, NULL, NULL, cart_ram_write};
573 memory_map[4] = (memmap_chunk){0xC000, 0xFFFC, sizeof(sms->ram)-1, 0, 0, MMAP_READ|MMAP_WRITE|MMAP_CODE, sms->ram, NULL, NULL, NULL, NULL};
574 memory_map[5] = (memmap_chunk){0xFFFC, 0x10000, 0x0003, 0, 0, MMAP_READ, ram_reg_overlap, NULL, NULL, NULL, mapper_write};
575 } else {
576 sms->header.info.map_chunks = 2;
577 memory_map[0] = (memmap_chunk){0x0000, 0xC000, rom_size-1, 0, 0, MMAP_READ, media->buffer, NULL, NULL, NULL, NULL};
578 memory_map[1] = (memmap_chunk){0xC000, 0x10000, sizeof(sms->ram)-1, 0, 0, MMAP_READ|MMAP_WRITE|MMAP_CODE, sms->ram, NULL, NULL, NULL, NULL};
579 };
580 sms->header.info.map = malloc(sizeof(memmap_chunk) * sms->header.info.map_chunks);
581 memcpy(sms->header.info.map, memory_map, sizeof(memmap_chunk) * sms->header.info.map_chunks);
582 z80_options *zopts = malloc(sizeof(z80_options));
583 init_z80_opts(zopts, sms->header.info.map, sms->header.info.map_chunks, io_map, 4, 15, 0xFF);
584 sms->z80 = init_z80_context(zopts);
585 sms->z80->system = sms;
586 sms->z80->Z80_OPTS->gen.debug_cmd_handler = debug_commands;
587
588 sms->rom = media->buffer;
589 sms->rom_size = rom_size;
590 if (sms->header.info.map_chunks > 2) {
591 sms->z80->mem_pointers[0] = sms->rom;
592 sms->z80->mem_pointers[1] = sms->rom + 0x4000;
593 sms->z80->mem_pointers[2] = sms->rom + 0x8000;
594 sms->bank_regs[1] = 0;
595 sms->bank_regs[2] = 0x4000 >> 14;
596 sms->bank_regs[3] = 0x8000 >> 14;
597 }
598
599 //TODO: Detect region and pick master clock based off of that
600 sms->normal_clock = sms->master_clock = 53693175;
601
602 sms->psg = malloc(sizeof(psg_context));
603 psg_init(sms->psg, sms->master_clock, 15*16);
604
605 set_gain_config(sms);
606
607 sms->vdp = init_vdp_context(0);
608 sms->vdp->system = &sms->header;
609
610 sms->header.info.save_type = SAVE_NONE;
611 sms->header.info.name = strdup(media->name);
612
613 setup_io_devices(config, &sms->header.info, &sms->io);
614 sms->header.has_keyboard = io_has_keyboard(&sms->io);
615
616 sms->header.set_speed_percent = set_speed_percent;
617 sms->header.start_context = start_sms;
618 sms->header.resume_context = resume_sms;
619 sms->header.load_save = load_save;
620 sms->header.persist_save = persist_save;
621 sms->header.load_state = load_state;
622 sms->header.free_context = free_sms;
623 sms->header.get_open_bus_value = get_open_bus_value;
624 sms->header.request_exit = request_exit;
625 sms->header.soft_reset = soft_reset;
626 sms->header.inc_debug_mode = inc_debug_mode;
627 sms->header.gamepad_down = gamepad_down;
628 sms->header.gamepad_up = gamepad_up;
629 sms->header.mouse_down = mouse_down;
630 sms->header.mouse_up = mouse_up;
631 sms->header.mouse_motion_absolute = mouse_motion_absolute;
632 sms->header.mouse_motion_relative = mouse_motion_relative;
633 sms->header.keyboard_down = keyboard_down;
634 sms->header.keyboard_up = keyboard_up;
635 sms->header.config_updated = config_updated;
636 sms->header.serialize = serialize;
637 sms->header.deserialize = deserialize;
638 sms->header.type = SYSTEM_SMS;
639
640 return sms;
641}