Hallo Besucher, der Thread wurde 44k mal aufgerufen und enthält 337 Antworten

letzter Beitrag von Boulderdash64 am

Optimierungsgrad KERNAL /BASIC - fiktiver Talk

  • Hallo,
    eine Frage rein akademischer Natur an die Assemblerhelden hier: Wie schätzt ihr den Optimierungsgrad der Kernal- und Basicroutinen des C64 ein?
    Mir geht es nicht um den Funktionsumfang, sondern mehr um ein: Sind die bestehenden Routinen optimal umgesetzt oder könnte man
    a) die Routinen bei maximal gleichem Platzverbrauch schneller machen
    b) bei mindestens gleicher Geschwindigkeit die Routinen einkürzen ?


    Anders gefragt: Haben Commodore und MS einen guten Job getan als sie das gezimmert haben oder glaubt ihr da wäre noch mehr gegangen in der Kiste?


    Wie gesagt geht es mir weniger um den Funktionsumfang als solches (wobei natürlich b) in obiger Frage Platz für die Frage liesse: Was hätte dann noch reingepasst?) als mehr um die Effizienz des vorhandenen.


    Ich bin gespannt auf eure Meinungen :)

  • Kürzen geht immer, ich bin neulich über so ne Stelle gestolpert:


    Code
    1. bcs irgendwo
    2. clc
    3. ...
    4. irgendwo

    Geschwindigkeit wär aber mal interessant, insbesondere in Bezug auf die Floppy. Da reichts ja i.d.R schon die Bittransferroutinen zu beschleunigen, um ca. 5x schneller zu sein.


    Ob das dann aber auch ohne andere Routinen rauszukegeln einbaubar ist weiss ich nicht, ich hab jetzt auch den Funktionsumfang diverser 3rd Party Kernals nicht im Kopf und man müsste ja dann auch davon ausgehen, dass das Werksdos in der 1541 den passenden Gegencode schon enthielte und nicht erst vom C64 empfangen müsste...

  • Lassen wir mal den Speeder aussen vor- auch wenn es sicher interessant wäre ien Kernal zu zimmern was alle Funktionen enthält die das Original hat ohne was rauszuschmeissen und dennoch flotter läuft.
    Aber nehmen wir zB die Routinen zum Verschieben von Speicherbereichen, die Argumentübergabe, Rechnen mit Floats etc. - Dinge die in der Kiste echt oft laufen, da wäre in der Summe dann sicher viel Performance rauszuholen, wenn man sie innerhalb des bestehenden Plates deutlich beschleunigen könnte.

  • Oh, ich möchte das auch nicht am "Hersteller" messen, also mir ist es recht wurscht ob die Optimierungsmöglichkeiten MS- oder C-Code betreffen. Ich selbst bin kein Crack was ASM angeht und sehe sicher vieles nicht was man besser gestalten könnte, daher meine neugierige Frage.

  • Interessante Frage 8o .


    Am krassesten finde ich immer noch den Code für die Ein-/Ausgabe im Screen. Was da alles drangehängt, mag man erst gar nicht glauben (allein ja fast 50 Zeropage-Adressen). Das Problem ist: Die sind wohl wirklich alle nötig. Über die Hälfte davon (und wohl auch locker soviel vom Code) nur wegen der 80-Zeichen-Zeilen. Ich hatte mir das vor einiger Zeit mal ausgiebig zur Gemüte geführt. Der Code ist auch wirklich schwer durchschaubar. Da haben die ganz schön schon was gezaubert.


    Wenn man auf den 80-Zeichen-Kram verzichten könnte, könnte man da schon einigen Platz schaffen. Die meisten Sachen dürften wohl trotzdem noch laufen (solang nicht z. B. mit INST/DEL über die Steuerzeichen was gemacht wird). Ein BASIC-Listing anzeigen würde nicht mehr ganz sauber funktionieren, da er dann nicht immer grundsätzlich oben eine ganze BASIC-Zeile mit Zeilen-Nummer anzeigen würde, sondern ggf. nur die zweite Hälfte (was aber auch nicht tragisch wäre). Vielleicht könnte man das vereinfachen, indem nur ganz oben oder ganz unten vom Screen Doppel-Zeilen erlaubt wären.


    Was auch ziemlich 'wild' ist, ist die Umwandlung Tastatureingabe nach Bildschirm-Codes. Auch Wahnsinn, was da alles beachtet/abgefragt/umgewandelt werden muss. Ginge (bei mehr Platz) sicher einfacher (Tabellen).


    Aber zu a) & b) zurück: Ich denke kaum.


    Ansonsten: Tape-Routinen raus -> MC-Monitor rein.

  • 1. Die Routinen zum Verschieben von Speicherbereichen. Hier gibt es zwei kritische Situationen: Einfügen einer Zeile am Anfang eines größeren Programm und Anlegen einer einfachen Variable, wenn bereits große Arrays definiert worden waren. Im ersteren Fall muß der Interpreter aber zusätzlich noch rödeln um die Link-Pointer zu korrigieren. Gelegenlich also etwas zäh, aber m.M.n. unproblematisch.


    2. "Argumentübergabe" also Ausdrucksauswertung: Das dürfte wohl der Schwachpunkt eines jeden Interpreters sein, da alle Operanden immer ausgewertet, der Ausdrucksbaum "erstellt" werden muß (entweder explizit, oder implizit über die Operatorprioritäten auf dem Stack), die Variablen gesucht werden müssen, etc. Kann man, meines Erachtens, nicht viel dran optimieren.


    3. Rechnen mit Floats. Hier lehne ich mich mal recht weit aus dem Fenster: ich denke nicht, daß man hier bei gleicher Codegröße wesentlich schneller werden kann, ohne Genauigkeit und Zuverlässigkeit aus dem Fenster zu werfen (abgesehen von dem gesch******n Multiplikations-Bug).


    Ärgerlich sind ein paar weitere Fehler aufgrund fehlender Typ-Überprüfungen, wodurch der Interpreter in einen undefinierten Zustand gebracht werden kann. Und die langsame GC hätte echt nicht sein müssen. Ansonsten ein gutes Ergebnis für 9K verfügbaren Platz.

  • Kürzen geht immer, ich bin neulich über so ne Stelle gestolpert:


    bcs irgendwo
    clc
    ...
    irgendwo


    (Wie geht das jetzt bloß mit den Zitieren :thumbdown: ?)


    Von irgendwo hab ich sowas auch in Erinnerung. Hatte dann aber doch irgendwie Sinn gemacht, weil es auch einen Branch direkt zum CLC gab. Wo auch immer ich das gesehen habe...



    Nochwas:


    Fehlermeldungs-Texte einkürzen (fast 400 Byte)!


    "DEVICE NOT PRESENT" -> "DNP"
    "OUT OF MEMORY" -> "OOM"
    etc..


    Oder sogar nur eine Nummer. Die (oder die Abkürzungen) hätte man nach kurzer Zeit auch intus (oder schlägt halt nach).


    BASIC-Befehle können zum Teil auch kürzer sein ("RESTORE"); "PRINT" könnte doch einfach "?" bleiben. Spart jetzt aber kaum was bei den wenigen Befehlen. Den "RESTORE" hätte man sich in der Form eigentlich ja eh sparen können.



    Was aber auf jeden Fall irgendwie im BASIC platzmässig hätte drin sein müssen: CLS und INK (oder COL oder so). Die paar Bytes hätte man auch an der Einschaltmeldung einsparen können.

  • (Wie geht das jetzt bloß mit den Zitieren :thumbdown: ?)

    Anführungszeichen-Icon im Posting (meist rechts unten) anklicken, runterscrollen, Antworten-Knopf klicken


    Du hasst BASIC-Programmierer, oder?

  • (..)
    oder könnte man
    a) die Routinen bei maximal gleichem Platzverbrauch schneller machen
    b) bei mindestens gleicher Geschwindigkeit die Routinen einkürzen ?


    Anders gefragt: Haben Commodore und MS einen guten Job getan als sie das gezimmert haben oder glaubt ihr da wäre noch mehr gegangen in der Kiste?

    Ich glaube dass die Fragestellung ein "Nein" impliziert, aber eine schon geringfügige Änderung der Fragestellung durchaus ein Ja auf beide Fragen ermöglichen würde.


    Wenn du "die Routinen" also die Gesamtheit der Unterprogramme von Kernel und Basic meinst, und damit einen feststehenden Kanon, dann ist die Frage wohl mit "nein" zu beantworten, weil da ein sehr enges Korsett feststeht.


    Aber sobald man das Konzept neu aufzieht und z.b. die Mehrfachnutzung von Code (= Routinen möglichst vielseitig und möglichst oft von verschiedenen Stellen aus aufrufen und ausnutzen) verbessert, dann ergibt sich ein völlig anderer "Zuschnitt" und die Code-Effizienz ließe sich steigern. (Natürlich mit allen unerwünschten Nebenwirkungen, wie Maschinencode-Aufrufe aus Basic-Programm mit SYS, die nicht mehr funktionieren oder anders konzipiert werden müssen).


    Einen Ansatzpunkt sehe ich in dem Umstand, dass BASIC von Bill Gates, Monte Davidoff und einer weiteren person geschaffen wurde; diese haben sich die Arbeit aufgeteilt und recht unabhängig voneinander codiert. Und später der Kernel als 4. Programmkomplex.


    Interessant fände ich, hier in den 4 "Programmteilen" die aufgrund ihrer Entstehungsgeschichte vielleicht recht wenig verflochten sind, nach gemeinsamer "Funktionalität" Ausschau zu halten und soviel wie möglich an "generischen" Codeteilen in einen gemeinsamen Code-Pool-Unterbau /Unterroutinensammlung auszulagern.


    Insgesamt würde diese Optimierung zu einer leicht erhöhten "maximalen Verschachtelungstiefe" des Basic/Kernel-Komplexes führen und die Gesamtcodegröße senken.


    Ich habe damals als Schüler ein spezielles Disassembler-Programm geschrieben (in basic) das die Firmware statistisch ausgewertet hat (Opcodes) und primitive Vergleiche ermöglicht hat.


    Das Ergebnis war in etwa, dass der C116 /plus4 einen wesentlich, fast möchte ich sagen, drastisch höheren Anteil des Opcodes $20 (= JSR) in seiner Firmware hatte; ebenso der C128, im Vergleich zum C64. dabei wurde nicht stupide das Byte $20 gesucht, sondern der gesamte Text disassembliert und nur wenn $20 am Anfang eines Maschinenbefehls stand, mitgezählt.


    Dass in der Firmware einiges an Entropie steckt, oder vielleicht besser Redundanz, entnehme ich auch dem Umstand, dass die BASIC und KERNEL dateien in Vice sich mit PKZIP noch ganz ordentlich komprimieren lassen.(überschlägig 50%). Wäre dasselbe unter Steve Wozniaks "Sweet 16-Interpreter" realisiert, wäre wohl kaum noch Redundanz drin.


    Sicher würde man andere Wege gehen müssen die Redundanz durch einen speziellen Programmierstil rauszuwerfen, als es der symbolische Weg über PKZIP ermöglichen würde.


    zu der "anderen" Frage: Ja, ich glaube dass insbesondere Bill Gates bei dem was er selber schrieb, (sehr) gut gearbeitet hat. Soweit ich das beurteilen kann.


    Hexworx: Michael Steil schrieb auf Pagetable einen interessanten Vergleich der Interpreterversionen von MS-Basic (auf PET usw.). Da gab es auch eine Version mit verkürzten Fehlermeldungen (Nummern?) und eine Version die OK statt READY. meldete. Ich wundere mich über diesen deinen Vorschlag "so" auf Nebenschauplätzen Bytes zu sparen - Optimieren ist doch viel mehr als nur "Sparen"!

  • Gemischt. Teilweise war das schon ganz schnieke, wie da Bytes gespart wurden, teilweise wurde aber auch übler Spaghetti-Code draus. Manche Sachen hab ich auch gar nicht verstanden, z.B. wie der Eingabepuffer gefüllt wird, wenn man mit dem Cursor die Bildschirmzeile wechselt.


    An anderen Stellen wurden dann aber im Großen komische Konzepte verfolgt.
    Peiselulli hat ja schon die Datasette genannt, die seriellen Routinen sind mir auch noch in schlechter Erinnerung geblieben.
    Und letztens hatten wir ja eine Diskussion über GOTO und überhaupt das Format der Basic-Zeilen im Speicher. Da kann man sich einiges denken, was einen Basic-Interpreter schneller und Programme kürzer machen kann - fragt sich nur, wie sich das dann auf den ROM-Verbraucht auswirkt.


    Wäre bestimmt eine lustige Sache, Bildschirmausgabe und Screen-Editor mit mindestens gleichen Fähigkeiten nachzubauen oder sich ein alternatives Basic zu überlegen und dann zu vergleichen.

  • Anführungszeichen-Icon im Posting (meist rechts unten) anklicken, runterscrollen, Antworten-Knopf klicken


    Markieren - "Zitat einfügen" -> so läuft's scheinbar wie vorher.



    Du hasst BASIC-Programmierer, oder?


    Nö, bin nur tipp- und lese-faul. Es reichen so kurze knackige Meldungen/Befehle, bevor man Romane liest/schreibt. Bei vielen anderen Befehlen, die derbe eingekürzt sind, geht's ja auch (CONT, SYS, USR, FRE, CLR, DIM, CMD, DEF und die ganzen numerischen und die meisten String-Befehle).

  • Der neue Zitier-Kram ist doch die hinterletzte Grütze. Ich platzte gleich... :encolere20: . Das macht doch, was es will :gahh: .




    ...Jetz' geht's 8\| !!!!1!?!111?

    Hexworx: Michael Steil schrieb auf Pagetable einen interessanten Vergleich der Interpreterversionen von MS-Basic (auf PET usw.). Da gab es auch eine Version mit verkürzten Fehlermeldungen (Nummern?) und eine Version die OK statt READY. meldete. Ich wundere mich über diesen deinen Vorschlag "so" auf Nebenschauplätzen Bytes zu sparen - Optimieren ist doch viel mehr als nur "Sparen"!

    READY/OK: Da hätte ich ja auch mal drauf kommen können :facepalm:8\| . Drei/vier Bytes ham oder nich ham :rolleyes: .


    Steil/Pagetable muss ich mal nach suchen. Danke. Klingt interessant.


    Wäre bestimmt eine lustige Sache, Bildschirmausgabe und Screen-Editor mit mindestens gleichen Fähigkeiten nachzubauen oder sich ein alternatives Basic zu überlegen und dann zu vergleichen.

    Das denke ich auch. Das ist schon verdammt ausgefeilt, so wie es ist.

  • Spilling over 8 KB anyway, they decided to also offer an improved version with extra features in a little under 9 KB: This version had a 40 bit floating point library (“9 digits”) instead of the 32 bit one (“6 digits”), and the two-character error codes were replaced with actual error messages:

    6 digit BASIC 9 digit BASIC
    ?NF ERROR
    OK
    ?NEXT WITHOUT FOR ERROR
    OK
    Zitat von Steil

    Commodore added the “OPEN”, “CLOSE”, “PRINT#”, “INPUT#” and “CMD” statements for file I/O and added VERIFY to compare a program in memory to a file on a storage device. They also added “SYS” to call into assembly code – Microsoft’s code had only provided the “USR” function with a similar purpose. It seems Commodore didn’t like the “OK” prompt, so they renamed it to “READY.”.

    War doch schon mal irgendwann über die Seite gestolpert. Sehr schön. Werde mir das die Tage aber nochmal reinziehen :ilikeit: .

  • (Klicken um eine Quelle anzugeben)

  • Sehr schöne Zitate! die Hervorhebung "well-written" scheint von dir zu stammen? vielleicht habe ich diese Einschätzung von dort übernommen (mich angeschlossen).


    ich trau mir wetten, dass der BIT und BPL-, Clear by LSR -Trick von den MOS-Leuten durchaus beim Design des 6502 vorhergesehen wurde und Commodore-Leute Zugang zu diesem Wissen hatten.


    Zu den Datasetten-Routinen fällt mir noch ein, dass ich mich damals (tm) ja sehr dafür interessiert habe und die Routinen auch analysiert habe. Ein wahrhaftes Gestrüpp.... es gab eine Hauptschleife, in der das Ende von Load-Save abgeprüft wurde und regelmässig die Uhr aktualisiert und die Stop-taste abgefragt wurde. Ich glaube, dazu war die Spaltenleitung der Keyboard-Matrix dauerhaft auf denselben Wert festgelegt, damit die STOP-Taste immer "Im Blickfeld" des Lese-Ports war und nicht (zeitraubend) umgeschaltet werden musste. Die normale Tastaturabfrage wurde nicht verwendet, weil sie zuviel Rechenzeit beansprucht hätte. Für TI/TI$ gab es gleichwohl eine getreue Aktualisierung. Beides lief im "Hauptprogramm" also nicht im Interrupt. Ich weiß nicht was beim C64 und später beim C16 alles im Detail vereinfacht wurde (C64 glaube ich flankengetriggerter Eingang aber evtl. kein Interrupt mehr; C16 kein Flankengetriggerter CA1-/CB1 Eingang eines CIA (VIA) sondern nur noch plumper Pegel der durch primitives Polling abgefragt wird. Großer Rückschritt...). PPPP = plumpes Polling eines primitiven Pegels ...


    Die eigentliche Arbeit z.B. des Lesens von Tape geschah beim PET in insgesamt 4 Interrupt-Routinen zwischen denen umgeschaltet wurde (oder es waren insgesamt 4 Interrupt-Einsprungpunkte einschließlich des "normalen" Tastatur/Uhr-Interrupts). Dabei wurde einerseits die Zeit zwischen Flanken gemessen, andererseits - damit überlappend bzw. "gleichzeitig" - auch ein Time-out gesetzt bzw. Zeitüberschreitung überwacht. Es wurde auch sehr reger Gebrauch von den Timern (oder wars nur einer?) im VIA gemacht ; im C16 fand ich stattdessen im ROM-Listing Zeitschleifen zumindest für SAVE nach dem Muster "LDX#00 DEX BNE *-3".


    Es handelte sich um ein regelrechtes Multithreading! und daher sehr schwer zu verstehen. Bevor ich eine vollkommene Aufklärung erreichte, ließ leider meine Motivation und Konzentration nach. Das ganze war entschieden zu wirr und zu intransparent aufgesetzt. Die TurboTape-ähnlichen Alternativen waren deutlich leichter zu verstehen. Vermutlich hat da jemand bei MOS eine Routine von einem anderen Rechnersystem die mit der Messung von Frequenzen zu tun hatte, eingekapselt und adaptiert, daher vielleicht die Umständlichkeit. Bei KIM1 wurde die Frequenzmessung bzw. -Demodulation von TAPE noch in Hardware gemacht (ähnlich MODEM-Chipsatz), vielleicht hat man versucht das in Software eins-zu-eins nachzubauen?


    MIr fällt auch auf, dass manche gleichartige Aufgaben die mit dem langwierigen Hinausrotieren von bits zu tun haben, mehrfach vorkommen oder vorkommen könnten, da könnte vielleicht zusammengelegt werden. Langwierig deshalb, weil ja das Byte geholt werden muss, ein Zähler gesetzt, heruntergezählt und am Schleifenende überwacht, evtl. noch ein zweites Byte oder ein 9. Bit (Parity bei Tape, Schlußquittierung bei IEC (EOI), Stopbit bei RS232, Überlauf bei Fließkommaarithmetik und RND-Schieberegister...) behandelt werden muss.Vielleicht kann man da eine "generische" universelle Bit-Rotier-und Zähl-Überwach-Routine draus machen.Und ähnliche Zusammenlegungen ...


    Rotieren beim Multiplizieren, beim seriellen Bus, bei der rs232 schnittstelle, bei der Datasette, bei der Generierung von RND Zufallszahlen

  • C64 glaube ich flankengetriggerter Eingang aber evtl. kein Interrupt mehr; C16 kein Flankengetriggerter CA1-/CB1 Eingang eines CIA (VIA) sondern nur noch plumper Pegel der durch primitives Polling abgefragt wird. Großer Rückschritt...). PPPP = plumpes Polling eines primitiven Pegels ...

    Was wäre das schön, wenn man den Pegel wüsste... Um den Flankentrigger auszulösen muss man immer ein 0- und ein 1-Bit schreiben, mit dem Pegel käme man mit einem aus.

  • Ähhh .... mißverstehst du mich vielleicht?


    Der Flankengetriggerte Pin ist ein EINGANG. Kein Ausgang.
    Da kann und muss man nix "schreiben" um etwas auszulösen,
    da muss man nur sich zurücklehnen, auf-sich-zukommen-lassen-was-da-kommen-mag, warten und ggf. einen Interrupt bearbeiten.


    Das ist vielmehr Aufgabe der externen Hardware (z.b. Datasette-Leseverstärkerausgang) für den Pegelwechsel zu sorgen.


    Wenn du für selbstgebastelte Labor-Hardware jederzeit einen *Pegel* wissen willst, ist natürlich ein gewöhnlicher Portpin (PA0-PA7 oder PB0-PB7) der richtigere, statt des Flankengetriggerten EINGANGs.


    Nachtrag: bei CA2 / CB2 des VIA könnte man sogar versuchen, die Polarität des Flankensensors bei jeder Flanke umzuschalten (togglen), vom abfragenden Interruptprogramm aus, dann könnte man im RAM-Speicher ein Flag mitführen, das per EOR#$01 o.ä. jederzeit dem Pegel des Portpins folgt! Das würde genau deinem Ziel entsprechen, jederzeit über den Pegel des Portpins bescheid zu wissen!


    (Sorry für offtopic)

  • die Hervorhebung "well-written" scheint von dir zu stammen?

    JA.


    Nochwas gefunden:


    Hier werden ja sämtliche Adressen von $D000 bis $D02E aus einer Tabelle mit Werten vorbelegt (46 Bytes). Die X/Y-Sprite-Positionen, die Farben $D022-D02E und einige Adressen mehr sind da doch recht überflüssig, da sie


    a) entweder gar nicht gesetzt werden müssen oder können oder


    b) eh kein Mensch von den vorgegebenen Werten ausgehen würde.



    Den "jsr $ecb8" bei $E5A8 hab' ich an der Stelle gemacht, falls jemand meinte, da reinspringen zu müssen. Sonst hätte man es vielleicht noch anders aufbauen können (und evtl. noch 3 Byte sparen können).


    Ich hab's jetzt nicht wirklich getestet - sollte aber wohl laufen.


    Oder sieht jemand noch einen Fehler?

  • Wäre bestimmt eine lustige Sache, Bildschirmausgabe und Screen-Editor mit mindestens gleichen Fähigkeiten nachzubauen oder sich ein alternatives Basic zu überlegen und dann zu vergleichen.

    Welche Adressen gäbe es denn überhaupt im BASIC bei $A000-$BFFF, wo jemand evtl. mal direkt reingesprungen sein könnte? Mir fällt da höchstens $BDCD ein (wird sonst wohl nur von der Einschaltmeldung [KERNAL $E43A] und bei LIST [BASIC $A6EA] genutzt). Aber sonst? Worauf müsste man denn sonst überhaupt Rücksicht nehmen (Einsprungadressen)? :nixwiss: