isrs.S

/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * Joerg Wunsch wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.        Joerg Wunsch
 * ----------------------------------------------------------------------------
 *
 * Demo combining C and assembly source files.
 *
 * $Id$
 */
/*
 * This file contains the interrupt service routine implementations
 * when compiling the project for the ATtiny13 target.
 */

#include <avr/io.h>

#include "project.h"

#if defined(__AVR_ATtiny13__)

/*
 * Timer 0 hit TOP (0xff), i.e. it turns from up-counting
 * into down-counting direction.
 */
.global TIM0_COMPA_vect
TIM0_COMPA_vect:
        in      sreg_save, _SFR_IO_ADDR(SREG)
        inc     counter_hi
        clr     flags
        out     _SFR_IO_ADDR(SREG), sreg_save
        reti

/*
 * Timer 0 hit BOTTOM (0x00), i.e. it turns from down-counting
 * into up-counting direction.
 */
.global TIM0_OVF_vect
TIM0_OVF_vect:
        in      sreg_save, _SFR_IO_ADDR(SREG)
        inc     counter_hi
        ser     flags
        out     _SFR_IO_ADDR(SREG), sreg_save
        reti

;;; one 16-bit word to store our rising edge's timestamp
.lcomm  starttime.0, 2

.extern pwm_incoming
.extern intbits

.global PCINT0_vect
PCINT0_vect:
        in      sreg_save, _SFR_IO_ADDR(SREG)

        ;; save our working registers
        push    r18
        push    r19
        push    r20
        push    r21

        ;; Now that we are ready to fetch the current
        ;; value of TCNT0, allow interrupts for a
        ;; moment.  As the effect of the SEI will be
        ;; deferred by one instruction, any possible
        ;; rollover of TCNT0 (hitting BOTTOM when
        ;; counting down, or MAX when counting up) will
        ;; allow the above ISRs to trigger right here,
        ;; and update their status, so our combined
        ;; 16-bit time from [counter_hi, TCNT0] will
        ;; be correct.
        sei
        in      r20, _SFR_IO_ADDR(TCNT0)
        cli
        ;; Now, make our working copy of the status,
        ;; so we can re-enable interrupts again.
        mov     r21, counter_hi
        mov     r19, flags
        sei

        ;; what direction were we counting?
        sbrs    r19, 0
        ;; we are down-counting, invert TCNT0
        com     r20
        ;; at this point, r21:20 has our current
        ;; 16-bit time

        ;; now, look which of the edges triggered
        ;; our pin-change interrupt
        sbis    _SFR_IO_ADDR(PINB), 4
        rjmp    10f
        ;; rising edge detected, just record starttime
        sts     (starttime.0) + 1, r21
        sts     starttime.0, r20
        rjmp    99f             ; we are done here

        ;; Falling edge: compute pulse width, store it
        ;; into pwm_incoming, disable pin-change
        ;; interrupt until the upper layers had a chance
        ;; to fetch the result.

10:     in      r18, _SFR_IO_ADDR(GIMSK)
        andi    r18, ~(1 << PCIE)
        out     _SFR_IO_ADDR(GIMSK), r18

        ;; pwm_incoming = current_time - starttime
        lds     r19, (starttime.0) + 1
        lds     r18, starttime.0
        sub     r20, r18
        sbc     r21, r19
        sts     (pwm_incoming) + 1, r21
        sts     pwm_incoming, r20

        ;; signal upper layer
        lds     r18, intbits
        ori     r18, 1
        sts     intbits, r18
99:
        pop     r21
        pop     r20
        pop     r19
        pop     r18

        out     _SFR_IO_ADDR(SREG), sreg_save
        reti
#endif  /* ATtiny13 */