Pfad: Home => cq-dl-Beiträge => Teil 4 => Cw01_500.asm
STK200

cq-dl-Beiträge zu ATMEL-AVR-Mikrocontrollern


Assembler-Quellcode des CW-Programmes

; **********************************************************
; * CW.asm gibt Morsezeichen aus, die im EEPROM-Speicher   *
; * gespeichert sind und die über die serielle Schnitt-    *
; * stelle des STK500-Boards eingegeben werden. Die Aus-   *
; * gabe der NF erfolgt an PD5. Baudrate 9k6. 3,96 MHz     *
; * AVR AT90S8515. Programm (C)2002 by DG4FAC G.Schmidt    *
; * Version 0.1-500 vom 12.1.2002                          *
; * Bugs und Dankschreiben an info!at!avr-asm-tutorial.net *
; **********************************************************
;
.NOLIST
.INCLUDE "C:\avrtools\appnotes\8515def.inc"
.LIST
;
; Benutzte Register
;
; Register R0 wird für Lesen im Programmspeicher benutzt
; Register R0..R9 werden für Berechnungen benutzt
;
.DEF rcml = R10 ; Compare Match-Zahl, LSB
.DEF rcmh = R11 ; dto., MSB
.DEF rikl = R12 ; Interrupt-Anzahl kurz, LSB
.DEF rikh = R13 ; dto., MSB
.DEF rill = R14 ; Interrupt-Anzahl lang, LSB
.DEF rilh = R15 ; dto., MSB
.DEF rmp = R16 ; Multipurpose register, nicht bei Ints
.DEF rim = R17 ; Interrupt multi-purpose
.DEF rfl = R18 ; Flag Register, bei Ints und Normal
.DEF rst = R19 ; Statusregister bei Interrupts
.DEF rnsc = R20 ; Anzahl Punkte/Striche beim Senden
.DEF rmcd = R21 ; Morsecode beim Senden
.DEF x22 = R22 ; unbenutzt
.DEF x23 = R23 ; unbenutzt
.DEF rctl = R24 ; Zähler für Interrupt Tonerzeugung
.DEF rcth = R25 ;   (wird als Doppelregister verwendet)
;
; Register XH:XL R27:R26 Zeiger in den Empfangspuffer
; Register YH:YL R29:R28 Zeiger beim Senden
; Register ZH:ZL R31:R30 Zeiger für Lesen im Programmspeicher
;
; Bits im Flagregister
;
.EQU besc = 7 ; ESCape-Sequenz, hole Parameter
.EQU bmesc = 0x80 ; Maskenbyte für ESC
.EQU bstx = 6 ; Starte Sendeprozess
.EQU bmstx = 0x40 ; Maskenbyte für bstx
.EQU bctx = 5 ; Beende Sendeprozess nach Leerzeichen
.EQU bmctx = 0x20 ; Maskenbyte für bctx
.EQU btxa = 4 ; Sendeprozess aktiv
.EQU bmtxa = 0x10 ; Maskenbyte für btxc
.EQU btxe = 3 ; Abschluss des Sendens
.EQU bmtxe = 0x08 ; Maskenbyte für btxe
.EQU bpl = 2 ; Lange Pause zur Buchstabentrennung
.EQU bmpl = 0x04 ; Maskenbyte für bpl
.EQU bpk = 1 ; Kurze Pause zur Punkt/Strich-Trennung
.EQU bmpk = 0x02 ; Maskenbyte für bpk
.EQU bquiet = 0 ; Dauerhaft inaktiviert bei Leerzeichen
.EQU bmquiet = 0x01 ; Maskenbyte für bquiet
;
; Default-Werte
;
.EQU cfrq=1000 ; NF-Frequenz
.EQU cbpm=60 ; Gebegeschwindigkeit
;
; Basisdefinitionen variabel nach Schaltung
;
.EQU fqu = 3960000  ; Quarzfrequenz des AVR
.EQU fbd = 9600     ; Baudrate des UART
.EQU pctrl = PORTB  ; Control-Port für RTS/CTS (nicht benutzt)
.EQU pdrr = DDRB    ; Datenrichtungsregister Controlport
.EQU brts = PB2     ; RTS bit Input (beim STK nicht benutzt)
.EQU bcts = PB4     ; CTS bit Output (beim STK nicht benutzt)
.EQU pnfd = DDRD    ; Port für Richtung NF-Ausgabe (8515)
.EQU doc1 = PD5     ; NF-Ausgabe über OC1B-Pin (8515)
;
; Umrechnungen in Timer- und Counterwerte
;
.EQU nps = 8        ; Prescaler-Einstellung von Timer 1
.EQU ccm = fqu/nps/2; Konstante für Compare Match
.EQU cint =2941     ; Konstante für Int-Berechnung,
;                     experimentell ermittelt
;
; Definitionen fix
;
.EQU bddv = (fqu/(16*fbd))-1 ; Baudraten-Teiler
.EQU cnul = 0x00 ; Ende für nullterminierte Strings
.EQU chbsp = 0x08 ; Backspace character
.EQU chcr = 0x0D ; Carriage Return character
.EQU chlf = 0x0A ; Line Feed character
.EQU chff = 0x0C ; Form Feed character
.EQU chesc= 0x1B ; ESCape-Zeichen
;
; Definitionen I/O
;
; Definitionen für Timer-Controlregister TCCR1A
.EQU t1aus = 0b10000000 ; Schaltet den NF-Ausgang aus
.EQU t1ein = 0b01000000 ; Schaltet den NF-Ausgang ein
; Definition für Timer-Interrupt-Mask-Register TIMSK
.EQU t1CompInt=0b01000000 ; Interrupt bei Compare Match
; Definition für Timer-Controlregister TCCR1B
.EQU t1TaktInt=0b00001010 ; Timer-Takt CLK / 8, Clear Comp
; Definitionen für UART-Betrieb in UCR
.EQU siorxtx = 0b00011000 ; Betrieb RX und TX ohne Int
.EQU siorxint= 0b10011000 ; Betrieb RX mit Int, TX ohne Int
; Definition für den SLEEP-Mode in MCUCR
.EQU sleepmode=0b00100000 ; Aufwachen bei Interrupt
;
; Positionen im SRAM
;
.EQU sfrq = 0x0060 ; Eingestellte NF-Frequenz, Wort
.EQU sbpm = 0x0062 ; Eingestellte Geschwindigkeit, Byte
.EQU srtx = 0x0063 ; RAM-Puffer für Ein-/Ausgabezeile
.EQU srte = 0x00B3 ; Ende des nutzbaren Puffers, benutzt werden
;     auch zwei weitere Folgebytes
;
; Programm beginnt hier
;
.CSEG
.ORG 0x0000
;
; Reset- und Interrupt-Sprungtabelle 8515
;
	rjmp start ; Reset-Vector
	reti       ; Ext Int 0, nicht benutzt
	reti       ; Ext Int 1, nicht benutzt
	reti       ; Timer 1 Capture Event, nicht benutzt
	rjmp itc1m ; Timer 1 Compare Match A
	reti       ; Timer 1 Compare Match B
	reti       ; Timer 1 Overflow, nicht benutzt
	reti       ; Timer 0 Overflow, nicht benutzt
	reti       ; SPI STC, nicht benutzt
	rjmp iurxc ; UART Rx Complete
	reti       ; UART Tx data register empty, nicht benutzt
	reti       ; UART Tx All sent, nicht benutzt
	reti       ; ANA_COMP, nicht benutzt
