Hello, Guest the thread was viewed5.4k times and contains 20 replies

last post from RKSoft at the

LOAD innerhalb eines Programmes

  • Nabend, ich arbeite derzeit ja an einem Spiel und habe erst einen Loader für das Laden des Zeichensatzes und anschließend des Hauptprogrammes erstellt (per PRINT und anschließend per Tastenmemory ausgeführt). Okay, soviel dazu.
    Ich möchte nun im Hauptprogramm den Zeichensatz laden lassen. Beim C64er funktioniert dies ja einwandfrei, z.B:


    Code
    1. 10 ifpeek(14336)<>60thenload"charset",8,1
    2. 20 rem...hier gehts weiter...


    Mach ich dies unter BASIC 3.5 bzw. auf einem Plus/4 (oder im Emulator YAPE oder VICE), fängt das Laufwerk an zu arbeiten, hört nach 2 Sekunden aber wieder auf und das Programm läuft weiter. Kein Neustart des Programmes (würde ja eine Endlosschleife geben, wenn er das Charset nicht laden konnte bzw. abbruch), sondern es läuft weiter. Kann mir das jemand mal bestätigen? Wie bereits gesagt, habe ich das nicht nur im Emulator sondern auch am original Plus/4 getestet!


    PS.:
    hab eben in YAPE mal aus dem ,8,1 ein ,32 gemacht. Dies müßte eigentlich ein DEVICE NOT READY ERROR geben. Nix! Es scheint, als würde BASIC 3.5 den LOAD-Befehl nach dem THEN ignorieren

  • Nimm doch BLOAD, dafür ist es da. LOAD wäre der kleinste gemeinsame Nenner, aber wegen des unterschiedlichen Speicherschemas ist es ja eh nicht portabel.


    EDIT: BLOAD gibt es erst ab Basic7... :anonym


    Die PEEK-Adresse ist das erste Byte Deines Charsets? Zum Debuggen mach mal:

    Code
    1. 0 print "starting"
    2. 10 if peek(14336) <> 60 then print "loading":load "charset", 8, 1
    3. 15 print "ok"
  • Es scheint, als würde BASIC 3.5 den LOAD-Befehl nach dem THEN ignorieren

    dann versuche es doch mal mit GOSUB oder "GOTO" hinterm THEN ;)


    EDIT:
    und vielleicht einen/mehrere POKEs, die an den richtigen Stellen die Border-Farben ändert - zum debuggen ....
    Allerdings ist 264er nicht mein Gebiet, also keine Ahnung, wie solch ein POKE heissen könnte .... :anonym

  • Die Konstruktion soll ja ohnehin nur dazu dienen, daß das BASIC-Programm bei dem durch den LOAD-Befehl unerwünscht erfolgenden Neustart des Programms nicht in eine Endlosschleife gerät. Steht allerdings vorher durch Zufall bereits der "richtige" Wert an dieser Stelle, wird der LOAD-Befehl dann natürlich nicht ausgeführt.


    Entweder man benutzt anstelle dessen meine "Liebling-Konstruktion" (nicht!) mit "IFP=0THENP=1:LOAD..."


    oder:


    man setzt das hinlänglich bekannte Verfahren mit dem Spezial-SYS zum Holen der Parameter von LOAD/SAVE + KERNAL-Aufruf von LOAD auf C16/C116/+4 um:


    Code
    1. DN=PEEK(174):SYS43115"CHARSET.PRG",DN,1:POKE2034,0:SYS65493


    Zur Erklärung:


    "DN=PEEK(174)" holt die Geräteadresse des letzten Datei-Zugriffs. Damit funktioniert die Methode auch mit anderen Laufwerksnummern als 8.


    SYS43115 holt sich direkt dahinter (also ohne noch ein Komma, o.ä.) die Parameter so, als würde gerade LOAD oder SAVE ausgeführt. Allerdings tut es auch genau nur das.


    POKE2034,0 sorgt dafür, daß die Maschinenroutine beim nächsten SYS-Aufruf mit Akku=0 gestartet wird.


    SYS65493 ruft die KERNAL-LOAD Routine auf. Da das mit A=0 (und nicht mit A<>0) erfolgt, ist das wirklich ein LOAD (und nicht VERIFY).


    Gruß,


    Michael

  • @GI-Joe
    Danke für den Tipp, hätt ich auch mal drauf kommen können! :)


    Jedenfalls funktioniert es so. Und, der Charset wird nicht beim BASIC Speicher angerechnet, goil!


    @Mac Bacon und @Hexworx
    Die Adresse 14336 ist $3800 und dort möchte ich den Charset hinlegen. Der erste Wert dort ist dann 60 bzw 3C.



    @Mike
    Thx, diese Variante kannte ich nun noch nicht. Werde ich mir aber mal gleich notieren. :)

  • Quote from RKSoft

    der Charset wird nicht beim BASIC Speicher angerechnet, goil!

    Also, das fällt ja wohl eindeutig in die Kategorie Wunschdenken.


    Bei 16K RAM liegt der Charset am Ende, bei 64K sogar mitten im vom BASIC-Interpreter genutzten Platz für BASIC-Programme und deren Variablen. Wenn das Programm + numerische Variablen und Arrays noch nicht von unten aufgrund derer Größe mit dem Charset kollidieren, macht spätestens der von oben herab wachsende Stringheap deinen Zeichensatzdaten den Garaus.


    Du mußt dem BASIC-Interpreter schon explizit mitteilen, daß er $3800 bis $3FFF nicht mehr nutzen darf. Dies geht auch immer einher mit dem Löschen aller derzeit bestehenden Variablen, weswegen das unmittelbar nach dem Start des Programms geschehen sollte, mit:

    Code
    1. POKE55,0:POKE56,56:CLR

    Damit sperrst Du allerdings *alles* oberhalb von $3800 weg - Speicher ab $4000 ist dann also auch nicht mehr nutzbar.


    Aus diesem Grund ist es häufig sinnvoller, den Charset *unter* das BASIC-Programm zu legen. Das geht mit einem kleinen BASIC-Stub am normalen BASIC-Start, welcher die Start-Adresse für das BASIC-RAM hinter den Charset verschiebt und dann die "Nutzlast" (sprich: dein Spiel) startet. Der Charset ist dann "automatisch" geschützt, und die obige Konstruktion mit den zwei POKEs + CLR nicht mehr notwendig.


    Netter Nebeneffekt: der Charset ist dann auch integraler Bestandteil deines Programms, und das Nachladen beim Programmstart entfällt. :)

  • Die Adresse 14336 ist $3800 und dort möchte ich den Charset hinlegen. Der erste Wert dort ist dann 60 bzw 3C.


    O.K.


    Jedenfalls funktioniert es so.


    Da fehlt mir aber noch die logische Erklärung. Deine erste Version hätte dann aber genauso funktionieren müssen.

  • Mit anrechnen mein ich eigentlich, das FRE(0) mir nicht mehr den kompletten RAM Bereich $1000 bis $c7fff anrechnet. Ab $C000 hatte ich erst den Zeichensatz hingepackt, bei 64KB RAM kein Problem. Über einen kleinen BASIC Loader habe ich dann erst den Zeichensatz geladen, dann den BASIC Bereich kleiner gePOKEt und anschließend das eigentlich Spiel geladen. Soweit so gut. Nur die Ladezeiten gefielen mir nicht. Erst die 9 Blöcke laden und danach noch weitere 12 Blöcke für das BASIC Programm.


    Meine Idee:
    Pack das BASIC Programm zusammen mit dem Charset als ein PRG File auf Disk. Gesagt getan! +4 resetet und das PRG geladen. Ergebnis, ich hatte von 60669 nur noch um die 10-20KB. Klar, weil er halt den kompletten Bereich lädt und der +4 erkennt, im RAM liegt ein so großes Programm (Anm.: obwohl das eigentliche Programm keine 12KB hat, aber er den Zeichensatz mitrechnet inkl. alles was zwischen BASIC Programm und Zeichensatz liegt = ergo, Verschwendung!).


    Also eine neue Idee, die ich beim C64er immer angewandt habe:
    Beim Programmstart mittels PEEK den ersten Wert an der Adresse, wo der Zeichensatz liegt bzw. liegen soll, auslesen. Ist er nicht identisch mit dem ersten Wert meines Zeichensatzes, dann per LOAD den Zeichensatz laden; fertig! Funzt prima...am C64.


    Aber, der +4 will das so nicht. Wie anfangs beschrieben, führt der +4 oder C16 den Befehl nicht korrekt aus. Es gibt keine Fehlermeldung! Die Floppy läuft nur kurz an (getestet am original +4 mit 1541-2 sowie unter den Emulatoren YAPE und WinVICE) und das Programm läuft OHNE Neustart des Programmes weiter, so, als wäre da kein LOAD passiert.


    Soviel zu meinem Problem, siehe oben!


    Zum Thema $3800 bzw. bis $4000:
    Ich überlegte (und tu es immer noch), mein Spiel auch für den C16 zu entwickeln, da die Levels per OPEN,GET,CLOSE nachgeladen werden. RAM wäre noch genug da derzeit. Es gibt ja noch keine Musik ;). Vergiss also einfach die Begrenzung bis zu 16KB bzw. $4000. Dennoch danke für deine 2 POKEs. Ich kenn sie abeer bereits und wende sie auch an (siehe Frutti Man +4).



    O.K.


    Da fehlt mir aber noch die logische Erklärung. Deine erste Version hätte dann aber genauso funktionieren müssen.

    Dann teste das doch mal bitte selber aus. Ich versteh die Logik dahinter ebenfalls nicht. Wieso funktioniert ein LOAD nach IF...THEN nicht, aber nach IF...THENGOSUBXXX und bei XXX dann LOAD?



    Nachtrag:
    Hab es eben erneut getestet und bin total verwirrt. Enweder habe ich einen Fehler gemacht (Zeichensatz 14336 bzw. $3800 mit 49152 bzw. $c000 vertauscht) oder Emus und Originalkisten wollen mich vera*schen.


    Ich tippe, da dieser Programmcode nun wirklich geht, auf menschliches Versagen hin. Dennoch wundert es mich immer noch, wieso der Computer (Original) den Zeichensatz von Disk (habe die Dateien auf eine 1541 formatierte Arbeitsdisk gezogen) so nicht geladen hat bzw die Ladezeiten für 9 Blöcke so extrem kurz waren. Und nein, ich habe keine Turbolader am +4.


    Ich teste die Disk morgen nochmal bzw. les sie per USB2IEC neu ein auf meinem PC und schau, was dort falsch lief.
    Dennoche erstmal danke für eure Tipps.

  • wieso der Computer (Original) den Zeichensatz von Disk (habe die Dateien auf eine 1541 formatierte Arbeitsdisk gezogen) so nicht geladen hat

    Er hat sicherlich geladen, aber endlos immer wieder, weil das Byte nicht passte.


    Emus und Originalkisten wollen mich vera*schen

    Machen sie beim Programmieren ständig. Arxxxlöcher halt :D .


    menschliches Versagen

    So schwer es manchmal einzugestehen sein mag: So wird's sein :winke: .

  • Da gibt es in der Tat einen subtilen Unterschied zwischen LOAD im Programm und LOAD im Direktmodus:


    LOAD im Direktmodus setzt den Zeiger auf den Start der Variablen immer auf das Byte hinter der geladenen Datei und führt dann CLR aus. LOAD im Programmmodus macht beides nicht (und startet das derzeit laufende Programm neu, mit erhaltenen Variablen).


    Für mich stellt sich das jetzt so dar, daß Du die Zeichensatzdaten erst mal vom C64 bei $C000 übernommen hast. Dann auf dem +4 mit einem "Batchlader" im Direktmodus erst das Spiel und dann den Zeichensatz geladen hast und dann mit RUN das Spiel gestartet. Als Start der Variablen gilt dann eben $C800 - hinter dem Zeichensatz. Das BASIC-RAM ist beim +4 bei $FD00 (-1) zuende, damit liefert FRE(0) nach CLR den Wert 13568.


    Kann ausreichen, kann nicht ausreichen, auf jeden Fall ärgerlich ist, daß diese Methode massig Speicher zwischen Programmcode und Zeichensatz verschwendet. Und die Geschichte läuft so nur auf dem +4, weil der C16 da kein RAM hat (die Spiegel zählen nicht).


    Wird der Zeichensatz jetzt im Programm geladen, so wird der Pointer auf den Start der Variablen nicht angepaßt, er liegt immer noch direkt hinter dem BASIC-Programm! Damit liegt der Zeichensatz jetzt ungeschützt mitten im Variablenspeicher und wartet nur darauf von unten her durch numerische Einzelvariablen + Arrays und von oben her durch den Stringheap sturmreif geschossen, d.h., überschrieben zu werden. Nochmal: wenn's von unten her nicht klappt, von oben her auf jeden Fall wenn das Programm mit Stringvariablen hantiert: der Stringheap wächst bei jeder Zuweisung an einen String kontinuierlich nach unten. Ungültige Strings werden nicht gelöscht, sondern nur als ungültig markiert und erst bei der GC gelöscht, wenn Stringheap und Variablen+Arrays in der Mitte zusammenzustoßen drohen.


    Die Probleme im OP schienen aber wohl tatsächlich mit unglücklicher Handhabung (evtl. Benennung der Datei) zusammenzuhängen, bzw. Datei woanders hin geladen als erwartet, etc. Sowas passiert immer mal.


    ...


    Ich möchte dir nur wirklich von der Methode, das erste Byte der Datei abzuprüfen, abraten. Aus Sicht des Programms ist das beim ersten Programmstart ein Zugriff auf nicht-initialisierten Speicher und damit schlechter Stil. Die Speicherzelle kann alle möglichen Werte enthalten, zufälligerweise eben auch den gesuchten Wert. Mit dem Ergebnis, daß der Zeichensatz nicht geladen wird, obwohl man es vielleicht möchte.


    Die von mir genannte Konstruktion hält die Kontrolle über den Programmablauf beim Programmierer, es erfolgt kein "Neustart". Damit wird zwar immer beim Programmstart der Zeichensatz geladen, aber das erfolgt nachher - im "Produktionsbetrieb" - ja auch, und auch nur einmal, und das zuverlässig.

  • Ich hab' das jetzt auch noch mal selbst nachgestellt, und auch mit deiner Methode:

    Code
    1. 1 PRINT"PROGRAMMSTART"
    2. 2 IFPEEK(32768)<>85THENPRINT"LADE DATEI":LOAD"DATEI",8,1:PRINT"ICH MACH NIX."
    3. 3 PRINT"BIN HINTER LOAD"
    4. 4 IFPEEK(32768)=85THENPRINT"DATEI IST GELADEN"
    5. 5 IFPEEK(32768)<>85THENPRINT"DATEI IST NICHT GELADEN" READY.

    Auf dem *.d64 befindet sich neben dem Testprogramm auch die 3 Byte große Datei "DATEI" - die ersten zwei Bytes mit der Ladeadresse $8000 und dann dem Byte mit Wert 85.


    Nachdem "TEST" geladen wurde, führe ich vor dem ersten Start noch POKE32768,0 aus. Und dann sieht das ganze so aus:

    Da ist für mich kein irreguläres Verhalten erkennbar.



    Edit: Ändert man im Testprogramm den Dateinamen ab, um so zu "simulieren", daß es einen Übertragungsfehler beim Dateinamen gab (evtl. Groß-/Kleinschreibung etc.) bzw. die Datei nicht vorhanden ist, so gibt es - wie zu erwarten - einen ?FILE NOT FOUND ERROR.

  • Vielen, vielen Dank für eure zahlreichen Ratschläge. Es war tatsächlich menschliches Versagen meinerseits. Allerdings nicht, wie oben beschrieben. Ich habe beim Programmstart den Charset (mit dieser PEEK(14336) Prüfung) an Adresse $3800 geladen, danach die Variablen und Strings geDIMed.GROßER Fehler. Das Hauptprogramm geht bis $3100. Da nach dem Laden des Charsets die Variablen und Strings erstellt (DIM) wurden, passierte das unweigerliche. Nach dem BASIC Programm war dann im Speicher ab $3800 ebenfalls, so ein Zufall ^^, mit 00 gefüllt.


    Also habe ich den Charset auf $C000 bzw. 49152 gelegt und siehe da? Es geht!


    Also sorry, daß ich für so einen dämlichen Fehler eure Zeit verschwendet habe. Normalerweise passieren mir solche Fehler nicht.


    Fazit:
    LOAD nach IF...THEN funktioniert auch unter BASIC 3.5!

  • So als Abschluß: auf diese Weise erstellt man den Zeichensatz "unter" dem BASIC-Programm: CHARSET.PRG ist eine Kopie des VC-20-Zeichensatzes, und zwar nur des nicht-inversen GROSS/GRAFIK-SATZ, 1 KB groß, bei $1400.



    Beim Neuladen ab Einschaltmeldung sieht das dann so aus:



    POKEV+7,8 löscht unter anderem Bit 7 des Registers, so daß ein Zeichensatz nach wie vor nur 1K groß ist - also nur die ersten 128 Zeichen definiert werden brauchen, TED invertiert die dann automatisch. Mit Bit 7 = 1 können alle 256 Zeichen frei definiert werden. Der Cursor funktioniert übrigens unabhängig davon!


    POKEV+18,192 löscht unter anderem Bit 2 des Registers, der Zeichensatz liegt damit im RAM. Und POKEV+19,20 setzt die Startadresse des Zeichengenerator eben auf $1400.



    Zwischen dem BASIC-Stub ab $1001 und dem Zeichensatz ist noch etwas Platz wo man auch Maschinencode gut unterbringen kann. Sämtlicher Speicher ab Programmende bis Speicherende ist für die BASIC-Variablen problemlos nutzbar.


    Soll das Programm zusammen mit dem Zeichensatz nach Änderungen wieder abgespeichert werden, gibt man vor SAVE wie im ersten Bild zu sehen, POKE44,16 ein.



    CHARSET.PRG und ONE-FILED.PRG sind im *.zip Archiv drin.

  • Thx! Darf ich dann eigentlich, wenn ich den BASIC RAM Start verändert habe, noch überhaupt Änderungen am BASIC Programm (Hauptteil, welche halt nicht meh bei $1001 liegt) machen? Habe da irgendwas im Kopf, daß dann der Rechner abschmiert.

    Natürlich kannst du auch in einen Basic-Programm, welches nicht ab $1001 liegt, auch Änderungen vornehmen. Der Basicanfang sollte natürlich an die richtige Stelle verschoben sein.

  • RKSoft,


    selbstverständlich geht so eine Programmänderung auch bei geändertem BASIC-Start. Das ist es ja gerade, was ich im Beispiel ja ohnehin schon tue: ausgehend von einem nicht verhandenen BASIC-Programm gebe ich eine neue, erste Programmzeile ein.


    Was Du da in dem Screenshot machst, ist ziemlicher Murks: der Pointer auf den BASIC-Anfang steht in 43/44 mit Low-Byte in Adresse 43, High-Byte in Adresse 44 (nix Z80 ;) ) und üblicherweise bei $xx01. Im Byte davor muß der "Verschieber" Sorge dafür tragen, daß da eine 0 drin steht (hab ich in meinem Beispiel auch gemacht). Weiterhin muß ggfs. mit NEW auch wirklich der Zustand eines gelöschten Programms hergestellt werden - damit stehen dann hinter dem Nullbyte vor dem BASIC-Start noch zwei Nullbytes am BASIC-Start und der Start der Variablen in 45(Lo)/46(Hi) (a.k.a. "Ende des Programms + 1") ist dann das Byte dahinter.


    Also, für dein Beispiel:

    Code
    1. POKE43,1:POKE44,32:POKE8192,0:NEW
    2. 10 PRINT"BASICSTART IST NUN $2001"

    ... und das ohne Crash.


    P.S. Im Post oben hatte ich den POKE43,1 weggelassen, eben weil nach Power-On der BASIC-Start immer bei $xx01 liegt.

  • Das geht auch, zwischen dem tokenisierten BASIC-Programm und den Variablen kann man so Platz schaffen.


    Der Pointer wird auch passend geändert und der Inhalt mit verschoben, wenn im Programm Zeilen hinzufügt, geändert oder gelöscht werden.


    Da 65xx Code aber i.A. nicht relokabel ist, ist diese Methode nur geeignet, Maschinencode mit dem BASIC-Programm abzuspeichern, wenn sich am BASIC-Teil voraussehbar nichts mehr ändert. [1] Ein paar Programme auf der 1541-Test/Demo-Disk verwenden einen derart versteckten Teil in Maschinencode.


    Im Extremfall hat man dann eben nur noch den BASIC-Stub mit XXXX SYS XXXX, wonach dann der Rest des Programm nur noch aus Maschinencode besteht, der vom SYS aufgerufen wird.



    [1] ... und man auf der Zielmaschine arbeitet. Mit einer geeigneten Entwicklungsumgebung auf dem PC könnte man den MC-Teil neu assemblieren lassen, wenn der BASIC-Teil geändert wurde, und das ganze dann frisch zusammenlinken. Läuft aber dadurch effektiv auf einen "Neuaufbau" hinaus.