;**** A P P L I C A T I O N   N O T E   LCD-Anzeige  ************************
;*
;* Title:		Ansteuerung 16*4 LCD über Ser.Schnittstelle
;*                      Display Modell 2
;* Version:		0.6
;* Last updated:	21.1.2001
;* Target:		AT90S2333
;*
;* Author:              Majunke Michael
;* Support E-mail:	majunke@t-online.de
;*
;* Beschreibung:
;* mit diesem Programm kann ein 16*4 LCD-Display angesteuert werden..   
;* die Zeichen die an die Ser. Schnittstelle gesendet werden, gibt das Disp.
;* aus und reagiert auf Befehle.. s. Anlage..
;* weiterhin kann das Display Daten an den Rechner senden..
;* Einstellungen:
;*	- 38400 Baud auf der ser. Seite..
;*	- Version mit XON/XOFF Protokoll und CTS
;* 
;* Anmerk.: Empfangspufferfehler behoben...
;*
;* geplant:
;* Menu für Standarteinstellungen wie Baudrate u.a 
;* AD-WANDLER
;* 
;* GPL
;*****************************************************************************

;*** Portbelegungen **********************************************************
;* Port	  -	Aufgabe	   -	Pins     - 	Richtung
;*
;* PD0 		RxD		Pin 2		IN
;* PD1		TxD		Pin 3		OUT	
;* PD2 		CTS		Pin 4		OUT
;* PD3 		LED 1(3)	Pin 5		OUT
;* PD4 		LED 2(2)	Pin 6		OUT
;* PD5 		LED 3(1)	Pin 11		OUT
;* PD6 		Taster 1	Pin 12		IN
;* PD7 		Taster 2	Pin 13		IN
;*
;* PC0		Display E	Pin 23		OUT
;* PC1		Display Bel.	Pin 24		OUT
;* PC2          Taster 3	Pin 25		IN
;* PC3          Helligkeitssens Pin 26		IN
;* PC4		Taster 4	Pin 27		IN
;* PC5          AD-Eingang	Pin 28		IN          
;*
;* PB0		Display D0	Pin 14		OUT/IN
;* PB1		Display D1	Pin 15		OUT/IN
;* PB2		Display D2	Pin 16		OUT/IN
;* PB3		Display D3	Pin 17		OUT/IN
;* PB4		Display R/S	Pin 18		OUT
;* PB5		Display R/W	Pin 19		OUT
;* 
;******************************************************************************

.DEVICE	AT90S2333

; ***** Portadressen
.EQU    PIND       = 0x10
.EQU	PORTD_DDR  = 0x11
.EQU	PORTD_D    = 0x12

.EQU    PINC       = 0x13
.EQU	PORTC_DDR  = 0x14
.EQU	PORTC_D    = 0x15

.EQU    PINB       = 0x16
.EQU	PORTB_DDR  = 0x17
.EQU	PORTB_D    = 0x18

; ***** I/O-Memory
.EQU	RAMEND		= 0xDF		; Adresse SRAM-End
.EQU    STATUS_FLAG     = 0x3F		; Status Register
.EQU	SP		= 0x3D		; Stackpointer 

.EQU    EEADRESS	= 0x1E		; EEPROM adressregister
.EQU	EEDAT		= 0x1D		; 	 Datenregister
.EQU	EECONTR		= 0x1C		;        controlregister

.EQU	UART_UDR   	= 0x0C		; UART   Datenregister
.EQU    UART_UCSRA 	= 0x0B		;	 Contr./Statusreg.A
.EQU	UART_UCSRB   	= 0x0A		;	 Contr./Statusreg.B
.EQU	UART_UBRR_L  	= 0x09		;	 Baudratenreg. Low
.EQU	UART_UBRR_H	= 0x03		;	 Baudratenreg. High

.EQU	ADMUX		= 0x07		; Kanalselectregister f. AD-Wandler
.EQU	ADCSR		= 0x06		; AD-Controllregister
.EQU	ADCH		= 0x05		; AD-Data High
.EQU	ADCL		= 0x04		; AD-Data Low

.EQU	Sensor_Kanal_Hellig = 3		; auf welchen Kanal liegt Helligkeitssensor
;* später fest eingeben da ja nicht mehr verändert wird

.EQU	TIMSK		= 0x39		; Timer/Counter Interupt Mask Register
.EQU	TIFR		= 0x38		; Timer/Counter Interupt Flag Register
.EQU	TCCR1A		= 0x2F		; Timer/Counter1 Controll Register A  16bit
.EQU	TCCR1B		= 0x2E		; Timer/Counter1 Controll Register B
.EQU	TCCR0		= 0x33		; Timer/Counter 0 Controll Register 8bit


; ***** Variablen
.EQU	UART_TEILER	= 12		; Teiler nach Datenblatt einstellen, 
					; 12 für 38400Baud
					; 51 für 9600 
				        ; Teiler=(Mhz/Baudrate/16)-1
		; später evt. ins Flash damit über SETUP änderbar
		
.EQU	UART_CR		= 0b10011000	; UCSRB, IRQ erlaubt, Empfang/Senden erlaubt
.EQU	UART_CR_OFF   	= 0b00011000	; int off

.EQU	PUFFER_LAENGE_GESAMT  = 22	; Puffer auf 22 Zeichen 
.EQU	PUFFER_LAENGE_RESERVE = 2	; ReserveZeichen falls CTS zu lang braucht 2

.EQU	ASC_UG	= 32	; innerhalb der ASC-Spanne werden Zeichen empfangen und gepuffert 
.EQU	ASC_OG	= 126   ; von SPACE(32) bis ~(126)
.EQU	ASC_CODE = 0x7E	; 126d '~' Steuerzeichen, wird auch gepuffert 
			; -> gehe auf CODE-EMPFANG und werte nachfolg. Zeichen aus
.EQU    ASC_CODE_Clear  = 0x43		 ; 67d 'C' - für display clear 
.EQU    ASC_CODE_LED    = 0x4C  	 ; 76d 'L' - LED Steuern 'L' an/aus/blinken
.EQU	ASC_CODE_BEL    = 0x42		 ; 66d 'B' - Beleuchtung an/aus/automatik
.EQU	ASC_CODE_LOCATE = 0x50  	 ; 80d 'P' - Cursor Position Z_S

.EQU	DDR_READ_DISPLAY   = 0b00110000  ; Datenrichtung vordef. zum Einlesen v Display
.EQU    DDR_WRITE_DISPLAY  = 0b00111111  ;                       zum Ausgeben auf Display
.EQU    DISPLAY_FUNK_SET   = 0b00101000	 ; für init_Display_ hier kann Font,
.EQU    DISPLAY_DISP       = 0b00001100  ; Cusour usw. geändert werden, s.Datenblatt 
.EQU 	DISPLAY_CLEAR      = 0b00000001  ; Befehle f. Display ..
.EQU 	DISPLAY_EMODE      = 0b00000110
.EQU	DISPLAY_DDRAM_NULL = 0b10000000

.EQU	Timer1_Set	   = 0b10000000  ; Timer1 IRQ an
;.EQU	Timer_Clock_1	   = 0b00000100  ; Timerfrequenz einstellen hier CK/256
.EQU	Timer_Clock_1	   = 0b00000011  ; Timerfrequenz einstellen hier CK/64

.EQU	Timer0_Set	   = 0b00000010  ; Timer0 IRQ an
;.EQU	Timer_Clock_0	   = 0b00000100  ; Timerfrequenz einstellen hier CK/256
.EQU	Timer_Clock_0	   = 0b00000101  ; Timerfrequenz einstellen hier CK/1024

.EQU Timer_Aktiv_Byte_Def  = 0b00000111  ; Voreinstellung welche Timer aktiv sein sollen
			  		; Bit 0 = Beleuchtung
			  		; Bit 1 = Taster
			  		; Bit 2 = LED (blinken)
					; Bit 3/4/5 = welche LED soll blinken - FEST !
					; Bit 6 für AD-Wandler -> später
					; Bit 8 frei
				; später evt. ins Flash damit über SETUP änderbar

.EQU Timer_T_Counter_Time   = 8	; Nutzung zum entprellen der Taster

