Anschrift eMail
Assembler-Beispiele für PIC-Controller
Makros
Definitionen
Konstanten
Variablen-Deklarationen
Flag-Deklarationen
General Purpose Register
Register in Bank 1...3
Reset-Vektor / Programm-Beginn
Interrupt-Service-Routine ISR
ISR Timer 0
ISR Timer 2
ISR Recive
Initialisierung
HauptProgramm-Schleife
Ram-Bereich indirekt loeschen
A/D-Wandler einlesen / starten
EmpfangsByte im Ringpuffer ablegen
SendeByte aus RingPuffer senden
Datum und Uhrzeit + 1 Sekunde
Computed GOTO
KOMMANDO-STRING-AUSWERTUNG
Cmd-Puffer ruecksetzen
RxD-Byte in Cmd-Puffer ablegen / auswerten
Kommando D - Datum (D Tag Mon Jahr)
Kommando H - Hilfe
Kommando U - Uhrzeit (U Std Min Sek)
Kommando V - Version des Programms
Buchstabe suchen
Ziffer suchen (0...255)
Byte aus Cmd-Puffer laden
Byte aus RxD-Puffer laden
ASCII-ZEICHEN-AUSGABE
Ziffern-Ausgabe in ASCII
Diverse ASCII-Zeichen senden
Byte senden (im TxD-Puffer ablegen)
String aus FlashSpeicher senden
Ziffer 1-stellig dezimal senden
Ziffer 2-stellig dezimal senden
Ziffer 3-stellig dezimal senden
Ziffer hexadezimal senden
 
8 Bit Multiplikation mit 10d
8 Bit Division durch 10d
EEPROM- und FLASH-ROUTINEN
Daten aus Eeprom in Ram kopieren
Byte aus EEPROM lesen
Byte in EEPROM schreiben (ohne Verify)
Byte aus FlashSpeicher lesen
Text im Programmspeicher Page 3
Daten im Eeprom
 
ZEIT- UND WARTESCHLEIFEN
Das Prinzip des gesamtes Programms:
Jede Millisekunde wird von Timer 0 und Timer 2 ein Interrupt ausgelöst. Einige PIC's haben keinen Timer 2; es geht auch mit nur einem Timer. Timer 0 muss in der ISR immer neu geladen werden, Timer 2 verfügt über ein Reload-Register, welches nur 1 mal geladen wird.
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.
; Programm-Anfang
   list P=16f877, E=1, R=DEC	; Prozessortyp, Errorlevel und Zahlenformat
   include "p16f877.inc"	; Registerdefinitionen einbinden
   errorlevel	1		; Meldungen ignorieren

 __CONFIG _CP_OFF & _WDT_OFF & _XT_OSC & _PWRTE_ON & _LVP_OFF

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

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

; ===============================================================
; Makros
; ===============================================================
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_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

		; ---------------------------
		; 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: Bit-Toggle
		; ---------------------------
M_BitTog	macro	VarAdr,BitNr
		movlw	1<<BitNr		; 1 auf Bit-Position schieben
		xorwf	VarAdr
		endm

; ===============================================================
; Definitionen
; ===============================================================
#DEFINE	D_GlobIntEin	bsf	INTCON,GIE	; Globaler Interrupt Ein
#DEFINE	D_GlobIntAus	bcf	INTCON,GIE	; Globaler Interrupt Aus
#DEFINE	D_PeriIntEin	bsf	INTCON,PEIE	; Peripherer Interrupt Ein
#DEFINE	D_PeriIntAus	bcf	INTCON,PEIE	; Peripherer Interrupt Aus

#DEFINE	_C		STATUS,0
#DEFINE	_Z		STATUS,2
#DEFINE	skip_0		btfsc	; ... Bit	; Ueberspringe, wenn Bit = 0
#DEFINE	skip_1		btfss	; ... Bit	; Ueberspringe, wenn Bit = 1
#DEFINE	skip_NC		btfsc	STATUS,0	; Ueberspringe, wenn Carry = 0
#DEFINE	skip_C		btfss	STATUS,0	; Ueberspringe, wenn Carry = 1
#DEFINE	skip_NZ		btfsc	STATUS,2	; Ueberspringe, wenn Zero = 0
#DEFINE	skip_Z		btfss	STATUS,2	; Ueberspringe, wenn Zero = 1

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

Ptime		EQU	50	; 50 ms Prellzeit

; ===============================================================
; Variablen-Deklarationen
; ===============================================================
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
MathArgB0	EQU	0x24	; Argument B (Word)
MathArgB1	EQU	0x25
MathTemp0	EQU	0x26	; Temporaer
MathTemp1	EQU	0x27
MathZlr		EQU	0x28	; SchleifenZaehler

		; ---------------------------
		; Zaehl-Register fuer Datum und Uhrzeit
		; ---------------------------
zSek		EQU	0x2A	; Sekunden 0...59
zMin		EQU	0x2B	; Minuten 0...59
zStd		EQU	0x2C	; Stunden 0...23
zTag		EQU	0x2D	; Tag 0...28/29/30/31
zMon		EQU	0x2E	; Monat 0...12
zJahr		EQU	0x2F	; Jahr 0...99

		; ---------------------------
		; Zaehler- und Timer-Register
		; ---------------------------
z1ms		EQU	0x30	; Inc-Zaehler 1 ms-Takt
z10ms		EQU	0x31	; Dec-Zaehler  10 ms-Takt
z100ms		EQU	0x32	; Dec-Zaehler 100 ms-Takt

