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 }