+static __xdata struct ao_debounce ao_quadrature_debounce[AO_QUADRATURE_COUNT];
+
+#define debounce_id(d) ((d) - ao_quadrature_debounce)
+
+__xdata int32_t ao_quadrature_count[AO_QUADRATURE_COUNT];
+
+static uint8_t ao_quadrature_state[AO_QUADRATURE_COUNT];
+
+#define BIT(a,b) ((a) | ((b) << 1))
+#define STATE(old_a, old_b, new_a, new_b) (((BIT(old_a, old_b) << 2) | BIT(new_a, new_b)))
+
+#define port(q) AO_QUADRATURE_ ## q ## _PORT
+#define bita(q) AO_QUADRATURE_ ## q ## _A
+#define bitb(q) AO_QUADRATURE_ ## q ## _B
+#define pina(q) AO_QUADRATURE_ ## q ## _A ## _PIN
+#define pinb(q) AO_QUADRATURE_ ## q ## _B ## _PIN
+
+#define q_case(q) case q: v = (!ao_gpio_get(port(q), bita(q), pina(q))) | ((!ao_gpio_get(port(q), bitb(q), pinb(q))) << 1); break
+
+uint8_t quad_raw[64];
+uint8_t quad_r;
+
+static uint8_t
+_ao_quadrature_get(struct ao_debounce *debounce) {
+ uint8_t q = debounce_id(debounce);
+ uint8_t v = 0;
+
+ switch (q) {
+#if AO_QUADRATURE_COUNT > 0
+ q_case(0);
+#endif
+#if AO_QUADRATURE_COUNT > 1
+ q_case(1);
+#endif
+ }
+ if (q == 0) {
+ quad_raw[quad_r] = v;
+ quad_r = (quad_r + 1) & 63;
+ }
+ return v;
+}