t10ms		EQU	0x35	; Dec-Timer  10 ms
t100ms		EQU	0x36	; Dec-Timer 100 ms
t1Sek		EQU	0x37	; Dec-Timer   1 Sek

zTast1		EQU	0x38	; PrellZaehler fuer Taster
zTast2		EQU	0x39	; PrellZaehler fuer Taster
; ... usw.

		; ---------------------------
		; Flag-Deklarationen
		; ---------------------------
Flags0		EQU	0x40
#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_Neg	Flags0,6	; 1=Negativer Wert

Flags1		EQU	0x41
#DEFINE	F_Sek	Flags1,0	; 1=Uhr / Datum aktualisieren
F_Blink		EQU	1	; Flag Blinker 100 ms

FlgTast		EQU	0x42
#DEFINE	fTast1	FlgTast,1	; Flag Taster betaetigt = 1
#DEFINE	fTast2	FlgTast,2
; ... usw.

		; ---------------------------
		; Register fuer Aktuelle Ein-/Ausg-Zustaende
		; ---------------------------
aAdw1		EQU	0x43	; Akt Analog/Digital-Wert 1
aAdw2		EQU	0x44	; Akt Analog/Digital-Wert 2
aAdw3		EQU	0x45	; Akt Analog/Digital-Wert 3

		; ---------------------------
		; Temporaere Register
		; ---------------------------
Temp1		EQU	0x49	; Temporaer (nicht fuer Interrupt)
Temp2		EQU	0x4A
Temp3		EQU	0x4B

Para1		EQU	0x4C	; Temporaer Parameter
Para2		EQU	0x4D
Para3		EQU	0x4E

CmdByte		EQU	0x4F	; Temporaer bei Cmd-Auswertung

		; ---------------------------
		; Konfigurations-Daten
		; ---------------------------
RamParam	EQU	0x50

		; ---------------------------
		; Zeiger-Register
		; ---------------------------
RxDOfs		EQU	0x60	; Offset RxD-RingPuffer
RxDPos		EQU	0x61	; Position RxD-RingPuffer
TxDOfs		EQU	0x62	; Offset TxD-RingPuffer
TxDPos		EQU	0x63	; Position TxD-RingPuffer
TxDAnz		EQU	0x64	; Anzahl Bytes im TxD-Puffer
CmdPos		EQU	0x65	; Position Cmd-Puffer

		; ---------------------------
		; Von Bank 0 erreichbar
		; ---------------------------
iBakSTAT	EQU	0x6D	; Interrupt Backup Status
iBakPCL		EQU	0x6E	; Interrupt Backup Rom-Page (PCLATH)
iBakFSR		EQU	0x6F	; Interrupt Backup Ind-Adresse

RamEnd0		EQU	0x70	; Ende Ram-Bereich

		; ---------------------------
		; General Purpose Register
		; Von Bank 0...3 erreichbar (0x71...0x7F) !
		; ACHTUNG !!! 0x70 ist bei Verwendung von MPLAB ICD belegt
		; ---------------------------
iBakW		EQU	0x71	; Interrupt Backup W-Register
izUni		EQU	0x72	; Interrupt Universeller Zaehler
zUni		EQU	0x73	; Universeller Zaehler
AddrL		EQU	0x74	; Addresse Low fuer Flash & Eeprom
AddrH		EQU	0x75	; Addresse High fuer Flash
EepByte		EQU	0x76	; Byte aus Flash & Eeprom

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

; ===============================================================
; Register in Bank 1
; ===============================================================
		; ---------------------------
		; Kommando-Puffer fuer max 60 Byte
		; ---------------------------
CmdLen		EQU	60	; Puffer fuer 60 Byte
CmdPuf		EQU	0xB4	; Adresse 0xB4 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

; ***************************************************************
; PROGRAMM-BEGINN
; ***************************************************************
		org	0x0000		; 00h Reset-Vektor
Reset		clrf	STATUS		; 00h Page 0, Bank 0
		goto	Init		; 01h Initialisierung / ProgrammBeginn
		goto	Reset		; 02h Reset-Vektor
		goto	Reset		; 03h Reset-Vektor
	;;	goto	Interrupt	; 04h Interrupt-Vektor
	;;	Kein GOTO !!!, falls Int in Rom-Page <> 0 ausgeloest wurde

; ACHTUNG ! Hier muss die InterruptServiceRoutine beginnen !!
		org	0x0004		; 04h Interrupt-Vektor
; ***************************************************************
; Interrupt-Service-Routine ISR
; Ausloesung durch Receive
; Ausloesung durch Timer 2 / PR2 (Ausloesung jede MilliSekunde)
; Ausloesung durch Timer 0 (Ausloesung jede MilliSekunde)
; Benutzt:	iBakW, iBakSTAT (iBakPCL, iBakFSR)
; ***************************************************************
Isr	; W und STATUS immer retten
		movwf	iBakW		; W retten nach iBakW
		swapf	STATUS,W	; Status mit swap nach W
		clrf	STATUS		; Bank 0
		movwf	iBakSTAT	; Status retten ohne Veraenderung

	; wenn PCLATH in der ISR benutzt wird:
		movfw	PCLATH		; Rom-Page (PCLATH) retten
		movwf	iBakPCL
		clrf	PCLATH		; Auf Rom-Page 0 schalten

	; wenn FSR in der ISR benutzt wird:
		movfw	FSR
		movwf	iBakFSR

		; ---------------------------
		; Die Isr wird erst verlassen, wenn auch die Interrupts
		; verarbeitet sind, deren xxxIF waehrend der Isr gesetzt wurde.
		; Weitere Backup und Restore von W, STATUS, etc. werden damit eingespart.
		; ---------------------------
