Immer wenn ich was in Assembler mache, suche ich eine Funktion die mir den Wert aus einem Register 8/16 Bit in eine Zahl umwandelt. Und jedes mal steh ich vor dieser Aufgabe und weiß nicht recht wie das am besten geht.
Ok, in meinem Fall suche ich sowas für Z80. Aber irgendwann steht ja auch mal 6510 auf dem Plan und dann wüßte ich immer noch nicht wie das vom Grundsatz her zu machen ist, also Prozessorunabhängig. 68000 ist ja auch was feines.
Wer weiß wie es geht?
Hallo Besucher, der Thread wurde 7,7k mal aufgerufen und enthält 29 Antworten
letzter Beitrag von Zirias/Excess am
Zahl in String umwandeln
- oobdoo
- Erledigt
-
-
Ich habe das auf einen 6502 für die Ausgabe eines Bytes mal so gemacht:
- Drei Speicherstellen mit je ASCII 0 initialisiert, (000)
- Vom Bytewert dann x mal 100 abgezogen, bis Bytewert < 100 ist, Speicherstelle 0 um x erhöht.
- Vom Bytewert dann x mal 10 abgezogen, bis Bytewert < 10 ist, Speicherstelle 1 um x erhöht.
- Speicherstelle 2 dann um Bytewert erhöht.Hier mal in C für eine 16 Bit Zahl:
-
-
Prozessorunabhängig ist nur bedingt, es ist vom einzelnen Computer abhängig, so zB davon wie das verwendete System Strings speichert etc.
Beim C64 sind die Screencodes für die einzelnen Ziffern bei $30 - $39 hinterlegt (PETSCII)
Um nun den Inhalt des Registers (ich beschränke mich mal auf 8bit) in einen String zu bekommen, musst Du:
-mit der höchsten Dezimalstelle vergleichen (in diesem Fall 100). Ist das Ergebnis größer, ziehst Du die Dezimalstelle ab, erhöhst die vorderste Position deines Strings um 1 und vergleichst erneut. Ist das Ergebnis kleiner wird die nächst niedere Dezimalstelle genommen und ebenso verfahren, bis zu bei den 'Einern' bist. Dann hast Du das Ergebnis im String. Den Platz für den String musst Du halt vorher auch schon allozieren.Beispiel: $65 (Dezimal 101) soll als String erstellt werden. Ein Byte kann maximal 3 Stellen haben die Du vorher auf $30 (die Null, siehe oben) setzt.
Nun wird dein Register mit $64(100) verglichen. Das Ergebnis ist größer, also wird das erste Byte deines Strings auf $31 erhöht (Per INC), und $64 von deinem Register abgezogen.
Danach Steht in deinem Register noch $01. Der erneute Vergleich mit $64 ergibt kleiner, also wird stattdessen mit $0A verglichen(und der Pointer auf die zu bearbeitende Stringstelle um eins verschoben). Auch hier ergibt der Vergleich kleiner. also wird nun mit $01 verglichen (und erneut der Pointer eins weiter gesetzt).
Hier ergibt sich Gleichstand, also wird der letzte Stringchar ebenfalls per INC erhöht. Bei einem Gleichstand und den einern muss natürlich nicht mehr weiter verglichen werden, ende der Fahnenstange.
In deinem String steht nun: $31 $30 $31. Bingo, das ist 101 zum Ausgeben auf den Screen.Bei 16/32 Bit läuft das analog, es wird nur komplexer weil die Vergleichsbefehle zumindest beim 6510 auf 1 Byte begrenzt sind, hier musst Du also in der Reihenfolge MSB /LSB vergleichen und gucken ob eine Subtraktion erfolgen muss.
Die Vergleichszahlen solltest Du Dir als Table anlegen.
Ach ja, das BasicROM hat btw. auch schon Funktionen zum Umwandeln von 16-Bit ints in Strings - siehe Dazu ab $aab8(Variable in String umwandeln und ausgeben) bzw. $BDDD (FAC in String) -
Eine allgemeine Lösung ist:
Code- Wenn Zahl < 0, dann gib '-' aus und negiere die Zahl.
- Zahl, Ziffer = Zahl / Basis (Integer-Division mit zwei Rückgabewerten, nämlich Quotient und Rest)
- Wenn Zahl != 0, dann
- push(Ziffer) (letzte Ziffer auf den Stack)
- rekursiver_Aufruf_dieser_Funktion() (gib die vorderen Stellen aus)
- pull(Ziffer) (letzte Ziffer zurückholen)
- Gib Ziffer aus
Das funktioniert auch mit anderen Basen als 10 und erzeugt keine führenden Nullen. Um eine Ziffer auszugeben, muss man natürlich noch zu ASCII wandeln, was bei Dezimalziffern durch Addition des '0'-Zeichens gemacht wird.
Einziger Wermutstropfen: Funktioniert nicht mit der kleinstmöglichen Integer-Zahl, da diese nicht sinnvoll negiert werden kann. Als ich sowas mal für 32-Bit-Ints programmiert habe, stand deshalb als erste Zeile davor: -
Irgendwie ganz einfach und dann doch wieder voll schwer.
-
Also ich bekomme das nur hin, indem ich nachträglich die führenden Nullen wechmache.
Ich mein mich erinnern zu können, das ich das damals auf dem 68000er ohne führende Nullen hinbekommen hatte, aber da hatte ich glaube ich auch mit einer Division arbeiten können.Naja, hier mal mein zusammengefrickelter Versuch.
So, jetzt aba in die Heia...
Zitatorg &4000
ld hl,65535
ld de,strcall int2str
ld hl,str
.PrintText ; hl,textbuf
ld a,(hl)
or a
ret z
call &bb5a
inc hl
jr PrintText;;----------------------------------------------------
;; Converts a 6-digit BCD number to a hex ASCII string
;;
;; input: DE = pointer to start of ASCII string
;; C:HL number to be converted
;; output: DE = pointer past end of ASCII string
;; destroys: A,F,D,E
;;-----------------------------------------------------
.int2str
call Num2Dec
ld b,5
ld hl,str
.w2
ld a,(hl)
cp "0"
jr nz,w1
inc hl
djnz w2
.w1
ld de,str
ld c,b
ld b,0
ldir
ld a,0ld (de),a
retNum2Dec ld bc,-10000
call Num1
ld bc,-1000
call Num1
ld bc,-100
call Num1
ld c,-10
call Num1
ld c,bNum1 ld a,'0'-1
Num2 inc a
add hl,bc
jr c,Num2
sbc hl,bcld (de),a
inc de
ret
.str
defb 32,32,32,32,32,0 -
Noch einer anderer Lösungsvorschlag.
Vorweg wäre es hierbei natürlich sinnvoll gleich in Dezimal zu rechnen und nicht den Wert erst später umzurechnen. Hier mal eine 'Umrechnung' durch simples abzählen:
CodeBei #$FF wäre das Ergebnis '02' in 'tausihunni' und '55' in 'zehnereiner'. Die beiden Nibble müsste man für die Ausgabe dann halt noch trennen. Für das untere Nibble reicht ja ein AND #$0F (
bzw. auch schon ein #$09); für das obere Nibble entweder AND #$F0 und 4 x ROR oder einfacher 4 x LSR.Wie gesagt, ist es natürlich schöner, wenn man gleich Dezimal mit SED & ADC rechnet. Das 'CLD' zum Schluß aber bitte nie vergessen, sonst crashed es ganz sicher im nachfolgenden Code.
Der BCD ist zugegebenermaßen zunächst sehr gewöhnungsbedürftig, z. B. wird aus einer direkten Umwandlung von #$FF zu Dezimal ein #65 + Carry-Flag. Das setzt sich zusammen aus Carry-Flag=100 + 50 Rest im oberen Nibble + 15 aus dem unterem Nibble. Und ein ADC #$10 ergibt das Gleiche wie ein ADC #$0A. Irgendwie aber auch wieder logisch .
-
Für das untere Nibble reicht ja ein AND #$0F (bzw. auch schon ein #$09)
Das üben wir aber noch mal.
-
Das wäre alles viel einfacher, wenn der Mensch 16 Finger hätte...
-
natürlich schöner, wenn man gleich Dezimal mit SED & ADC
Schönheit ist gerade hier GeschmackssacheBCD ist zugegebenermaßen zunächst sehr gewöhnungsbedürftig
Also ich konnte mich nicht dran gewöhnen.Habe mir damals[tm] mal eingebildet, der DEC mode MÜSSE doch für irgendwas gut sein, weil man ihn beim Cracken in so vielen Spielen für Score oder ähnliches wirklich findet. Habe demzufolge bei Highscore-Savern dann auch mit diesem Mode gerechnet.
Heute finde ich, dass SED und das DEC Flag und alles, was damit zu tun hat, eigentlich nur Pain in the Ass ist. Gerade wenn man sowieso String verwenden will, ist es oft praktischer einfach Digit für Digit gleich im PETSCII Code der Ziffern irgendwo im Screen abzulegen, spart Speicher und Hirnverknoterei mit dem dörrigen DEC Mode, aber das ist natürlich auch nur meine Meinung
Spannender ist HEX nach DEC und umgekehrt, aber dafür findet man Schnipsel auf Codebase
PS: dabyter hat es erfasst
-
Zitat von »Hexworx«
Für das untere Nibble reicht ja ein AND #$0F (bzw. auch schon ein #$09)
Das üben wir aber noch mal.
Ähm... Wie meinen ?einfach Digit für Digit gleich im PETSCII Code der Ziffern irgendwo im Screen abzulegen
Direkt in PETSCII/Screen-Codes hab' ich das wohl nie gemacht. Eigentlich immer 0-9 in einzelne Bytes. Einfacher geht's ja nicht. Wenn die Scores o. ä. direkt als Chars geprinted werden sollen ginge das natürlich. Ist aber ja auch nicht die Regel.Die Ausgabe HEX in DEC ist zum Glück ja auch eher die Ausnahme. Aber man könnte...
-
Das wäre alles viel einfacher, wenn der Mensch 16 Finger hätte...
Oder zumindest nur vier pro Hand und Fuss als Kompromiss...
Gleich mal das Hackmesser hol...
-
-
-
Ist sogar für 32 Bit, lässt sich aber natürlich leicht auf 16 Bit reduzieren:
-
jau den snippet meinte ich oben, danke
-
Auflösung: Auch wenn bei max $99 (<- DEC Mode) die Idee ADC #$09 kreativ war, Nibble bleibt Nibble
Quellcode
1
ADC #%00001001ist da zum "Nibblen" eher ungeeignet
ADC wär' nicht das Problem. Es ging um AND... ÄÄÄÄÄND...@fröhn: Das sieht nett aus.
-
jaaa schon selbst gefixt ist spät
-