Sinn der USR()-Funktion?

  • LordRudi schrieb:

    Hab nie den Sinn der USR()-Funktion im C64-Basic verstanden.
    Ja diese Frage ist schon sehr gerechtfertigt.
    Was ist die Existenzberechtigung von USR() wenn es doch eh schon SYS gibt.

    Die Antwort ist (aus meiner Sicht): Es gibt nicht wirklich eine.

    Mit SYS kann man eine Assembler routiene aus Basic heraus aufruhfen.
    Und mit USR eben auch.
    Bei USR ist eben zusätzlich noch definiert wie Parameter an die Routine übergeben werde und von dort zurückgegeben werden.
    Das ginge mit SYS auch, - man muss es halt nur selber definieren.

    Von der Idee der Basic-Designer, war SYS wohl eher zum Aufruhf von AssemblerProzeduren und USR zum Aufruhf von AssemblerFunktionen gedacht (Funktionen vs. Prozeduren; das war so ein HauptPattern damals).
    CIA and Co have reached the C64 - see the proof in the Hack Attack Trailer
  • Zaadii schrieb:

    Bei USR ist eben zusätzlich noch definiert wie Parameter an die Routine übergeben werde und von dort zurückgegeben werden.
    Das ist aber schon superpraktisch, oder? Sonst muss man ja die Eingabeparameter erst irgendwohin poken, was langsamer ist und den Basiccode unnötig aufbläht.
    ────────────────────────────────────────────────────────────
    Time of Silence - Time of Silence 2 Development Blog
    ────────────────────────────────────────────────────────────
  • DJ SID schrieb:

    Gibt es Basicprogramme, die es nutzen?
    Ja.

    ...

    Klassiker für eine Anwendung sind z.B. ein PEEK() unter ROM und I/O, ein mehrfach schnellerer Ersatz für die SQR()-Funktion mit dem Heron-Algorithmus oder eine Funktion um die Adresse einer BASIC-Zeile herauszufinden. Mit letzterem als Grundlage läßt sich dann recht einfach ein RESTORE <Zeilennummer> nachbilden.
  • Claus schrieb:

    Zaadii schrieb:

    Bei USR ist eben zusätzlich noch definiert wie Parameter an die Routine übergeben werde und von dort zurückgegeben werden.
    Das ist aber schon superpraktisch, oder? Sonst muss man ja die Eingabeparameter erst irgendwohin poken, was langsamer ist und den Basiccode unnötig aufbläht.
    Naja, bei SYS gibt man ja direkt die Sprungadresse an. Dafür muss man sich die Parameter poken.
    Bei USR muss man einen Parameter weniger poken. - Dafür muss man ja die Adresse mit zwei Pokes (highe and Low) vorher wegpacken.
    CIA and Co have reached the C64 - see the proof in the Hack Attack Trailer
  • Die USR-Fuktion ist das Äqivalent zu sys.
    Sys ist für Befehle, usr für Formeln, wie z.B. peek()
    Also usr ist für Berechnungen und sys für Befehle.


    Peek kann man beispielsweise komplett durch usr() ersetzen, wenn man den usr-Vektor auf peek zeigen läßt.

    Schönen Gruß.
  • Gibt es Basic-Programme, die usr nutzen?

    Dazu sag ich mal natürlich ja, aber im allgemeinen wird es natürlich selten benutzt.
    Man könnte z.B. mit usr(), sin(), cos(), tan() ,len(), abs() peek() und so ersetzen oder sonstige Rechenvorschriften in Berechnungen einbinden.
    Das wird aber im allgemeinen wohl nur von wenigen Programmierern gemacht.

    Ich habe beispielsweise usr bei der Berechnung der Zeilenadresse verwendet oder zur Umschaltung in den Befehlsmodus bei Funktionen.
    Man kann aber nicht nur Berechungen von usr ausführen lassen, sondern natürlich jedes im Speicher stehende Programm.

    z.B. wenn ich den usr-Vektor auf Bildschirm löschen verbiege,
    dann kann ich innerhalb einer Berechnung den Bildschirm löschen.

    z.B. :a=1+2+usr(0): würde dann nicht nur 1 und 2 zusammenrechnen, sondern auch den Bildschirm löschen.

    Schönen Gruß.
  • Zaadii schrieb:

    Naja, bei SYS gibt man ja direkt die Sprungadresse an. Dafür muss man sich die Parameter poken.Bei USR muss man einen Parameter weniger poken. - Dafür muss man ja die Adresse mit zwei Pokes (highe and Low) vorher wegpacken.
    Um auch an 1570 und Mike anzuschließen, so hat sich das bei mir in der Praxis so gestaltet, dass SYS üblich war (mit anschließendem Parameterparsing zusätzlicher Parameter). Auch deswegen, weil ich nicht nur eine Routine hatte, sondern eine ganze Library mit vielleicht ein Dutzend Prozeduren. Da nützt einem eine einzelne USR()-Funktion nicht wirklich.
    Da im Gegensatz zu USR() der Aufruf nicht in einem Expression-Kontext stattfindet (also als Funktion), sind Ergebnisse mit SYS nicht so einfach retournierbar (wenn man mal von Trivial-Werten, die in den Registern Platz haben und mit PEEK() ausgelesen werden können/müssen). Da muss man dann schon gezielt in Variablen, Arrays zurückgeben (mit entsprechendem Aufwand im Bemühen der dafür notwendigen Interpreterroutinen). Mit PEEK() ist es unter Umständen zu langwierig und zu langsam (z.B. wenn man mit einem retournierten Float oder einem String gleich weiterarbeiten).

    Aus Geschwindigkeitssicht kann USR() aber von Vorteil sein, da es direkt über einen Funktionstoken aufgerufen wird und SYS im Gegensatz dazu die Adresse erst parsen muss, bis es den Absprung schafft. Die Adresse als Konstante (die ja oft mind. 4-stellig sind) braucht viel Zeit um in in ein Float und dann erst in ein Integer gewandelt zu werden. Selbst die Adresse in eine Variable gepackt, leidet mitunter an der Variablensuchzeit. Wenn ein Parameter gebraucht wird (der immer automatisch beim Aufruf verarbeitet wird und der aufrufenden Routine zur Verfügung steht) und mit dem Ergebnis im Floating-Point-Akku weitergerechnet werden soll, dann drängt sich USR() ziemlich auf.

    Abgesehen davon, sieht man auch wie halbherzig USR() im CBM-BASIC verankert ist. Auf anderen Plattformen haben BASIC-Varianten mit MS-Wurzeln z.B. die Möglichkeit die USR-Funktion auch elegant ohne POKE zu definieren, etwa mit DEF USR=<Adresse>. Andere gehen noch weiter und haben mehrere USR-Funktionen, die sich USR1(), USR2(), ... nennen (z.B. Extended Color Basic bei Dragon/Tandy Color Computer).
  • JeeK schrieb:

    Da im Gegensatz zu USR() der Aufruf nicht in einem Expression-Kontext stattfindet (also als Funktion),
    Die mir verfügbare und erinnerliche Dokumentation (v.a. "Franz Wunderlich, Erfolgreicher mit CBM arbeiten, Franzis Verlag") hat m.E. etwas um den heißen Brei herumgeredet.

    Wie gibt die USR-Maschinenroutine ihren Gleitkomma-Ergebniswert (Rückgabewert; RETURN x in C) an den Formelinterpreter weiter?
    Muß sie den Wert dazu in den FAC#1 abspeichern?

    Habe nirgends, v.a. auch in keinem ROM-Listing oder auch den reichlich verfügbaren Zeropage-Tabellen jemals einen brauchbaren Hinweis gefunden.

    Edit: @JeeK: wieviel kB ROM-Speicherplatz hatte das Extended ROM Basic des Tandy (6809 Prozessor) zur Verfügung?
    Gesendet über das Bimetall Thermorelais meines Rowenta DW 4015 Dampfbügeleisens

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Stephan ()

  • Stephan schrieb:

    JeeK schrieb:

    Da im Gegensatz zu USR() der Aufruf nicht in einem Expression-Kontext stattfindet (also als Funktion),
    Die mir verfügbare und erinnerliche Dokumentation (v.a. "Franz Wunderlich, Erfolgreicher mit CBM arbeiten, Franzis Verlag") hat m.E. etwas um den heißen Brei herumgeredet.
    Wie gibt die USR-Maschinenroutine ihren Gleitkomma-Ergebniswert (Rückgabewert; RETURN x in C) an den Formelinterpreter weiter?
    Muß sie den Wert dazu in den FAC#1 abspeichern?

    Habe nirgends, v.a. auch in keinem ROM-Listing oder auch den reichlich verfügbaren Zeropage-Tabellen jemals einen brauchbaren Hinweis gefunden.
    Genau, im FAC#1. Den Parameter bekommt man auch von dort (man muss noch gegebenenfalls eine Typüberprüfung machen, könnte ja auch ein String sein).
    Wenn man mit dem Parameter "herumrechnet", dann landen ja Zwischenergebnisse ja üblicherweise schon so nebenbei im FAC#1 ...
    Wenn die Funktion auf ein RTS zeigen würde, hätte man als Retourwert den Aufrufparameter.
  • entschuldige dass ich so laienhaft nachfasse ....
    weil du sagst "Strings":
    im FAC#1 wird dann der "Pointer" zusammen mit dem Namen (2 Byte + leeres Byte) hinterlegt?
    Anders gefragt:
    Übergabe by value oder by name?
    Gesendet über das Bimetall Thermorelais meines Rowenta DW 4015 Dampfbügeleisens
  • Stephan schrieb:

    entschuldige dass ich so laienhaft nachfasse ....
    weil du sagst "Strings":
    im FAC#1 wird dann der "Pointer" zusammen mit dem Namen (2 Byte + leeres Byte) hinterlegt?
    Anders gefragt:
    Übergabe by value oder by name?
    Eigentlich wird USR lediglich als Funktion angesprungen (das war eigentlich von mir oben falsch dargestellt), die sich selbst um die Parameterauswertung kümmern muss. Das muss nicht notwendigerweise nur genau ein Parameter sein, eben das was man (mit den üblichen Interpreterunterroutinen) parsen möchte. Der Interpreter erwartet nur die schließende runde Klammer. D.h. auch, dass eine Syntax USR(X),Y,Z eher unpraktisch ist bzw. gar nicht geht (weil man dann die runde Klammer selbst parsen muss, um die anderen Parameter lesen zu können und dann aber die erwartete runde Klammer für den Funktionsaufruf des Interpreters fehlen würde ... das ist nicht so einfach unterzuschieben. Macht glaub ich auch keiner. Wenn dann würde man gleich USR(X,Y,Z) machen.
    Was auch immer man damit (wo auch immer) macht, beim Ausstieg rechnet die die USR()-Funktion aufrufende Ausdrucksauswertung mit dem Wert und Typ in FAC#1 weiter ...
    Z.B. hier kopiert USR() das Verhalten von VAL() ...

    Quellcode

    1. 10 poke785,173
    2. 11 poke786,183
    3. 20 print usr("129")+1

    Was den Retourwert angeht ist es ähnlich. Bei der Rückkehr ist in FAC#1 je nach gesetztem Typ-Flag ein numerischer Wert oder der Zeiger ($64/$65) auf einen String-Descriptor (der meistens in den String-Descriptor-Stack - für temporäre Strings - liegt) verweist. Z.B. verwendet CHR$() die Routine $B4D5/46293 Save String Descriptor (wenn man sich das im Detail ansehen möchte).
    Hier USR() auf CHR$() und wird damit zur Stringfunktion, die entgegen der üblichen Konvention damit kein "$" im Namen hat. :D

    Quellcode

    1. 10 poke785,236
    2. 11 poke786,182
    3. 20 a$=usr(49)
    4. 30 print "<"+a$+">"