diff options
author | Bdale Garbee <bdale@gag.com> | 2014-06-15 17:47:41 -0600 |
---|---|---|
committer | Bdale Garbee <bdale@gag.com> | 2014-06-15 17:47:41 -0600 |
commit | c5cfc0d6e507d093987741b6ffaf69ebb24caa4b (patch) | |
tree | a75b83343939e96592ac07178f2011d82ea6ded8 /src/attiny/ao_async.c | |
parent | 1b3d07ede530fa40cb7257fb1725c969ba60e0f0 (diff) | |
parent | 9ab3a1de95b705783c31a7e16447f52c10b6b480 (diff) |
Merge branch 'branch-1.4' into debian
Diffstat (limited to 'src/attiny/ao_async.c')
-rw-r--r-- | src/attiny/ao_async.c | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/src/attiny/ao_async.c b/src/attiny/ao_async.c index 3556f54c..f64f7bde 100644 --- a/src/attiny/ao_async.c +++ b/src/attiny/ao_async.c @@ -40,11 +40,98 @@ ao_async_byte(uint8_t byte) { uint8_t b; uint16_t w; + uint8_t v; + uint8_t bit; + uint8_t w_hi, w_lo; /* start data stop */ w = (0x000 << 0) | (byte << 1) | (0x001 << 9); + w_hi = w >> 8; + w_lo = w; + ao_arch_block_interrupts(); + + /* Ok, this is a bit painful. + * We need this loop to be precisely timed, which + * means knowing exactly how many instructions will + * be executed for each bit. It's easy to do that by + * compiling the C code and looking at the output, + * but we need this code to work even if the compiler + * changes. So, just hand-code the whole thing + */ + + asm volatile ( + " ldi %[b], 10\n" // loop value + "loop:\n" + " in %[v], %[port]\n" // read current value + " andi %[v], %[led_mask]\n" // mask to clear LED bit + " mov %[bit], %[w_lo]\n" // get current data byte + " andi %[bit], 0x01\n" // get current data bit +#if AO_LED_SERIAL >= 1 + " add %[bit],%[bit]\n" // shift by one +#else + " nop\n" +#endif +#if AO_LED_SERIAL >= 2 + " add %[bit],%[bit]\n" // shift by one +#else + " nop\n" +#endif +#if AO_LED_SERIAL >= 3 + " add %[bit],%[bit]\n" // shift by one +#else + " nop\n" +#endif +#if AO_LED_SERIAL >= 4 + " add %[bit],%[bit]\n" // shift by one +#else + " nop\n" +#endif +#if AO_LED_SERIAL >= 5 + " add %[bit],%[bit]\n" // shift by one +#else + " nop\n" +#endif +#if AO_LED_SERIAL >= 6 + " add %[bit],%[bit]\n" // shift by one +#else + " nop\n" +#endif +#if AO_LED_SERIAL >= 7 + " add %[bit],%[bit]\n" // shift by one +#else + " nop\n" +#endif + " or %[v], %[bit]\n" // add to register + " out %[port], %[v]\n" // write current value + " lsr %[w_hi]\n" // shift data + " ror %[w_lo]\n" // ... + " nop\n" + " nop\n" + " nop\n" + " nop\n" + " nop\n" + + " nop\n" + " nop\n" + " nop\n" + " subi %[b], 1\n" // decrement bit count + " brne loop\n" // jump back to top + : [v] "=&r" (v), + [bit] "=&r" (bit), + [b] "=&r" (b), + [w_lo] "+r" (w_lo), + [w_hi] "+r" (w_hi) + : [port] "I" (_SFR_IO_ADDR(LED_PORT)), + [led_mask] "M" ((~(1 << AO_LED_SERIAL)) & 0xff) + ); + +#if 0 + /* + * Here's the equivalent C code to document + * what the above assembly code does + */ for (b = 0; b < 10; b++) { uint8_t v = LED_PORT & ~(1 << AO_LED_SERIAL); v |= (w & 1) << AO_LED_SERIAL; @@ -54,6 +141,7 @@ ao_async_byte(uint8_t byte) /* Carefully timed to hit around 9600 baud */ asm volatile ("nop"); asm volatile ("nop"); + asm volatile ("nop"); asm volatile ("nop"); asm volatile ("nop"); @@ -67,5 +155,6 @@ ao_async_byte(uint8_t byte) asm volatile ("nop"); asm volatile ("nop"); } +#endif ao_arch_release_interrupts(); } |