;
; Interrupt-Service-Routinen
;
itc1m: ; Timer 1 Compare Match Interrupt
	in rst,SREG ; Rette Statusregister
	sbiw rctl,1 ; Zähler eins abzählen
	brne itc1mz ; Raus, wenn nicht Null
	mov rcth,rikh ; Setze Zähler auf kurze Dauer
	mov rctl,rikl
	ldi rim,t1aus ; Ton auf aus
	tst rnsc ; Zeichen fertig gesendet?
	breq itc1m0 ; Verzweige, wenn Zeichen fertig
	sbrc rfl,bpk ; kurze Pausen-Flag?
	rjmp itc1ms ; Pause zwischen Punkt/Strich war schon
	sbr rfl,bmpk ; Setze kurze Pause-Flag
	rjmp itc1my ; und Ausgabe auf inaktiv, fertig
itc1ms: ; Sende nächsten Punkt/Strich
	cbr rfl,bmpk ; Lösche kurze Pause-Flag
	ldi rim,t1ein; Ton an = Toggle
	dec rnsc ; Weiteren Punkt/Strich gesendet
	rol rmcd ; Punkt oder Strich senden?
	brcc itc1my ; Punkt senden
	mov rcth,rilh ; Lange Dauer einstellen
	mov rctl,rill
	rjmp itc1my
itc1m0: ; Zeichen fertig gesendet
	sbrc rfl,bctx ; Sendung beenden?
	rjmp itc1mx
	sbrc rfl,bpl ; Lange Pause senden?
	rjmp itc1mn ; Nächsten Buchstaben beginnen
	sbr rfl,bmpl ; Setze langes Pausen-Flag
	mov rcth,rilh ; Dauer auf lang stellen
	mov rctl,rill
itc1my: ; Stelle Modus inaktiv/toggle ein
	sbrc rfl,bquiet ; bei Leerzeichen Ton aus
	ldi rim,t1aus ; Ton auf aus
	out TCCR1A,rim
itc1mz:
	out SREG,rst ; Stelle Status wieder her
	reti
itc1mn:
	cbr rfl,bmpl ; Langes Pausen-Flag aus
	ld rim,y+ ; Nächsten Buchstaben lesen
	tst rim ; Null-Terminator
	breq itc1mn1
	cpi rim,chcr ; Ende der Zeile?
	brne itc1mn2
itc1mn1:
	sbr rfl,bmctx ; Setze beenden-Flag
	ldi rim,' ' ; Sende noch ein Leerzeichen
itc1mn2:
	out UDR,rim ; Debug
	subi rim,0x20 ; ASCII-Control-Zeichen weg
	brcc itc1mn3
	ldi rim,'?'-0x20 ; Sende Fragezeichen
itc1mn3:
	cpi rim,0x40 ; Kleinbuchstabe?
	brcs itc1mn4
	subi rim,0x20 ; in Grossbuchstaben wandeln
	cpi rim,0x40
	brcs itc1mn4
	ldi rim,'?'-0x20
itc1mn4:
	add rim,rim ; Mal 2 für Tabelle
	push ZH ; Register retten
	push ZL
	push R0
	ldi ZH,HIGH(2*morse) ; Zeichentabelle laden
	ldi ZL,LOW(2*morse)
	add ZL,rim ; Zeichen dazu zählen
	brcc itc1mn5 ; Kein Übertrag?
	inc ZH ; Übertrag
itc1mn5:
	lpm ; Lese Zeichencode aus Tabelle
	mov rmcd,R0 ; in Zeichencode-Register
	adiw ZL,1 ; Zeiger auf nächstes Byte
	lpm ; aus Tabelle lesen
	mov rnsc,R0 ; Anzahl Striche/Punkte
	pop R0 ; Wieder herstellen der Register
	pop ZL
	pop ZH
	tst rnsc ; Undefiniertes Zeichen?
	breq itc1mn ; Überspringe Zeichen
	sbr rfl,bmquiet ; Leerzeichen
	sbrs rnsc,7 ; Leerzeichen?
	cbr rfl,bmquiet ; Kein Leerzeichen
	cbr rnsc,0x80 ; Lösche höchstes Bit
	mov rim,YL ; CTS einschalten?
	sub rim,XL
	brcs itc1mn6 ; Ausschalten
	cpi rim,3
	brcs itc1mn6
	cbi pctrl,bcts ; CTS einschalten
	rjmp itc1ms
itc1mn6:
	sbi pctrl,bcts ; CTS ausschalten
	rjmp itc1ms ; Sende ein Zeichen
itc1mx: ; Sendung einstellen
	clr rim ; Timer 1 abschalten
	out TCCR1B,rim ; Timer-Takt aus
	out TCNT1H,rim ; Timer auf Null stellen
	out TCNT1L,rim
	out TIMSK,rim ; Timer Interrupts aus
	out TCCR1A,rim ; Timer Compare Mode aus
	cbr rfl,bmctx+bmtxa ; Ende-Flag und aktiv ausschalten
	sbr rfl,bmtxe ; Beenden-Flag einschalten
	ldi YH,HIGH(srte) ; Buffer auf Ende
	ldi YL,LOW(srte)
	st Y,rim ; Null-terminieren
	rjmp itc1mz
;
; UART Rx Complete Interrupt
;
iurxc:
	in rst,SREG ; Rette Statusregister
	in rim,UDR ; Lese Zeichen von SIO
	cpi rim,chesc ; ESCape-Sequenz?
	brne iurx1
	sbr rfl,bmesc ; Setze ESCape-Bit
	rjmp iurxz
iurx1:
	cpi rim,chbsp ; Backspace-Char?
	brne iurx2
	cpi XL,LOW(srtx) ; Schon auf Anfang?
	breq iurxz
	sbiw XL,1 ; Eine Position zurück
	ldi rim,chcr ; Zeilenabschluss
	st x+,rim
	clr rim ; Null-terminieren
	st x,rim
	sbiw XL,1 ; zurück
	ldi rim,chbsp ; Backspace zurücksenden
	rjmp iurxe ; Echo character
iurx2:
	cpi XL,low(srte) ; Pufferüberlauf?
	brcs iurx3 ; Nein, weiter
	ldi rim,chcr ; Character überschreiben
iurx3:
	cpi rim,chcr ; Zeilenende?
	brne iurxw ; Nein, in Puffer und fertig
	sbrs rfl,btxa ; Überspringe Ausgabe wenn aktiv
	out UDR,rim ; Carriage Return schon mal ausgeben
	st x+,rim ; CR-Zeichen anhängen
	clr rim ; Null terminieren
	st x,rim
	ldi XH,HIGH(srtx) ; Puffer auf Anfang
	ldi XL,LOW(srtx)
	sbr rfl,bmstx ; Über Flag Sender starten
	ldi rim,chlf ; Sende Zeilenvorschub
	rjmp iurxe ; Echo Line-Feed
