git.y1.nz

SameBoy

Accurate GB/GBC emulator
download: https://git.y1.nz/archives/sameboy.tar.gz
README | Files | Log | Refs | LICENSE

Core/joypad.c

      1 #include "gb.h"
      2 #include <assert.h>
      3 #include <math.h>
      4 
      5 static inline bool should_bounce(GB_gameboy_t *gb)
      6 {
      7     // Bouncing is super rare on an AGS, so don't emulate it on GB_MODEL_AGB_B (when addeed)
      8     return !GB_is_sgb(gb) && !gb-> no_bouncing_emulation && !(gb->model & GB_MODEL_GBP_BIT) /*&& gb->model != GB_MODEL_AGB_B*/;
      9 }
     10 
     11 static inline uint16_t bounce_for_key(GB_gameboy_t *gb, GB_key_t key)
     12 {
     13     if (gb->model > GB_MODEL_CGB_E) {
     14         // AGB are less bouncy
     15         return 0xBFF;
     16     }
     17     if (key == GB_KEY_START || key == GB_KEY_SELECT) {
     18         return 0x1FFF;
     19     }
     20     return 0xFFF;
     21 }
     22 
     23 static inline bool get_input(GB_gameboy_t *gb, uint8_t player, GB_key_t key)
     24 {
     25     if (gb->use_faux_analog[player] && key <= GB_KEY_DOWN) {
     26         if (gb->keys[player][key]) return true;
     27         uint8_t pattern = 0;
     28         uint8_t index_in_pattern;
     29         switch (key) {
     30             // Most games only sample inputs in 30FPS, so we shift right once.
     31             case GB_KEY_RIGHT:
     32                 if (gb->faux_analog_inputs[player].x <= 0) return false;
     33                 pattern = gb->faux_analog_inputs[player].x - 1;
     34                 index_in_pattern = gb->faux_analog_ticks;
     35                 break;
     36             case GB_KEY_LEFT:
     37                 if (gb->faux_analog_inputs[player].x >= 0) return false;
     38                 pattern = -gb->faux_analog_inputs[player].x - 1;
     39                 index_in_pattern = gb->faux_analog_ticks;
     40                 break;
     41             case GB_KEY_UP:
     42                 if (gb->faux_analog_inputs[player].y >= 0) return false;
     43                 pattern = -gb->faux_analog_inputs[player].y - 1;
     44                 index_in_pattern = gb->faux_analog_ticks + 2;
     45                 break;
     46             case GB_KEY_DOWN:
     47                 if (gb->faux_analog_inputs[player].y <= 0) return false;
     48                 pattern = gb->faux_analog_inputs[player].y - 1;
     49                 index_in_pattern = gb->faux_analog_ticks + 2;
     50                 break;
     51             nodefault;
     52         }
     53         if (pattern == 7) return true;
     54         /* Dithering pattern */
     55         static const uint8_t patterns[] = {
     56             0x1,
     57             0x11,
     58             0x94,
     59             0x55,
     60             0x6d,
     61             0x77,
     62             0x7f
     63         };
     64         return patterns[pattern] & (1 << (index_in_pattern & 6));
     65     }
     66     if (player != 0) {
     67         return gb->keys[player][key];
     68     }
     69     bool ret = gb->keys[player][key];
     70     
     71     if (likely(gb->key_bounce_timing[key] == 0)) return ret;
     72     if (likely((gb->key_bounce_timing[key] & 0x3FF) > 0x300)) return ret;
     73     uint16_t semi_random = ((((key << 5) + gb->div_counter) * 17) ^ ((gb->apu.apu_cycles + gb->display_cycles) * 13));
     74     semi_random >>= 3;
     75     if (semi_random < gb->key_bounce_timing[key]) {
     76         ret ^= true;
     77     }
     78     return ret;
     79 }
     80 
     81 void GB_update_joyp(GB_gameboy_t *gb)
     82 {
     83     if (gb->model & GB_MODEL_NO_SFC_BIT) return;
     84     
     85     uint8_t key_selection = 0;
     86     uint8_t previous_state = 0;
     87 
     88     previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
     89     key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
     90     gb->io_registers[GB_IO_JOYP] &= 0xF0;
     91     uint8_t current_player = gb->sgb? gb->sgb->current_player : 0;
     92     switch (key_selection) {
     93         case 3:
     94             if (gb->sgb && gb->sgb->player_count > 1) {
     95                 gb->io_registers[GB_IO_JOYP] |= 0xF - current_player;
     96             }
     97             else {
     98                 /* Nothing is wired, all up */
     99                 gb->io_registers[GB_IO_JOYP] |= 0x0F;
    100             }
    101             break;
    102 
    103         case 2:
    104             /* Direction keys */
    105             for (uint8_t i = 0; i < 4; i++) {
    106                 gb->io_registers[GB_IO_JOYP] |= (!get_input(gb, current_player, i)) << i;
    107             }
    108             /* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */
    109             if (likely(!gb->illegal_inputs_allowed)) {
    110                 if (!(gb->io_registers[GB_IO_JOYP] & 1)) {
    111                     gb->io_registers[GB_IO_JOYP] |= 2;
    112                 }
    113                 if (!(gb->io_registers[GB_IO_JOYP] & 4)) {
    114                     gb->io_registers[GB_IO_JOYP] |= 8;
    115                 }
    116             }
    117             break;
    118 
    119         case 1:
    120             /* Other keys */
    121             for (uint8_t i = 0; i < 4; i++) {
    122                 gb->io_registers[GB_IO_JOYP] |= (!get_input(gb, current_player, i + 4)) << i;
    123             }
    124             break;
    125 
    126         case 0:
    127             for (uint8_t i = 0; i < 4; i++) {
    128                 gb->io_registers[GB_IO_JOYP] |= (!(get_input(gb, current_player, i) || get_input(gb, current_player, i + 4))) << i;
    129             }
    130             break;
    131 
    132         nodefault;
    133     }
    134     
    135     // TODO: Implement the lame anti-debouncing mechanism as seen on the DMG schematics
    136     if (previous_state & ~(gb->io_registers[GB_IO_JOYP] & 0xF)) {
    137         if (!(gb->io_registers[GB_IO_IF] & 0x10)) {
    138             gb->joyp_accessed = true;
    139             gb->io_registers[GB_IO_IF] |= 0x10;
    140         }
    141     }
    142     
    143     gb->io_registers[GB_IO_JOYP] |= 0xC0;
    144 }
    145 
    146 void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value)
    147 {
    148     uint8_t previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
    149     gb->io_registers[GB_IO_JOYP] &= 0xF0;
    150     gb->io_registers[GB_IO_JOYP] |= value & 0xF;
    151     
    152     if (previous_state & ~(gb->io_registers[GB_IO_JOYP] & 0xF)) {
    153         if (!(gb->io_registers[GB_IO_IF] & 0x10)) {
    154             gb->joyp_accessed = true;
    155             gb->io_registers[GB_IO_IF] |= 0x10;
    156         }
    157     }
    158     gb->io_registers[GB_IO_JOYP] |= 0xC0;
    159 }
    160 
    161 void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed)
    162 {
    163     assert(index >= 0 && index < GB_KEY_MAX);
    164     if (should_bounce(gb) && pressed != gb->keys[0][index]) {
    165         gb->joypad_is_stable = false;
    166         gb->key_bounce_timing[index] = bounce_for_key(gb, index);
    167     }
    168     gb->keys[0][index] = pressed;
    169     GB_update_joyp(gb);
    170 }
    171 
    172 void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed)
    173 {
    174     assert(index >= 0 && index < GB_KEY_MAX);
    175     assert(player < 4);
    176     if (should_bounce(gb) && pressed != gb->keys[player][index]) {
    177         gb->joypad_is_stable = false;
    178         gb->key_bounce_timing[index] = bounce_for_key(gb, index);
    179     }
    180     gb->keys[player][index] = pressed;
    181     GB_update_joyp(gb);
    182 }
    183 
    184 void GB_set_key_mask(GB_gameboy_t *gb, GB_key_mask_t mask)
    185 {
    186     for (unsigned i = 0; i < GB_KEY_MAX; i++) {
    187         bool pressed = mask & (1 << i);
    188         if (should_bounce(gb) && pressed != gb->keys[0][i]) {
    189             gb->joypad_is_stable = false;
    190             gb->key_bounce_timing[i] = bounce_for_key(gb, i);
    191         }
    192         gb->keys[0][i] = pressed;
    193     }
    194     
    195     GB_update_joyp(gb);
    196 }
    197 
    198 void GB_set_key_mask_for_player(GB_gameboy_t *gb, GB_key_mask_t mask, unsigned player)
    199 {
    200     for (unsigned i = 0; i < GB_KEY_MAX; i++) {
    201         bool pressed = mask & (1 << i);
    202         if (should_bounce(gb) && pressed != gb->keys[player][i]) {
    203             gb->joypad_is_stable = false;
    204             gb->key_bounce_timing[i] = bounce_for_key(gb, i);
    205         }
    206         gb->keys[player][i] = pressed;
    207     }
    208     
    209     GB_update_joyp(gb);
    210 }
    211 
    212 void GB_joypad_run(GB_gameboy_t *gb, unsigned cycles)
    213 {
    214     if (gb->joypad_is_stable) return;
    215     bool should_update_joyp = gb->use_faux_analog[gb->sgb? gb->sgb->current_player : 0];
    216     gb->joypad_is_stable = true;
    217     if (gb->joyp_switching_delay) {
    218         gb->joypad_is_stable = false;
    219         if (gb->joyp_switching_delay > cycles) {
    220             gb->joyp_switching_delay -= cycles;
    221         }
    222         else {
    223             gb->joyp_switching_delay = 0;
    224             gb->io_registers[GB_IO_JOYP] = (gb->joyp_switch_value & 0xF0) | (gb->io_registers[GB_IO_JOYP] & 0x0F);
    225             should_update_joyp = true;
    226         }
    227     }
    228     
    229     for (unsigned i = 0; i < GB_KEY_MAX; i++) {
    230         if (gb->key_bounce_timing[i]) {
    231             gb->joypad_is_stable = false;
    232             should_update_joyp = true;
    233             if (gb->key_bounce_timing[i] > cycles) {
    234                 gb->key_bounce_timing[i] -= cycles;
    235             }
    236             else {
    237                 gb->key_bounce_timing[i] = 0;
    238             }
    239         }
    240     }
    241     
    242     if (should_update_joyp) {
    243         GB_update_joyp(gb);
    244     }
    245 }
    246 
    247 bool GB_get_joyp_accessed(GB_gameboy_t *gb)
    248 {
    249     return gb->joyp_accessed;
    250 }
    251 
    252 void GB_clear_joyp_accessed(GB_gameboy_t *gb)
    253 {
    254     gb->joyp_accessed = false;
    255 }
    256 
    257 void GB_set_allow_illegal_inputs(GB_gameboy_t *gb, bool allow)
    258 {
    259     gb->illegal_inputs_allowed = allow;
    260 }
    261 
    262 void GB_set_emulate_joypad_bouncing(GB_gameboy_t *gb, bool emulate)
    263 {
    264     gb->no_bouncing_emulation = !emulate;
    265 }
    266 
    267 void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback)
    268 {
    269     if (!callback) {
    270         GB_ASSERT_NOT_RUNNING_OTHER_THREAD(gb)
    271     }
    272     gb->update_input_hint_callback = callback;
    273 }
    274 
    275 void GB_set_use_faux_analog_inputs(GB_gameboy_t *gb, unsigned player, bool use)
    276 {
    277     if (gb->use_faux_analog[player] == use) return;
    278     gb->use_faux_analog[player] = use;
    279     for (GB_key_t key = GB_KEY_RIGHT; key <= GB_KEY_DOWN; key++) {
    280         gb->keys[player][key] = false;
    281     }
    282     GB_update_joyp(gb);
    283 }
    284 
    285 void GB_set_faux_analog_inputs(GB_gameboy_t *gb, unsigned player, double x, double y)
    286 {
    287 
    288     if (x > 1) x = 1;
    289     else if (x < -1) x = -1;
    290     if (y > 1) y = 1;
    291     else if (y < -1) y = -1;
    292     
    293     double abs_x = fabs(x), abs_y = fabs(y);
    294     if (abs_x <= 0.1) x = abs_x = 0;
    295     if (abs_y <= 0.1) y = abs_y = 0;
    296     if (!x && !y) {
    297         gb->faux_analog_inputs[player].x = gb->faux_analog_inputs[player].y = 0;
    298     }
    299     else {
    300         if (x) {
    301             abs_x = (abs_x - 0.1) / 0.9;
    302             x = x > 0? abs_x : -abs_x;
    303         }
    304         if (y) {
    305             abs_y = (abs_y - 0.1) / 0.9;
    306             y = y > 0? abs_y : -abs_y;
    307         }
    308         // Square the circle
    309         double distance = MIN(sqrt(x * x + y * y), 1);
    310         double multiplier = 8 * distance / MAX(abs_x, abs_y);
    311         
    312         gb->faux_analog_inputs[player].x = round(x * multiplier);
    313         gb->faux_analog_inputs[player].y = round(y * multiplier);
    314     }
    315     GB_update_joyp(gb);
    316 }
    317 
    318 void GB_update_faux_analog(GB_gameboy_t *gb)
    319 {
    320     gb->faux_analog_ticks++;
    321     for (unsigned i = 0; i < 4; i++) {
    322         if (!gb->use_faux_analog[i]) continue;
    323         if ((gb->faux_analog_inputs[i].x != 0 &&
    324              gb->faux_analog_inputs[i].x != 8 &&
    325              gb->faux_analog_inputs[i].x != -8) ||
    326             (gb->faux_analog_inputs[i].y != 0 &&
    327              gb->faux_analog_inputs[i].y != 8 &&
    328              gb->faux_analog_inputs[i].y != -8)) {
    329             gb->joypad_is_stable = false;
    330             return;
    331         }
    332     }
    333 }

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.