Hallo Besucher, der Thread wurde 7,8k mal aufgerufen und enthält 29 Antworten

letzter Beitrag von Zirias/Excess am

Zahl in String umwandeln

  • 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?

  • 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:


    Arcade & Pinballs: F14 Tomcat, TOTAN, Visual Pinball, MAME Arcade Cab, Münzschieber Glückskarussel
    Musikproduktion: Korg Kronos 61 (2nd 256GB SSD 4GB), Korg M50 61, Akai MPD32, Alesis IO4, KORG Nanopad 2
    Retro: In liebevolle Hände abgegeben.
    Heimkino: Epson TW6000, Xbox 360 Kinect mit Amazon Prime, Nintendo Wii


    "Weise eine kluge Person auf einen Fehler hin und sie wird sich bedanken. Weise eine dumme Person darauf hin und sie wird dich beleidigen"


    Wenn man den Leuten erzählen würde, dass das Gehirn eine App ist, fangen sie vielleicht an, es zu gebrauchen.

  • 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
    1. Wenn Zahl < 0, dann gib '-' aus und negiere die Zahl.
    2. Zahl, Ziffer = Zahl / Basis (Integer-Division mit zwei Rückgabewerten, nämlich Quotient und Rest)
    3. Wenn Zahl != 0, dann
    4. push(Ziffer) (letzte Ziffer auf den Stack)
    5. rekursiver_Aufruf_dieser_Funktion() (gib die vorderen Stellen aus)
    6. pull(Ziffer) (letzte Ziffer zurückholen)
    7. 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:

    Code
    1. if (Zahl == -2147483648) output("-2147483648");

    :bgdev

  • 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... :zzz:


  • 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:



    Bei #$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 :freude:drunk: .

  • natürlich schöner, wenn man gleich Dezimal mit SED & ADC


    Schönheit ist gerade hier Geschmackssache :D

    BCD 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 :D


    Spannender ist HEX nach DEC und umgekehrt, aber dafür findet man Schnipsel auf Codebase :)


    PS: dabyter hat es erfasst :thumbsup:

  • 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... :hammer:

  • Das wäre alles viel einfacher, wenn der Mensch 16 Finger hätte...


    :dafuer:


    Oder zumindest nur vier pro Hand und Fuss als Kompromiss...



    Gleich mal das Hackmesser hol... :bgdev

  • Mac Bacon:


    Ich nehm' alles zurück... :anonym .


    Ich war wohl leicht dezimal verwirrt :drunk: .


    Edit zu Beitrag #08: Ein AND #$09 tut's natürlich NICHT :prof: .

  • Hehehe ich sag doch DEC in ASM ist einfach nur verwirrend, deshalb gleich lassen :bgdev
    Aber schön, dass du noch drauf gekommen bist
    Auflösung: Auch wenn bei max #$99 (<- DEC Mode) die Idee AND #$09 kreativ war, Nibble bleibt Nibble

    Code
    1. AND #%00001001


    ist da zum "Nibblen" eher ungeeignet ;)

  • Auflösung: Auch wenn bei max $99 (<- DEC Mode) die Idee ADC #$09 kreativ war, Nibble bleibt Nibble

    Quellcode
    1
    ADC #%00001001


    ist da zum "Nibblen" eher ungeeignet


    ADC wär' nicht das Problem. Es ging um AND... ÄÄÄÄÄND... :kill:


    @fröhn: Das sieht nett aus.