iurxw:
	st x+,rim ; Speichere character
	ldi rim,chcr ; Abschluss terminieren
	st x+,rim ; mit Carriage return
	clr rim ; Null-terminieren
	st x,rim
	sbrs rfl,btxa ; Sender aktiv?
	rjmp iurxs ; Nein, CTS nicht prüfen
	mov rim,YL ; CTS ein oder aus?
	sub rim,XL ; Distanz zwischen Ein- und Ausgabe
	brcs iurxo ; Eingabe > Ausgabe: CTS aus
	cpi rim,3 ; mindestens zwei Zeichen Vorlauf?
	brcs iurxo ; Nein, dann CTS ausschalten
	cbi pctrl,bcts ; CTS einschalten
	rjmp iurxs ; Zeichen herstellen und raus
iurxo:
	sbi pctrl,bcts ; CTS ausschalten
iurxs:
	sbiw XL,2 ; Char wieder herstellen
	ld rim,x+ ; durch Lesen im SRAM
iurxe:
	sbrs rfl,btxa ; Keine Ausgabe, wenn aktiv
	out UDR,rim ; Echo character an SIO zurück
iurxz:
	out SREG,rst ; Stelle Status wieder her
	reti ; Ende des Interrupts
;
; Diverse eigenständige Unterprogramme
;
; Kopiert den EEPROM-Inhalt in das SRAM
;
ecopy:
	sbic EECR,EEWE ; Warte, bis EEPROM bereit
	rjmp ecopy ; Noch nicht bereit
	clr YH ; Startadresse im EEPROM auf Null
	clr YL
	ldi ZH,HIGH(sfrq) ; Startadresse im SRAM
	ldi ZL,LOW(sfrq) ; auf ersten Parameter
ecopy1:
	out EEARH,YH ; Leseadresse im EEPROM
	out EEARL,YL
	; 2313 hat nur 128 Bytes, daher nur unteres Register
	sbi EECR,EERE ; Lese-Strobe setzen
	cbi EECR,EERE ; und wieder ausschalten
	in rmp,EEDR ; Byte aus EEPROM-Datenregister lesen
	st Z+,rmp ; und in SRAM schreiben
	adiw YL,1 ; nächste EEPROM-Adresse anwählen
	tst rmp ; Null-Terminator?
	brne ecopy1 ; Nein: weiter kopieren
	ret
;
; Schreibe Parameter in das EEPROM zurück
;
ewrite:
	ldi ZH,HIGH(sfrq) ; Zeiger auf SRAM
	ldi ZL,LOW(sfrq)
	clr XH ; Zeiger in das EEPROM
	clr XL
ewrite1:
	sbic EECR,EEWE ; Frage Write-Bit im EEPROM
	rjmp ewrite1 ; ab und wiederhole bis EEPROM ready
	out EEARH,XH ; Schreibadresse im EEPROM einstellen
	out EEARL,XL
	ld rmp,Z+ ; Lese Byte aus SRAM in Register
	out EEDR,rmp ; in das EEPROM-Datenregister
	cli ; Keine Interrupts mehr beim Schreibvorgang
	sbi EECR,EEMWE ; Setze EEPROM Master Write Enable
	sbi EECR,EEWE ; Löse Schreibvorgang im EEPROM aus
	sei ; Jetzt Interrupts wieder zulassen
	adiw XL,1 ; Nächste EEPROM-Adresse in X-Zeiger
	cpi XH,$02 ; Ende EEPROM erreicht?
	brcc ewrite2 ; Überlänge! Null fehlt! Abbrechen!
	tst rmp ; Nullterminiert?
	brne ewrite1 ; Noch weitere Bytes schreiben
ewrite2:
	ret
;
; Lese16 wandelt eine ASCII-Zahl im Puffer in binär
; in R1:R0, bei Fehler Rückkehr mit gesetztem Carry-Bit
;
lese16:
	clr R0 ; Leeren Resultat-Register R1:R0
	clr R1
	ldi ZH,HIGH(srtx) ; Zeige auf Pufferanfang
	ldi ZL,LOW(srtx)
lese16a:
	ld rmp,Z+ ; Lese ASCII-Ziffer aus SRAM
	cpi rmp,chcr ; Ende der Zahl mit Carriage Return?
	breq lese16ok ; Zahl mit Carriage Return beendet
	cpi rmp,cnul ; Ende der Zahl mit Nullterminiert?
	breq lese16ok ; Ende mit Nullterminierung
	cpi rmp,'9'+1 ; Ziffer > ASCII-9?
	brcc lese16no ; Ja, Fehler!
	cpi rmp,'0' ; Ziffer < ASCII-0
	brcs lese16no ; Ja, auch Fehler
	subi rmp,'0' ; Wandle Ziffer in binär
	; Ab hier wird das bisherige Resultat mit 10 multi-
	; pliziert
	mov R2,R0 ; Kopiere Binärzahl in Hilfsregister R3:R2
	mov R3,R1
	add R0,R0 ; Multipliziere mit 2 durch Addieren 16 Bit
	adc R1,R1
	brcs lese16no ; Überlauf beim Addieren, Fehler!
	add R0,R0 ; Multipliziere mit 2 durch Addieren
	adc R1,R1
	brcs lese16no ; Überlauf beim Addieren, Fehler!
	add R0,R2 ; Addiere die Kopie der Zahl
	adc R1,R3
	brcs lese16no ; Überlauf beim Addieren, Fehler!
	add R0,R0 ; Multipliziere mit 2 durch Addieren
	adc R1,R1
	brcs lese16no ; Überlauf beim Addieren, Fehler!
	; Hier ist das Multiplizieren mit 10 beendet.
	add R0,rmp ; Addiere Ziffer zum Resultat hinzu
	brcc lese16a ; Kein Überlauf des unteren Bytes
	inc R1 ; Überlauf, oberes Byte erhöhen
	brne lese16a ; Kein Überlauf des oberen Bytes
lese16no:
	sec ; Setze Carry-Bit bei Fehler und kehre zurück
	ret
lese16ok:
	clc ; Clear Carry bei fehlerfrei
	ret
;
; Wandle 16-Bit-Zahl in R1:R0 in ASCII in R4..R9 (nullterm.)
; unterdrücke führende Nullen, sende Ergebnis ohne führende
; Nullen über die SIO
;
b16asc:
	clr ZH ; Z zeigt auf höchste ASCII-Ziffer in
	ldi ZL,4 ; Register R4 (Zeiger in Register!)
	ldi rmp,HIGH(10000) ; Beginne mit Zehntausendern
	mov R3,rmp ; oberes Byte in R3
	ldi rmp,LOW(10000)
	mov R2,rmp ; unteres Byte in R2
	rcall b16sub ; Ermittle ASCII-code der Zehntausender
	; Stelle durch fortgesetztes Subtrahieren von 10000
	ldi rmp,HIGH(1000) ; Weiter mit Tausendern
	mov R3,rmp
	ldi rmp,LOW(1000)
	mov R2,rmp
	rcall b16sub ; Ermittle ASCII-Code der Tausender
	clr R3 ; Weiter mit Hunderten
	ldi rmp,100
	mov R2,rmp
	rcall b16sub ; Ermittle ASCII-Code der Hunderter
	ldi rmp,10 ; Weiter mit Zehnern
	mov R2,rmp
	rcall b16sub ; Ermittle ASCII-Code der Zehner
	ldi rmp,'0' ; Rest sind Einer
	add rmp,R0
	mov R8,rmp ; R8 kriegt die ASCII-Einer
	clr R9 ; Nullterminieren in R9
	ldi ZL,4 ; Z auf 10000-er Zeichen