Isr_TestIF	bcf	STATUS,RP0	; Bank 0, falls nach xxxIE-Abfrage auf Bank 1

		skip_0	INTCON,T0IF	; ... T0IF ist nicht gesetzt
		goto	Isr_TMR0	; ... ist gesetzt

		skip_0	PIR1,TMR2IF	; ... TMR2IF ist nicht gesetzt
		goto	Isr_TMR2	; ... ist gesetzt

		skip_0	PIE1,RCIF	; ... RCIF ist nicht gesetzt
		goto	Isr_RC		; ... ist gesetzt

Isr_Ende
	; wenn FSR in der ISR benutzt wird:
		movfw	iBakFSR
		movwf	FSR

	; wenn PCLATH in der ISR benutzt wird:
		movfw	iBakPCL		; Rom-Page (PCLATH) zurueck
		movwf	PCLATH

	; STATUS und W immer restaurieren
		swapf	iBakSTAT,W	; Statusregister und W zurueck
		movwf	STATUS
		swapf	iBakW,F		; swapf veraendert kein Flag
		swapf	iBakW,W
		retfie

; ===============================================================
; ISR Timer 0 (Aufruf jede Millisekunde)
; ===============================================================
Isr_TMR0	bcf	INTCON,T0IF	; Int-Flag ruecksetzen
		M_movlf	256-yyy,TMR0	; Timer 0 neu laden
		; yyy: siehe Tabelle bei Init Timer 0

		skip_1	INTCON,T0IE	; ... Timer 0-Interrupt ist enabled
		goto	Isr_TestIF	; ... ist nicht enabled

		call	SioTxD		; evtl. Byte seriell ausgeben
		call	RdAdw		; A/D-Wandler einlesen

		; ... weitere Befehle (1 ms)

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

		; ---------------------------
		; Aufruf alle 10 ms
		; ---------------------------
Int10ms		M_movlf	10,z1ms		; 1 ms-Zaehler = 10 (= 10 ms)
		movfw	t10ms		; 10 ms-Timer = 0 ?
		skip_Z			; ... ja
		decf	t10ms		; ... sonst - 1

		; ... weitere Befehle (10 ms)

		decfsz	z10ms,F		; ... 10 ms-Zaehler - 1 = 0
		goto	Isr_TestIF	; ... 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

		; ---------------------------
		; 100 ms-Blinker
		; ---------------------------
		M_BitTog Flags1,F_Blink	; Blinker-Flag togglen

		; ... weitere Befehle (100 ms)

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

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

		bsf	F_Sek		; Uhrzeit und Datum aktualisieren
		movfw	TxDAnz		; Evtl. Anzahl verringern, falls nichts gesendet wird
		skip_Z			; ... ist schon 0
		decf	TxDAnz		; ... sonst - 1

		; ... weitere Befehle (1 Sek)

		goto	Isr_TestIF

; ===============================================================
; ISR Timer 2 (Aufruf jede MilliSekunde)
; Beispiel zum Entprellen von Tastern
; Fuer jeden Taster muss ein Zaehler (zTast1...x) und ein Flag (fTast1...x) definiert werden
;
; xxxIE braucht nur dann getestet werden, wenn der Interrupt zeitweise deaktiviert wird
; ===============================================================
Isr_TMR2	bcf	PIR1,TMR2IF	; Int-Flag ruecksetzen
		bsf	STATUS,RP0	; Bank 1
		skip_1	PIE1,TMR2IE	; ... Timer 2-Interrupt ist enabled
		goto	Isr_TestIF	; ... ist nicht enabled

		bcf	STATUS,RP0	; Bank 0
EntprTast1	skip_0	PORTB,1		; ... Taster 1 ist betaetigt (Eing = 0)
		goto	EntprTast1_1	; ... Taster 1 ist nicht betaetigt (Eing = 1)

		; ---------------------------
		; Taster ist betaetigt; Zaehler bis 76 incrementieren
		; ---------------------------
		movlw	75
		subwf	zTast1,W	; Ist Zaehler > 75 / Carry = 1 ?
		skip_C			; ... ja
		incf	zTast1		; ... sonst: Zaehler + 1

		goto	EntprTast2

; ACHTUNG !!! Bei SUB ist C und DC invertiert !!!
; Berechnung: F bzw. L minus W  genauer gesagt:  F bzw. L plus 2-er-Complement von W
; Wenn W >  L bzw. F, dann Carry = 0  /  Wenn L bzw. F <= W, dann Carry = 0
; Wenn W <= L bzw. F, dann Carry = 1  /  Wenn L bzw. F >  W, dann Carry = 1

		; ---------------------------
		; Taster ist nicht betaetigt; Zaehler bis 0 decrementieren
		; ---------------------------
EntprTast1_1	movfw	zTast1		; Ist Zaehler auf 0 / Zero = 1 ?
		skip_Z			; ja
		decf	zTast1		; ... sonst: Zaehler - 1

		; ---------------------------
		; Taster-Flag bei <= 50 ruecksetzen / > 50 setzen
		; ---------------------------
EntprTast1_2	bcf	fTast1		; Flag erst mal ruecksetzen
		movlw	50
		subwf	zTast1,W	; Ist Zaehler <= 50 / Carry = 0 ?
		skip_NC			; ja
		bsf	fTast1		; ... sonst: Flag setzen

		; ---------------------------
		; Naechster Taster
		; ---------------------------
EntprTast2	skip_0	PORTB,2		; ... Taster 2 ist betaetigt (Eing = 0)
		goto	EntprTast2_1	; ... Taster 2 ist nicht betaetigt (Eing = 1)
		; ...
