c28a7c81 |
1 | /* |
ebeef6c6 |
2 | * TeensyTP, Team Player/4-Player Adaptor implementation for Teensy3 |
3 | * using a host machine with USB hub |
4 | * Copyright (c) 2015 notaz |
c28a7c81 |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining |
7 | * a copy of this software and associated documentation files (the |
8 | * "Software"), to deal in the Software without restriction, including |
9 | * without limitation the rights to use, copy, modify, merge, publish, |
10 | * distribute, sublicense, and/or sell copies of the Software, and to |
11 | * permit persons to whom the Software is furnished to do so, subject to |
12 | * the following conditions: |
13 | * |
14 | * The above copyright notice and this permission notice shall be |
15 | * included in all copies or substantial portions of the Software. |
16 | * |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
20 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
21 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
22 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
24 | * SOFTWARE. |
25 | */ |
26 | |
0c1e003e |
27 | #include <stdint.h> |
28 | #include <stdio.h> |
29 | #include <string.h> |
30 | #include "teensy3/core_pins.h" |
31 | #include "teensy3/usb_seremu.h" |
32 | #include "teensy3/usb_rawhid.h" |
f20de073 |
33 | #include "pkts.h" |
34 | |
19560e5f |
35 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) |
36 | |
ebeef6c6 |
37 | #define noinline __attribute__((noclone,noinline)) |
f20de073 |
38 | |
0c1e003e |
39 | ssize_t _write(int fd, const void *buf, size_t nbyte) |
40 | { |
41 | char tbuf[64]; |
42 | int ret; |
43 | |
44 | if (fd != 1 && fd != 2) { |
45 | snprintf(tbuf, sizeof(tbuf), "write to fd %d\n", fd); |
46 | usb_seremu_write(tbuf, strlen(tbuf)); |
47 | } |
48 | |
49 | ret = usb_seremu_write(buf, nbyte); |
50 | return ret < 0 ? ret : nbyte; |
51 | } |
52 | |
53 | void yield(void) |
54 | { |
55 | } |
56 | |
ebeef6c6 |
57 | /* |
58 | * 00157:231: w 70 @ 007ef2 |
59 | * 00157:231: r 7f @ 007f06 |
60 | * 00157:231: w 30 @ 007efa |
61 | * 00157:231: r 33 @ 007f06 |
62 | * 00157:232: w 40 @ 007f6a |
63 | * 00157:232: r 7f @ 007f70 |
64 | */ |
65 | |
66 | /* ?0SA 00DU, ?1CB RLDU */ |
67 | |
68 | static struct { |
69 | union { |
70 | uint16_t hw; |
71 | uint8_t b[2]; |
72 | } btn3_state[2]; |
73 | //uint8_t btn6_state[2][4]; |
74 | uint8_t tp_state[16]; |
75 | |
76 | uint32_t cnt; |
77 | uint32_t mode; |
78 | uint32_t pl1th:1; |
79 | } g; |
19560e5f |
80 | |
81 | /* player1 TH */ |
ebeef6c6 |
82 | #define PL1_ISFR PORTD_ISFR |
83 | #define PL1_TH() ((CORE_PIN21_PINREG >> CORE_PIN21_BIT) & 1) |
84 | #define PL1_TR() ((CORE_PIN20_PINREG >> CORE_PIN20_BIT) & 1) |
85 | #define PL1_TH_MASK CORE_PIN21_BITMASK |
86 | #define PL1_TR_MASK CORE_PIN20_BITMASK |
19560e5f |
87 | |
ebeef6c6 |
88 | static void pl1_isr_3btn(void) |
9c4f55f4 |
89 | { |
ebeef6c6 |
90 | uint32_t isfr; |
9c4f55f4 |
91 | |
19560e5f |
92 | isfr = PL1_ISFR; |
93 | PL1_ISFR = isfr; |
4af6d4e5 |
94 | |
ebeef6c6 |
95 | GPIOD_PDOR = g.btn3_state[0].b[PL1_TH()]; |
4af6d4e5 |
96 | } |
97 | |
ebeef6c6 |
98 | static noinline void pl1_isr_tp_do_th(void) |
8e400118 |
99 | { |
ebeef6c6 |
100 | uint32_t cnt, th; |
8e400118 |
101 | |
19560e5f |
102 | th = PL1_TH(); |
ebeef6c6 |
103 | cnt = !th; |
f20de073 |
104 | |
ebeef6c6 |
105 | GPIOD_PDOR = g.tp_state[cnt] | (PL1_TR() << 4); |
106 | g.cnt = cnt; |
107 | g.pl1th = th; |
f20de073 |
108 | } |
109 | |
ebeef6c6 |
110 | static void pl1_isr_tp(void) |
4af6d4e5 |
111 | { |
ebeef6c6 |
112 | uint32_t isfr; |
4af6d4e5 |
113 | |
19560e5f |
114 | isfr = PL1_ISFR; |
115 | PL1_ISFR = isfr; |
4af6d4e5 |
116 | |
ebeef6c6 |
117 | if (isfr & PL1_TH_MASK) |
118 | pl1_isr_tp_do_th(); |
119 | if (!(isfr & PL1_TR_MASK)) |
120 | return; |
121 | if (g.pl1th) |
122 | return; |
123 | |
124 | g.cnt++; |
125 | GPIOD_PDOR = g.tp_state[g.cnt & 0x0f] | (PL1_TR() << 4); |
4af6d4e5 |
126 | } |
127 | |
19560e5f |
128 | /* player2 TH */ |
129 | #define PL2_ISFR PORTC_ISFR |
130 | #define PL2_TH() ((CORE_PIN15_PINREG >> CORE_PIN15_BIT) & 1) |
131 | #define PL2_ADJ(x) ((x) | ((x) << 12)) |
132 | |
ebeef6c6 |
133 | static void pl2_isr_nop(void) |
19560e5f |
134 | { |
ebeef6c6 |
135 | uint32_t isfr; |
19560e5f |
136 | |
137 | isfr = PL2_ISFR; |
138 | PL2_ISFR = isfr; |
4af6d4e5 |
139 | |
ebeef6c6 |
140 | //GPIOB_PDOR = PL2_ADJ(v); |
e8e66d02 |
141 | } |
142 | |
19560e5f |
143 | /* * */ |
ebeef6c6 |
144 | static void clear_state() |
6d4349fc |
145 | { |
ebeef6c6 |
146 | int p, i; |
147 | |
148 | // no hw connected |
149 | for (p = 0; p < 2; p++) { |
150 | for (i = 0; i < 2; i++) |
151 | g.btn3_state[p].b[i] = 0x3f; |
152 | //for (i = 0; i < 4; i++) |
153 | // g.btn6_state[p][i] = 0x3f; |
6d4349fc |
154 | } |
f20de073 |
155 | |
ebeef6c6 |
156 | // 4 pads, nothing pressed |
157 | g.tp_state[0] = 0x03; |
158 | g.tp_state[1] = 0x0f; |
159 | g.tp_state[2] = 0x00; |
160 | g.tp_state[3] = 0x00; |
161 | for (i = 4; i < 8; i++) |
162 | g.tp_state[i] = 0x00; // 3btn |
163 | for (; i < 16; i++) |
164 | g.tp_state[i] = 0x0f; |
165 | |
166 | g.cnt = 0; |
167 | g.pl1th = PL1_TH(); |
f20de073 |
168 | } |
169 | |
ebeef6c6 |
170 | static void switch_mode(int mode) |
f20de073 |
171 | { |
ebeef6c6 |
172 | void (*pl1_handler)(void) = pl1_isr_3btn; |
173 | void (*pl2_handler)(void) = pl2_isr_nop; |
f20de073 |
174 | |
ebeef6c6 |
175 | clear_state(); |
f20de073 |
176 | |
ebeef6c6 |
177 | switch (mode) { |
178 | default: |
179 | case OP_MODE_3BTN: |
180 | case OP_MODE_6BTN: |
181 | pinMode(20, OUTPUT); |
182 | break; |
183 | case OP_MODE_TEAMPLAYER: |
184 | pinMode(20, INPUT); |
185 | attachInterrupt(20, pl1_handler, CHANGE); |
186 | pl1_handler = pl1_isr_tp; |
187 | pl2_handler = pl2_isr_nop; |
188 | GPIOB_PDOR = PL2_ADJ(0x3f); |
189 | break; |
f20de073 |
190 | } |
191 | |
ebeef6c6 |
192 | attachInterruptVector(IRQ_PORTD, pl1_handler); |
193 | attachInterruptVector(IRQ_PORTC, pl2_handler); |
194 | g.mode = mode; |
f20de073 |
195 | } |
196 | |
ebeef6c6 |
197 | // btns: MXYZ SACB RLDU |
198 | static void update_btns_3btn(const struct tp_pkt *pkt, uint32_t player) |
e8e66d02 |
199 | { |
ebeef6c6 |
200 | // ?1CB RLDU ?0SA 00DU |
201 | uint16_t s_3btn; |
e8e66d02 |
202 | |
ebeef6c6 |
203 | s_3btn = (~pkt->bnts[player] << 8) & 0x3f00; |
204 | s_3btn |= (~pkt->bnts[player] >> 2) & 0x0030; // SA |
205 | s_3btn |= (~pkt->bnts[player] ) & 0x0003; // DU |
206 | g.btn3_state[player].hw = s_3btn; |
e8e66d02 |
207 | } |
208 | |
ebeef6c6 |
209 | static void update_btns_tp(const struct tp_pkt *pkt, uint32_t player) |
e8e66d02 |
210 | { |
ebeef6c6 |
211 | uint8_t b0, b1; |
212 | |
213 | b0 = ~pkt->bnts[player] & 0x0f; |
214 | b1 = (~pkt->bnts[player] >> 4) & 0x0f; |
215 | g.tp_state[8 + player * 2 ] = b0; |
216 | g.tp_state[8 + player * 2 + 1] = b1; |
f20de073 |
217 | } |
218 | |
ebeef6c6 |
219 | static void do_usb(const void *buf) |
f20de073 |
220 | { |
ebeef6c6 |
221 | const struct tp_pkt *pkt = buf; |
222 | uint32_t i; |
f20de073 |
223 | |
224 | switch (pkt->type) { |
ebeef6c6 |
225 | case PKT_UPD_MODE: |
e8e66d02 |
226 | __disable_irq(); |
ebeef6c6 |
227 | switch_mode(pkt->mode); |
4af6d4e5 |
228 | __enable_irq(); |
f20de073 |
229 | break; |
ebeef6c6 |
230 | case PKT_UPD_BTNS: |
231 | switch (g.mode) { |
232 | case OP_MODE_3BTN: |
233 | if (pkt->changed_players & 1) |
234 | update_btns_3btn(pkt, 0); |
235 | if (pkt->changed_players & 2) |
236 | update_btns_3btn(pkt, 1); |
237 | break; |
238 | case OP_MODE_TEAMPLAYER: |
239 | for (i = 0; i < 4; i++) { |
240 | if (!(pkt->changed_players & (1 << i))) |
241 | continue; |
242 | update_btns_tp(pkt, i); |
243 | } |
f20de073 |
244 | } |
f20de073 |
245 | break; |
246 | default: |
247 | printf("got unknown pkt type: %04x\n", pkt->type); |
248 | break; |
249 | } |
9c4f55f4 |
250 | } |
251 | |
0c1e003e |
252 | int main(void) |
253 | { |
beaf6d89 |
254 | uint32_t led_time = 0; |
f20de073 |
255 | uint8_t buf[64]; |
ebeef6c6 |
256 | int ret; |
0c1e003e |
257 | |
258 | delay(1000); // wait for usb.. |
259 | |
260 | printf("starting, rawhid: %d\n", usb_rawhid_available()); |
261 | |
ebeef6c6 |
262 | switch_mode(OP_MODE_3BTN); |
19560e5f |
263 | |
264 | // md pin th tr tl r l d u vsync th tr tl r l d u |
265 | // 7 9 6 4 3 2 1 7 9 6 4 3 2 1 |
266 | // md bit* 6 5 4 3 2 1 0 6 5 4 3 2 1 0 |
267 | // t bit d6 d5 d4 d3 d2 d1 d0 a12 c0 b17 b16 b3 b2 b1 b0 |
268 | // t pin 21 20 6 8 7 14 2 3 15 1 0 18 19 17 16 |
9c4f55f4 |
269 | // * - note: tl/tr mixed in most docs |
19560e5f |
270 | |
271 | // player1 |
272 | pinMode(21, INPUT); |
ebeef6c6 |
273 | // note: func is not used, see attachInterruptVector() |
274 | attachInterrupt(21, pl1_isr_3btn, CHANGE); |
19560e5f |
275 | NVIC_SET_PRIORITY(IRQ_PORTD, 0); |
f20de073 |
276 | |
9c4f55f4 |
277 | pinMode( 2, OUTPUT); |
278 | pinMode(14, OUTPUT); |
279 | pinMode( 7, OUTPUT); |
280 | pinMode( 8, OUTPUT); |
281 | pinMode( 6, OUTPUT); |
282 | pinMode(20, OUTPUT); |
283 | |
19560e5f |
284 | // player2 |
285 | pinMode(15, INPUT); |
ebeef6c6 |
286 | attachInterrupt(15, pl2_isr_nop, CHANGE); |
19560e5f |
287 | NVIC_SET_PRIORITY(IRQ_PORTC, 0); |
288 | |
289 | pinMode(16, OUTPUT); |
290 | pinMode(17, OUTPUT); |
291 | pinMode(19, OUTPUT); |
292 | pinMode(18, OUTPUT); |
293 | pinMode( 0, OUTPUT); |
294 | pinMode( 1, OUTPUT); |
295 | |
9c4f55f4 |
296 | // led |
297 | pinMode(13, OUTPUT); |
9c4f55f4 |
298 | |
19560e5f |
299 | // lower other priorities |
300 | SCB_SHPR1 = SCB_SHPR2 = SCB_SHPR3 = 0x10101010; |
301 | |
9c4f55f4 |
302 | // CORE_PIN0_PORTSET CORE_PIN0_BITMASK PORTB_PCR16 |
4af6d4e5 |
303 | printf("GPIOB PDDR, PDIR: %08x %08x\n", GPIOB_PDIR, GPIOB_PDDR); |
9c4f55f4 |
304 | printf("GPIOC PDDR, PDIR: %08x %08x\n", GPIOC_PDIR, GPIOC_PDDR); |
305 | printf("GPIOD PDDR, PDIR: %08x %08x\n", GPIOD_PDIR, GPIOD_PDDR); |
306 | printf("PORTB_PCR16: %08x\n", PORTB_PCR16); |
4af6d4e5 |
307 | printf("PORTC_PCR6: %08x\n", PORTC_PCR6); |
8e400118 |
308 | printf("PORTD_PCR0: %08x\n", PORTD_PCR0); |
9c4f55f4 |
309 | |
f20de073 |
310 | asm("mrs %0, BASEPRI" : "=r"(ret)); |
8e400118 |
311 | printf("BASEPRI: %d, SHPR: %08x %08x %08x\n", |
312 | ret, SCB_SHPR1, SCB_SHPR2, SCB_SHPR3); |
0c1e003e |
313 | |
1cb2822f |
314 | while (1) { |
beaf6d89 |
315 | uint32_t now; |
e8e66d02 |
316 | |
beaf6d89 |
317 | now = millis(); |
318 | |
beaf6d89 |
319 | // led? |
320 | if (CORE_PIN13_PORTREG & CORE_PIN13_BITMASK) { |
321 | if ((int)(now - led_time) > 10) |
322 | CORE_PIN13_PORTCLEAR = CORE_PIN13_BITMASK; |
1cb2822f |
323 | } |
beaf6d89 |
324 | |
325 | // something on rawhid? |
326 | if (usb_rawhid_available() > 0) |
327 | { |
328 | ret = usb_rawhid_recv(buf, 20); |
329 | if (ret == 64) { |
330 | led_time = millis(); |
331 | CORE_PIN13_PORTSET = CORE_PIN13_BITMASK; |
332 | |
333 | do_usb(buf); |
334 | } |
335 | else { |
336 | printf("usb_rawhid_recv: %d\n", ret); |
337 | } |
1cb2822f |
338 | } |
0c1e003e |
339 | } |
1cb2822f |
340 | |
341 | return 0; |
0c1e003e |
342 | } |