.EQU Timer_T2_Counter_Time   = 8	; durch veringern bzw. erhöhen an eigene Taster anpassen
.EQU Timer_T3_Counter_Time   = 8      
.EQU Timer_T4_Counter_Time   = 8        ; !!!!!!! nicht über 15 !!!!!!!!!!!!!!
		; evt. nur Eines für Alle  (falls gleiche 4 Taster)

; ***** Register

.DEF	SAVE_STATUS_FLAG = r1	; wenn INT ausgeführt, hier das StatusRegister sichern
.DEF	PAUSE_LANG	 = r2	; für Lange Pause

.DEF	PUFFER_POS	 = r3	; Position d. Pufferzeigers
.DEF	PUFFER_AKT_LAENG = r4	; akt. anzahl v. Zeichen im Puffer (wenn Null dann Leer)
.DEF	C_MERK		 = r5	; Cursor Position merken

.DEF    TASTER_PRESSED   = r6   ; welcher taster wurde gedrückt ? TimerIRQ speichert 
				; gedrückten Taster in diesem Register
				
.DEF	Timer_T1_Counter = r7   ; Nutzung zum entprellen der Taster
.DEF    Timer_T2_Counter = r8   ; 
.DEF	Timer_T3_Counter = r9   ; 
.DEF    Timer_T4_Counter = r10  ; Erkl.  bei Variablen   Timer_Tx_Counter_Time
	; !! später die Register aufteilen da nur ein halbes benötigt wird !!

.DEF	TIMER_AKTIV_BYTE = r11	; nicht den kompletten Teiler lahm legen, 
				; sondern abspeichern für was (Hellig, LED usw. )
				; dieser aktiv sein soll und dann in der Timer-Routine
				; überspringen
				; Bit 0 = Beleuchtung
				; Bit 1 = Taster
				; Bit 2 = LED (blinken)
				; Bit 3/4/5 = welche LED soll blinken - FEST !
				; Bit 6 für AD-Wandler -> später
				; Bit 8 frei
				; s.o.
				
.DEF    WORK		 = r16	; allgem. verwend.
.DEF	WORK2		 = r17	
.DEF    WORK_UART	 = r18
.DEF	TEMP		 = r19

.DEF    TEMP_DISP	 = r20  ; temp f. untpr. DATEN an DISP
.DEF    TEMP_DISP_DS	 = r21  ; temp f. untpr. DATEN an DISP
.DEF	DISPLAY_DAT	 = r22	; übergabe eines Bytes an Disp_Sendxxx
				; und 2. f. LocateZ_S
.DEF    PAUSEWERT	 = r23	; die Länge der Pause
.DEF    PAUSE1    	 = r24	; f. Schleife in PauseRoutine

.DEF	CODE_MERK	 = r25	; für Code-Zeichen ermitteln, zw-Speicher

; SRAM -Register
.DEF	X_L		 = r26  ; AD   -  Hauptanwendungszweck
.DEF	X_H		 = r27
.DEF	Y_L		 = r28	; UART_RX_C,
.DEF	Y_H		 = r29	;		
.DEF	Z_L		 = r30	; READ_PUFFER,
.DEF	Z_H		 = r31	; 		


; ******************************************************************************
; **** daten die ins SRAM kommen (der EmpfangsPuffer)
.DSEG
PUFFER:			.byte	PUFFER_LAENGE_GESAMT

; ******************************************************************************
; **** daten die ins EEPROM kommen (Meldung u.a )
.ESEG
;                               '11111111111111113333333333333333'
Intro_Meldung:		.DB	"..16x4 Display..Majunke..02/2001"

; ******************************************************************************
;*** Sprunktabelle für Interrups ***
.CSEG
.ORG	$0
Reset:			rjmp	Init_Reset	; Reset behandlung, Neuinitaliesieren
IRQ0:			reti			;
IRQ1:			reti			;
TIMER1_CAP:		reti			;
TIMER1_COMP:		reti			;
TIMER1_OVFL:		rjmp	TIMER1_OL	; Timer 16bit, Helligsteuerung, LED blinken
TIMER0_OVFL:		rjmp    TIMER0_OL	; Timer 8bit zur Tasterabfrage
SPI_TRANSF_COMPL:	reti			;
UART_RX_COMPL:		rjmp	UART_RX_C	; ein zeichen wurde empfangen
UART_DR_EMPTY:		reti			;
UART_TX_COMPL:		reti			;
ADC_CONV_COMPL:		rjmp  	ADC_CONV_C	; eine AD-wandlung ist fertig
EE_RDY:			reti			;
ANA_COMP:		reti			;



;****** Routine nach Reset bzw. Neustart - Vorinitalisieren
Init_Reset:
	cli				; Interupts aus
	;*** Initialize stack pointer
	;* StackPointer auf höchste Adresse im SRAM legen
	ldi	WORK, RAMEND	 	; SP auf RAM_END_Adresse 
	out	SP, WORK		; und setzen
	;*** I/O-Pins in Schaltung auf defin. Werte einstellen
	; Port D 
	ldi	WORK,0b00111110  	; Rxd/TxD/cts/Led1-3/taster1-2 Richtung s. tabelle 
	out	PORTD_DDR,WORK		; AusgabeRichtung - DataDIR Port D
	ldi	WORK,0b11111000  	; LED/CTS aus .. Taster pull-up
	out	PORTD_D,WORK		; Daten - Data Port D
	; Port C
	ldi	WORK,0b00000011	 	; E/Bel./taster3/helligkeit/taster4/ad Richtung
	out	PORTC_DDR,WORK		; Ausgaberichtung - DataDIR Port C
	ldi	WORK,0b00010101		; Bel. aus / E high
	out	PORTC_D,WORK		; Daten - DataDIR Port C
	; Port B
	ldi	WORK,DDR_READ_DISPLAY  	; Datenricht. zum lesen LESEN v. Display
	out	PORTB_DDR,WORK		; Ausgaberichtung - DataDIR Port B
	ldi	WORK,0b111111	  	;
	out	PORTB_D,WORK		; Daten - Data Port B	
	; UART - Einstellen
	ldi	WORK,UART_TEILER	; Baudratenteiler
	out	UART_UBRR_L,WORK	; low-teil
	clr	WORK
	out	UART_UBRR_H,WORK	; h-teil
	ldi	WORK,UART_CR		; Betriebsart des UART
	out	UART_UCSRB,WORK
	; Puffer Variablen setzen
	ldi	WORK, PUFFER 		
	mov	PUFFER_POS, WORK	; Adresse v. Puffer in Reg. 
	clr	PUFFER_AKT_LAENG	; akt. Größe (zeichenanzahl) löschen
	clr	C_MERK			; Akt. CursorPosition löschen
	
	; Taster und Timerpuffer
	clr 	TASTER_PRESSED          ; kein Taster gedrückt

	; Tasterentprellzeiten einstellen
	ldi	WORK, Timer_T_Counter_Time
	mov	Timer_T1_Counter, WORK
	mov	Timer_T2_Counter, WORK
	mov	Timer_T3_Counter, WORK
	mov	Timer_T4_Counter, WORK
		
	; Timer1 Standart ANSCHALTEN
	clr	WORK			; Reg 1A einstellen  betriebsart 16bit Counter 
	out	TCCR1A, WORK		; 
	ldi	WORK,Timer_Clock_1	; Reg 1B einstellen  Frequenz = Einstellung oben
	out	TCCR1B, WORK
	ldi	WORK, Timer1_Set	; Timer1 OverflowInterupt AN
	out	TIMSK, WORK
	
	; Timer0 Standart ANSCHALTEN
	ldi	WORK,Timer_Clock_0	; TCCR0 einstellen  Frequenz = s. Einstellung oben
	out	TCCR0, WORK
	in	WORK, TIMSK		; Timer 0 AN
 	ori	WORK, Timer0_Set
	out	TIMSK, WORK

	; Welche Routinen sollen in den Timern durchlaufen werden
	ldi	WORK, Timer_Aktiv_Byte_Def	; timer start init
	mov	TIMER_AKTIV_BYTE, WORK		
		
	
