;****** NeoMat 1.2 (c) Oliver Mezger 22.02.2007.

.include "2313def.inc"

.equ papagei = 1

.equ epromVirgin = 99
.equ epromSerLLow = 100
.equ epromSerLHigh = 101
.equ epromSerRLow = 102
.equ epromSerRHigh = 103

;********* Hardware-Verbindungen
;*** PortD
.equ rAb = 6	;Ausgang des AB-Relais
.equ sLi = 5	;Eingang Servo links
.equ sRi = 4	;Eingang Servo rechts
.equ tPr = 3	;Eingang Programmier-Taste
.equ tSp = 2	;Taster Spanisch / +
.equ tEn = 1	;Taster Englisch / -
.equ tDe = 0	;Taster Deutsch  / mode

;*** PortB
.equ lGr = 7	;LED Green / Spanisch
.set lRe = 6	;LED Red / Englisch
.set lYe = 5	;LED Yellow / Deutsch
.equ rSp = 4	;Relais Spanisch
.equ rEn = 3	;Relais Englisch
.equ rDe = 2	;Relais Deutsch
.equ sLo = 1	;Pin von linkem Servo-Ausgang
.equ sRo = 0	;Pin von rechtem Servo-Ausgang

;********* Software 
.equ servosAn = 0		;MerkerBit 0 für Servos

.def merker = R1		;MerkerRegister

.def stat	= R3		;Speichern des Status während Interrupt
.def itempL = R4		;Temoräre Register für Interrupts
.def itempH = R5
.def sLlow = R6			;Linkes Servo Mund ganz zu
.def sLhigh = R7		;Linkes Servo Mund ganz auf
.def sRlow = R8			;Rechtes Servo Mund ganz zu
.def sRhigh = R9		;Rechtes Servo Mund ganz auf
.def temp = R16			;Temp-Register zum Arbeiten
.def temp2 = R17
.def servoL = R18		;Position von ServoL
.def servoR = R19		;Position von ServoR
.def itemp  = R20		;Temoräre Register für Interrupts
.def keystat = R21		;Alle Taster die gedrückt sind
.def keytest = R22		;Test gedrückte Taster
.def keyenter = R23		;Taster, die gerade gedrückt wurden
.def keyexit = R24		;Taster, die gerade losgelassen wurden
.def mode = R25			;Zustand / Betriebsmodus
.def epromAdr = R26
.def epromData = R27
.def blinkdivider = R28
.def blinkcounter = R29

.set cpuclock = 6000				;Frequenz 6 MHz
.set mothercycle = cpuclock *20 / 8	;Ziel: 20 mSec Zyklus bei 1/8 cputakt
.set servoMi = cpuclock/49 			;Mittelstellung der Servos bei ca 0.8 mSec

; ***** Feste Einstellungen fuer Servoendpositonen, bis Reset-Problem geloest ist
.set servoLo = cpuclock/57 			;MundZu
.set servoHi = cpuclock/42 			;MundAuf

.set wait20mc = cpuclock / 50		;Justage der 20msec Zählschleife
.set wait12mc = cpuclock / 80		;Justage der 12msec Zählschleife
.set blinkrate = 20					;20 mSec * blinkrate
.set time10 = 1000 / 2 / blinkrate	;Zeit 10sec mit Blinkzähler
.set time6 = 600 / 2 / blinkrate	;Zeit 6sec mit Blinkzähler
.set time4 = 400 / 2 / blinkrate	;Zeit 4sec mit Blinkzähler
.set time2 = 200 / 2 / blinkrate	;Zeit 2sec mit Blinkzähler

	rjmp init			;Reset
	reti				;IRQ0
	reti				;IRQ1
	reti				;T1 Capture
	rjmp T1_Comp		;T1 Compare
	rjmp T1_Over		;T1 Overflow
	reti				;T0	Overflow
