;  No Copyright
;
;  The person who associated a work with this deed has dedicated the work to the
;  public domain by waiving all of his or her rights to the work worldwide under
;  copyright law, including all related and neighboring rights, to the extent
;  allowed by law.
;
;  You can copy, modify, distribute and perform the work, even for commercial
;  purposes, all without asking permission.
;
;  See https://creativecommons.org/share-your-work/public-domain/cc0/ for more
;  information on the license.


;  This file is part of the digital chandelier project, made by
;  Maarten Tromp <maarten@geekabit.nl>, 2006.
;
;  More info on the project:
;  https://www.geekabit.nl/projects/digitally-enhanced-chandelier/


; include register defenitions for this proc
.include "2313def.inc"

; register defenitions
	.def b0		= r1	; output0 brightness
	.def b1		= r2
	.def b2		= r3
	.def b3		= r4
	.def b4		= r5
	.def b5		= r6
	.def b6		= r7
	.def b7		= r8	; output7 brightness
	.def bnext	= r10	; next brightness value
	.def timl	= r11	; timer1 readout low
	.def timh	= r12	; timer1 readout high
	.def cmpl	= r13	; timer1 compare low
	.def cmph	= r14	; timer1 compare high
	.def tmp	= r16	; general purposse temp register
	.def tmp2	= r17	;
	.def outmask	= r18	; led output

; static values
	.cseg			; code segment
	.org 0			; code starts at address 0


; interrupt vectors
	rjmp reset		; reset
	rjmp ext_int0		; external int0
	reti	 		; external int1
	reti			; timer1 capture
	rjmp t1_cmp		; timer1 compare match
	reti			; timer1 (16bit) overflow
	reti			; timer0 (8bit) overflow
	rjmp u_rxc		; uart rx complete
	rjmp u_dre		; udr empty
	rjmp u_txc		; uart tx complete
	reti			; analog comparator

reset:
	; set stackpointer
	ldi tmp, RAMEND
	out spl, tmp

	; watchdog
	ldi tmp, 0		; disable
	out WDTCR, tmp
	
	; PORTB
	ser outmask
	out PORTB, outmask	; values
	ser tmp			; all output
	out DDRB, tmp

	; PORTD
	ldi tmp, 0		; no pull-up
	out PORTD, tmp
	clr tmp			; all input
	out DDRD, tmp

	; uart
	ldi tmp, (1<<RXCIE) | (1<<RXEN) | (1<<TXEN)
	out UCR, tmp		; uart control register
	ldi tmp, 0		; 1.8432MHz, 115k2 -> div=0
	out UBRR, tmp		; uart boud rate register
	ldi tmp, 'r'		; send 'r' to indicate that device is reset
	out UDR, tmp

	; timer0
	clr tmp			; timer0 off
	out TCCR0, tmp

	; timer1 will be used to detect the mains frequency and time the phase cut
	clr tmp			; disable timer1 compare output and pwm
	out TCCR1A, tmp
	ldi tmp, (1<<CS10)	; timer1 clock input = system clock
	out TCCR1B, tmp		; timer frequency = 1.8432MHz
				; overflow frequency = 1.8432MHz / 65536 = 28.125Hz
				; which is lower than 100 Hz, so no problem.

	; timer interrupt
	ldi tmp, (1<<OCIE1A)	; enable timer1 compare match interrupt
	out TIMSK, tmp

	; external interrupt
	ldi tmp, (1<<INT0)	; enable external int0
	out GIMSK, tmp
	ldi tmp, (1<<SE) | (1<<ISC01) | (1<<ISC00)	; enable sleep + int0 on rising edge
	out MCUCR, tmp

	; set initial brightness
	ldi tmp, 18
	mov b0, tmp
	mov b1, tmp
	mov b2, tmp
	mov b3, tmp
	mov b4, tmp
	mov b5, tmp
	mov b6, tmp
	mov b7, tmp

	sei			; global interrupt enable


main:
	sleep
	rjmp main


ext_int0:				; external interrupt on mains zero crossing
	; save timer1 values
	in timl, TCNT1L
	in timh, TCNT1H

	; reset timer1 to zero
	clr tmp
	out TCNT1H, tmp
	out TCNT1L, tmp

	; todo: use rotating buffer of 8 samples

	; divide captured timer value for interrupts
	ldi tmp, 5		; divide by 2^5 = 32
divide_loop:
	clc			; clear carry
	ror timh
	ror timl
	dec tmp
	cpi tmp, 0
	brne divide_loop

	; set outputs
	out PORTB, outmask

	; setup timer and portmask
	clr tmp
	mov bnext, tmp
	rcall setup_timer

	; flip rising edge / falling edge interrupt flag
	in tmp, MCUCR
	ldi tmp2, (1<<ISC00)
	eor tmp, tmp2
	out MCUCR, tmp

	reti


t1_cmp:				; timer1 compare match
	; set outputs
	out PORTB, outmask

	; setup timer and portmask
	rcall setup_timer

	reti


u_rxc:				; serial receive interrupt
	push sreg		; save context
	push tmp
	in tmp, uer		; read errors + 9th bit
	in tmp, udr		; read data byte (to clear interrupt)

	; test for '+'
	cpi tmp, '+'
	brne not_plus
	inc b0
	rjmp copy_val
not_plus:
	; test for '-'
	cpi tmp, '-'
	brne not_minus
	dec b0
	rjmp copy_val
not_minus:
	; test for zero
	cpi tmp, '0'
	brlo not_zero
	clr b0
	rjmp copy_val
not_zero:
	; test for 1
	cpi tmp, '1'
	brlo not_one
	ldi tmp2, 32
	mov b0, tmp2
	rjmp copy_val
not_one:
	rjmp u_rxc_end
copy_val:
	mov b1, b0
	mov b2, b0
	mov b3, b0
	mov b4, b0
	mov b5, b0
	mov b6, b0
	mov b7, b0
u_rxc_end:
	pop tmp			; restore context
	pop sreg
	reti


u_dre:				; udr empty
	; ready to send another byte
	reti


u_txc:				; uart tx complete
	reti


setup_timer:
	; find outputs that need to be enabled my next timer match
	ser outmask
	inc bnext
setup_b0:
	cp b0, bnext
	brne setup_b1
	cbr outmask, (1<<PB0)
setup_b1:
	cp b1, bnext
	brne setup_b2
	cbr outmask, (1<<PB1)
setup_b2:
	cp b2, bnext
	brne setup_b3
	cbr outmask, (1<<PB2)
setup_b3:
	cp b3, bnext
	brne setup_b4
	cbr outmask, (1<<PB3)
setup_b4:
	cp b4, bnext
	brne setup_b5
	cbr outmask, (1<<PB4)
setup_b5:
	cp b5, bnext
	brne setup_b6
	cbr outmask, (1<<PB5)
setup_b6:
	cp b6, bnext
	brne setup_b7
	cbr outmask, (1<<PB6)
setup_b7:
	cp b7, bnext
	brne setup_end
	cbr outmask, (1<<PB7)
setup_end:

	; setup timer for this brightness
	clr tmp			; clear compare register
	mov cmph, tmp
	mov cmpl, tmp
	mov tmp, bnext
multiply_loop:
	add cmpl, timl
	adc cmph, timh
	dec tmp
	cpi tmp, 0
	brne multiply_loop

	; set timer1 compare value
	out OCR1AH, cmph
	out OCR1AL, cmpl

	ret


; end of file