; **** Main ********************************************************************
; ****                                                                     *****
Start:
	rcall	Init_Display		;*** Initalisieren des Display
	rcall	Intro			;*** Intro anzeigen
	sei				; schalte IRQs zu
			
	ldi 	WORK,17			; sende ein XON (asc17) - Display bereit zum 
    	out 	UART_UDR,WORK		; Zeichenempfang
	 	
  Start_SerIn:				;*** starte Hauptschleife
	
	rcall   Taster_Abfrage		; Abfrage nach gedrückten Tasten
		
	tst	PUFFER_AKT_LAENG	; ist ein Zeichen im Puffer ?
	breq	Start_SerIn		; ja 
	rcall	Read_Puffer		; dann lese das Zeichen aus Puffer
   Code_Empfang:			; und Werte es aus ...
	cpi	DISPLAY_DAT,ASC_CODE	; ist es das Code-Einleitungs-Zeichen ?
	breq	Code_Locate_Cursor	; ja, dann CODE-Behandlung
	rjmp 	Main_Send_Data_MIT_LOC	; sonst ausgeben ALS NORMALER TEXT
	; *** Befehl Locate Z_S ****** 
        Code_Locate_Cursor:
         tst	PUFFER_AKT_LAENG	; ist ein Zeichen im Puffer ? Befehlszeichen holen
	 breq	Code_Locate_Cursor	; nein ? dann warte -> sonst auswerten
	 rcall	Read_Puffer		; *****************	
         clr	CODE_MERK
	 cpi	DISPLAY_DAT,ASC_CODE_LOCATE ; ist es das Code-Zeichen f. Locate ?
	 brne	Code_Clear_Display	; nein, -> test next  ->else Code
	 ; Eigentliche Code für Locate Z_S
	 Code_Locate_Z:
	 tst	PUFFER_AKT_LAENG	; ist ein Zeichen im Puffer ? Zeile
	 breq	Code_Locate_Z		; nein ? dann warte
	 rcall	Read_Puffer		; sonst lesen und entsprechend auswerten
	 subi	DISPLAY_DAT,48		; aus ASC Zahl machen - ZEILE
	 cpi	DISPLAY_DAT,4		; über 3 ??
	 brlo	Code_Locate_W1		; außerhalb dann ABBRUCH,keine ZEICHENAUSGABE
	 rjmp	End_SerIn		;           (rjmp weil sprungweite zu kurz)
	 Code_Locate_W1:
	 swap	DISPLAY_DAT             ; Zeile Zwischenspeichern und weier Spalte ermitteln
	 mov	TEMP, DISPLAY_DAT	; Zeile an richtige Position f. AusgabeFunktion 
	 Code_Locate_S:			; 2 Zahlen müssen eingelesen werden für Spalte !!
	 tst	PUFFER_AKT_LAENG	; ist ein Zeichen im Puffer ?
	 breq	Code_Locate_S		; nein ? dann warte
	 rcall	Read_Puffer		; sonst lesen und entsprechend auswerten
	 subi	DISPLAY_DAT,48		; aus ASC Zahl machen - SPALTE*(10hoch1)
	 cpi	DISPLAY_DAT,2		; über 1 ?? Spalte bis max ->16 Zeichen
	 brlo	Code_Locate_W2		; außerhalb dann ABBRUCH,keine ZEICHENAUSGABE
	 rjmp	End_SerIn		;            ( rjmp weil sprungweite zu kurz)
	 Code_Locate_W2:
	 tst	DISPLAY_DAT
	 breq	Code_Locate_S2		; Null dann direkt 2. Wert lesen, 
	 ori	CODE_MERK, 10		; sonst 10hoch1 dazu
 	 Code_Locate_S2:
 	 tst	PUFFER_AKT_LAENG	; und 10hoch0 lesen
	 breq	Code_Locate_S2		; nein ? dann warte
	 rcall	Read_Puffer		; sonst lesen und entsprechend auswerten
	 subi	DISPLAY_DAT,48		; aus ASC Zahl machen - SPALTE*(10hoch0)
	 cpi	DISPLAY_DAT,10		; über 0-15 ?  F
	 brlo	Code_Locate_W3		; außerhalb dann ABBRUCH,keine ZEICHENAUSGABE
	 rjmp	End_SerIn		;             (rjmp weil sprungweite zu kurz)
	 Code_Locate_W3:
	 add	DISPLAY_DAT, CODE_MERK
	 or	DISPLAY_DAT, TEMP	; und Zeile zu SPlate in ein Byte f. Funktion
	 rcall	locateZ_S	 	; LOCATE Z_S
	 rjmp	End_SerIn		; und end
	; *** Befehl CLS ************
	Code_Clear_Display:
	 cpi	DISPLAY_DAT,ASC_CODE_CLEAR ; ist es das Code-Zeichen für CLEAR_DISPLAY
	 brne	Code_LED_Steuer 	; nein, dann auf nächsten Befehlswort testen
	 ldi	DISPLAY_DAT,DISPLAY_CLEAR  ; sonst Clear Display	
	 rcall	Disp_Send_Code
	 clr	DISPLAY_DAT		; und auf position eins
	 rcall	locateZ_S
	 rjmp	End_SerIn		; und ENDE ohne Zeichenausgabe	
	; *** Befehl um LEDs zu steuern *********** 
	; fehlt noch blinken .....
	Code_LED_Steuer:
	 cpi	DISPLAY_DAT,ASC_CODE_LED ; ist es das Code-Zeichen für LED ON/OFF
	 brne	Code_Beleucht_Display	; nein, dann test auf nächsten Befehl
	 Code_LED_Nr:			; erwarte welche LED verändert werden soll  
	 tst	PUFFER_AKT_LAENG	; ist ein Zeichen im Puffer ?
	 breq	Code_LED_Nr		; nein ? dann warte
	 rcall	Read_Puffer		; sonst lesen und entsprechend auswerten
	 subi	DISPLAY_DAT,48		; aus ASC Zahl machen
	 cpi	DISPLAY_DAT,3		; welche LED ? 0-2
	 brlo	Code_LED_w		; außerhalb der LEDs dann ABBRUCH,keine ZEICHENAUSGABE
	 rjmp	End_SerIn		;               (rjmp weil sprunglänge zu kurz)
	 Code_LED_w:
	 mov	CODE_MERK, DISPLAY_DAT  ; Merke die Nummer und weiter zu -> teste ob LED an/AUS
	 ldi	TEMP,8			; schiebe die LED-Nummer auf richtigen Port
         two_h:				; 2hochLED-Nummer
	 tst	CODE_MERK
	 breq	n_two_h
	 lsl	TEMP
	 dec	CODE_MERK
	 rjmp	two_h
	 n_two_h:
	 mov	CODE_MERK,TEMP
	 Code_LED_Zustand:		; und soll LED AN o. AUS sein
	 tst	PUFFER_AKT_LAENG	; ist ein Zeichen im Puffer ?
	 breq	Code_LED_Zustand	; nein ? dann warte
	 rcall	Read_Puffer		; sonst lesen und entsprechend auswerten
	 cpi	DISPLAY_DAT, 48		; Null '0' ? dann weiter ob AN oder blinken
	 brne	Code_LED_Nr_ANorBlink
	 in	DISPLAY_DAT, PORTD_D	; Zustand ? CTS usw nicht verändern 
	 or	DISPLAY_DAT,CODE_MERK	; jew. bit setzen
	 out	PORTD_D,DISPLAY_DAT
	 com	TEMP			; entpsr. bit in Timer-REgister löschen
	 and	TIMER_AKTIV_BYTE, TEMP	; damit led blinken ausgeschalten wird
	 rjmp	End_SerIn	
	 Code_LED_Nr_ANorBlink:
  	 cpi	DISPLAY_DAT, 49		; '1' ? dann LED AN sonst blinken setzen
  	 brne	Code_LED_Nr_Blink
	 in	DISPLAY_DAT, PORTD_D	; Zustand ? CTS usw nicht verändern
	 com 	CODE_MERK		; Complement (neg.)
	 and 	DISPLAY_DAT,CODE_MERK	; UND                jew. bit löschen
	 out	PORTD_D,DISPLAY_DAT
	 com	TEMP			; entpsr. bit in Timer-REgister löschen
	 and	TIMER_AKTIV_BYTE, TEMP	; damit led blinken ausgeschalten wird
	 rjmp	End_SerIn		; end
	 Code_LED_Nr_Blink:
	 cpi	DISPLAY_DAT, 50		; '2' ? dann LED Blinken sonst ende
	 brne	End_SerIn 
	 or	TIMER_AKTIV_BYTE, TEMP	; entpsr. bit in Timer-REgister setzen damit LED blinkt
	 rjmp	End_SerIn		; abfrage in Timer0 IRQ
	 ; *** Befehl für Beleuchtungssteuerung *****
	 ; *** manuelle Steuerung(~B0/1) oder Automatik nach LDR (~B2)
	Code_Beleucht_Display: 
	 cpi	DISPLAY_DAT,ASC_CODE_BEL ; ist es das Code-Zeichen f. Beleuchtung
	 brne	Main_Send_Data_MIT_LOC	; nein, dann end
	 Code_Beleucht_Z:		; hole den Zustand den Beleuchtung bekommen soll
	 tst	PUFFER_AKT_LAENG	; ist ein Zeichen im Puffer ?
	 breq	Code_Beleucht_Z		; nein ? dann warte
	 rcall	Read_Puffer		; sonst lesen und entsprechend auswerten
	 cpi	DISPLAY_DAT,48 		; ist es  '0'
	 brne 	Code_Beleucht_ONorAUTO	; nein, dann Beleuchtung AN oder AUTOmatik
 	 mov	WORK , TIMER_AKTIV_BYTE	 ; Timerfunktion übergehen ohne Timer abzuschalten
 	 andi	WORK, 0b11111110	 ; Bit 1 ausschalten (Beleuchtung)
 	 mov	TIMER_AKTIV_BYTE ,  WORK	
	 cbi	PORTC_D,1		; und Beleuchtung AUS
	 rjmp	End_SerIn		; und ENDE ohne Zeichenausgabe	
	 Code_Beleucht_ONorAUTO:	; Beleuchtung An oder Automatik
 	 cpi    DISPLAY_DAT,49		; ist es '1' für Beleuchtung AN oder höher f. AUTOmatik ?
 	 brne   Code_Beleucht_AUTO	; nein dann Automatik einschalten sonst Beleuchtung an
 	 mov	WORK , TIMER_AKTIV_BYTE	 ; Timerfunktion übergehen ohne Timer abzuschalten
 	 andi	WORK, 0b11111110	 ; Bit 1 ausschalten (Beleuchtung)
 	 mov	TIMER_AKTIV_BYTE ,  WORK	
	 sbi	PORTC_D,1		; und Beleuchtung AN
	 rjmp	End_SerIn		; und ENDE ohne Zeichenausgabe	
	 Code_Beleucht_AUTO:		; Automatik einschalten
	 cpi    DISPLAY_DAT,50		; ist es '2' für Beleuchtung AUTO 
	 brne   Code_Beleucht_Fail	; nein dann Ende ohne Funktion
	 mov	WORK , TIMER_AKTIV_BYTE	 ; Timerfunktion für Beleuchtung aktivieren
 	 ori	WORK, 0b00000001	 ; Bit 1 anschalten (Beleuchtung)
 	 mov	TIMER_AKTIV_BYTE ,  WORK	
	 Code_Beleucht_Fail:
	 rjmp	End_SerIn		; und ENDE ohne Zeichenausgabe		 
	; **********************
  Main_Send_Data_MIT_LOC:	
 	; **********************
 	; ** überspringen der 'toten' Bereiche des Displays, 
 	; ** damit alle Zeichen umlaufend angezeigt werden falls kein Positionsbefehl 
 	; ** vom Benutzer kommt
 	; !!! standart wenn keine besonderen Befehle kommen !!!
	push	DISPLAY_DAT		; rette Zeichen
	mov	DISPLAY_DAT,C_MERK	; Positionier Cursor entsprechend akt. Cursorposition
	rcall	locateZ_S		
	inc	DISPLAY_DAT		; erhöhe akt. Cursorposition
	andi	DISPLAY_DAT,0x3F	; bis max. 64 Zeichen (wenn anderes Display anpassen)
	mov	C_MERK,DISPLAY_DAT	; in C_Merk (akt. Cursorposit.)
	pop	DISPLAY_DAT		; und Zeichen wieder nach Display_Dat
 	; **********************
  Main_Send_Data_Ohne_LOC:
  	; Cursor wird über Befehle(LOCATE des Benutzers) positioniert
	rcall	Disp_Send_Data
  End_SerIn:
	clr	DISPLAY_DAT		; löschen und zurück
	rjmp    Start_SerIn
