Hallo Besucher, der Thread wurde 1,7k mal aufgerufen und enthält 17 Antworten

letzter Beitrag von Mac Bacon am

Strings füttern...

  • Jetzt hab ich mal ne ASM-Frage:


    ich möchte einen Basic-String mit 32 Byte Daten füttern, und das möglichst schnell. Der String ist immer der gleiche (von mir aus A$) und kann auch schon vom Basic aus vordefiniert werden (A$="{spc}*32").


    Wie schubse ich die Bytes in den String, so das er mit Basic weiterverwendet werden kann?


    Gruß, Gerd


    PS: keine BIF'sche-Basic-Lösung (die es auch geben mag), das soll schnell sein :D

  • @ADAC : Ich habe keine direkte Lösung, aber es mag helfen, wenn Du verstehst, wie BASIC Variablen abgelegt werden im Speicher.
    Hatte das mal analysiert und dokumentiert hier: Wie verschlüsselt V2 Basic Variablenwerte?

  • Buch 'C64 für Insider' besorgen *hust* wolkig heute *hust* und dirch die BASIC-Stringverwaltung stöbern. 'Wie finde ich eine String-/Variablen-Adresse', 'Wie baue ich einen temporären String', 'Wie weise ich den einer Variablen zu'. Bitte nicht der Versuchung erliegen, den String fix bei $c000 abzulegen und die Adresse in die Variable zu schreiben- das gibt Abstürze bei der Garbage Collection.

  • Du meist den Schmöker von Florian Müller.... gleich mal reingucken...


    Gruß, Gerd


    Nachtrag:
    Wenn ich ganz an Anfang eine Variable A$="TEST" definiere und an dieser auch nicht weiter rumspiele (ausser per ASM den inhalt verändern), kann ich davon ausgehen, dass die auch an ihrem Platz bleibt? Oder _muss_ ich jedesmal neu raussuchen lassen?

  • Laut Wiki klammert die GC bestimmte Strings aus:


    "Obiger Algorithmus klammert dabei jene Strings aus, die ihren Wert von einer im Programmtext befindlichen String-Konstante per Zuweisungen oder via READ erhält. In diesen Fällen wird tatsächlich der String im Programmtext referenziert und befindet sich damit nicht am String-Heap."


    Wobei das Wiki ja nicht unbedingt Recht haben muss...
    Aber falls es so ist, wäre eine praktikable Lösung eventuell sowas:


    1 a$="12345678901234567890123456789012"
    100 for i=0 to 31
    110 poke 2057+i,65+i
    120 next
    130 print a$


    Natürlich darf die Stringvariable dann nicht nochmal im Basic zugewiesen oder (anders als durch Pokes) verändert werden.

  • Der String ist immer der gleiche (von mir aus A$) und kann auch schon vom Basic aus vordefiniert werden (A$="{spc}*32")

    Vordefinieren klingt gut, das würde ich wohl auch machen - und zwar aus reiner Faulheit, weil man dann nicht die Interpreterfunktionen für Stringhandling heraussuchen muss.
    Allerdings darf die Definition nicht so aussehen: A$="HINZ", denn dann zeigt der String-Descriptor direkt in das Basic-Programm und die Asm-Routine würde darin herumschreiben (blöd, wenn z.B. ein Nullbyte in den String geschrieben wird - allgemein kommen CBM-Basic-Strings damit klar, aber Basic-Zeilen eben nicht).
    Zur Lösung des Problems reicht bereits sowas wie A$=""+"HINZ" oder A$="HINZ"+"KUNZ".

    'Wie finde ich eine String-/Variablen-Adresse'

    Je nach Anwendungsfall fände ich es durchaus legitim, wenn in der ersten Programmzeile steht:

    Code
    1. 0 a$="":rem create a$ before any other var so machine code knows where to find it

    Dann kann der Asm-Code direkt mit $2d/$2e loslegen und muss nicht erst die Variablenliste nach dem richtigen Deskriptor absuchen:

    anschließend kann man dann mit STA($fb),Y direkt in den String schreiben.

  • Das wird es dann wohl werden, denke ich. Ist ab einfachsten.
    Die Variable wird nachher nur lesend benutzt, beschrieben wird die mit einer kleinen ASM routine... und die muss eben wissen wohin sie schreiben muss (das was ist schon geklärt)...


    Vielen Dank an alle :thumbsup:


    Gruß, Gerd

  • Ich bin mir jetzt nicht sicher, ob es das trifft, was gesucht ist, aber mit folgender Routing kann man von einer Adresse in einen vorhandenen String
    hineinkopieren (genau soviel, wie der String lang ist).
    Aufruf: (angenommen die Routine liegt ab 49152)

    Code
    1. 10 A$="0123456890":A$=A$+A$
    2. 20 ADRESSE=1024
    3. 30 SYS 49152,ADRESSE,A$

    Wichtig ist, dass A$ keine String-Konstante ist, sondern "errechnet" wird, sonst verändert die Routine den im BASIC-Code liegenden String (was vielleicht unschön werden könnte). Damit wird der String am String-Heap gefüllt.

  • Die Lösund von Jeek finde ich auch sehr schön, da werde ich sicher noch einen Teil von übernehmen.


    Die Daten kommen Byteweise von der Seriellen in 32 Byte Sätzen, die könnte ich matürlich auch einfach zwichenparken.


    Ich hab jetzt erstmal die Einfachstlösung gemacht: Variable definieren und auch mir irgenwas Füllen (ganz am Anfang des Programms). Die 32 Byte abholen und einfach ab $9FE0 in die Variable schiessen.


    Wenn ich das Compilieren will muss ich nur die Startadresse der Variable auf 9FDE anpassen, sonst fährt das Mopped gegen die Wand. Ob das mit Pointer in der compilierten Version klappt weiss ich noch nicht... am C128 geht das, da ist das Handling vom Compiler aber wohl auch etwas anders... und solanges es nur das eine Byte zu ändern ist....


    Gruß, Gerd

  • Bitte nicht der Versuchung erliegen, den String fix bei $c000 abzulegen und die Adresse in die Variable zu schreiben- das gibt Abstürze bei der Garbage Collection.

    Bitte nochmal die Garbage Collection checken- könnte sein, daß das beim V2-BASIC tatsächlich funktioniert und Strings außerhalb des String-Heap einfach übersprungen werden. String-Konstanten im Programmtext gehen ja auch. Beim v3.5 bräuchte man einen gültigen R-Pointer, beim V7 bekommt man Probleme mit dem Banking...

  • Wilde Geschichte ... :D


    Die Variante mit dem vorbereiteten String, kann man natürlich auch noch dahingehend erweitern, dass bei Bedarf (wenn der String zu klein ist oder nicht im String-Heap liegt) einfach einen Platz vom Heap anfordert ...
    in A die gewünschte Länge
    JSR $B47D
    in X/Y bzw. $62/$63 ist die Adresse und in A bzw. $61 die Länge ($61-$63 ist der String-Descriptor).
    Da muss man dann den Descriptor nur noch in die Variable kopieren (der alte wird überschrieben, ein etwaige alter String ist damit String-Müll und ein Fall für die GC ;) ):

    Code
    1. LDY #2
    2. LOOP LDA $61,Y
    3. STA ($47),Y
    4. DEY
    5. BPL LOOP

    Wozu braucht man da eigentlich noch einen Compiler, wenn man schon zeitkritischen Sachen ohnehin in MC-Routinen auslagert? Solche Interpreter-MC-Module mögen die Laufzeitsyteme von Compiler wohl nicht so gerne (kann mich auch täuschen).

  • Bitte nochmal die Garbage Collection checken- könnte sein, daß das beim V2-BASIC tatsächlich funktioniert und Strings außerhalb des String-Heap einfach übersprungen werden. String-Konstanten im Programmtext gehen ja auch. Beim v3.5 bräuchte man einen gültigen R-Pointer, beim V7 bekommt man Probleme mit dem Banking...

    Ja, wird übersprungen. Es werden nur nach Strings gesucht, die unterhalb des neu aufgebauten String-Heaps (und oberhalb des Programms+Vars+Arrays) liegen. $C000 liegt drüber und sieht für die GC so aus, als wäre der String bereits "collected". ;)
    Bei der Backpointer-basierten GC von 3.5 und 7.0 werden Strings, die nicht im Heap liegen nicht berücksichtigt, weil der Heap sequenziell von oben unten durchgegangen wird. Wenn auf diesem Weg der der String nicht liegt, wird er auch nicht korrigiert. Wenn die Variable mit einem solchen "externen" String neu zugewiesen wird, dann erhält der String statt dem Backlink eine "to be collected"-Markierung mit FF+Len (die den Pointer auf den Descriptor in der Variable ersetzt) Mehr sollte da auch nicht passieren.
    Bei 7.0 muss man in der Tat aufpassen, da der String-Pointer immer in die Bank 1 zeigt (wenn ich mich recht erinnere).

  • Weil die weitere Stringverarbeitung und Ausgabe (Monitor und Datenträger) per Basic geht. Läuft natürlich auch so, aber die compilierte Fassung ist einfach deutlich geschmeidiger :D


    Die Data-Becker Compiler (64 und 128) fressen das ziemlich ohne murren und ist bisher auch die einzige Stelle um das grottenlahme 'GET#1,A$' loszuwerden.. das wird schnell ein paar hundertmal in einer Schleife aufgerufen.


    Die String-Länge ist in diesem speziellen Fall ja immer gleich (32 Byte) nur der Inhalt ändert sich. Eigentlich müsste die GC den in Ruhe lassen, der Sting ist ja zu jederzeit gültig und an den Pointer wird ja auch nicht mehr rumgespielt...


    Also quasi nur neuer Inhalt in alter Verpackung... also im Prinzip: Beschiss des Systems :anonym

  • Die String-Länge ist in diesem speziellen Fall ja immer gleich (32 Byte) nur der Inhalt ändert sich. Eigentlich müsste die GC den in Ruhe lassen, der Sting ist ja zu jederzeit gültig und an den Pointer wird ja auch nicht mehr rumgespielt...

    Die GC kann und wird ihn dennoch verschieben, denn die vereint ja alle derzeit aktiven Strings in einen kompakten Block am oberen Ende des Speichers. Um eine Verschiebung zu verhindern, müsste der String ganz oben im Speicher liegen (EDIT: Gerade noch gesehen, genau das tust Du ja schon durch die Fixierung auf $9fe0). Das lässt sich zwar auch erreichen, aber das Lesen des Deskriptors vor jedem Zugriff ist ja weder schwierig noch zeitaufwendig.