EntprTast2_1
		; ... usw. fuer jeden Taster
		; ... weitere Befehle

		goto	Isr_TestIF

; ===============================================================
; ISR Receive (Sio RxD)
; ===============================================================
Isr_RC	;;	bcf	PIR1,RCIF	; geht nicht ! RCIF und TXIF sind nur zum Lesen
		movfw	RCREG		; Empfangsbyte laden, dabei wird RCIF geloescht
		movwf	iTemp1		; Byte retten

		bsf	STATUS,RP0	; Bank 1
		btfss	PIE1,RCIE	; ... Receive-Interrupt ist enabled ?
		goto	Isr_TestIF	; ... ist nicht enabled

		bcf	STATUS,RP0	; Bank 0
		call	SioRxD		; Byte im Ringpuffer ablegen
		goto	Isr_TestIF

; ===============================================================
; Initialisierung
; ===============================================================
Init		M_Bank0			; Bank 0
		clrf	INTCON		; Alle Interrupts sperren
		M_Page0			; auf Rom-Page 0 schalten
		clrwdt			; Clear WDT and postscaler
		movlw	255		; Alle Ports auf 1 setzen,
		movwf	PORTB		; damit beim Setzen der TRIS-Register
					; definierte Zustaende am Port sind
		; ... weitere Port-Pins setzen

		; -----------------------------------------------
		; USART Receive (Bank 0)
		; ------76543210---------------------------------
		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
		; ------76543210---------------------------------
		movlw	10000101b
		;	10		= Fosc/32
		;	  000		= Analog Channel 0
		;	     1		= GO
		;	       1	= ADON
		movwf	ADCON0
		M_Bank1			; Bank 1

		; -------------------------------------
		; RA0/ANA0	In/Analog
		; RA1/ANA1	In/Analog
		; RA2		ST-In
		; RA3/ANA3	In/Analog
		; RA4/T0CKI	ST-In
		; RA5		ST-In
		; RA6		---
		; RA7		---
		; ------76543210---------------------------------
		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
		; ------76543210---------------------------------
		movlw	00000010b
		;	0		  = Result format = Left
		;	    0100	  = Analog RA0,RA1&RA3
		movwf	ADCON1

		; -----------------------------------------------
		; TRISB	(Bank 1)	Richtungen
		; RB0/INT	In	Eingang	Int
		; RB1		Out	Ausgang	1
		; RB2		Out	Ausgang	2
		; RB3/PGM	In	ICD1
		; RB4		Out	Ausgang	3
		; RB5		Out	Ausgang	4
		; RB6/PGC	In	ICD1
		; RB7/PGD	In	ICD1
		; ------76543210---------------------------------
		movlw	11001001b	; 0=Ausgang / 1=Eingang
		movwf	TRISB

		; ... weitere Richtungen konfigurieren

		; -----------------------------------------------
		; USART, Timer, IntEnable (Bank 1)
		; ------76543210---------------------------------
		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	64		; Baudrate = 10,24 MHz / (16 * (65 + 1)) = 9696,969697 Baud
		movwf	SPBRG		; (Bank 1) BaudrateGenerator
		movlw	K_1msT2
		movwf	PR2		; (Bank 1) 1 ms-Wert fuer Timer 2 setzen
		; ------76543210---------------------------------
		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	GPIO pull up	0=enabled / 1=disabled
		; 6	Int Edg Sel	0=fallende / 1=steigende Flanke an GP2/INT
		; 5	T0 Clk Source	0=intern CLKOUT / 1=extern GP2/T0CKI
		; 4	T0 Src Edg Sel	0=Low to High / High to Low Increment an GP2/T0CKI pin
		; 3	Prescaler Ass	0=TMR0 / 1=WDT
		; 2-0	Prescaler	000=1:2 ... 111=1:256 TMR0
		;			000=1:1 ... 111=1:128 WDT
		; -----------------------------------------------
		;         1             ; Pull Up	= disabled
		;          0            ; Interrupt	= fallende Flanke
		;           0           ; TMR0-Quelle	= intern CLKOUT
		;            0          ; Flanke	= Low To High
		;             0         ; Prescaler Ass	= TMR0
		;              xxx      ; Prescaler
		; -----------------------------------------------
		movlw	10000xxxb	; Select TMR0, prescale, and clock source

; Quarz-Frequenz ... Timer 0 Prescaler xxx ... Timer 0 Wert yyy (256 - yyy)
;      Quarz           F/4         Prescaler          TMR0     ISR
;       [Hz]       [Hz] = [us]     xxx        = [ms]   yyy    [ms]
; 20.000.000  5.000.000   0,2      100 = /32  0,0064   156  0,9984
; 10.240.000  2.560.000   0,390625 011 = /16  0,00625  160  1
; 10.000.000  2.500.000   0,4      011 = /16  0,0064   156  0,9984
;  4.000.000  1.000.000   1        001 = / 4  0,004    250  1
;  2.000.000    500.000   2        000 = / 2  0,004    250  1
yyy		EQU	160	; Quarz	10,24 MHz

		movwf	OPTION_REG
		bcf	STATUS,RP0	; Bank 0

		; ... sonst noch was in Bank 1 ?

		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,24  MHz / 4 = 2,56 MHz = 0,390625 µs)
		; Timer 2 / PR2 (8 Bit) wird fuer 1 ms-Interrupt benutzt
		; ------76543210---------------------------------
		movlw	00100101b
		;	      01	; Prescale	=   4 * 0,3906250 µs = 0,0015625 ms
		;	 0100		; Postscale	=   5 * 0,0015625 ms = 0,0078125 ms