; **** End-Main ****************************************************************



; ******* Unterprogrammaufrufe **************************************************
; ******************************************************************************

; ******************************************************************************
; ****** Timer1 Überlauf Interuptroutine ***************************************
; ****** Welche Routinen ausgeführt werden sollen, steht im Register ***********
; ****** TIMER_AKTIV_BYTE .. nur wenn dort entspr. Bit gesetzt wird z.B. die ***
; ****** Helligkeitsfühler abgefragt *******************************************
; ******
; ******************************************************************************
TIMER1_OL:
;	push 	SAVE_STATUS_FLAG   
	in	SAVE_STATUS_FLAG,STATUS_FLAG	; Rette StatusRegister da es verändert wird !!		
	; *********** BIT 0 in TIMER_AKTIV_BYTE
	; *** messen des Helligkeitsfühlers und danach Beleuchtung steuern 
	mov	X_L,  TIMER_AKTIV_BYTE	; soll Beleuchtautom. ausgeführt werden ?
	sbrs	X_L,  0			; also bit 0 gesetzt ?
	rjmp	No_Timer_Beleucht	; nein dann weiter zu nächster Aufg. bzw. Ende
	 ldi	X_L, Sensor_Kanal_Hellig ; Kanal zum Messen an dem Helligkeitsfühler
	 out	ADMUX, X_L
	 ldi	X_L, 0b11011110		; AD-Wandler initaliesieren und 1x messen
	 out	ADCSR,	X_L
	No_Timer_Beleucht:
	; *********** BIT 1 in TIMER_AKTIV_BYTE in Timer0 !
	; *********** BIT 2 in TIMER_AKTIV_BYTE (Unterbits 3/4/5)
	; *** LEDs blinken 
	mov	X_L,  TIMER_AKTIV_BYTE	; soll LED-Blink-Test genrell ausgeführt werden ?
	sbrs	X_L,  2			; also bit 1 gesetzt ?
	rjmp	No_LED_Blink		; nein dann weiter zu nächster Aufg. bzw. Ende
 	 andi	X_L,0b00111000  	; ausfiltern der 3 LED-Bits
	  sbrs	X_L, 3			; led 0 blinken lassen ? set = aus
	  rjmp	LED_Blink_led1  	; nein dann test ob Led1 	  
	  in 	WORK, PORTD_D		; blinken
	  sbrs	WORK, 3
	  rjmp	l_on4
	  cbi	PORTD_D, 3
	  rjmp	Led_Blink_led1
	  l_on4:
	  sbi	PORTD_D, 3
	 Led_Blink_led1:
	  sbrs	X_L, 4    
	  rjmp    Led_Blink_led2
	  in 	WORK, PORTD_D
	  sbrs	WORK, 4
	  rjmp	l_on5
	  cbi	PORTD_D, 4
	  rjmp	Led_Blink_led2
	  l_on5:
	  sbi	PORTD_D, 4
	 Led_Blink_led2:
	  sbrs	X_L, 5
	  rjmp	No_LED_Blink
	  in 	WORK, PORTD_D
	  sbrs	WORK, 5
	  rjmp	l_on6
	  cbi	PORTD_D, 5
	  rjmp	No_LED_Blink
	  l_on6:
	  sbi	PORTD_D, 5
	No_LED_Blink:
	out	STATUS_FLAG,SAVE_STATUS_FLAG ; und Flags in orig. zustand
;	pop 	SAVE_STATUS_FLAG
	reti
; *** End Timer1 Überlauf ******************************************************


; ******************************************************************************
; ****** Timer0 Überlauf Interuptroutine ***************************************
; ****** Benutzt für Abgfagezeit der Taster ************************************
TIMER0_OL:
	push	WORK
