Anschrift eMail
Flackerlicht / 8 PWM-Ausgänge mit PIC 16F877
Das Prinzip dieses Programms:
Alle 0,1 ms wird von Timer 2 ein Interrupt ausgelöst. In der ISR (Interupt-Service-Routine) werden 8 Zähler bis auf 0 dekrementiert. Ist ein Zähler ungleich 0, wird das entsprechende Bit in einem zu Beginn der ISR gelöschten Byte gesetzt. Das Byte wird abschließend an PORTD ausgegeben.
Nach jeweils 10 ms wird in alle 8 Zähler ein Wert zwischen 0 und 100 geladen. Dieser Wert ist die Prozent-Zahl für Duty-Cycle / Takt-Pausen-Verhältnis des PWM-Signals. 10 ms ergeben eine PWM-Frequenz von 100 Hz.
Jede ms wird einer von 8 Soll-/Ist-Werten für die Lampen-Helligkeit verglichen und der Ist-Wert entsprechend der vorgegebenen Geschwindigkeit und Schritt-Größe an den Soll-Wert angenähert. Zusätzlich wird einer von 4 AD-Kanälen mit angeschlossenem Poti eingelesen, damit Werte zur manuellen Helligkeits-Einstellung bereit stehen.
Alle 10 ms wird einer von 8 Soll-Werten entsprechend dem gewählten Modus neu gesetzt. Dadurch ist eine Helligkeits-Änderung alle 100 ms möglich.

Dieses Programm kann mit jedem ASCII-Terminal-Programm kommunizieren. Die Kommunikation erfolgt über die asynchrone serielle Schnittstelle des PIC mit 9600 Baud, 8 Bit, ohne Parität und besteht aus Kommandos, beginnend mit einem Kennbuchstaben oder Kennwort, gefolgt von weiteren Parametern. Ein Kommando ohne Parameter führt zur Ausgabe der aktuellen Werte.
Alle ankommenden Bytes lösen einen Interrupt aus und werden von der ISR in einem Empfangs-Ring-Puffer abgelegt. Von dort können sie "bei Gelegenheit" entnommen und weiter verarbeitet werden.
Ausgabe-Bytes werden in einem Sende-Ring-Puffer abgelegt. Jede Millisekunde wird in der ISR überprüft, ob Bytes zum Versenden abgelegt wurden und ggf. an die USART übergeben.

Schaltplan als PDF-Datei
; ###############################################################
;  			FlaLi (FlackerLicht)
;			PIC 16F877
;	Geaendert:	KH Domnick
; ###############################################################

		;1234567 Stellen
#DEFINE	Version	"FlaLi12 "	; <=== hier die Version aendern
		;tt.mm.jj
#DEFINE	Datum	"16.09.06"	; <=== hier das Datum aendern

; ###############################################################

	list P=16f877, E=1, R=DEC	; Prozessortyp, Errorlevel und Zahlenformat
	include "p16f877.inc"		; Registerdefinitionen einbinden

	errorlevel	1

	__CONFIG	_PWRTE_ON & _WDT_OFF & _HS_OSC & _BODEN_OFF & _LVP_OFF

Debug		set	0	; 0 = Debug aus
				; 1 = Debug ein

Quarz		set	5	; 0 = 10,0000 MHz
				; 1 = 10,2400 MHz
				; 5 =  4,9152 MHz *9600 Baud
				; 4 =  4,0960 MHz

; ***************************************************************
; Makros und Definitionen
; ***************************************************************
		; ---------------------------
		; Makro: Umschalten nach Rom-Page X
		; ---------------------------
M_Page0		macro
		bcf	PCLATH,3	; Rom-Page 0
		bcf	PCLATH,4
		endm

M_Page1		macro
		bsf	PCLATH,3	; Rom-Page 1
		bcf	PCLATH,4
		endm

M_Page2		macro
		bcf	PCLATH,3	; Rom-Page 2
		bsf	PCLATH,4
		endm

M_Page3		macro
		bsf	PCLATH,3	; Rom-Page 3
		bsf	PCLATH,4
		endm

		; ---------------------------
		; Makro: Umschalten auf RegisterBank X
		; ---------------------------
M_ClSt_Bank0	macro
		clrf	STATUS		; Bank 0
		endm

M_Bank0		macro
		bcf	STATUS,RP0	; Bank 0
		bcf	STATUS,RP1
		endm

M_Bank1		macro
		bsf	STATUS,RP0	; Bank 1
		bcf	STATUS,RP1
		endm

M_Bank2		macro
		bcf	STATUS,RP0	; Bank 2
		bsf	STATUS,RP1
		endm

M_Bank3		macro
		bsf	STATUS,RP0	; Bank 3
		bsf	STATUS,RP1
		endm

		; ---------------------------
		; Makro: MOV FileReg to FileReg
		; ---------------------------
M_movff		macro	VonFileReg,NachFileReg
		movfw	VonFileReg
		movwf	NachFileReg
		endm

		; ---------------------------
		; Makro: MOV Literal to FileReg
		; ---------------------------
M_movlf		macro	Literal,NachFileReg
		movlw	Literal
		movwf	NachFileReg
		endm

		; ---------------------------
		; Makro: MOV FileReg Bank 1 to W
		; ---------------------------
M_movb1w	macro	VonFileReg
		bsf	STATUS,RP0	; Bank 1
		movfw	VonFileReg
		bcf	STATUS,RP0	; Bank 0
		endm

		; ---------------------------
		; Makro: MOV W to FileReg Bank 1
		; ---------------------------
M_movwb1	macro	NachFileReg
		bsf	STATUS,RP0	; Bank 1
		movwf	NachFileReg
		bcf	STATUS,RP0	; Bank 0
		endm

		; ---------------------------
		; Makro: SUB W from FileReg Bank 1 to W
		; ---------------------------
M_subwb1	macro	VonFileReg
		bsf	STATUS,RP0	; Bank 1
		subwf	VonFileReg,W
		bcf	STATUS,RP0	; Bank 0
		endm

; ***************************************************************
; Definitionen
; ***************************************************************
#DEFINE	D_GlobIntEin	bsf	INTCON,GIE
#DEFINE	D_GlobIntAus	bcf	INTCON,GIE
#DEFINE	D_PeriIntEin	bsf	INTCON,PEIE
#DEFINE	D_PeriIntAus	bcf	INTCON,PEIE

#DEFINE	_C		STATUS,0
#DEFINE	_Z		STATUS,2
#DEFINE	skip_0		btfsc
#DEFINE	skip_1		btfss
#DEFINE	skip_NC		btfsc	STATUS,C	; oder skpNC
#DEFINE	skip_C		btfss	STATUS,C	; oder skpC
#DEFINE	skip_NZ		btfsc	STATUS,Z	; oder skpNZ
#DEFINE	skip_Z		btfss	STATUS,Z	; oder skpZ

; ***************************************************************
; Konstanten
; ***************************************************************
cr		EQU	0x0D	; Return
lf		EQU	0x0A	; LineFeed
hk		EQU	0x22	; Hochkomma
klauf		EQU	"<"
klzu		EQU	">"

Ptime		EQU	50	; 25 * 10 = 250 ms Prellzeit

; ***************************************************************
; Register in Bank 0
; ***************************************************************
RamBeg0		EQU	0x20	; Beginn Ram-Bereich

		; ---------------------------
		; Register fuer Mathe-Routinen
		; ---------------------------
MathErgA0	EQU	0x20	; Ergebnis (DoubleWord)
MathErgA1	EQU	0x21
MathArgA0	EQU	0x22	; Argument A (Word)
MathArgA1	EQU	0x23
MathTemp0	EQU	0x24	; Temporaer
MathTemp1	EQU	0x25

		; ---------------------------
		; Register fuer Zaehler- und Timer
		; ---------------------------
zZuf		EQU	0x26	; Dec-Zaehler Zufallszahl
z01ms		EQU	0x27	; Inc-Zaehler 0,1 ms-Takt
z1ms		EQU	0x28	; Dec-Zaehler   1 ms-Takt
z10ms		EQU	0x29	; Dec-Zaehler  10 ms-Takt
z100ms		EQU	0x2A	; Dec-Zaehler 100 ms-Takt

t10ms		EQU	0x2B	; Dec-Timer  10 ms
t100ms		EQU	0x2C	; Dec-Timer 100 ms
t1Sek		EQU	0x2D	; Dec-Timer   1 Sek

zPwm		EQU	0x30	; 8 x Dec-Zaehler   0,1 ms-Takt
		; ...	0x37
zTempo		EQU	0x38	; 8 x Dec-Zaehler   1 ms-Takt
		; ...	0x3F
aIst		EQU	0x40	; 8 x Akt Ist-Wert
		; ...	0x47
aSoll		EQU	0x48	; 8 x Akt Soll-Wert
		; ...	0x4F

		; ---------------------------
		; Register fuer Aktuelle Ein-/Ausg-Zustaende
		; ---------------------------
aInp1		EQU	0x50	; Akt Eingaenge
aOutp1		EQU	0x51	; Akt Ausgaenge
aAdw1		EQU	0x52	; Akt Analog/Digital-Wert 1
aAdw2		EQU	0x53	; Akt Analog/Digital-Wert 2
aAdw3		EQU	0x54	; Akt Analog/Digital-Wert 3
aAdw4		EQU	0x55	; Akt Analog/Digital-Wert 4

aAdwNr		EQU	0x56	; Akt Analog-Kanal

		; ---------------------------
		; Aktuelle Parameter
		; ---------------------------
pPrgNr		EQU	0x58	; Akt Programm-Nummer


		; ---------------------------
		; Register fuer Flags
		; ---------------------------
Flags0		EQU	0x60
#DEFINE	F_OK	Flags0,0	; Flag OK = 1 / nicht OK = 0
#DEFINE	F_Ret	Flags0,1	; Flag CR
#DEFINE	F_Semi	Flags0,2	; Flag Semikolon
#DEFINE	F_CmdE	Flags0,3	; Flag Cmd-Puffer Ende
#DEFINE	F_Fmt	Flags0,4	; 1=Ziffern formatiert senden
#DEFINE	F_Zif	Flags0,5	; 1=Ziffer 1...9 gesendet
#DEFINE	F_Alle	Flags0,6	; 1=Alle den gleichen Param
#DEFINE	F_Eing	Flags0,7	; Flag fuer Eingangs-Zustand

		; ---------------------------
		; Temporaere Register
		; ---------------------------
Temp1		EQU	0x62	; Temporaer (nicht fuer Interrupt)
Temp2		EQU	0x63
Temp3		EQU	0x64

Para1		EQU	0x65	; Temporaer Parameter
Para2		EQU	0x66
Para3		EQU	0x67

CmdByte		EQU	0x68	; Temporaer bei Cmd-Auswertung

		; ---------------------------
		; Zeiger-Register
		; ---------------------------
RxDOfs		EQU	0x6A	; Offset RxD-RingPuffer
RxDPos		EQU	0x6B	; Position RxD-RingPuffer
TxDOfs		EQU	0x6C	; Offset TxD-RingPuffer
TxDPos		EQU	0x6D	; Position TxD-RingPuffer
TxDAnz		EQU	0x6E	; Anzahl Bytes im TxD-Puffer
CmdPos		EQU	0x6F	; Position Cmd-Puffer

RamEnd0		EQU	0x70	; Ende Ram-Bereich

		; ---------------------------
		; Diverse Register
		; Von Bank0...3 erreichbar (0x71...0x7F) !
		; ACHTUNG !!! 0x70 ist bei Verwendung von MPLAB ICD belegt
		; ---------------------------
iBakW		EQU	0x71	; Interrupt Backup W-Register
iBakSTAT	EQU	0x72	; Interrupt Backup Status
iBakPCL		EQU	0x73	; Interrupt Backup Rom-Page (PCLATH)
iBakFSR		EQU	0x74	; Interrupt Backup Ind-Adresse
izUni		EQU	0x75	; Interrupt Universeller Zaehler

zUni		EQU	0x76	; Universeller Zaehler
AddrL		EQU	0x77	; Addresse Low  fuer Flash & Eeprom
AddrH		EQU	0x78	; Addresse High fuer Flash
EepByte		EQU	0x79	; Byte aus Flash & Eeprom

iTemp1		EQU	0x7A	; Temporaer im Interrupt
iTemp2		EQU	0x7B
iTemp3		EQU	0x7C

ramP		EQU	0x7D	; Init P
ramI		EQU	0x7E	; Init I
ramC		EQU	0x7F	; Init C

; ***************************************************************
; Register in Bank 1
; ***************************************************************
		; ---------------------------
		; Aktuelle Daten (max 32 Bytes)
		; ---------------------------
RamParam	EQU	0xA0
pModus		EQU	0xA0	; 8 x Modus - Steps
		; ...	0xA7
pTempo		EQU	0xA8	; 8 x Tempo
		; ...	0xAF
pHell		EQU	0xB0	; 8 x Helligkeit
		; ...	0xB7
pDunk		EQU	0xB8	; 8 x Dunkelheit
		; ...	0xBF

		; ---------------------------
		; Kommando-Puffer fuer max 48 Byte
		; ---------------------------
CmdLen		EQU	48	; Puffer fuer 48 Byte
CmdPuf		EQU	0xC0	; Adresse 0xC0 bis 0xEF

; ***************************************************************
; Register in Bank 2
; ***************************************************************
		; ---------------------------
		; RingPuffer fuer RxD
		; ---------------------------
RxDLen		EQU	80	; Puffer fuer 80 Byte
RxDPuf		EQU	0x110	; Adresse 0x110 bis 0x16F

; ***************************************************************
; Register in Bank 3
; ***************************************************************
		; ---------------------------
		; RingPuffer fuer TxD
		; ---------------------------
TxDLen		EQU	80	; Puffer fuer 80 Byte
TxDPuf		EQU	0x190	; Adresse 0x190 bis 0x1EF

; ***************************************************************
; Eeprom-Register
; ***************************************************************
		; ---------------------------
		; Register ab Eeprom-Adr. 0x2100 max 256 Byte
		; ---------------------------

; ###############################################################
; Programmspeicher Page 0
; RESET: Aufsprungadresse 0000h
; ###############################################################
		org	0x0000

Reset		clrf	PCLATH		; 00h Page 0
		goto	ProgBeg		; 01h Sprung zum ProgrammBeginn
		goto	Reset		; 02h Resetvektor
		goto	Reset		; 03h Resetvektor
	;;	goto	Interrupt	; 04h Interruptvektor
	;;	Kein GOTO !!!, falls in Rom-Page <> 0 ausgeloest wurde

; ***************************************************************
; Interrupt-Serviceroutine ISR
; Ausloesung durch Receive
; Ausloesung durch Timer 2 / PR2
; ***************************************************************
Interrupt	movwf	iBakW		; W retten nach iBakW
		swapf	STATUS,W	; Status mit swap nach W
		clrf	STATUS		; Bank 0
		movwf	iBakSTAT	; Status retten ohne Veraenderung

		movfw	PCLATH		; Rom-Page (PCLATH) retten
		movwf	iBakPCL
		clrf	PCLATH		; Auf Rom-Page 0 schalten

		movfw	FSR
		movwf	iBakFSR

		; ---------------------------
		; Wer hat den Interrupt ausgeloest ?
		; ---------------------------
		skip_0	PIR1,RCIF	; ... Receive nicht
		goto	Isr_RC

		skip_0	PIR1,TMR2IF	; ... Timer 2 nicht
		goto	Isr_TMR2

	;;	movlw	00100010b	; Alle nicht benutzten IntFlags ruecksetzen
	;;	andwf	PIR1,F

Isr_Ende	movfw	iBakFSR
		movwf	FSR

		movfw	iBakPCL		; Rom-Page (PCLATH) zurueck
		movwf	PCLATH

		swapf	iBakSTAT,W	; Statusregister und W zurueck
		movwf	STATUS
		swapf	iBakW,F
		swapf	iBakW,W
		retfie

; ===============================================================
; ISR Receive (Sio RxD)
; ===============================================================
Isr_RC		call	SioRxD
Isr_RC_Ende	bcf	PIR1,RCIF	; Receive InterruptFlag ruecksetzen
		goto	Isr_Ende

; ===============================================================
; ISR Timer 2
; ===============================================================
		; ---------------------------
		; Aufruf alle 0,1 ms
		; ---------------------------
Isr_TMR2	clrf	iTemp1		; Bits loeschen

UpdPwm_0	movfw	zPwm+0		; Zaehler = 0 ?
		skip_NZ			; ... nein
		goto	UpdPwm_1

		decf	zPwm+0		; Zaehler - 1
		bsf	iTemp1,0

		; ---------------------------
UpdPwm_1	movfw	zPwm+1		; Zaehler = 0 ?
		skip_NZ			; ... nein
		goto	UpdPwm_2

		decf	zPwm+1		; Zaehler - 1
		bsf	iTemp1,1

		; ---------------------------