K_1msT2		EQU	128		; Timer-Wert	= 128 * 0,0078125 ms = 1,0000000 ms
		;	     1		; TimerOn	= Ein
		movwf	T2CON

		; -----------------------------------------------
		; 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)
		; ------76543210---------------------------------
		movlw	00100100b
		;	  10		; Prescale	= 4 * 0,390625 µs = 1,5625 µs
		;	    0		; Osci		= shut off
		;	     1		; Ext Clk Sync	= Aus
		;	      0		; Clk Source	= Intern
		;	       0	; TimerOn	= Aus
		movwf	T1CON

		; ... weitere Initialisierungen

		call	ResRam		; Ram-Bereich auf 0 setzen
		call	ResCmdPuf	; Cmd-Puffer ruecksetzen
		call	RdEepKonf	; Konfig-Daten aus Eeprom lesen

		M_movlf	9,z1ms
		M_movlf	1,z10ms
		M_movlf	1,z100ms

		; ... weitere Variablen setzen

		movlw	256-yyy		; Timer 0 setzen
		; yyy: siehe Tabelle bei Init Timer 0

		movwf	TMR0
		bcf	INTCON,T0IF	; Timer 0 IntFlag ruecksetzen
		bsf	INTCON,T0IE	; Timer 0 IntEnable setzen

		; ... weitere Peripherie und Interrupts aktivieren

		D_PeriIntEin		; Periphere Interrupts freigeben
		D_GlobIntEin		; Globalen Interrupt freigeben

		movlw	LOW(TxtSysErr_1) ; Kalt-Start
		movwf	AddrL
		movlw	HIGH(TxtSysErr_1)
		movwf	AddrH

		call	PrnCrLf		; cr/lf senden
		call	PrnStrF		; String senden
		call	PrnCrLf		; cr/lf senden

; ===============================================================
; HauptProgramm-Schleife
; ===============================================================
Main		skip_0	F_Sek		; ... noch keine Sekunde vorbei
		call	UpdUhrDat	; ... sonst Sekunden + 1

		; ---------------------------
		; Command-Byte auswerten
		; ---------------------------
		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
		; ---------------------------
		goto	Cmd_Ausw	; RxD-Byte speichern, evtl. Cmd-Puffer auswerten
Main_5
		; ... weitere Befehle in der HauptProgramm-Schleife

		goto	Main		; Zum Anfang der Schleife

; ***************************************************************
; DIVERSE ROUTINEN
; ***************************************************************
; ===============================================================
; Ram-Bereich 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

; ===============================================================
; A/D-Wandler einlesen und naechsten Kanal setzen
; A/D-Wandler starten
; Aufruf jede ms im Interrupt
;		an7  an6  an5	 AN4  AN3  AN2	AN1  AN0  Ref+	Ref-
;	Bits	re2  re1  re0	 RA5  RA3  RA2	RA1  RA0
;  **	0100	 D    D	   D	  D    A    D	 A    A	  Vdd	Vss
; ===============================================================
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
		goto	RdAdw_1		; 1
		return			; 2
		goto	StartAdw	; 3
		goto	RdAdw_3		; 4
		return			; 5
		goto	StartAdw	; 6
		goto	RdAdw_0		; 7
		return			; 8
		goto	StartAdw	; 9
		return			; 10

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

		; ---------------------------
RdAdw_0		movfw	ADRESH		; AD-Wert laden
		movwf	aAdw1		; und speichern
		movlw	10000001b	; AD-Quelle auf Kanal RA0
		;	10		= Fosc/32 A/D-Conversion
		;	  000		= Kanal	0
		;	     0		= Not GO
		;	       1	= ADON
		movwf	ADCON0		; AD-Quelle setzen
		return

		; ---------------------------
RdAdw_1		movfw	ADRESH		; AD-Wert laden
		movwf	aAdw2		; und speichern
		movlw	10001001b	; AD-Quelle auf Kanal RA1
		;	  001		= Kanal 1
		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

; ===============================================================
; EmpfangsByte im RxD-Ringpuffer ablegen
; ===============================================================
SioRxD		movfw	iTemp1
		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

; ===============================================================
; SendeByte evtl. aus TxD-RingPuffer 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

; ===============================================================
; Datum und Uhrzeit + 1 Sekunde
; ===============================================================
UpdUhrDat	bcf	F_Sek		; Flag ruecksetzen
		incf	zSek		; Sekunden + 1
		movfw	zSek
		sublw	59		; zSek > 59 ?
		skip_NC			; ... ja
		return

		clrf	zSek		; Sekunden = 0
		incf	zMin		; Minuten + 1
		movfw	zMin
		sublw	59		; zMin > 59 ?
		skip_NC			; ... ja
		return

		clrf	zMin		; Minuten = 0
		incf	zStd		; Stunden + 1
		movfw	zStd
		sublw	23		; zStd > 23 ?
		skip_NC			; ... ja
		return

		clrf	zStd		; Stunden = 0
		incf	zTag		; Tage + 1
		call	AnzTage		; Anzahl Tage des Monats laden
		subwf	zTag,W		; zTag > Monatsende ?
		skip_NC			; ... ja
		return

		M_movlf	1,zTag		; Tag = 1
		incf	zMon		; Monat + 1
		movfw	zMon
		sublw	12		; zMon > 12 ?
		skip_NC			; ... ja
		return

		M_movlf	1,zMon		; Monat = 1
		incf	zJahr		; Jahr + 1
		movfw	zJahr
		sublw	99		; zJahr > 99 ?
		skip_NC			; ... ja
		return

		clrf	zJahr		; Jahr = 0
		return