;	push 	SAVE_STATUS_FLAG   
	in	SAVE_STATUS_FLAG,STATUS_FLAG	; Rette StatusRegister da es verändert wird !!		


	; *********** BIT 1 in TIMER_AKTIV_BYTE testen
	; *** wenn gesetzt dann Tasterroutine ausführen
	; *** Wenn eine Taste gedrückt wurde, wird das im Register TASTER_PRESSED
	; *** vermerkt und ein entsprechendes Bit gesetzt
	; *** Bit 0=T1  1=T2  2=T3  4=T4
	mov	WORK, TIMER_AKTIV_BYTE	        ; sollen Taster abgefragt werden ?
	sbrs	WORK, 1				; also bit 0 gesetzt ?
	rjmp	No_Timer_Taster			; nein dann weiter zu nächster Aufg. bzw. Ende
	
 	 ; *** Wurde Taster 1 gedrückt dann setze Bit in TASTER_PRESSED entsprechend ??
	 in 	WORK, PIND	                ; lese INPUT-Pins ein
	 sbrc	WORK, 6				; Taster 1 gedrückt ?
	 rjmp	tast2_test			; nicht gedrückt dann nächsten testen
	 dec	Timer_T1_Counter		; Entprellzähler veringern
	 brne	tast2_test			; nur wenn einmal abgelaufen nächter Tdruck möglich
	 mov	WORK, TASTER_PRESSED            ; dann setzte bit 0  = 1   
	 ori	WORK, 0b1	 		; im Register das in Hauptschleife abefragt wird
	 mov	TASTER_PRESSED, WORK
	 ldi	WORK, Timer_T_Counter_Time   ; und Entprellzähler wieder zurücksetzen 
	 mov	Timer_T1_Counter, WORK 

	 ; *** Wurde Taster 2 gedrückt dann setze Bit in TASTER_PRESSED entsprechend ??
	 tast2_test:
	 in 	WORK, PIND	
	 sbrc	WORK, 7				; Taster 2 gedrückt ?
	 rjmp	tast3_test			; nicht gedrückt test next
	 dec	Timer_T2_Counter		; Entprellzähler veringern
	 brne	tast3_test			; nur wenn einmal abgelaufen nächter Tdruck möglich
	 mov	WORK, TASTER_PRESSED
	 ori	WORK, 0b10			; dann setzte bit 0 = 2
	 mov	TASTER_PRESSED, WORK
	 ldi	WORK, Timer_T_Counter_Time     ; und Entprellzähler wieder zurücksetzen 
	 mov	Timer_T2_Counter, WORK 

	 ; *** Wurde Taster 3 gedrückt dann setze Bit in TASTER_PRESSED entsprechend ??
	 tast3_test:
	 in 	WORK, PINC
	 sbrc	WORK, 2				; Taster 3 gedrückt ?
	 rjmp	tast4_test			; nicht gedrückt test next
	 dec	Timer_T3_Counter		; Entprellzähler veringern
	 brne	tast4_test			; nur wenn einmal abgelaufen nächter Tdruck möglich
	 mov	WORK, TASTER_PRESSED
	 ori	WORK, 0b100			; dann setzte bit 0 = 4
	 mov	TASTER_PRESSED, WORK
	 ldi	WORK, Timer_T_Counter_Time     ; und Entprellzähler wieder zurücksetzen 
	 mov	Timer_T3_Counter, WORK 

	 ; *** Wurde Taster 4 gedrückt dann setze Bit in TASTER_PRESSED entsprechend ??
	 tast4_test:
	 in 	WORK, PINC
	 sbrc	WORK, 4				; Taster 4 gedrückt ?
	 rjmp	end_tast_test			; nicht gedrückt test next
	 dec	Timer_T4_Counter		; Entprellzähler veringern
	 brne	end_tast_test			; nur wenn einmal abgelaufen nächter Tdruck möglich
	 mov	WORK, TASTER_PRESSED
	 ori	WORK, 0b1000			; dann setzte bit 0 = 8
	 mov	TASTER_PRESSED, WORK
	 ldi	WORK, Timer_T_Counter_Time     ; und Entprellzähler wieder zurücksetzen 
	 mov	Timer_T4_Counter, WORK 
	 
	 ; *** Keine Tastendruck ermittelt
	 end_tast_test:

	No_Timer_Taster:
	out	STATUS_FLAG,SAVE_STATUS_FLAG ; und Flags in orig. zustand
;	pop 	SAVE_STATUS_FLAG
	pop 	WORK
	reti
; *** End Timer1 Überlauf ******************************************************


; ******************************************************************************
; ****** AD-Wandlung komplett IRQ-Routine **************************************
ADC_CONV_C:
;	push    SAVE_STATUS_FLAG
	in	SAVE_STATUS_FLAG,STATUS_FLAG	; Rette StatusRegister da es verändert wird !!
	in	X_L, ADCL 		; immer zuerst CL ! auslesen - Messwert
	in 	X_H, ADCH		; dann CH sonst hängt AD-Wandler fest 
	; steuere die Beleuchtung anhand des Messwertes
	lsr	X_L			; oberen Wert in X_L packen
	sbrc	X_H, 0			; aus 10bit - 8bit machen
	ori	X_L, 128
	lsr	X_L
	sbrc	X_H, 1
	ori	X_L, 128		; fertige Messwert
	in	X_H, PORTC_D		; Art Hysterese
	sbrc	X_H, 1
	rjmp	Bel_Is_On
	cpi	X_L, 120
	brlo	Bel_AN
	cbi	PORTC_D, 1
	rjmp	Bel_END
	Bel_Is_On:
	cpi	X_L, 130
	brlo	Bel_AN
	cbi	PORTC_D, 1
	rjmp	Bel_END
	Bel_AN:
	sbi	PORTC_D, 1
	Bel_END:
	out	STATUS_FLAG,SAVE_STATUS_FLAG ; und Flags in orig. zustand
;	pop     SAVE_STATUS_FLAG
	reti	
; *** end AD-Wandlung komplett ***********************************************

; ******************************************************************************
; ****** Intro nach Reset ******************************************************
; ***** -- Register ***************************************************
; ***** zwischenspeicher		:	TEMP ******************
; ***** kommt EEPROM-Adresse rein	:	WORK2   ***************
; ***** übergabe des Wertes an Display	:	DISPLAY_DAT ***********
; ***** PAUSE länge			:	PAUSEWERT *************
; ***** Display löschen			:	DISPLAY_DAT ***********
; ***** -- Variblen ***************************************************
; ***** Intro_Meldung-Adresse EEPROM	:	Intro_Meldung *********
; ***** EEProm-Adresse			:	EEADRESS **************
; ***** EEProm-Controll-Reg.		:	EECONTR ***************
; ***** EEProm-Daten Reg.		:	EEDAT *****************
; ***** -- Routinen ***************************************************
; ***** Daten an Displ. senden		:	Disp_Send_Data ********
; ***** Code an Displ. senden		:	Disp_Send_Code ********
; ***** Pause				:	Delay_X ***************
; *********************************************************************
Intro:					; ersten 32 Zeichen aus EEPROM ausgeben
	ldi	WORK2,Intro_Meldung   	; ausgeben der IntroMeldung aus EEPROM
	ldi	TEMP,33			; Zähler für die 32 Zeichen
Intro_Start:
	out 	EEADRESS,WORK2		; setze EEPROM-Adresse auf Null
	sbi	EECONTR,0		; setze das Daten gelsene werden sollen
	in	DISPLAY_DAT,EEDAT	; und schiebe die Daten nach DISPLAY_DAT
	
	wait_for_intern_uart:
	in	WORK_UART, UART_UCSRA	; warten auf UART, sonst zeichen verschluckt
	sbrs	WORK_UART, 5 		; ist UDRE gesetzt dann weiter 
	rjmp	wait_for_intern_uart
	out 	UART_UDR, DISPLAY_DAT	; auch nach TxD schicken, Test der ser. Ausgabe
    		
	rcall 	Disp_Send_Data		; wo sie angezeigt werden	
	inc	WORK2			; erhöhe EEPROM-ADRESSE um 1
	dec	TEMP			; und veringer Zeichenzähler um 1
	brne	Intro_Start		; wenn alle Zeichen gesendet Ende
	ldi 	PAUSEWERT, 70		; 50 mal 25ms warten = 1,75sec
	rcall	Delay_X_long
	ldi	DISPLAY_DAT,DISPLAY_CLEAR	;** Clear Display	
	rcall	Disp_Send_Code
	ldi	DISPLAY_DAT,DISPLAY_DDRAM_NULL  ;** und DDRAM auf NULL
	rcall	Disp_Send_Code
	ret