b16asc1:
	cpi ZL,9 ; Ende der Zeichenkette erreicht?
	breq b16asc2 ; Ja, raus
	ld rmp,z+ ; Zeichen aus Register kopieren
	cpi rmp,'0' ; Führende Null?
	breq b16asc1 ; Ja, weitermachen
b16asc2:
	dec ZL ; auf vorheriges Zeichen setzen
;
; Sende nullterminierten Text aus SRAM an SIO
; Z zeigt nicht ins SRAM, sondern in die Register mit
; dem Ergebnis!
; Das Registerpaar Z zeigt auf das erste Zeichen
;
txstxt:
	ld rmp,z+ ; Lese Zeichen aus SRAM/Register
	tst rmp ; Nullterminator erreicht?
	breq txstxt1 ; Ja, raus und fertig
	rcall txch ; Sende character über SIO
	rjmp txstxt ; Weitermachen mit nächstem Zeichen
txstxt1:
	ret
;
; Ermittle ASCII-Code einer Ziffer der 16-Bit-Binärzahl in
; R1:R0 durch fortgesetztes Subtrahieren einer 16-Bit-
; Hilfszahl (10000, 1000, 100, 10) in R3:R2. Tritt ein
; Überlauf ab, dann ist die Ziffer gefunden
; (Unterroutine für b16asc)
;
b16sub:
	ldi rmp,'0' ; Setze ASCII-Null
b16sub1:
	sub R0,R2 ; Subtrahiere die Hilfszahl
	sbc R1,R3 ; in 16-bit
	brcs b16sub2 ; Ende subtrahieren erreicht?
	inc rmp ; Einer geht noch!
	rjmp b16sub1 ; Weiter mit subtrahieren!
b16sub2:
	add R0,R2 ; Zu weit, addiere wieder zurück
	adc R1,R3
	st Z+,rmp ; ASCII-Ziffer in Register schreiben
	ret ; und zurück
;
; Sende nullterminierten Text aus dem Programmspeicher
; über die SIO aus
;
txtext:
	lpm ; Lese Zeichen aus Programmspeicher mit Zeiger Z
	tst R0 ; Ergebnis im Register R0 hat Null erreicht?
	breq txtend ; Ja, raus aus der Routine
	mov rmp,R0 ; Kopiere Zeichen in Senderegister
	rcall txch ; Sende character mit Prüfung
	adiw zl,1 ; Zeiger auf nächstes Zeichen
	rjmp txtext ; Weitermachen bis Null
txtend:
	ret ; Kehre zurück
;
; Liste alle Parameter über die SIO-Schnittstelle auf
;
txpar:
	ldi ZH,HIGH(2*txtpar1) ; Sende Parametervorspann
	ldi ZL,LOW(2*txtpar1) ; für NF-Frequenz
	rcall txtext
	lds R0,sfrq ; Zeige eingestellte Frequenz an
	lds R1,sfrq+1
	rcall b16asc ; Wandle in ASCII und sende Zahl
	ldi ZH,HIGH(2*txtpar2) ; Zeige Vorspann für
	ldi ZL,LOW(2*txtpar2) ; Geschwindigkeit an
	rcall txtext
	lds R0,sbpm ; Zeige Geschwindigkeit an
	clr R1 ; nur 8 Bit!
	rcall b16asc ; Wandle in ASCII und sende Zahl
	ldi ZH,HIGH(2*txtpar3) ; Zeige Vorspann für Counter-
	ldi ZL,LOW(2*txtpar3) ; Compare-Match-Zahl ccm an
	rcall txtext
	mov R1,rcmh ; Compare-Match-Zahl in R1:R0
	mov R0,rcml
	rcall b16asc ; Wandle in ASCII und sende
	ldi ZH,HIGH(2*txtpar4) ; Zeige Vorspann für
	ldi ZL,LOW(2*txtpar4) ; Anzahl NF-Ints bei Punkt an
	rcall txtext
	mov R1,rikh ; Anzahl Ints bei Punkt in R1:R0
	mov R0,rikl
	rcall b16asc ; Wandle in ASCII und sende
	ldi ZH,HIGH(2*txtpar5) ; Zeige Vorspann für
	ldi ZL,LOW(2*txtpar5) ; Anzahl NF-Ints bei Strich an
	rcall txtext
	mov R1,rilh ; Anzahl Ints bei Strich in R1:R0
	mov R0,rill
	rcall b16asc ; Wandle in ASCII und sende
	ldi ZH,HIGH(2*txtpar6) ; Zeige Vorspann für
	ldi ZL,LOW(2*txtpar6) ; Ausgabetext an
	rcall txtext
	ldi ZH,HIGH(srtx) ; Zeiger Z auf Ausgabetext im SRAM
	ldi ZL,LOW(srtx)
	rjmp txstxt ; Sende Inhalt SRAM nullterminiert
;
; 32-Bit durch 16-Bit-Division
;
; Dividiert 32-Bit-Zahl in R3:R2:R1:R0 durch R5:R4
; Ergebnis in R7:R6 (Ergebnis darf nicht > 16 Bit sein!)
;
div32:
	clr R7 ; Clear Ergebnis-Register R7:R6
	clr R6
	inc R6 ; Stopbit setzen für 16 Divisionsschritte
div32a:
	clc ; Null in Carry schieben
	rol R0 ; Multipliziere Divident mit 2
	rol R1
	rol R2
	rol R3
	brcs div32e ; Carry ist herausgerollt? Dann 1!
	cp R3,R5 ; Vergleiche oberes Byte
	brcs div32n ; Ergebnis ist kleiner, also eine 0
	brne div32e ; Ergebnis ist größer, also eine 1
	cp R2,R4 ; Vergleich MSB gleich, vergleiche unteres
	; Byte
	brcs div32n ; Unteres Byte kleiner, also eine 0
div32e:
	sub R2,R4 ; Ziehe den Divisor von den oberen 16 Bit ab
	sbc R3,R5
	sec ; Setze das Carry-Bit, Ergebnis ist eine 1
	rjmp div32s ; Zum Reinschieben in das Ergebnis
div32n:
	clc ; Lösche Carry-Bit, Ergebnis ist eine 0
div32s:
	rol R6 ; Schiebe Carry-Bit von rechts in Ergebnis
	rol R7
	brcc div32a ; Ende, wenn eine 1 links herausrollt
	ret
;
; Multipliziert 16-Bit-Zahl in R1:R0 mit 16-Bit-Zahl in R5:R4
; Ergebnis 32 Bit in R9:R8:R7:R6, für jeden Zahlenbereich
;
Mul16:
	clr R3 ; Leere obere zwei Bytes der 16-Bit-Zahl
	clr R2
	clr R9 ; Leere Ergebnis-Register R9:R8:R7:R6
	clr R8
	clr R7
	clr R6