; ---------------------------------------------------------------
; Computed GOTO
; Anzahl Tage des Monats
; Eingang:	zMon	Monat 1...12
;		zJahr	Jahr  0...99
; Ausgang:	W	Anzahl Tage des Monats 28...31
; ---------------------------------------------------------------
AnzTage		movwf	Temp0		; Monat retten
		movlw	HIGH(TblTage)
		movwf	PCLATH
		movfw	Temp0		; 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	Temp0
TblTage		addwf	PCL
		retlw	31
		goto	GetTageFeb
		retlw	31
		retlw	30
		retlw	31
		retlw	30
		retlw	31
		retlw	31
		retlw	30
		retlw	31
		retlw	30
		retlw	31

GetTageFeb	movfw	zJahr
		andlw	00000011b	; Schaltjahr ?
		btfss	STATUS,Z	; ... ja
		retlw	28

		retlw	29

; ***************************************************************
; KOMMANDO-STRING-AUSWERTUNG
; ***************************************************************
; ===============================================================
; Cmd-Puffer ruecksetzen
; ===============================================================
ResCmdPuf	M_Bank1
		clrf	CmdPuf
		M_Bank0
		clrf	CmdPos
		return

; ===============================================================
; RxD-Byte in Cmd-Puffer ablegen
; Bei CR Cmd-Puffer auswerten
; ===============================================================
Cmd_Ausw	call	GetRxDByte	; 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	SuchA_Z		; Cmd-Buchstabe suchen
		skip_1	F_OK		; ... gefunden
		goto	PrnCmdErr_1	; ... nicht gefunden

		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	PrnCmdErr_2	; A
		goto	PrnCmdErr_2	; B
		goto	PrnCmdErr_2	; C
		goto	Cmd_Datum	; D	Datum
		goto	PrnCmdErr_2	; E
		goto	PrnCmdErr_2	;*F	-- Fehler
		goto	PrnCmdErr_2	; G
		goto	Cmd_Hilfe	; H	Hilfe
		goto	PrnCmdErr_2	; I
		goto	PrnCmdErr_2	; J
		goto	PrnCmdErr_2	; K
		goto	PrnCmdErr_2	; L
		goto	PrnCmdErr_2	; M
		goto	PrnCmdErr_2	; N
		goto	PrnCmdErr_2	; O
		goto	PrnCmdErr_2	; P
		goto	PrnCmdErr_2	; Q
		goto	PrnCmdErr_2	; R
		goto	PrnCmdErr_2	; S
		goto	PrnCmdErr_2	; T
		goto	Cmd_Uhr		; U	Uhrzeit
		goto	Cmd_Vers	; V	Version des Programms
		goto	PrnCmdErr_2	; W
		goto	PrnCmdErr_2	; X
		goto	PrnCmdErr_2	; Y
		goto	PrnCmdErr_2	; Z

; ---------------------------------------------------------------
Cmd_Fertig	call	ResCmdPuf
Cmd_Ausw_E	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	PrnStrF			; String senden
		call	PrnCrLf			; cr/lf senden
		goto	Cmd_Fertig

; ===============================================================
; D - Datum	D Tag Mon Jahr
; ===============================================================
Cmd_Datum	call	Such0_9		; Tag
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Datum_7	; ... Uhrzeit senden

		movwf	Para1		; Tag retten
		movfw	Para1		; = 0 ?
		skip_NZ			; ... nein
		goto	PrnCmdErr_4	; ... Parameter zu klein

		sublw	31		; > 31 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5	; ... Parameter zu gross

		; ---------------------------
		call	Such0_9		; Monat
		skip_1	F_OK		; ... gefunden
		goto	PrnCmdErr_3

		movwf	Para2		; Monat retten
		movfw	Para2		; = 0 ?
		skip_NZ			; ... nein
		goto	PrnCmdErr_4	; ... Parameter zu klein

		sublw	12		; > 12 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5

		; ---------------------------
		call	Such0_9		; Jahr
		skip_1	F_OK		; ... gefunden
		goto	PrnCmdErr_3

		movwf	Para3		; Jahr retten
		sublw	99		; > 99 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5

		; ---------------------------
		M_movff	Para1,zTag	; Tag speichern
		M_movff	Para2,zMon	; Monat speichern
		M_movff	Para3,zJahr	; Jahr speichern

		; ---------------------------
		; Datum senden
		; ---------------------------
Cmd_Datum_7	call	PrnDDatum
		call	PrnCrLf		; CR / LF senden
		goto	Cmd_Fertig

		; ---------------------------
		; "D" und Datum senden
		; ---------------------------
PrnDDatum	movlw	"D"
		call	PrnChr		; D senden
		call	PrnSpc		; Leerzeichen senden
PrnDatum	movfw	zTag		; Tag senden
		call	PrnZif2
		call	PrnPkt		; Punkt senden
		movfw	zMon		; Monat senden
		call	PrnZif2
		call	PrnPkt		; Punkt senden
		movfw	zJahr		; Jahr senden
		goto	PrnZif2

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

; ===============================================================
; U - Uhrzeit	U Std Min Sek
; ===============================================================
Cmd_Uhr		call	Such0_9		; Stunden
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Uhr_7	; ... Uhrzeit senden

		movwf	Para1		; Stunden retten
		sublw	23		; > 23 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5

		; ---------------------------
		call	Such0_9		; Minuten
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Uhr_3	; Minuten und Sek = 0

		movwf	Para2		; Minuten retten
		sublw	59		; > 59 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5

		; ---------------------------
		call	Such0_9		; Sekunden
		skip_1	F_OK		; ... gefunden
		goto	Cmd_Uhr_4	; Sekunden = 0

		movwf	Para3		; Sekunden retten
		sublw	59		; > 59 ?
		skip_C			; ... nein
		goto	PrnCmdErr_5

		goto	Cmd_Uhr_5

		; ---------------------------