; *** End Intro *******************************************************


; *********************************************************************
; **** UART ***********************************************************
; **** Interuptbehandlung nachdem ein Zeichen empfangen ***************
; **** in Puffer speichern u.CTS/XOFF entspr. setzen, w Puffer voll ***
; **** puffert nur ASC(32-126) !!! ************************************            
; ***** -- Register ***************************************************
; ***** Allg.				: WORK_UART *( ab r16)*********
; ***** schreiben in RAM                :       r28,r29 Y_L,Y_H *******
; ***** Pufferposisition und Größe	: 2x unter r16 ****************
; ***** zwischenspeicher		: 	TEMP *(ab r16)*********
; *****                                      in Stack gesichert !******
; ***** zwischenspeicher		: 	WORK *(ab r16)*********
; *****                                      in Stack gesichert !******
; ***** -- Routinen ***************************************************
; ***** DD-RAM ADresse an Display	:	Disp_Send_Code ********
; *********************************************************************
UART_RX_C:
;	push    SAVE_STATUS_FLAG
	in	SAVE_STATUS_FLAG,STATUS_FLAG	; Rette StatusRegister da es verändert wird !!
					; teste ob es ein Fehler war der Empfangen wurde
	in	WORK_UART,UART_UCSRA	; Status einlesen
	sbrs	WORK_UART,4		; wenn bit=1 dann ein FramError u. 'jmp Read'
	rjmp	SAVE_CHAR		; wird überspr. , wenn kein Fehler ab zu Zeichen lesen
	in 	WORK_UART,UART_UDR	; nur wenn FrameError, UDR muß gelesen werden damit IRQ frei wird
	reti				; EXIT
    SAVE_CHAR:				; Zeichen einlesen u. in Puffer speichern
  	in 	WORK_UART,UART_UDR	; lese UDR - Zeichen, gib int frei
	; Code Zeichen ~ wird auch gepuffert, sonst ->
	; was war es für Zeichen ? nur Zeichen 32-127 wird gepuffert !!!!
	; wenn ä/ö/ü usw. benötigt, dann noch anpassen !!!!!!!!!!!!!!!!!!
	cpi	WORK_UART,ASC_CODE      ; Code einleitungszeichen ?
	breq	SAVE_C
	cpi	WORK_UART, ASC_UG	; Zeichen ab SPACE speichern in Puffer
	brlo	End_UART_RX
	cpi	WORK_UART, ASC_OG	; obere Grenze für Zeichen die gesp. werden
	brsh	End_UART_RX
	; und weiter mit speichern in SRAM (Puffer)
    SAVE_C:
  	mov	Y_L,PUFFER_POS		; Akt. Pufferpos. lesen
  	clr	Y_H			
  	st	Y+,WORK_UART		; schreibe Zeichen in Puffer(SRAM) an entsp. Posit.
	mov	PUFFER_POS,Y_L
	inc	PUFFER_AKT_LAENG	; akt. PufferLänge = PufferLänge + 1
	ldi	Y_H, PUFFER_LAENGE_GESAMT-PUFFER_LAENGE_RESERVE  ; Schutz vor Überlauf
	cp	PUFFER_AKT_LAENG,Y_H	; PUFFER_LAENGE  auf Puffer Voll ?
	brlt	End_UART_RX		; dann CTS / od. XOFF aktiv machen sonst P_überlauf	
	
	sbi	PORTD_DDR,2  		; Port_D-CTS auf Ausgabe
	sbi	PORTD_D,2		; und CTS aktiv damit keine Daten mehr kommen
	ldi	WORK_UART,19
	out	UART_UDR, WORK_UART	; XOFF senden
		
	ldi	Y_H, PUFFER_LAENGE_GESAMT+1  ; BUFFERÜBERLAUF doch passiert ?
	cp	PUFFER_AKT_LAENG,Y_H	
	brlo	End_UART_RX		; dann RESET
	rcall	Init_Reset
    End_UART_RX:		
    	out	STATUS_FLAG,SAVE_STATUS_FLAG ; und Flags in orig. zustand
 ;   	pop    SAVE_STATUS_FLAG
	reti
; *** End UART_RX_C ***************************************************


; *********************************************************************
; **** Tasterbehandlung ***********************************************
; **** hier stehen die Ereignisse die ausgeführt werden wenn ein Taster
; **** gedrückt wurde *************************************************

Taster_Abfrage:
	push 	DISPLAY_DAT
	push	WORK
;	push 	SAVE_STATUS_FLAG   
;	in	SAVE_STATUS_FLAG,STATUS_FLAG	; Rette StatusRegister da es verändert wird !!		
	 
	tst 	TASTER_PRESSED
	breq	No_Tast_press

	; *** Aufgabe für Taster 1
	pressed_taster_1:
	mov	WORK, TASTER_PRESSED		; wurde Taster 1 gedrückt ???
	cpi     WORK, 1
	brne	pressed_taster_2		; nein dann test next 
	andi	WORK, 0b11111110		; ja dann mache Bit frei f. nächsten Druck
	mov	TASTER_PRESSED, WORK
	ldi	DISPLAY_DAT, 'T'		; und erfülle Aufgabe 
	out 	UART_UDR, DISPLAY_DAT	        ; T1 senden
	ldi	DISPLAY_DAT, '1'
	out 	UART_UDR, DISPLAY_DAT	
;prelltest	
;	in 	WORK, PORTD_D
;	sbrs	WORK, 3
;	rjmp	l_on
;	cbi	PORTD_D, 3
;	rjmp	No_Tast_press
;	l_on:
;	sbi	PORTD_D, 3
	
	
	; *** Aufgabe für Taster 2		; w.o.
	pressed_taster_2:	
	mov	WORK, TASTER_PRESSED	
	cpi     WORK, 2
	brne	pressed_taster_3	
	andi	WORK, 0b11111101
	mov	TASTER_PRESSED, WORK	
	ldi	DISPLAY_DAT, 'T'
	out 	UART_UDR, DISPLAY_DAT	
	ldi	DISPLAY_DAT, '2'
	out 	UART_UDR, DISPLAY_DAT	
;prelltest
;	in 	WORK, PORTD_D
;	sbrs	WORK, 4
;	rjmp	l_on1
;	cbi	PORTD_D, 4
;	rjmp	No_Tast_press
;	l_on1:
;	sbi	PORTD_D, 4
	
	; *** Aufgabe für Taster 3		; w.o
	pressed_taster_3:	
	mov	WORK, TASTER_PRESSED	
	cpi     WORK, 4
	brne	pressed_taster_4	
	andi	WORK, 0b11111011
	mov	TASTER_PRESSED, WORK
	ldi	DISPLAY_DAT, 'T'
	out 	UART_UDR, DISPLAY_DAT	
	ldi	DISPLAY_DAT, '3'
	out 	UART_UDR, DISPLAY_DAT	
;prelltest
;	in 	WORK, PORTD_D
;	sbrs	WORK, 5
;	rjmp	l_on2
;	cbi	PORTD_D, 5
;	rjmp	No_Tast_press
;	l_on2:
;	sbi	PORTD_D, 5
	
	; *** Aufgabe für Taster 4		; w.o
	pressed_taster_4:	
	mov	WORK, TASTER_PRESSED	
	cpi     WORK, 8
	brne	No_Tast_Press		
	andi	WORK, 0b11110111
	mov	TASTER_PRESSED, WORK
	ldi	DISPLAY_DAT, 'T'
	out 	UART_UDR, DISPLAY_DAT	
	ldi	DISPLAY_DAT, '4'
	out 	UART_UDR, DISPLAY_DAT	
;prelltest
;	in 	WORK, PORTD_D
;	sbrs	WORK, 3
;	rjmp	l_on3
;	cbi	PORTD_D, 3
;	rjmp	No_Tast_press
;	l_on3:
;	sbi	PORTD_D, 3
	
	No_Tast_press:
