Hello, Guest the thread was called760 times and contains 41 replays

last post from JeeK at the

VIC20 : ASM-Code in String unterbringen und aufrufen ?

  • Ich möchte bitte eine ASM-Routine in einem String unterbringen und dann aufrufen, wie geht das beim VIC20?


    Das ist dann aber harter Stoff. :) Wäre es nicht angebrachter in einen freien Speicherbereich per Poke zu schreiben ?

  • Je nach Speicherausbau (5k, 8K, 16K, ...) verschiebt sich der Beginn des BASIC Speichers und damit die notwendige SYS Adresse.

  • Ich nutze das übrigens genau andersrum: eine kleine ASM-Routine schreibt mir hier einen immer gleich formatierten Datensatz in den String (um genau zu sein einen Directoryeintrag), den ich per Basic weiter verwurste.


    String einrichten, ASM-Code einlesen (warum ich das jetzt in zwei Blöcken gemacht habe weis ich gar nicht mehr.... können auch zwei kleine Routinen sein)


    Mit SYS49152 liest er dann einen DirEintrag in Z$ ein.

  • Ein paar Hinweise zur Vorgehensweise:


    1) Zuerst sollte man die ASM-Routine fertig assembliert haben und die Länge der Routine kennen.


    2) Das Ende der Routine sollte ein RTS sein, damit man vom Basic aus das Programm per SYS aufrufen kann...


    3) Jetzt (wie ADAC beschreibt) einen Platzhalter für die Routine anfertigen: Z.B. mittels 0 A$="XXXXXXXXXX" (die Anzahl der X spiegelt dann die Länge der Routine wider). Damit sollte auch klar sein, dass eine so abgelegte ASM-Routine nur eine bestimmte Länge haben kann, da die Länge einer Basic-Zeile begrenzt ist.


    4) Jetzt kann man mittels eines (Maschinensprache-)Monitors die Basic-Zeile suchen, dann die einzelnen Bytes dort händisch einfügen (sind ja maximal nur rund 80 Bytes (ja, geht auch länger, wäre aber komplizierter...)) und danach das Programm speichern.


    5) Jetzt ist das Programm nach dem Laden automatisch wieder enthalten.


    6) Hinweis: Entweder relokatiblen Code schreiben (besser, da ohne feste Sprungadressen) oder vor dem finalen Einfügen den Quelltext für die Adresse übersetzen.


    7) Es kann sein, dass das Basic-Programm nach dem Einfügen der Bytes nicht mehr zu Listen ist. Das beruht auf die "krummen" Bytes, die evtl. den Basic-Editor aus dem Tritt bringen. Deswegen ist dieses Verfahren eigentlich nur ganz am Schluss, wenn das Programm zu 100 % fertig ist, anzuwenden...



    Viel Erfolg wünscht

    Thomas

  • Man könnte den String auch in die letze Zeile verlegen und damit das List-Problem umgehen.


    Also


    1 goto 50000

    2 rem Hier gehts weiter..

    .

    .

    .

    50000 a$="<dein code>"

    50001 goto 2


    Nachteil: bei jeder Längenänderung des Codes davor verschiebt sich natürlich das ganze Spass .... ich würde das (wenn überhaupt) ganz vorne einbauen.



    Gruß, Gerd

  • Nachteil: bei jeder Längenänderung des Codes davor verschiebt sich natürlich das ganze Spass .... ich würde das (wenn überhaupt) ganz vorne einbauen.


    Das wird nichts bringen. Die Strings liegen immer hinter dem Programm. Wenn das Programm verändert wird, ändert sich auch immer wieder deren Offset. Man müsste das Assembler-Programm zuerst im Speicher haben und dann auf den Screen dumpen. Wie schon erwähnt wurde, weiss man dann wie gross der Platzhalter sein muss. Da stellt sich dann allerdings die Frage, warum man sich diese Quälerei antut, wenn das ASM-Programm doch schon im Speicher steht ? :)

  • Das wird nichts bringen. Die Strings liegen immer hinter dem Programm.

    Stimmt. Der Nachteil ist keiner. Das sollte auch gehen. Wichtig ist nur: der String muss als erstes zur Laufzeit definiert werden (wo das dann später im Basic gemacht wird ist egal)

  • Wo der String während der Laufzeit vom Interpreter abgelegt wird, ist nicht relevant. Der Einsprung erfolgt ja direkt in den Basic-Quelltext, irgendwo in den ersten Bytes des Basic-Speicherbereichs. Der kann sich ja allerdings aber mit einer Speichererweiterung verschieben und deshalb ist hier relokatibler Code sehr zu empfehlen. Man will ja nicht unbedingt noch eine Baustelle aufmachen...


    Gruß

    Thomas

  • Wo der String während der Laufzeit vom Interpreter abgelegt wird, ist nicht relevant.

    Du meinst 'wann' (also wann zur Laufzeit) und nicht 'wo' (also wo im Code), oder?

    Das ist schon relevant. Packst du den in einen String in der als erstes definiert wird, dann bleibt der auch da wo er ist. Jetzt die verschiedenen SpeicherConfigs beim VC20 mal außen vorgelassen.


    Also:

    10 B$="Test"

    20 A$="<dein code>"

    30 B$="Tiefgarage"


    Kann zwar laufen (nicht probiert) aber spätestens bei einer GarbadgeCollction isses dann aus.


    Oder reden wir grad aneinander vorbei? :gruebel

  • Eigentlich fällt das alles unter die Rubrik: So sollte man nicht programmieren!

    Sauberer ist es ein Assembler Unterprogramm in einem sicheren Speicherbereich unterzubringen (Z.B. im Cassettenpuffer, wenn keine Cassette benutzt wird) und und es dann mit einer festen SYS Adresse aufzurufen.

  • Strings, die direkt im Programmcode stehen, sind nicht von der Garbage Collection betroffen.

    Für das obige Beispiel werden für A$ und B$ keine Strings im oberen RAM Bereich angelegt.

    Stattdessen verweisen die Stringpointer von A$ und B$ direkt auf die Adressen im Code.

  • Ja, im Grunde haben wir beide Recht.


    Wenn

    10 A$="<dein Code>" machst, dann kannst du das natürlich direkt per SYS im Basicprogramm selber aufrufen.

    Aber genausogut auch am anderen Ende des freien Speichers (wo die Variablen liegen). Müsste beides gehen.


    Mein Anwendungsfall war ja etwas anders (aber doch ähnlich), ich beschreibe ja eine Variable (die am Anfang im Basic definiert ist) mit einer ASM-Routine um die dann nachher ohne viel Heckmeck im Basic auswerten zu können.


    Eine kleine ASM-Routine kann man auch so mal eben ins Basic einbauen (ist aber 128er Basic)

    Code
    1. 0 FAST:GRAPHIC5:TRAP9000
    2. 1 DATAAAEA20C6FFA000A590D00B20CFFF997002C8C020D0F14CCCFFFF
    3. !-
    4. !- * HILFSPROGRAMM GENERIEREN
    5. !-
    6. 4 AD=DEC("e00"):IFPEEK(AD)<>170THENREADE$:FORI=1TO52 STEP2:POKEAD+H,DEC(MID$(E$,I,2)):H=H+1:NEXT
  • Kann zwar laufen (nicht probiert) aber spätestens bei einer GarbadgeCollction isses dann aus.


    Oder reden wir grad aneinander vorbei? :gruebel

    Ja, wir reden etwas aneinander vorbei:


    Ich habe mal schnell den VICE als VC20 gestartet und "10 A$="xxxxxxxxx" eingegeben. Im Monitor sieht das dann so aus:


    1000: 00 17 10 0A 00 41 24 B2 22 58 58 58 58 58 58 58

    1010: 58 58 58 58 58 22 00 2B 10 14 00 8F .......


    In der ersten Zeile ist das "41 24 B2" das "A$=" und das dann folgende "22 58 58 58 ... 58 22" sind die xxxx in den Anführungszeichen.


    Ich würde jetzt einfach die x ändern:


    1000: 00 17 10 0A 00 41 24 B2 22 A2 FF CA D0 FD 60 58 <--- Habe ich jetzt einfach mal von meinem Oberarm abgeschrieben... :D

    1010: 58 58 58 58 58 22 00 2B 10 14 00 8F .......


    Aufrufen kann man die Schleife jetzt einfach mittels "SYS 4105" im Basic. Mag sein, dass der String auch in den Variablenspeicher kopiert wird, wäre mir aber egal, weil ich hier unten im Programmspeicher die Routine aufrufe...


    Gruß

    Thomas

  • Klar... wir reden von zwei (aber trotzdem den gleichen) Sachen. :rolleyes:


    Der ganze Spaß liegt natürlich (zu Laufzeit) auch im Variablenspeicher (ganz oben im freien Ram).


    Aber es ist wirklich ziemlich egal/wurst/wumpe welches von den Beiden dann aufgerufen wird. Machen beide das gleiche.


    Gruß, Gerd

  • Klar... wir reden von zwei (aber trotzdem den gleichen) Sachen. :rolleyes:

    Der ganze Spaß liegt natürlich (zu Laufzeit) auch im Variablenspeicher (ganz oben im freien Ram).

    Aber es ist wirklich ziemlich egal/wurst/wumpe welches von den Beiden dann aufgerufen wird. Machen beide das gleiche.

    Gruß, Gerd

    Sorry, wenn ich da nochmal dran rühre, aber es nicht das gleiche.

    Wenn ich auf dem VC20 oder C64 eine BASIC Zeile anlege:


    10 A$="TEXT"


    dann wird im Variablenspeicher die Variable A$ angelegt.

    Sie hat 3 Byte Informationen, eines für die Länge des Strings (hier 4) und 2 Byte für die Adresse.

    Und hier kommt der springende Punkt: BASIC legt in diesem Fall keine Kopie des Strings im Top RAM an,

    sondern lässt den Pointer der Variablen A$ direkt auf den String in der Code-Zeile 10 zeigen.

    Das ist eine Optimierungsmaßnahme, die Speicher spart.

    Wenn also der String "TEXT" durch Assembler Code überschrieben wird, kann man ihn über

    SYS (Adresse des Strings im Code) direkt aufrufen.

    Wenn man das über die Variable A$ machen will, muss man erst die Lage der Variablen A$ im RAM ausfindig machen,

    dort den Pointer auslesen um die Adresse des Strings zu erfahren und dann zur Adresse springen,

    die letztlich wieder auf den String im Code zeigt.


    Es ist also leichter, die Adresse von "TEXT" ausfindig zu machen und dorthin zu springen,

    als sich kompliziert über die Variable A$ dorthin zu hangeln.

    Hier bleibt nur das Problem unterschiedlicher Memory-Konfigurationen des VC20.


    Diese Eigenschaft, keine Kopien von konstanten Strings anzulegen, gilt übrigens nicht für den C128.

    Weil dort Strings immer in der Bank 1 gespeichert werden, der Code aber in Bank 0 steht,

    würde bei obigem Statement eine Kopie von "TEXT" in Bank 1 angelegt werden und der Pointer von A$

    dorthin verweisen. Stringpointer von Stringvariablen im C128 verweisen immer auf Adressen in Bank 1.

  • Sorry, wenn ich da nochmal dran rühre

    Kein Problem ;)

    Und hier kommt der springende Punkt: BASIC legt in diesem Fall keine Kopie des Strings im Top RAM an,

    sondern lässt den Pointer der Variablen A$ direkt auf den String in der Code-Zeile 10 zeigen.


    Hmmm das kann eigentlich nicht sein.

    Also ich mache das beim Programmstart:

    Code
    1. 10 Z$="123456{space*5}abcde":Z$=Z$+"123456{space*5}abcde"

    und beschreibe dann Z$ via ASM ab $9FE0

    Das ist diese kleine Routine die nach $C000 geladen wird (liest einen Directory-Eintrag aus)

    Da Z$ in Basic korrekt auslesbar ist muss auch der Pointer auf $9FE0 zeigen.....


    Gruß, Gerd