Mul16a:
	clc ; Null ins Carry-Bit
	ror R5 ; Schiebe unterstes Bit Divisor in Carry
	ror R4 ; und dividiere Multiplikant durch 2
	brcc Mul16b ; Bit war eine 0, Addition überspringen
	add R6,R0 ; addiere Multiplikator 32 Bit zum
	adc R7,R1 ; bisherigen Ergebnis, jeweils mit
	adc R8,R2 ; Überlauf
	adc R9,R3
Mul16b:
	tst R4 ; Schon alle Bits ausmultipliziert?
	brne Mul16c ; Nein, LSB nicht Null, weiter
	tst R5 ; Teste auch oberes Byte
	brne Mul16c ; Nein, MSB nicht Null, weiter
	ret ; Fertig
Mul16c:
	clc ; Null in Carry-Bit schieben
	rol R0 ; Zahl durch Linksschieben mit 2
	rol R1 ; multiplizieren
	rol R2
	rol R3
	rjmp Mul16a ; und weiter dividieren
;
; Dividiert 32 Bit-Zahl in R3:R2:R1:R0 durch eine
; 8-Bit-Zahl in R4 und durch 256, Ergebnis gerundet
; in R8:R7, Wertebereich beachten!
;
Div32_8:
	clr R8 ; Ergebnisspeicher R8:R7:R6 leeren
	clr R7
	clr R6
	inc R6 ; Stopbit nach 24 Schritten setzen
Div32_8a:
	clc ; Carry-Bit leeren
	rol R0 ; Divident mit 2 multiplizieren
	rol R1 ; durch Linksschieben
	rol R2
	rol R3
	brcs Div32_8e ; 1 herausgerollt, Ergebnis=1
	cp R3,R4 ; Vergleiche oberstes Byte mit Divisor
	brcs Div32_8n ; Kleiner, Ergebnis = 0
Div32_8e:
	sub R3,R4 ; Ziehe Divisor von oberstem Byte ab
	sec ; Setze Carry für Ergebnis = 1
	rjmp Div32_8b ; Ins Ergebnis schieben
Div32_8n:
	clc ; Clear Carry für Ergebnis = 0
Div32_8b:
	rol R6 ; Ergebnis-Bit von rechts her hineinrotieren
	rol R7
	rol R8
	brcc Div32_8a ; Weiter, wenn eine Null herausrollt
	rol R6 ; Binäre Kommastelle eine 1, aufrunden?
	brcc Div32_8z ; Nein, wenn Null
	inc R7 ; Aufrunden LSB Ergebnis
	brne Div32_8z ; Kein Überlauf bei LSB-Erhöhung
	inc R8 ; Aufrunden MSB
Div32_8z:
	ret
;
; Rechne Parameter um in Timer- und Zählerwerte
; Oben definiert: ccm = Taktfrequenz / Prescale 8 / 2
;                 cint = Konstante
; Compare-Match-Zahl für Timer 1 = ccm / NF-Frequenz
; Anzahl Ints bei Punkt = cint * NF-Frequenz / Speed /256
; Anzahl Ints bei Punkt = Anzahl Ints bei Strich * 3
;
calctc:
	ldi rmp,BYTE4(ccm) ; Taktabhängige Konstante ccm
	mov R3,rmp ; in R3:R2:R1:R0, bei 4 MHz: 625.000
	ldi rmp,BYTE3(ccm) ; oder 0x00098968
	mov R2,rmp
	ldi rmp,BYTE2(ccm)
	mov R1,rmp
	ldi rmp,BYTE1(ccm)
	mov R0,rmp
	lds R4,sfrq ; durch NF-Frequenz in R5:R4
	lds R5,sfrq+1
	rcall div32 ; ergibt Compare Match-Zahl 16-Bit
;	rcall txreg ; Debug-Code!!!
	mov rcmh,R7 ; Ergebnis in Speicher rcmh:rcml
	mov rcml,R6
	ldi rmp,HIGH(cint) ; Konstante für Anzahl Ints
	mov R1,rmp ; bei 4 MHz: 1250
	ldi rmp,LOW(cint)
	mov R0,rmp
	lds R4,sfrq ; Mal NF-Frequenz in Hz
	lds R5,sfrq+1
	rcall mul16 ; Multplizieren 16 Bit * 16 Bit
;	rcall txreg ; Debug code!!!
	mov R3,R9 ; Ergebnis in R9..R6 nach R3..R0 kopieren
	mov R2,R8
	mov R1,R7
	mov R0,R6
	lds R4,sbpm ; durch Gebegeschwindigkeit teilen
	rcall div32_8 ; teilen 32-Bit durch 8-Bit
;	rcall txreg ; Debug code!!!
	mov rikh,R8 ; in Kurzspeicher kopieren
	mov rikl,R7
	mov rilh,R8 ; und in Langspeicher kopieren
	mov rill,R7
	add rill,rill ; Langspeicher mit 2 malnehmen
	adc rilh,rilh ; durch Addieren
	add rill,rikl ; und noch einmal dazu zählen
	adc rilh,rikh ; macht 3 mal so lang
	ret
;
; Debug code
;
; Display Byte in rmp in Hex an die SIO
;
;txbhx:
;	push rmp
;	swap rmp
;	rcall txnhx1
;	pop rmp
;txnhx1:
;	cbr rmp,0b11110000
;	subi rmp,-'0'
;	cpi rmp,'9'+1
;	brcs txnhx2
;	subi rmp,-7
;txnhx2:
;	rcall txch
;	ret
;
; Display Register R0..R9 in Hex an SIO
;
;txreg:
;	ldi rmp,chcr
;	rcall txch
;	clr ZH
;	clr ZL
;txreg1:
;	ld rmp,Z+
;	rcall txbhx
;	ldi rmp,' '
;	rcall txch
;	cpi ZL,11
;	brcs txreg1
;	ldi rmp,chcr
;	rcall txch
;	ret
;
; Hauptprogramm-Loop, Restart-Vektor
;
start:
	ldi rmp,HIGH(RAMEND) ; Init stack pointer im SRAM
	out SPH,rmp
	ldi rmp,LOW(RAMEND)
	out SPL,rmp
	ldi rmp,bddv ; Baudrate des UART einstellen
	out UBRR,rmp
	ldi rmp,siorxtx ; UART Tx und Rx ein, Ints aus
	out UCR,rmp
	cbi pdrr,brts ; Bit Richtung ist RTS-Eingang
	sbi pdrr,bcts ; Bit Richtung ist CTS-Ausgang
	sbi pctrl,bcts ; CTS ausschalten (invertiert!)
	rcall ecopy ; Kopiere EEPROM-Inhalt nach RAM
	ldi ZH,high(2*txtid) ; Sende ID-Text an SIO
	ldi ZL,low (2*txtid)
	rcall txtext
	clr rfl ; Flag auf Anfangswert leer