init:
	ldi temp,0xff		
	out DDRB,temp		;PortB als Ausgang
    ldi temp, 0x00
	out PORTB,temp		;PortB auf 0
	
	ldi temp,0b11000000	;PortD7,D6 als Ausgang
	out DDRD,temp		;PortD0..D5 als Eingang
	ldi temp,0b00111111	
	out PORTD,temp		;Alle Pull-Up an

	ldi temp,RAMEND		;Oberste RAM-Adresse holen
	out	SPL, temp		;Stack-Pointer initialisieren
; ***** EEPROM auslesen...
	rjmp no_eprom		; keine EEPROM_Daten verwenden, solange Resetproblem
		
	ldi epromAdr,epromVirgin	;Betrachte das Hymen
	rcall EERead
	cpi epromData,'F'			;noch Jungfrau
	brne no_eprom				;Ja, dann Standartwerte
	ldi epromAdr,epromSerLLow
	rcall EERead				;EEProm auslesen
	mov sLlow,epromData
	mov servoL,epromData		;Mund zu!
    ldi epromAdr,epromSerLHigh
	rcall EERead				;EEProm auslesen
	mov sLhigh,epromData
	ldi epromAdr,epromSerRLow
	rcall EERead
	mov sRlow,epromData
	mov servoR,epromData		;Mund zu!
	ldi epromAdr,epromSerRHigh
	rcall EERead				;EEProm auslesen
	mov sRhigh,epromData
	rjmp timer1_first_set
no_eprom:
	;ldi temp,servoMi			;Standart-Mittelwert
	ldi temp,servoLo			;MundZu Wert
	mov sLlow,temp
	mov sRlow,temp
	ldi temp,servoHi			;MundAuf Wert
	mov sLhigh,temp
	mov sRhigh,temp
	ldi servoL,servoLo
	ldi servoR,servoLo
timer1_first_set:
	ldi temp,High(-mothercycle)
	out TCNT1H,temp		;Timer1 High laden
	ldi temp,Low(-mothercycle)
	out TCNT1L,temp		;Timer1 Low laden
	ldi temp,2
	out TCCR1B,temp		;Timer starten Clock = Systemtakt/8
	ldi temp,0x80		;Timer1 Overflow Interupt Flag
	out TIMSK,temp		;Timer-Interrupt freigeben 
	
	clr mode			;Betriebsmodus 0

	clr merker			;MerkerRegister löschen	
	ldi keystat,7		;Sichere Annahme, alle Münzer verstopft
	sbis PIND,tPr		;Gehe in Tesmode wenn bei Power-On PGRM gedrückt
	rjmp testmode
	sei					;Interrupts global freigeben (I-Bit)
	rjmp main

testmode:				;Einfacher Hardware-Test
	in temp,PIND
	com temp			;Eingang invertieren
	bst temp, tPr		;Prog-Taste merken
	brts progs			;Wenn prog gedrückt
	cbi PORTD,rAb		;Relais AB löschen
	rjmp lbl0
progs:
	sbi PORTD,rAb
lbl0:
	andi temp,0b00000111;Eingangstasten maskieren
	lsl temp
	lsl temp
	mov temp2,temp
	lsl temp
	lsl temp
	lsl temp
	or temp2,temp
	out PORTB,temp2
    sbi PORTB,lGr
	rcall wait20ms
	cbi PORTB,lGr
	rcall wait20ms
	rjmp testmode

T1_Over:
	in stat,SREG				;Statusregister retten
	ldi itemp,High(-mothercycle)
	out TCNT1H,itemp			;Timer1 High laden
	ldi itemp,Low(-mothercycle)
	out TCNT1L,itemp			;Timer1 Low laden
	
	inc blinkdivider	;
	cpi blinkdivider,blinkrate
	brmi t1over1
	clr blinkdivider
	inc blinkcounter
t1over1:
	sbrs merker,servosAn;wenn keine Servos
	rjmp t1overout		;zum Ende springen
	mov itempL,servoL	;Linker Servo
	rcall T1_timeMark	;Ende Marke setzen
	sbi PORTB,sLo	;Linger Servo start
	ldi itemp,0xc0		;Timer1 Overflow Interupt Flag
	out TIMSK,itemp		;Timer-Interrupt freigeben 	

