/*
 * ----------------------------------------------------------------------------
 * "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.
 */
/*
 * 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

;;; Pull in startup code from libgcc that clears .bss so that
;;; starttime.0 is initialized to 0.
.global __do_clear_bss

.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 */
