;Integer-zu-ASCII-Konvertierung für ATmega
;h#s 01/06
#ifndef ITOA_I90
#define ITOA_I90
;Im Gegensatz zu "itoa()" wird auf die serielle
;Schnittstelle ausgegeben, also eher wie "printf()".
;Ein "richtiges" itoa() entsteht durch Ausgabeumlenkung
;auf einen Speicherbereich innerhalb von putchar().
;Benötigt:
; putchar(r16) Zeichen schreiben (blockierend)
;Bietet (r32 bezeichnet r5:r4:r3:r2):
; neg4(r32) Negation 32bit
; zero4(r32) Ist 32-bit-Register Null
; divu4u1(r32,r17) Division 0:32bit/8bit=32bitR8bit
; SendNumberXxx(r32,...) Zahlenausgabe (immer 32bit!)
; Dez dezimal
; DezInt dezimal ohne Komma, vzb.
; DezIntMin dezimal ohne Komma, vzb. und minimale Feldbreite
;Gegenüber printf() folgende Einschränkungen:
; hexadezimale Kleinbuchstaben nur per Kompilierung
; Nur rechtsbündige Ausgabe
; Kein Zwangs-Plus, kein abgesetztes Vorzeichen
;Gegenüber printf() folgende Erweiterungen:
; beliebige Zahlenbasis (auch 2)
; Festkomma-Ausgabe
; Festlegbare Zeichen
.include "makros.i90"
#ifndef ITOA_DELIM ;Anwender kann Ausgabezeichen ändern
# define ITOA_DELIM ',' ;Komma-Zeichen (ggf. '.')
# define ITOA_MINUS '-' ;Minus-Zeichen (ggf. typografisches Minus)
# define ITOA_PLUS '+' ;Plus-Zeichen (für Zwangs-Plus)
# define ITOA_SPACE ' ' ;führende Leerzeichen (ggf. )
# define ITOA_DEC_0 '0' ;Ziffern (ggf. Präsentationsformen)
# define ITOA_HEX_A 'A' ;hexadezimales 'A' (ggf. Kleinbuchstabe)
#endif
neg4:
;32-bit-Zahl in R5:R4:R3:R2 negieren
;VR: R2-R5, R16
neg r2
negc r3
negc r4
negc r5
ret
zero4:
;32-bit-Zahl in R5:R4:R3:R2 auf Null testen
;VR: R16
mov r16,r2 ;Ist da noch ein Bit?
or r16,r3
or r16,r4
or r16,r5
ret
divu4u1: ;speziell für 32-bit-Zahlenausgabe
;PE: R5:R4:R3:R2 = Zähler vzl.
; R17 = Nenner, Divisor vzl. (bleibt erhalten)
;PA: R5:R4:R3:R2 = Ergebnis
; R16 = Rest
;VR: R2-R5,R16
push r18
clr r16 ;40-bit-Akku
ldi r18,32
div4: lslw r3,r2 ;Null einschieben
rolw r5,r4
rol r16
brcs div4sub ;Bei Überlauf immer subtrahieren
cp r16,r17
brcs div4nosub ;Wenn High-Teil < Nenner, dann nicht subtrahieren
div4sub:
sub r16,r17
inc r2 ;LSB im Divisionsergebnis setzen
div4nosub:
djnz r18,div4
pop r18
ret
SendNumberDezIntMin:
clr r19 ;minimale Feldbreite (1)
SendNumberDezInt:
ldi r18,0x80 ;ohne Komma, vzb.
SendNumberDez:
ldi r17,10 ;dezimal
SendNumber:
;32-bit-Zahl ausgeben
;PE: R5:R4:R3:R2 = Zahl
; R17 = Zahlenbasis
; R18 = Low-Nibble: Nachkommastellen (0 = kein Komma); Bit-Flags:
; 7: vorzeichenbehaftet (sonst vorzeichenlos)
; 6: Führende Nullen statt Dezimalstellen erzeugen
; 5: Zwangs-Plus (bei nichtnegativem Ergebnis, auch bei Null)
; 4: reserviert für Gruppierung (3 für dezimal, 4 für hex.)
; R19 = Feldbreite (0 = minimal)
;PA: R19 = -Anzahl der über die Feldbreite verbrauchten Zeichen
;VR: R0,R2-R5=0, R1,R16,R18,R19
mov r1,r18 ;Flags retten
andi r18,0x0F;Kommastellen im Low-Nibble
ldi r16,0x7F
sbrs r5,7 ;Vorzeichen?
and r1,r16 ;Negativ-Merker weg wenn positiv
clr r0 ;PUSH-Zähler
sbrc r1,7 ;negativ?
;Argument negieren
rcall neg4
sn4: rcall divu4u1
;aus Rest ASCII-Zeichen machen
addi r16,ITOA_DEC_0
cpi r16,ITOA_DEC_0+10 ;'0'..'9' OK, alles drüber nachbessern
brlo sn3 ;'A' bis 'F' (oder höher) draus machen
addi r16,ITOA_HEX_A-ITOA_DEC_0-10
sn3: ;Ziffer wegpushen (LIFO)
push r16
inc r0 ;PUSH-Zähler
dec r18 ;Dezimalkomma-Zähler
brne sn8
jbrs r1,6, sn9
ldi r16,ITOA_DELIM
push r16
inc r0 ;N-Bit bleibt gelöscht
sn8: brpl sn4 ;Weitere Ziffern zwangsweise ausgeben
sn9: rcall zero4
brne sn4 ;ja, weiter dividieren
ldi r16,ITOA_MINUS
jbrs r1,7, sn10
jbrc r1,5, sn7
ldi r16,ITOA_PLUS
sn10: push r16
inc r0
;Leerzeichen links ausgeben (immer rechtsbündig)
sn7: sub r19,r0 ;Noch Platz für Leerzeichen?
brcs sn5 ;nein, negativ
breq sn5 ;nein, gerade keins
sn6: ldi r16,ITOA_SPACE
rcall putchar
djnz r19,sn6
;Ziffern ausgeben
sn5: pop r16
rcall putchar ;Zeichen in umgekehrter Reihenfolge ausgeben
djnz r0,sn5
ret
#endif//ITOA_I90
Detected encoding: UTF-8 | 0
|