;
; Bedingungen für Interrupt-Betrieb herstellen
;
start1:
	rcall calctc ; Rechne Timerwerte und Ints aus
	rcall txpar ; Gib die eingestellten Werte über SIO
	ldi ZH,HIGH(2*txtcur) ; Sende Cursor-String
	ldi ZL,LOW(2*txtcur)
	rcall txtext
	ldi rmp,sleepmode ; Mode Idle für Sleep einstellen
	out MCUCR,rmp ; Aufwachen durch Interrupts
	ldi rmp,siorxint ; Enable RX mit Interrupt
	out UCR,rmp
	cbi pctrl,bcts ; Aktiviere CTS-Leitung
	sei ; Enable General Interrupt Flag
	ldi XH,HIGH(srtx) ; Puffer auf Anfang stellen
	ldi XL,LOW(srtx)
	rcall starttx ; und Text in CW aussenden
	ldi YH,HIGH(srte+3) ; Ausgabepointer hinter Ende
	ldi YL,LOW(srte+3) ; des Puffers stellen
	ldi XH,high(srtx+1) ; Puffer wieder auf Anfang
	ldi XL,low(srtx+1) ; und überschreiben des Textes
	clr rmp  ; Null-Terminierung schreiben
	st -x,rmp
	ldi rmp,chcr ; Setze leeres Textfeld mit CR
	st -x,rmp ; Jetzt zeigt X-Zeiger auf Anfang
;
; Interrupt loop, ab jetzt praktisch nur Kreisverkehr
; mit Ausbruch bei gesetzten Flagbits
;
loop:
	sleep	; CPU schlafen schicken
	nop ; Dummy-Befehl, bleibt bei Schlaf hier stehen
	nop ; nach dem Aufwachen ausgeführt
	sbrc rfl,bstx ; Sendeflag von Int-Routine gesetzt?
	rjmp starttx ; Ja: Starte Sendevorgang
	sbrc rfl,btxe ; Senden-beenden von Int-Routine?
	rjmp stoptx ; Beende Sendevorgang
	sbrc rfl,btxa ; Ist die Sendeausgabe aktiv?
	rjmp loop ; (Während Aussendung keine Parameter!)
	sbrc rfl,besc ; Parameter mit Menue holen?
	rjmp getpar ; Hole Parameter über SIO
	rjmp loop ; weiter im Kreis herum
;
; Startet Sendeprozess
;
starttx:
	sbrc rfl,btxa ; Nicht neu starten, wenn schon aktiv
	rjmp loop ; Wieder raus!
	cbr rfl,bmstx ; Setze Flag bit zurück
	sbi pctrl,bcts ; Stop CTS-Leitung
	ldi YH,HIGH(srtx) ; Sende-Pointer auf Pufferanfang
	ldi YL,LOW(srtx)
	mov rcth,rikh ; Kurze Verzögerung bis zum Start
	mov rctl,rikl
	clr rnsc ; Auszugebendes Zeichen beendet, provoziert
	; Laden des nächsten Zeichens bei der Senderoutine
	sbi pnfd,doc1 ; Ausgabe Pin OC1B=PD5 auf Ausgang (8515)
	clr rmp ; Setze Timer-Werte
	out TCCR1A,rmp ; OC1 inaktivieren
	out TCNT1H,rmp ; Timer-Register auf Null setzen
	out TCNT1L,rmp
	out OCR1AH,rcmh ; Compare Match auf Dauer ent-
	out OCR1AL,rcml ; sprechend der NF-Frequenz
	ldi rmp,t1CompInt ; Ermögliche Compare Int
	out TIMSK,rmp
	ldi rmp,t1TaktInt ; Clear Timer on Compare
	     ; Match und Prescaler CK/8
	out TCCR1B,rmp ; Timer-Takt starten
	rjmp loop ; und CPU bis zum Timer-Int schlafen legen
	; ab jetzt läuft wieder alles automatisch ab
;
; Beendet Sendevorgang
;
stoptx:
	cbr rfl,bmtxe ; Setze Beenden-Flag zurück
	cbi pctrl,bcts ; CTS wieder einschalten
	ldi ZH,HIGH(2*txtcur) ; Gib Cursor an SIO aus
	ldi ZL,LOW(2*txtcur)
	rcall txtext
	cpi XL,LOW(srtx) ; Schon Zeichen eingegeben?
	breq loop ; Nein, schlafen legen bis SIO-RX kommt
	ldi ZH,HIGH(srtx) ; Gepufferte Zeichen auf Cursor-
	ldi ZL,LOW(srtx) ; zeile ausgeben an SIO-TX
stoptx1:
	ld rmp,z+ ; Zeichen aus Puffer lesen
	rcall txch ; an SIO senden
	cp ZL,XL ; Schon alle ausgegeben?
	brcs stoptx1 ; Weiter ausgeben
	rjmp loop ; Alles ausgegeben, schlafen legen
;
; Getpar holt menuegesteuert Parameter vom User
;
getpar:
	ldi rmp,siorxtx ; Rx-Interrupts abschalten
	out UCR,rmp ; reiner Polling-Betrieb
getpar0:
	rcall calctc ; Rechne aktuelle Parameter um
getpara:
	rcall txpar ; Gib die Parameter aus
getparb:
	ldi ZH,HIGH(2*txtmenue) ; Gib Menue aus
	ldi ZL,LOW(2*txtmenue)
	rcall txtext
	rcall rxch ; Hole ein Zeichen von SIO
	cpi rmp,chesc ; Ende-Zeichen ESCAPE?
	brne getpar1 ; Nein, mach weiter im Menue
	cbr rfl,bmesc ; Setze Menue-Flag zurück
	rjmp start1 ; Ende, starte fast alles neu
getpar1:
	cpi rmp,'s' ; Speichern gewählt?
	brne getpar1a
	rjmp getpars ; geh zum Speichern
getpar1a:
	cpi rmp,'l' ; Lesen gewählt?
	brne getpar1b
	rjmp getparl ; geh zum Lesen
getpar1b:
	cpi rmp,'d' ; Default-Werte gewählt?
	brne getpar1c
	rjmp getpard ; Setze Default Werte
getpar1c:
	cpi rmp,'x' ; Testtext gewählt?
	brne getpar1d
	rjmp getparx
getpar1d:
	ldi ZH,HIGH(2*txtf); Zeiger auf Frequenzzeile
	ldi ZL,LOW(2*txtf) ; im Menue
	cpi rmp,'f' ; Frequenzeingabe gewählt?
	brne getpar1e
	rjmp getpar2 ; ja, hole Zahl
getpar1e:
	ldi ZH,HIGH(2*txtg); Geschwindigkeitseingabe
	ldi ZL,LOW(2*txtg) ; Zeiger setzen
	cpi rmp,'g' ; Geschwindigkeitseingabe gewählt?
	brne getpar1f
	rjmp getpar2 ; ja, hole Zahl
getpar1f:
	ldi ZH,HIGH(2*txtt) ; Zeiger auf Texteingabe
	ldi ZL,LOW(2*txtt)
	cpi rmp,'t' ; Texteingabe gewählt?
	brne getpar0 ; Nein, gib dem User noch mal das Menue
getpar2: ; Hole eine Zahlen- oder Texteingabe in den Puffer
	push rmp ; wird noch gebraucht (gewählter Menuepunkt)
	rcall txch ; Echo char an SIO zurück
	ldi rmp,chcr ; Mache neue Zeile
	rcall txch
	rcall txtext ; Gib den ausgewählten Menuetext aus
	ldi XH,HIGH(srtx) ; Empfangspuffer auf Anfang
	ldi XL,LOW(srtx)