t1overout:	
	out SREG,stat		;Statusregister herstellen
	reti

T1_Comp:
	in stat,SREG		;Statusregister retten
	sbic PORTB,sLo		;Test, ob ServoL set
	rjmp serLfin		;Ja, dann zu ServoL-Finish
	sbic PORTB,sRo	;Test, ob ServoR set
	rjmp serRfin		;Ja, dann zu ServoR-Finish
	rjmp T1_fin			; Hier weitere Servos einfügen
serLfin:
	cbi PORTB,sLo		;Bit ServoL clear
	mov itempL,servoR	;Weiter mit ServoR
	rcall T1_timeMark	;Ende Marke setzen
	sbi PORTB,sRo		;ServoR set
	rjmp T1_fin
serRfin:
	cbi PORTB,sRo		;Bit ServoR clear
	ldi itemp,0x80		;Timer1 Overflow Interupt Flag
	out TIMSK,itemp		;Timer-Interrupt freigeben 
T1_fin:	
	out SREG,stat		;Statusregister herstellen
	reti

T1_timeMark:
	clr itempH			; Löschen
	lsl itempL			;mit 2 multiplizieren
	rol itempH			;Carry in itempH bewegen
	lsl itempL			;mit 2 multiplizieren
	rol itempH			;Carry in itempH bewegen
	lsl itempL			;mit 2 multiplizieren
	rol itempH			;Carry in itempH bewegen
	in itemp,TCNT1L		;Counter1 Low einlesen
	add itempL,itemp	;Addiere zu Counter1
	in itemp,TCNT1H		;Conter1 High einlesen
	adc itempH,itemp	;Addiere zu Counter1
	out OCR1AH,itempH	;ins Compare1 Register schreiben
	out OCR1AL,itempL	;
	ret

main:
	ldi temp,0
	out PORTB,temp		;erst mal alles aus
	cbi PORTD,rAb
	clt					;lösche das T-Flag
	bld merker,servosAn	;Transferiere T-Flag in Merker, d.h. lösche servosAn
main_cycle:
	rcall keycheck
	sbrc keyenter,tPr	;wenn Programmtaste gedrückt
	rjmp progmode_enter	;dann Programmieren
	rcall betriebsblinken
	andi keyenter,0b00000111	;wenn eine Münze
	brne start_game		;fang an
	rjmp main_cycle
start_game:
	ldi temp,0
	out PORTB,temp		;erst mal alles aus
	cbi PORTD,rAb
	clt					;lösche das T-Flag
	bld merker,servosAn	;Transferiere T-Flag in Merker, d.h. lösche servosAn
	clr blinkcounter	;Timer auf Null setzen
chill:
	cpi blinkcounter,time2	;warte bis 2 Sec
	brlo chill				; um sind
	sbrc keyenter,tSp	;wenn Spanisch gedrückt
	sbi PORTB,rSp		;das spanische Relais
	sbrc keyenter,tEn	;wenn Englisch
	sbi PORTB,rEn		;das englische Relais
	sbrc keyenter,tDe	;wenn Deutsch
	sbi PORTB,rDe		;dann Deutsch
.ifdef papagei			;wenn Papagei
	sbi PORTD,rAB		;dann AB-Relais als Summenrelais
.endif
	clr blinkcounter	;Timer auf Null setzen
	sbi PORTB,lYe		;Anzeige Zustand 1
start_wait:
	rcall keycheck
	sbrc keyenter,tPr		;wenn Programmtaste gedrückt
	rjmp progmode_enter		;dann Programmieren
	;rcall betriebsblinken
	andi keyenter,0b00000111;wenn eine Münze
	brne start_game			;fang wieder an
	cpi blinkcounter,time6	;warte bis 4 Sec
	brlo start_wait			;um sind
	cbi PORTB,lYe		;Anzeige Zustand 2
	sbi PORTB,lRe