;	out	STATUS_FLAG,SAVE_STATUS_FLAG ; und Flags in orig. zustand
;	pop 	SAVE_STATUS_FLAG
	pop	WORK
	pop	DISPLAY_DAT
	ret
; *********************************************************************	
	

; *********************************************************************
; **** zeichen aus Puffer lesen und Puffer-Zeiger enstpr. Posit. ******
; **** und CTS/XOFF entspr. setzen, falls an war **********************
; ***** -- Register ***************************************************
; ***** Ausgabe des gelesenen Wertes	:	DISPLAY_DAT *( ab r16)*
; ***** Lesen aus RAM                   :       r30,r31 Z_L,Z_H *******
; ***** Pufferposisition und Größe	: 2x unter r16 ****************
; ***** zwischenspeicher		: 	TEMP *(ab r16)*********
; *****                                      in Stack gesichert !******
; ***** zwischenspeicher		: 	WORK *(ab r16)*********
; *****                                      in Stack gesichert !******
; ***** -- Routinen ***************************************************
; ***** DD-RAM ADresse an Display	:	Disp_Send_Code ********
; *********************************************************************
READ_PUFFER:	
	push	TEMP			; zum test
    	push	WORK
	ldi	Z_L,UART_CR_OFF		; UART - Einstellen das kein Int 
	out	UART_UCSRB,Z_L		; beim auslesen ausgelöst wird
	tst	PUFFER_AKT_LAENG	; Puffer leer ? also lange null
	breq	End_leer		; dann ende
    	ldi	Z_L,PUFFER		; 1.Zeichen aus Puffer lesen
    	clr	Z_H			; aus SRAM 
    	ld	DISPLAY_DAT, Z		; und nach DISPLAY_DAT     (vorerst!-evt. extraReg.)
	mov	WORK, PUFFER_AKT_LAENG	; sichern
	dec	WORK
    	dec	PUFFER_AKT_LAENG	; Pufferlänge minus 1
	breq	End_jetzt_leer		; ist länge jetzt 0 ? (also puffer leer) 
   READ_Loop:				; Puffer schieben (später evt. andere Art)
   	ldd	TEMP, Z+1		; lese nächstes Zeichen
   	st	Z+, TEMP
   	dec	WORK
   	brne	READ_Loop
    End_jetzt_leer:
    	clr	TEMP
    	st	Z, TEMP			; 1.Zeichen löschen
  	mov	PUFFER_POS,Z_L
    End_leer:    
	mov	WORK, PUFFER_AKT_LAENG	; mache Puffer erstmal etwas leer bevor CTS frei wird
	cpi	WORK, 2			; 
	brge	End_E			; 
	; muß ERST dafor angeschalten werden, sont evt überlappungen I/O
	ldi	Z_L,UART_CR		; Betriebsart des UART auf AN
	out	UART_UCSRB,Z_L
	sbi	PORTD_DDR,2		; ansonsten cts aus
	cbi	PORTD_D,2		; damit neue Zeichnen kommen können
    	ldi 	WORK, 17		; XON
	out 	UART_UDR,WORK
    End_E:
   	ldi	Z_L,UART_CR		; Betriebsart des UART auf AN
	out	UART_UCSRB,Z_L
	pop 	WORK  	
    	pop 	TEMP
	ret
; *** End Read_Puffer**************************************************

; *********************************************************************
; ***** setzt DD-RAM ADRESSE auf angegeb. Position ********************
; ***** locate Zeile, Spalte als HEX_ZAHL *****************************
; ***** bsp. locate mit übergabe 0x23 -> 2Zeile, 3Spalte **************
; ***** Zeile 0-3, Spalte 0-F !! *** KEINE FEHLERAUSWERTUNG !!!!! *****
; *****
; ***** -- Register ***************************************************
; ***** übergabe des Wertes		:	DISPLAY_DAT *( ab r16)*
; ***** zwischenspeicher		: 	TEMP *(ab r16)*********
; *****                                      in Stack gesichert !******
; ***** -- Routinen ***************************************************
; ***** DD-RAM ADresse an Display	:	Disp_Send_Code ********
; *********************************************************************
LocateZ_S:
	push	DISPLAY_DAT		; rette
	mov	C_MERK,DISPLAY_DAT	; setze Merkposition
	tst	DISPLAY_DAT		; ZP -Zeile,Spalte hex
	breq	loc			; Zeile,Spalte ist Null dann direkt zum Ende
	push	TEMP			; retten damit nur ein REG belegt
	mov	TEMP,DISPLAY_DAT	; rette Wert
	swap	DISPLAY_DAT		; Zeile in untere Bits..
	andi 	DISPLAY_DAT,0x0f 	; Zeile filtern (Sp. löschen)
	breq	loc_S			; Z=0 ?                  Zeile = 0
	dec	DISPLAY_DAT		; -1 Z1
	breq	loc_Z1			; ist es Null ? dann war Zeile = 1
	dec	DISPLAY_DAT		; -1 Z2
	breq	loc_Z2			; ist es Null ? dann war Zeile = 2
    	ldi	DISPLAY_DAT,0x50	; Zeilenstart     , hier Zeile = 3
    	rjmp	loc_S
    loc_Z2:
    	ldi	DISPLAY_DAT,0x10	; Zeilenstart
    	rjmp	loc_S
    loc_Z1:
    	ldi	DISPLAY_DAT,0x40	; Zeilenstart
    	rjmp	loc_S
    loc_S:   				; und SPalte addieren
       	andi 	TEMP,0x0f	 	; Spalte filtern (Zeile. löschen)
    	or	DISPLAY_DAT, TEMP 	; dann addieren
    	pop 	TEMP			; Wiederherstellen
    loc:
    	ori	DISPLAY_DAT,DISPLAY_DDRAM_NULL	; und Basis dazu
	rcall	Disp_Send_Code		;  ergibt DDRAM-ADresse
	pop	DISPLAY_DAT
	ret	
; *** end LocateZ_S ***************************************************	
	
; *********************************************************************
; ***** Sende Commando oder Daten an das Display **********************
; ***** -- Register ***************************************************
; ***** übergabe des Wertes		:	DISPLAY_DAT ***********
; ***** zwischenspeicher		:	TEMP_DISP *************
; ***** Verknüpfung ent Dat/Code	:	TEMP_DISP_DS **********
; ***** PAUSE länge			:	PAUSEWERT *************
; ***** zu and. arbeiten 		: 	WORK ******************
; ***** -- Variblen ***************************************************
; ***** Display auf Empfang setzen	:	DDR_WRITE_DISPLAY *****
; ***** -- Ports ******************************************************
; ***** PortB -DDR			:	PORTB_DDR *************
; ***** PortB -Daten			:       PORTB_D ***************
; ***** -- Routinen ***************************************************
; ***** den E-Puls senden		:	Send_E_Puls ***********
; ***** Pause				:	Delay_X ***************
; *********************************************************************
; *** für Datensenden muß R/S H
Disp_Send_Data:
	ldi     TEMP_DISP_DS,16		; R/S auf H für Daten (f. spät. or)
	rjmp	Disp_Send
; *** für Codesenden muß R/S L		
Disp_Send_Code: 
	clr     TEMP_DISP_DS		; R/S auf L für Code
; *** Sende Daten
Disp_Send:
	; das Display-Busy abfragen ob Display bereit
	ldi	TEMP_DISP, DDR_READ_DISPLAY  	; lesen von Display einstellen
	out	PORTB_DDR, TEMP_DISP
	ldi	TEMP_DISP, 0b101111		; Code lesen v. Display u. EingangsPull-up an
	out	PORTB_D, TEMP_DISP
	Disp_Busy_Wait:
	 sbi	PORTC_D, 0		; E auf High
	 nop				; warte kurz bis Display Daten sendet
	 nop
	 nop
	 in	TEMP_DISP, PINB		; und einlesen
	 cbi	PORTC_D, 0		; E auf Low
	 nop
	 nop
	 rcall  Send_E_Puls		; unteren 4Bits lesen (werden aber nicht benötigt)
	 bst    TEMP_DISP, 3   		; Busyflag bit nach T schieben
	 brtc   Disp_Ready		; T=0 ? dann Display bereit f. nächste Daten