getpar3:
	rcall rxch ; Hole char von SIO
	st x+,rmp ; in Puffer
	out UDR,rmp ; Echo an SIO
	cpi rmp,chcr ; Ende der Eingabe?
	brne getpar3 ; Weiter mit Eingabe
	clr rmp ; String Nullterminieren
	st x,rmp
	pop rmp ; Menuepunkt wieder vom Stapel holen
	cpi rmp,'t' ; Texteingabe gewählt?
	breq getpara ; Ja, schon fertig, gib die Parameter aus
	push rmp ; Wird weiterhin gebraucht (Menuepunkt)
	rcall lese16 ; ASCII-Zahl im Puffer in binär
	; in R1:R0 umwandeln
	pop rmp ; Menuepunkt wieder herstellen
	brcs getpare ; Fehler in Zahl, Fehlermeldung ausgeben
	cpi rmp,'f' ; Frequenz gewählt?
	brne getparg ; Nein, Geschwindigkeit gewählt!
	mov rmp,R1 ; Zahl zu groß?
	cpi rmp,0x10 ; Frequenz > 4095 Hz?
	brcc getparh ; Fehlermeldung Zahl zu groß
	cpi rmp,0x01 ; Frequenz < 256 Hz?
	brcs getpark ; Fehlermeldung Zahl zu niedrig
	sts sfrq,R0 ; Zahl ok, übertragen
	sts sfrq+1,R1
	rjmp getpar0 ; Rechne Parameter neu aus und gebe aus
getparg: ; Neue Geschwindigkeit eingegeben
	tst R1 ; Zahl <256?
	brne getparh ; Nein, Fehlermeldung Zahl zu groß
	mov rmp,R0
	cpi rmp,201 ; Zahl >200?
	brcc getparh ; Fehlermeldung Zahl zu groß
	cpi rmp,10 ; Zahl <10?
	brcs getpark ; Zahl zu niedrig
	sts sbpm,R0 ; Zahl ok, übertragen
	rjmp getpar0 ; Beginne Neu mit Berechnung und Ausgabe
getpars: ; Speichern der eingestellten Werte im EEPROM
	rcall ewrite ; Alles übertragen
	ldi ZH,HIGH(2*txteepw); Meldung ausgeben
	ldi ZL,LOW(2*txteepw)
	rcall txtext
	rjmp getparb ; Menuepunkte ausgeben und weiter
getparl: ; Lesen der Werte aus dem EEPROM
	rcall ecopy ; Alles ins SRAM übertragen
	rjmp getpar0 ; Alle Parameter neu berechnen und weiter
getpard: ; Default-Werte setzen
	ldi ZH,HIGH(sfrq) ; Zeiger auf Frequenz
	ldi ZL,LOW(sfrq)
	ldi rmp,LOW(cfrq) ; LSB Default-Frequenz setzen
	st z+,rmp ; in SRRAM-Speicher
	ldi rmp,HIGH(cfrq) ; MSB Default-Frequenz setzen
	st z+,rmp ; in SRAM-Speicher
	ldi rmp,cbpm ; Default-Geschwindigkeit
	st z+,rmp ; in SRAM-Speicher
	rcall getdeftext ; Default-text in Speicher
	rjmp getpar0 ; Alle Parameter neu berechnen und weiter
getpare: ; Fehlermeldung ausgeben
	ldi ZH,HIGH(2*txtzahl) ; Fehler in Zahl
	ldi ZL,LOW(2*txtzahl)
	rcall txtext
	rjmp getpara
getpark: ; Fehlermeldung ausgeben
	ldi ZH,HIGH(2*txtklein) ; Zahl zu niedrig
	ldi ZL,LOW(2*txtklein)
	rcall txtext
	rjmp getpara
getparh: ; Fehlermeldung ausgeben
	ldi ZH,HIGH(2*txthoch) ; Zahl zu hoch
	ldi ZL,LOW(2*txthoch)
	rcall txtext
	rjmp getpara
getparx: ; Test-Text ausgeben (nur in der STK-Version!)
	ldi XH,HIGH(srtx) ; Zeiger X auf SRAM-Text
	ldi XL,LOW(srtx)
	ldi ZH,HIGH(2*tsttext) ; Zeiger Z auf Testtext
	ldi ZL,LOW(2*tsttext)
getparx1:
	lpm ; Lese Zeichen
	st x+,R0 ; Zeichen in SRAM-Puffer
	adiw ZL,1 ; nächstes Zeichen
	tst R0 ; Zeichen eine Null?
	brne getparx1 ; weiter mit Zeichen
	rjmp getpara ; fertig
getdeftext: ; Default text in Speicher ab Z
	ldi rmp,'<' ; Verkehrsanfang, eingefügt 2313=>8515
	st z+,rmp
	ldi rmp,10 ; Testtext in Speicher
	mov R0,rmp ; in Zähler
getdeftext1:
	ldi rmp,'P' ; Paris
	st z+,rmp
	ldi rmp,'A'
	st z+,rmp
	ldi rmp,'R'
	st z+,rmp
	ldi rmp,'I'
	st z+,rmp
	ldi rmp,'S'
	st z+,rmp
	ldi rmp,' '
	st z+,rmp
	dec R0
	brne getdeftext1
	sbiw ZL,1 ; Eins zurück
	ldi rmp,'>'
	st z+,rmp
	ldi rmp,chcr ; Textpuffer mit Carriage Return
	st z+,rmp
	clr rmp ; und Nullterminator
	st z,rmp ; entleeren
	ret ; eingefügt bis hier 2313=>8515
;
; Warte auf char von der SIO mit Echo
;
rxch:
	in rmp,USR ; Lese Control register
	sbrs rmp,RXC ; Character vorhanden?
	rjmp rxch ; Noch nicht, weiter warten
	in rmp,UDR ; Lese character aud Datenregister
	ret ; und zurück
;
; Sende text character in rmp an SIO mit Prüfung
;
txch:
	push rmp ; Rette Zeichen auf Stapel
txch1:
	in rmp,USR ; Senderegister leer?
	sbrs rmp,UDRE ; Ist das UDRE-Bit gesetzt
	rjmp txch1 ; Nein, weiter warten
	pop rmp ; Hole Zeichen vom Stapel
	out UDR,rmp ; Character senden
	cpi rmp,chcr ; Nach Carriage Return noch ein
	brne txch2 ; Linefeed?
	ldi rmp,chlf ; auch den noch senden!
	rcall txch
	ldi rmp,chcr ; und Zeichen wieder herstellen
txch2:
	ret
