X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=blobdiff_plain;f=src%2Fattiny%2Fao_async.c;h=f7a29d3b799f88e0ab71dd2e92f4db3f167d4e30;hp=3556f54c00d511932baabad43a3d358311233d0f;hb=0448b9b638f8599633227a639ef9d8572780bbd9;hpb=690094e2d7d9cfe5eb4edb478fd79e5d133c6b4b diff --git a/src/attiny/ao_async.c b/src/attiny/ao_async.c index 3556f54c..f7a29d3b 100644 --- a/src/attiny/ao_async.c +++ b/src/attiny/ao_async.c @@ -3,7 +3,8 @@ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of @@ -40,11 +41,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 +142,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 +156,6 @@ ao_async_byte(uint8_t byte) asm volatile ("nop"); asm volatile ("nop"); } +#endif ao_arch_release_interrupts(); }