c28a7c81 |
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 | |
0c1e003e |
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" |
f20de073 |
32 | #include "pkts.h" |
33 | |
e8e66d02 |
34 | #define noinline __attribute__((noinline)) |
35 | |
f20de073 |
36 | // use power of 2 |
37 | #define STREAM_BUF_SIZE 512 |
38 | #define STREAM_BUF_MASK (512 - 1) |
0c1e003e |
39 | |
9c4f55f4 |
40 | /* ?0SA 00DU, ?1CB RLDU */ |
e8e66d02 |
41 | #define STREAM_EL_SZ 2 |
42 | |
f20de073 |
43 | static struct { |
e8e66d02 |
44 | uint8_t stream_to[STREAM_BUF_SIZE][STREAM_EL_SZ]; |
45 | uint8_t stream_from[STREAM_BUF_SIZE][STREAM_EL_SZ]; |
46 | union { |
47 | uint8_t fixed_state[4]; |
48 | uint32_t fixed_state32; |
49 | }; |
72ef318c |
50 | union { |
51 | uint8_t pending_state[4]; |
52 | uint32_t pending_state32; |
53 | }; |
e8e66d02 |
54 | uint32_t stream_enable_to:1; |
55 | uint32_t stream_enable_from:1; |
f20de073 |
56 | uint32_t stream_started:1; |
e8e66d02 |
57 | uint32_t stream_ended:1; |
4af6d4e5 |
58 | uint32_t use_readinc:1; |
72ef318c |
59 | uint32_t use_pending:1; |
4af6d4e5 |
60 | uint32_t frame_cnt; |
f20de073 |
61 | uint32_t edge_cnt; |
e8e66d02 |
62 | uint32_t t_i; |
63 | uint32_t t_o; |
64 | uint32_t f_i; |
65 | uint32_t f_o; |
f20de073 |
66 | } g; |
67 | |
0c1e003e |
68 | ssize_t _write(int fd, const void *buf, size_t nbyte) |
69 | { |
70 | char tbuf[64]; |
71 | int ret; |
72 | |
73 | if (fd != 1 && fd != 2) { |
74 | snprintf(tbuf, sizeof(tbuf), "write to fd %d\n", fd); |
75 | usb_seremu_write(tbuf, strlen(tbuf)); |
76 | } |
77 | |
78 | ret = usb_seremu_write(buf, nbyte); |
79 | return ret < 0 ? ret : nbyte; |
80 | } |
81 | |
82 | void yield(void) |
83 | { |
84 | } |
85 | |
72ef318c |
86 | /* portb handles TH */ |
4af6d4e5 |
87 | static void portb_isr_fixed(void) |
9c4f55f4 |
88 | { |
f20de073 |
89 | uint32_t isfr, th; |
9c4f55f4 |
90 | |
4af6d4e5 |
91 | isfr = PORTB_ISFR; |
92 | PORTB_ISFR = isfr; |
93 | th = (GPIOB_PDIR >> CORE_PIN0_BIT) & 1; |
94 | |
95 | GPIOD_PDOR = g.fixed_state[th]; |
96 | g.edge_cnt++; |
97 | } |
98 | |
e8e66d02 |
99 | static void portb_isr_do_to_inc(void) |
4af6d4e5 |
100 | { |
101 | uint32_t isfr, th; |
9c4f55f4 |
102 | |
9c4f55f4 |
103 | isfr = PORTB_ISFR; |
104 | PORTB_ISFR = isfr; |
f20de073 |
105 | th = (GPIOB_PDIR >> CORE_PIN0_BIT) & 1; |
106 | |
e8e66d02 |
107 | GPIOD_PDOR = g.stream_to[g.t_o][th]; |
4af6d4e5 |
108 | if (th) { |
e8e66d02 |
109 | g.t_o = (g.t_o + 1) & STREAM_BUF_MASK; |
110 | if (g.t_o == g.t_i) |
4af6d4e5 |
111 | // done |
112 | attachInterruptVector(IRQ_PORTB, portb_isr_fixed); |
113 | g.frame_cnt++; |
f20de073 |
114 | } |
115 | g.edge_cnt++; |
116 | } |
117 | |
e8e66d02 |
118 | static void portb_isr_do_to(void) |
4af6d4e5 |
119 | { |
120 | uint32_t isfr, th; |
121 | |
122 | isfr = PORTB_ISFR; |
123 | PORTB_ISFR = isfr; |
124 | th = (GPIOB_PDIR >> CORE_PIN0_BIT) & 1; |
125 | |
e8e66d02 |
126 | GPIOD_PDOR = g.stream_to[g.t_o][th]; |
4af6d4e5 |
127 | g.edge_cnt++; |
128 | } |
129 | |
130 | static void portc_isr_nop(void) |
131 | { |
132 | uint32_t isfr; |
133 | |
134 | isfr = PORTC_ISFR; |
135 | PORTC_ISFR = isfr; |
136 | } |
137 | |
138 | // /vsync starts at line 235/259 (ntsc/pal), just as vcounter jumps back |
139 | // we care when it comes out (/vsync goes high) after 3 lines at 238/262 |
140 | static void portc_isr_frameinc(void) |
141 | { |
142 | uint32_t isfr; |
143 | |
144 | isfr = PORTC_ISFR; |
145 | PORTC_ISFR = isfr; |
146 | |
e8e66d02 |
147 | g.t_o = (g.t_o + 1) & STREAM_BUF_MASK; |
148 | if (g.t_o == g.t_i) { |
4af6d4e5 |
149 | attachInterruptVector(IRQ_PORTB, portb_isr_fixed); |
150 | attachInterruptVector(IRQ_PORTC, portc_isr_nop); |
151 | } |
152 | g.frame_cnt++; |
153 | } |
154 | |
e8e66d02 |
155 | /* "recording" data */ |
156 | static noinline void do_from_step(void) |
157 | { |
158 | uint32_t s; |
159 | |
160 | // should hopefully give atomic fixed_state read.. |
161 | s = g.fixed_state32; |
72ef318c |
162 | g.fixed_state32 = g.pending_state32; |
e8e66d02 |
163 | g.stream_from[g.f_i][0] = s; |
164 | g.stream_from[g.f_i][1] = s >> 8; |
165 | g.f_i = (g.f_i + 1) & STREAM_BUF_MASK; |
166 | } |
167 | |
168 | static void portb_isr_fixed_do_from(void) |
169 | { |
170 | uint32_t isfr, th; |
171 | |
172 | isfr = PORTB_ISFR; |
173 | PORTB_ISFR = isfr; |
174 | th = (GPIOB_PDIR >> CORE_PIN0_BIT) & 1; |
175 | |
176 | GPIOD_PDOR = g.fixed_state[th]; |
177 | if (th) |
178 | do_from_step(); |
179 | g.edge_cnt++; |
180 | } |
181 | |
182 | static void portc_isr_frameinc_do_from(void) |
183 | { |
184 | uint32_t isfr; |
185 | |
186 | isfr = PORTC_ISFR; |
187 | PORTC_ISFR = isfr; |
188 | |
189 | do_from_step(); |
190 | g.frame_cnt++; |
191 | } |
192 | |
f20de073 |
193 | static void udelay(uint32_t us) |
194 | { |
195 | uint32_t start = micros(); |
196 | |
197 | while ((micros() - start) < us) { |
198 | asm volatile("nop; nop; nop; nop"); |
199 | yield(); |
200 | } |
201 | } |
202 | |
203 | static void do_start_seq(void) |
204 | { |
205 | uint32_t edge_cnt_last; |
206 | uint32_t edge_cnt; |
207 | uint32_t start, t1, t2; |
208 | int tout; |
209 | |
210 | start = micros(); |
211 | edge_cnt = g.edge_cnt; |
212 | |
213 | /* magic value */ |
214 | g.fixed_state[0] = |
215 | g.fixed_state[1] = 0x25; |
216 | |
217 | for (tout = 10000; tout > 0; tout--) { |
218 | edge_cnt_last = edge_cnt; |
219 | udelay(100); |
220 | edge_cnt = g.edge_cnt; |
221 | |
222 | if (edge_cnt != edge_cnt_last) |
223 | continue; |
224 | if (!(GPIOB_PDIR & CORE_PIN0_BITMASK)) |
225 | break; |
226 | } |
227 | |
228 | g.fixed_state[0] = 0x33; |
229 | g.fixed_state[1] = 0x3f; |
230 | GPIOD_PDOR = 0x33; |
231 | |
232 | t1 = micros(); |
233 | if (tout == 0) { |
234 | printf("start_seq timeout1, t=%u\n", t1 - start); |
235 | return; |
236 | } |
237 | |
238 | for (tout = 100000; tout > 0; tout--) { |
239 | udelay(1); |
240 | |
241 | if (GPIOB_PDIR & CORE_PIN0_BITMASK) |
242 | break; |
243 | } |
244 | |
245 | t2 = micros(); |
246 | if (tout == 0) { |
247 | printf("start_seq timeout2, t1=%u, t2=%u\n", |
248 | t1 - start, t2 - t1); |
249 | return; |
250 | } |
251 | |
252 | //printf(" t1=%u, t2=%u\n", t1 - start, t2 - t1); |
253 | |
254 | if (g.stream_started) { |
255 | printf("got start_seq when already started\n"); |
256 | return; |
257 | } |
258 | |
e8e66d02 |
259 | if (!g.stream_enable_to && !g.stream_enable_from) { |
f20de073 |
260 | printf("got start_seq, without enable from USB\n"); |
261 | return; |
262 | } |
263 | |
e8e66d02 |
264 | if (g.stream_enable_to && g.t_i == g.t_o) { |
265 | printf("got start_seq while stream_to is empty\n"); |
266 | return; |
267 | } |
268 | |
269 | if (g.stream_enable_from && g.f_i != g.f_o) { |
270 | printf("got start_seq while stream_from is not empty\n"); |
f20de073 |
271 | return; |
272 | } |
273 | |
4af6d4e5 |
274 | __disable_irq(); |
f20de073 |
275 | g.stream_started = 1; |
e8e66d02 |
276 | if (g.stream_enable_to) { |
277 | if (g.use_readinc) { |
278 | attachInterruptVector(IRQ_PORTB, portb_isr_do_to_inc); |
279 | attachInterruptVector(IRQ_PORTC, portc_isr_nop); |
280 | } |
281 | else { |
282 | attachInterruptVector(IRQ_PORTB, portb_isr_do_to); |
283 | attachInterruptVector(IRQ_PORTC, portc_isr_frameinc); |
284 | } |
4af6d4e5 |
285 | } |
e8e66d02 |
286 | else if (g.stream_enable_from) { |
72ef318c |
287 | g.use_pending = 1; |
e8e66d02 |
288 | if (g.use_readinc) { |
289 | attachInterruptVector(IRQ_PORTB, |
290 | portb_isr_fixed_do_from); |
291 | attachInterruptVector(IRQ_PORTC, portc_isr_nop); |
292 | } |
293 | else { |
294 | attachInterruptVector(IRQ_PORTB, portb_isr_fixed); |
295 | attachInterruptVector(IRQ_PORTC, |
296 | portc_isr_frameinc_do_from); |
297 | } |
4af6d4e5 |
298 | } |
299 | __enable_irq(); |
f20de073 |
300 | } |
301 | |
e8e66d02 |
302 | // callers must disable IRQs |
303 | static void clear_state(void) |
304 | { |
305 | g.stream_enable_to = 0; |
306 | g.stream_enable_from = 0; |
e8e66d02 |
307 | g.stream_started = 0; |
308 | g.stream_ended = 0; |
72ef318c |
309 | g.use_readinc = 0; |
310 | g.use_pending = 0; |
e8e66d02 |
311 | g.t_i = g.t_o = 0; |
312 | g.f_i = g.f_o = 0; |
313 | g.frame_cnt = 0; |
314 | attachInterruptVector(IRQ_PORTB, portb_isr_fixed); |
315 | attachInterruptVector(IRQ_PORTC, portc_isr_nop); |
316 | } |
317 | |
318 | static int get_space_to(void) |
f20de073 |
319 | { |
e8e66d02 |
320 | return STREAM_BUF_SIZE - ((g.t_i - g.t_o) & STREAM_BUF_MASK); |
321 | } |
322 | |
323 | static int get_used_from(void) |
324 | { |
325 | return (g.f_i - g.f_o) & STREAM_BUF_MASK; |
f20de073 |
326 | } |
327 | |
328 | static void do_usb(void *buf) |
329 | { |
330 | struct tas_pkt *pkt = buf; |
e8e66d02 |
331 | uint32_t t_i, i; |
f20de073 |
332 | int space; |
333 | |
334 | switch (pkt->type) { |
335 | case PKT_FIXED_STATE: |
72ef318c |
336 | memcpy(&i, pkt->data, sizeof(i)); |
337 | if (g.use_pending) |
338 | g.pending_state32 = i; |
339 | else |
340 | g.fixed_state32 = i; |
f20de073 |
341 | break; |
342 | case PKT_STREAM_ENABLE: |
4af6d4e5 |
343 | __disable_irq(); |
e8e66d02 |
344 | clear_state(); |
4af6d4e5 |
345 | /* wait for start from MD */ |
e8e66d02 |
346 | g.stream_enable_to = pkt->enable.stream_to; |
347 | g.stream_enable_from = pkt->enable.stream_from; |
348 | g.use_readinc = pkt->enable.use_readinc; |
349 | __enable_irq(); |
350 | break; |
351 | case PKT_STREAM_ABORT: |
352 | __disable_irq(); |
353 | clear_state(); |
4af6d4e5 |
354 | __enable_irq(); |
f20de073 |
355 | break; |
356 | case PKT_STREAM_END: |
e8e66d02 |
357 | g.stream_ended = 1; |
4af6d4e5 |
358 | printf("end of stream\n"); |
f20de073 |
359 | break; |
e8e66d02 |
360 | case PKT_STREAM_DATA_TO: |
361 | t_i = g.t_i; |
362 | space = get_space_to(); |
363 | if (space <= pkt->size / STREAM_EL_SZ) { |
f20de073 |
364 | printf("got data pkt while space=%d\n", space); |
365 | return; |
366 | } |
e8e66d02 |
367 | for (i = 0; i < pkt->size / STREAM_EL_SZ; i++) { |
368 | memcpy(&g.stream_to[t_i++], |
f20de073 |
369 | pkt->data + i * STREAM_EL_SZ, |
370 | STREAM_EL_SZ); |
e8e66d02 |
371 | t_i &= STREAM_BUF_MASK; |
f20de073 |
372 | } |
e8e66d02 |
373 | g.t_i = t_i; |
f20de073 |
374 | break; |
375 | default: |
376 | printf("got unknown pkt type: %04x\n", pkt->type); |
377 | break; |
378 | } |
9c4f55f4 |
379 | } |
380 | |
0c1e003e |
381 | int main(void) |
382 | { |
beaf6d89 |
383 | uint32_t led_time = 0; |
384 | uint32_t scheck_time = 0; |
f20de073 |
385 | uint32_t edge_cnt_last; |
386 | uint32_t edge_cnt; |
387 | uint8_t buf[64]; |
1cb2822f |
388 | int ret; |
0c1e003e |
389 | |
390 | delay(1000); // wait for usb.. |
391 | |
f20de073 |
392 | /* ?0SA 00DU, ?1CB RLDU */ |
393 | g.fixed_state[0] = 0x33; |
394 | g.fixed_state[1] = 0x3f; |
395 | |
0c1e003e |
396 | printf("starting, rawhid: %d\n", usb_rawhid_available()); |
397 | |
4af6d4e5 |
398 | // md pin th tr tl r l d u vsync |
9c4f55f4 |
399 | // md bit* 6 5 4 3 2 1 0 |
4af6d4e5 |
400 | // t bit b16 d5 d4 d3 d2 d1 d0 c6 |
401 | // t pin 0 20 6 8 7 14 2 11 |
9c4f55f4 |
402 | // * - note: tl/tr mixed in most docs |
403 | pinMode(0, INPUT); |
4af6d4e5 |
404 | attachInterrupt(0, portb_isr_fixed, CHANGE); |
405 | attachInterruptVector(IRQ_PORTB, portb_isr_fixed); |
406 | pinMode(11, INPUT); |
407 | attachInterrupt(11, portc_isr_nop, RISING); |
408 | attachInterruptVector(IRQ_PORTC, portc_isr_nop); |
9c4f55f4 |
409 | |
4af6d4e5 |
410 | NVIC_SET_PRIORITY(IRQ_PORTB, 0); |
411 | NVIC_SET_PRIORITY(IRQ_PORTC, 16); |
f20de073 |
412 | |
9c4f55f4 |
413 | pinMode( 2, OUTPUT); |
414 | pinMode(14, OUTPUT); |
415 | pinMode( 7, OUTPUT); |
416 | pinMode( 8, OUTPUT); |
417 | pinMode( 6, OUTPUT); |
418 | pinMode(20, OUTPUT); |
419 | |
420 | // led |
421 | pinMode(13, OUTPUT); |
9c4f55f4 |
422 | |
423 | // CORE_PIN0_PORTSET CORE_PIN0_BITMASK PORTB_PCR16 |
4af6d4e5 |
424 | printf("GPIOB PDDR, PDIR: %08x %08x\n", GPIOB_PDIR, GPIOB_PDDR); |
9c4f55f4 |
425 | printf("GPIOC PDDR, PDIR: %08x %08x\n", GPIOC_PDIR, GPIOC_PDDR); |
426 | printf("GPIOD PDDR, PDIR: %08x %08x\n", GPIOD_PDIR, GPIOD_PDDR); |
427 | printf("PORTB_PCR16: %08x\n", PORTB_PCR16); |
4af6d4e5 |
428 | printf("PORTC_PCR6: %08x\n", PORTC_PCR6); |
9c4f55f4 |
429 | |
f20de073 |
430 | asm("mrs %0, BASEPRI" : "=r"(ret)); |
431 | printf("BASEPRI: %d\n", ret); |
0c1e003e |
432 | |
f20de073 |
433 | edge_cnt_last = g.edge_cnt; |
9c4f55f4 |
434 | |
1cb2822f |
435 | while (1) { |
f20de073 |
436 | struct tas_pkt pkt; |
beaf6d89 |
437 | uint32_t now; |
e8e66d02 |
438 | |
439 | while (g.stream_enable_to && !g.stream_ended |
440 | && get_space_to() > sizeof(pkt.data) / STREAM_EL_SZ) |
f20de073 |
441 | { |
beaf6d89 |
442 | if (g.t_i == g.t_o && g.frame_cnt != 0) { |
443 | printf("underflow detected\n"); |
444 | g.stream_enable_to = 0; |
445 | break; |
446 | } |
447 | |
f20de073 |
448 | pkt.type = PKT_STREAM_REQ; |
4af6d4e5 |
449 | pkt.req.frame = g.frame_cnt; |
450 | |
f20de073 |
451 | ret = usb_rawhid_send(&pkt, 1000); |
452 | if (ret != sizeof(pkt)) { |
453 | printf("send STREAM_REQ: %d\n", ret); |
454 | break; |
455 | } |
456 | |
457 | ret = usb_rawhid_recv(buf, 1000); |
458 | if (ret != 64) |
459 | printf("usb_rawhid_recv/s: %d\n", ret); |
460 | else |
461 | do_usb(buf); |
462 | } |
463 | |
e8e66d02 |
464 | while (g.stream_enable_from && !g.stream_ended |
465 | && get_used_from() >= sizeof(pkt.data) / STREAM_EL_SZ) |
466 | { |
467 | uint32_t f_o; |
468 | int i; |
469 | |
470 | f_o = g.f_o; |
471 | for (i = 0; i < sizeof(pkt.data); i += STREAM_EL_SZ) { |
472 | memcpy(pkt.data + i, &g.stream_from[f_o++], |
473 | STREAM_EL_SZ); |
474 | f_o &= STREAM_BUF_MASK; |
475 | } |
476 | g.f_o = f_o; |
477 | |
478 | pkt.type = PKT_STREAM_DATA_FROM; |
479 | pkt.size = i; |
480 | |
481 | ret = usb_rawhid_send(&pkt, 1000); |
482 | if (ret != sizeof(pkt)) { |
483 | printf("send DATA_FROM: %d\n", ret); |
484 | break; |
485 | } |
486 | } |
487 | |
beaf6d89 |
488 | now = millis(); |
489 | |
490 | // start condition check |
491 | if (now - scheck_time > 1000) { |
f20de073 |
492 | edge_cnt = g.edge_cnt; |
493 | //printf("e: %d th: %d\n", edge_cnt - edge_cnt_last, |
494 | // (GPIOB_PDIR >> CORE_PIN0_BIT) & 1); |
beaf6d89 |
495 | if ((g.stream_enable_to || g.stream_enable_from) |
496 | && !g.stream_started |
497 | && edge_cnt - edge_cnt_last > 10000) |
498 | { |
f20de073 |
499 | do_start_seq(); |
500 | edge_cnt = g.edge_cnt; |
501 | } |
502 | edge_cnt_last = edge_cnt; |
beaf6d89 |
503 | scheck_time = now; |
f20de073 |
504 | } |
505 | |
beaf6d89 |
506 | // led? |
507 | if (CORE_PIN13_PORTREG & CORE_PIN13_BITMASK) { |
508 | if ((int)(now - led_time) > 10) |
509 | CORE_PIN13_PORTCLEAR = CORE_PIN13_BITMASK; |
1cb2822f |
510 | } |
beaf6d89 |
511 | |
512 | // something on rawhid? |
513 | if (usb_rawhid_available() > 0) |
514 | { |
515 | ret = usb_rawhid_recv(buf, 20); |
516 | if (ret == 64) { |
517 | led_time = millis(); |
518 | CORE_PIN13_PORTSET = CORE_PIN13_BITMASK; |
519 | |
520 | do_usb(buf); |
521 | } |
522 | else { |
523 | printf("usb_rawhid_recv: %d\n", ret); |
524 | } |
1cb2822f |
525 | } |
0c1e003e |
526 | } |
1cb2822f |
527 | |
528 | return 0; |
0c1e003e |
529 | } |