;
; Morsecode der ASCII-Zeichen 0x20 bis 0x5F
; unteres Byte = Code (0=Punkt, 1=Strich)
; oberes Byte  = Anzahl Punkte/Striche
;                Bit 7 = 1: Leerzeichen
;
morse:
; Zeichen 20 .. 2F
.DB 0b11100000,0b10000011 ; Blank
.DB 0b01000000,5 ; ! = Warten
.DB 0b01001000,6 ; "
.DB 0b11011000,5 ; # = ñ
.DB 0b01101000,5 ; $ = á, å
.DB 0b01000000,5 ; % = é
.DB 0b00000000,0 ; & = nicht benutzt
.DB 0b01111000,6 ; '
.DB 0b10110000,5 ; (
.DB 0b10110100,6 ; )
.DB 0b00000000,0 ; * = nicht benutzt
.DB 0b00010100,6 ; + = Spruchende
.DB 0b11001100,6 ; ,
.DB 0b10000100,6 ; -
.DB 0b01010100,6 ; .
.DB 0b10010000,5 ; /
;Zeichen 30 .. 3F
.DB 0b11111000,5 ; 0
.DB 0b01111000,5 ; 1
.DB 0b00111000,5 ; 2
.DB 0b00011000,5 ; 3
.DB 0b00001000,5 ; 4
.DB 0b00000000,5 ; 5
.DB 0b10000000,5 ; 6
.DB 0b11000000,5 ; 7
.DB 0b11100000,5 ; 8
.DB 0b11110000,5 ; 9
.DB 0b11100000,6 ; :
.DB 0b10101000,6 ; ;
.DB 0b10101000,5 ; < = Verkehrsanfang
.DB 0b10001000,5 ; =
.DB 0b01010000,5 ; > = Verkehrsende
.DB 0b00110000,6 ; ?
;Zeichen 40 .. 4F
.DB 0b11110000,4 ; @ = ch
.DB 0b01000000,2 ; A
.DB 0b10000000,4 ; B
.DB 0b10100000,4 ; C
.DB 0b10000000,3 ; D
.DB 0b00000000,1 ; E
.DB 0b00100000,4 ; F
.DB 0b11000000,3 ; G
.DB 0b00000000,4 ; H
.DB 0b00000000,2 ; I
.DB 0b01110000,4 ; J
.DB 0b10100000,3 ; K
.DB 0b01000000,4 ; L
.DB 0b11000000,2 ; M
.DB 0b10000000,2 ; N
.DB 0b11100000,3 ; O
;Zeichen 50 .. 5F
.DB 0b01100000,4 ; P
.DB 0b11010000,4 ; Q
.DB 0b01000000,3 ; R
.DB 0b00000000,3 ; S
.DB 0b10000000,1 ; T
.DB 0b00100000,3 ; U
.DB 0b00010000,4 ; V
.DB 0b01100000,3 ; W
.DB 0b10010000,4 ; X
.DB 0b10110000,4 ; Y
.DB 0b11000000,4 ; Z
.DB 0b01010000,4 ; [ = Ä
.DB 0b11100000,4 ; \ = Ö
.DB 0b00110000,4 ; ] = Ü
.DB 0b00000000,8 ; ^ = Irrung
.DB 0b00110100,6 ; _
morseende:
.DW morseende-morse ; Prüfzahl, muss 0x0040 sein
;
; Testtext, nur in der STK-Version implementiert!
tsttext:
.DB "<THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 1234567890>",chcr,cnul
ChkT:
.DW ChkT
;
; Texte für die serielle Schnittstelle
; Hinweis: Die chk-Werte sind zum Überprüfen im Listing
; eingefügt. Es gibt einen Bug im AVR-Assembler von ATMEL,
; der zu falschen Adressen führt, wenn bestimmte Kombina-
; tionen von Byte-Konstanten verwendet werden. Dieser Bug
; ist seit zwei Jahren gemeldet und noch immer nicht besei-
; tigt! Teilweise sind die abenteuerlichen Konstruktionen
; in dieser Liste zur Umgehung dieses Bugs verwendet.
;
; Eingangsmeldung zu Beginn
txtid:
.DB chff,chcr
.DB "+---------------------------------+",chcr
.DB "| Morseprogramm (C)2002 by DG4FAC |",chcr
.DB "+---------------------------------+",chcr
.DB cnul,cnul
chk1:
.DW chk1 ; für Assembler Bug
; Text für Parameterliste
txtpar1:
.DB chcr,'E',"ingestellte Parameter:",chcr,'*'
txtf:
.DB "NF-Frequenz (256..4095 Hz) = ",cnul
chk2: ; Bug-Check
.DW chk2
txtpar2:
.DB " Hz ",chcr,'*'
txtg:
.DB "Geschwindigkeit (10..200 BpM)  = ",cnul
chk3: ; Bug-Check
.DW chk3
txtpar3:
.DB " BpM",chcr,' ',"ccm = ",cnul,cnul
chk4: ; Bug-Check
.DW chk4
txtpar4:
.DB ", Ints (kurz) = ",cnul,cnul
chk5: ; Bug-Check
.DW chk5
txtpar5:
.DB ", Ints (lang) = ",cnul,cnul
chk6: ; Bug-Check
.DW chk6
txtpar6:
.DB chcr,'*'
txtt:
.DB "Text = ",cnul
chk7: ; Bug-Check
.DW chk7
txtcur:
.DB chcr,'=','>',cnul
chk8: ; Bug-Check
.DW chk8
txtmenue:
.DB "Einstellungen: f=NF-Frequenz, g=Geschwindigkeit,"
.DB " t=Text, s=Speichern,",chcr
.DB " l=Lesen, d=Default, x=Test, ESC=Ende! ",chcr
.DB "(f,g,t,s,l,d,x,ESC) => ",cnul
chk9: ; Bug-Check
.DW chk9 ; Prüfzahl für Assembler-Bug
txtzahl:
.DB chcr,'F',"ehler in Ziffer oder Zahl bzw. Zahl zu gross!",cnul
chk10: ; Bug-Check
.DW chk10 ; Prüfzahl für Assembler-Bug
txtklein:
.DB chcr,'*',"* Zahl zu niedrig! **",cnul,cnul
chk11: ; Bug-Check
.DW chk11 ; Prüfzahl für Assembler-Bug
txthoch:
.DB chcr,'*',"* Zahl zu hoch! **",cnul,cnul
chk12: ; Bug-Check
.DW chk12 ; Prüfzahl für Assembler-Bug
txteepw:
.DB chcr,'P',"arameter ins EEPROM geschrieben.",chcr,cnul
chk13: ; Bug-Check
.DW chk13
;
; Copyright-Info
.DB "C(2)00 2ybD 4GAF C"
;
; Programm-Code Ende
;
; ******************************************
; * EEPROM-Inhalt mit Default beginnt hier *
; ******************************************
;
.ESEG
.ORG 0x0000
;
efrq:
; Die Default-NF-Frequenz
.DW cfrq
ebpm:
; Die Default-Geschwindigkeit in BpM
.DB cbpm
etxt:
; Dieser Text wird zu Beginn ausgegeben
.DB "hello!"
.DB chcr,cnul
etxte:
;
; Copyright-Info
;
.DB "(C)2002 by DG4FAC"
;
; Ende des EEPROM-Segmentes
;


©2002 by Gerhard Schmidt, DG4FAC
Webseite mit den Beiträgen: http://www.avr-asm-tutorial.net/cq-dl/index.html
Benutzung, Kopie und Weiterverbreitung der Seiten zulässig, solange die Copyright-Angaben im Text bleiben.