Source file: /~heha/basteln/PC/FunkUsb/dcf77franz9.zip/uprintf.S

#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-80