wait_to10:
	rcall keycheck
	sbrc keyenter,tPr		;wenn Programmtaste gedrückt
	rjmp progmode_enter		;dann Programmieren
	;rcall betriebsblinken
	rcall schwaetzo			;nun mit Servos
	andi keyenter,0b00000111;wenn eine Münze
	brne start_game			;fang wieder an
	cpi blinkcounter,time10	;warte bis 10 Sec
	brlo wait_to10			;um sind
	clr blinkcounter
	sbi PORTB,lYe			;Anzeige Zustand 3
wait_silence:
    sbis PIND,sLi		;wenn linker Eingang = 0,dh.wenn Sound
	clr blinkcounter	;Timer auf Null setzen
	sbis PIND,sRi		;wenn rechter Eingang = 0
	clr blinkcounter	;Timer auf Null setzen
	rcall keycheck
	sbrc keyenter,tPr		;wenn Programmtaste gedrückt
	rjmp progmode_enter		;dann Programmieren
	;rcall betriebsblinken
	cbi PORTB,lGr
	cpi blinkcounter,0		;wenn sound
	brne nosound
	sbi PORTB,lGr			;leuchte
nosound:	
	rcall schwaetzo			;nun mit Servos
	andi keyenter,0b00000111;wenn eine Münze
	brne start_game			;fang wieder an
	cpi blinkcounter,time6	;warte bis 5 Sec
	brlo wait_silence		;um sind
	;*** Nun AB
	clr blinkcounter
	ldi temp,0
	out PORTB,temp		;erst mal alles aus
	clt					;lösche das T-Flag
	bld merker,servosAn	;Transferiere T-Flag in Merker, d.h. lösche servosAn
.ifndef papagei			;wenn normal
	sbi PORTD,rAb		;das AB-Relais setzen
wait_ab:
	rcall keycheck
	sbrc keyenter,tPr		;wenn Programmtaste gedrückt
	rjmp progmode_enter		;dann Programmieren
	cpi blinkcounter,200 / 2 / blinkrate	;warte bis 2 Sec
	brlo wait_ab		;um sind
.endif		

	rjmp main


schwaetzo:				;Steuerung für die Münder
	set
	bld merker,servosAn	;Servos an
	cp sLlow,servoL		;ist Mund zu
	brsh mundL_zu		;ja, dann tue nix
	dec servoL			;Mund ist noch auf
mundL_zu:
	cp sRlow,servoR		;ist Mund zu
	brsh mundR_zu
	dec servoR			;Mund ist noch auf
mundR_zu:
	sbis PIND,sLi		;wenn linker Eingang = 0,dh.wenn Sound
	subi servoL,-5		;Addiere 5 -> Mach Mund auf
	sbis PIND,sRi		;wenn rechter Eingang = 0
	subi servoR,-5		;Addiere 5 -> Mach Mund auf
	cp sLhigh,servoL	;ist Mund ganz auf
	brsh mundL_auf		;nein, dann tue nix
	mov servoL,sLhigh	;nicht zu sehr aufreissen
mundL_auf:
	cp sRhigh,servoR	;ist Mund ganz auf
	brsh mundR_auf		;nein, dann tue nix
	mov servoR,sRhigh	;nicht zu sehr aufreissen
mundR_auf:
	ret
    
betriebsblinken:
	sbrc blinkcounter,1	;wenn Bit 1 gesetzt
	rjmp ma_setled		;setze Leds
	cbi PORTB,lGr		;sonst lösche ledS
	cbi PORTB,lRe		;sonst lösche ledE
	cbi PORTB,lYe		;sonst lösche ledD
	rjmp ma_ledweiter
ma_setled:
	sbrs keystat,tSp	;ist Taste Spain nicht gedrückt
	sbi PORTB,lGr		;LED Spain leuchtet
	sbrs keystat,tEn	;ist Taste Enlish nicht gedrückt
	sbi PORTB,lRe		;LED English leuchtet
	sbrs keystat,tDe	;ist Taste Deutsch nicht gedrückt
	sbi PORTB,lYe		;LED Deutsch leuchtet
