#define __SFR_OFFSET 0
#include "avr/io.h"
.global read_byte_pp,uprintf
// Liest ein Byte von Z und inkrementiert Z. Nicht aus C/C++ aufrufbar.
read_byte_pp:
sbrc r31,7
lpm r0,Z+
sbrs r31,7
ld r0,Z+
99: ret
88: rcall chrout
vuprintf: // R16 = Zusätzliches Arbeitsregister, um R22..R25 für 32 Bit frei zu halten
// „mini-usart-printf“ — schon recht umfangreich trotz kleiner Codegröße,
// kein Bedarf nach strrev() und Division
// Und benutzt von-neumannisierte Stringadressen
// PE: X = bidirektionale Durchreiche für chrout(byte r24 = Zeichen)
// Y = const va_list
// Z = const Formatstring
// PA: Z = hinter '\0' im Formatstring
// VR: R0, R16..R31 außer X, zurzeit außer W22
// chrout() darf L22 verändern, auch W20. X nach eigenem Ermessen bis zum Wiederaufruf.
// Y und Z nicht! Mit Y hat die Funktion Zugriff auf va_list, mit Z auf den Formatstring.
// Diese Funktion muss der Anwender (in Assembler) bereitstellen!
76: rcall read_byte_pp
tst r0
breq 99b
mov r24,r0
cpi r24,'%'
brne 88b
clr r17 // Push-Zähler
clr r18 // char width=0
ldi r19,-1 // char decimals=-1, darf nicht 0 sein/werden sonst hängt's
ldi r21,0 // byte flags=0
// Flags Bit 0: # Alternative Ausgabe (ungenutzt; etwa: Nachkomma-Nullen unterdrücken)
// 1: . Punkt (2E) oder Komma (2C)
// 2: Leerzeichen als Platzhalter für Vorzeichen
// 3: - Linksausrichtung (ungenutzt)
// 4: 0 Führende Nullen (30) statt Leerzeichen (20) (Nicht für negative Zahlen)
// 5: x Kleinbuchstaben (6x) statt Großbuchstaben (4x)
// 6: + Positives Vorzeichen
// 7: i Negatives Vorzeichen; vorzeichenbehaftete Zahl
75: rcall read_byte_pp
tst r0
breq 99b
mov r24,r0
ldi r25,1<<0
cpi r24,'#'
breq 1f // Bit 0 setzen
cpi r24,'.'
breq 8f
cpi r24,','
breq 9f
ldi r25,1<<2
cpi r24,' ' // does not conflict with '+' but suppresses "+0.0" -> " 0.0"
breq 1f
ldi r25,1<<3
cpi r24,'-' // linksbündig: nicht ausgewertet!
breq 1f
ldi r25,1<<4
cpi r24,'0' // leading zeros (nicht als Nachkommastelle erlaubt)
breq 1f
ldi r25,1<<6
cpi r24,'+'
brne 2f
1: or r21,r25
rjmp 75b // weitere Flags oder Formatierzeichen einlesen
8: ori r21,1<<1 // Punkt ist 2E, Komma ist 2C, deshalb Bit 1
9: ldi r19,1 // automatically assume one decimal place
rjmp 75b
2: // Weiter mit Flagzeichen
cpi r24,'*'
brne 2f
ld r24,Y+
ld r25,Y+
andi r24,15 // Stacküberläufen vorbeugen
4: // hier R24 = Feldbreite oder Dezimalstellen
tst r19 // Kein Komma oder Punkt = Dezimalstellen noch negativ?
brmi 1f // ja, Feldbreite setzen
mov r19,r24 // Dezimalstellen setzen
rjmp 75b
1: mov r18,r24
rjmp 75b
2: // weiter mit Konvertierungszeichen: "%c"?
cpi r24,'c'
brne 2f
ld r24,Y+
ld r25,Y+
888: rjmp 88b
2: // weiter mit Konvertierungszeichen: "%s"?
cpi r24,'s' // String vom RAM oder Flash, _nur_ linksbündig
brne 3f
push ZL
push ZH
ld ZL,Y+
ld ZH,Y+
1: rcall read_byte_pp
tst r0
breq 2f
mov r24,r0
rcall chrout
dec r18 // Feldbreite herunterzählen
dec r19 // Zeichenzahl herunterzählen
brne 1b
rjmp 2f
1: ldi r24,' ' // Ausgabe bei %s stets linksbündig: Leerzeichen danach
rcall chrout
2: dec r18
brpl 1b
pop ZH
pop ZL
rjmp 76b
33: // Ziffer?
cpi r24,'1' // Eins
brcs 888b // zur Zeichenausgabe ausbrechen
cpi r24,'9'+1
brcc 888b // zur Zeichenausgabe ausbrechen
subi r24,'0'
rjmp 4b // Feldbreite oder Dezimalstellen setzen
3: // weiter mit Konvertierungszeichen: Integerausgaben
ldi r20,2
cpi r24,'b' // binär?
breq 6f
ldi r20,8
cpi r24,'o' // oktal?
breq 6f
ldi r20,16
ldi r25,1<<5 // Kleinbuchstaben setzen
cpi r24,'x'
breq 7f
cpi r24,'X'
breq 6f // ohne Zusatzflags
ldi r20,10
cpi r24,'u'
breq 6f
ldi r25,1<<7 // Vorzeichenbehafteter Typ ("%i")
cpi r24,'i'
brne 33b
7: // weiter: Integerausgabe, R20 = Zahlenbasis, R25 = Zusatzflags
or r21,r25
6: ld r24,Y+ // Vorzeichenlose Typen (alles andere)
ld r25,Y+
tst r21
brpl 8f // Keine Vorzeichenbehandlung
tst r25
brpl 7f // Keine Vorzeichenbehandlung + kein Vorzeichen
com r25
neg r24
sbci r25,-1 // negieren
rjmp 8f // Minus bleibt
7: cbr r21,1<<7
8: sbrs r21,2 // Nicht +0 ausgeben bei "%+ 5,i °C"
rjmp 8f
adiw r24,0
brne 8f
cbr r21,1<<6 // Plus-Marker weg wenn Null
8:
//Divisionsroutine für großen Zähler und kleinen Nenner (<128)
// r0 = Rundenzähler Division, r17 = Push-Zähler für Stack, r16 = Rest => Zeichen
0: clr r16 // Mit Rest=0 anfangen
clr r0 // Rundenzähler, Bitzähler
7: lsl r24
rol r25
rol r16
cp r16,r20
brcs 2f
inc r24
sub r16,r20
2: inc r0
sbrs r0,4
rjmp 7b // 16 Runden (bis Bit 4 gesetzt)
// Ende Divisionsroutine: %0=Quotient, XL=Rest
//Rest in ASCII-Ziffer wandeln und pushen für LIFO
subi r16,-'0'
cpi r16,'9'+1
brcs 1f
subi r16,-7
sbrc r21,5
ori r16,1<<5
1: push r16
inc r17
// Ende der Wandlung
//Prüfen ob Dezimaltrenner 'raus muss
dec r19 // Nachkommastelle?
brmi 3f // <0: Im Vorkommabereich, zur Abbruchprüfung
brne 0b // Noch >0: weiter Ziffern ausgeben
ldi r16,',' // Null geworden: Punkt/Komma pushen
sbrc r21,1
ldi r16,'.'
push r16
inc r17
rjmp 0b // Vor dem Dezimaltrenner mindestens 1 Ziffer ausgeben
// Ende Dezimaltrenner
//Abbruchprüfung (wenn Quotient 0 ist)
3: adiw r24,0
brne 0b // weiter Ziffern ausgeben
cp r17,r18 // Feldbreite erreicht?
sbrc r21,4 // Bei führenden Nullen…
brcs 0b // Weitere Nullen pushen
// Ende Abbruchprüfung: Keine weiteren Ziffern.
//Vorzeichenausgabe
clr r16
sbrc r21,2 // geringste Priorität: Leerzeichen
ldi r16,' '
sbrc r21,6 // mittlere Prioritär: Plus
ldi r16,'+'
sbrc r21,7 // höchste Prioritär: Minus
ldi r16,'-'
tst r16 // Die Zeichen " +-" haben Bit 5 gesetzt
breq 33f
push r16
inc r17
// Ende Vorzeichenausgabe
//Restfeldbreite mit Leerzeichen auffüllen
33: sub r18,r17 // Restfeldbreite
rjmp 44f
4: ldi r16,' '
push r16
inc r17
44: dec r18
brpl 4b
// Ende Leerzeichenauffüllung der Restfeldbreite
//LIFO abbauen: Alles in umgekehrter Reihenfolge ausgeben
5: pop r24
rcall chrout
dec r17
brne 5b
// Ende LIFO-Abbau: r17 (Stapelzähler) ist wieder 0
rjmp 76b
uprintf: // von C aufrufbar. Kriege ich nicht mit Inline-Assembler hin!
push YL
push YH
in YL,SPL
in YH,SPH
adiw YL,5 // auf erstes Funktionsargument setzen
ld ZL,Y+
ld ZH,Y+
push r16
push r17
rcall vuprintf // diese Funktion bedient sich dann bei Y = va_list
pop r17
pop r16
pop YH
pop YL
ret
Detected encoding: UTF-8 | 0
|