Posts by Mike

    Um noch mal auf diesen Aspekt im Eingangsposting zurückzukommen:

    Dabei geht es mir eigentlich nur darum, für einen kleinen Artikel ein kleines Programm in Basic und die gleiche Ausgabe in Assembler gegenüber zu stellen.

    Ich habe sowas vor einiger Zeit, aber für den VC-20 und auf Englisch im Denial-Forum mal gemacht.


    Die Grundidee bzw. das Szenario war, daß man gerade eine Monitor-Cartridge (VICMON) zu Weihnachten geschenkt bekommen hat, und sich nun drangibt, einige BASIC-Programme aus dem Benutzer-Handbuch in Maschinensprache zu übersetzen. :D


    A sample programming session in VICMON


    Die BASIC-Originale stehen im Thread mit drin und soweit möglich/sinnvoll sind die Programme so übersetzt, daß dies Zeile für Zeile nachvollzogen werden kann. Inklusive aller Fallstricke, die sich mit einem Direkt-Assembler halt so auftun, wie etwa Vorwärtsverweise bei Sprüngen (wo man also beim Schreiben die Zieladresse noch nicht kennt bzw. nicht kennen kann).


    Das sieht nicht nur so aus, als könnte es wehtun, es tut weh. Es gibt heute komfortablere Programmierumgebungen. Auf einer Demoparty macht so was aber durchaus Laune. ;)

    Wegen CLR: what kinzi says. Durch das CLR werden ein paar weitere ZP-Adressen 'geradegerückt', die auch in den Stringheap zeigen.


    Davon ab ist "POKE55,0:POKE56,128:CLR" noch vorzuziehen: 55/56 zeigt auf das erste Byte nach dem BASIC-Speicher. Schau dir einfach den Originalwert (C64 ohne Cartridge mit ROML) an - 40960 = $A000.

    Da haben goloMAK und ich ja (zufällig) richtig getippt. ;)

    [...] merke gerade, das ich auch in Assembler super ineffektiv programmieren kann - das Assemblerprogramm ist genau so lahm wie das Basicprogramm

    Umgekehrt wird ein Schuh daraus: PRINT ist für das was er macht, eher einer der effizienteren Befehle in BASIC. Schneller jedenfalls als Einzel-POKE-s. Das liegt daran, daß der Overhead im wesentlichen schon bei CHROUT liegt und nicht beim Zeichenketten-Scanner im Interpreter.


    Wenn Du schnell sein willst, kommst Du in Maschinensprache allerdings wiederum nicht an Direktzugriffen auf den Bildschirmspeicher vorbei. LDA #$xx:STA $xxxx sind nochmal 2 bis 3 Größenordungen schneller als CHROUT. Dafür geht dir das automatische Handling der Schreibmarke, die Berücksichtigung vom Farb-RAM, das Scrolling und die Umleitbarkeit der Ausgabe auf Dateien oder Drucker verloren. CHROUT nimmst Du her, wenn es dir auf Kompaktheit, Flexibilität und OS-konforme Ausgabe ankommt - nicht wegen der Geschwindigkeit.

    ich bekomme einfach das CR, also den Zeilenumbruch nicht hin, da die Routine von chrout in Assembler mein $0D das ich übergebe nicht umsetzt.

    Dann machst Du irgendwas falsch, denn CHROUT handhabt CR (Wert: $0D) als Eingabe schon korrekt.


    Nach einer Alternative zu CHROUT brauchst Du also in diesem Zusammenhang nicht zu suchen.


    Poste mal den Quellcode. Ohne den sind alle Erklärversuche hier nur Fischen im Trüben. (<- Ein Beispiel dazu, und hier nur angebracht, weil das ein Klassiker bei Anfängern ist: Bist Du sicher, daß Du vor JSR $FFD2 den Befehl LDA #$0D geschrieben hast und nicht etwa "LDA $0D"?)

    Ich weiss halt nur nicht, ob es überhaupt eine gute Idee ist, BBG überhaupt zu fragen ... :-( Schlafende Hunde wecken und so ....

    Fragen kostet nichts. BBG sitzt in Deutschland, was die Kommunikation sicher auch einfacher macht.


    Dein BD Port kommt genau richtig zum 40. Geburtstag des VC-20! :)

    Wäre ja technisch auch möglich gewesen, dass die Module sich in den Tastatur-IRQ mit reinhängen oder an den Basic-Interpreter dran und dann über Befehl oder Tastenkombination aktiv werden, ...

    So ist das beim Super-Expander (Erweiterung des BASIC-Interpreters mit zusätzlichen (Grafik-)Befehlen) oder Programmer's Aid (u.a. Funktionserweiterung des Bildschirmeditors über CTRL-Tastaturkürzel). Monitore werden meistens mit SYS gestartet. Danach ist dann auch häufig der BRK-Vektor umgestellt, so daß man auch bei der Ausführung eines BRK-Befehls in den Monitor kommt - mit Registerdump - und auch die Möglichkeit besteht den unterbrochenen Programmablauf mit G fortzusetzen.


    Für einen Monitor empfiehlt es sich nicht unbedingt, sich auch noch in den BASIC-Interpreter "einzuhängen". Vielleicht ist es ja gerade eine BASIC-Erweiterung, die man anschauen möchte und dann kommen sich die zwei u.U. ins Gehege.

    ... direkt im Monitor landen macht natürlich nur sehr begrenzt Sinn.

    Genau das hat aber z.B. die HESMON-Cartridge gemacht. Nicht unbedingt nachahmenswert.


    Zudem hatte ich damals ja kaum finanzielle Ressourcen ...

    Hieß früher evtl. auch Taschengeld? ;)

    ... an Kopien war beim VC20 auch nicht so leicht ranzukommen (insb. ohne Floppy), als später am sehr weit verbreiteten C64... Oder ich kannte einfach nur die falschen Leute.

    Mit einem VC-20 als erstem eigenen Rechner standen die "Chancen" gut, das kaum jemand anders im Freundes- oder Bekannten- oder Familienkreis überhaupt einen Rechner hatte. Bei mir hatte in der erweiterten Familie nur ein Onkel noch eher einen privaten Rechner, einen ZX81. Das war 1982. Meinen VC-20 gab's dann zu Weihnachten 1983.


    Da war erstmal nichts mit "tauschen". Erste Quelle an Software (neben zwei Kaufkassetten mit UFO und Biorhythmus) waren Abtipplistings in diversen Computermagazinen.



    Auf jeden Fall lohnt sich der Wiedereinstieg beim VC-20 - da sind in den letzten Jahren einige neue Sachen entwickelt worden, Hardware und Software. Läuft nur leider hier im Forum64 häufig unter dem Radar. Denial ist da einfach die bessere Quelle, benötigt aber Englischkenntnisse.

    Du hast hier z.B. nicht beschrieben, was wie genau auf den Stack gepusht werden muss.

    Schau mal hier:

    [...] indem 'über' dem ursprünglichen IRQ-Stackframe (Y,X,A,P,PC) ein Dummy-Stackframe angelegt wird [...]

    in Beitrag #27. Daraus läßt sich dann schon ableiten, wie der Dummy-Stackframe auszusehen hat - im Aufbau nämlich genau wie der originale IRQ-Stackframe, nur anderer Inhalt.

    [...] Der KERNAL-IRQ macht die Tastaturabfrage und sonstiges Gedöns und hebt zum Schluß den Dummy-Stackframe ab - womit die CPU aber zunächst nicht beim Hauptprogramm weitermacht, sondern beim "Rest" des Maustreibers! [...]

    Daraus läßt sich wiederum ableiten, daß der ge-stack-te PC im Dummy-Stackframe auf den "Rest" des Maustreibers verweisen soll. Mit ein wenig knobeln kommt man dann noch darauf, daß das Statusregister mit PHP in den Dummy-Stackframe gepusht werden kann und automatisch auch das I Flag gesetzt hat. Mit noch etwas mehr knobeln kommt man dann auch noch darauf, daß es auf die Inhalte von A, X, Y nach dem Abheben des Dummy-Stackframes nicht ankommt, weil der "Rest" des Maustreibers diese nicht genau definiert braucht. Sieht man denn auch an der Implementierung von wizball6502.


    Das steht alles bereits in meinem Beitrag #27.


    MacBacon hat die andere Herangehensweise gewählt. Im anderen Thread steht einfach der Code zum Erstellen des Dummy-Stackframes auf dem Silbertablett. Wie er darauf gekommen ist, steht da nicht!


    Ansonsten aber ...

    Damit weiß ich nun natürlich, wie ich es implementiere. Am Umsetzen scheitert es sicher nicht. Es geht mir ums Verständnis. Ich weiß nun, dass $ea31 am Ende drei Bytes vom Stack zieht und die in Y,X und A schreibt und dann per RTI zur Adresse springt, die danach im Stack steht. Das ist dann der zweite Teil unseres Maustreibers. Ziemlich clever! Am Ende des Treibers wird dann $ea81 aufgerufen, was den ursprünglichen Stack-Frame zieht, und alles läuft normal weiter. So richtig?

    ... so richtig. :)

    Jo, sicher.

    Ganz genau!

    Schon der erste jsr in $ea31 verweist auf vier weitere jsr's im Kernal. In der ganzen Routine ist natürlich auch viel dabei, was ich gar nicht wissen will. Mich interessiert z.B. nicht, wie die Tastaturabfrage funktioniert, nur wo sie passiert.

    Bei einem gut kommentierten ROM-Listing ist das auch ganz klar ersichtlich und Du kannst die Sachen überspringen, die für die Anwendung erstmal nicht relevant sind.

    Was ich gerne hätte wäre ein grober Ablauf, wo was passiert.

    Ganz knallhart gesagt, da mußt Du dich jetzt selbst durchbeißen! Ich habe deine Frage wie man den Dummy-IRQ-Stackframe aufbaut im anderen Thread nochmal ausführlich beantwortet. Wie weit aufdröseln soll ich das denn jetzt noch? Du siehst doch, daß wizball6502 aufgrund der Blaupause von MacBacon ohne weiteres in der Lage war, den Maustreiber umzubauen und das, obwohl die Blaupause wesentlich knapper beschrieben war als hier in dem Thread. Also kriegen andere sowas hin. Es gibt also noch was für dich zu studieren und das nimmt dir niemand ab.

    Wozu dient das?

    Damit RTI beim ersten Aufruf von $EA81 richtig an das Statusregister und PC des Dummy-IRQ-Stackframes "herankommt".

    Ist es egal, was ich da in den Stack pushe?

    In diesem Fall, ja.

    Könnte ich z.B. auch dreimal php machen?

    Könntest Du, aber es werden nominell Registerinhalte gepusht, nicht der Status.


    Ansonsten gilt das, was ich bereits im anderen Thread geschrieben hab: schau dir das Verhalten der CPU, die Stackbefehle, den Interrupthandler im KERNAL bitte mal genauer an. Für wizball6502 war es kein größerer Akt, die Sache zu implementieren nachdem die Grundidee klar war.

    Na ja, der Variablenname "P" der FOR-Schleife ist schon mit Bedacht gewählt: logisches Prädikat.


    Damit "liest" sich der FOR-Befehl in meiner Fassung wie folgt: "Schleife mit wahrem Prädikat P, solange bis P falsch ist".


    Vor NEXT setzt P=ST=0 das Prädikat entsprechend der genannten Bedingung auf TRUE (=-1) oder FALSE (=0), so daß das Ganze stimmig ist.

    Ist soweit ich gemessen habe ist das schneller.

    Die Anwendung ist I/O-bound, die paar Millisekunden können's jetzt auch nicht reißen. ;)

    Was trage ich denn in den Dummy-Stackframe ein, damit mich $EA81 zum Rest des Maustreibers schickt, wo der Multiplexer eingestellt wird?

    Das, was MacBacon im Solitaire-Thread geschrieben hat. Er hatte die gleiche Idee in dem anderen Thread und war auch schneller. Dafür steht hier meine etwas ausführliche Erklärung, wie es funktioniert.


    WebFritzi, ich würde dir schon anraten, dir mal genau anzuschauen was die CPU und der KERNAL beim Interrupt machen und wie die Stackbefehle und RTI arbeiten. Dann ergibt sich die Erklärung, ausgehend bereits von meinem Beitrag #27, schon von selbst. Mit meinen Beiträgen #29 und #32 kam nichts substantiell neues dazu - die Blaupause in Beitrag #27 reicht völlig aus, wenn man sich mit der Sache auskennt und nur noch nicht auf diese Idee gekommen war.


    wizball6502, saubere Umsetzung! 8o

    oder nicht?

    Oder nicht.


    Häng' meine oben beschriebene Abfolge in Gedanken mal zweimal untereinander. Wer (ver)stellt den Multiplexer auf Port 1? Die Tastaturabfrage in Punkt 6. Wer stellt den Multiplexer (wieder) auf Port 2? Der "Rest" des Maustreibers in Punkt 8. Wo werden die POT-Register gelesen? In Punkt 3, beim nächsten Interrupt. tWieviel Zeit vergeht zwischen Punkt 8 und Punkt 3? Circa 16 Millisekunden, die Zeit zwischen zwei Tastatur-IRQs. Ausreichend Zeit in denen sich die Combo Multiplexer-A/D-Wandler "einpendeln" kann und das Hauptprogramm ohne 4-ms-Busy-Wait unbehindert läuft.


    Beanworte dir die Frage jetzt selbst, welche Instanz als letztes vor der POT-Abfrage den Multiplexer eingestellt hat.


    Ganz am Anfang, die allererste Abfrage liest bei dieser Darstellung noch von Port 1. Nur die. Macht das was? Wenn dir das nicht gefällt, stell' den Multiplexer in der Init-Routine auf Port 2, warte hier eiin einziges mal 4 Millisekunden und gebe dann den IRQ frei. Brauchen tut's das aber nicht.


    Wenn dir das zur Erklärung nicht ausreicht, kann ich dir leider nicht weiterhelfen.

    Das mit A000 Modulen ist mir klar, allerdings starten die ja üblicherweise auch automatisch, meine Frage war gewesen, ob die Module, die -wie eben das Maschinensprachemodul weiter unten im Speicher liegen, auch eine solche Kennung haben und möglicherweise sogar vom System automatisch dann vom Basic-Bereich ausgenommen werden. Scheint nicht so zu sein, man ist halt verwöhnt heutzutage...

    Der KERNAL fragt nur eine Modulkennung ab $A000 ab.


    Es gibt auch Module im "unteren" Bereich mit einer Modulkennung, es gibt aber nur eine mir bekannte Kombination, die das ausnutzt: "Japanischer" Super-Expander + (evtl.) Programmer's Aid + (evtl.) IEEE-488. Die letzteren beiden werden vom japanischen Super-Expander sozusagen "mitgestartet". Details findest Du im Denial-Forum:


    VIC-1211A and VIC-1211M - What's the difference?


    Allerdings funktionert diese Zusammenarbeit im Falle der Original-Module nicht 100%ig und wurde darum vermutlich von CBM beim "USA/Europa"-Super-Expander rausgepatcht. Im o.g. Thread habe ich das Problem gelöst und diese Sache läuft.


    ...


    Der Speicherschutz hat sonst nichts mit der Modulkennung zu tun. Der Speichertest findet entweder RAM (bis $7FFF) oder läuft auf ROM oder eben einen "leeren" Speicherbereich auf. Beide letztere Fälle verhalten sich nicht wie RAM und dann endet der BASIC-Speicher eben da.


    Bei Utility-Modulen wie Programmer's Aid oder IEEE-488 könnte man sich noch darüber ärgern, daß die normalerweise nicht automatisch gestartet werden (Ausnahme, s.o.), aber so ein Tool wie VICMON wird man wohl eher erst aufrufen wenn man es braucht - aber dann ist es besser da, wenn man es braucht ohne es vorher laden zu müssen ... und das ist nur einer der Gründe, die mich zur Entwicklung der MINIMON-Cartridge gebracht haben.

    Und wie "legt man" einen Stackframe "an"?

    Von Hand, mit PHA und PHP.


    Der Stackframe muß dann genau den gleichen Aufbau haben wie sonst auch beim Eintritt in die IRQ-Routine ab ($0314): die CPU hat bereits den PC und das Statusregister abgelegt, der KERNAL-IRQ-Handler legt noch A, X und Y dazu.


    Bei der Rückkehr aus dem Interrupt holt die Routine ab $EA81 das A, X und Y-Register wieder ab, RTI dann das Status-Register und den PC.


    Den Dummy-Stackframe "fabriziert" man jetzt so, daß der im Zwischengang aufgerufene Tastaturinterrupt bei dessen "Verwendung" in $EA81 nicht direkt zum Hauptprogramm zurückkehrt, sondern zu einer anderen vorgegebenen Stelle - eben dem "Rest" des Maustreibers! Wenn der dann fertig ist und seinerseits $EA81 aufruft, wird der ursprüngliche Stackframe abgehoben und dann erst geht's im Hauptprogramm weiter.


    Der Ablauf ist also jetzt so:

    1. Hauptprogramm läuft,
    2. Interrupt passiert,
    3. Maustreiber liest POT-Register und aktualisiert Mauszeiger,
    4. Maustreiber legt Dummy-IRQ-Stackframe an,
    5. Maustreiber springt zu $EA31 (bzw. "zu vorherigem Besitzer des IRQ-Vektors"),
    6. $EA31 ff. macht sein Interrrupt-Processing (Tastatur, Uhr, ...),
    7. Ende der IRQ-Routine bei $EA81 hebt Dummy-IRQ-Stackframe ab (!!!),
    8. "Rest" des Maus-Treibers wird ausgeführt und stellt Portregister für Multiplexer richtig ein,
    9. Maustreiber springt $EA81 an,
    10. $EA81 hebt jetzt originalen IRQ-Stackframe ab,
    11. Hauptprogramm läuft weiter.


    wizball6502 hat in einem anderen Thread übrigens eine Lösung per Raster IRQ gefunden.

    Ich denke nicht, daß die Verwendung eines Raster-IRQs an dieser Stelle für die Lösung des Portproblems relevant ist. Man wird ohnehin sinnvollerweise den Raster-IRQ anstelle des Tastatur-(CIA)-Interrupts verwenden, damit die Sprite-Register des Mauszeigers sauber "im Frame" geändert werden.