ma_ledweiter:
	ret

progmode_enter:
	set					;setze das T-Flag
	bld merker,servosAn	;Transferiere T-Flag in Merker, d.h. setze servoAn
	ldi temp,0
	out PORTB,temp		;Alle Ausgänge Löschen
	cbi PORTD,rAb
	sbi PORTB,lYe
	clr mode			;Betriebsmodus wird nachher 0 sein
	sei 				;Interrupts an
	;**** ServoL-Low
	mov servoL,sLlow	;Progmode1 enter
	mov servoR,sRlow	;Mund zu
progmode1:
    sbrc blinkcounter,1	;wenn Bit 1 gesetzt, langsames Blinken
	rjmp pr_setled1		;setze Led
	cbi PORTB,lGr		;sonst lösche LED
	rjmp pr_ledweiter1
pr_setled1:
	sbi PORTB,lGr
pr_ledweiter1:
	rcall keycheck
	sbrc keyenter,tSp	;Taster Spanisch / +
	inc servoL
	sbrc keyenter,tEn	;Taster Englisch / -
	dec servoL
	sbrs keyenter,tDe	;wenn progmode nicht verlassen
	rjmp progmode1		;weiter verstellen
	mov sLlow,servoL
		;**** ServoL-High
	cp	servoL,sLhigh	;steht Servo schon über dem Maximum
	brsh progmode2		;tue nichts
	mov servoL,sLhigh	;lade Maximum
progmode2:
    sbrc blinkcounter,0	;wenn Bit 0 gesetzt, schnelleres Blinken
	rjmp pr_setled2		;setze Led
	cbi PORTB,lGr		;sonst lösche LED
	rjmp pr_ledweiter2
pr_setled2:
	sbi PORTB,lGr
pr_ledweiter2:
	rcall keycheck
	sbrc keyenter,tSp	;Taster Spanisch / +
	inc servoL
	sbrc keyenter,tEn	;Taster Englisch / -
	dec servoL
	sbrs keyenter,tDe	;wenn progmode nicht verlassen
	rjmp progmode2		;weiter verstellen
	mov sLhigh,servoL
	;**** ServoR-Low
	mov servoR,sRlow	;Progmode3 enter
	cbi PORTB,lGr
progmode3:
    sbrc blinkcounter,1	;wenn Bit 1 gesetzt, langsames Blinken
	rjmp pr_setled3		;setze Led
	cbi PORTB,lRe		;sonst lösche LED
	rjmp pr_ledweiter3
pr_setled3:
	sbi PORTB,lRe
pr_ledweiter3:
	rcall keycheck
	sbrc keyenter,tSp	;Taster Spanisch / +
	inc servoR
	sbrc keyenter,tEn	;Taster Englisch / -
	dec servoR
	sbrs keyenter,tDe	;wenn progmode nicht verlassen
	rjmp progmode3		;weiter verstellen
	mov sRlow,servoR
		;**** ServoR-High
	cp	servoR,sRhigh	;steht Servo schon über dem Maximum
	brsh progmode4		;tue nichts
	mov servoR,sRhigh	;lade Maximum
progmode4:
    sbrc blinkcounter,0	;wenn Bit 0 gesetzt, schnelleres Blinken
	rjmp pr_setled4		;setze Led
	cbi PORTB,lRe		;sonst lösche LED
	rjmp pr_ledweiter4
pr_setled4:
	sbi PORTB,lRe
pr_ledweiter4:
	rcall keycheck
	sbrc keyenter,tSp	;Taster Spanisch / +
	inc servoR
	sbrc keyenter,tEn	;Taster Englisch / -
	dec servoR
	sbrs keyenter,tDe	;wenn progmode nicht verlassen
	rjmp progmode4		;weiter verstellen
	mov sRhigh,servoR
		;**** progmode_exit:
	clt					;lösche das T-Flag
	bld merker,servosAn	;Transferiere T-Flag in Merker, d.h. lösche servosAn
	ldi temp,0
	out PORTB,temp		;Ausgänge aus
	cli					;sperre Interrupts
