git.y1.nz

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.