;	 sbrs	TEMP_DISP, 3
;	 rjmp	Disp_Ready
	 nop				; sonst warte noch mind. 1µS
	 nop
	 nop
	 nop
	 nop
	 rjmp	Disp_Busy_Wait	 	     			
	Disp_Ready:			; Weiter da Display Brereit
	ldi	WORK,UART_CR_OFF	; UART - Einstellen das kein Int 
	out	UART_UCSRB,WORK		; beim auslesen ausgelöst wird
	ldi 	WORK,DDR_WRITE_DISPLAY	; Portrichtung zum schreiben auf display
	out 	PORTB_DDR,WORK
	cbi	PORTB_D,5		; R/W auf L damit Displ. auf Empfang geht 
	; denn 8bit-WErt in 2x4bit  Wert umwandeln um senden
	mov 	TEMP_DISP, DISPLAY_DAT  ; sichere übergeb. Wert
	swap 	DISPLAY_DAT		; tausche Nibbles, damit obere hälfte zuerst gesendet
	andi	DISPLAY_DAT,0x0f	; lösche obere Hälfte
	or 	DISPLAY_DAT,TEMP_DISP_DS ; und setze wieder R/S
	out 	PORTB_D,DISPLAY_DAT	; ausgeben an Ports
	rcall 	Send_E_Puls		; und Displ. übernimmt Daten durch E-Impuls 
	andi	TEMP_DISP,0x0f		; jetzt noch den LOW-Teil der ja im gesicht. TEMP steht
	or 	TEMP_DISP,TEMP_DISP_DS	; R/S wieder setzen
	out 	PORTB_D,TEMP_DISP	; ...
	rcall 	Send_E_Puls		; ...	
	ldi	WORK,UART_CR		; UART - wieder an
	out	UART_UCSRB,WORK		; 
	ret
;*** End Daten/Code senden *******************************************	


; ********************************************************************
; ***** Display initalisieren ****************************************
; ***** s. Datenblatt zum Display ************************************
; ***** -- Register **************************************************
; ***** zu allg. arbeiten 		: 	WORK ( ab r16) *******
; ***** PAUSE länge			:	PAUSEWERT ( ab r16) **
; ***** übergabe eine Wertes an Display	:	DATEN_DAT ( ab r16) **
; ***** -- Variblen **************************************************
; ***** Display auf Empfang setzen	:	DDR_WRITE_DISPLAY ****
; *****	setzen der Modi s. Datenblatt,  :	DISPLAY_FUNK_SET *****
; *****	werte vordef. am Anfang des 	:	DISPLAY_DISP *********
; *****	programmes			:	DISPLAY_CLEAR ********
; *****					:	DISPLAY_EMODE ********
; ***** -- Ports *****************************************************
; ***** PortB -DDR			:	PORTB_DDR ************
; ***** PortB -Daten			:       PORTB_D **************
; ***** -- Routinen **************************************************
; ***** den E-Puls senden		:	Send_E_Puls **********
; ***** Pause				:	Delay_X **************
; ********************************************************************
Init_Display:
	ldi 	WORK,DDR_WRITE_DISPLAY	; Portrichtung zum schreiben auf display
	out 	PORTB_DDR,WORK
	;*** ab hier siehe datenblatt
	ldi 	PAUSEWERT, 170		; 170 x ca. 100us = ca. 17ms
	rcall 	Delay_X			; Pause mind. 15ms
	;* ERSTE (4bit) sequ. laut datenblatt
	ldi 	WORK, 0b000011		
	out 	PORTB_D, WORK 		
	rcall 	Send_E_Puls		; mit E_PULS datenübernahme
	ldi 	PAUSEWERT, 50		; 5ms
	rcall 	Delay_X			; und eine Pause mind. 4ms
	;* ZWEITE
	; da Ports schon gesetzt kann auf nochmal. setzen verzichtet werden
	; und einfach mit den Pausen und E_pulsen fortgefahren werden
	rcall 	Send_E_Puls		; E_PULS datenübernahme
	ldi 	PAUSEWERT, 20		; 2ms
	rcall 	Delay_X			; und eine Pause 
	;* DRITTE sequ. laut datenblatt 		
	rcall 	Send_E_Puls		; E_PULS datenübernahme
	ldi 	PAUSEWERT, 20		; zwar nicht angeb. aber nötig ! 
	rcall 	Delay_X			; und eine Pause 
	;* VIERTE sequ. laut datenblatt
	cbi 	PORTB_D,0		; einfach bit 0 löschen
	rcall 	Send_E_Puls		; E_PULS datenübernahme
	ldi 	PAUSEWERT, 20		; w.o.
	rcall 	Delay_X			; und eine Pause 
	;** ab hier ist auf 4bit umgeschalten !!! und es muß dem Display mitgeteilt
	;* werden das Code kommt !!!			
	;** funktions-set
	ldi 	DISPLAY_DAT, DISPLAY_FUNK_SET
	rcall	Disp_Send_Code		; routine zum CODE an Displ. zu senden
	;** display an
	ldi 	DISPLAY_DAT,DISPLAY_DISP
	rcall 	Disp_Send_Code
	;** Clear Display
	ldi 	DISPLAY_DAT,DISPLAY_CLEAR 
	rcall 	Disp_Send_Code
	;** Entry-Mode-Set
	ldi 	DISPLAY_DAT,DISPLAY_EMODE 
	rcall 	Disp_Send_Code
	; DD-RAM auf NULL 	
	ldi	DISPLAY_DAT,DISPLAY_DDRAM_NULL
	rcall	Disp_Send_Code
	ret	
; *** End Display initalisieren **************************************	
	
; ********************************************************************	
; ***** sende E-Puls damit Displ. Daten übernimmt ********************
; ***** -- Ports *****************************************************
; ***** PortC -DDR			:	PORTC_DDR ************
; ***** PortC -Daten			:       PORTC_D **************
; ********************************************************************
Send_E_Puls:
	sbi	PORTC_D,0		; E auf HIGH
	nop				; ...
	nop				; kurz E halten
	nop				; ...
	cbi	PORTC_D,0		; und E wieder LOW
	ret
; *** End E-Puls senden **********************************************

; ********************************************************************
; ***** Pause von ca. 100us bis 6.4sec / bei 8Mhz ********************
; *****
; ******** innere Pause Delay_X :    (bei 8Mhz u. Fakt=1 ca. 100us)***
; ******** -- Register ***********************************************
; ***** Übergebener Wert, die Länge d.P.:	PAUSEWERT ************
; ***** Faktor : 1-255 ergibt 100us-25ms , 0 maximum *****************
; ***** äußerer Schleifen Zähler 	:	PAUSE1 (>=r16) *******
; *****
; ******** Große Pause Delay_X_long :(bei 8Mhz u. Fakt=1 ca. 25ms )***
; ******** -- Register ***********************************************
; ***** Übergebener Wert, die Länge d.P.:	PAUSEWERT ************
; ***** Faktor : 1-255 ergibt 25ms-6,4sec , 0 maximum ****************
; ***** zus. zu PAUSE1 noch das Register PAUSE_LANG, welches *********
; ***** nicht direkt beschrieben werden kann (unter r16) *************
; ********************************************************************
Delay_X_long:				; Schleife Pause Lang
	mov	PAUSE_LANG,PAUSEWERT	; (indirk.Reg) Wert sichern 
	clr	PAUSEWERT		; clr damit Delay_X auf max läuft
	rjmp	Dlong			; und beginn Schleife
Delay_X:				; Schleife Pause Klein
	ldi	PAUSE1,1		; wenn kl.Pause muß PAUSE_LANG (r0)
	mov	PAUSE_LANG,PAUSE1	; so sein, das Gr.Schleife u. nicht durchlaufen wird !! 
    Dlong:				; die äußere Schleife
	clr	PAUSE1			; 256 durchläufe
    Loop1:				; die innere Schleife
	dec	PAUSE1				
	brne	Loop1				; **
	dec	PAUSEWERT
	brne	Dlong			; **
	dec	PAUSE_LANG
	brne	Dlong		; **
	ret	
; *** End Pause ******************************************************

; *** End Unterprogramme *******************************************************
		
		