Cmd_Uhr_3	clrf	Para3		; Sekunden = 0
Cmd_Uhr_4	clrf	Para2		; Minuten = 0
Cmd_Uhr_5	M_movff	Para1,zStd	; Stunden speichern
		M_movff	Para2,zMin	; Minuten speichern
		M_movff	Para3,zSek	; Sekunden speichern

		; ---------------------------
		; Uhrzeit senden
		; ---------------------------
Cmd_Uhr_7	call	PrnUUhr		; U und Uhrzeit senden
		call	PrnCrLf		; CR / LF senden
		goto	Cmd_Fertig

		; ---------------------------
		; "U" und Uhrzeit senden
		; ---------------------------
PrnUUhr		movlw	"U"
		call	PrnChr		; Cmd senden
		call	PrnSpc		; Leerzeichen senden
PrnUhr		movfw	zStd		; Stunden senden
		call	PrnZif2
		call	PrnPkt		; Punkt senden
		movfw	zMin		; Minuten senden
		call	PrnZif2
		call	PrnPkt		; Punkt senden
		movfw	zSek		; Sekunden senden
		goto	PrnZif2

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

; ===============================================================
; Buchstabe 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
; ===============================================================
SuchA_Z		bcf	F_OK		; Erst mal nix gefunden
SuchA_Z_1	call	GetCmdByte	; 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	SuchA_Z__	; ... ja: Cmd gefunden

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

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

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

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

SuchA_Z_Ok	movfw	CmdByte		; Cmd-Byte zurueck
		andlw	01011111b	; Klein nach Gross
		movwf	CmdByte
SuchA_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
; ===============================================================
Such0_9		bcf	F_OK		; Erst mal nix gefunden
		clrf	MathArgA0	; Wert erst mal 0

		; ---------------------------
		; 1. Ziffer suchen
		; ---------------------------
Such0_9_1	call	GetCmdByte	; 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	Such0_9_1	; ... nein: Naechster

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

		; ---------------------------
		; Bisheriger Wert * 10
		; Neuen Wert addieren
		; ---------------------------
Such0_9_2	call	Mulu0810	; 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	GetCmdByte	; Cmd-Byte aus Cmd-Puffer laden
		skip_0	F_CmdE		; ... Cmd-Puffer noch nicht zu Ende
		goto	Such0_9_Ok	; ... ja: Ende

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

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

Such0_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
; ===============================================================
GetCmdByte	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
; ===============================================================
GetRxDByte	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

; ===============================================================
; ASCII-ZEICHEN-AUSGABE
; ===============================================================
PrnTrenn	movlw	" "		; Leerzeichen senden
		call	PrnChr
		movlw	":"		; Doppelpunkt senden
		call	PrnChr
		movlw	" "		; Leerzeichen senden
		goto	PrnChr

PrnCrLf		movlw	0x0D		; CR
		call	PrnChr
		movlw	0x0A		; LF
		goto	PrnChr

		; ---------------------------
		; Ziffern-Ausgabe in ASCII
		; ---------------------------
PrnZif0_9	bsf	F_Zif		; 1...9 gesendet
		addlw	"0"		; + ASCII-0
		goto	PrnChr		; Ziffer senden

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

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

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

PrnSpc		movlw	" "		; Leerzeichen
		goto	PrnChr

		; ---------------------------
PrnPkt		movlw	"."		; Punkt
		goto	PrnChr

PrnDpkt		movlw	":"		; DoppelPunkt
		goto	PrnChr

PrnSemi		movlw	";"		; Semikolon
		goto	PrnChr

PrnStern	movlw	"*"		; Stern
		goto	PrnChr

PrnKlauf	movlw	klauf		; Klammer auf
		goto	PrnChr

PrnKlzu		movlw	klzu		; Klammer zu
		goto	PrnChr

; ===============================================================
; Byte senden (im TxD-Puffer ablegen)
; Eingang:	W	Charakter (Byte)
; Ausgang:	TxDPos + 1 oder 0
;		TxDAnz + 1
; Benutzt:	MathTemp1, FSR, INDF
; ===============================================================
PrnChr		movwf	MathTemp1	; Byte retten
PrnChr_1	movfw	TxDAnz		; TxD-Puffer voll ?
		sublw	TxDLen-4	; TxDAnz > TxDLen-4 ?
		skip_C			; ... nein
		goto	PrnChr_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
; ===============================================================
PrnStrF		call	RdFlash		; Byte aus FlashSpeicher lesen
		skip_NZ			; ... kein StringEnde
		return

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

		goto	PrnStrF

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

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

		movfw	MathArgA0	; 1-er senden
		goto	PrnZif0_9

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

		movfw	MathErgA0	; 10-er evtl. senden
		call	FmtZif
		movfw	MathArgA0	; 1-er senden
		goto	PrnZif0_9

PrnZif2_8	call	PrnStern
		goto	PrnStern

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

; ===============================================================
; Ziffer hexadezimal senden
; Eingang:	W		Byte 0...255
; ===============================================================
PrnZifH		movwf	MathArgA0	; Byte retten
		movlw	"0"		; "0" senden
		call	PrnChr
		movlw	"x"		; "x" senden
		call	PrnChr
		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	PrnZifH_1

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

		movfw	MathArgA1	; AsciiLowNibbl zurueck
		movlw	7		; A...F
		addwf	MathArgA1	; und retten
