teensytp: initial implementation (derived from teensytas)
[megadrive.git] / teensytp / main.c
1 /*
2  * TeensyTP, Team Player/4-Player Adaptor implementation for Teensy3
3  * using a host machine with USB hub
4  * Copyright (c) 2015 notaz
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
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"
33 #include "pkts.h"
34
35 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
36
37 #define noinline __attribute__((noclone,noinline))
38
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
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;
80
81 /* player1 TH */
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
87
88 static void pl1_isr_3btn(void)
89 {
90         uint32_t isfr;
91
92         isfr = PL1_ISFR;
93         PL1_ISFR = isfr;
94
95         GPIOD_PDOR = g.btn3_state[0].b[PL1_TH()];
96 }
97
98 static noinline void pl1_isr_tp_do_th(void)
99 {
100         uint32_t cnt, th;
101
102         th = PL1_TH();
103         cnt = !th;
104
105         GPIOD_PDOR = g.tp_state[cnt] | (PL1_TR() << 4);
106         g.cnt = cnt;
107         g.pl1th = th;
108 }
109
110 static void pl1_isr_tp(void)
111 {
112         uint32_t isfr;
113
114         isfr = PL1_ISFR;
115         PL1_ISFR = isfr;
116
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);
126 }
127
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
133 static void pl2_isr_nop(void)
134 {
135         uint32_t isfr;
136
137         isfr = PL2_ISFR;
138         PL2_ISFR = isfr;
139
140         //GPIOB_PDOR = PL2_ADJ(v);
141 }
142
143 /* * */
144 static void clear_state()
145 {
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;
154         }
155
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();
168 }
169
170 static void switch_mode(int mode)
171 {
172         void (*pl1_handler)(void) = pl1_isr_3btn;
173         void (*pl2_handler)(void) = pl2_isr_nop;
174
175         clear_state();
176
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;
190         }
191
192         attachInterruptVector(IRQ_PORTD, pl1_handler);
193         attachInterruptVector(IRQ_PORTC, pl2_handler);
194         g.mode = mode;
195 }
196
197 // btns: MXYZ SACB RLDU
198 static void update_btns_3btn(const struct tp_pkt *pkt, uint32_t player)
199 {
200         // ?1CB RLDU ?0SA 00DU
201         uint16_t s_3btn;
202
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;
207 }
208
209 static void update_btns_tp(const struct tp_pkt *pkt, uint32_t player)
210 {
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;
217 }
218
219 static void do_usb(const void *buf)
220 {
221         const struct tp_pkt *pkt = buf;
222         uint32_t i;
223
224         switch (pkt->type) {
225         case PKT_UPD_MODE:
226                 __disable_irq();
227                 switch_mode(pkt->mode);
228                 __enable_irq();
229                 break;
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                         }
244                 }
245                 break;
246         default:
247                 printf("got unknown pkt type: %04x\n", pkt->type);
248                 break;
249         }
250 }
251
252 int main(void)
253 {
254         uint32_t led_time = 0;
255         uint8_t buf[64];
256         int ret;
257
258         delay(1000); // wait for usb..
259
260         printf("starting, rawhid: %d\n", usb_rawhid_available());
261
262         switch_mode(OP_MODE_3BTN);
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
269         // * - note: tl/tr mixed in most docs
270
271         // player1
272         pinMode(21, INPUT);
273         // note: func is not used, see attachInterruptVector()
274         attachInterrupt(21, pl1_isr_3btn, CHANGE);
275         NVIC_SET_PRIORITY(IRQ_PORTD, 0);
276
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
284         // player2
285         pinMode(15, INPUT);
286         attachInterrupt(15, pl2_isr_nop, CHANGE);
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
296         // led
297         pinMode(13, OUTPUT);
298
299         // lower other priorities
300         SCB_SHPR1 = SCB_SHPR2 = SCB_SHPR3 = 0x10101010;
301
302         // CORE_PIN0_PORTSET CORE_PIN0_BITMASK PORTB_PCR16
303         printf("GPIOB PDDR, PDIR: %08x %08x\n", GPIOB_PDIR, GPIOB_PDDR);
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);
307         printf("PORTC_PCR6:  %08x\n", PORTC_PCR6);
308         printf("PORTD_PCR0:  %08x\n", PORTD_PCR0);
309
310         asm("mrs %0, BASEPRI" : "=r"(ret));
311         printf("BASEPRI: %d, SHPR: %08x %08x %08x\n",
312                 ret, SCB_SHPR1, SCB_SHPR2, SCB_SHPR3);
313
314         while (1) {
315                 uint32_t now;
316
317                 now = millis();
318
319                 // led?
320                 if (CORE_PIN13_PORTREG & CORE_PIN13_BITMASK) {
321                         if ((int)(now - led_time) > 10)
322                                 CORE_PIN13_PORTCLEAR = CORE_PIN13_BITMASK;
323                 }
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                         }
338                 }
339         }
340
341         return 0;
342 }