;Integer-zu-ASCII-Konvertierung für ATmega
;h#s 01/06
;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
; divu4u1(r32,r17) Division 0:32bit/8bit=32bitR8bit
; IsZero4(r32) Prüft die vier Bytes auf Null
; 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
#ifndef ITOA_I90
#define ITOA_I90
.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 '+' ;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
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
push r18
clr r16 ;40-bit-Akku
ldi r18,32
div4: lsl r2 ;Null einschieben
rol r3
rol r4
rol r5
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
IsZero4:
mov r16,r2
or r16,r3
or r16,r4
or r16,r5
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); Flags:
; 7: vorzeichenbehaftet (sonst vorzeichenlos)
; 6: Führende Nullen statt Dezimalstellen erzeugen
; 5: linksbündig (rechtsseitig Leerzeichen ausgeben)
; 4: Zwangs-Plus (Nicht bei Null? Muss der Aufrufer testen!)
; (Gruppierung o.ä. denkbar)
; R19 = Feldbreite (0 = minimal, 127=maximal)
;PA: R19 = -Anzahl der über die Feldbreite verbrauchten Zeichen
;VR: R0,R1,R2-R5=0, 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 IsZero4 ;Ist da noch ein Bit?
brne sn4 ;ja, weiter dividieren
ldi r16,ITOA_MINUS ;Minus hat Vorrang vor Plus!
jbrs r1,7, sn7a ;Minus pushen
ldi r16,ITOA_PLUS
jbrc r1,4, sn7 ;Kein Plus? Nichts pushen
sn7a: push r16
inc r0
;Leerzeichen links ausgeben
sn7: sbrs r1,5
rcall sn5b
;Ziffern ausgeben
sn5: pop r16
rcall putchar ;Zeichen in umgekehrter Reihenfolge ausgeben
sbrc r1,5
dec r19
djnz r0,sn5
;Leerzeichen rechts ausgeben
sbrs r1,5
ret
sn5b: sub r19,r0 ;Noch Platz für Leerzeichen?
brmi sn5a ;nein, negativ
breq sn5a ;nein, gerade keins
sn6: ldi r16,ITOA_SPACE
rcall putchar
djnz r19,sn6
sn5a: ret
#endif//ITOA_I90
Vorgefundene Kodierung: ANSI (CP1252) | 4
|
|