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.