wait_to_prog:
	sbi PORTB,lYe		;gelbe LED an
	rcall wait20ms
	rcall wait20ms
	rcall wait20ms
	cbi PORTB,lYe
	rcall wait20ms
	rcall wait20ms
	rcall wait20ms
	rcall keycheck
	sbrc keyenter,tDe	;wenn Mode-Taste gedrückt
	rjmp progmode_enter	;noch mal überprüfen
	sbrs keyenter,tPr	;wenn Programmiertaste nicht gedrückt
	rjmp wait_to_prog	;aufs Programmieren warten
	ldi epromAdr,epromSerLLow
    mov epromData,sLlow
	rcall EEWrite		;EEProm schreiben
	ldi epromAdr,epromSerLHigh
    mov epromData,sLhigh
	rcall EEWrite		;EEProm schreiben
	ldi epromAdr,epromSerRLow
    mov epromData,sRlow
	rcall EEWrite		;EEProm schreiben
	ldi epromAdr,epromSerRHigh
    mov epromData,sRhigh
	rcall EEWrite		;EEProm schreiben
	ldi epromAdr,epromVirgin
    ldi epromData,'F'
	rcall EEWrite		;EEProm schreiben
	sei					;Interrupts wieder frei geben
	rjmp main

keycheck:
	ldi keyenter,0
	ldi keyexit,0
	in keytest,PIND				;Lade PD
	com keytest					;invertieren
	andi keytest,0b00001111		;Taster Maskieren
	cpse keystat,keytest		;Test ob Veränderung
	rjmp keynew					;Veränderung
	ret							;alles gleich
keynew:
	rcall wait12ms				;etwas warten
	in temp,PIND				;nochmal einlesen
	com temp					;invertieren
	andi temp,0b00001111		;maskieren
	cpse keytest,temp			;ist es stabil
	ret							;war nix, da nicht stabil
	mov keyenter,keystat
	com keyenter				;invertieren
	and keyenter,keytest		;steigende Flanken, !alt & neu
	mov keyexit,keytest
	com keyexit					;invertieren
	and keyexit,keystat			;fallende Flanken, alt & !neu
	mov keystat,keytest			;alt := neu
	ret

wait20ms:
	ldi temp,wait20mc	;äusseren Zähler laden: 80 bei 4MHz
wl1:
	ldi temp2,250		;inneren Zähler laden
wl2:
	nop
	dec temp2			;temp2--
	brne wl2			;Sprung wenn nicht null
	dec temp			;temp--
	brne wl1			;Sprung wenn nicht null
	ret

wait12ms:
	ldi temp,wait12mc	;äusseren Zähler laden: 50 bei 4MHz
wl1b:
	ldi temp2,250		;inneren Zähler laden
wl2b:
	nop
	dec temp2			;temp2--
	brne wl2b			;Sprung wenn nicht null
	dec temp			;temp--
	brne wl1b			;Sprung wenn nicht null
	ret


EEWrite:
	sbic EECR,EEWE		;falls EEWE nicht log.0
	rjmp EEWrite		;  weiter warten
	out EEAR,epromAdr	;Adresse festlegen
	out EEDR,epromData	;Daten festlegen
	sbi EECR,EEMWE		;EEMWE-Bit gibt Programmierung frei
	sbi EECR,EEWE		;EEWE-Bit startet Programmierung
	ret

EERead:
	sbic EECR,EEWE		;falls EEWE nicht log. 0
	rjmp EERead			;  weiter warten
	out EEAR,epromAdr	;Adresse festlegen
	sbi EECR,EERE		;EERE-Bit setzen: Lesevorgang starten
	in epromData,EEDR	;Datenbyte einlesen
	ret


