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