Mercurial > repos > blastem
comparison libblastem.c @ 2053:3414a4423de1 segacd
Merge from default
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 15 Jan 2022 13:15:21 -0800 |
parents | f77d36a975ff |
children | 0f2855db27ea 0343f0d5add0 |
comparison
equal
deleted
inserted
replaced
1692:5dacaef602a7 | 2053:3414a4423de1 |
---|---|
1 #include <stdlib.h> | |
2 #include <string.h> | |
3 #include "libretro.h" | |
4 #include "system.h" | |
5 #include "util.h" | |
6 #include "vdp.h" | |
7 #include "render.h" | |
8 #include "io.h" | |
9 #include "genesis.h" | |
10 #include "sms.h" | |
11 | |
12 static retro_environment_t retro_environment; | |
13 RETRO_API void retro_set_environment(retro_environment_t re) | |
14 { | |
15 retro_environment = re; | |
16 # define input_descriptor_macro(pad_num) \ | |
17 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }, \ | |
18 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }, \ | |
19 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }, \ | |
20 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }, \ | |
21 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "A" }, \ | |
22 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "B" }, \ | |
23 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" }, \ | |
24 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "X" }, \ | |
25 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "Z" }, \ | |
26 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "C" }, \ | |
27 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Mode" }, \ | |
28 { pad_num, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }, \ | |
29 | |
30 static const struct retro_input_descriptor desc[] = { | |
31 input_descriptor_macro(0) | |
32 input_descriptor_macro(1) | |
33 input_descriptor_macro(2) | |
34 input_descriptor_macro(3) | |
35 input_descriptor_macro(4) | |
36 input_descriptor_macro(5) | |
37 input_descriptor_macro(6) | |
38 input_descriptor_macro(7) | |
39 { 0 }, | |
40 }; | |
41 | |
42 re(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, (void *)desc); | |
43 } | |
44 | |
45 static retro_video_refresh_t retro_video_refresh; | |
46 RETRO_API void retro_set_video_refresh(retro_video_refresh_t rvf) | |
47 { | |
48 retro_video_refresh = rvf; | |
49 } | |
50 | |
51 RETRO_API void retro_set_audio_sample(retro_audio_sample_t ras) | |
52 { | |
53 } | |
54 | |
55 static retro_audio_sample_batch_t retro_audio_sample_batch; | |
56 RETRO_API void retro_set_audio_sample_batch(retro_audio_sample_batch_t rasb) | |
57 { | |
58 retro_audio_sample_batch = rasb; | |
59 } | |
60 | |
61 static retro_input_poll_t retro_input_poll; | |
62 RETRO_API void retro_set_input_poll(retro_input_poll_t rip) | |
63 { | |
64 retro_input_poll = rip; | |
65 } | |
66 | |
67 static retro_input_state_t retro_input_state; | |
68 RETRO_API void retro_set_input_state(retro_input_state_t ris) | |
69 { | |
70 retro_input_state = ris; | |
71 } | |
72 | |
73 int headless = 0; | |
74 int exit_after = 0; | |
75 int z80_enabled = 1; | |
76 char *save_filename; | |
77 tern_node *config; | |
78 uint8_t use_native_states = 1; | |
79 system_header *current_system; | |
80 system_media media; | |
81 | |
82 RETRO_API void retro_init(void) | |
83 { | |
84 render_audio_initialized(RENDER_AUDIO_S16, 53693175 / (7 * 6 * 4), 2, 4, sizeof(int16_t)); | |
85 } | |
86 | |
87 RETRO_API void retro_deinit(void) | |
88 { | |
89 if (current_system) { | |
90 retro_unload_game(); | |
91 } | |
92 } | |
93 | |
94 RETRO_API unsigned retro_api_version(void) | |
95 { | |
96 return RETRO_API_VERSION; | |
97 } | |
98 | |
99 RETRO_API void retro_get_system_info(struct retro_system_info *info) | |
100 { | |
101 info->library_name = "BlastEm"; | |
102 info->library_version = "0.6.3-pre"; //TODO: share this with blastem.c | |
103 info->valid_extensions = "md|gen|sms|bin|rom"; | |
104 info->need_fullpath = 0; | |
105 info->block_extract = 0; | |
106 } | |
107 | |
108 static vid_std video_standard; | |
109 static uint32_t last_width, last_height; | |
110 static uint32_t overscan_top, overscan_bot, overscan_left, overscan_right; | |
111 static void update_overscan(void) | |
112 { | |
113 uint8_t overscan; | |
114 retro_environment(RETRO_ENVIRONMENT_GET_OVERSCAN, &overscan); | |
115 if (overscan) { | |
116 overscan_top = overscan_bot = overscan_left = overscan_right = 0; | |
117 } else { | |
118 if (video_standard == VID_NTSC) { | |
119 overscan_top = 11; | |
120 overscan_bot = 8; | |
121 overscan_left = 13; | |
122 overscan_right = 14; | |
123 } else { | |
124 overscan_top = 30; | |
125 overscan_bot = 24; | |
126 overscan_left = 13; | |
127 overscan_right = 14; | |
128 } | |
129 } | |
130 } | |
131 | |
132 static int32_t sample_rate; | |
133 RETRO_API void retro_get_system_av_info(struct retro_system_av_info *info) | |
134 { | |
135 update_overscan(); | |
136 last_width = LINEBUF_SIZE; | |
137 info->geometry.base_width = info->geometry.max_width = LINEBUF_SIZE - (overscan_left + overscan_right); | |
138 info->geometry.base_height = (video_standard == VID_NTSC ? 243 : 294) - (overscan_top + overscan_bot); | |
139 last_height = info->geometry.base_height; | |
140 info->geometry.max_height = info->geometry.base_height * 2; | |
141 info->geometry.aspect_ratio = 0; | |
142 double master_clock = video_standard == VID_NTSC ? 53693175 : 53203395; | |
143 double lines = video_standard == VID_NTSC ? 262 : 313; | |
144 info->timing.fps = master_clock / (3420.0 * lines); | |
145 info->timing.sample_rate = master_clock / (7 * 6 * 24); //sample rate of YM2612 | |
146 sample_rate = info->timing.sample_rate; | |
147 render_audio_initialized(RENDER_AUDIO_S16, info->timing.sample_rate, 2, 4, sizeof(int16_t)); | |
148 //force adjustment of resampling parameters since target sample rate may have changed slightly | |
149 current_system->set_speed_percent(current_system, 100); | |
150 } | |
151 | |
152 RETRO_API void retro_set_controller_port_device(unsigned port, unsigned device) | |
153 { | |
154 } | |
155 | |
156 /* Resets the current game. */ | |
157 RETRO_API void retro_reset(void) | |
158 { | |
159 current_system->soft_reset(current_system); | |
160 } | |
161 | |
162 /* Runs the game for one video frame. | |
163 * During retro_run(), input_poll callback must be called at least once. | |
164 * | |
165 * If a frame is not rendered for reasons where a game "dropped" a frame, | |
166 * this still counts as a frame, and retro_run() should explicitly dupe | |
167 * a frame if GET_CAN_DUPE returns true. | |
168 * In this case, the video callback can take a NULL argument for data. | |
169 */ | |
170 static uint8_t started; | |
171 RETRO_API void retro_run(void) | |
172 { | |
173 if (started) { | |
174 current_system->resume_context(current_system); | |
175 } else { | |
176 current_system->start_context(current_system, NULL); | |
177 started = 1; | |
178 } | |
179 } | |
180 | |
181 /* Returns the amount of data the implementation requires to serialize | |
182 * internal state (save states). | |
183 * Between calls to retro_load_game() and retro_unload_game(), the | |
184 * returned size is never allowed to be larger than a previous returned | |
185 * value, to ensure that the frontend can allocate a save state buffer once. | |
186 */ | |
187 static size_t serialize_size_cache; | |
188 RETRO_API size_t retro_serialize_size(void) | |
189 { | |
190 if (!serialize_size_cache) { | |
191 uint8_t *tmp = current_system->serialize(current_system, &serialize_size_cache); | |
192 free(tmp); | |
193 } | |
194 return serialize_size_cache; | |
195 } | |
196 | |
197 /* Serializes internal state. If failed, or size is lower than | |
198 * retro_serialize_size(), it should return false, true otherwise. */ | |
199 RETRO_API bool retro_serialize(void *data, size_t size) | |
200 { | |
201 size_t actual_size; | |
202 uint8_t *tmp = current_system->serialize(current_system, &actual_size); | |
203 if (actual_size > size) { | |
204 free(tmp); | |
205 return 0; | |
206 } | |
207 memcpy(data, tmp, actual_size); | |
208 free(tmp); | |
209 return 1; | |
210 } | |
211 | |
212 RETRO_API bool retro_unserialize(const void *data, size_t size) | |
213 { | |
214 current_system->deserialize(current_system, (uint8_t *)data, size); | |
215 return 0; | |
216 } | |
217 | |
218 RETRO_API void retro_cheat_reset(void) | |
219 { | |
220 } | |
221 | |
222 RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char *code) | |
223 { | |
224 } | |
225 | |
226 /* Loads a game. */ | |
227 static system_type stype; | |
228 RETRO_API bool retro_load_game(const struct retro_game_info *game) | |
229 { | |
230 serialize_size_cache = 0; | |
231 if (game->path) { | |
232 media.dir = path_dirname(game->path); | |
233 media.name = basename_no_extension(game->path); | |
234 media.extension = path_extension(game->path); | |
235 } | |
236 media.buffer = malloc(nearest_pow2(game->size)); | |
237 memcpy(media.buffer, game->data, game->size); | |
238 media.size = game->size; | |
239 stype = detect_system_type(&media); | |
240 current_system = alloc_config_system(stype, &media, 0, 0); | |
241 | |
242 unsigned format = RETRO_PIXEL_FORMAT_XRGB8888; | |
243 retro_environment(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &format); | |
244 | |
245 return current_system != NULL; | |
246 } | |
247 | |
248 /* Loads a "special" kind of game. Should not be used, | |
249 * except in extreme cases. */ | |
250 RETRO_API bool retro_load_game_special(unsigned game_type, const struct retro_game_info *info, size_t num_info) | |
251 { | |
252 return retro_load_game(info); | |
253 } | |
254 | |
255 /* Unloads a currently loaded game. */ | |
256 RETRO_API void retro_unload_game(void) | |
257 { | |
258 free(media.dir); | |
259 free(media.name); | |
260 free(media.extension); | |
261 media.dir = media.name = media.extension = NULL; | |
262 //buffer is freed by the context | |
263 media.buffer = NULL; | |
264 current_system->free_context(current_system); | |
265 current_system = NULL; | |
266 } | |
267 | |
268 /* Gets region of game. */ | |
269 RETRO_API unsigned retro_get_region(void) | |
270 { | |
271 return video_standard == VID_NTSC ? RETRO_REGION_NTSC : RETRO_REGION_PAL; | |
272 } | |
273 | |
274 /* Gets region of memory. */ | |
275 RETRO_API void *retro_get_memory_data(unsigned id) | |
276 { | |
277 switch (id) { | |
278 case RETRO_MEMORY_SYSTEM_RAM: | |
279 switch (stype) { | |
280 case SYSTEM_GENESIS: { | |
281 genesis_context *gen = (genesis_context *)current_system; | |
282 return (uint8_t *)gen->work_ram; | |
283 } | |
284 #ifndef NO_Z80 | |
285 case SYSTEM_SMS: { | |
286 sms_context *sms = (sms_context *)current_system; | |
287 return sms->ram; | |
288 } | |
289 #endif | |
290 } | |
291 break; | |
292 case RETRO_MEMORY_SAVE_RAM: | |
293 if (stype == SYSTEM_GENESIS) { | |
294 genesis_context *gen = (genesis_context *)current_system; | |
295 if (gen->save_type != SAVE_NONE) | |
296 return gen->save_storage; | |
297 } | |
298 break; | |
299 default: | |
300 break; | |
301 } | |
302 return NULL; | |
303 } | |
304 | |
305 RETRO_API size_t retro_get_memory_size(unsigned id) | |
306 { | |
307 switch (id) { | |
308 case RETRO_MEMORY_SYSTEM_RAM: | |
309 switch (stype) { | |
310 case SYSTEM_GENESIS: | |
311 return RAM_WORDS * sizeof(uint16_t); | |
312 #ifndef NO_Z80 | |
313 case SYSTEM_SMS: | |
314 return SMS_RAM_SIZE; | |
315 #endif | |
316 } | |
317 break; | |
318 case RETRO_MEMORY_SAVE_RAM: | |
319 if (stype == SYSTEM_GENESIS) { | |
320 genesis_context *gen = (genesis_context *)current_system; | |
321 if (gen->save_type != SAVE_NONE) | |
322 return gen->save_size; | |
323 } | |
324 break; | |
325 default: | |
326 break; | |
327 } | |
328 return 0; | |
329 } | |
330 | |
331 //blastem render backend API implementation | |
332 uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b) | |
333 { | |
334 return r << 16 | g << 8 | b; | |
335 } | |
336 | |
337 uint8_t render_create_window(char *caption, uint32_t width, uint32_t height, window_close_handler close_handler) | |
338 { | |
339 //not supported in lib build | |
340 return 0; | |
341 } | |
342 | |
343 void render_destroy_window(uint8_t which) | |
344 { | |
345 //not supported in lib build | |
346 } | |
347 | |
348 static uint32_t fb[LINEBUF_SIZE * 294 * 2]; | |
349 static uint8_t last_fb; | |
350 uint32_t *render_get_framebuffer(uint8_t which, int *pitch) | |
351 { | |
352 *pitch = LINEBUF_SIZE * sizeof(uint32_t); | |
353 if (which != last_fb) { | |
354 *pitch = *pitch * 2; | |
355 } | |
356 | |
357 if (which) { | |
358 return fb + LINEBUF_SIZE; | |
359 } else { | |
360 return fb; | |
361 } | |
362 } | |
363 | |
364 void render_framebuffer_updated(uint8_t which, int width) | |
365 { | |
366 unsigned height = (video_standard == VID_NTSC ? 243 : 294) - (overscan_top + overscan_bot); | |
367 width -= (overscan_left + overscan_right); | |
368 unsigned base_height = height; | |
369 if (which != last_fb) { | |
370 height *= 2; | |
371 last_fb = which; | |
372 } | |
373 if (width != last_width || height != last_height) { | |
374 struct retro_game_geometry geometry = { | |
375 .base_width = width, | |
376 .base_height = height, | |
377 .aspect_ratio = (float)LINEBUF_SIZE / base_height | |
378 }; | |
379 retro_environment(RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry); | |
380 last_width = width; | |
381 last_height = height; | |
382 } | |
383 retro_video_refresh(fb + overscan_left + LINEBUF_SIZE * overscan_top, width, height, LINEBUF_SIZE * sizeof(uint32_t)); | |
384 system_request_exit(current_system, 0); | |
385 } | |
386 | |
387 uint8_t render_get_active_framebuffer(void) | |
388 { | |
389 return 0; | |
390 } | |
391 | |
392 void render_set_video_standard(vid_std std) | |
393 { | |
394 video_standard = std; | |
395 } | |
396 | |
397 int render_fullscreen(void) | |
398 { | |
399 return 1; | |
400 } | |
401 | |
402 uint32_t render_overscan_top() | |
403 { | |
404 return overscan_top; | |
405 } | |
406 | |
407 uint32_t render_overscan_bot() | |
408 { | |
409 return overscan_bot; | |
410 } | |
411 | |
412 void process_events() | |
413 { | |
414 static int16_t prev_state[2][RETRO_DEVICE_ID_JOYPAD_L2]; | |
415 static const uint8_t map[] = { | |
416 BUTTON_A, BUTTON_X, BUTTON_MODE, BUTTON_START, DPAD_UP, DPAD_DOWN, | |
417 DPAD_LEFT, DPAD_RIGHT, BUTTON_B, BUTTON_Y, BUTTON_Z, BUTTON_C | |
418 }; | |
419 //TODO: handle other input device types | |
420 //TODO: handle more than 2 ports when appropriate | |
421 retro_input_poll(); | |
422 for (int port = 0; port < 2; port++) | |
423 { | |
424 for (int id = RETRO_DEVICE_ID_JOYPAD_B; id < RETRO_DEVICE_ID_JOYPAD_L2; id++) | |
425 { | |
426 int16_t new_state = retro_input_state(port, RETRO_DEVICE_JOYPAD, 0, id); | |
427 if (new_state != prev_state[port][id]) { | |
428 if (new_state) { | |
429 current_system->gamepad_down(current_system, port + 1, map[id]); | |
430 } else { | |
431 current_system->gamepad_up(current_system, port + 1, map[id]); | |
432 } | |
433 prev_state[port][id] = new_state; | |
434 } | |
435 } | |
436 } | |
437 } | |
438 | |
439 void render_errorbox(char *title, char *message) | |
440 { | |
441 } | |
442 void render_warnbox(char *title, char *message) | |
443 { | |
444 } | |
445 void render_infobox(char *title, char *message) | |
446 { | |
447 } | |
448 | |
449 uint8_t render_is_audio_sync(void) | |
450 { | |
451 //whether this is true depends on the libretro frontend implementation | |
452 //but the sync to audio path works better here | |
453 return 1; | |
454 } | |
455 | |
456 uint8_t render_should_release_on_exit(void) | |
457 { | |
458 return 0; | |
459 } | |
460 | |
461 void render_buffer_consumed(audio_source *src) | |
462 { | |
463 } | |
464 | |
465 void *render_new_audio_opaque(void) | |
466 { | |
467 return NULL; | |
468 } | |
469 | |
470 void render_free_audio_opaque(void *opaque) | |
471 { | |
472 } | |
473 | |
474 void render_lock_audio(void) | |
475 { | |
476 } | |
477 | |
478 void render_unlock_audio() | |
479 { | |
480 } | |
481 | |
482 uint32_t render_min_buffered(void) | |
483 { | |
484 //not actually used in the sync to audio path | |
485 return 4; | |
486 } | |
487 | |
488 uint32_t render_audio_syncs_per_sec(void) | |
489 { | |
490 return 0; | |
491 } | |
492 | |
493 void render_audio_created(audio_source *src) | |
494 { | |
495 } | |
496 | |
497 void render_do_audio_ready(audio_source *src) | |
498 { | |
499 int16_t *tmp = src->front; | |
500 src->front = src->back; | |
501 src->back = tmp; | |
502 src->front_populated = 1; | |
503 src->buffer_pos = 0; | |
504 if (all_sources_ready()) { | |
505 int16_t buffer[8]; | |
506 int min_remaining_out; | |
507 mix_and_convert((uint8_t *)buffer, sizeof(buffer), &min_remaining_out); | |
508 retro_audio_sample_batch(buffer, sizeof(buffer)/(2*sizeof(*buffer))); | |
509 } | |
510 } | |
511 | |
512 void render_source_paused(audio_source *src, uint8_t remaining_sources) | |
513 { | |
514 } | |
515 | |
516 void render_source_resumed(audio_source *src) | |
517 { | |
518 } | |
519 | |
520 void render_set_external_sync(uint8_t ext_sync_on) | |
521 { | |
522 } | |
523 | |
524 void bindings_set_mouse_mode(uint8_t mode) | |
525 { | |
526 } | |
527 | |
528 void bindings_release_capture(void) | |
529 { | |
530 } | |
531 | |
532 void bindings_reacquire_capture(void) | |
533 { | |
534 } | |
535 | |
536 extern const char rom_db_data[]; | |
537 char *read_bundled_file(char *name, uint32_t *sizeret) | |
538 { | |
539 if (!strcmp(name, "rom.db")) { | |
540 *sizeret = strlen(rom_db_data); | |
541 char *ret = malloc(*sizeret+1); | |
542 memcpy(ret, rom_db_data, *sizeret + 1); | |
543 return ret; | |
544 } | |
545 return NULL; | |
546 } |