PrnZifH_2	movfw	MathArgA1	; AsciiLowNibbl senden
		goto	PrnChr

; ===============================================================
; 8 Bit Multiplikation mit 10d
; Eingang:	MathArgA0	Multiplikant
; Ausgang:	MathErgA1..0	Multiplikant * 10
; ===============================================================
Mulu0810	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
; ===============================================================
Divu0810	movlw	255
		movwf	MathErgA0	; Ergebnis  10-er = 255
		movwf	MathErgA1	; Ergebnis 100-er = 255
		movlw	100
Divu0810_1	incf	MathErgA1	; Ergebnis 100-er + 1
		bcf	_C		; Carry = 0
		subwf	MathArgA0	; Divident - 100
		btfsc	_C		; ... Unterlauf
		goto	Divu0810_1

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

		addwf	MathArgA0	; Rest 1-er
		return

; ***************************************************************
; EEPROM- und FLASH-ROUTINEN
; ***************************************************************
; ---------------------------------------------------------------
; Daten aus Eeprom in Ram kopieren
; ---------------------------------------------------------------
RdEepKonf	movlw	16		; 16 Parameter
		movwf	zUni
		movlw	LOW(EepParam)	; Adr setzen
		movwf	AddrL
		movlw	RamParam
		movwf	FSR
		bcf	STATUS,IRP	; Bank 0 / 1
RdEepKonf_1	call	RdEeprom	; Eeprom lesen
		movwf	INDF
		incf	AddrL		; Adr + 1
		incf	FSR
		decfsz	zUni		; ... Zlr - 1 = 0
		goto	RdEepKonf_1

		return

; ---------------------------------------------------------------
; Byte aus EEPROM lesen
; Eingang:	AddrL		Eeprom-Adresse Low
; Ausgang:	W & EepByte	Daten-Byte aus Eeprom
; ---------------------------------------------------------------
RdEeprom	bsf	STATUS,RP0	; Bank 3
		bsf	STATUS,RP1
RdEeprom_1	btfsc	EECON1,WR	; ... letzter Schreibvorgang ist fertig
		goto	RdEeprom_1	; ... sonst warten

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

; ---------------------------------------------------------------
; Byte in EEPROM schreiben (ohne Verify)
; Eingang:	W	Daten-Byte
;		AddrL	Eeprom-Adresse
; Ausgang:	Daten-Byte im Eeprom
; Benutzt:	EepByte
; ---------------------------------------------------------------
WrEeprom	movwf	EepByte		; Daten-Byte retten
		bsf	STATUS,RP0	; Bank 3
		bsf	STATUS,RP1
WrEeprom_1	btfsc	EECON1,WR	; ... letzter Schreibvorgang ist fertig
		goto	WrEeprom_1	; ... sonst warten

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

		; ---------------------------
		; 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 ?
		btfsc	STATUS,Z	; ... 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
		movlw	0x55		; SchreibZyklus initialisieren
		movwf	EECON2
		movlw	0xAA
		movwf	EECON2
		bsf	EECON1,WR	; Schreibzugriff beginnen
		nop
		bsf	INTCON,GIE	; Globaler Int wieder ein
		bcf	EECON1,WREN	; Schreibzugriff sperren
		bcf	STATUS,RP0	; Bank 0
		bcf	STATUS,RP1
		return

; ---------------------------------------------------------------
; Byte aus FlashSpeicher lesen
; Eingang:	AddrL		FlashAdresse Low
;		AddrH		FlashAdresse High
; Ausgang:	W & EepByte	Daten-Byte aus FlashSpeicher
; ---------------------------------------------------------------
RdFlash		bsf	STATUS,RP0	; Bank 3
		bsf	STATUS,RP1
RdFlash_1	btfsc	EECON1,WR	; ... letzter Schreibvorgang ist fertig
		goto	RdFlash_1	; ... sonst warten

		bcf	STATUS,RP0	; Bank 2
		movfw	AddrH		; AddrH speichern
		movwf	EEADRH
		movfw	AddrL		; AddrL speichern
		movwf	EEADR
		bsf	STATUS,RP0	; Bank 3
		bsf	EECON1,EEPGD	; Zugriff auf FlashSpeicher
		bsf	EECON1,RD	; FlashSpeicher lesen
		nop
		nop
		bcf	STATUS,RP0	; Bank 2
		movfw	EEDATA		; Daten-Byte aus FlashSpeicher lesen
					; EEDATH wird nicht benutzt
		bcf	STATUS,RP1	; Bank 0
		movwf	EepByte		; Daten-Byte nach EepByte
		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 und 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

; ***************************************************************
; Text im 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

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

TxtHilfe	de cr, lf
		de "; V    = Version des Programms abfragen", cr, lf
		de "; D 24.06.05  = Datum setzen / abfragen", cr, lf
		de "; U 13.45.00  = Uhrzeit setzen / abfragen", cr, lf
		de cr, lf, 0

; ***************************************************************
; Daten im Eeprom
; ***************************************************************
		org	0x2100

		; -------------------------------------
		; VersionsEintrag ab Eeprom Adr. 0x2100...0x210F (max 16 Byte)
		de	Version
		de	Datum
		; -------------------------------------

		; Konfiguration ab Eeprom-Adr. 0x2110...0x211F (max 16 Byte)
		org	0x2110
EepParam	de	   2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
		de	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff

		end

  
		; Was fehlt hier noch ?