Hello, Guest the thread was viewed1.3k times and contains 44 replies

last post from BIF at the

(Wie) kann ein Basicprogramm seine eigenen Zeilen (teilweise) löschen?

  • Genauso wie man BASIC Zeilen editiert.

    Also zum komplett löschen einfach direkt die Zeilennummer eingeben, und RETURN drücken, weg ist die Zeile. Willst Du nur einen Teil der Zeile löschen, den Teil den man löschen will, mit DEL oder der Leertaste entfernen und dann RETURN drücken.


    Oder hab ich die Frage jetzt komplett falsch verstanden?

  • Ein gelegentlich verwendeter Trick für diverse Selbstmodifikation in BASIC ist, Direktmodusbefehle auf den Bildschirm zu schreiben, Cursor positionieren, ein paar "RETURN"-Zeichen in den Tastaturpuffer fummeln, so dass die Befehle (inklusive Rücksprung ins eigene Programm) automatisch ausgeführt werden.


    Ist natürlich hässlicher Murks, weil es erstens sichtbar ist (kann man mit Farben tricksen) und zweitens ein wenig fragil...

  • ja, ich glaube du hast mich falsch verstanden oder ich habe mich nicht genau genug

    ausgedrückt.


    Das Programm soll einzelne Zeilen selbst löschen, wenn sie durchlaufen wurden.


    Code
    1. also:
    2. 10 definiere Variablen
    3. 20 lese DATA Zeilen
    4. ...
    5. 100 lösche 10 & 20
    6. 110 ...
  • Also bei DATA-Zeilen nehme ich an, dass gewisse DATA-Werte/-Zeilen inaktiv werden sollen, weil nicht mehr gebraucht? Da ist es vermutlich einfacher das DATA-Token im BASIC-Text durch z. B. ein REM-Token zu ersetzen und die DATA-Zeilen sind für das nächste RESTORE:READ ... nicht mehr vorhanden.


    Wenn man die DATA-Zeilen am Anfang eines Programms positioniert kann man diese dann recht leicht identifizieren. Sonst muss man mit entsprechenden SYS-Aufrufen die Zeilenadresse ausfindig machen und da das erste Byte ansteuern und dort das erste Byte (was das DATA-Token sein solllte) ersetzen.


    Sonst könnte man auch noch Zeilen durch Umsetzen der Link-Adresse (auf die folgende Zeile) so manipulieren, dass diese auf die übernächste Zeile zeigt und somit die neue Zeile "übergeht".


    So richtig löschen, im Sinne von die Zeilen oder Zeiilenteile aus dem BASIC-Code zu entfernen, wäre schon recht aufwändig, würd ich meinen, aber wohl nur unter gewissen Umständen wirklich angebracht ...

  • Ein gelegentlich verwendeter Trick für diverse Selbstmodifikation in BASIC ist, Direktmodusbefehle auf den Bildschirm zu schreiben, Cursor positionieren, ein paar "RETURN"-Zeichen in den Tastaturpuffer fummeln, so dass die Befehle (inklusive Rücksprung ins eigene Programm) automatisch ausgeführt werden.


    Ist natürlich hässlicher Murks, weil es erstens sichtbar ist (kann man mit Farben tricksen) und zweitens ein wenig fragil...

    Okay...

    Ziel wäre, hinterher mehr Speicher zur Verfügung zu haben als die Löschzeilen in Anspruch nehmen.

  • Ziel wäre, hinterher mehr Speicher zur Verfügung zu haben als die Löschzeilen in Anspruch nehmen.

    Wenn DATA-Zeilen am Ende positioniert werden, kann man das BASIC-Text-Ende anpassen und mit CLR den Interpreter das verkürzte Programm zur Verfügung stellen. Das geht wirklich nur bei DATA-Zeilen mit Daten, die etwa in Speicher ge-POKE-t werden, da Variablen damit zerstört werden.

    Welche Daten sollen das sein?

  • Ziel wäre, hinterher mehr Speicher zur Verfügung zu haben als die Löschzeilen in Anspruch nehmen.

    Ein Lösungsvorschlag (aber halt nicht das, wonach du gefragt hast):

    Die DATAs in eine Datei schreiben und statt DATAs im Programm zu lesen, die Daten von dort zu holen.

    Dann benötigst du nur den Speicher für die Variablen selber, aber der Programmcode bleibt klein.

  • JeeK Ja, dass könnte es schon sein.


    Mir ist bei einem VC20-Programm aufgefallen, dass ich vor dem Start noch rel. viel Speicher zur Verfügung habe.

    Nachdem ein eigener Zeichensatz geladen und vorallem alle Variablen deklariert sind, wird es schon eng.

    Alles rund um den Zeichensatz kann nach dem Laden ja eigentlich weg.


    Die Idee war:

    - den Zeichensatz zu laden

    - die DATA-Zeilen und die Laderoutine zu "löschen"

    - um mehr freien Speicher für die Variablen zu haben


    Wenn ich dich richtig verstehe meinst du sowas wie:

    Code
    1. 10 GOSUB500
    2. 20 POKE 55,XX:POKE56,YY:CLR
    3. 30 ....
    4. 500 REM ZEICHENSATZ LADEN
    5. 510 DATA ...
    6. 570 RETURN

    LB & HB für die POKE-Zeilen durch Rumprobieren rausfinden?


    [EDIT] Goodwell Ja, das ist natürlich eine Möglichkeit aber ich wollte es mit einem One-FILEr probieren.

  • Gute Idee!

  • Oh, und was natürlich auch geht, wenn man einen Onefiler will aber irgendwie keinen Exomizer o.ä. mag: Alles in VICE laden, VICE-Monitor öffnen, bank ram, save "onefiler.prg" 0 0801 cfff (ggf. mit angepasster Endadresse statt cfff und "8" statt "0", wenn man ins Diskettenimage und nicht ins Host-Dateisystem schreiben will). Das schreibt dann einfach ein Speicherabbild so wie's ist in ein ladbares Programm und funktioniert auch mit Daten unterm BASIC-ROM. Geht auch analog mit diversen Monitoren direkt auf dem C64.


    Ansonsten (wenn man alles in BASIC halten will) gibt's auch speicherplatzsparende Möglichkeiten, Daten in DATA-Zeilen unterzubringen, da gab's hier auch letztens einen Thread zu. Alles besser und robuster als "selbstmodifizierender" BASIC-Code...

  • Mit folgendem Gerüst, schneidet man die Zeilen nach Zeile 999 ab.

    Bei Zeile 10 beginnt das eigentliche Hauptprogramm.

    Bei Zeile 1000 die Initialisierungsroutine, die einmalig - in diesem Fall die DATA-Zeilen verarbeitend - "initialisiert".

    Wenn man das Programm ein weiteres Mal aufruft, ist dann alles nach 999 weg. Die Daten sollten dann bereits irgendwo im Speicher liegen (Maschinencode, Spritedaten etc.). In Variablen einlesen bringt hier nichts, da zum Korrigieren aller entsprechenden Zeiger per CLR alle Variablen und Array gelöscht werden.

    In Zele 999 wird der CHRGET-Zeiger ausgelesen, der dann in 990 in Variable A zum Zeilenanfang umgerechnet wird. Zum Zeitpunkt des PEEK() befindet sich der CHRGET-Zeiger nach der geschlossenen Klammer. Da zwischen den beiden PEEKs eine Page-Grenze liegen könnte, erfolgt die Korrektur mit dem L>246 Ausdruck (der -1 wird, was das High-Byte korrigiert, weil es dann schon um eine Page höher liegt).

  • Für das Erstellen eines Onefilers gibt es mehrere Möglichkeiten, klingt hier im Thread ja auch schon an.


    Für die Entwicklungsphase reicht ein

    Code
    1. IF A=0 THEN A=1:LOAD"ZEICHENSATZ",8,1

    oder besser:

    Code
    1. IF [STARTADRESSE]<>[1. Byte Zeichensatz] OR [STARTADRESSE+1]<>[2. Byte Zeichensatz] THEN LOAD...

    (um nur ein mal nachzuladen, bei weiteren Startversuchen dann nicht mehr).


    Um aus Programm und Zeichensatz dann einem Onefiler zu erstellen, nutzt man einen Packer. Oder man lädt den Zeichensatz an den Anfang des BASIC-Programms und manipuliert die entsprechenden Zeiger in der Zeropage beim letzten Speichervorgang. Oder... usw. usf. Ist alles erheblich eleganter und weniger fehleranfälliger als ein selbstmodifizierendes BASIC-Programm.

  • Mit folgendem Gerüst, schneidet man die Zeilen nach Zeile 999 ab.

    Bei Zeile 10 beginnt das eigentliche Hauptprogramm.

    Bei Zeile 1000 die Initialisierungsroutine, die einmalig - in diesem Fall die DATA-Zeilen verarbeitend - "initialisiert".

    Wenn man das Programm ein weiteres Mal aufruft, ist dann alles nach 999 weg. Die Daten sollten dann bereits irgendwo im Speicher liegen (Maschinencode, Spritedaten etc.). In Variablen einlesen bringt hier nichts, da zum Korrigieren aller entsprechenden Zeiger per CLR alle Variablen und Array gelöscht werden.

    In Zele 999 wird der CHRGET-Zeiger ausgelesen, der dann in 990 in Variable A zum Zeilenanfang umgerechnet wird. Zum Zeitpunkt des PEEK() befindet sich der CHRGET-Zeiger nach der geschlossenen Klammer. Da zwischen den beiden PEEKs eine Page-Grenze liegen könnte, erfolgt die Korrektur mit dem L>246 Ausdruck (der -1 wird, was das High-Byte korrigiert, weil es dann schon um eine Page höher liegt).

    Das muss ich mir in Ruhe anschauen.

    Vielen Dank✌🏼

  • Wie liegt der Basic-Programmcode eigentlich im Speicher?

    In der Reihenfolge der Zeilennummern, oder in der Reihenfolge, in der ich die Zeilen eingebe?

    Wenn ich also die Programmzeilen 10 20 30 und 40 eingebe und anschließend die Zeile 5, liegt die Zeile 5 dann hinten im Speicher, oder wird der gesamte Programmcode dann neu sortiert und der Inhalt der Zeile 5 wandert nach ganz vorne und der Rest verschiebt sich im Speicher nach hinten?

    Stelle mir das bei größeren Programmen recht rechenintensiv vor.

  • https://www.c64-wiki.de/wiki/Speicherbelegung_(BASIC)

    https://www.c64-wiki.de/wiki/BASIC#Technische_Realisierung


    Edit: Naja "Rechenintensiv". Es muss ja nur etwas RAM verschoben werden (das kann auch der C64 sehr schnell) und die Verkettungspointer aktualisiert werden. Zweiteres WÄRE auch schnell, wenn der Code clever statt platzeffizient geschrieben worden wäre.


    Auf jeden Fall ist die Entscheidung, bei der Eingabe zu sortieren, wesentlich besser, als alles andere (LIST, GOTO, Ausführung generell) zu verlangsamen, weil die Interpreter letztendlich immer den ganzen Speicher nach der "richtigen" Zeile durchsuchen müsste...