UpdPwm_2	movfw	zPwm+2		; Zaehler = 0 ?
		skip_NZ			; ... nein
		goto	UpdPwm_3

		decf	zPwm+2		; Zaehler - 1
		bsf	iTemp1,2

		; ---------------------------
UpdPwm_3	movfw	zPwm+3		; Zaehler = 0 ?
		skip_NZ			; ... nein
		goto	UpdPwm_4

		decf	zPwm+3		; Zaehler - 1
		bsf	iTemp1,3

		; ---------------------------
UpdPwm_4	movfw	zPwm+4		; Zaehler = 0 ?
		skip_NZ			; ... nein
		goto	UpdPwm_5

		decf	zPwm+4		; Zaehler - 1
		bsf	iTemp1,4

		; ---------------------------
UpdPwm_5	movfw	zPwm+5		; Zaehler = 0 ?
		skip_NZ			; ... nein
		goto	UpdPwm_6

		decf	zPwm+5		; Zaehler - 1
		bsf	iTemp1,5

		; ---------------------------
UpdPwm_6	movfw	zPwm+6		; Zaehler = 0 ?
		skip_NZ			; ... nein
		goto	UpdPwm_7

		decf	zPwm+6		; Zaehler - 1
		bsf	iTemp1,6

		; ---------------------------
UpdPwm_7	movfw	zPwm+7		; Zaehler = 0 ?
		skip_NZ			; ... nein
		goto	UpdPwm_8

		decf	zPwm+7		; Zaehler - 1
		bsf	iTemp1,7

		; ---------------------------
UpdPwm_8	movfw	iTemp1
		movwf	aOutp1
		comf	aOutp1,W
		movwf	PORTD
		decfsz	z01ms		; ... 0,1 ms-Zaehler - 1 = 0
		goto	Isr_TMR2_Ende	; ... sonst Ende

		; ---------------------------
		; Aufruf alle 1 ms
		; ---------------------------
		M_movlf	10,z01ms	; 0,1 ms-Zaehler = 10 (= 1,0 ms)

		call	SioTxD		; evtl. Byte seriell ausgeben
		call	RdAdw		; A/D-Wandler einlesen
		call	UpdIst		; 1 Ist-Wert aktualisieren

Isr1ms_9	decfsz	z1ms,F		; ... 1 ms-Zaehler - 1 = 0
		goto	Isr_TMR2_Ende	; ... sonst Ende

		; ---------------------------
		; Aufruf alle 10 ms
		; ---------------------------
		M_movlf	10,z1ms		; 1 ms-Zaehler = 10 (= 10 ms)

		call	UpdSoll		; 1 Sollwert aktualisieren

	; ....

		movfw	t10ms		; 10 ms-Timer = 0 ?
		skip_Z			; ... ja
		decf	t10ms		; ... sonst - 1

		decfsz	z10ms,F		; ... 10 ms-Zaehler - 1 = 0
		goto	Isr_TMR2_Ende	; ... sonst Ende

		; ---------------------------
		; Aufruf alle 100 ms
		; ---------------------------
		M_movlf	10,z10ms	; 10 ms-Zaehler = 10 (= 100 ms)
		movfw	t100ms		; 100 ms-Timer = 0 ?
		skip_Z			; ... ja
		decf	t100ms		; ... sonst - 1


	; ....

		decfsz	z100ms		; ... 100 ms-Zaehler - 1 = 0
		goto	Isr_TMR2_Ende	; ... sonst Ende

		; ---------------------------
		; Aufruf jede Sekunde
		; ---------------------------
		M_movlf	10,z100ms	; 100 ms-Zaehler = 10 (= 1 Sek)
		movfw	t1Sek		; 1 Sek-Timer laden
		skip_Z			; ... = 0
		decf	t1Sek		; ... sonst - 1

		movfw	TxDAnz		; Evtl. Anzahl verringern, falls nichts gesendet wird
		skip_Z			; ... ist schon 0
		decf	TxDAnz		; ... sonst - 1

	; ....

Isr_TMR2_Ende	bcf	PIR1,TMR2IF	; Timer 2 InterruptFlag ruecksetzen
		goto	Isr_Ende

; ===============================================================
; A/D-Wandler einlesen und naechsten Kanal setzen
; A/D-Wandler starten
; ===============================================================
RdAdw		movlw	HIGH(TblAdw)
		movwf	PCLATH
		movfw	z1ms		; Test auf 256 Byte Grenz-Überschreitung
		addwf	PCL,W
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		addlw	6		; + 6 Befehle
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		movfw	z1ms
TblAdw		addwf	PCL
		return			; 0
		return			; 1
		return			; 2
		goto	RdAdw_1
		goto	StartAdw
		goto	RdAdw_2
		goto	StartAdw
		goto	RdAdw_3
		goto	StartAdw
		goto	RdAdw_4
		goto	StartAdw

		; ---------------------------
StartAdw	bsf	ADCON0,GO	;A/D-Wandler starten
		return

		; ---------------------------
RdAdw_1		movfw	ADRESH		; AD-Wert laden
		movwf	aAdw2		; und speichern
		movlw	10001001b	; AD-Quelle auf Kanal RA1
		;	10		= Fosc/32 A/D-Conversion
		;	  001		= Kanal 1
		;	     0		= Not GO
		;	       1	= ADON
		movwf	ADCON0		; AD-Quelle setzen
		return

		; ---------------------------
RdAdw_2		movfw	ADRESH		; AD-Wert laden
		movwf	aAdw1		; und speichern
		movlw	10010001b	; AD-Quelle auf Kanal RA2
		;	  010		= Kanal 2
		movwf	ADCON0		; AD-Quelle setzen
		return

		; ---------------------------
RdAdw_3		movfw	ADRESH		; AD-Wert laden
		movwf	aAdw3		; und speichern
		movlw	10011001b	; AD-Quelle auf Kanal RA3
		;	  011		= Kanal 3
		movwf	ADCON0		; AD-Quelle setzen
		return

		; ---------------------------
RdAdw_4		movfw	ADRESH		; AD-Wert laden
		movwf	aAdw4		; und speichern
		movlw	10000001b	; AD-Quelle auf Kanal RA0
		;	  000		= Kanal 0
		movwf	ADCON0		; AD-Quelle setzen
		return

; ===============================================================
; Auf-/Ab-Zaehler aktualisieren
; ===============================================================
UpdIst		movlw	3
		subwf	z1ms,W		; Offset - 3
		addlw	pModus		; + Adr Modus
		movwf	FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		swapf	INDF,W		; AnzSteps
		andlw	0x0F		; Nur Bit 3...0
		movwf	izUni		; AnzSteps retten
		incf	izUni		; + 1

		movlw	HIGH(TblIst)
		movwf	PCLATH
		movfw	z1ms		; Test auf 256 Byte Grenz-Überschreitung
		addwf	PCL,W
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		addlw	6		; + 6 Befehle
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		movfw	z1ms
TblIst		addwf	PCL
		return			; 0
		return			; 1
		return			; 2
		goto	UpdIst_0
		goto	UpdIst_1
		goto	UpdIst_2
		goto	UpdIst_3
		goto	UpdIst_4
		goto	UpdIst_5
		goto	UpdIst_6
		goto	UpdIst_7

; -------------------------------------------
; aIst nach zPwm kopieren
; -------------------------------------------
UpdIst_0	movfw	aIst+0
		movwf	zPwm+0

		; ---------------------------
		; Wenn ZlrTempo > 0, dann - 1
		; sonst ZlrTempo neu setzen
		; ---------------------------
		movfw	zTempo+0	; ZlrTempo = 0 ?
		skip_NZ			; ... nein
		goto	UpdIst_01	; ... ja

		decf	zTempo+0	; Zaehler - 1
		return

UpdIst_01	bsf	STATUS,RP0	; Bank 1
		movfw	pTempo+0	; ParamTempo
		bcf	STATUS,RP0	; Bank 0
		movwf	zTempo+0	; zum Zaehler

		; ---------------------------
		; Richtung ermitteln: Soll - Ist
		; ---------------------------
		movfw	aIst+0		; Soll - Ist
		subwf	aSoll+0,W
		skip_NZ			; ... Soll <> Ist
		return

		skip_C			; ... positiv, also rauf
		goto	UpdIst_03	; ... runter

		; ---------------------------
		; Rauf
		; ---------------------------
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_02

		movfw	izUni		; Ist + Steps
		addwf	aIst+0
		return

UpdIst_02	movfw	iTemp1		; Ist + Diff
		addwf	aIst+0
		return

		; ---------------------------
		; Runter
		; ---------------------------
UpdIst_03	movfw	aSoll+0		; Ist - Soll
		subwf	aIst+0,W
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_04

		movfw	izUni		; Ist - Steps
		subwf	aIst+0
		return

UpdIst_04	movfw	iTemp1		; Ist - Diff
		subwf	aIst+0
		return

; -------------------------------------------
; aIst nach zPwm kopieren
; -------------------------------------------
UpdIst_1	movfw	aIst+1
		movwf	zPwm+1

		; ---------------------------
		; Wenn ZlrTempo > 0, dann - 1
		; sonst ZlrTempo neu setzen
		; ---------------------------
		movfw	zTempo+1	; ZlrTempo = 0 ?
		skip_NZ			; ... nein
		goto	UpdIst_11	; ... ja

		decf	zTempo+1	; Zaehler - 1
		return

UpdIst_11	bsf	STATUS,RP0	; Bank 1
		movfw	pTempo+1	; ParamTempo
		bcf	STATUS,RP0	; Bank 0
		movwf	zTempo+1	; zum Zaehler

		; ---------------------------
		; Richtung ermitteln: Soll - Ist
		; ---------------------------
		movfw	aIst+1		; Soll - Ist
		subwf	aSoll+1,W
		skip_NZ			; ... Soll <> Ist
		return

		skip_C			; ... positiv, also rauf
		goto	UpdIst_13	; ... runter

		; ---------------------------
		; Rauf
		; ---------------------------
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_12

		movfw	izUni		; Ist + Steps
		addwf	aIst+1
		return

UpdIst_12	movfw	iTemp1		; Ist + Diff
		addwf	aIst+1
		return

		; ---------------------------
		; Runter
		; ---------------------------
UpdIst_13	movfw	aSoll+1		; Ist - Soll
		subwf	aIst+1,W
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_14

		movfw	izUni		; Ist - Steps
		subwf	aIst+1
		return

UpdIst_14	movfw	iTemp1		; Ist - Diff
		subwf	aIst+1
		return

; -------------------------------------------
; aIst nach zPwm kopieren
; -------------------------------------------
UpdIst_2	movfw	aIst+2
		movwf	zPwm+2

		; ---------------------------
		; Wenn ZlrTempo > 0, dann - 1
		; sonst ZlrTempo neu setzen
		; ---------------------------
		movfw	zTempo+2	; ZlrTempo = 0 ?
		skip_NZ			; ... nein
		goto	UpdIst_21	; ... ja

		decf	zTempo+2	; Zaehler - 1
		return

UpdIst_21	bsf	STATUS,RP0	; Bank 1
		movfw	pTempo+2	; ParamTempo
		bcf	STATUS,RP0	; Bank 0
		movwf	zTempo+2	; zum Zaehler

		; ---------------------------
		; Richtung ermitteln: Soll - Ist
		; ---------------------------
		movfw	aIst+2		; Soll - Ist
		subwf	aSoll+2,W
		skip_NZ			; ... Soll <> Ist
		return

		skip_C			; ... positiv, also rauf
		goto	UpdIst_23	; ... runter

		; ---------------------------
		; Rauf
		; ---------------------------
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_22

		movfw	izUni		; Ist + Steps
		addwf	aIst+2
		return

UpdIst_22	movfw	iTemp1		; Ist + Diff
		addwf	aIst+2
		return

		; ---------------------------
		; Runter
		; ---------------------------
UpdIst_23	movfw	aSoll+2		; Ist - Soll
		subwf	aIst+2,W
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_24

		movfw	izUni		; Ist - Steps
		subwf	aIst+2
		return

UpdIst_24	movfw	iTemp1		; Ist - Diff
		subwf	aIst+2
		return

; -------------------------------------------
; aIst nach zPwm kopieren
; -------------------------------------------
UpdIst_3	movfw	aIst+3
		movwf	zPwm+3

		; ---------------------------
		; Wenn ZlrTempo > 0, dann - 1
		; sonst ZlrTempo neu setzen
		; ---------------------------
		movfw	zTempo+3	; ZlrTempo = 0 ?
		skip_NZ			; ... nein
		goto	UpdIst_31	; ... ja

		decf	zTempo+3	; Zaehler - 1
		return

UpdIst_31	bsf	STATUS,RP0	; Bank 1
		movfw	pTempo+3	; ParamTempo
		bcf	STATUS,RP0	; Bank 0
		movwf	zTempo+3	; zum Zaehler

		; ---------------------------
		; Richtung ermitteln: Soll - Ist
		; ---------------------------
		movfw	aIst+3		; Soll - Ist
		subwf	aSoll+3,W
		skip_NZ			; ... Soll <> Ist
		return

		skip_C			; ... positiv, also rauf
		goto	UpdIst_33	; ... runter

		; ---------------------------
		; Rauf
		; ---------------------------
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_32

		movfw	izUni		; Ist + Steps
		addwf	aIst+3
		return

UpdIst_32	movfw	iTemp1		; Ist + Diff
		addwf	aIst+3
		return

		; ---------------------------
		; Runter
		; ---------------------------
UpdIst_33	movfw	aSoll+3		; Ist - Soll
		subwf	aIst+3,W
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_34

		movfw	izUni		; Ist - Steps
		subwf	aIst+3
		return

UpdIst_34	movfw	iTemp1		; Ist - Diff
		subwf	aIst+3
		return

; -------------------------------------------
; aIst nach zPwm kopieren
; -------------------------------------------
UpdIst_4	movfw	aIst+4
		movwf	zPwm+4

		; ---------------------------
		; Wenn ZlrTempo > 0, dann - 1
		; sonst ZlrTempo neu setzen
		; ---------------------------
		movfw	zTempo+4	; ZlrTempo = 0 ?
		skip_NZ			; ... nein
		goto	UpdIst_41	; ... ja

		decf	zTempo+4	; Zaehler - 1
		return

UpdIst_41	bsf	STATUS,RP0	; Bank 1
		movfw	pTempo+4	; ParamTempo
		bcf	STATUS,RP0	; Bank 0
		movwf	zTempo+4	; zum Zaehler

		; ---------------------------
		; Richtung ermitteln: Soll - Ist
		; ---------------------------
		movfw	aIst+4		; Soll - Ist
		subwf	aSoll+4,W
		skip_NZ			; ... Soll <> Ist
		return

		skip_C			; ... positiv, also rauf
		goto	UpdIst_43	; ... runter

		; ---------------------------
		; Rauf
		; ---------------------------
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_42

		movfw	izUni		; Ist + Steps
		addwf	aIst+4
		return

UpdIst_42	movfw	iTemp1		; Ist + Diff
		addwf	aIst+4
		return

		; ---------------------------
		; Runter
		; ---------------------------
UpdIst_43	movfw	aSoll+4		; Ist - Soll
		subwf	aIst+4,W
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_44

		movfw	izUni		; Ist - Steps
		subwf	aIst+4
		return

UpdIst_44	movfw	iTemp1		; Ist - Diff
		subwf	aIst+4
		return

; -------------------------------------------
; aIst nach zPwm kopieren
; -------------------------------------------
UpdIst_5	movfw	aIst+5
		movwf	zPwm+5

		; ---------------------------
		; Wenn ZlrTempo > 0, dann - 1
		; sonst ZlrTempo neu setzen
		; ---------------------------
		movfw	zTempo+5	; ZlrTempo = 0 ?
		skip_NZ			; ... nein
		goto	UpdIst_51	; ... ja

		decf	zTempo+5	; Zaehler - 1
		return

UpdIst_51	bsf	STATUS,RP0	; Bank 1
		movfw	pTempo+5	; ParamTempo
		bcf	STATUS,RP0	; Bank 0
		movwf	zTempo+5	; zum Zaehler

		; ---------------------------
		; Richtung ermitteln: Soll - Ist
		; ---------------------------
		movfw	aIst+5		; Soll - Ist
		subwf	aSoll+5,W
		skip_NZ			; ... Soll <> Ist
		return

		skip_C			; ... positiv, also rauf
		goto	UpdIst_53	; ... runter

		; ---------------------------
		; Rauf
		; ---------------------------
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_52

		movfw	izUni		; Ist + Steps
		addwf	aIst+5
		return

UpdIst_52	movfw	iTemp1		; Ist + Diff
		addwf	aIst+5
		return

		; ---------------------------
		; Runter
		; ---------------------------
UpdIst_53	movfw	aSoll+5		; Ist - Soll
		subwf	aIst+5,W
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_54

		movfw	izUni		; Ist - Steps
		subwf	aIst+5
		return

UpdIst_54	movfw	iTemp1		; Ist - Diff
		subwf	aIst+5
		return

