initial support for 2 players
[teensytas.git] / main.c
... / ...
CommitLineData
1/*
2 * TeensyTAS, TAS input player for MegaDrive
3 * Copyright (c) 2014 notaz
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26#include <stdint.h>
27#include <stdio.h>
28#include <string.h>
29#include "teensy3/core_pins.h"
30#include "teensy3/usb_seremu.h"
31#include "teensy3/usb_rawhid.h"
32#include "pkts.h"
33
34#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
35
36#define noinline __attribute__((noinline))
37
38// use power of 2
39#define STREAM_BUF_SIZE 512
40#define STREAM_BUF_MASK (512 - 1)
41
42/* ?0SA 00DU, ?1CB RLDU */
43#define STREAM_EL_SZ 2
44
45static struct {
46 uint8_t stream_to[2][STREAM_BUF_SIZE][STREAM_EL_SZ];
47 uint8_t stream_from[STREAM_BUF_SIZE][STREAM_EL_SZ];
48 struct {
49 union {
50 uint8_t fixed_state[4];
51 uint32_t fixed_state32;
52 };
53 union {
54 uint8_t pending_state[4];
55 uint32_t pending_state32;
56 };
57 } pl[2];
58 uint32_t stream_enable_to:1;
59 uint32_t stream_enable_from:1;
60 uint32_t stream_started:1;
61 uint32_t stream_ended:1;
62 uint32_t inc_mode:2;
63 uint32_t use_pending:1;
64 uint32_t frame_cnt;
65 uint32_t edge_cnt;
66 struct {
67 uint32_t i;
68 uint32_t o;
69 } pos_to_p[2], pos_from;
70} g;
71
72ssize_t _write(int fd, const void *buf, size_t nbyte)
73{
74 char tbuf[64];
75 int ret;
76
77 if (fd != 1 && fd != 2) {
78 snprintf(tbuf, sizeof(tbuf), "write to fd %d\n", fd);
79 usb_seremu_write(tbuf, strlen(tbuf));
80 }
81
82 ret = usb_seremu_write(buf, nbyte);
83 return ret < 0 ? ret : nbyte;
84}
85
86void yield(void)
87{
88}
89
90static noinline void choose_isrs_idle(void);
91
92/* player1 TH */
93#define PL1_ISFR PORTD_ISFR
94#define PL1_TH() ((CORE_PIN21_PINREG >> CORE_PIN21_BIT) & 1)
95
96static void pl1th_isr_fixed(void)
97{
98 uint32_t isfr, th;
99
100 isfr = PL1_ISFR;
101 PL1_ISFR = isfr;
102 th = PL1_TH();
103
104 GPIOD_PDOR = g.pl[0].fixed_state[th];
105 g.edge_cnt++;
106}
107
108static noinline void do_to_step(void)
109{
110 g.frame_cnt++;
111
112 g.pos_to_p[0].o = (g.pos_to_p[0].o + 1) & STREAM_BUF_MASK;
113 if (g.pos_to_p[0].o == g.pos_to_p[0].i)
114 // done
115 choose_isrs_idle();
116}
117
118static void pl1th_isr_do_to_inc(void)
119{
120 uint32_t isfr, th;
121
122 isfr = PL1_ISFR;
123 PL1_ISFR = isfr;
124 th = PL1_TH();
125
126 GPIOD_PDOR = g.stream_to[0][g.pos_to_p[0].o][th];
127 if (th)
128 do_to_step();
129}
130
131static void pl1th_isr_do_to(void)
132{
133 uint32_t isfr, th;
134
135 isfr = PL1_ISFR;
136 PL1_ISFR = isfr;
137 th = PL1_TH();
138
139 GPIOD_PDOR = g.stream_to[0][g.pos_to_p[0].o][th];
140 g.edge_cnt++;
141}
142
143/* player2 TH */
144#define PL2_ISFR PORTC_ISFR
145#define PL2_TH() ((CORE_PIN15_PINREG >> CORE_PIN15_BIT) & 1)
146#define PL2_ADJ(x) ((x) | ((x) << 12))
147
148static void pl2th_isr_fixed(void)
149{
150 uint32_t isfr, th, v;
151
152 isfr = PL2_ISFR;
153 PL2_ISFR = isfr;
154 th = PL2_TH();
155
156 v = g.pl[1].fixed_state[th];
157 GPIOB_PDOR = PL2_ADJ(v);
158}
159
160static void pl2th_isr_do_to(void)
161{
162 uint32_t isfr, th, v;
163
164 isfr = PL2_ISFR;
165 PL2_ISFR = isfr;
166 th = PL2_TH();
167
168 v = g.stream_to[1][g.pos_to_p[0].o][th];
169 GPIOB_PDOR = PL2_ADJ(v);
170
171 g.pos_to_p[1].o = g.pos_to_p[0].o;
172}
173
174static void pl2th_isr_do_to_inc_pl1(void)
175{
176 uint32_t isfr, th, v;
177
178 isfr = PL2_ISFR;
179 PL2_ISFR = isfr;
180 th = PL2_TH();
181
182 v = g.stream_to[1][g.pos_to_p[1].o][th];
183 GPIOB_PDOR = PL2_ADJ(v);
184 if (th) {
185 do_to_step();
186 g.pos_to_p[1].o = g.pos_to_p[0].o;
187 }
188}
189
190/* vsync handler */
191#define VSYNC_ISFR PORTC_ISFR
192
193static void vsync_isr_nop(void)
194{
195 uint32_t isfr;
196
197 isfr = VSYNC_ISFR;
198 VSYNC_ISFR = isfr;
199}
200
201// /vsync starts at line 235/259 (ntsc/pal), just as vcounter jumps back
202// we care when it comes out (/vsync goes high) after 3 lines at 238/262
203static void vsync_isr_frameinc(void)
204{
205 uint32_t isfr;
206
207 isfr = VSYNC_ISFR;
208 VSYNC_ISFR = isfr;
209
210 g.pos_to_p[0].o = (g.pos_to_p[0].o + 1) & STREAM_BUF_MASK;
211 g.pos_to_p[1].o = g.pos_to_p[0].o;
212
213 if (g.pos_to_p[0].o == g.pos_to_p[0].i)
214 choose_isrs_idle();
215 g.frame_cnt++;
216}
217
218/* "recording" data */
219static noinline void do_from_step(void)
220{
221 uint32_t s;
222
223 // should hopefully give atomic fixed_state read..
224 s = g.pl[0].fixed_state32;
225 g.pl[0].fixed_state32 = g.pl[0].pending_state32;
226 g.stream_from[g.pos_from.i][0] = s;
227 g.stream_from[g.pos_from.i][1] = s >> 8;
228 g.pos_from.i = (g.pos_from.i + 1) & STREAM_BUF_MASK;
229}
230
231static void pl1th_isr_fixed_do_from(void)
232{
233 uint32_t isfr, th;
234
235 isfr = PL1_ISFR;
236 PL1_ISFR = isfr;
237 th = PL1_TH();
238
239 GPIOD_PDOR = g.pl[0].fixed_state[th];
240 if (th)
241 do_from_step();
242 g.edge_cnt++;
243}
244
245static void vsync_isr_frameinc_do_from(void)
246{
247 uint32_t isfr;
248
249 isfr = VSYNC_ISFR;
250 VSYNC_ISFR = isfr;
251
252 do_from_step();
253 g.frame_cnt++;
254}
255
256/* * */
257static void choose_isrs(void)
258{
259 void (*pl1th_handler)(void) = pl1th_isr_fixed;
260 void (*pl2th_handler)(void) = pl2th_isr_fixed;
261 void (*vsync_handler)(void) = vsync_isr_nop;
262
263 if (g.stream_enable_to) {
264 switch (g.inc_mode) {
265 case INC_MODE_VSYNC:
266 pl1th_handler = pl1th_isr_do_to;
267 pl2th_handler = pl2th_isr_do_to;
268 vsync_handler = vsync_isr_frameinc;
269 break;
270 case INC_MODE_SHARED_PL1:
271 pl1th_handler = pl1th_isr_do_to_inc;
272 pl2th_handler = pl2th_isr_do_to;
273 break;
274 case INC_MODE_SHARED_PL2:
275 pl1th_handler = pl1th_isr_do_to;
276 pl2th_handler = pl2th_isr_do_to_inc_pl1;
277 break;
278 }
279 }
280 else if (g.stream_enable_from) {
281 g.use_pending = 1;
282 switch (g.inc_mode) {
283 case INC_MODE_VSYNC:
284 vsync_handler = vsync_isr_frameinc_do_from;
285 break;
286 case INC_MODE_SHARED_PL1:
287 pl1th_handler = pl1th_isr_fixed_do_from;
288 break;
289 case INC_MODE_SHARED_PL2:
290 /* TODO */
291 break;
292 }
293 }
294
295 attachInterruptVector(IRQ_PORTD, pl1th_handler);
296 attachInterruptVector(IRQ_PORTC, pl2th_handler);
297 attachInterruptVector(IRQ_PORTA, vsync_handler);
298}
299
300static noinline void choose_isrs_idle(void)
301{
302 attachInterruptVector(IRQ_PORTD, pl1th_isr_fixed);
303 attachInterruptVector(IRQ_PORTC, pl2th_isr_fixed);
304 attachInterruptVector(IRQ_PORTA, vsync_isr_nop);
305}
306
307static void udelay(uint32_t us)
308{
309 uint32_t start = micros();
310
311 while ((micros() - start) < us) {
312 asm volatile("nop; nop; nop; nop");
313 yield();
314 }
315}
316
317static void do_start_seq(void)
318{
319 uint32_t edge_cnt_last;
320 uint32_t edge_cnt;
321 uint32_t start, t1, t2;
322 int tout;
323
324 start = micros();
325 edge_cnt = g.edge_cnt;
326
327 /* magic value */
328 g.pl[0].fixed_state[0] =
329 g.pl[0].fixed_state[1] = 0x25;
330
331 for (tout = 10000; tout > 0; tout--) {
332 edge_cnt_last = edge_cnt;
333 udelay(100);
334 edge_cnt = g.edge_cnt;
335
336 if (edge_cnt != edge_cnt_last)
337 continue;
338 if (!PL1_TH())
339 break;
340 }
341
342 g.pl[0].fixed_state[0] = 0x33;
343 g.pl[0].fixed_state[1] = 0x3f;
344 GPIOD_PDOR = 0x33;
345
346 t1 = micros();
347 if (tout == 0) {
348 printf("start_seq timeout1, t=%u\n", t1 - start);
349 return;
350 }
351
352 for (tout = 100000; tout > 0; tout--) {
353 udelay(1);
354
355 if (PL1_TH())
356 break;
357 }
358
359 t2 = micros();
360 if (tout == 0) {
361 printf("start_seq timeout2, t1=%u, t2=%u\n",
362 t1 - start, t2 - t1);
363 return;
364 }
365
366 //printf(" t1=%u, t2=%u\n", t1 - start, t2 - t1);
367
368 if (g.stream_started) {
369 printf("got start_seq when already started\n");
370 return;
371 }
372
373 if (!g.stream_enable_to && !g.stream_enable_from) {
374 printf("got start_seq, without enable from USB\n");
375 return;
376 }
377
378 if (g.stream_enable_to && g.pos_to_p[0].i == g.pos_to_p[0].o) {
379 printf("got start_seq while stream_to is empty\n");
380 return;
381 }
382
383 if (g.stream_enable_from && g.pos_from.i != g.pos_from.o) {
384 printf("got start_seq while stream_from is not empty\n");
385 return;
386 }
387
388 __disable_irq();
389 choose_isrs();
390 g.stream_started = 1;
391 __enable_irq();
392}
393
394// callers must disable IRQs
395static void clear_state(void)
396{
397 int i;
398
399 g.stream_enable_to = 0;
400 g.stream_enable_from = 0;
401 g.stream_started = 0;
402 g.stream_ended = 0;
403 g.inc_mode = INC_MODE_VSYNC;
404 g.use_pending = 0;
405 for (i = 0; i < ARRAY_SIZE(g.pos_to_p); i++)
406 g.pos_to_p[i].i = g.pos_to_p[i].o = 0;
407 g.pos_from.i = g.pos_from.o = 0;
408 g.frame_cnt = 0;
409 choose_isrs_idle();
410}
411
412static int get_space_to(int p)
413{
414 return STREAM_BUF_SIZE - ((g.pos_to_p[p].i - g.pos_to_p[p].o)
415 & STREAM_BUF_MASK);
416}
417
418static int get_used_from(void)
419{
420 return (g.pos_from.i - g.pos_from.o) & STREAM_BUF_MASK;
421}
422
423static void do_usb(void *buf)
424{
425 struct tas_pkt *pkt = buf;
426 uint32_t pos_to_i, i, p;
427 int space;
428
429 switch (pkt->type) {
430 case PKT_FIXED_STATE:
431 memcpy(&i, pkt->data, sizeof(i));
432 if (g.use_pending)
433 g.pl[0].pending_state32 = i;
434 else
435 g.pl[0].fixed_state32 = i;
436 break;
437 case PKT_STREAM_ENABLE:
438 __disable_irq();
439 clear_state();
440 /* wait for start from MD */
441 g.stream_enable_to = pkt->enable.stream_to;
442 g.stream_enable_from = pkt->enable.stream_from;
443 g.inc_mode = pkt->enable.inc_mode;
444 if (pkt->enable.no_start_seq) {
445 GPIOD_PDOR = 0x3f;
446 choose_isrs();
447 g.stream_started = 1;
448 }
449 __enable_irq();
450 break;
451 case PKT_STREAM_ABORT:
452 __disable_irq();
453 clear_state();
454 __enable_irq();
455 break;
456 case PKT_STREAM_END:
457 g.stream_ended = 1;
458 printf("end of stream\n");
459 break;
460 case PKT_STREAM_DATA_TO_P1:
461 case PKT_STREAM_DATA_TO_P2:
462 p = pkt->type == PKT_STREAM_DATA_TO_P1 ? 0 : 1;
463 pos_to_i = g.pos_to_p[p].i;
464 space = get_space_to(p);
465 if (space <= pkt->size / STREAM_EL_SZ) {
466 printf("got data pkt while space=%d\n", space);
467 return;
468 }
469 for (i = 0; i < pkt->size / STREAM_EL_SZ; i++) {
470 memcpy(&g.stream_to[p][pos_to_i++],
471 pkt->data + i * STREAM_EL_SZ,
472 STREAM_EL_SZ);
473 pos_to_i &= STREAM_BUF_MASK;
474 }
475 g.pos_to_p[p].i = pos_to_i;
476 break;
477 default:
478 printf("got unknown pkt type: %04x\n", pkt->type);
479 break;
480 }
481}
482
483static void check_get_data(int p)
484{
485 struct tas_pkt pkt;
486 uint8_t buf[64];
487 int ret;
488
489 if (get_space_to(p) <= sizeof(pkt.data) / STREAM_EL_SZ)
490 return;
491
492 if (g.pos_to_p[p].i == g.pos_to_p[p].o && g.frame_cnt != 0) {
493 printf("underflow detected\n");
494 g.stream_enable_to = 0;
495 return;
496 }
497
498 pkt.type = PKT_STREAM_REQ;
499 pkt.req.frame = g.frame_cnt;
500 pkt.req.is_p2 = p;
501
502 ret = usb_rawhid_send(&pkt, 1000);
503 if (ret != sizeof(pkt)) {
504 printf("send STREAM_REQ/%d: %d\n", p, ret);
505 return;
506 }
507
508 ret = usb_rawhid_recv(buf, 1000);
509 if (ret != 64)
510 printf("usb_rawhid_recv/s: %d\n", ret);
511 else
512 do_usb(buf);
513}
514
515int main(void)
516{
517 uint32_t led_time = 0;
518 uint32_t scheck_time = 0;
519 uint32_t edge_cnt_last;
520 uint32_t edge_cnt;
521 uint8_t buf[64];
522 int i, ret;
523
524 delay(1000); // wait for usb..
525
526 /* ?0SA 00DU, ?1CB RLDU */
527 for (i = 0; i < 2; i++) {
528 g.pl[i].fixed_state[0] = 0x33;
529 g.pl[i].fixed_state[1] = 0x3f;
530 }
531
532 printf("starting, rawhid: %d\n", usb_rawhid_available());
533
534 choose_isrs_idle();
535
536 // md pin th tr tl r l d u vsync th tr tl r l d u
537 // 7 9 6 4 3 2 1 7 9 6 4 3 2 1
538 // md bit* 6 5 4 3 2 1 0 6 5 4 3 2 1 0
539 // t bit d6 d5 d4 d3 d2 d1 d0 a12 c0 b17 b16 b3 b2 b1 b0
540 // t pin 21 20 6 8 7 14 2 3 15 1 0 18 19 17 16
541 // * - note: tl/tr mixed in most docs
542
543 // player1
544 pinMode(21, INPUT);
545 attachInterrupt(21, pl1th_isr_fixed, CHANGE);
546 NVIC_SET_PRIORITY(IRQ_PORTD, 0);
547
548 pinMode( 2, OUTPUT);
549 pinMode(14, OUTPUT);
550 pinMode( 7, OUTPUT);
551 pinMode( 8, OUTPUT);
552 pinMode( 6, OUTPUT);
553 pinMode(20, OUTPUT);
554
555 // player2
556 pinMode(15, INPUT);
557 attachInterrupt(15, pl1th_isr_fixed, CHANGE);
558 NVIC_SET_PRIORITY(IRQ_PORTC, 0);
559
560 pinMode(16, OUTPUT);
561 pinMode(17, OUTPUT);
562 pinMode(19, OUTPUT);
563 pinMode(18, OUTPUT);
564 pinMode( 0, OUTPUT);
565 pinMode( 1, OUTPUT);
566
567 // vsync line
568 pinMode(3, INPUT);
569 attachInterrupt(3, vsync_isr_nop, RISING);
570 NVIC_SET_PRIORITY(IRQ_PORTA, 16);
571
572 // led
573 pinMode(13, OUTPUT);
574
575 // lower other priorities
576 SCB_SHPR1 = SCB_SHPR2 = SCB_SHPR3 = 0x10101010;
577
578 // CORE_PIN0_PORTSET CORE_PIN0_BITMASK PORTB_PCR16
579 printf("GPIOB PDDR, PDIR: %08x %08x\n", GPIOB_PDIR, GPIOB_PDDR);
580 printf("GPIOC PDDR, PDIR: %08x %08x\n", GPIOC_PDIR, GPIOC_PDDR);
581 printf("GPIOD PDDR, PDIR: %08x %08x\n", GPIOD_PDIR, GPIOD_PDDR);
582 printf("PORTB_PCR16: %08x\n", PORTB_PCR16);
583 printf("PORTC_PCR6: %08x\n", PORTC_PCR6);
584 printf("PORTD_PCR0: %08x\n", PORTD_PCR0);
585
586 asm("mrs %0, BASEPRI" : "=r"(ret));
587 printf("BASEPRI: %d, SHPR: %08x %08x %08x\n",
588 ret, SCB_SHPR1, SCB_SHPR2, SCB_SHPR3);
589
590 edge_cnt_last = g.edge_cnt;
591
592 while (1) {
593 struct tas_pkt pkt;
594 uint32_t now;
595
596 if (g.stream_enable_to && !g.stream_ended) {
597 check_get_data(0);
598 if (g.inc_mode == INC_MODE_SHARED_PL2)
599 check_get_data(1);
600 }
601
602 while (g.stream_enable_from && !g.stream_ended
603 && get_used_from() >= sizeof(pkt.data) / STREAM_EL_SZ)
604 {
605 uint32_t o;
606 int i;
607
608 o = g.pos_from.o;
609 for (i = 0; i < sizeof(pkt.data); i += STREAM_EL_SZ) {
610 memcpy(pkt.data + i, &g.stream_from[o++],
611 STREAM_EL_SZ);
612 o &= STREAM_BUF_MASK;
613 }
614 g.pos_from.o = o;
615
616 pkt.type = PKT_STREAM_DATA_FROM;
617 pkt.size = i;
618
619 ret = usb_rawhid_send(&pkt, 1000);
620 if (ret != sizeof(pkt)) {
621 printf("send DATA_FROM: %d\n", ret);
622 break;
623 }
624 }
625
626 now = millis();
627
628 // start condition check
629 if (now - scheck_time > 1000) {
630 edge_cnt = g.edge_cnt;
631 //printf("e: %d th: %d\n", edge_cnt - edge_cnt_last,
632 // PL1_TH());
633 if ((g.stream_enable_to || g.stream_enable_from)
634 && !g.stream_started
635 && edge_cnt - edge_cnt_last > 10000)
636 {
637 do_start_seq();
638 edge_cnt = g.edge_cnt;
639 }
640 edge_cnt_last = edge_cnt;
641 scheck_time = now;
642 }
643
644 // led?
645 if (CORE_PIN13_PORTREG & CORE_PIN13_BITMASK) {
646 if ((int)(now - led_time) > 10)
647 CORE_PIN13_PORTCLEAR = CORE_PIN13_BITMASK;
648 }
649
650 // something on rawhid?
651 if (usb_rawhid_available() > 0)
652 {
653 ret = usb_rawhid_recv(buf, 20);
654 if (ret == 64) {
655 led_time = millis();
656 CORE_PIN13_PORTSET = CORE_PIN13_BITMASK;
657
658 do_usb(buf);
659 }
660 else {
661 printf("usb_rawhid_recv: %d\n", ret);
662 }
663 }
664 }
665
666 return 0;
667}