GCC Code Coverage Report


Directory: main/
File: adapter/wired/n64.c
Date: 2025-10-04 14:03:00
Exec Total Coverage
Lines: 74 148 50.0%
Functions: 5 9 55.6%
Branches: 36 79 45.6%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2019-2025, Jacques Gagnon
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include <string.h>
7 #include "zephyr/types.h"
8 #include "tools/util.h"
9 #include "adapter/adapter.h"
10 #include "adapter/config.h"
11 #include "adapter/wired/wired.h"
12 #include "adapter/wireless/wireless.h"
13 #include "tests/cmds.h"
14 #include "bluetooth/mon.h"
15 #include "n64.h"
16
17 #define N64_AXES_MAX 2
18 enum {
19 N64_LD_RIGHT,
20 N64_LD_LEFT,
21 N64_LD_DOWN,
22 N64_LD_UP,
23 N64_START,
24 N64_Z,
25 N64_B,
26 N64_A,
27 N64_C_RIGHT,
28 N64_C_LEFT,
29 N64_C_DOWN,
30 N64_C_UP,
31 N64_R,
32 N64_L,
33 };
34
35 static DRAM_ATTR const uint8_t n64_axes_idx[ADAPTER_MAX_AXES] =
36 {
37 /* AXIS_LX, AXIS_LY, AXIS_RX, AXIS_RY, TRIG_L, TRIG_R */
38 0, 1, 0, 1, 0, 1
39 };
40
41 static DRAM_ATTR const struct ctrl_meta n64_axes_meta[N64_AXES_MAX] =
42 {
43 {.size_min = -128, .size_max = 127, .neutral = 0x00, .abs_max = 0x54, .abs_min = 0x54},
44 {.size_min = -128, .size_max = 127, .neutral = 0x00, .abs_max = 0x54, .abs_min = 0x54},
45 };
46
47 static DRAM_ATTR const struct ctrl_meta n64_mouse_axes_meta[N64_AXES_MAX] =
48 {
49 {.size_min = -128, .size_max = 127, .neutral = 0x00, .abs_max = 0x7F, .abs_min = 0x80},
50 {.size_min = -128, .size_max = 127, .neutral = 0x00, .abs_max = 0x7F, .abs_min = 0x80},
51 };
52
53 struct n64_map {
54 uint16_t buttons;
55 uint8_t axes[2];
56 } __packed;
57
58 struct n64_mouse_map {
59 uint16_t buttons;
60 uint8_t relative[2];
61 int32_t raw_axes[2];
62 } __packed;
63
64 struct n64_kb_map {
65 uint16_t key_codes[3];
66 uint8_t bitfield;
67 } __packed;
68
69 static const uint32_t n64_mask[4] = {0x77DF0FFF, 0x00000000, 0x00000000, BR_COMBO_MASK};
70 static const uint32_t n64_desc[4] = {0x0000000F, 0x00000000, 0x00000000, 0x00000000};
71 static DRAM_ATTR const uint32_t n64_btns_mask[32] = {
72 0, 0, 0, 0,
73 BIT(N64_C_LEFT), BIT(N64_C_RIGHT), BIT(N64_C_DOWN), BIT(N64_C_UP),
74 BIT(N64_LD_LEFT), BIT(N64_LD_RIGHT), BIT(N64_LD_DOWN), BIT(N64_LD_UP),
75 0, 0, 0, 0,
76 BIT(N64_B), BIT(N64_C_DOWN), BIT(N64_A), BIT(N64_C_LEFT),
77 BIT(N64_START), 0, 0, 0,
78 BIT(N64_Z), BIT(N64_L), BIT(N64_C_UP), 0,
79 BIT(N64_Z), BIT(N64_R), BIT(N64_C_RIGHT), 0,
80 };
81
82 static const uint32_t n64_mouse_mask[4] = {0x110000F0, 0x00000000, 0x00000000, BR_COMBO_MASK};
83 static const uint32_t n64_mouse_desc[4] = {0x000000F0, 0x00000000, 0x00000000, 0x00000000};
84 static const uint32_t n64_mouse_btns_mask[32] = {
85 0, 0, 0, 0,
86 0, 0, 0, 0,
87 0, 0, 0, 0,
88 0, 0, 0, 0,
89 0, 0, 0, 0,
90 0, 0, 0, 0,
91 BIT(N64_B), 0, 0, 0,
92 BIT(N64_A), 0, 0, 0,
93 };
94
95 static const uint32_t n64_kb_mask[4] = {0xE6FF0F0F, 0xFFFFFFFF, 0x2D7FFFFF, 0x0007C000 | BR_COMBO_MASK};
96 static const uint32_t n64_kb_desc[4] = {0x00000000, 0x00000000, 0x00000000, 0x00000000};
97 static const uint16_t n64_kb_scancode[KBM_MAX] = {
98 /* KB_A, KB_D, KB_S, KB_W, MOUSE_X_LEFT, MOUSE_X_RIGHT, MOUSE_Y_DOWN MOUSE_Y_UP */
99 0x070D, 0x0705, 0x070C, 0x0105, 0x0000, 0x0000, 0x0000, 0x0000,
100 /* KB_LEFT, KB_RIGHT, KB_DOWN, KB_UP, MOUSE_WX_LEFT, MOUSE_WX_RIGHT, MOUSE_WY_DOWN, MOUSE_WY_UP */
101 0x0502, 0x0504, 0x0503, 0x0402, 0x0000, 0x0000, 0x0000, 0x0000,
102 /* KB_Q, KB_R, KB_E, KB_F, KB_ESC, KB_ENTER, KB_LWIN, KB_HASH */
103 0x010C, 0x0107, 0x0106, 0x0706, 0x080A, 0x040D, 0x070F, 0x050D,
104 /* MOUSE_RIGHT, KB_Z, KB_LCTRL, MOUSE_MIDDLE, MOUSE_LEFT, KB_X, KB_LSHIFT, KB_SPACE */
105 0x0000, 0x080D, 0x0711, 0x0000, 0x0000, 0x080C, 0x010E, 0x0206,
106
107 /* KB_B, KB_C, KB_G, KB_H, KB_I, KB_J, KB_K, KB_L */
108 0x0807, 0x0805, 0x0707, 0x0708, 0x0408, 0x0709, 0x0309, 0x0308,
109 /* KB_M, KB_N, KB_O, KB_P, KB_T, KB_U, KB_V, KB_Y */
110 0x0809, 0x0808, 0x0407, 0x0406, 0x0108, 0x0409, 0x0806, 0x0109,
111 /* KB_1, KB_2, KB_3, KB_4, KB_5, KB_6, KB_7, KB_8 */
112 0x050C, 0x0505, 0x0506, 0x0507, 0x0508, 0x0509, 0x0609, 0x0608,
113 /* KB_9, KB_0, KB_BACKSPACE, KB_TAB, KB_MINUS, KB_EQUAL, KB_LEFTBRACE, KB_RIGHTBRACE */
114 0x0607, 0x0606, 0x060D, 0x010D, 0x0605, 0x060C, 0x040C, 0x0604,
115
116 /* KB_BACKSLASH, KB_SEMICOLON, KB_APOSTROPHE, KB_GRAVE, KB_COMMA, KB_DOT, KB_SLASH, KB_CAPSLOCK */
117 0x0410, 0x0307, 0x0306, 0x0405, 0x0209, 0x0208, 0x0207, 0x050F,
118 /* KB_F1, KB_F2, KB_F3, KB_F4, KB_F5, KB_F6, KB_F7, KB_F8 */
119 0x010B, 0x010A, 0x080B, 0x070A, 0x070B, 0x020A, 0x020B, 0x030A,
120 /* KB_F9, KB_F10, KB_F11, KB_F12, KB_PSCREEN, KB_SCROLL, KB_PAUSE, KB_INSERT */
121 0x030B, 0x040A, 0x0302, 0x060B, 0x050B, 0x0802, 0x0702, 0x0000,
122 /* KB_HOME, KB_PAGEUP, KB_DEL, KB_END, KB_PAGEDOWN, KB_NUMLOCK, KB_KP_DIV, KB_KP_MULTI */
123 0x0000, 0x0000, 0x0511, 0x0602, 0x0000, 0x050A, 0x0000, 0x0000,
124
125 /* KB_KP_MINUS, KB_KP_PLUS, KB_KP_ENTER, KB_KP_1, KB_KP_2, KB_KP_3, KB_KP_4, KB_KP_5 */
126 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
127 /* KB_KP_6, KB_KP_7, KB_KP_8, KB_KP_9, KB_KP_0, KB_KP_DOT, KB_LALT, KB_RCTRL */
128 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0810, 0x0610,
129 /* KB_RSHIFT, KB_RALT, KB_RWIN */
130 0x060E, 0x020E, 0x0210,
131 };
132
133 2 void IRAM_ATTR n64_init_buffer(int32_t dev_mode, struct wired_data *wired_data) {
134
1/3
✗ Branch 0 (2→3) not taken.
✗ Branch 1 (2→4) not taken.
✓ Branch 2 (2→9) taken 2 times.
2 switch (dev_mode) {
135 case DEV_KB:
136 {
137 memset(wired_data->output, 0, sizeof(struct n64_kb_map));
138 break;
139 }
140 case DEV_MOUSE:
141 {
142 struct n64_mouse_map *map = (struct n64_mouse_map *)wired_data->output;
143
144 for (uint32_t i = 0; i < N64_AXES_MAX; i++) {
145 map->raw_axes[i] = 0;
146 map->relative[i] = 1;
147 }
148 map->buttons = 0x0000;
149 break;
150 }
151 2 default:
152 {
153 2 struct n64_map *map = (struct n64_map *)wired_data->output;
154
155 2 map->buttons = 0x0000;
156
2/2
✓ Branch 0 (11→10) taken 4 times.
✓ Branch 1 (11→12) taken 2 times.
6 for (uint32_t i = 0; i < N64_AXES_MAX; i++) {
157 4 map->axes[n64_axes_idx[i]] = n64_axes_meta[i].neutral;
158 }
159 2 memset(wired_data->output_mask, 0xFF, sizeof(struct n64_map));
160 2 break;
161 }
162 }
163 2 }
164
165 22 void n64_meta_init(struct wired_ctrl *ctrl_data) {
166 22 memset((void *)ctrl_data, 0, sizeof(*ctrl_data)*4);
167
168
2/2
✓ Branch 0 (11→12) taken 176 times.
✓ Branch 1 (11→13) taken 22 times.
198 for (uint32_t i = 0; i < WIRED_MAX_DEV; i++) {
169
2/2
✓ Branch 0 (9→4) taken 352 times.
✓ Branch 1 (9→10) taken 176 times.
528 for (uint32_t j = 0; j < N64_AXES_MAX; j++) {
170
1/3
✗ Branch 0 (4→5) not taken.
✗ Branch 1 (4→6) not taken.
✓ Branch 2 (4→7) taken 352 times.
352 switch (config.out_cfg[i].dev_mode) {
171 case DEV_KB:
172 ctrl_data[i].mask = n64_kb_mask;
173 ctrl_data[i].desc = n64_kb_desc;
174 break;
175 case DEV_MOUSE:
176 ctrl_data[i].mask = n64_mouse_mask;
177 ctrl_data[i].desc = n64_mouse_desc;
178 ctrl_data[i].axes[j + 2].meta = &n64_mouse_axes_meta[j];
179 break;
180 352 case DEV_PAD:
181 default:
182 352 ctrl_data[i].mask = n64_mask;
183 352 ctrl_data[i].desc = n64_desc;
184 352 ctrl_data[i].axes[j].meta = &n64_axes_meta[j];
185 352 break;
186 }
187 }
188 }
189 22 }
190
191 22 static void n64_ctrl_special_action(struct wired_ctrl *ctrl_data, struct wired_data *wired_data) {
192 /* Memory / Rumble toggle */
193
1/2
✓ Branch 0 (2→3) taken 22 times.
✗ Branch 1 (2→15) not taken.
22 if (ctrl_data->map_mask[0] & generic_btns_mask[PAD_MT]) {
194
2/2
✓ Branch 0 (3→4) taken 2 times.
✓ Branch 1 (3→7) taken 20 times.
22 if (ctrl_data->btns[0].value & generic_btns_mask[PAD_MT]) {
195
1/2
✓ Branch 0 (5→6) taken 2 times.
✗ Branch 1 (5→15) not taken.
2 if (!atomic_test_bit(&wired_data->flags, WIRED_WAITING_FOR_RELEASE)) {
196 2 atomic_set_bit(&wired_data->flags, WIRED_WAITING_FOR_RELEASE);
197 }
198 }
199 else {
200
2/2
✓ Branch 0 (8→9) taken 2 times.
✓ Branch 1 (8→15) taken 18 times.
20 if (atomic_test_bit(&wired_data->flags, WIRED_WAITING_FOR_RELEASE)) {
201 2 atomic_clear_bit(&wired_data->flags, WIRED_WAITING_FOR_RELEASE);
202
203 /* Change config directly but do not update file */
204
2/2
✓ Branch 0 (10→11) taken 1 times.
✓ Branch 1 (10→13) taken 1 times.
2 if (config.out_cfg[ctrl_data->index].acc_mode == ACC_MEM) {
205 1 config.out_cfg[ctrl_data->index].acc_mode = ACC_RUMBLE;
206 1 adapter_toggle_fb(ctrl_data->index, 300000, 0xFF, 0xFF);
207 1 printf("# %s: Set rumble pak\n", __FUNCTION__);
208 }
209 else {
210 1 config.out_cfg[ctrl_data->index].acc_mode = ACC_MEM;
211 1 adapter_toggle_fb(ctrl_data->index, 150000, 0xFF, 0xFF);
212 1 printf("# %s: Set ctrl pak\n", __FUNCTION__);
213 }
214 }
215 }
216 }
217
218 /* Bank rotation */
219
1/2
✓ Branch 0 (15→16) taken 22 times.
✗ Branch 1 (15→24) not taken.
22 if (ctrl_data->map_mask[0] & generic_btns_mask[PAD_MQ]) {
220
2/2
✓ Branch 0 (16→17) taken 2 times.
✓ Branch 1 (16→20) taken 20 times.
22 if (ctrl_data->btns[0].value & generic_btns_mask[PAD_MQ]) {
221
1/2
✓ Branch 0 (18→19) taken 2 times.
✗ Branch 1 (18→24) not taken.
2 if (!atomic_test_bit(&wired_data->flags, WIRED_WAITING_FOR_RELEASE2)) {
222 2 atomic_set_bit(&wired_data->flags, WIRED_WAITING_FOR_RELEASE2);
223 }
224 }
225 else {
226
2/2
✓ Branch 0 (21→22) taken 1 times.
✓ Branch 1 (21→24) taken 19 times.
20 if (atomic_test_bit(&wired_data->flags, WIRED_WAITING_FOR_RELEASE2)) {
227 1 atomic_clear_bit(&wired_data->flags, WIRED_WAITING_FOR_RELEASE2);
228
229 1 config.global_cfg.banksel = (config.global_cfg.banksel + 1) & 0x3;
230 1 printf("# %s: Set ctrl pak bank to %d\n", __FUNCTION__, config.global_cfg.banksel);
231 }
232 }
233 }
234 22 }
235
236 22 static void n64_ctrl_from_generic(struct wired_ctrl *ctrl_data, struct wired_data *wired_data) {
237 22 struct n64_map map_tmp;
238 22 uint32_t map_mask = 0xFFFF;
239
240 22 memcpy((void *)&map_tmp, wired_data->output, sizeof(map_tmp));
241
242
2/2
✓ Branch 0 (11→4) taken 704 times.
✓ Branch 1 (11→12) taken 22 times.
726 for (uint32_t i = 0; i < ARRAY_SIZE(generic_btns_mask); i++) {
243
2/2
✓ Branch 0 (4→5) taken 330 times.
✓ Branch 1 (4→6) taken 374 times.
704 if (ctrl_data->map_mask[0] & generic_btns_mask[i]) {
244
2/2
✓ Branch 0 (6→7) taken 26 times.
✓ Branch 1 (6→8) taken 348 times.
374 if (ctrl_data->btns[0].value & generic_btns_mask[i]) {
245 26 map_tmp.buttons |= n64_btns_mask[i];
246 26 map_mask &= ~n64_btns_mask[i];
247 26 wired_data->cnt_mask[i] = ctrl_data->btns[0].cnt_mask[i];
248 }
249
2/2
✓ Branch 0 (8→5) taken 129 times.
✓ Branch 1 (8→9) taken 219 times.
348 else if (map_mask & n64_btns_mask[i]) {
250 219 map_tmp.buttons &= ~n64_btns_mask[i];
251 219 wired_data->cnt_mask[i] = 0;
252 }
253 }
254 }
255
256 22 n64_ctrl_special_action(ctrl_data, wired_data);
257
258
2/2
✓ Branch 0 (23→14) taken 44 times.
✓ Branch 1 (23→24) taken 22 times.
66 for (uint32_t i = 0; i < N64_AXES_MAX; i++) {
259
1/2
✓ Branch 0 (15→16) taken 44 times.
✗ Branch 1 (15→21) not taken.
44 if (ctrl_data->map_mask[0] & (axis_to_btn_mask(i) & n64_desc[0])) {
260
1/2
✗ Branch 0 (16→17) not taken.
✓ Branch 1 (16→18) taken 44 times.
44 if (ctrl_data->axes[i].value > ctrl_data->axes[i].meta->size_max) {
261 map_tmp.axes[n64_axes_idx[i]] = 127;
262 }
263
1/2
✗ Branch 0 (18→19) not taken.
✓ Branch 1 (18→20) taken 44 times.
44 else if (ctrl_data->axes[i].value < ctrl_data->axes[i].meta->size_min) {
264 map_tmp.axes[n64_axes_idx[i]] = -128;
265 }
266 else {
267 44 map_tmp.axes[n64_axes_idx[i]] = (uint8_t)(ctrl_data->axes[i].value + ctrl_data->axes[i].meta->neutral);
268 }
269 }
270 44 wired_data->cnt_mask[axis_to_btn_id(i)] = ctrl_data->axes[i].cnt_mask;
271 }
272
273 22 memcpy(wired_data->output, (void *)&map_tmp, sizeof(map_tmp));
274
275 22 TESTS_CMDS_LOG("\"wired_output\": {\"axes\": [%d, %d], \"btns\": %d},\n",
276 map_tmp.axes[n64_axes_idx[0]], map_tmp.axes[n64_axes_idx[1]], map_tmp.buttons);
277 BT_MON_LOG("\"wired_output\": {\"axes\": [%02X, %02X], \"btns\": %04X},\n",
278 22 map_tmp.axes[n64_axes_idx[0]], map_tmp.axes[n64_axes_idx[1]], map_tmp.buttons);
279 22 }
280
281 static void n64_mouse_from_generic(struct wired_ctrl *ctrl_data, struct wired_data *wired_data) {
282 struct n64_mouse_map map_tmp;
283 int32_t *raw_axes = (int32_t *)(wired_data->output + 4);
284
285 memcpy((void *)&map_tmp, wired_data->output, sizeof(map_tmp));
286
287 for (uint32_t i = 0; i < ARRAY_SIZE(generic_btns_mask); i++) {
288 if (ctrl_data->map_mask[0] & BIT(i)) {
289 if (ctrl_data->btns[0].value & generic_btns_mask[i]) {
290 map_tmp.buttons |= n64_mouse_btns_mask[i];
291 }
292 else {
293 map_tmp.buttons &= ~n64_mouse_btns_mask[i];
294 }
295 }
296 }
297
298 for (uint32_t i = 2; i < 4; i++) {
299 if (ctrl_data->map_mask[0] & (axis_to_btn_mask(i) & n64_mouse_desc[0])) {
300 if (ctrl_data->axes[i].relative) {
301 map_tmp.relative[n64_axes_idx[i]] = 1;
302 atomic_add(&raw_axes[n64_axes_idx[i]], ctrl_data->axes[i].value);
303 }
304 else {
305 map_tmp.relative[n64_axes_idx[i]] = 0;
306 raw_axes[n64_axes_idx[i]] = ctrl_data->axes[i].value;
307 }
308 }
309 }
310
311 memcpy(wired_data->output, (void *)&map_tmp, sizeof(map_tmp) - 8);
312 }
313
314 static void n64_kb_from_generic(struct wired_ctrl *ctrl_data, struct wired_data *wired_data) {
315 struct n64_kb_map map_tmp = {0};
316 uint32_t code_idx = 0;
317
318 for (uint32_t i = 0; i < KBM_MAX && code_idx < ARRAY_SIZE(map_tmp.key_codes); i++) {
319 if (ctrl_data->map_mask[i / 32] & BIT(i & 0x1F)) {
320 if (ctrl_data->btns[i / 32].value & BIT(i & 0x1F)) {
321 if (n64_kb_scancode[i]) {
322 map_tmp.key_codes[code_idx] = n64_kb_scancode[i];
323 code_idx++;
324 }
325 }
326 }
327 }
328
329 /* map_mask is in IRAM but somehow GCC emit an l8ui which crash the ESP32 */
330 /* volatile var force it to use a l32 */
331 volatile uint32_t mask = ctrl_data->map_mask[2];
332 if (mask & BIT(KB_HOME & 0x1F)) {
333 if (ctrl_data->btns[2].value & BIT(KB_HOME & 0x1F)) {
334 map_tmp.bitfield = 0x01;
335 }
336 }
337
338 memcpy(wired_data->output, (void *)&map_tmp, sizeof(map_tmp));
339 }
340
341 22 void n64_from_generic(int32_t dev_mode, struct wired_ctrl *ctrl_data, struct wired_data *wired_data) {
342
1/3
✗ Branch 0 (2→3) not taken.
✗ Branch 1 (2→5) not taken.
✓ Branch 2 (2→7) taken 22 times.
22 switch (dev_mode) {
343 case DEV_KB:
344 n64_kb_from_generic(ctrl_data, wired_data);
345 break;
346 case DEV_MOUSE:
347 n64_mouse_from_generic(ctrl_data, wired_data);
348 break;
349 22 case DEV_PAD:
350 default:
351 22 n64_ctrl_from_generic(ctrl_data, wired_data);
352 22 break;
353 }
354 22 }
355
356 void n64_fb_to_generic(int32_t dev_mode, struct raw_fb *raw_fb_data, struct generic_fb *fb_data) {
357 fb_data->wired_id = raw_fb_data->header.wired_id;
358 fb_data->type = raw_fb_data->header.type;
359
360 /* This stop rumble when BR timeout trigger */
361 if (raw_fb_data->header.data_len == 0) {
362 fb_data->state = 0;
363 fb_data->lf_pwr = fb_data->hf_pwr = 0;
364 }
365 else {
366 fb_data->state = raw_fb_data->data[0];
367 fb_data->lf_pwr = (fb_data->state) ? 0xFF : 0x00;
368 fb_data->hf_pwr = (fb_data->state) ? 0xFF : 0x00;
369 }
370 }
371
372 void IRAM_ATTR n64_gen_turbo_mask(struct wired_data *wired_data) {
373 struct n64_map *map_mask = (struct n64_map *)wired_data->output_mask;
374
375 memset(map_mask, 0xFF, sizeof(*map_mask));
376
377 wired_gen_turbo_mask_btns16_pos(wired_data, &map_mask->buttons, n64_btns_mask);
378 wired_gen_turbo_mask_axes8(wired_data, map_mask->axes, N64_AXES_MAX, n64_axes_idx, n64_axes_meta);
379 }
380