; -------------------------------------------
; aIst nach zPwm kopieren
; -------------------------------------------
UpdIst_6	movfw	aIst+6
		movwf	zPwm+6

		; ---------------------------
		; Wenn ZlrTempo > 0, dann - 1
		; sonst ZlrTempo neu setzen
		; ---------------------------
		movfw	zTempo+6	; ZlrTempo = 0 ?
		skip_NZ			; ... nein
		goto	UpdIst_61	; ... ja

		decf	zTempo+6	; Zaehler - 1
		return

UpdIst_61	bsf	STATUS,RP0	; Bank 1
		movfw	pTempo+6	; ParamTempo
		bcf	STATUS,RP0	; Bank 0
		movwf	zTempo+6	; zum Zaehler

		; ---------------------------
		; Richtung ermitteln: Soll - Ist
		; ---------------------------
		movfw	aIst+6		; Soll - Ist
		subwf	aSoll+6,W
		skip_NZ			; ... Soll <> Ist
		return

		skip_C			; ... positiv, also rauf
		goto	UpdIst_63	; ... runter

		; ---------------------------
		; Rauf
		; ---------------------------
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_62

		movfw	izUni		; Ist + Steps
		addwf	aIst+6
		return

UpdIst_62	movfw	iTemp1		; Ist + Diff
		addwf	aIst+6
		return

		; ---------------------------
		; Runter
		; ---------------------------
UpdIst_63	movfw	aSoll+6		; Ist - Soll
		subwf	aIst+6,W
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_64

		movfw	izUni		; Ist - Steps
		subwf	aIst+6
		return

UpdIst_64	movfw	iTemp1		; Ist - Diff
		subwf	aIst+6
		return

; -------------------------------------------
; aIst nach zPwm kopieren
; -------------------------------------------
UpdIst_7	movfw	aIst+7
		movwf	zPwm+7

		; ---------------------------
		; Wenn ZlrTempo > 0, dann - 1
		; sonst ZlrTempo neu setzen
		; ---------------------------
		movfw	zTempo+7	; ZlrTempo = 0 ?
		skip_NZ			; ... nein
		goto	UpdIst_71	; ... ja

		decf	zTempo+7	; Zaehler - 1
		return

UpdIst_71	bsf	STATUS,RP0	; Bank 1
		movfw	pTempo+7	; ParamTempo
		bcf	STATUS,RP0	; Bank 0
		movwf	zTempo+7	; zum Zaehler

		; ---------------------------
		; Richtung ermitteln: Soll - Ist
		; ---------------------------
		movfw	aIst+7		; Soll - Ist
		subwf	aSoll+7,W
		skip_NZ			; ... Soll <> Ist
		return

		skip_C			; ... positiv, also rauf
		goto	UpdIst_73	; ... runter

		; ---------------------------
		; Rauf
		; ---------------------------
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_72

		movfw	izUni		; Ist + Steps
		addwf	aIst+7
		return

UpdIst_72	movfw	iTemp1		; Ist + Diff
		addwf	aIst+7
		return

		; ---------------------------
		; Runter
		; ---------------------------
UpdIst_73	movfw	aSoll+7		; Ist - Soll
		subwf	aIst+7,W
		movwf	iTemp1		; Diff retten
		subwf	izUni,W		; Steps < Diff ?
		skip_NC			; ... ja
		goto	UpdIst_74

		movfw	izUni		; Ist - Steps
		subwf	aIst+7
		return

UpdIst_74	movfw	iTemp1		; Ist - Diff
		subwf	aIst+7
		return

; ===============================================================
; ZlrSoll je nach Modus setzen setzen		(10 ms)
; ===============================================================
UpdSoll		movlw	3		; z10ms >= 3 ?
		subwf	z10ms,W
		skip_C			; ... ja
		return

		movwf	izUni		; Kanal retten
		; ---------------------------
		; Eingangs-Bit laden
		; ---------------------------
		bcf	F_Eing		; Flag erst mal 0
		movlw	HIGH(TblEBit)
		movwf	PCLATH
		movfw	izUni		; Test auf 256 Byte Grenz-Überschreitung
		addwf	PCL,W
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		addlw	6		; + 6 Befehle
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		movfw	izUni		; Kanal zurueck
TblEBit		addwf	PCL
		goto	GetEBit_0
		goto	GetEBit_1
		goto	GetEBit_2
		goto	GetEBit_3
		goto	GetEBit_4
		goto	GetEBit_5
		goto	GetEBit_6
		goto	GetEBit_7

		; ---------------------------
GetEBit_0	skip_0	PORTB,0		; ... Eingang ist 0
		goto	GetModus
		goto	SetEBit

		; ---------------------------
GetEBit_1	skip_0	PORTB,1		; ... Eingang ist 0
		goto	GetModus
		goto	SetEBit

		; ---------------------------
GetEBit_2	skip_0	PORTB,2		; ... Eingang ist 0
		goto	GetModus
		goto	SetEBit

		; ---------------------------
GetEBit_3	skip_0	PORTB,3		; ... Eingang ist 0
		goto	GetModus
		goto	SetEBit

		; ---------------------------
GetEBit_4	skip_0	PORTB,4		; ... Eingang ist 0
		goto	GetModus
		goto	SetEBit

		; ---------------------------
GetEBit_5	skip_0	PORTB,5		; ... Eingang ist 0
		goto	GetModus
		goto	SetEBit

		; ---------------------------
GetEBit_6	skip_0	PORTB,6		; ... Eingang ist 0
		goto	GetModus
		goto	SetEBit

		; ---------------------------
GetEBit_7	skip_0	PORTB,7		; ... Eingang ist 0
		goto	GetModus

SetEBit		bsf	F_Eing		; Flag setzen

		; ---------------------------
		; Welcher Modus ?
		; ---------------------------
GetModus	movfw	izUni		; Kanal zurueck
		addlw	pModus		; + Adr
		movwf	FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		movfw	INDF		; Modus laden
		andlw	0x0F		; Nur Bit 3...0
		movwf	iTemp1

		movlw	HIGH(TblModus)
		movwf	PCLATH
		movfw	iTemp1		; Test auf 256 Byte Grenz-Überschreitung
		addwf	PCL,W
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		addlw	6		; + 6 Befehle
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		movfw	iTemp1		; Modus zurueck
TblModus	addwf	PCL
		goto	Modus_1		; 100 %
		goto	Modus_2		; 0 %
		goto	Modus_3		; Hell-Wert
		goto	Modus_4		; Dunkel-Wert
		goto	Modus_5		; Eing 0 = 100 %, 1 = 0 %
		goto	Modus_6		; Eing 0 = 0 %, 1 = 100 %
		goto	Modus_7		; Eing 0 = Hell-Wert, 1 = Dunkel-Wert
		goto	Modus_8		; Eing 0 = Dunkel-Wert, 1 = Hell-Wert
		goto	Modus_9		; Wechsel 0 <--> 100 %
		goto	Modus_10	; Wechsel Hell <--> Dunkel
		goto	Modus_11	; A/D-Wandler 1
		goto	Modus_12	; A/D-Wandler 2
		goto	Modus_13	; A/D-Wandler 3
		goto	Modus_14	; A/D-Wandler 4
		goto	Modus_15	; Zufall Hell <--> Dunkel
		goto	Modus_16	; Zufall 0 <--> 100 %

		; ---------------------------
Modus_1		movlw	100		; 100 %
		goto	Modus_18

		; ---------------------------
Modus_2		movlw	0		; 0 %
		goto	Modus_18

		; ---------------------------
Modus_3		movlw	pHell		; Adr Hell-Werte
		goto	Modus_17

		; ---------------------------
Modus_4		movlw	pDunk		; Adr Dunkel-Werte
		goto	Modus_17

		; ---------------------------
Modus_5		movlw	100		; Erst mal 100 %
		skip_0	F_Eing		; Flag ist 0
		movlw	0		; 0 %

		goto	Modus_18

		; ---------------------------
Modus_6		movlw	0		; Erst mal 0 %
		skip_0	F_Eing		; Flag ist 0
		movlw	100		; 100 %

		goto	Modus_18

		; ---------------------------
Modus_7		movlw	pHell		; Erst mal Adr Hell-Werte
		skip_0	F_Eing		; Flag ist 0
		movlw	pDunk		; Adr Dunkel-Werte

		goto	Modus_17

		; ---------------------------
Modus_8		movlw	pDunk		; Erst mal Adr Dunkel-Werte
		skip_0	F_Eing		; Flag ist 0
		movlw	pHell		; Adr Hell-Werte

		goto	Modus_17

		; ---------------------------
Modus_9		movfw	izUni		; Kanal zurueck
		addlw	aIst		; + Adr
		movwf	FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		movfw	INDF		; Ist laden
		movwf	iTemp1		; und retten
		movfw	izUni		; Kanal zurueck
		addlw	aSoll		; + Adr
		movwf	FSR
		movfw	INDF		; Soll laden
		subwf	iTemp1,W	; Soll = Ist ?
		skip_Z			; ... ja
		return

		movlw	100
		subwf	iTemp1,W	; 100 % = Ist ?
		skip_Z			; ... ja
		goto	Modus_91
		
		movlw	0		; 0 %
		goto	Modus_18

Modus_91	movlw	100		; 100 %
		goto	Modus_18

		; ---------------------------
Modus_10	movfw	izUni		; Kanal zurueck
		addlw	aIst		; + Adr
		movwf	FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		movfw	INDF		; Ist laden
		movwf	iTemp1		; und retten
		movfw	izUni		; Kanal zurueck
		addlw	aSoll		; + Adr
		movwf	FSR
		movfw	INDF		; Soll laden
		subwf	iTemp1,W	; Soll = Ist ?
		skip_Z			; ... ja
		return

		movfw	izUni		; Kanal zurueck
		addlw	pHell		; + Adr
		movwf	FSR
		movfw	INDF		; Hell laden
		subwf	iTemp1,W	; Hell = Ist ?
		skip_Z			; ... ja
		goto	Modus_101
		
		movlw	pDunk		; Adr Dunkel
		goto	Modus_17

Modus_101	movlw	pHell		; Adr Hell
		goto	Modus_17

		; ---------------------------
Modus_11	bcf	_C
		rrf	aAdw1,W		; D/A-Wandler 1
		goto	Modus_18

		; ---------------------------
Modus_12	bcf	_C
		rrf	aAdw2,W		; D/A-Wandler 2
		goto	Modus_18

		; ---------------------------
Modus_13	bcf	_C
		rrf	aAdw3,W		; D/A-Wandler 3
		goto	Modus_18

		; ---------------------------
Modus_14	bcf	_C
		rrf	aAdw4,W		; D/A-Wandler 4
		goto	Modus_18

		; ---------------------------
Modus_15	movfw	izUni		; Kanal zurueck
		addlw	aIst		; + Adr
		movwf	FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		movfw	INDF		; Ist laden
		movwf	iTemp1		; und retten
		movfw	izUni		; Kanal zurueck
		addlw	aSoll		; + Adr
		movwf	FSR
		movfw	INDF		; Soll laden
		subwf	iTemp1,W	; Soll = Ist ?
		skip_Z			; ... ja
		return

		movfw	zZuf
		goto	Modus_18

		; ---------------------------
Modus_16	movfw	izUni		; Kanal zurueck
		addlw	aIst		; + Adr
		movwf	FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		movfw	INDF		; Ist laden
		movwf	iTemp1		; und retten
		movfw	izUni		; Kanal zurueck
		addlw	aSoll		; + Adr
		movwf	FSR
		movfw	INDF		; Soll laden
		subwf	iTemp1,W	; Soll = Ist ?
		skip_Z			; ... ja
		return

		movfw	izUni		; Kanal zurueck
		addlw	pDunk		; + Adr
		movwf	FSR
		movfw	INDF		; Dunkel-Wert laden
		subwf	zZuf,W		; Dunkel <= Zufall ?
		skip_C			; ... ja
		goto	Modus_161

		movfw	izUni		; Kanal zurueck
		addlw	pHell		; + Adr
		movwf	FSR
		movfw	INDF		; Hell-Wert laden
		subwf	zZuf,W		; Hell > Zufall ?
		skip_NC			; ... ja
		goto	Modus_161

		movfw	zZuf		; Zufalls-Wert laden
		goto	Modus_18
		
Modus_161	movfw	INDF		; Dunkel-/Hell-Wert laden
		goto	Modus_18

		; ---------------------------
Modus_17	addwf	izUni,W		; + Offs
		movwf	FSR
		movfw	INDF		; Wert laden
Modus_18	movwf	iTemp3		; Wert retten
		movlw	aSoll		; Adr ZlrSoll
		addwf	izUni,W		; + Offs
		movwf	FSR
		movfw	iTemp3		; Wert zurueck
		movwf	INDF		; Wert speichern
		return

; ***************************************************************
; INITIALISIERUNG
; ***************************************************************
ProgBeg		M_Bank0			; Bank 0
		clrf	INTCON		; Alle Interrupts sperren
		M_Page0			; auf Rom-Page 0 schalten

		movlw	255
		movwf	PORTB		; Alle Ports auf 1 setzen,
		movwf	PORTC		; damit beim Setzen der Tris-Register
		movwf	PORTD		; definierte Zustaende am Port sind
		movwf	PORTE

		; -----------------------------------------------
		; STATUS (Bank 0)	Status-Register:
		; 7	Bank Sel IndAdr  0=Bank 0-1 (00-FFh) / 1=Bank 2-3 (100-1FFh)
		; 6-5	Bank Sel	00=Bank 0 ... 11=Bank 3
		; 4	TimeOut		0=WD off / 1=WD on
		; 3	Power Down Bit
		; 2	Z	Zero Bit
		; 1	DC	Digit Carry/Borrow# Bit
		; 0	C	Carry/Borrow# Bit
		;		C wird nur bei ADD, SUB, RLF und RRF veraendert
		;		ACHTUNG !!! Bei SUB ist C und DC invertiert !!!
		;		Berechnung: F/L minus W bzw. F/L plus 2cpl-W
		;		Wenn W >  L/F dann Carry=0
		;		Wenn W <= L/F dann Carry=1
		; -----------------------------------------------

		; -----------------------------------------------
		; USART Receive (Bank 0)
		; -----------------------------------------------
		movlw	10010000b	; (Bank 0) Receive Status ???
		movwf	RCSTA		; bit 7 = 1	SPEN (RC7=Rx und RC6=Tx)
					; bit 4 = 1	CREN

		; -----------------------------------------------
		; ADCON0 (Bank 0)	AD Control-Register 0 :
		; 7-6	A/D Conversion Clock	00=Fosc/2 / 01=Fosc/8 / 10=Fosc/32 / 11=RCosci
		; 5-3	Analog Channel		000=AN0 (RA0) ... 111=AN7
		; 2	A/D Conversion bit	0=DONE# / 1=GO
		; 1	-
		; 0	A/D On bit
		; -----------------------------------------------
		movlw	10000101b
		;	10		= Fosc/32
		;	  000		= Analog Channel 0
		;	     1		= GO
		;	       1	= ADON
		movwf	ADCON0

		M_Bank1			; Bank 1
		; -------------------------------------
		; RA0/ANA0	In/Ana
		; RA1/ANA1	In/Ana
		; RA2/ANA2	In/Ana
		; RA3/ANA3	In/Ana
		; RA4/T0CKI	ST-In
		; RA5/ANA4	In/Ana
		; RA6		---
		; RA7		---
		; -------------------------------------
		movlw	00111111b	; PortA 6 x Eingang
		movwf	TRISA

		; -----------------------------------------------
		; ADCON1 (Bank 1)	AD Control-Register 1 :
		; 7	Result Format	8 Bits in ADRESH / 2 Bits in ADRESL(7..6 000000)
		; 6-4	-
		; 3-0	Port Config
		;		an7  an6  an5    AN4  AN3  AN2  AN1  AN0  Ref+  Ref-
		;	Bits	RE2  RE1  RE0    RA5  RA3  RA2  RA1  RA0
		;	0000	 A    A    A      A    A    A    A    A   Vdd   Vss
		;	0001	 A    A    A      A   Ref+  A    A    A   RA3   Vss
		;  **	0010	 D    D    D      A    A    A    A    A   Vdd   Vss
		;	0011	 D    D    D      A   Ref+  A    A    A   RA3   Vss
		;	0100	 D    D    D      D    A    D    A    A   Vdd   Vss
		;	0101	 D    D    D      D   Ref+  D    A    A   RA3   Vss
		;	011x	 D    D    D      D    D    D    D    D   Vdd   Vss
		;	1000	 A    A    A      A   Ref+ Ref-  A    A   RA3   RA2
		;	1001	 D    D    A      A    A    A    A    A   Vdd   Vss
		;	1010	 D    D    A      A   Ref+  A    A    A   RA3   Vss
		;	1011	 D    D    A      A   Ref+ Ref-  A    A   RA3   RA2
		;	1100	 D    D    D      A   Ref+ Ref-  A    A   RA3   RA2
		;	1101	 D    D    D      D   Ref+ Ref-  A    A   RA3   RA2
		;	1110	 D    D    D      D    D    D    D    A   Vdd   Vss
		;	1111	 D    D    D      D   Ref+ Ref-  D    A   RA3   RA2
		; -----------------------------------------------
		movlw	00000010b
		;	0		= Result format = Left
		;	    0010	= Analog RA0-RA3 & RA5
		;			= Digital RA4,RE0-RE2
		movwf	ADCON1

		; -------------------------------------
		; RB0/INT	In	X3-0
		; RB1		In	X3-1
		; RB2		In	X3-2
		; RB3/PGM	In	X3-3 Programmierung
		; RB4		In	X3-4
		; RB5		In	X3-5
		; RB6/PGC	In	X3-6 Programmierung
		; RB7/PGD	In	X3-7 Programmierung
		; -------------------------------------
		movlw	11111111b	; 0=Ausgang / 1=Eingang
		movwf	TRISB

		; -------------------------------------
		; RC0/T1	Out	X2-0
		; RC1/PWM2	Out	X2-1
		; RC2/CCP1	Out	X2-2
		; RC3/SCL	Out	X2-3
		; RC4/SDA	Out	X2-4
		; RC5/SDO	Out	sio_Dir
		; RC6/TX	Out	sio_TxD
		; RC7/RX	In	sio_RxD
		; -------------------------------------
		movlw	11000000b	; 0=Ausgang / 1=unverändert
		andwf	TRISC

		; -------------------------------------
		; RD0...7	Out	pData0...7
		; -------------------------------------
		movlw	00000000b	; 0=Ausgang / 1=Eingang
		movwf	TRISD

		; -------------------------------------
		; RE0/RD	Out	X2-5
		; RE1/WR	Out	X2-6
		; RE2/CS	Out	X2-7
		; RE3...7	---	---
		; -------------------------------------
		movlw	11111000b	; 0=Ausgang / 1=unverändert
		andwf	TRISE

		; -----------------------------------------------
		; USART, Timer, IntEnable (Bank 1)
		; -----------------------------------------------
		movlw	00100100b	; (Bank 1) Transmit Status
		movwf	TXSTA		; bit 5 = 1	TXEN Transmitter Freigabe
					; bit 4 = 0	SYNC Asynchronous
					; bit 2 = 1	BRGH High Speed
					; bit 0 = 0	no Parity
		movlw	K_9600		; (Bank 1) BaudrateGenerator
		movwf	SPBRG
		movlw	K_01ms		; (Bank 1) 0,1 ms-Wert fuer Timer 2 setzen
		movwf	PR2
		movlw	00100010b	; (Bank 1) Interrupt enable = 1 / disable = 0
		movwf	PIE1		; bit 7 = 0	PSIPIE	Parallel Slave Port
					; bit 6 = 0	ADIE	A/D Converter
					; bit 5 = 1	RCIE	USART Receive
					; bit 4 = 0	TXIE	USART Transmit
					; bit 3 = 0	SSPIE	Synch Serial Port
					; bit 2 = 0	CCP1IE	CCP1
					; bit 1 = 1	TMR2IE	Timer 2
					; bit 0 = 0	TMR1IE	Timer 1
		bsf	PCON,NOT_POR	; (Bank 1) PowerOnReset Bit ruecksetzen (=1)

		; -----------------------------------------------
		; OPTION_REG (Bank 1)	Options-Register:
		; 7	PortB pull up	0=enabled / 1=disabled
		; 6	Int Edg Sel	0=fallende / 1=steigende Flanke an RB0/INT
		; 5	T0 Clk Source	0=intern CLKOUT / 1=extern RA4/T0CK
		; 4	T0 Src Edg Sel	0=Low to High / High to Low Increment an RA4/T0CK
		; 3	Prescaler Ass	0=TMR0 / 1=WDT
		; 2-0	Prescaler	000=1:2 ... 111=1:256 TMR0
		;			000=1:1 ... 111=1:128 WDT
		; -----------------------------------------------
		;	    0		; Prescaler Ass	= TMR0
		;	   0		; Flanke	= Low To High
		;	  0		; TMR0-Quelle	= intern CLKOUT
		;	 0		; Interrupt	= fallende Flanke
		;	1		; Pull Up	= disabled
		; -----------------------------------------------
 if Quarz == 0	; 10,000 MHz
		movlw	10000111b
		;	     111	; TMR-Prescaler	= 10,00 MHz : 4 = 0,400000 us * 256 = 0,1024 ms
		movwf	OPTION_REG
 endif		; -----------------------------------------------
 if Quarz == 1	; 10,240 MHz
		movlw	10000111b
		;	     111	; TMR-Prescaler	= 10,24 MHz : 4 = 0,390625 us * 256 = 0,1000 ms
		movwf	OPTION_REG
 endif		; -----------------------------------------------
 if Quarz == 5	; 4,9152 MHz
		movlw	10000110b
		;	     110	; TMR-Prescaler	= 4,9152 MHz : 4 = 0,813802083 us * 128 = 0,1042 ms
		movwf	OPTION_REG
 endif		; -----------------------------------------------
 if Quarz == 4	; 4,096 MHz
		movlw	10000110b
		;	     110	; TMR-Prescaler	= 4,096 MHz : 4 = 0,976562 us * 128 = 0,1250 ms
		movwf	OPTION_REG
 endif		; -----------------------------------------------

		M_Bank0			; Bank 0
		clrf	TMR0		; Timer 0 loeschen
		clrf	TMR1L		; Timer 1 loeschen
		clrf	TMR1H

		; -----------------------------------------------
		; T2CON	(Bank 0)	Timer 2 Control-Register:
		; 7	-
		; 6-3	Postscale	0000=1:1 ... 1111=1:16 Postscale
		; 2	TimerOnBit	1=On / 0=Off
		; 1-0	Prescale	00=1:1 / 01=1:4 / 1x=1:16
		;	(Fosc / 4 = 10,000  MHz / 4 = 2,500  MHz = 0,4000000 µs)
		;	(Fosc / 4 = 10,240  MHz / 4 = 2,560  MHz = 0,3906250 µs)
		;	(Fosc / 4 =  4,9152 MHz / 4 = 1,2288 MHz = 0,8138021 µs)
		;	(Fosc / 4 =  4,096  MHz / 4 = 1,024  MHz = 0,9765625 µs)
		;	(Fosc / 4 =  2,4576 MHz / 4 = 0,6144 MHz = 1,6276042 µs)
		; Timer 2 / PR2 (8 Bit) wird fuer 0,1 ms-Interrupt benutzt
		; -----------------------------------------------
 if Quarz == 0	; 10,000 MHz
K_9600		EQU	64		; Baudrate = 10,00 MHz / (16 * (64 + 1)) = 9615,384615 Baud
		movlw	00001100b
		movwf	T2CON
		;	     1		; TimerOn	= Ein
		;	      00	; Prescale	=   1 * 0,4    µs = 0,0004 ms
K_01ms		EQU	125		; PR2-Wert	= 125 * 0,0004 ms = 0,05 ms
 endif		;	 0001		; Postscale	=   2 * 0,05   ms = 0,1 ms
		; -----------------------------------------------
 if Quarz == 1	; 10,240 MHz
K_9600		EQU	65		; Baudrate = 10,24 MHz / (16 * (65 + 1)) = 9696,969697 Baud
		movlw	00001100b
		movwf	T2CON
		;	     1		; TimerOn	= Ein
		;	      00	; Prescale	=   1 * 0,390625    µs = 0,000390625 ms
K_01ms		EQU	128		; PR2-Wert	= 128 * 0,000390625 ms = 0,05 ms
 endif		;	 0001		; Postscale	=   2 * 0,05        ms = 0,1  ms
		; -----------------------------------------------
 if Quarz == 5	; 4,9152 MHz
K_9600		EQU	31		; Baudrate = 4,9152 MHz / (16 * (31 + 1)) = 9600 Baud
		movlw	00001100b
		movwf	T2CON
		;	     1		; TimerOn	= Ein
		;	      00	; Prescale	=  1 * 0,813802083    µs = 0,000813802083 ms
K_01ms		EQU	62		; PR2-Wert	= 62 * 0,000813802083 ms = 0,05045573 ms
 endif		;	 0001		; Postscale	=  2 * 0,05045573     ms = 0,100911458 ms
		; -----------------------------------------------
 if Quarz == 4	; 4,096 MHz
K_9600		EQU	25		; Baudrate = 4,096 MHz / (16 * (25 + 1)) = 9846,153846 Baud
		movlw	00001100b
		movwf	T2CON
		;	     1		; TimerOn	= Ein
		;	      00	; Prescale	=  1 * 0,9765625    µs = 0,0009765625 ms
K_01ms		EQU	51		; PR2-Wert	= 51 * 0,0009765625 ms = 0,04980469 ms
 endif		;	 0001		; Postscale	=  2 * 0,04980469   ms = 0,099609375 ms

		; -----------------------------------------------
		; T1CON (Bank 0)	Timer 1 Control-Register:
		; 7-6	-
		; 5-4	Prescale	11=1:8 / 10=1:4 / 01=1:2 / 00=1:1
		; 3	Osci Enable	1=enabled / 0=shut off
		; 2	Ext Clk Sync
		; 1	Clk Source	1=Extern (RC0) / 0=Intern
		; 0	TimerOnBit	1=On / 0=Off
		;	(Fosc / 4 = 10,24 MHz / 4 = 2,56 MHz = 0,390625 µs)
		; Timer 1 (16 Bit) wird fuer Warte-Zeiten benutzt
		; -----------------------------------------------
		movlw	00000101b
		;	  00		; Prescale	= 1:1 = 2,56 MHz = 0,390625 µs
		;	    0		; Osci		= shut off
		;	     1		; Ext Clk Sync	= Aus
		;	      0		; Clk Source	= Intern
		;	       0	; TimerOn	= Aus
		movwf	T1CON

	;;	bsf	PIR2,EEIF	; Flag Eeprom Write complete setzen
	;;				; notwendig da dieses Flag vor dem Schreiben abgefragt
	;;				; wird und unnoetige Wartezeit verkuerzt

		; ---------------------------
		; KaltStart: Ram neu initialisieren
		; ---------------------------
Init_1		call	ResRam		; Ram auf 0 setzen
Init_2		M_Page1
		call	p1ResCmdPuf	; Cmd-Puffer ruecksetzen
		call	p1RdEepKonf	; Konfig-Daten aus Eeprom lesen
		M_Page0

		decf	z01ms		; = 255
		movlw	9
		movwf	z1ms		; = 9
		incf	z10ms		; = 1
		incf	z100ms		; = 1

		D_PeriIntEin		; Periphere Interrupts freigeben
		D_GlobIntEin		; Alle Interrupts freigeben

		movlw	LOW(TxtSysErr_1)	; Kalt-Start
		movwf	AddrL
		movlw	HIGH(TxtSysErr_1)
		movwf	AddrH
		M_Page1			; Rom-Page 1
		call	p1PrnCrLf		; cr/lf senden
		call	p1PrnStrF		; String senden
		call	p1PrnCrLf		; cr/lf senden
		M_Page0			; Rom-Page 0

; ***************************************************************
; Hauptprogramm-Schleife
; ***************************************************************
Main
		decfsz	zZuf		; ... Zufallszahl - 1 = 0
		goto	Main_1

		movlw	100		; Zufallszahl = 100
		movwf	zZuf
		
		; ---------------------------
		; Kommando auswerten
		; ---------------------------
Main_1		movfw	RxDPos		; CmdByte empfangen ?
		subwf	RxDOfs,W
		skip_NZ			; ... ja
		goto	Main_5		; ... nein

		; ---------------------------
		; ACHTUNG !!!     Aufruf mit "goto Cmd_Ausw" und
		; Ruecksprung mit "goto Main_5" wegen StackTiefe
		; ---------------------------
		M_Page1			; Rom-Page 1
		goto	Cmd_Ausw	; RxD-Byte speichern, evtl. Cmd-Puffer auswerten

Main_5		goto	Main		; Hauptprogrammschleife

; ***************************************************************
; DIVERSE ROUTINEN
; ***************************************************************
; ===============================================================
; Ram indirekt loeschen
; ===============================================================
ResRam		movlw	RamBeg0		; Ram-Anfang
		movwf	FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		movlw	RamEnd0-RamBeg0	; Anzahl Adressen
		movwf	zUni
ResRam_3	clrf	INDF		; Ram indirekt loeschen
		incf	FSR
		decfsz	zUni		; ... Zaehler - 1 = 0
		goto	ResRam_3

		return

; ===============================================================
; EmpfangsByte im Ringpuffer ablegen
; ===============================================================
SioRxD		movfw	RCREG		; Empfangsbyte laden
		movwf	iTemp1		; und retten
		sublw	0x0D		; Return ?
		skip_NZ			; ... nein
		goto	SioRxD_1	; ... ja: Return speichern

		movfw	iTemp1
		sublw	0x0FF		; = 0x0FF ?
		skip_NZ			; ... nein
		return			; ... ja: verwerfen

		movfw	iTemp1
		sublw	0x1F		; Byte > 0x1F ?
		skip_NC			; ... ja
		return			; ... nein: verwerfen

SioRxD_1	movlw	LOW(RxDPuf)	; Puffer-Adresse
		addwf	RxDOfs,W	; plus Offset
		movwf	FSR		; fuer indirekte Adressierung
		bsf	STATUS,IRP	; Bank 2 / 3
		movfw	iTemp1		; EmpfangsByte laden
		movwf	INDF		; und im Puffer ablegen
		incf	RxDOfs		; Offset + 1
		movfw	RxDOfs
		sublw	RxDLen-1	; RxDOfs > RxDLen-1 ?
		skip_C			; ... nein
		clrf	RxDOfs		; ... ja: Offset = 0

		return

; ===============================================================
; Byte evtl. aus TxD-Puffer zum Senden in Transmit-Buffer ablegen
; Aufruf jede ms im Interrupt
; ===============================================================
SioTxD		movfw	TxDPos		; Byte zum Senden ?
		subwf	TxDOfs,W
		skip_NZ			; ... ja
		return			; ... nix zum Senden

		skip_1	PIR1,TXIF	; ... sendebereit
		return			; ... nicht sendebereit

		movlw	LOW(TxDPuf)	; Puffer-Adresse
		addwf	TxDOfs,W	; plus Offset
		movwf	FSR		; fuer indirekte Adressierung
		bsf	STATUS,IRP	; Bank 2 / 3
		movfw	INDF		; SendeByte aus Ringpuffer laden
		movwf	TXREG		; Byte senden (TXIF=0)
		incf	TxDOfs		; Offset + 1
		movfw	TxDOfs
		sublw	TxDLen-1	; TxDOfs > TxDLen-1 ?
		skip_C			; ... nein
		clrf	TxDOfs		; ... ja: Offset = 0

		movfw	TxDAnz		; 1 Byte weniger im TxD-Puffer
		skip_Z			; ... ist schon 0
		decf	TxDAnz		; ... sonst - 1

		return

; ***************************************************************
; ZEIT- UND WARTESCHLEIFEN
; ***************************************************************
; ---------------------------------------------------------------
; Timer 0 setzen und Interrupt-Flag ruecksetzen
; Eingang:	W	Timer-Wert
; ---------------------------------------------------------------
SetTimer0	movwf	TMR0		; Timer 0 setzen
		bcf	INTCON,T0IF	; Int-Flag ruecksetzen
		return

; ---------------------------------------------------------------
; Timer 0 setzen und den Ablauf von Timer 0 abwarten
; Eingang:	W	Timer-Wert
; ---------------------------------------------------------------
SetWaitTimer0	movwf	TMR0		; Timer 0 setzen
		bcf	INTCON,T0IF	; Int-Flag ruecksetzen

WaitTimer0	skip_1	INTCON,T0IF	; ... Timer ist abgelaufen
		goto	WaitTimer0	; ... sonst warten

		bcf	INTCON,T0IF	; Int-Flag ruecksetzen
		return

; ---------------------------------------------------------------
; Timer 1 starten
; Ablauf von Timer 1 abwarten
; ---------------------------------------------------------------
StartWaitTimer1	bcf	PIR1,TMR1IF	; Timer 1 Interrupt-Flag ruecksetzen
		M_movlf	00000101b,T1CON	; Prescaler 1:1, Timer 1 an

WaitTimer1	skip_1	PIR1,TMR1IF	; ... Timer ist abgelaufen
 		goto	WaitTimer1	; ... sonst warten

		return

; ###############################################################
; Programmspeicher Page 1 (0x0800...0x0FFF)
; ###############################################################
	org	0x0800

p1Reset		nop		; 00h Resetvektor
		goto	P1Init	; 01h Resetvektor
		goto	P1Init	; 02h Resetvektor
		goto	P1Init	; 03h Resetvektor

P1Isr		M_Page0		; auf Rom-Page 0 schalten
		goto	Interrupt

P1Init		M_Page0		; auf Rom-Page 0 schalten
		goto	ProgBeg

; ***************************************************************
; Kommando auswerten
; ***************************************************************
; ===============================================================
; Cmd-Puffer ruecksetzen
; ===============================================================
p1ResCmdPuf	M_Bank1
		clrf	CmdPuf
		M_Bank0
		clrf	CmdPos
		return

; ===============================================================
; RxD-Byte in Cmd-Puffer ablegen
; Bei CR Cmd-Puffer auswerten
; ===============================================================
Cmd_Ausw	call	p1GetRxDByte	; Byte aus RxD-Puffer laden

		; ---------------------------
		; Byte auf CR testen
		; ---------------------------
		movfw	CmdByte		; Byte laden
		sublw	0x0D		; CR ?
		skip_NZ			; ... nein
		goto	Cmd_Ret

		; ---------------------------
		; Byte auf Doppelpunkt testen
		; ---------------------------
		movfw	CmdByte		; Byte laden
		sublw	":"		; Doppelpunkt ?
		skip_NZ			; ... nein
		goto	Cmd_Ret

		; ---------------------------
		; Byte auf Semikolon testen
		; ---------------------------
		movfw	CmdByte		; Byte laden
		sublw	";"		; Semikolon ?
		skip_NZ			; ... nein
		goto	Cmd_Semi

		; ---------------------------
		; Kommentar verwerfen
		; ---------------------------
		skip_0	F_Semi		; ... kein Kommentar
		goto	Cmd_Ausw_E	; ... sonst verwerfen

		goto	Cmd_Ausw_1	; Byte speichern

		; ---------------------------
		; Bei CR Flag setzen und 0 speichern
		; ---------------------------
Cmd_Ret		bsf	F_Ret		; Kommando ausfuehren
		bcf	F_Semi		; Alles ruecksetzen
		clrf	CmdByte		; Null anfuegen
		goto	Cmd_Ausw_1

		; ---------------------------
		; Bei Semikolon Flag setzen
		; ---------------------------
Cmd_Semi	bsf	F_Semi		; Ab jetzt Kommentar, alles verwerfen
		goto	Cmd_Ausw_E

		; ---------------------------
		; Byte im Cmd-Puffer ablegen
		; ---------------------------
Cmd_Ausw_1	movlw	CmdPuf		; Cmd-Puffer-Adresse
		addwf	CmdPos,W	; plus Offset
		movwf	FSR		; fuer indirekte Adressierung
		bcf	STATUS,IRP	; Bank 0 / 1
		movfw	CmdByte		; Cmd-Byte laden
		movwf	INDF		; und im Cmd-Puffer ablegen
		movfw	CmdPos
		sublw	CmdLen-2	; CmdPos > CmdLen-2 ?
		skip_NC			; ... ja
		incf	CmdPos		; ... nein: Offset + 1

		; ---------------------------
		; Nach CR oder Doppelpunkt Kommando ausfuehren
		; ---------------------------
		skip_1	F_Ret		; ... es war CR / Doppelpunkt
		goto	Cmd_Ausw_E

		bcf	F_Ret		; Flag ruecksetzen
		clrf	CmdPos		; Offset = 0
		call	p1SuchA_Z	; Cmd-Buchstabe suchen
		skip_1	F_OK		; ... gefunden
		goto	PrnCmdErr_1	; ... nicht gefunden

		movlw	"?"		; Fragezeichen ?
		subwf	CmdByte,W
		skip_NZ			; ... nein
		goto	Cmd_Hilfe

		movlw	HIGH(TblCmd)
		movwf	PCLATH
		movlw	"A"		; Test auf 256 Byte Grenz-Überschreitung
		subwf	CmdByte,W	; Offset berechnen
		addlw	8		; + 8 Befehle
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		addwf	PCL,W		; + PCL
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		movlw	"A"		; Tabellen-Offset berechnen
		subwf	CmdByte,W	; F - W --> W
TblCmd		addwf	PCL
		goto	Cmd_Anal	; A	Analog-Eingaenge abfragen
		goto	PrnCmdErr_2	; B	
		goto	PrnCmdErr_2	; C	
		goto	Cmd_Dunk	; D	Dunkelheit
		goto	PrnCmdErr_2	; E	
		goto	PrnCmdErr_2	;*F	--	Fehler
		goto	PrnCmdErr_2	; G	
		goto	Cmd_Hell	; H	Helligkeit
		goto	Cmd_Inp		; I	Input Digital
		goto	PrnCmdErr_2	; J
		goto	PrnCmdErr_2	; K	
		goto	PrnCmdErr_2	; L	
		goto	Cmd_Modus	; M	Modus
		goto	PrnCmdErr_2	; N	Nummer bei RS485
		goto	Cmd_Outp	; O	Output Digital
		goto	Cmd_Hell	; P	Prozentwert
		goto	PrnCmdErr_2	; Q
		goto	PrnCmdErr_2	; R	
		goto	Cmd_Steps	; S	Steps Steigerung / Senkung
		goto	Cmd_Tempo	; T	Tempo
		goto	PrnCmdErr_2	; U	(Uhrzeit)
		goto	Cmd_Vers	; V	Version des Programms
		goto	Cmd_Werte	; W	Werte anzeigen
		goto	PrnCmdErr_2	; X	
		goto	PrnCmdErr_2	; Y
		goto	PrnCmdErr_2	; Z	

; ---------------------------------------------------------------
Cmd_Fertig	call	p1ResCmdPuf
Cmd_Ausw_E	M_Page0
		goto	Main_5

; ===============================================================
; FehlerMeldungen
; ===============================================================
PrnCmdErr_1	movlw	LOW(TxtCmdErr_1)	; Kommando fehlt
		movwf	AddrL
		movlw	HIGH(TxtCmdErr_1)
		goto	PrnCmdErr

PrnCmdErr_2	movlw	LOW(TxtCmdErr_2)	; Kommando falsch
		movwf	AddrL
		movlw	HIGH(TxtCmdErr_2)
		goto	PrnCmdErr

PrnCmdErr_3	movlw	LOW(TxtCmdErr_3)	; Parameter falsch
		movwf	AddrL
		movlw	HIGH(TxtCmdErr_3)
		goto	PrnCmdErr

PrnCmdErr_4	movlw	LOW(TxtCmdErr_4)	; Parameter zu klein
		movwf	AddrL
		movlw	HIGH(TxtCmdErr_4)
		goto	PrnCmdErr

PrnCmdErr_5	movlw	LOW(TxtCmdErr_5)	; Parameter zu gross
		movwf	AddrL
		movlw	HIGH(TxtCmdErr_5)
		goto	PrnCmdErr

PrnCmdErr_6	movlw	LOW(TxtCmdErr_6)	; Bestaetigung falsch
		movwf	AddrL
		movlw	HIGH(TxtCmdErr_6)
		goto	PrnCmdErr

PrnCmdErr_7	movlw	LOW(TxtCmdErr_7)	; frei
		movwf	AddrL
		movlw	HIGH(TxtCmdErr_7)
PrnCmdErr	movwf	AddrH
		call	p1PrnStrF		; String senden
		call	p1PrnCrLf		; cr/lf senden
		goto	Cmd_Fertig

; ===============================================================
; ? - Hilfe
; ===============================================================
Cmd_Hilfe	movlw	LOW(TxtHilfe)
		movwf	AddrL
		movlw	HIGH(TxtHilfe)
		movwf	AddrH
		call	p1PrnStrF
		goto	Cmd_Fertig

; ===============================================================
; A - Analog-Eingang abfragen	A
; ===============================================================
Cmd_Anal	call	p1Such0_9	; ParamNr suchen

		; ---------------------------
		; 4 Analog-Werte senden
		; ---------------------------
Cmd_Anal_7	bcf	F_Fmt		; Formatierung aus
		call	p1PrnAAnal
		call	p1PrnCrLf	; cr/lf senden
		goto	Cmd_Fertig

		; ---------------------------
		; "A 0 " und 4 Analog-Werte senden
		; ---------------------------
p1PrnAAnal	movlw	aAdw1		; Adr Akt Analog-Werte
		movwf	Temp1
		movlw	4		; 4 Param
		movwf	zUni
		movlw	"A"
		goto	p1PrnParam3

; ===============================================================
; D - Dunk		D 0 0...255 0...255 0...255 0...255 0...255 0...255 0...255 0...255
;			D 1...8 0...255
;			D 9 0...255	(fuer alle)
; ===============================================================
Cmd_Dunk	call	p1Such0_9	; ParamNr suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Dunk_7	; ... Param senden

		movwf	Para1		; ParamNr retten
		sublw	9		; ParamNr > 9 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5	; ... ja: Param zu gross

		bcf	F_Alle		; Erst mal loeschen
		movfw	Para1		; ParamNr zurueck
		skip_NZ			; ... > 0
		goto	Cmd_Dunk_0

		movfw	Para1		; ParamNr zurueck
		sublw	9		; ParamNr = 9 ?
		skip_NZ			; ... nein
		goto	Cmd_Dunk_09

		; ---------------------------
		; ParamNr 1...8
		; ---------------------------
		movlw	1		; 1 Param abfragen
		movwf	zUni
		decf	Para1,W		; Offset laden
		movwf	Temp1
		movwf	Temp2
		goto	Cmd_Dunk_1

		; ---------------------------
		; ParamNr 9
		; ---------------------------
Cmd_Dunk_09	bsf	F_Alle

		; ---------------------------
		; ParamNr 0 und 9
		; ---------------------------
Cmd_Dunk_0	movlw	8		; 8 Param abfragen
		movwf	zUni
		clrf	Temp1		; Offset 0
		clrf	Temp2
Cmd_Dunk_1	movlw	pDunk		; RamAdr
		addwf	Temp1
		movlw	LOW(EepDunk)	; EepAdr
		addwf	Temp2
		goto	Cmd_Dunk_3

		; ---------------------------
		; Naechste Schleife
		; ---------------------------
Cmd_Dunk_2	skip_1	F_Alle		; ... Alle den gleichen Param
		goto	Cmd_Dunk_3	; ... naechsten Param suchen

		movfw	Para1		; Param zurueck
		movwf	Para2
		goto	Cmd_Dunk_4

		; ---------------------------
		; Param suchen
		; ---------------------------
Cmd_Dunk_3	call	p1Such0_9	; Param suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Dunk_7	; ... Param senden

		movwf	Para1		; Param retten
		movwf	Para2

		; ---------------------------
		; Param speichern
		; ---------------------------
Cmd_Dunk_4	movfw	Temp1		; RamAdr zurueck
		movwf	FSR		; nach FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		movfw	Para2		; Param zurueck
		movwf	INDF		; und speichern
		incf	Temp1		; Naechste RamAdr

		movfw	Temp2		; EepAdr zurueck
		movwf	AddrL		; nach AddrL
		movfw	Para2		; Param zurueck
		call	p1WrEeprom	; und speichern
		incf	Temp2		; Naechste EepAdr

		decfsz	zUni		; ... Zaehler - 1 = 0
		goto	Cmd_Dunk_2	; ... Naechste Schleife

		; ---------------------------
		; Param senden
		; ---------------------------
Cmd_Dunk_7	bcf	F_Fmt		; Formatierung aus
		call	p1PrnDunk
		call	p1PrnCrLf	; cr/lf senden
		goto	Cmd_Fertig

; -------------------------------------------
; 8 Param senden
; -------------------------------------------
p1PrnDunk	movlw	pDunk		; Adresse
		movwf	Temp1
		movlw	8		; 8 Param
		movwf	zUni
		movlw	"D"
		goto	p1PrnParam3

; ===============================================================
; H - Hell		H 0 0...255 0...255 0...255 0...255 0...255 0...255 0...255 0...255
;			H 1...8 0...255
;			H 9 0...255	(fuer alle)
; ===============================================================
Cmd_Hell	call	p1Such0_9	; ParamNr suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Hell_7	; ... Param senden

		movwf	Para1		; ParamNr retten
		sublw	9		; ParamNr > 9 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5	; ... ja: Param zu gross

		bcf	F_Alle		; Erst mal loeschen
		movfw	Para1		; ParamNr zurueck
		skip_NZ			; ... > 0
		goto	Cmd_Hell_0

		movfw	Para1		; ParamNr zurueck
		sublw	9		; ParamNr = 9 ?
		skip_NZ			; ... nein
		goto	Cmd_Hell_09

		; ---------------------------
		; ParamNr 1...8
		; ---------------------------
		movlw	1		; 1 Param abfragen
		movwf	zUni
		decf	Para1,W		; Offset laden
		movwf	Temp1
		movwf	Temp2
		goto	Cmd_Hell_1

		; ---------------------------
		; ParamNr 9
		; ---------------------------
Cmd_Hell_09	bsf	F_Alle

		; ---------------------------
		; ParamNr 0 und 9
		; ---------------------------
Cmd_Hell_0	movlw	8		; 8 Param abfragen
		movwf	zUni
		clrf	Temp1		; Offset 0
		clrf	Temp2
Cmd_Hell_1	movlw	pHell		; RamAdr
		addwf	Temp1
		movlw	LOW(EepHell)	; EepAdr
		addwf	Temp2
		goto	Cmd_Hell_3

		; ---------------------------
		; Naechste Schleife
		; ---------------------------
Cmd_Hell_2	skip_1	F_Alle		; ... Alle den gleichen Param
		goto	Cmd_Hell_3	; ... naechsten Param suchen

		movfw	Para1		; Param zurueck
		movwf	Para2
		goto	Cmd_Hell_4

		; ---------------------------
		; Param suchen
		; ---------------------------
Cmd_Hell_3	call	p1Such0_9	; Param suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Hell_7	; ... Param senden

		movwf	Para1		; Param retten
		movwf	Para2

		; ---------------------------
		; Param speichern
		; ---------------------------
Cmd_Hell_4	movfw	Temp1		; RamAdr zurueck
		movwf	FSR		; nach FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		movfw	Para2		; Param zurueck
		movwf	INDF		; und speichern
		incf	Temp1		; Naechste RamAdr

		movfw	Temp2		; EepAdr zurueck
		movwf	AddrL		; nach AddrL
		movfw	Para2		; Param zurueck
		call	p1WrEeprom	; und speichern
		incf	Temp2		; Naechste EepAdr

		decfsz	zUni		; ... Zaehler - 1 = 0
		goto	Cmd_Hell_2	; ... Naechste Schleife

		; ---------------------------
		; Param senden
		; ---------------------------
Cmd_Hell_7	bcf	F_Fmt		; Formatierung aus
		call	p1PrnHell
		call	p1PrnCrLf	; cr/lf senden
		goto	Cmd_Fertig

; -------------------------------------------
; 8 Param senden
; -------------------------------------------
p1PrnHell	movlw	pHell		; Adresse
		movwf	Temp1
		movlw	8		; 8 Param
		movwf	zUni
		movlw	"H"
		goto	p1PrnParam3

