SameBoy | Accurate GB/GBC emulator |
| download: https://git.y1.nz/archives/sameboy.tar.gz | |
| README | Files | Log | Refs | LICENSE |
libretro/libretro.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdbool.h>
4 #include <unistd.h>
5 #include <time.h>
6 #include <assert.h>
7 #include <signal.h>
8 #include <stdarg.h>
9 #include <stdlib.h>
10
11 #define WIIU_SAMPLE_RATE 48000
12
13 #ifdef _WIN32
14 #include <direct.h>
15 #include <windows.h>
16 #define snprintf _snprintf
17 #endif
18
19 #include <Core/gb.h>
20 #include "libretro.h"
21 #include "libretro_core_options.inc"
22
23 #ifdef _WIN32
24 static const char slash = '\\';
25 #else
26 static const char slash = '/';
27 #endif
28
29 #define MAX_VIDEO_WIDTH 256
30 #define MAX_VIDEO_HEIGHT 224
31 #define MAX_VIDEO_PIXELS (MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT)
32
33 #define RETRO_MEMORY_GAMEBOY_1_SRAM ((1 << 8) | RETRO_MEMORY_SAVE_RAM)
34 #define RETRO_MEMORY_GAMEBOY_1_RTC ((2 << 8) | RETRO_MEMORY_RTC)
35 #define RETRO_MEMORY_GAMEBOY_2_SRAM ((3 << 8) | RETRO_MEMORY_SAVE_RAM)
36 #define RETRO_MEMORY_GAMEBOY_2_RTC ((4 << 8) | RETRO_MEMORY_RTC)
37
38 #define RETRO_GAME_TYPE_GAMEBOY_LINK_2P 0x101
39
40 enum rom_type {
41 ROM_TYPE_INVALID,
42 ROM_TYPE_DMG,
43 ROM_TYPE_SGB,
44 ROM_TYPE_CGB
45 };
46
47 #define GB_MODEL_AUTO (-1)
48
49 enum screen_layout {
50 LAYOUT_TOP_DOWN,
51 LAYOUT_LEFT_RIGHT
52 };
53
54 enum audio_out {
55 AUDIO_OUT_GB_1,
56 AUDIO_OUT_GB_2,
57 AUDIO_OUT_BOTH,
58 };
59
60 static GB_model_t model[2] = {
61 GB_MODEL_DMG_B,
62 GB_MODEL_DMG_B
63 };
64 static GB_model_t auto_model[2] = {
65 GB_MODEL_CGB_E,
66 GB_MODEL_CGB_E
67 };
68 static GB_model_t auto_sgb_model[2] = {
69 GB_MODEL_SGB_NTSC,
70 GB_MODEL_SGB_NTSC
71 };
72 static bool auto_sgb_enabled[2] = {
73 false,
74 false
75 };
76
77 static uint32_t *frame_buf = NULL;
78 static uint32_t *frame_buf_copy = NULL;
79 static uint32_t retained_frame_1[256 * 224];
80 static uint32_t retained_frame_2[256 * 224];
81 static struct retro_log_callback logging;
82 static retro_log_printf_t log_cb;
83
84 static retro_video_refresh_t video_cb;
85 static retro_audio_sample_batch_t audio_batch_cb;
86 static retro_input_poll_t input_poll_cb;
87 static retro_input_state_t input_state_cb;
88
89 static bool libretro_supports_bitmasks = false;
90
91 static unsigned emulated_devices = 1;
92 static bool initialized = false;
93 static unsigned screen_layout = 0;
94 static unsigned audio_out = 0;
95
96 static bool geometry_updated = false;
97 static bool link_cable_emulation = false;
98 /*static bool infrared_emulation = false;*/
99
100 static struct {
101 int16_t *data;
102 uint32_t sizes[2];
103 uint32_t capacity;
104 } output_audio_buffer = {NULL, 0, 0};
105
106 char retro_system_directory[4096];
107
108 GB_gameboy_t gameboy[2];
109
110 extern const unsigned char dmg_boot[], mgb_boot[], cgb0_boot[], cgb_boot[], agb_boot[], sgb_boot[], sgb2_boot[];
111 extern const unsigned dmg_boot_length, mgb_boot_length, cgb0_boot_length, cgb_boot_length, agb_boot_length, sgb_boot_length, sgb2_boot_length;
112 bool vblank1_occurred = false, vblank2_occurred = false;
113
114 static void fallback_log(enum retro_log_level level, const char *fmt, ...)
115 {
116 (void)level;
117 va_list va;
118 va_start(va, fmt);
119 vfprintf(stderr, fmt, va);
120 va_end(va);
121 }
122
123 static struct retro_rumble_interface rumble;
124
125 static void GB_update_keys_status(GB_gameboy_t *gb, unsigned port)
126 {
127 uint16_t joypad_bits = 0;
128
129 input_poll_cb();
130
131 if (libretro_supports_bitmasks) {
132 joypad_bits = input_state_cb(port, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_MASK);
133 }
134 else {
135 unsigned j;
136
137 for (j = 0; j < (RETRO_DEVICE_ID_JOYPAD_R3 + 1); j++) {
138 if (input_state_cb(port, RETRO_DEVICE_JOYPAD, 0, j)) {
139 joypad_bits |= (1 << j);
140 }
141 }
142 }
143
144 GB_set_key_state_for_player(gb, GB_KEY_RIGHT, emulated_devices == 1 ? port : 0,
145 joypad_bits & (1 << RETRO_DEVICE_ID_JOYPAD_RIGHT));
146 GB_set_key_state_for_player(gb, GB_KEY_LEFT, emulated_devices == 1 ? port : 0,
147 joypad_bits & (1 << RETRO_DEVICE_ID_JOYPAD_LEFT));
148 GB_set_key_state_for_player(gb, GB_KEY_UP, emulated_devices == 1 ? port : 0,
149 joypad_bits & (1 << RETRO_DEVICE_ID_JOYPAD_UP));
150 GB_set_key_state_for_player(gb, GB_KEY_DOWN, emulated_devices == 1 ? port : 0,
151 joypad_bits & (1 << RETRO_DEVICE_ID_JOYPAD_DOWN));
152 GB_set_key_state_for_player(gb, GB_KEY_A, emulated_devices == 1 ? port : 0,
153 joypad_bits & (1 << RETRO_DEVICE_ID_JOYPAD_A));
154 GB_set_key_state_for_player(gb, GB_KEY_B, emulated_devices == 1 ? port : 0,
155 joypad_bits & (1 << RETRO_DEVICE_ID_JOYPAD_B));
156 GB_set_key_state_for_player(gb, GB_KEY_SELECT, emulated_devices == 1 ? port : 0,
157 joypad_bits & (1 << RETRO_DEVICE_ID_JOYPAD_SELECT));
158 GB_set_key_state_for_player(gb, GB_KEY_START, emulated_devices == 1 ? port : 0,
159 joypad_bits & (1 << RETRO_DEVICE_ID_JOYPAD_START));
160
161 }
162
163 static void rumble_callback(GB_gameboy_t *gb, double amplitude)
164 {
165 if (!rumble.set_rumble_state) return;
166
167 if (gb == &gameboy[0]) {
168 rumble.set_rumble_state(0, RETRO_RUMBLE_STRONG, 65535 * amplitude);
169 }
170 else if (gb == &gameboy[1]) {
171 rumble.set_rumble_state(1, RETRO_RUMBLE_STRONG, 65535 * amplitude);
172 }
173 }
174
175 static void ensure_output_audio_buffer_capacity(int32_t capacity)
176 {
177 if (capacity <= output_audio_buffer.capacity) {
178 return;
179 }
180 output_audio_buffer.data = realloc(
181 output_audio_buffer.data, capacity * sizeof(*output_audio_buffer.data));
182 output_audio_buffer.capacity = capacity;
183 log_cb(RETRO_LOG_DEBUG, "Output audio buffer capacity set to %d\n", capacity);
184 }
185
186 static void init_output_audio_buffer(int32_t capacity)
187 {
188 output_audio_buffer.data = NULL;
189 output_audio_buffer.sizes[0] = output_audio_buffer.sizes[1] = 0;
190 output_audio_buffer.capacity = 0;
191 ensure_output_audio_buffer_capacity(capacity);
192 }
193
194 static void free_output_audio_buffer()
195 {
196 free(output_audio_buffer.data);
197 output_audio_buffer.data = NULL;
198 output_audio_buffer.sizes[0] = output_audio_buffer.sizes[1] = 0;
199 output_audio_buffer.capacity = 0;
200 }
201
202 static void upload_output_audio_buffer()
203 {
204
205 uint32_t remaining_frames;
206 if (emulated_devices == 2) {
207 remaining_frames = MIN(output_audio_buffer.sizes[0], output_audio_buffer.sizes[1]) / 2;
208 output_audio_buffer.sizes[1] -= remaining_frames * 2;
209 }
210 else {
211 remaining_frames = output_audio_buffer.sizes[0] / 2;
212 }
213 output_audio_buffer.sizes[0] -= remaining_frames * 2;
214 int16_t *buf_pos = output_audio_buffer.data;
215
216 while (remaining_frames > 0) {
217 size_t uploaded_frames = audio_batch_cb(buf_pos, remaining_frames);
218 buf_pos += uploaded_frames * 2;
219 remaining_frames -= uploaded_frames;
220 }
221 if (emulated_devices == 2) {
222 memcpy(output_audio_buffer.data, buf_pos, MAX(output_audio_buffer.sizes[0], output_audio_buffer.sizes[1]));
223 }
224 }
225
226 static void audio_callback(GB_gameboy_t *gb, GB_sample_t *sample)
227 {
228 unsigned index = 0;
229 if (gb == &gameboy[1]) {
230 index = 1;
231 }
232
233 if (output_audio_buffer.capacity - MAX(output_audio_buffer.sizes[0], output_audio_buffer.sizes[1]) < 2) {
234 ensure_output_audio_buffer_capacity(output_audio_buffer.capacity * 1.5);
235 }
236
237 if ((index == 0 && audio_out == AUDIO_OUT_GB_1) ||
238 (index == 1 && audio_out == AUDIO_OUT_GB_2)) {
239 output_audio_buffer.data[output_audio_buffer.sizes[0]++] = sample->left;
240 output_audio_buffer.data[output_audio_buffer.sizes[0]++] = sample->right;
241 output_audio_buffer.sizes[1] = output_audio_buffer.sizes[0];
242 }
243 else if (audio_out == AUDIO_OUT_BOTH) {
244 if (output_audio_buffer.sizes[index] < output_audio_buffer.sizes[!index]) {
245 // We're the second instance to reach this sample, add and divide (To prevent overflow)
246 output_audio_buffer.data[output_audio_buffer.sizes[index]] =
247 (output_audio_buffer.data[output_audio_buffer.sizes[index]] + (signed)sample->left) / 2;
248 output_audio_buffer.sizes[index]++;
249
250 output_audio_buffer.data[output_audio_buffer.sizes[index]] =
251 (output_audio_buffer.data[output_audio_buffer.sizes[index]] + (signed)sample->right) / 2;
252 output_audio_buffer.sizes[index]++;
253 }
254 else {
255 // We're the first instance, set its contents
256 output_audio_buffer.data[output_audio_buffer.sizes[index]++] = sample->left;
257 output_audio_buffer.data[output_audio_buffer.sizes[index]++] = sample->right;
258 }
259 }
260 }
261
262 static void vblank1(GB_gameboy_t *gb, GB_vblank_type_t type)
263 {
264 if (type == GB_VBLANK_TYPE_REPEAT) {
265 memcpy(GB_get_pixels_output(gb),
266 retained_frame_1,
267 GB_get_screen_width(gb) * GB_get_screen_height(gb) * sizeof(uint32_t));
268 }
269 vblank1_occurred = true;
270 }
271
272 static void vblank2(GB_gameboy_t *gb, GB_vblank_type_t type)
273 {
274 if (type == GB_VBLANK_TYPE_REPEAT) {
275 memcpy(GB_get_pixels_output(gb),
276 retained_frame_2,
277 GB_get_screen_width(gb) * GB_get_screen_height(gb) * sizeof(uint32_t));
278 }
279 vblank2_occurred = true;
280 }
281
282 static void lcd_status_change_1(GB_gameboy_t *gb, bool on)
283 {
284 if (!on) {
285 memcpy(retained_frame_1,
286 GB_get_pixels_output(gb),
287 GB_get_screen_width(gb) * GB_get_screen_height(gb) * sizeof(uint32_t));
288 }
289 }
290
291 static void lcd_status_change_2(GB_gameboy_t *gb, bool on)
292 {
293 if (!on) {
294 memcpy(retained_frame_2,
295 GB_get_pixels_output(gb),
296 GB_get_screen_width(gb) * GB_get_screen_height(gb) * sizeof(uint32_t));
297 }
298 }
299
300 static bool bit_to_send1 = true, bit_to_send2 = true;
301
302 static void serial_start1(GB_gameboy_t *gb, bool bit_received)
303 {
304 bit_to_send1 = bit_received;
305 }
306
307 static bool serial_end1(GB_gameboy_t *gb)
308 {
309 bool ret = GB_serial_get_data_bit(&gameboy[1]);
310 GB_serial_set_data_bit(&gameboy[1], bit_to_send1);
311 return ret;
312 }
313
314 static void serial_start2(GB_gameboy_t *gb, bool bit_received)
315 {
316 bit_to_send2 = bit_received;
317 }
318
319 static bool serial_end2(GB_gameboy_t *gb)
320 {
321 bool ret = GB_serial_get_data_bit(&gameboy[0]);
322 GB_serial_set_data_bit(&gameboy[0], bit_to_send2);
323 return ret;
324 }
325
326 static void infrared_callback1(GB_gameboy_t *gb, bool output)
327 {
328 GB_set_infrared_input(&gameboy[1], output);
329 }
330
331 static void infrared_callback2(GB_gameboy_t *gb, bool output)
332 {
333 GB_set_infrared_input(&gameboy[0], output);
334 }
335
336 static uint32_t rgb_encode(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b)
337 {
338 return r <<16 | g <<8 | b;
339 }
340
341 static retro_environment_t environ_cb;
342
343 static void set_variable_visibility(void)
344 {
345 struct retro_core_option_display option_display_singlecart;
346 struct retro_core_option_display option_display_dualcart;
347
348 size_t i;
349 size_t num_options = 0;
350
351 // Show/hide options depending on the number of emulated devices
352 if (emulated_devices == 1) {
353 option_display_singlecart.visible = true;
354 option_display_dualcart.visible = false;
355 }
356 else if (emulated_devices == 2) {
357 option_display_singlecart.visible = false;
358 option_display_dualcart.visible = true;
359 }
360
361 // Determine number of options
362 while (true) {
363 if (!option_defs_us[num_options].key) break;
364 num_options++;
365 }
366
367 // Copy parameters from option_defs_us array
368 for (i = 0; i < num_options; i++) {
369 const char *key = option_defs_us[i].key;
370 if ((strcmp(key, "sameboy_model") == 0) ||
371 (strcmp(key, "sameboy_auto_sgb_model") == 0) ||
372 (strcmp(key, "sameboy_scaling_filter") == 0) ||
373 (strcmp(key, "sameboy_mono_palette") == 0) ||
374 (strcmp(key, "sameboy_color_correction_mode") == 0) ||
375 (strcmp(key, "sameboy_light_temperature") == 0) ||
376 (strcmp(key, "sameboy_border") == 0) ||
377 (strcmp(key, "sameboy_high_pass_filter_mode") == 0) ||
378 (strcmp(key, "sameboy_audio_interference") == 0) ||
379 (strcmp(key, "sameboy_rumble") == 0)) {
380 option_display_singlecart.key = key;
381 environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display_singlecart);
382 }
383 else if ((strcmp(key, "sameboy_link") == 0) ||
384 (strcmp(key, "sameboy_screen_layout") == 0) ||
385 (strcmp(key, "sameboy_audio_output") == 0) ||
386 (strcmp(key, "sameboy_model_1") == 0) ||
387 (strcmp(key, "sameboy_auto_sgb_model_1") == 0) ||
388 (strcmp(key, "sameboy_model_2") == 0) ||
389 (strcmp(key, "sameboy_auto_sgb_model_2") == 0) ||
390 (strcmp(key, "sameboy_mono_palette_1") == 0) ||
391 (strcmp(key, "sameboy_mono_palette_2") == 0) ||
392 (strcmp(key, "sameboy_color_correction_mode_1") == 0) ||
393 (strcmp(key, "sameboy_color_correction_mode_2") == 0) ||
394 (strcmp(key, "sameboy_light_temperature_1") == 0) ||
395 (strcmp(key, "sameboy_light_temperature_2") == 0) ||
396 (strcmp(key, "sameboy_high_pass_filter_mode_1") == 0) ||
397 (strcmp(key, "sameboy_high_pass_filter_mode_2") == 0) ||
398 (strcmp(key, "sameboy_audio_interference_1") == 0) ||
399 (strcmp(key, "sameboy_audio_interference_2") == 0) ||
400 (strcmp(key, "sameboy_rumble_1") == 0) ||
401 (strcmp(key, "sameboy_rumble_2") == 0)) {
402 option_display_dualcart.key = key;
403 environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display_dualcart);
404 }
405 }
406 }
407
408 static const struct retro_subsystem_memory_info gb1_memory[] = {
409 { "srm", RETRO_MEMORY_GAMEBOY_1_SRAM },
410 { "rtc", RETRO_MEMORY_GAMEBOY_1_RTC },
411 };
412
413 static const struct retro_subsystem_memory_info gb2_memory[] = {
414 { "srm", RETRO_MEMORY_GAMEBOY_2_SRAM },
415 { "rtc", RETRO_MEMORY_GAMEBOY_2_RTC },
416 };
417
418 static const struct retro_subsystem_rom_info gb_roms[] = {
419 { "GameBoy #1", "gb|gbc", false, false, true, gb1_memory, 1 },
420 { "GameBoy #2", "gb|gbc", false, false, true, gb2_memory, 1 },
421 };
422
423 static const struct retro_subsystem_info subsystems[] = {
424 { "2 Player Game Boy Link", "gb_link_2p", gb_roms, 2, RETRO_GAME_TYPE_GAMEBOY_LINK_2P },
425 { NULL },
426 };
427
428 static const struct retro_controller_description controllers[] = {
429 { "Nintendo Game Boy", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0) },
430 };
431
432 static const struct retro_controller_description controllers_sgb[] = {
433 { "SNES/SFC Gamepad", RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0) },
434 };
435
436 static struct retro_input_descriptor descriptors_1p[] = {
437 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
438 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
439 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
440 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
441 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
442 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
443 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
444 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
445 { 0 },
446 };
447
448 static struct retro_input_descriptor descriptors_2p[] = {
449 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
450 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
451 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
452 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
453 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
454 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
455 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
456 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
457 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
458 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
459 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
460 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
461 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
462 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
463 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
464 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
465 { 0 },
466 };
467
468 static struct retro_input_descriptor descriptors_4p[] = {
469 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
470 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
471 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
472 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
473 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
474 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
475 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
476 { 0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
477 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
478 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
479 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
480 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
481 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
482 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
483 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
484 { 1, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
485 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
486 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
487 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
488 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
489 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
490 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
491 { 2, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
492 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
493 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left" },
494 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "Up" },
495 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "Down" },
496 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "Right" },
497 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "B" },
498 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "A" },
499 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT, "Select" },
500 { 3, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" },
501 { 0 },
502 };
503
504
505 static void set_link_cable_state(bool state)
506 {
507 if (state && emulated_devices == 2) {
508 GB_set_serial_transfer_bit_start_callback(&gameboy[0], serial_start1);
509 GB_set_serial_transfer_bit_end_callback(&gameboy[0], serial_end1);
510 GB_set_serial_transfer_bit_start_callback(&gameboy[1], serial_start2);
511 GB_set_serial_transfer_bit_end_callback(&gameboy[1], serial_end2);
512 GB_set_infrared_callback(&gameboy[0], infrared_callback1);
513 GB_set_infrared_callback(&gameboy[1], infrared_callback2);
514 }
515 else if (!state) {
516 GB_set_serial_transfer_bit_start_callback(&gameboy[0], NULL);
517 GB_set_serial_transfer_bit_end_callback(&gameboy[0], NULL);
518 GB_set_serial_transfer_bit_start_callback(&gameboy[1], NULL);
519 GB_set_serial_transfer_bit_end_callback(&gameboy[1], NULL);
520 GB_set_infrared_callback(&gameboy[0], NULL);
521 GB_set_infrared_callback(&gameboy[1], NULL);
522 }
523 }
524
525 static void boot_rom_load(GB_gameboy_t *gb, GB_boot_rom_t type)
526 {
527 const char *model_name = (char *[]) {
528 [GB_BOOT_ROM_DMG_0] = "dmg0",
529 [GB_BOOT_ROM_DMG] = "dmg",
530 [GB_BOOT_ROM_MGB] = "mgb",
531 [GB_BOOT_ROM_SGB] = "sgb",
532 [GB_BOOT_ROM_SGB2] = "sgb2",
533 [GB_BOOT_ROM_CGB_0] = "cgb0",
534 [GB_BOOT_ROM_CGB] = "cgb",
535 [GB_BOOT_ROM_CGB_E] = "cgbE",
536 [GB_BOOT_ROM_AGB_0] = "agb0",
537 [GB_BOOT_ROM_AGB] = "agb",
538 }[type];
539
540 const uint8_t *boot_code = (const unsigned char *[]) {
541 [GB_BOOT_ROM_DMG_0] = dmg_boot, // DMG_0 not implemented yet
542 [GB_BOOT_ROM_DMG] = dmg_boot,
543 [GB_BOOT_ROM_MGB] = mgb_boot,
544 [GB_BOOT_ROM_SGB] = sgb_boot,
545 [GB_BOOT_ROM_SGB2] = sgb2_boot,
546 [GB_BOOT_ROM_CGB_0] = cgb0_boot,
547 [GB_BOOT_ROM_CGB] = cgb_boot,
548 [GB_BOOT_ROM_AGB] = agb_boot,
549 }[type];
550
551 unsigned boot_length = (unsigned []) {
552 [GB_BOOT_ROM_DMG_0] = dmg_boot_length, // DMG_0 not implemented yet
553 [GB_BOOT_ROM_DMG] = dmg_boot_length,
554 [GB_BOOT_ROM_MGB] = mgb_boot_length,
555 [GB_BOOT_ROM_SGB] = sgb_boot_length,
556 [GB_BOOT_ROM_SGB2] = sgb2_boot_length,
557 [GB_BOOT_ROM_CGB_0] = cgb0_boot_length,
558 [GB_BOOT_ROM_CGB] = cgb_boot_length,
559 [GB_BOOT_ROM_AGB] = agb_boot_length,
560 }[type];
561
562 char buf[4096 + 1 + 4 + 9 + 1];
563 snprintf(buf, sizeof(buf), "%s%c%s_boot.bin", retro_system_directory, slash, model_name);
564 log_cb(RETRO_LOG_INFO, "Initializing as model: %s\n", model_name);
565 log_cb(RETRO_LOG_INFO, "Loading boot image: %s\n", buf);
566
567 if (GB_load_boot_rom(gb, buf)) {
568 if (type == GB_BOOT_ROM_CGB_E) {
569 boot_rom_load(gb, GB_BOOT_ROM_CGB);
570 return;
571 }
572 if (type == GB_BOOT_ROM_AGB_0) {
573 boot_rom_load(gb, GB_BOOT_ROM_AGB);
574 return;
575 }
576 GB_load_boot_rom_from_buffer(gb, boot_code, boot_length);
577 }
578 }
579
580 static void retro_set_memory_maps(void)
581 {
582 struct retro_memory_descriptor descs[11];
583 size_t size;
584 uint16_t bank;
585 unsigned i;
586
587
588 /* todo: add netplay awareness for this so achievements can be granted on the respective client */
589 i = 0;
590 memset(descs, 0, sizeof(descs));
591
592 descs[0].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_IE, &size, &bank);
593 descs[0].start = 0xFFFF;
594 descs[0].len = 1;
595
596 descs[1].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_HRAM, &size, &bank);
597 descs[1].start = 0xFF80;
598 descs[1].len = 0x0080;
599
600 descs[2].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_RAM, &size, &bank);
601 descs[2].start = 0xC000;
602 descs[2].len = 0x1000;
603
604 descs[3].ptr = descs[2].ptr + 0x1000; /* GB RAM/GBC RAM bank 1 */
605 descs[3].start = 0xD000;
606 descs[3].len = 0x1000;
607
608 descs[4].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_CART_RAM, &size, &bank);
609 descs[4].start = 0xA000;
610 descs[4].len = 0x2000;
611
612 descs[5].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_VRAM, &size, &bank);
613 descs[5].start = 0x8000;
614 descs[5].len = 0x2000;
615
616 descs[6].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_ROM, &size, &bank);
617 descs[6].start = 0x0000;
618 descs[6].len = 0x4000;
619 descs[6].flags = RETRO_MEMDESC_CONST;
620
621 descs[7].ptr = descs[6].ptr + (bank * 0x4000);
622 descs[7].start = 0x4000;
623 descs[7].len = 0x4000;
624 descs[7].flags = RETRO_MEMDESC_CONST;
625
626 descs[8].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_OAM, &size, &bank);
627 descs[8].start = 0xFE00;
628 descs[8].len = 0x00A0;
629 descs[8].select = 0xFFFFFF00;
630
631 descs[9].ptr = descs[2].ptr + 0x2000; /* GBC RAM bank 2 */
632 descs[9].start = 0x10000;
633 descs[9].len = GB_is_cgb(&gameboy[i]) ? 0x6000 : 0; /* 0x1000 per bank (2-7), unmapped on GB */
634 descs[9].select = 0xFFFF0000;
635
636 descs[10].ptr = GB_get_direct_access(&gameboy[i], GB_DIRECT_ACCESS_IO, &size, &bank);
637 descs[10].start = 0xFF00;
638 descs[10].len = 0x0080;
639 descs[10].select = 0xFFFFFF00;
640
641 struct retro_memory_map mmaps;
642 mmaps.descriptors = descs;
643 mmaps.num_descriptors = sizeof(descs) / sizeof(descs[0]);
644 environ_cb(RETRO_ENVIRONMENT_SET_MEMORY_MAPS, &mmaps);
645 }
646
647 static void init_for_current_model(unsigned id)
648 {
649 unsigned i = id;
650 GB_model_t effective_model;
651
652 effective_model = model[i];
653 if (effective_model == GB_MODEL_AUTO) {
654 effective_model = auto_model[i];
655 }
656
657 if (GB_is_inited(&gameboy[i])) {
658 GB_switch_model_and_reset(&gameboy[i], effective_model);
659 retro_set_memory_maps();
660 }
661 else {
662 GB_init(&gameboy[i], effective_model);
663 }
664 geometry_updated = true;
665
666 GB_set_boot_rom_load_callback(&gameboy[i], boot_rom_load);
667
668 /* When running multiple devices they are assumed to use the same resolution */
669
670 GB_set_pixels_output(&gameboy[i],
671 (uint32_t *)(frame_buf + GB_get_screen_width(&gameboy[0]) * GB_get_screen_height(&gameboy[0]) * i));
672 GB_set_rgb_encode_callback(&gameboy[i], rgb_encode);
673 #ifdef WIIU
674 GB_set_sample_rate(&gameboy[i], WIIU_SAMPLE_RATE);
675 #else
676 GB_set_sample_rate(&gameboy[i], GB_get_clock_rate(&gameboy[i]) / 2);
677 #endif
678 GB_apu_set_sample_callback(&gameboy[i], audio_callback);
679 GB_set_rumble_callback(&gameboy[i], rumble_callback);
680
681 /* todo: attempt to make these more generic */
682 GB_set_vblank_callback(&gameboy[0], (GB_vblank_callback_t) vblank1);
683 GB_set_lcd_status_callback(&gameboy[0], lcd_status_change_1);
684 if (emulated_devices == 2) {
685 GB_set_vblank_callback(&gameboy[1], (GB_vblank_callback_t) vblank2);
686 GB_set_lcd_status_callback(&gameboy[1], lcd_status_change_2);
687 if (link_cable_emulation) {
688 set_link_cable_state(true);
689 }
690 }
691
692 /* Let's be extremely nitpicky about how devices and descriptors are set */
693 if (emulated_devices == 1 && (model[0] == GB_MODEL_SGB_PAL || model[0] == GB_MODEL_SGB_NTSC || model[0] == GB_MODEL_SGB2)) {
694 static const struct retro_controller_info ports[] = {
695 { controllers_sgb, 1 },
696 { controllers_sgb, 1 },
697 { controllers_sgb, 1 },
698 { controllers_sgb, 1 },
699 { NULL, 0 },
700 };
701 environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
702 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, descriptors_4p);
703 }
704 else if (emulated_devices == 1) {
705 static const struct retro_controller_info ports[] = {
706 { controllers, 1 },
707 { NULL, 0 },
708 };
709 environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
710 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, descriptors_1p);
711 }
712 else {
713 static const struct retro_controller_info ports[] = {
714 { controllers, 1 },
715 { controllers, 1 },
716 { NULL, 0 },
717 };
718 environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
719 environ_cb(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, descriptors_2p);
720 }
721 }
722
723 static GB_model_t string_to_model(const char *string)
724 {
725 static const struct {
726 const char *name;
727 GB_model_t model;
728 } models[] = {
729 { "Game Boy", GB_MODEL_DMG_B},
730 { "Game Boy Pocket", GB_MODEL_MGB},
731 { "Game Boy Color 0", GB_MODEL_CGB_0},
732 { "Game Boy Color A", GB_MODEL_CGB_A},
733 { "Game Boy Color B", GB_MODEL_CGB_B},
734 { "Game Boy Color C", GB_MODEL_CGB_C},
735 { "Game Boy Color D", GB_MODEL_CGB_D},
736 { "Game Boy Color", GB_MODEL_CGB_E},
737 { "Game Boy Advance", GB_MODEL_AGB_A},
738 { "Game Boy Player", GB_MODEL_GBP_A},
739 { "Super Game Boy", GB_MODEL_SGB_NTSC},
740 { "Super Game Boy PAL", GB_MODEL_SGB_PAL},
741 { "Super Game Boy 2", GB_MODEL_SGB2},
742 };
743 for (unsigned i = 0; i < sizeof(models) / sizeof(models[0]); i++) {
744 if (strcmp(models[i].name, string) == 0) {
745 return models[i].model;
746 }
747 }
748 return GB_MODEL_AUTO;
749 }
750
751 static void check_variables()
752 {
753 struct retro_variable var = {0};
754 if (emulated_devices == 1) {
755
756 var.key = "sameboy_model";
757 var.value = NULL;
758
759 model[0] = GB_MODEL_AUTO;
760 auto_sgb_enabled[0] = false;
761
762 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
763 GB_model_t new_model = model[0];
764 new_model = string_to_model(var.value);
765 if (new_model == GB_MODEL_AUTO) {
766 if (strcmp(var.value, "Auto (SGB)") == 0) {
767 new_model = GB_MODEL_AUTO;
768 auto_sgb_enabled[0] = true;
769 }
770 }
771
772 model[0] = new_model;
773 }
774
775 var.key = "sameboy_auto_sgb_model";
776 var.value = NULL;
777
778 auto_sgb_model[0] = GB_MODEL_SGB_NTSC;
779
780 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
781 GB_model_t new_model = auto_sgb_model[0];
782 if (strcmp(var.value, "Super Game Boy PAL") == 0) {
783 new_model = GB_MODEL_SGB_PAL;
784 }
785 else if (strcmp(var.value, "Super Game Boy 2") == 0) {
786 new_model = GB_MODEL_SGB2;
787 }
788 else {
789 new_model = GB_MODEL_SGB_NTSC;
790 }
791
792 auto_sgb_model[0] = new_model;
793 }
794
795 var.key = "sameboy_rtc";
796 var.value = NULL;
797 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
798 if (strcmp(var.value, "sync to system clock") == 0) {
799 GB_set_rtc_mode(&gameboy[0], GB_RTC_MODE_SYNC_TO_HOST);
800 }
801 else if (strcmp(var.value, "accurate") == 0) {
802 GB_set_rtc_mode(&gameboy[0], GB_RTC_MODE_ACCURATE);
803 }
804 }
805
806 var.key = "sameboy_mono_palette";
807 var.value = NULL;
808 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
809 if (strcmp(var.value, "greyscale") == 0) {
810 GB_set_palette(&gameboy[0], &GB_PALETTE_GREY);
811 }
812 else if (strcmp(var.value, "lime") == 0) {
813 GB_set_palette(&gameboy[0], &GB_PALETTE_DMG);
814 }
815 else if (strcmp(var.value, "olive") == 0) {
816 GB_set_palette(&gameboy[0], &GB_PALETTE_MGB);
817 }
818 else if (strcmp(var.value, "teal") == 0) {
819 GB_set_palette(&gameboy[0], &GB_PALETTE_GBL);
820 }
821 }
822
823 var.key = "sameboy_color_correction_mode";
824 var.value = NULL;
825 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
826 if (strcmp(var.value, "off") == 0) {
827 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_DISABLED);
828 }
829 else if (strcmp(var.value, "correct curves") == 0) {
830 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_CORRECT_CURVES);
831 }
832 else if (strcmp(var.value, "emulate hardware") == 0) {
833 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_MODERN_BALANCED);
834 }
835 else if (strcmp(var.value, "preserve brightness") == 0) {
836 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_MODERN_BOOST_CONTRAST);
837 }
838 else if (strcmp(var.value, "reduce contrast") == 0) {
839 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_REDUCE_CONTRAST);
840 }
841 else if (strcmp(var.value, "harsh reality") == 0) {
842 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_LOW_CONTRAST);
843 }
844 else if (strcmp(var.value, "accurate") == 0) {
845 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_MODERN_ACCURATE);
846 }
847 }
848
849 var.key = "sameboy_light_temperature";
850 var.value = NULL;
851 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
852 GB_set_light_temperature(&gameboy[0], atof(var.value));
853 }
854
855 var.key = "sameboy_border";
856 var.value = NULL;
857 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
858 if (strcmp(var.value, "never") == 0) {
859 GB_set_border_mode(&gameboy[0], GB_BORDER_NEVER);
860 }
861 else if (strcmp(var.value, "Super Game Boy only") == 0) {
862 GB_set_border_mode(&gameboy[0], GB_BORDER_SGB);
863 }
864 else if (strcmp(var.value, "always") == 0) {
865 GB_set_border_mode(&gameboy[0], GB_BORDER_ALWAYS);
866 }
867 geometry_updated = true;
868 }
869
870 var.key = "sameboy_high_pass_filter_mode";
871 var.value = NULL;
872 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
873 if (strcmp(var.value, "off") == 0) {
874 GB_set_highpass_filter_mode(&gameboy[0], GB_HIGHPASS_OFF);
875 }
876 else if (strcmp(var.value, "accurate") == 0) {
877 GB_set_highpass_filter_mode(&gameboy[0], GB_HIGHPASS_ACCURATE);
878 }
879 else if (strcmp(var.value, "remove dc offset") == 0) {
880 GB_set_highpass_filter_mode(&gameboy[0], GB_HIGHPASS_REMOVE_DC_OFFSET);
881 }
882 }
883
884 var.key = "sameboy_audio_interference";
885 var.value = NULL;
886 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
887 GB_set_interference_volume(&gameboy[0], atoi(var.value) / 100.0);
888 }
889
890 var.key = "sameboy_rumble";
891 var.value = NULL;
892 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
893 if (strcmp(var.value, "never") == 0) {
894 GB_set_rumble_mode(&gameboy[0], GB_RUMBLE_DISABLED);
895 }
896 else if (strcmp(var.value, "rumble-enabled games") == 0) {
897 GB_set_rumble_mode(&gameboy[0], GB_RUMBLE_CARTRIDGE_ONLY);
898 }
899 else if (strcmp(var.value, "all games") == 0) {
900 GB_set_rumble_mode(&gameboy[0], GB_RUMBLE_ALL_GAMES);
901 }
902 }
903
904 }
905 else {
906 GB_set_border_mode(&gameboy[0], GB_BORDER_NEVER);
907 GB_set_border_mode(&gameboy[1], GB_BORDER_NEVER);
908 GB_set_rtc_mode(&gameboy[0], GB_RTC_MODE_ACCURATE);
909 GB_set_rtc_mode(&gameboy[1], GB_RTC_MODE_ACCURATE);
910
911 var.key = "sameboy_link";
912 var.value = NULL;
913 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
914 bool tmp = link_cable_emulation;
915 if (strcmp(var.value, "enabled") == 0) {
916 link_cable_emulation = true;
917 }
918 else {
919 link_cable_emulation = false;
920 }
921 if (link_cable_emulation && link_cable_emulation != tmp) {
922 set_link_cable_state(true);
923 }
924 else if (!link_cable_emulation && link_cable_emulation != tmp) {
925 set_link_cable_state(false);
926 }
927 }
928
929 var.key = "sameboy_screen_layout";
930 var.value = NULL;
931 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
932 if (strcmp(var.value, "top-down") == 0) {
933 screen_layout = LAYOUT_TOP_DOWN;
934 }
935 else {
936 screen_layout = LAYOUT_LEFT_RIGHT;
937 }
938
939 geometry_updated = true;
940 }
941
942 var.key = "sameboy_audio_output";
943 var.value = NULL;
944 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
945 if (strcmp(var.value, "Game Boy #1") == 0) {
946 audio_out = AUDIO_OUT_GB_1;
947 }
948 else if (strcmp(var.value, "Game Boy #2") == 0) {
949 audio_out = AUDIO_OUT_GB_2;
950 }
951 else {
952 audio_out = AUDIO_OUT_BOTH;
953 }
954 }
955
956 var.key = "sameboy_model_1";
957 var.value = NULL;
958
959 model[0] = GB_MODEL_AUTO;
960 auto_sgb_enabled[0] = false;
961
962 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
963 GB_model_t new_model = model[0];
964 new_model = string_to_model(var.value);
965 if (new_model == GB_MODEL_AUTO) {
966 if (strcmp(var.value, "Auto (SGB)") == 0) {
967 new_model = GB_MODEL_AUTO;
968 auto_sgb_enabled[0] = true;
969 }
970 }
971
972 model[0] = new_model;
973 }
974
975
976 var.key = "sameboy_auto_sgb_model_1";
977 var.value = NULL;
978
979 auto_sgb_model[0] = GB_MODEL_SGB_NTSC;
980
981 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
982 GB_model_t new_model = auto_sgb_model[0];
983 if (strcmp(var.value, "Super Game Boy PAL") == 0) {
984 new_model = GB_MODEL_SGB_PAL;
985 }
986 else if (strcmp(var.value, "Super Game Boy 2") == 0) {
987 new_model = GB_MODEL_SGB2;
988 }
989 else {
990 new_model = GB_MODEL_SGB_NTSC;
991 }
992
993 auto_sgb_model[0] = new_model;
994 }
995
996 var.key = "sameboy_model_2";
997 var.value = NULL;
998
999 model[1] = GB_MODEL_AUTO;
1000 auto_sgb_enabled[1] = false;
1001
1002 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1003 GB_model_t new_model = model[1];
1004 new_model = string_to_model(var.value);
1005 if (new_model == GB_MODEL_AUTO) {
1006 if (strcmp(var.value, "Auto (SGB)") == 0) {
1007 new_model = GB_MODEL_AUTO;
1008 auto_sgb_enabled[0] = true;
1009 }
1010 }
1011
1012 model[1] = new_model;
1013 }
1014
1015 var.key = "sameboy_auto_sgb_model_2";
1016 var.value = NULL;
1017
1018 auto_sgb_model[1] = GB_MODEL_SGB_NTSC;
1019
1020 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1021 GB_model_t new_model = auto_sgb_model[1];
1022 if (strcmp(var.value, "Super Game Boy PAL") == 0) {
1023 new_model = GB_MODEL_SGB_PAL;
1024 }
1025 else if (strcmp(var.value, "Super Game Boy 2") == 0) {
1026 new_model = GB_MODEL_SGB2;
1027 }
1028 else {
1029 new_model = GB_MODEL_SGB_NTSC;
1030 }
1031
1032 auto_sgb_model[1] = new_model;
1033 }
1034
1035 var.key = "sameboy_rtc";
1036 var.value = NULL;
1037 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1038 if (strcmp(var.value, "sync to system clock") == 0) {
1039 GB_set_rtc_mode(&gameboy[0], GB_RTC_MODE_SYNC_TO_HOST);
1040 GB_set_rtc_mode(&gameboy[1], GB_RTC_MODE_SYNC_TO_HOST);
1041 }
1042 else if (strcmp(var.value, "accurate") == 0) {
1043 GB_set_rtc_mode(&gameboy[0], GB_RTC_MODE_ACCURATE);
1044 GB_set_rtc_mode(&gameboy[1], GB_RTC_MODE_ACCURATE);
1045 }
1046 }
1047
1048 var.key = "sameboy_mono_palette_1";
1049 var.value = NULL;
1050 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1051 if (strcmp(var.value, "greyscale") == 0) {
1052 GB_set_palette(&gameboy[0], &GB_PALETTE_GREY);
1053 }
1054 else if (strcmp(var.value, "lime") == 0) {
1055 GB_set_palette(&gameboy[0], &GB_PALETTE_DMG);
1056 }
1057 else if (strcmp(var.value, "olive") == 0) {
1058 GB_set_palette(&gameboy[0], &GB_PALETTE_MGB);
1059 }
1060 else if (strcmp(var.value, "teal") == 0) {
1061 GB_set_palette(&gameboy[0], &GB_PALETTE_GBL);
1062 }
1063 }
1064
1065 var.key = "sameboy_mono_palette_2";
1066 var.value = NULL;
1067 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1068 if (strcmp(var.value, "greyscale") == 0) {
1069 GB_set_palette(&gameboy[1], &GB_PALETTE_GREY);
1070 }
1071 else if (strcmp(var.value, "lime") == 0) {
1072 GB_set_palette(&gameboy[1], &GB_PALETTE_DMG);
1073 }
1074 else if (strcmp(var.value, "olive") == 0) {
1075 GB_set_palette(&gameboy[1], &GB_PALETTE_MGB);
1076 }
1077 else if (strcmp(var.value, "teal") == 0) {
1078 GB_set_palette(&gameboy[1], &GB_PALETTE_GBL);
1079 }
1080 }
1081
1082 var.key = "sameboy_color_correction_mode_1";
1083 var.value = NULL;
1084 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1085 if (strcmp(var.value, "off") == 0) {
1086 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_DISABLED);
1087 }
1088 else if (strcmp(var.value, "correct curves") == 0) {
1089 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_CORRECT_CURVES);
1090 }
1091 else if (strcmp(var.value, "emulate hardware") == 0) {
1092 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_MODERN_BALANCED);
1093 }
1094 else if (strcmp(var.value, "preserve brightness") == 0) {
1095 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_MODERN_BOOST_CONTRAST);
1096 }
1097 else if (strcmp(var.value, "reduce contrast") == 0) {
1098 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_REDUCE_CONTRAST);
1099 }
1100 else if (strcmp(var.value, "harsh reality") == 0) {
1101 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_LOW_CONTRAST);
1102 }
1103 else if (strcmp(var.value, "accurate") == 0) {
1104 GB_set_color_correction_mode(&gameboy[0], GB_COLOR_CORRECTION_MODERN_ACCURATE);
1105 }
1106 }
1107
1108 var.key = "sameboy_color_correction_mode_2";
1109 var.value = NULL;
1110 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1111 if (strcmp(var.value, "off") == 0) {
1112 GB_set_color_correction_mode(&gameboy[1], GB_COLOR_CORRECTION_DISABLED);
1113 }
1114 else if (strcmp(var.value, "correct curves") == 0) {
1115 GB_set_color_correction_mode(&gameboy[1], GB_COLOR_CORRECTION_CORRECT_CURVES);
1116 }
1117 else if (strcmp(var.value, "emulate hardware") == 0) {
1118 GB_set_color_correction_mode(&gameboy[1], GB_COLOR_CORRECTION_MODERN_BALANCED);
1119 }
1120 else if (strcmp(var.value, "preserve brightness") == 0) {
1121 GB_set_color_correction_mode(&gameboy[1], GB_COLOR_CORRECTION_MODERN_BOOST_CONTRAST);
1122 }
1123 else if (strcmp(var.value, "reduce contrast") == 0) {
1124 GB_set_color_correction_mode(&gameboy[1], GB_COLOR_CORRECTION_REDUCE_CONTRAST);
1125 }
1126 else if (strcmp(var.value, "harsh reality") == 0) {
1127 GB_set_color_correction_mode(&gameboy[1], GB_COLOR_CORRECTION_LOW_CONTRAST);
1128 }
1129 else if (strcmp(var.value, "accurate") == 0) {
1130 GB_set_color_correction_mode(&gameboy[1], GB_COLOR_CORRECTION_MODERN_ACCURATE);
1131 }
1132 }
1133
1134 var.key = "sameboy_light_temperature_1";
1135 var.value = NULL;
1136 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1137 GB_set_light_temperature(&gameboy[0], atof(var.value));
1138 }
1139
1140 var.key = "sameboy_light_temperature_2";
1141 var.value = NULL;
1142 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1143 GB_set_light_temperature(&gameboy[1], atof(var.value));
1144 }
1145
1146 var.key = "sameboy_high_pass_filter_mode_1";
1147 var.value = NULL;
1148 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1149 if (strcmp(var.value, "off") == 0) {
1150 GB_set_highpass_filter_mode(&gameboy[0], GB_HIGHPASS_OFF);
1151 }
1152 else if (strcmp(var.value, "accurate") == 0) {
1153 GB_set_highpass_filter_mode(&gameboy[0], GB_HIGHPASS_ACCURATE);
1154 }
1155 else if (strcmp(var.value, "remove dc offset") == 0) {
1156 GB_set_highpass_filter_mode(&gameboy[0], GB_HIGHPASS_REMOVE_DC_OFFSET);
1157 }
1158 }
1159
1160 var.key = "sameboy_high_pass_filter_mode_2";
1161 var.value = NULL;
1162 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1163 if (strcmp(var.value, "off") == 0) {
1164 GB_set_highpass_filter_mode(&gameboy[1], GB_HIGHPASS_OFF);
1165 }
1166 else if (strcmp(var.value, "accurate") == 0) {
1167 GB_set_highpass_filter_mode(&gameboy[1], GB_HIGHPASS_ACCURATE);
1168 }
1169 else if (strcmp(var.value, "remove dc offset") == 0) {
1170 GB_set_highpass_filter_mode(&gameboy[1], GB_HIGHPASS_REMOVE_DC_OFFSET);
1171 }
1172 }
1173
1174 var.key = "sameboy_audio_interference_1";
1175 var.value = NULL;
1176 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1177 GB_set_interference_volume(&gameboy[0], atoi(var.value) / 100.0);
1178 }
1179
1180 var.key = "sameboy_audio_interference_2";
1181 var.value = NULL;
1182 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1183 GB_set_interference_volume(&gameboy[1], atoi(var.value) / 100.0);
1184 }
1185
1186 var.key = "sameboy_rumble_1";
1187 var.value = NULL;
1188 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1189 if (strcmp(var.value, "never") == 0) {
1190 GB_set_rumble_mode(&gameboy[0], GB_RUMBLE_DISABLED);
1191 }
1192 else if (strcmp(var.value, "rumble-enabled games") == 0) {
1193 GB_set_rumble_mode(&gameboy[0], GB_RUMBLE_CARTRIDGE_ONLY);
1194 }
1195 else if (strcmp(var.value, "all games") == 0) {
1196 GB_set_rumble_mode(&gameboy[0], GB_RUMBLE_ALL_GAMES);
1197 }
1198 }
1199
1200 var.key = "sameboy_rumble_2";
1201 var.value = NULL;
1202 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
1203 if (strcmp(var.value, "never") == 0) {
1204 GB_set_rumble_mode(&gameboy[1], GB_RUMBLE_DISABLED);
1205 }
1206 else if (strcmp(var.value, "rumble-enabled games") == 0) {
1207 GB_set_rumble_mode(&gameboy[1], GB_RUMBLE_CARTRIDGE_ONLY);
1208 }
1209 else if (strcmp(var.value, "all games") == 0) {
1210 GB_set_rumble_mode(&gameboy[1], GB_RUMBLE_ALL_GAMES);
1211 }
1212 }
1213
1214 }
1215 set_variable_visibility();
1216 }
1217
1218 void retro_init(void)
1219 {
1220 const char *dir = NULL;
1221
1222 if (environ_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &dir) && dir) {
1223 snprintf(retro_system_directory, sizeof(retro_system_directory), "%s", dir);
1224 }
1225 else {
1226 snprintf(retro_system_directory, sizeof(retro_system_directory), "%s", ".");
1227 }
1228
1229 if (environ_cb(RETRO_ENVIRONMENT_GET_LOG_INTERFACE, &logging)) {
1230 log_cb = logging.log;
1231 }
1232 else {
1233 log_cb = fallback_log;
1234 }
1235
1236 if (environ_cb(RETRO_ENVIRONMENT_GET_INPUT_BITMASKS, NULL)) {
1237 libretro_supports_bitmasks = true;
1238 }
1239
1240 init_output_audio_buffer(16384);
1241 }
1242
1243 void retro_deinit(void)
1244 {
1245 free(frame_buf);
1246 free(frame_buf_copy);
1247 frame_buf = NULL;
1248 frame_buf_copy = NULL;
1249
1250 free_output_audio_buffer();
1251
1252 libretro_supports_bitmasks = false;
1253 }
1254
1255 unsigned retro_api_version(void)
1256 {
1257 return RETRO_API_VERSION;
1258 }
1259
1260 void retro_set_controller_port_device(unsigned port, unsigned device)
1261 {
1262 log_cb(RETRO_LOG_INFO, "Connecting device %u into port %u\n", device, port);
1263 }
1264
1265 void retro_get_system_info(struct retro_system_info *info)
1266 {
1267 memset(info, 0, sizeof(*info));
1268 info->library_name = "SameBoy";
1269 #ifdef GIT_VERSION
1270 info->library_version = GB_VERSION GIT_VERSION;
1271 #else
1272 info->library_version = GB_VERSION;
1273 #endif
1274 info->need_fullpath = false;
1275 info->valid_extensions = "gb|gbc";
1276 }
1277
1278 void retro_get_system_av_info(struct retro_system_av_info *info)
1279 {
1280 struct retro_game_geometry geom;
1281 struct retro_system_timing timing = { GB_get_usual_frame_rate(&gameboy[0]), GB_get_sample_rate(&gameboy[audio_out == AUDIO_OUT_BOTH? 0 : audio_out])};
1282
1283 if (emulated_devices == 2) {
1284 if (screen_layout == LAYOUT_TOP_DOWN) {
1285 geom.base_width = GB_get_screen_width(&gameboy[0]);
1286 geom.base_height = GB_get_screen_height(&gameboy[0]) * emulated_devices;
1287 geom.aspect_ratio = (double)GB_get_screen_width(&gameboy[0]) / (emulated_devices * GB_get_screen_height(&gameboy[0]));
1288 }
1289 else if (screen_layout == LAYOUT_LEFT_RIGHT) {
1290 geom.base_width = GB_get_screen_width(&gameboy[0]) * emulated_devices;
1291 geom.base_height = GB_get_screen_height(&gameboy[0]);
1292 geom.aspect_ratio = ((double)GB_get_screen_width(&gameboy[0]) * emulated_devices) / GB_get_screen_height(&gameboy[0]);
1293 }
1294 }
1295 else {
1296 geom.base_width = GB_get_screen_width(&gameboy[0]);
1297 geom.base_height = GB_get_screen_height(&gameboy[0]);
1298 geom.aspect_ratio = (double)GB_get_screen_width(&gameboy[0]) / GB_get_screen_height(&gameboy[0]);
1299 }
1300
1301 geom.max_width = MAX_VIDEO_WIDTH * emulated_devices;
1302 geom.max_height = MAX_VIDEO_HEIGHT * emulated_devices;
1303
1304 info->geometry = geom;
1305 info->timing = timing;
1306 }
1307
1308 void retro_set_environment(retro_environment_t cb)
1309 {
1310 bool categories_supported;
1311
1312 environ_cb = cb;
1313
1314 libretro_set_core_options(environ_cb, &categories_supported);
1315
1316 environ_cb(RETRO_ENVIRONMENT_SET_SUBSYSTEM_INFO, (void*)subsystems);
1317 }
1318
1319 void retro_set_audio_sample(retro_audio_sample_t cb)
1320 {
1321 (void)cb;
1322 }
1323
1324 void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb)
1325 {
1326 audio_batch_cb = cb;
1327 }
1328
1329 void retro_set_input_poll(retro_input_poll_t cb)
1330 {
1331 input_poll_cb = cb;
1332 }
1333
1334 void retro_set_input_state(retro_input_state_t cb)
1335 {
1336 input_state_cb = cb;
1337 }
1338
1339 void retro_set_video_refresh(retro_video_refresh_t cb)
1340 {
1341 video_cb = cb;
1342 }
1343
1344 void retro_reset(void)
1345 {
1346 check_variables();
1347
1348 for (int i = 0; i < emulated_devices; i++) {
1349 init_for_current_model(i);
1350 GB_reset(&gameboy[i]);
1351 }
1352
1353 if (emulated_devices == 2) {
1354 if (GB_get_unmultiplied_clock_rate(&gameboy[0]) != GB_get_unmultiplied_clock_rate(&gameboy[1])) {
1355 audio_out = AUDIO_OUT_GB_1;
1356 }
1357 }
1358 else {
1359 audio_out = AUDIO_OUT_GB_1;
1360 }
1361
1362 geometry_updated = true;
1363 }
1364
1365 void retro_run(void)
1366 {
1367
1368 bool updated = false;
1369
1370 if (!initialized) {
1371 geometry_updated = false;
1372 }
1373
1374 if (geometry_updated) {
1375 struct retro_system_av_info info;
1376 retro_get_system_av_info(&info);
1377 environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &info.geometry);
1378 geometry_updated = false;
1379 }
1380
1381 if (!frame_buf) {
1382 return;
1383 }
1384
1385 if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated) && updated) {
1386 check_variables();
1387 }
1388
1389 if (emulated_devices == 2) {
1390 GB_update_keys_status(&gameboy[0], 0);
1391 GB_update_keys_status(&gameboy[1], 1);
1392 }
1393 else if (emulated_devices == 1 && (model[0] == GB_MODEL_SGB_PAL || model[0] == GB_MODEL_SGB_NTSC || model[0] == GB_MODEL_SGB2)) {
1394 for (unsigned i = 0; i < 4; i++) {
1395 GB_update_keys_status(&gameboy[0], i);
1396 }
1397 }
1398 else {
1399 GB_update_keys_status(&gameboy[0], 0);
1400 }
1401
1402 vblank1_occurred = vblank2_occurred = false;
1403 signed delta = 0;
1404 if (emulated_devices == 2) {
1405 while (!vblank1_occurred || !vblank2_occurred) {
1406 if (delta >= 0) {
1407 delta -= GB_run(&gameboy[0]);
1408 }
1409 else {
1410 delta += GB_run(&gameboy[1]);
1411 }
1412 }
1413 }
1414 else {
1415 GB_run_frame(&gameboy[0]);
1416 }
1417
1418 if (emulated_devices == 2) {
1419 if (screen_layout == LAYOUT_TOP_DOWN) {
1420 video_cb(frame_buf,
1421 GB_get_screen_width(&gameboy[0]),
1422 GB_get_screen_height(&gameboy[0]) * emulated_devices,
1423 GB_get_screen_width(&gameboy[0]) * sizeof(uint32_t));
1424 }
1425 else if (screen_layout == LAYOUT_LEFT_RIGHT) {
1426 unsigned pitch = GB_get_screen_width(&gameboy[0]) * emulated_devices;
1427 unsigned pixels_per_device = GB_get_screen_width(&gameboy[0]) * GB_get_screen_height(&gameboy[0]);
1428 for (int y = 0; y < GB_get_screen_height(&gameboy[0]); y++) {
1429 for (unsigned i = 0; i < emulated_devices; i++) {
1430 memcpy(frame_buf_copy + y * pitch + GB_get_screen_width(&gameboy[0]) * i,
1431 frame_buf + pixels_per_device * i + y * GB_get_screen_width(&gameboy[0]),
1432 GB_get_screen_width(&gameboy[0]) * sizeof(uint32_t));
1433 }
1434 }
1435
1436 video_cb(frame_buf_copy, GB_get_screen_width(&gameboy[0]) * emulated_devices, GB_get_screen_height(&gameboy[0]), GB_get_screen_width(&gameboy[0]) * emulated_devices * sizeof(uint32_t));
1437 }
1438 }
1439 else {
1440 video_cb(frame_buf,
1441 GB_get_screen_width(&gameboy[0]),
1442 GB_get_screen_height(&gameboy[0]),
1443 GB_get_screen_width(&gameboy[0]) * sizeof(uint32_t));
1444 }
1445
1446 upload_output_audio_buffer();
1447 initialized = true;
1448 }
1449
1450 static enum rom_type check_rom_header(const uint8_t *data, size_t size)
1451 {
1452 enum rom_type type;
1453 uint8_t cgb_flag;
1454 uint8_t sgb_flag;
1455
1456 if (!data || (size < 0x146 + 1)) {
1457 return ROM_TYPE_INVALID;
1458 }
1459
1460 type = ROM_TYPE_DMG;
1461 cgb_flag = data[0x143];
1462 sgb_flag = data[0x146];
1463
1464 if ((cgb_flag == 0x80) || (cgb_flag == 0xC0)) {
1465 type = ROM_TYPE_CGB;
1466 }
1467
1468 if ((type == ROM_TYPE_DMG) && (sgb_flag == 0x03)) {
1469 type = ROM_TYPE_SGB;
1470 }
1471
1472 return type;
1473 }
1474
1475 bool retro_load_game(const struct retro_game_info *info)
1476 {
1477 enum rom_type content_type = ROM_TYPE_INVALID;
1478 const uint8_t *content_data = NULL;
1479 size_t content_size;
1480
1481 if (info) {
1482 content_data = (const uint8_t *)info->data;
1483 content_size = info->size;
1484 content_type = check_rom_header(content_data, content_size);
1485 }
1486
1487 check_variables();
1488
1489 switch (content_type) {
1490 case ROM_TYPE_DMG:
1491 auto_model[0] = GB_MODEL_DMG_B;
1492 auto_model[1] = GB_MODEL_DMG_B;
1493 break;
1494 case ROM_TYPE_SGB:
1495 auto_model[0] = auto_sgb_enabled[0] ? auto_sgb_model[0] : GB_MODEL_DMG_B;
1496 auto_model[1] = auto_sgb_enabled[1] ? auto_sgb_model[1] : GB_MODEL_DMG_B;
1497 break;
1498 case ROM_TYPE_CGB:
1499 auto_model[0] = GB_MODEL_CGB_E;
1500 auto_model[1] = GB_MODEL_CGB_E;
1501 break;
1502 case ROM_TYPE_INVALID:
1503 default:
1504 log_cb(RETRO_LOG_ERROR, "Invalid content\n");
1505 return false;
1506 }
1507
1508 frame_buf = (uint32_t *)malloc(MAX_VIDEO_PIXELS * emulated_devices * sizeof(uint32_t));
1509 memset(frame_buf, 0, MAX_VIDEO_PIXELS * emulated_devices * sizeof(uint32_t));
1510
1511 enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
1512 if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
1513 log_cb(RETRO_LOG_ERROR, "XRGB8888 is not supported\n");
1514 return false;
1515 }
1516
1517 for (int i = 0; i < emulated_devices; i++) {
1518 init_for_current_model(i);
1519 GB_load_rom_from_buffer(&gameboy[i], content_data, content_size);
1520 }
1521
1522 bool achievements = true;
1523 environ_cb(RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS, &achievements);
1524
1525 if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble)) {
1526 log_cb(RETRO_LOG_INFO, "Rumble environment supported\n");
1527 }
1528 else {
1529 log_cb(RETRO_LOG_INFO, "Rumble environment not supported\n");
1530 }
1531
1532 check_variables();
1533
1534 retro_set_memory_maps();
1535
1536 return true;
1537 }
1538
1539 void retro_unload_game(void)
1540 {
1541 for (int i = 0; i < emulated_devices; i++) {
1542 log_cb(RETRO_LOG_INFO, "Unloading GB: %d\n", emulated_devices);
1543 GB_free(&gameboy[i]);
1544 }
1545 }
1546
1547 unsigned retro_get_region(void)
1548 {
1549 return RETRO_REGION_NTSC;
1550 }
1551
1552 bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num_info)
1553 {
1554 if ((type == RETRO_GAME_TYPE_GAMEBOY_LINK_2P) && (num_info >= 2)) {
1555 emulated_devices = 2;
1556 }
1557 else {
1558 return false; /* all other types are unhandled for now */
1559 }
1560
1561 check_variables();
1562
1563 frame_buf = (uint32_t*)malloc(emulated_devices * MAX_VIDEO_PIXELS * sizeof(uint32_t));
1564 frame_buf_copy = (uint32_t*)malloc(emulated_devices * MAX_VIDEO_PIXELS * sizeof(uint32_t));
1565
1566 memset(frame_buf, 0, emulated_devices * MAX_VIDEO_PIXELS * sizeof(uint32_t));
1567 memset(frame_buf_copy, 0, emulated_devices * MAX_VIDEO_PIXELS * sizeof(uint32_t));
1568
1569 enum retro_pixel_format fmt = RETRO_PIXEL_FORMAT_XRGB8888;
1570 if (!environ_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &fmt)) {
1571 log_cb(RETRO_LOG_ERROR, "XRGB8888 is not supported\n");
1572 return false;
1573 }
1574
1575 for (int i = 0; i < emulated_devices; i++) {
1576 enum rom_type content_type = ROM_TYPE_INVALID;
1577 const uint8_t *content_data = info[i].data;
1578 size_t content_size = info[i].size;
1579
1580 content_type = check_rom_header(content_data, content_size);
1581
1582 switch (content_type) {
1583 case ROM_TYPE_DMG:
1584 auto_model[i] = GB_MODEL_DMG_B;
1585 break;
1586 case ROM_TYPE_SGB:
1587 auto_model[i] = auto_sgb_enabled[i] ? auto_sgb_model[i] : GB_MODEL_DMG_B;
1588 break;
1589 case ROM_TYPE_CGB:
1590 auto_model[i] = GB_MODEL_CGB_E;
1591 break;
1592 case ROM_TYPE_INVALID:
1593 default:
1594 log_cb(RETRO_LOG_ERROR, "Invalid content\n");
1595 return false;
1596 }
1597
1598 init_for_current_model(i);
1599 GB_load_rom_from_buffer(&gameboy[i], content_data, content_size);
1600 }
1601
1602 bool achievements = true;
1603 environ_cb(RETRO_ENVIRONMENT_SET_SUPPORT_ACHIEVEMENTS, &achievements);
1604
1605 if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble)) {
1606 log_cb(RETRO_LOG_INFO, "Rumble environment supported\n");
1607 }
1608 else {
1609 log_cb(RETRO_LOG_INFO, "Rumble environment not supported\n");
1610 }
1611
1612 check_variables();
1613 return true;
1614 }
1615
1616 size_t retro_serialize_size(void)
1617 {
1618 static size_t maximum_save_size = 0;
1619 if (maximum_save_size) {
1620 return maximum_save_size * 2;
1621 }
1622
1623 GB_gameboy_t temp;
1624
1625 GB_init(&temp, GB_MODEL_DMG_B);
1626 maximum_save_size = GB_get_save_state_size(&temp);
1627 GB_free(&temp);
1628
1629 GB_init(&temp, GB_MODEL_CGB_E);
1630 maximum_save_size = MAX(maximum_save_size, GB_get_save_state_size(&temp));
1631 GB_free(&temp);
1632
1633 GB_init(&temp, GB_MODEL_SGB2);
1634 maximum_save_size = MAX(maximum_save_size, GB_get_save_state_size(&temp));
1635 GB_free(&temp);
1636
1637 return maximum_save_size * 2;
1638 }
1639
1640 bool retro_serialize(void *data, size_t size)
1641 {
1642
1643 if (!initialized || !data) {
1644 return false;
1645 }
1646
1647 size_t offset = 0;
1648
1649 for (int i = 0; i < emulated_devices; i++) {
1650 size_t state_size = GB_get_save_state_size(&gameboy[i]);
1651 if (state_size > size) {
1652 return false;
1653 }
1654
1655 GB_save_state_to_buffer(&gameboy[i], ((uint8_t *) data) + offset);
1656 offset += state_size;
1657 size -= state_size;
1658 }
1659
1660 return true;
1661 }
1662
1663 bool retro_unserialize(const void *data, size_t size)
1664 {
1665 for (int i = 0; i < emulated_devices; i++) {
1666 size_t state_size = GB_get_save_state_size(&gameboy[i]);
1667 if (state_size > size) {
1668 return false;
1669 }
1670
1671 if (GB_load_state_from_buffer(&gameboy[i], data, state_size)) {
1672 return false;
1673 }
1674
1675 size -= state_size;
1676 data = ((uint8_t *)data) + state_size;
1677 }
1678
1679 return true;
1680
1681 }
1682
1683 void *retro_get_memory_data(unsigned type)
1684 {
1685 void *data = NULL;
1686 if (emulated_devices == 1) {
1687 switch (type) {
1688 case RETRO_MEMORY_SYSTEM_RAM:
1689 data = gameboy[0].ram;
1690 break;
1691 case RETRO_MEMORY_SAVE_RAM:
1692 if (gameboy[0].cartridge_type->has_battery && gameboy[0].mbc_ram_size != 0) {
1693 data = gameboy[0].mbc_ram;
1694 }
1695 else {
1696 data = NULL;
1697 }
1698 break;
1699 case RETRO_MEMORY_VIDEO_RAM:
1700 data = gameboy[0].vram;
1701 break;
1702 case RETRO_MEMORY_RTC:
1703 if (gameboy[0].cartridge_type->has_battery) {
1704 data = GB_GET_SECTION(&gameboy[0], rtc);
1705 }
1706 else {
1707 data = NULL;
1708 }
1709 break;
1710 default:
1711 break;
1712 }
1713 }
1714 else {
1715 switch (type) {
1716 case RETRO_MEMORY_GAMEBOY_1_SRAM:
1717 if (gameboy[0].cartridge_type->has_battery && gameboy[0].mbc_ram_size != 0) {
1718 data = gameboy[0].mbc_ram;
1719 }
1720 else {
1721 data = NULL;
1722 }
1723 break;
1724 case RETRO_MEMORY_GAMEBOY_2_SRAM:
1725 if (gameboy[1].cartridge_type->has_battery && gameboy[1].mbc_ram_size != 0) {
1726 data = gameboy[1].mbc_ram;
1727 }
1728 else {
1729 data = NULL;
1730 }
1731 break;
1732 case RETRO_MEMORY_GAMEBOY_1_RTC:
1733 if (gameboy[0].cartridge_type->has_battery) {
1734 data = GB_GET_SECTION(&gameboy[0], rtc);
1735 }
1736 else {
1737 data = NULL;
1738 }
1739 break;
1740 case RETRO_MEMORY_GAMEBOY_2_RTC:
1741 if (gameboy[1].cartridge_type->has_battery) {
1742 data = GB_GET_SECTION(&gameboy[1], rtc);
1743 }
1744 else {
1745 data = NULL;
1746 }
1747 break;
1748 default:
1749 break;
1750 }
1751 }
1752
1753 return data;
1754 }
1755
1756 size_t retro_get_memory_size(unsigned type)
1757 {
1758 size_t size = 0;
1759 if (emulated_devices == 1) {
1760 switch (type) {
1761 case RETRO_MEMORY_SYSTEM_RAM:
1762 size = gameboy[0].ram_size;
1763 break;
1764 case RETRO_MEMORY_SAVE_RAM:
1765 if (gameboy[0].cartridge_type->has_battery && gameboy[0].mbc_ram_size != 0) {
1766 size = gameboy[0].mbc_ram_size;
1767 }
1768 else {
1769 size = 0;
1770 }
1771 break;
1772 case RETRO_MEMORY_VIDEO_RAM:
1773 size = gameboy[0].vram_size;
1774 break;
1775 case RETRO_MEMORY_RTC:
1776 if (gameboy[0].cartridge_type->has_battery) {
1777 size = GB_SECTION_SIZE(rtc);
1778 }
1779 else {
1780 size = 0;
1781 }
1782 break;
1783 default:
1784 break;
1785 }
1786 }
1787 else {
1788 switch (type) {
1789 case RETRO_MEMORY_GAMEBOY_1_SRAM:
1790 if (gameboy[0].cartridge_type->has_battery && gameboy[0].mbc_ram_size != 0) {
1791 size = gameboy[0].mbc_ram_size;
1792 }
1793 else {
1794 size = 0;
1795 }
1796 break;
1797 case RETRO_MEMORY_GAMEBOY_2_SRAM:
1798 if (gameboy[1].cartridge_type->has_battery && gameboy[1].mbc_ram_size != 0) {
1799 size = gameboy[1].mbc_ram_size;
1800 }
1801 else {
1802 size = 0;
1803 }
1804 break;
1805 case RETRO_MEMORY_GAMEBOY_1_RTC:
1806 if (gameboy[0].cartridge_type->has_battery) {
1807 size = GB_SECTION_SIZE(rtc);
1808 }
1809 break;
1810 case RETRO_MEMORY_GAMEBOY_2_RTC:
1811 if (gameboy[1].cartridge_type->has_battery) {
1812 size = GB_SECTION_SIZE(rtc);
1813 }
1814 break;
1815 default:
1816 break;
1817 }
1818 }
1819
1820 return size;
1821 }
1822
1823 void retro_cheat_reset(void)
1824 {}
1825
1826 void retro_cheat_set(unsigned index, bool enabled, const char *code)
1827 {
1828 (void)index;
1829 (void)enabled;
1830 (void)code;
1831 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.