+ GPIOD_PDOR = g.stream_to[g.t_o][th];
+ if (th)
+ do_to_step();
+}
+
+static void portb_isr_do_to(void)
+{
+ uint32_t isfr, th;
+
+ isfr = PORTB_ISFR;
+ PORTB_ISFR = isfr;
+ th = (GPIOB_PDIR >> CORE_PIN0_BIT) & 1;
+
+ GPIOD_PDOR = g.stream_to[g.t_o][th];
+ g.edge_cnt++;
+}
+
+static void portc_isr_nop(void)
+{
+ uint32_t isfr;
+
+ isfr = PORTC_ISFR;
+ PORTC_ISFR = isfr;
+}
+
+// /vsync starts at line 235/259 (ntsc/pal), just as vcounter jumps back
+// we care when it comes out (/vsync goes high) after 3 lines at 238/262
+static void portc_isr_frameinc(void)
+{
+ uint32_t isfr;
+
+ isfr = PORTC_ISFR;
+ PORTC_ISFR = isfr;
+
+ g.t_o = (g.t_o + 1) & STREAM_BUF_MASK;
+ if (g.t_o == g.t_i) {
+ attachInterruptVector(IRQ_PORTB, portb_isr_fixed);
+ attachInterruptVector(IRQ_PORTC, portc_isr_nop);
+ }
+ g.frame_cnt++;
+}
+
+/* "recording" data */
+static noinline void do_from_step(void)
+{
+ uint32_t s;
+
+ // should hopefully give atomic fixed_state read..
+ s = g.fixed_state32;
+ g.fixed_state32 = g.pending_state32;
+ g.stream_from[g.f_i][0] = s;
+ g.stream_from[g.f_i][1] = s >> 8;
+ g.f_i = (g.f_i + 1) & STREAM_BUF_MASK;
+}
+
+static void portb_isr_fixed_do_from(void)
+{
+ uint32_t isfr, th;
+
+ isfr = PORTB_ISFR;
+ PORTB_ISFR = isfr;
+ th = (GPIOB_PDIR >> CORE_PIN0_BIT) & 1;
+
+ GPIOD_PDOR = g.fixed_state[th];
+ if (th)
+ do_from_step();
+ g.edge_cnt++;
+}
+
+static void portc_isr_frameinc_do_from(void)
+{
+ uint32_t isfr;
+
+ isfr = PORTC_ISFR;
+ PORTC_ISFR = isfr;
+
+ do_from_step();
+ g.frame_cnt++;
+}
+
+static void choose_isrs(void)
+{
+ if (g.stream_enable_to) {
+ if (g.use_readinc) {
+ attachInterruptVector(IRQ_PORTB, portb_isr_do_to_inc);
+ attachInterruptVector(IRQ_PORTC, portc_isr_nop);
+ }
+ else {
+ attachInterruptVector(IRQ_PORTB, portb_isr_do_to);
+ attachInterruptVector(IRQ_PORTC, portc_isr_frameinc);