; ===============================================================
; I - Input_digital	I (0...255)	(8 Bit senden
;			I 81		(Byte senden)
; ===============================================================
Cmd_Inp		call	p1Such0_9	; ParamNr suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Inp_7	; ... Param senden

		movwf	Para1		; ParamNr retten
		sublw	8		; ParamNr > 8 ?	(L - W)
		skip_NC			; ... ja
		goto	Cmd_Inp_7	; ... nein

		movfw	Para1		; ParamNr zurueck
		sublw	81		; ParamNr = 81 ?
		skip_NZ			; ... nein
		goto	Cmd_Inp_81	; ... ja: Byte senden

		; ---------------------------
		; 8 Bit senden
		; ---------------------------
Cmd_Inp_7	bcf	F_Fmt		; Formatierung aus
		call	p1PrnInpBit
		call	p1PrnCrLf	; cr/lf senden
		goto	Cmd_Fertig

		; ---------------------------
		; "I 0 " und 8 Bit senden
		; ---------------------------
p1PrnInpBit	movfw	aInp1		; Output Digital Bits
		movwf	Temp1
		movlw	"I"
		call	p1PrnChr	; Cmd senden
		call	p1PrnSpc	; Leerzeichen senden
		movlw	"0"
		call	p1PrnChr	; "0" senden
		movlw	8		; 8 Bit
		movwf	zUni
p1PrnIInp_72	call	p1PrnSpc	; Leerzeichen senden
		movfw	Temp1		; Bits zurueck
		andlw	0x01		; Bit 0
		call	p1PrnZif1	; und senden
		rrf	Temp1		; Bits nach rechts schieben
		decfsz	zUni		; ... Zaehler - 1 = 0
		goto	p1PrnIInp_72	; ... Naechstes Bit

		return

		; ---------------------------
		; 1 Byte senden
		; ---------------------------
Cmd_Inp_81	bcf	F_Fmt		; Formatierung aus
		call	p1PrnIInpByt
		call	p1PrnCrLf	; cr/lf senden
		goto	Cmd_Fertig

		; ---------------------------
		; "I 81 " und 1 Byte senden
		; ---------------------------
p1PrnIInpByt	movlw	"I"
		call	p1PrnChr	; Cmd senden
		call	p1PrnSpc	; Leerzeichen senden
		movlw	"8"
		call	p1PrnChr	; "8" senden
		movlw	"1"
		call	p1PrnChr	; "1" senden
		call	p1PrnSpc	; Leerzeichen senden
		movfw	aInp1		; Output Digital Byte
		goto	p1PrnZif3	; senden

; ===============================================================
; O - Output_digital	O 0 0...2 0...2 0...2 0...2 0...2 0...2 0...2 0...2
;			O 1...8 0...2
;			O 99  0...2
; ===============================================================
Cmd_Outp	call	p1Such0_9	; ParamNr suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_OutpBit	; ... Param senden

		movwf	Para1		; ParamNr retten
		sublw	8		; ParamNr > 8 ?	(L - W)
		skip_NC			; ... ja
		goto	Cmd_Outp_4	; ... nein

		movfw	Para1		; ParamNr zurueck
		sublw	99		; ParamNr = 99 ?
		skip_NZ			; ... nein
		goto	Cmd_Outp_4	; ... ja

		movfw	Para1		; ParamNr zurueck
		sublw	81		; ParamNr = 81 ?
		skip_Z			; ... ja
		goto	PrnCmdErr_5	; ... nein: Param zu gross

Cmd_Outp_4	call	p1Such0_9	; Param suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Outp_78	; ... Param senden

		movwf	Para2		; Param retten
		movfw	Para1		; ParamNr zurueck = 0 ?
		skip_NZ			; ... nein
		goto	Cmd_Outp_0	; ... ja

		sublw	99		; ParamNr = 99 ?
		skip_NZ			; ... nein
		goto	Cmd_Outp_99	; ... ja

		movfw	Para1		; ParamNr zurueck
		sublw	81		; ParamNr = 81 ?
		skip_Z			; ... ja
		goto	Cmd_Outp_1	; ... nein

		; ---------------------------
		; Byte-Param
		; ---------------------------
Cmd_Outp_81	movfw	Para2		; Byte-Param zurueck
		movwf	aOutp1		; Byte speichern
		goto	Cmd_OutpByt	; Byte senden

		; ---------------------------
		; 1 Param fuer Alle
		; ---------------------------
Cmd_Outp_99	movfw	Para2		; Param = 0 ?
		skip_NZ			; ... nein
		goto	Cmd_Outp_990	; ... ja: Alle Bits loeschen

		sublw	1		; Param = 1 ?
		skip_NZ			; ... nein
		goto	Cmd_Outp_991	; ... ja: Alle Bits setzen

		comf	aOutp1		; Komplement
		goto	Cmd_OutpBit	; Param senden

Cmd_Outp_990	clrf	aOutp1		; Alle Bits loeschen
		goto	Cmd_OutpBit	; Param senden

Cmd_Outp_991	movlw	0xFF		; Alle Bits setzen
		movwf	aOutp1
		goto	Cmd_OutpBit	; 8 Bits senden

		; ---------------------------
		; 8 Param suchen und speichern
		; Para1		ParamNr
		; Para2		1. Param
		; ---------------------------
Cmd_Outp_0	movfw	aOutp1		; Output Digital Bits
		movwf	Temp1		; nach Temp1
		movlw	8		; 8 Param
		movwf	zUni
		goto	Cmd_Outp_02

Cmd_Outp_01	call	p1Such0_9	; Param suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Outp_07	; ... Bits auf alten Platz

		movwf	Para2		; Param retten
		rrf	Temp1		; Bit rechts schieben
		rrf	Temp2
Cmd_Outp_02	movfw	Para2		; Param zurueck = 0 ?
		skip_NZ			; ... nein
		goto	Cmd_Outp_03	; ... ja: Bit loeschen

		sublw	1		; Param = 1 ?
		skip_NZ			; ... nein
		goto	Cmd_Outp_04	; ... ja: Bit setzen

		skip_1	Temp1,0		; ... Bit = 1
		goto	Cmd_Outp_04	; ... sonst Bit setzen

Cmd_Outp_03	bcf	Temp1,0		; Bit loeschen
		goto	Cmd_Outp_05

Cmd_Outp_04	bsf	Temp1,0		; Bit setzen

Cmd_Outp_05	decfsz	zUni		; ... Zaehler - 1 = 0
		goto	Cmd_Outp_01	; ... Naechster Param

Cmd_Outp_07	comf	zUni,W		; 8 - zUni
		addlw	8+1
		movfw	zUni		; Zaehler neu setzen

Cmd_Outp_08	decf	zUni		; Zaehler - 1 = 0 ?
		skip_NZ			; ... nein
		goto	Cmd_Outp_09	; ... ja

		rlf	Temp2		; Bit auf alte Pos schieben
		rlf	Temp1
		goto	Cmd_Outp_08

Cmd_Outp_09	movfw	Temp1		; Bits zurueck
		movwf	aOutp1		; und speichern
		goto	Cmd_OutpBit	; Alle Param senden

		; ---------------------------
		; 1 Bit speichern
		; Para1		BitNr 1...8
		; Para2		Param
		; ---------------------------
Cmd_Outp_1	movfw	aOutp1		; Bits
		movwf	Temp1
		call	UpdBit		; Bit aktualisieren
		movfw	Temp1		; Akt Bits
		movwf	aOutp1		; speichern

		; ---------------------------
		; 8 Bit senden
		; ---------------------------
Cmd_OutpBit	bcf	F_Fmt		; Formatierung aus
		call	p1PrnOutpBit
		call	p1PrnCrLf	; cr/lf senden
		goto	Cmd_Fertig

		; ---------------------------
		; "O 0 " und 8 Bit senden
		; ---------------------------
p1PrnOutpBit	movfw	aOutp1		; Output Digital Bits
		movwf	Temp1
		movlw	"O"
		call	p1PrnChr	; Cmd senden
		call	p1PrnSpc	; Leerzeichen senden
		movlw	"0"
		call	p1PrnChr	; "0" senden
		movlw	8		; 8 Bit
		movwf	zUni
p1PrnOutpBit_1	call	p1PrnSpc	; Leerzeichen senden
		movfw	Temp1		; Bits zurueck
		andlw	0x01		; Bit 0
		call	p1PrnZif1	; und senden
		rrf	Temp1		; Bits nach rechts schieben
		decfsz	zUni		; ... Zaehler - 1 = 0
		goto	p1PrnOutpBit_1	; ... Naechstes Bit

		return

		; ---------------------------
		; 1 Byte oder 8 Bit senden
		; ---------------------------
Cmd_Outp_78	movfw	Para1		; ParamNr zurueck
		sublw	81		; ParamNr = 81 ?
		skip_Z			; ... ja
		goto	Cmd_OutpBit	; ... nein

		; ---------------------------
		; 1 Byte senden
		; ---------------------------
Cmd_OutpByt	bcf	F_Fmt		; Formatierung aus
		call	p1PrnOOutpByt
		call	p1PrnCrLf	; cr/lf senden
		goto	Cmd_Fertig

		; ---------------------------
		; "O 81 " und 1 Byte senden
		; ---------------------------
p1PrnOOutpByt	movlw	"O"
		call	p1PrnChr	; Cmd senden
		call	p1PrnSpc	; Leerzeichen senden
		movlw	"8"
		call	p1PrnChr	; "8" senden
		movlw	"1"
		call	p1PrnChr	; "1" senden
		call	p1PrnSpc	; Leerzeichen senden
		movfw	aOutp1		; Output Digital Byte
		goto	p1PrnZif3	; senden

; ===============================================================
; Bit aktualisieren
; Eingang:	Para1		Bit-Nr 1...8
;		Para2		Bit-Zustand 0...2
;		Temp1		Byte
; Ausgang:	Temp1		Byte mit akt Bit
; Benutzt:	zUni, Temp2
; ===============================================================
UpdBit		movfw	Para1		; AusgNr laden
		movwf	zUni		; und Zaehler setzen

UpdBit_1	decf	zUni		; Zaehler - 1 = 0 ?
		skip_NZ			; ... nein
		goto	UpdBit_2	; ... ja

		rrf	Temp1		; Bit rechts schieben
		rrf	Temp2
		goto	UpdBit_1

UpdBit_2	movfw	Para2		; Param = 0 ?
		skip_NZ			; ... nein
		goto	UpdBit_3	; ... ja: Bit loeschen

		sublw	1		; Param = 1 ?
		skip_NZ			; ... nein
		goto	UpdBit_4	; ... ja: Bit setzen

		skip_1	Temp1,0		; ... Bit = 1
		goto	UpdBit_4	; ... sonst Bit setzen

UpdBit_3	bcf	Temp1,0		; Bit loeschen
		goto	UpdBit_5

UpdBit_4	bsf	Temp1,0		; Bit setzen
		goto	UpdBit_5

UpdBit_5	movfw	Para1		; AusgNr laden
		movwf	zUni		; und Zaehler setzen

UpdBit_6	decf	zUni		; Zaehler - 1 = 0 ?
		skip_NZ			; ... nein
		goto	UpdBit_9	; ... ja

		rlf	Temp2		; Bit auf alte Pos schieben
		rlf	Temp1
		goto	UpdBit_6

UpdBit_9	return

; ===============================================================
; M - Modus		M 0 1...16 1...16 1...16 1...16 1...16 1...16 1...16 1...16
;			M 1...8 1...16
;			M 9 1...16	(fuer alle)
; ===============================================================
Cmd_Modus	call	p1Such0_9	; ParamNr suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Modus_7	; ... Param senden

		movwf	Para1		; ParamNr retten
		sublw	9		; ParamNr > 9 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5	; ... ja: Param zu gross

		bcf	F_Alle		; Erst mal loeschen
		movfw	Para1		; ParamNr zurueck
		skip_NZ			; ... > 0
		goto	Cmd_Modus_0

		movfw	Para1		; ParamNr zurueck
		sublw	9		; ParamNr = 9 ?
		skip_NZ			; ... nein
		goto	Cmd_Modus_09

		; ---------------------------
		; ParamNr 1...8
		; ---------------------------
		movlw	1		; 1 Param abfragen
		movwf	zUni
		decf	Para1,W		; Offset laden
		movwf	Temp1
		movwf	Temp2
		goto	Cmd_Modus_1

		; ---------------------------
		; ParamNr 9
		; ---------------------------
Cmd_Modus_09	bsf	F_Alle

		; ---------------------------
		; ParamNr 0 und 9
		; ---------------------------
Cmd_Modus_0	movlw	8		; 8 Param abfragen
		movwf	zUni
		clrf	Temp1		; Offset 0
		clrf	Temp2
Cmd_Modus_1	movlw	pModus		; RamAdr
		addwf	Temp1
		movlw	LOW(EepModus)	; EepAdr
		addwf	Temp2
		goto	Cmd_Modus_3

		; ---------------------------
		; Naechste Schleife
		; ---------------------------
Cmd_Modus_2	skip_1	F_Alle		; ... Alle den gleichen Param
		goto	Cmd_Modus_3	; ... naechsten Param suchen

		movfw	Para1		; Param zurueck
		movwf	Para2
		goto	Cmd_Modus_4

		; ---------------------------
		; Param suchen
		; ---------------------------
Cmd_Modus_3	call	p1Such0_9	; Param suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Modus_7	; ... Param senden

		movwf	Para1		; Param retten
		movwf	Para2
		movfw	Para2		; Param zurueck > 0 ?
		skip_NZ			; ... ja
		goto	PrnCmdErr_4	; ... Param zu klein

		sublw	16		; Param <= 16 ?
		skip_C			; ... ja
		goto	PrnCmdErr_5	; ... nein: Param zu gross

		; ---------------------------
		; Param speichern
		; ---------------------------
Cmd_Modus_4	decf	Para2		; Param - 1
		movfw	Temp1		; RamAdr zurueck
		call	WrModus		; Modus speichern
		incf	Temp1		; Naechste RamAdr

		movfw	Temp2		; EepAdr zurueck
		movwf	AddrL		; nach AddrL
		movfw	Para2		; Param zurueck
		call	p1WrEeprom
		incf	Temp2		; Naechste EepAdr

		decfsz	zUni		; ... Zaehler - 1 = 0
		goto	Cmd_Modus_2	; ... Naechste Schleife

		; ---------------------------
		; Param senden
		; ---------------------------
Cmd_Modus_7	bcf	F_Fmt		; Formatierung aus
		call	p1PrnModus
		call	p1PrnCrLf	; cr/lf senden
		goto	Cmd_Fertig

; -------------------------------------------
; 8 Param senden
; -------------------------------------------
p1PrnModus	movlw	pModus		; Adresse
		movwf	Temp1
		movlw	1		; Bits 3...0
		movwf	Temp2
		movlw	8		; 8 Param
		movwf	zUni
		movlw	"M"
		goto	p1PrnParam3X

; ---------------------------------------------------------------
; Modus-Bits im Ram speichern
; ---------------------------------------------------------------
WrModus		movwf	FSR		; Adr nach FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		movlw	0x0F		; Nur Bits 3...0
		andwf	Para2
		movfw	INDF		; Akt Byte laden
		andlw	0xF0		; Bits 3...0 loeschen
		iorwf	Para2,W		; Param setzen
		movwf	INDF		; und speichern
		movwf	Para2		; Fuer Eeprom retten
		return

; ===============================================================
; S - Steps		S 0 1...16 1...16 1...16 1...16 1...16 1...16 1...16 1...16
;			S 1...8 1...16
;			S 9 1...16	(fuer alle)
; ===============================================================
Cmd_Steps	call	p1Such0_9	; ParamNr suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Steps_7	; ... Param senden

		movwf	Para1		; ParamNr retten
		sublw	9		; ParamNr > 9 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5	; ... ja: Param zu gross

		bcf	F_Alle		; Erst mal loeschen
		movfw	Para1		; ParamNr zurueck
		skip_NZ			; ... > 0
		goto	Cmd_Steps_0

		movfw	Para1		; ParamNr zurueck
		sublw	9		; ParamNr = 9 ?
		skip_NZ			; ... nein
		goto	Cmd_Steps_09

		; ---------------------------
		; ParamNr 1...8
		; ---------------------------
		movlw	1		; 1 Param abfragen
		movwf	zUni
		decf	Para1,W		; Offset laden
		movwf	Temp1
		movwf	Temp2
		goto	Cmd_Steps_1

		; ---------------------------
		; ParamNr 9
		; ---------------------------
Cmd_Steps_09	bsf	F_Alle

		; ---------------------------
		; ParamNr 0 und 9
		; ---------------------------
Cmd_Steps_0	movlw	8		; 8 Param abfragen
		movwf	zUni
		clrf	Temp1		; Offset 0
		clrf	Temp2
Cmd_Steps_1	movlw	pModus		; RamAdr
		addwf	Temp1
		movlw	LOW(EepModus)	; EepAdr
		addwf	Temp2
		goto	Cmd_Steps_3

		; ---------------------------
		; Naechste Schleife
		; ---------------------------
Cmd_Steps_2	skip_1	F_Alle		; ... Alle den gleichen Param
		goto	Cmd_Steps_3	; ... naechsten Param suchen

		movfw	Para1		; Param zurueck
		movwf	Para2
		goto	Cmd_Steps_4

		; ---------------------------
		; Param suchen
		; ---------------------------
Cmd_Steps_3	call	p1Such0_9	; Param suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Steps_7	; ... Param senden

		movwf	Para1		; Param retten
		movwf	Para2
		movfw	Para2		; Param zurueck > 0 ?
		skip_NZ			; ... ja
		goto	PrnCmdErr_4	; ... Param zu klein

		sublw	16		; Param <= 16 ?
		skip_C			; ... ja
		goto	PrnCmdErr_5	; ... nein: Param zu gross

		; ---------------------------
		; Param speichern
		; ---------------------------
Cmd_Steps_4	decf	Para2		; Param - 1
		movfw	Temp1		; RamAdr zurueck
		call	WrSteps		; Steps speichern
		incf	Temp1		; Naechste RamAdr

		movfw	Temp2		; EepAdr zurueck
		movwf	AddrL		; nach AddrL
		movfw	Para2		; Param zurueck
		call	p1WrEeprom
		incf	Temp2		; Naechste EepAdr

		decfsz	zUni		; ... Zaehler - 1 = 0
		goto	Cmd_Steps_2	; ... Naechste Schleife

		; ---------------------------
		; Param senden
		; ---------------------------
Cmd_Steps_7	bcf	F_Fmt		; Formatierung aus
		call	p1PrnSteps
		call	p1PrnCrLf	; cr/lf senden
		goto	Cmd_Fertig

; -------------------------------------------
; 8 Param senden
; -------------------------------------------
p1PrnSteps	movlw	pModus		; Adresse
		movwf	Temp1
		movlw	2		; Bits 7...4
		movwf	Temp2
		movlw	8		; 8 Param
		movwf	zUni
		movlw	"S"
		goto	p1PrnParam3X

; ---------------------------------------------------------------
; Step-Bits im Ram speichern
; ---------------------------------------------------------------
WrSteps		movwf	FSR		; Adr nach FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		swapf	Para2		; Bits 3...0 nach 7...4
		movlw	0xF0		; Nur Bits 7...4
		andwf	Para2
		movfw	INDF		; Akt Byte laden
		andlw	0x0F		; Bits 7...4 loeschen
		iorwf	Para2,W		; Param setzen
		movwf	INDF		; und speichern
		movwf	Para2		; Fuer Eeprom retten
		return

; ===============================================================
; T - Tempo		T 0 0...255 0...255 0...255 0...255 0...255 0...255 0...255 0...255
;			T 1...8 0...255
;			T 9 0...255	(fuer alle)
; ===============================================================
Cmd_Tempo	call	p1Such0_9	; ParamNr suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Tempo_7	; ... Param senden

		movwf	Para1		; ParamNr retten
		sublw	9		; ParamNr > 9 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5	; ... ja: Param zu gross

		bcf	F_Alle		; Erst mal loeschen
		movfw	Para1		; ParamNr zurueck
		skip_NZ			; ... > 0
		goto	Cmd_Tempo_0

		movfw	Para1		; ParamNr zurueck
		sublw	9		; ParamNr = 9 ?
		skip_NZ			; ... nein
		goto	Cmd_Tempo_09

		; ---------------------------
		; ParamNr 1...8
		; ---------------------------
		movlw	1		; 1 Param abfragen
		movwf	zUni
		decf	Para1,W		; Offset laden
		movwf	Temp1
		movwf	Temp2
		goto	Cmd_Tempo_1

		; ---------------------------
		; ParamNr 9
		; ---------------------------
Cmd_Tempo_09	bsf	F_Alle

		; ---------------------------
		; ParamNr 0 und 9
		; ---------------------------
Cmd_Tempo_0	movlw	8		; 8 Param abfragen
		movwf	zUni
		clrf	Temp1		; Offset 0
		clrf	Temp2
Cmd_Tempo_1	movlw	pTempo		; RamAdr
		addwf	Temp1
		movlw	LOW(EepTempo)	; EepAdr
		addwf	Temp2
		goto	Cmd_Tempo_3

		; ---------------------------
		; Naechste Schleife
		; ---------------------------
Cmd_Tempo_2	skip_1	F_Alle		; ... Alle den gleichen Param
		goto	Cmd_Tempo_3	; ... naechsten Param suchen

		movfw	Para1		; Param zurueck
		movwf	Para2
		goto	Cmd_Tempo_4

		; ---------------------------
		; Param suchen
		; ---------------------------
Cmd_Tempo_3	call	p1Such0_9	; Param suchen
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Tempo_7	; ... Param senden

		movwf	Para1		; Param retten
		movwf	Para2

		; ---------------------------
		; Param speichern
		; ---------------------------
Cmd_Tempo_4	movfw	Temp1		; RamAdr zurueck
		movwf	FSR		; nach FSR
		bcf	STATUS,IRP	; Bank 0 / 1
		movfw	Para2		; Param zurueck
		movwf	INDF		; und speichern
		incf	Temp1		; Naechste RamAdr

		movfw	Temp2		; EepAdr zurueck
		movwf	AddrL		; nach AddrL
		movfw	Para2		; Param zurueck
		call	p1WrEeprom	; und speichern
		incf	Temp2		; Naechste EepAdr

		decfsz	zUni		; ... Zaehler - 1 = 0
		goto	Cmd_Tempo_2	; ... Naechste Schleife

		; ---------------------------
		; Param senden
		; ---------------------------
Cmd_Tempo_7	bcf	F_Fmt		; Formatierung aus
		call	p1PrnTempo
		call	p1PrnCrLf	; cr/lf senden
		goto	Cmd_Fertig

; -------------------------------------------
; 8 Param senden
; -------------------------------------------
p1PrnTempo	movlw	pTempo		; Adresse
		movwf	Temp1
		movlw	8		; 8 Param
		movwf	zUni
		movlw	"T"
		goto	p1PrnParam3

; ===============================================================
; V - Version des Programms
; ===============================================================
Cmd_Vers	movlw	LOW(TxtVers)
		movwf	AddrL
		movlw	HIGH(TxtVers)
		movwf	AddrH
		call	p1PrnStrF
		goto	Cmd_Fertig

; ===============================================================
; W - Werte
; ===============================================================
Cmd_Werte	movlw	LOW(TxtWerte)
		movwf	AddrL
		movlw	HIGH(TxtWerte)
		movwf	AddrH
		call	p1PrnStrF	; String aus Flash-Speicher senden

		bsf	F_Fmt		; Formatierung ein

		call	p1PrnOutpBit	; Output Digital
		call	p1PrnCrLf	; cr/lf
		call	p1PrnInpBit	; Input Digital
		call	p1PrnCrLf	; cr/lf
		call	p1PrnAAnal	; Analog-Input
		call	p1PrnCrLf	; cr/lf

		call	p1PrnModus	; Modus
		call	p1PrnCrLf	; cr/lf
		call	p1PrnSteps	; Steps
		call	p1PrnCrLf	; cr/lf
		call	p1PrnTempo	; Tempo
		call	p1PrnCrLf	; cr/lf
		call	p1PrnHell	; Helligkeit
		call	p1PrnCrLf	; cr/lf
		call	p1PrnDunk	; Dunkelheit
		call	p1PrnCrLf	; cr/lf

	;;	call	p1PrnTrenn	; Trennzeichen
		goto	Cmd_Vers	; !!! Als Letztes !!!

; ===============================================================
; Cmd-Buchstabe, 0 und 8 Parameter 3-stellig senden
; Eingang:	W		Cmd-Buchstabe
;		Temp1		Adr Param
;		zUni		Anz Param
;	X	(Temp2		Bit-Maske)
; ===============================================================
p1PrnParam3	clrf	Temp2		; Keine Bit-Maske
p1PrnParam3X	call	p1PrnChr	; Cmd senden
		call	p1PrnSpc	; Leerzeichen senden
		movlw	"0"
		call	p1PrnChr	; "0" senden
p1PrnParam3_1	call	p1PrnSpc	; Leerzeichen senden
		movfw	Temp1		; Adr zurueck
		movwf	FSR		; nach FSR
		bcf	STATUS,IRP	; Bank 0 / 1

		movlw	HIGH(TblBitMani)
		movwf	PCLATH
		movfw	Temp2		; Test auf 256 Byte Grenz-Überschreitung
		addwf	PCL,W
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		addlw	6		; + 6 Befehle
		skip_NC			; ... kein Uebertrag
		incf	PCLATH		; ... PCH + 1

		movfw	Temp2		; Routinen-Nr
		andlw	0x03		; Nur Bits 0...1
TblBitMani	addwf	PCL
		goto	BitMani_0
		goto	BitMani_1
		goto	BitMani_2
		goto	BitMani_3

		; ---------------------------
BitMani_0	movfw	INDF		; Param laden
		goto	p1PrnParam3_2

		; ---------------------------
BitMani_1	movfw	INDF		; Param laden
		andlw	0x0F		; Nur Bits 3...0
		addlw	1
		goto	p1PrnParam3_2

		; ---------------------------
BitMani_2	swapf	INDF,W		; Param laden Bits 7...4 => 3...0
		andlw	0x0F		; Nur Bits 3...0
		addlw	1
		goto	p1PrnParam3_2

		; ---------------------------
BitMani_3	movfw	INDF		; Param laden
		addlw	1

		; ---------------------------
p1PrnParam3_2	call	p1PrnZif3	; und senden
		incf	Temp1		; Naechste Adr
		decfsz	zUni		; ... Zaehler - 1 = 0
		goto	p1PrnParam3_1	; ... Naechster Param

		return

; ===============================================================
; Buchstaben suchen
; Eingang:	CmdPos		Cmd-Position
; Ausgang:	W & CmdByte	Ascii-Buchstabe A...Z
;		F_OK		0=kein Buchstabe / 1=Buchstabe gefunden
;		F_CmdE		1=Cmd-Puffer zu Ende
; ===============================================================
p1SuchA_Z	bcf	F_OK		; Erst mal nix gefunden
p1SuchA_Z_1	call	p1GetCmdByte	; Cmd-Byte aus Cmd-Puffer laden
		skip_0	F_CmdE		; ... Cmd-Puffer noch nicht zu Ende
		retlw	0x0FF

		movfw	CmdByte		; Cmd-Byte zurueck
		sublw	"?"		; = "?"
		skip_NZ			; ... nein
		goto	p1SuchA_Z__	; ... ja: Cmd gefunden

		movfw	CmdByte		; Cmd-Byte zurueck
		sublw	"A"-1		; > "A"-1
		skip_NC			; ... ja
		goto	p1SuchA_Z_1	; ... nein: Naechster

		movfw	CmdByte		; Cmd-Byte zurueck
		sublw	"Z"		; > "Z"
		skip_NC			; ... ja
		goto	p1SuchA_Z_Ok	; ... nein: A...Z

		movfw	CmdByte		; Cmd-Byte zurueck
		sublw	"a"-1		; > "a"-1
		skip_NC			; ... ja
		goto	p1SuchA_Z_1	; ... nein: Naechster

		movfw	CmdByte		; Cmd-Byte zurueck
		sublw	"z"		; > "z"
		skip_C			; ... nein
		goto	p1SuchA_Z_1	; ... ja: Naechster

p1SuchA_Z_Ok	movfw	CmdByte		; Cmd-Byte zurueck
		andlw	01011111b	; Klein nach Gross
		movwf	CmdByte
p1SuchA_Z__	bsf	F_OK		; OK
		return

; ===============================================================
; Ziffer suchen (0...255)
; Eingang:	CmdPos		Cmd-Position
; Ausgang:	W & CmdByte	Wert 0...255
;		F_OK		0=keine Ziffern / 1=Ziffern gefunden
;		F_CmdE		1=Cmd-Puffer zu Ende
; ===============================================================
p1Such0_9	bcf	F_OK		; Erst mal nix gefunden
		clrf	MathArgA0	; Wert erst mal 0

		; ---------------------------
		; 1. Ziffer suchen
		; ---------------------------
p1Such0_9_1	call	p1GetCmdByte	; Cmd-Byte aus Cmd-Puffer laden
		skip_0	F_CmdE		; ... Cmd-Puffer noch nicht zu Ende
		retlw	0x0FF

		movfw	CmdByte
		sublw	"0"-1		; > "0"-1
		skip_NC			; ... ja
		goto	p1Such0_9_1	; ... nein: Naechster

		movfw	CmdByte
		sublw	"9"		; > "9"
		skip_C			; ... nein
		goto	p1Such0_9_1	; ... ja: Naechster

		; ---------------------------
		; Bisheriger Wert * 10
		; Neuen Wert addieren
		; ---------------------------
p1Such0_9_2	call	p1Mulu0810	; Bisheriger Wert * 10
		movfw	MathErgA1	; HighByte = 0 ?
		skip_Z			; ... ja
		goto	PrnCmdErr_5	; Wert zu gross

		movlw	"0"		; F - W
		subwf	CmdByte,W	; Ascii nach Bin
		addwf	MathErgA0,W	; Bisheriger Wert + neuer Wert
		movwf	MathArgA0	; und retten
		skip_NC			; ... kein Übertrag
		goto	PrnCmdErr_5	; Wert zu gross

		; ---------------------------
		; Naechste Ziffer suchen
		; ---------------------------
		call	p1GetCmdByte	; Cmd-Byte aus Cmd-Puffer laden
		skip_0	F_CmdE		; ... Cmd-Puffer noch nicht zu Ende
		goto	p1Such0_9_Ok	; ... ja: Ende

		movfw	CmdByte
		sublw	"0"-1		; > "0"-1
		skip_NC			; ... ja
		goto	p1Such0_9_Ok	; ... nein: Ende

		movfw	CmdByte
		sublw	"9"		; > "9"
		skip_NC			; ... ja
		goto	p1Such0_9_2	; ... nein: addieren

p1Such0_9_Ok	bsf	F_OK		; OK
		movfw	MathArgA0	; Wert nach W & CmdByte
		movwf	CmdByte
		return

; ===============================================================
; Byte aus Cmd-Puffer laden
; Eingang:	CmdPos		Cmd-Position
; Ausgang:	CmdByte		Byte
;		F_CmdE		1=Cmd-Puffer Ende
; Benutzt:	FSR,	INDF
; ===============================================================
p1GetCmdByte	bsf	F_CmdE		; Puffer erst mal zu Ende
		movfw	CmdPos
		sublw	CmdLen-1	; CmdPos > CmdLen-1 ?
		skip_C			; ... nein
		retlw	0x0FF

		movlw	CmdPuf		; Cmd-Puffer-Adresse
		addwf	CmdPos,W	; plus Offset
		movwf	FSR		; fuer indirekte Adressierung
		bcf	STATUS,IRP	; Bank 0 / 1
		movfw	INDF		; Cmd-Byte laden
		movwf	CmdByte		; und retten
		skip_NZ			; ... Cmd-Byte <> 0: kein Cmd-Ende
		retlw	0x0FF

		bcf	F_CmdE		; Puffer nicht zu Ende
		incf	CmdPos		; Offset + 1
		return

; ===============================================================
; Byte aus RxD-Puffer laden
; Eingang:	RxDPos		RxD-Position
; Ausgang:	CmdByte		Byte
;		RxDPos		+ 1 oder 0
; Benutzt:	FSR,	INDF
; ===============================================================
p1GetRxDByte	movlw	LOW(RxDPuf)	; RxD-Puffer-Adresse
		addwf	RxDPos,W	; plus Offset
		movwf	FSR		; fuer indirekte Adressierung
		bsf	STATUS,IRP	; Bank 2 / 3
		movfw	INDF		; Byte aus RxD-Puffer laden
		movwf	CmdByte		; und retten
		incf	RxDPos		; Offset + 1
		movfw	RxDPos
		sublw	RxDLen-1	; RxDPos > RxDLen-1 ?
		skip_C			; ... nein
		clrf	RxDPos		; ... ja: Offset = 0

		return

; ===============================================================
; Diverse Zeichen senden
; ===============================================================
p1PrnTrenn	movlw	" "		; Leerzeichen senden
		call	p1PrnChr
		movlw	":"		; Doppelpunkt senden
		call	p1PrnChr
		movlw	" "		; Leerzeichen senden
		goto	p1PrnChr

p1PrnCrLf	movlw	0x0D		; CR
		call	p1PrnChr
		movlw	0x0A		; LF
		goto	p1PrnChr

; ---------------------------
; Ziffern-Ausgabe in Ascii
; ---------------------------
p1PrnZif0_9	bsf	F_Zif		; 1...9 gesendet
		addlw	"0"		; + Ascii-0
		goto	p1PrnChr	; Ziffer senden

; ---------------------------
; Ziffern-Ausgabe formatiert / unformatiert
; ---------------------------
p1FmtZif	addlw	0		; Wert = 0 ?
		skip_Z			; ... ja
		goto	p1PrnZif0_9	; ... sonst Ziffer senden

		skip_0	F_Zif		; ... noch keine Ziffer gesendet
		goto	p1PrnZif0_9	; ... sonst Ziffer senden

p1FmtSpc	skip_1	F_Fmt		; ... Leerzeichen senden
		return			; ... sonst nix senden

p1PrnSpc	movlw	" "		; Leerzeichen
		goto	p1PrnChr
; ---------------------------

p1PrnPkt	movlw	"."		; Punkt
		goto	p1PrnChr

p1PrnDpkt	movlw	":"		; DoppelPunkt
		goto	p1PrnChr

p1PrnSemi	movlw	";"		; Semikolon
		goto	p1PrnChr

p1PrnStern	movlw	"*"		; Stern
		goto	p1PrnChr

p1PrnKlauf	movlw	klauf		; Klammer auf
		goto	p1PrnChr

p1PrnKlzu	movlw	klzu		; Klammer zu
		goto	p1PrnChr

; ===============================================================
; Charakter (Byte) senden (im TxD-Puffer ablegen)
; Eingang:	W	Charakter (Byte)
; Ausgang:	TxDPos + 1 oder 0
;		TxDAnz + 1
; Benutzt:	MathTemp1,	FSR,	INDF
; ===============================================================
p1PrnChr	movwf	MathTemp1	; Byte retten
p1PrnChr_1	movfw	TxDAnz		; TxD-Puffer voll ?
		sublw	TxDLen-4	; TxDAnz > TxDLen-4 ?
		skip_C			; ... nein
		goto	p1PrnChr_1	; ... sonst warten

		incf	TxDAnz		; Anzahl TxD + 1
		movlw	LOW(TxDPuf)	; Puffer-Adresse
		addwf	TxDPos,W	; plus Offset
		movwf	FSR		; fuer indirekte Adressierung
		bsf	STATUS,IRP	; Bank 2 / 3
		movfw	MathTemp1	; Byte zurueck
		movwf	INDF		; und im Ringpuffer speichern
		incf	TxDPos		; Offset + 1
		movfw	TxDPos
		sublw	TxDLen-1	; TxDPos > TxDLen-1 ?
		skip_C			; ... nein
		clrf	TxDPos		; ... ja: Offset = 0

		return

; ===============================================================
; String aus FlashSpeicher senden
; Eingang:	AddrL		FlashAdresse Low
;		AddrH		FlashAdresse High
; ===============================================================
p1PrnStrF	call	p1RdFlash	; Byte aus FlashSpeicher lesen
		skip_NZ			; ... kein StringEnde
		return

		call	p1PrnChr	; Byte senden
		incf	AddrL		; Addresse + 1
		skip_NZ
		incf	AddrH

		goto	p1PrnStrF

; ===============================================================
; Ziffer 1-stellig dezimal senden
; Eingang:	W		Byte 0...9
; Wenn Byte > 9 ist, dann wird 1 Stern gesendet
; ===============================================================
p1PrnZif1	movwf	MathArgA0	; Byte ablegen
		call	p1Divu0810	; MathArgA0 / 10
		movfw	MathErgA1	; 100-er > 0 ?
		skip_Z			; ... nein
		goto	p1PrnStern	; ... sonst Stern senden

		movfw	MathErgA0	; 10-er > 0 ?
		skip_Z			; ... nein
		goto	p1PrnStern	; ... sonst Stern senden

		movfw	MathArgA0	; 1-er senden
		goto	p1PrnZif0_9

; ===============================================================
; Ziffer 2-stellig dezimal senden
; Eingang:	W		Byte 0...99
; Wenn Byte > 99 ist, dann werden 2 Sterne gesendet
; ===============================================================
p1PrnZif2	movwf	MathArgA0	; Byte ablegen
		call	p1Divu0810	; MathArgA0 / 10
		bcf	F_Zif		; Erst mal keine 1...9 gesendet
		movfw	MathErgA1	; 100-er = 0 ?
		skip_Z			; ... ja
		goto	p1PrnZif2_8	; ... sonst 2 Sterne senden

		movfw	MathErgA0	; 10-er evtl. senden
		call	p1FmtZif
		movfw	MathArgA0	; 1-er senden
		goto	p1PrnZif0_9

p1PrnZif2_8	call	p1PrnStern
		goto	p1PrnStern

; ===============================================================
; Ziffer 3-stellig dezimal senden
; Eingang:	W		Byte 0...255
; ===============================================================
p1PrnZif3	movwf	MathArgA0	; Byte ablegen
		call	p1Divu0810	; MathArgA0 / 10
		bcf	F_Zif		; Erst mal keine 1...9 gesendet
		movfw	MathErgA1	; 100-er evtl. senden
		call	p1FmtZif
		movfw	MathErgA0	; 10-er evtl. senden
		call	p1FmtZif
		movfw	MathArgA0	; 1-er senden
		goto	p1PrnZif0_9

; ===============================================================
; Ziffer hexadezimal senden
; Eingang:	W		Byte 0...255
; ===============================================================
p1PrnZifH	movwf	MathArgA0	; Byte retten
		movlw	"0"		; "0" senden
		call	p1PrnChr
		movlw	"x"		; "x" senden
		call	p1PrnChr
		swapf	MathArgA0,W	; HighNibbl laden
		andlw	00001111b	; Bit 3...0 maskieren
		addlw	"0"		; = AsciiHighNibbl
		movwf	MathArgA1	; und retten
		sublw	"9"		; > "9" ?
		skip_NC			; ... ja
		goto	p1PrnZifH_1

		movfw	MathArgA1	; AsciiHighNibbl zurueck
		movlw	7		; A...F
		addwf	MathArgA1	; und retten
p1PrnZifH_1	movfw	MathArgA1	; AsciiHighNibbl senden
		call	p1PrnChr
		movfw	MathArgA0	; LowNibbl laden
		andlw	00001111b	; Bit 3...0 maskieren
		addlw	"0"		; = AsciiLowNibbl
		movwf	MathArgA1	; und retten
		sublw	"9"		; > "9" ?
		skip_NC			; ... ja
		goto	p1PrnZifH_2

		movfw	MathArgA1	; AsciiLowNibbl zurueck
		movlw	7		; A...F
		addwf	MathArgA1	; und retten
p1PrnZifH_2	movfw	MathArgA1	; AsciiLowNibbl senden
		goto	p1PrnChr

; *****************************************************************
; 8 Bit Multiplikation mit 10d
; Eingang:	MathArgA0	Multiplikant
; Ausgang:	MathErgA1..0	Multiplikant * 10
; *****************************************************************
p1Mulu0810	clrf	MathErgA1
		bcf	_C		; C=0
		rlf	MathArgA0,W	; Multiplikant * 2
		movwf	MathErgA0
		movwf	MathTemp0
		rlf	MathErgA1,W	; HighByte Uebertrag
		movwf	MathErgA1
		movwf	MathTemp1
		bcf	_C		; C=0
		rlf	MathErgA0	; * 4
		rlf	MathErgA1
		rlf	MathErgA0	; * 8
		rlf	MathErgA1
		movfw	MathTemp0	; *8 + *2 = *10
		addwf	MathErgA0
		skip_NC			; ... kein Uebertrag
		incf	MathErgA1

		movfw	MathTemp1
		addwf	MathErgA1
		return

; *****************************************************************
; 8 Bit Division durch 10d
; Eingang:	MathArgA0
; Ausgang:	MathErgA0	Ergebnis 10-er
;		MathErgA1	Ergebnis 100-er
;		MathArgA0	Rest
; *****************************************************************
p1Divu0810	movlw	255
		movwf	MathErgA0	; Ergebnis  10-er = 255
		movwf	MathErgA1	; Ergebnis 100-er = 255

		movlw	100
p1Divu0810_1	incf	MathErgA1	; Ergebnis 100-er + 1
		bcf	_C		; Carry = 0
		subwf	MathArgA0	; Divident - 100
		btfsc	_C		; ... Unterlauf
		goto	p1Divu0810_1

		addwf	MathArgA0	; Rest 10-er
		movlw	10
p1Divu0810_2	incf	MathErgA0	; Ergebnis 10-er + 1
		bcf	_C		; Carry = 0
		subwf	MathArgA0	; Divident - 10
		btfsc	_C		; ... Unterlauf
		goto	p1Divu0810_2

		addwf	MathArgA0	; Rest 1-er
		return

; ***************************************************************
; EEPROM- und FLASH-ROUTINEN
; ***************************************************************
; ---------------------------------------------------------------
; Konfiguration aus Eeprom in Ram kopieren
; ---------------------------------------------------------------
p1RdEepKonf	movlw	32		; 4 * 8 Parameter
		movwf	zUni
		movlw	LOW(EepParam)	; Adr setzen
		movwf	AddrL
		movlw	RamParam
		movwf	FSR
		bcf	STATUS,IRP	; Bank 0 / 1

p1RdEepKonf_1	call	p1RdEeprom	; Eeprom lesen
		movwf	INDF
		incf	AddrL		; Adr + 1
		incf	FSR
		decfsz	zUni		; ... Zlr - 1 = 0
		goto	p1RdEepKonf_1

		return

; ---------------------------------------------------------------
; Byte aus Eeprom lesen
; Eingang:	AddrL		Eeprom-Adresse Low
; Ausgang:	W & EepByte	Daten-Byte
; ---------------------------------------------------------------
p1RdEeprom	M_movlf	10,t10ms	; 50 * 10 ms TimeOut-Zeit
p1RdEeprom_1	M_Bank0			; Bank 0
		movfw	t10ms		; TimeOut ?
		skip_NZ			; ... nein
		goto	p1WrEeprom_9	; ... sonst: Fehler

		bsf	STATUS,RP0	; Bank 3
		bsf	STATUS,RP1
		skip_0	EECON1,WR	; ... letzter Schreibvorgang ist fertig
		goto	p1RdEeprom_1	; ... sonst warten

		bcf	STATUS,RP0	; Bank 2
		clrf	EEADRH		; Eeprom-Adr High = 0
		M_movff	AddrL,EEADR	; AddrL speichern
		bsf	STATUS,RP0	; Bank 3
		bcf	EECON1,EEPGD	; Zugriff auf Eeprom
		bsf	EECON1,RD	; Lesezugriff freigeben
		bcf	STATUS,RP0	; Bank 2
		M_movff	EEDATA,EepByte	; Byte aus Eeprom nach EepByte
		M_Bank0			; Bank 0
		movfw	EepByte		; Daten-Byte nach W
		return

; ---------------------------------------------------------------
; Byte in Eeprom schreiben (ohne Verify)
; Eingang:	W		Daten-Byte
;		AddrL		Eeprom-Adresse
; Ausgang:	Daten-Byte im Eeprom
; ---------------------------------------------------------------
p1WrEeprom	movwf	EepByte		; Daten-Byte nach EepByte
		M_movlf	10,t10ms	; 50 * 10 ms TimeOut-Zeit

p1WrEeprom_1	M_Bank0			; Bank 0
		movfw	t10ms		; TimeOut ?
		skip_NZ			; ... nein
		goto	p1WrEeprom_9	; ... sonst: Fehler

		bsf	STATUS,RP0	; Bank 3
		bsf	STATUS,RP1
		skip_0	EECON1,WR	; ... letzter Schreibvorgang ist fertig
		goto	p1WrEeprom_1	; ... sonst warten

		bcf	STATUS,RP0	; Bank 2
		M_movff	AddrL,EEADR	; Eeprom-Adresse in EEADR ablegen
		clrf	EEADRH		; Eeprom-Adr High-Byte = 0

		; ---------------------------
		; Byte aus Eeprom lesen und mit neuem Byte vergleichen.
		; Wenn Eeprom-Byte und Neues Byte gleich sind,
		; ist kein Schreibvorgang erforderlich.
		; ---------------------------
		bsf	STATUS,RP0	; Bank 3
		bcf	EECON1,EEPGD	; Zugriff auf Eeprom
		bsf	EECON1,RD	; Lesezugriff freigeben
		bcf	STATUS,RP0	; Bank 2
		movfw	EEDATA		; Byte aus Eeprom nach W-Register
		bcf	STATUS,RP1	; Bank 0
		subwf	EepByte,W	; Eeprom-Byte = neues Byte ?
		skip_NZ			; ... nein
		return			; ... sonst kein Schreibvorgang

		; ---------------------------
		; Eeprom-Byte <> neues Byte:
		; Schreibvorgang starten
		; ---------------------------
		movfw	EepByte		; Byte zurueck
		bsf	STATUS,RP1	; Bank 2
		movwf	EEDATA		; Byte in Eeprom-Data ablegen
		bsf	STATUS,RP0	; Bank 3
		bcf	EECON1,EEPGD	; Zugriff auf Eeprom
		bsf	EECON1,WREN	; Schreibzugriff freigeben
		bcf	INTCON,GIE	; Globaler Int aus
		M_movlf	0x55,EECON2	; SchreibZyklus initialisieren
		M_movlf	0xAA,EECON2
		bsf	EECON1,WR	; Schreibzugriff beginnen
		nop
		bsf	INTCON,GIE	; Globaler Int wieder ein
		bcf	EECON1,WREN	; Schreibzugriff sperren
		M_Bank0			; Bank 0
		return

p1WrEeprom_9	movlw	7		; Eeprom TimeOut
		movlw	LOW(TxtSysErr_7); Kalt-Start
		movwf	AddrL
		movlw	HIGH(TxtSysErr_7)
		movwf	AddrH
		call	p1PrnStrF	; String senden
		call	p1PrnCrLf	; cr/lf senden
		return

; ---------------------------------------------------------------
; Byte aus FlashSpeicher lesen
; Eingang:	AddrL		FlashAdresse Low
;		AddrH		FlashAdresse High
; Ausgang:	W & EepByte	Daten-Byte
; ---------------------------------------------------------------
p1RdFlash	M_movlf	10,t10ms	; 50 * 10 ms TimeOut-Zeit
p1RdFlash_1	M_Bank0			; Bank 0
		movfw	t10ms		; TimeOut ?
		skip_NZ			; ... nein
		goto	p1WrEeprom_9	; ... sonst: Fehler

		bsf	STATUS,RP0	; Bank 3
		bsf	STATUS,RP1
		skip_0	EECON1,WR	; ... letzter Schreibvorgang ist fertig
		goto	p1RdFlash_1	; ... sonst warten

		bcf	STATUS,RP0	; Bank 2
		M_movff	AddrH,EEADRH	; AddrH speichern
		M_movff	AddrL,EEADR	; AddrL speichern
		bsf	STATUS,RP0	; Bank 3
		bsf	EECON1,EEPGD	; Zugriff auf FlashSpeicher
		bsf	EECON1,RD	; Flash lesen
		nop
		nop
		bcf	STATUS,RP0	; Bank 2
		M_movff	EEDATA,EepByte	; Byte in EepByte speichern
			; EEDATH wird nicht benutzt
		M_Bank0			; Bank 0
		movfw	EepByte		; Daten-Byte nach W
		return

; ###############################################################
; Programmspeicher Page 2 (0x1000...0x17FF)
; ###############################################################
		org	0x1000


; ###############################################################
; Programmspeicher Page 3 (0x1800...0x1FFF)
; ###############################################################
		org	0x1800

TxtSysErr_0	de	"F 00 ; Kein Fehler", 0
TxtSysErr_1	de	"F 01 ; KaltStart", 0
TxtSysErr_2	de	"F 02 ; WarmStart", 0

TxtSysErr_7	de	"F 07 ; TimeOut Eeprom", 0

TxtCmdErr_1	de	"F 21 ; Kommando fehlt", 0
TxtCmdErr_2	de	"F 22 ; Kommando falsch", 0
TxtCmdErr_3	de	"F 23 ; Parameter fehlt / falsch", 0
TxtCmdErr_4	de	"F 24 ; Parameter zu klein", 0
TxtCmdErr_5	de	"F 25 ; Parameter zu gross", 0
TxtCmdErr_6	de	"F 26 ; Bestaetigung fehlt / falsch", 0
TxtCmdErr_7	de	"F 27 ; frei", 0

TxtWerte	de	cr, lf
		de	"; ------ WERTE ---------", cr, lf, 0

TxtHilfe	de cr, lf
		de "; M 1-8 1-16  = Modus", cr, lf
		de "; S 1-8 1-16  = Steigerung / Senkung [%]", cr, lf
		de "; T 1-8 0-255 = Tempo [ms / Schritt]", cr, lf
		de "; H 1-8 0-100 = Helligkeit [%]", cr, lf
		de "; D 1-8 0-100 = Dunkelheit [%]", cr, lf
		de "; V           = Versions-Nummer abfragen", cr, lf
		de "; W           = Werte aktuell abfragen", cr, lf
		de "; A           = Analog-Eingaenge abfragen", cr, lf
		de "; I 1-8       = Input Digital Bit n abfragen", cr, lf
		de "; I 81        = Input Digital Byte abfragen", cr, lf
		de "; O 1-6,0-2   = Output Digital Bit n setzen/invert.", cr, lf
		de "; O 81,0-255  = Output Digital Byte", cr, lf
		de cr, lf, 0

TxtVers		de	"V ", klauf, Version, Datum, klzu, cr, lf, 0

; ***************************************************************
; Eeprom Vorgaben und Versions-Eintragung
; ***************************************************************
		; -------------------------------------
		; VersionsEintrag ab Eeprom Adr. 0x2100...0x210F (max 16 Byte)
	org	0x2100
		;01234567
	de	Version
		;89ABCDEF
	de	Datum

		; -------------------------------------
		; Konfiguration ab Eeprom-Adr. 0x2110...0x212F
		; Modus 0	Dunkel
		; Modus 1	Hell nach Parameter
		; Modus 2	Hell nach Parameter und Eingang 1...4
		; Modus 3	Hell nach Analog 1...4
		; Modus 4	Auf / Ab
		; Modus 5
		; Modus 6
		; Modus 7
	org	0x2110
EepParam		; 0...7
EepModus	de	  7 +  0	; 2110
		de	  7 + 16
		de	  7 + 32
		de	  7 + 48
		de	  1 +  0
		de	  1 + 16
		de	  1 + 32
		de	 11 + 48

EepTempo	de	  3	; 2118
		de	  2
		de	  1
		de	  0
		de	  3
		de	  2
		de	  1
		de	  0

EepHell		de	 65	; 2120
		de	 70
		de	 75
		de	 80
		de	 85
		de	 90
		de	 95
		de	100

EepDunk		de	  5	; 2128
		de	 10
		de	 15
		de	 20
		de	 25
		de	 30
		de	 35
		de	 40


	END