Viele Fragen (u.a. Timing, Badline, Double IRQ)

Es gibt 10 Antworten in diesem Thema, welches 1.536 mal aufgerufen wurde. Der letzte Beitrag (13. August 2008 um 13:30) ist von kHaos-Prinz.

  • Wer schwache Nerven hat, der sollte jetzt dann besser nicht weiterlesen, denn ich habe zig Fragen...

    Wo fang ich am besten an?

    Also: Könntet ihr mir bitte erklären bzw. ne Möglichkeit geben wo ich des am besten nachlesen kann, um was es sich bei Double IRQs genau handelt?
    Des Weiteren, wüsste ich gerne was es in Sachen Timing alles zu beachten gibt, wann das eintritt und was man machen kann (vorallem wie)... Soweit ich das jetzt Begriffen hab treten da vorallem Probleme auf, wenn mit einem Interrupt zuviel Code mitgeschleift wird, oder, bzw. zuviel Cycles verschmissen werden..
    Und was für Probleme gibts eig. bei den sog. Badlines? Also, ich hab recherchiert und herausgefunden, dass bei den Badlines (alle 8 Rasterlines oder?) die Farbinformationen neu geholt werden und somit 40 Cycles "vergeudet" werden. Was kann ich "dagegen machen" bzw. dem begegnen bzw. wo bekomm ich damit überhaupt Probleme?

    Abschließend hab ich noch 2 kleine Fragen: a) Dieser Code sollte eig. den Bildschirm in 2 Hälften teilen, das tut er auch, aber leider gibt es am Rand mal wieder kleine Verwerfungen... sind das Timing Probleme oder was ist da los.
    Und 2. Als ich den Code geschrieben hab und ausprobiert hab hab ich erst mal nicht schlecht gestaunt, über den Bildschirm wanderten lauter kleine Lininen in den Farben, die ich für die Obere und die Untere Hälfte definiert hatte. Dann hab ich noch etwas rumgeschaut und ne Seite gefunden, wo zusätztlich ein lda $d019 sta $d019 mit drin war... ich übernahm es (obwohl ich kaum Hoffnung hatte) und Tatsache es hat auf einmal funktioniert, doch über die Funktion bin ich mir bis heute nicht im klaren.

  • Zu deiner letzten Frage: In d019 werden die in d01a spezifizierten Interrupts gespeichert, wenn sie tatsächlich auftreten. Falls du also z.B. mit

    lda #$1b
    sta $d011
    lda Bitte melde dich an, um diesen Link zu sehen.
    sta $d012
    lda #%00000001
    sta $d01a

    n Rasterinterrupt aktivierst, wird in d019 bit 1 gesetzt, wenn die Rasterzeile 123 erreicht wird. Zusätzlich geht auch noch Bit 7 an, also d019 ist dann = %10000001.

    Solange der Rasterinterrupt über d01a aktiviert und die Bits in d019 an sind springt der Prozessor in den Interrupt-code, daher muss man die löschen, bevor der Interrupt abgearbeitet ist, sonst gehts sofort wieder zurück in die Interruptroutine.

    Erstaunlicherweise muss man zum Löschen die Bits nach $d019 zurückschreiben, ist halt einfach eine von zahlreichen Hardwaremerkwürdigkeiten am Cevi.

    Deswegen lda d019 / sta d019.

    Zu den anderen Fragen könnte man wahrscheinlich n ganzes Buch schreiben. Es gab aber auch hier schon den ein oder anderen Timingthread, der deine Fragen zum Teil beantworten dürfte.

    Ansonsten würd ich mal in der Codebase64 reinschauen ob da was brauchbares bei ist (in den dort angelinkten C=Hackings ist auch das ein oder andere gut erklärt) und den VIC Artikel von Christian Bauer besorgen (z.B. bei Bitte melde dich an, um diesen Link zu sehen.).
    Gibt aber bestimmt noch haufenweise andere Quellen...


  • Also: Könntet ihr mir bitte erklären bzw. ne Möglichkeit geben wo ich des am besten nachlesen kann, um was es sich bei Double IRQs genau handelt?

    Double IRQs sind ein bewährtes Mittel um Prozessor und Rasterstrahl exakt zu synchronisieren. Benötigt wird das für viele Demoeffekte, etwa um den Sideborder zu öffnen. Da musst du $d016 exakt zu dem Zeitpunkt beschreiben wenn der Rasterstrahl gerade das Zeichen unmittelbar vor dem rechten Border darstellt. Wenn dein Schreibzugriff auf $d016 auch nur einen Taktzyklus früher oder später stattfindet klappt's nicht.

    Das ganze funktioniert so:
    Wenn ein Interrupt auftritt dann arbeitet der Prozessor den aktuellen Befehl des Hauptprogramms noch zu Ende ab und springt dann erst zur Interruptroutine. Dadurch wird der IRQ halt mit einigen wenigen Zyklen Verzögerung ausgeführt (0-6 Zyklen, um genau zu sein), wobei man in der Regel nicht weiss wieviele Zyklen das sind. Beim Double IRQ fängt man diese unbekannte Verzögerung in zwei Schritten auf:
    1) Als erstes wird gleich zu Beginn der Interruptroutine ein zweiter Rasterinterrupt für die unmittelbar folgende Rasterzeile gesetzt. Danach werden eine Menge NOPs ausgeführt. Der Sinn dahinter ist, das der neu gesetzte Rasterinterrupt den gerade laufen ersten Interrupt unterbricht (das kann man problemlos machen, solange man keinen Müll auf dem Stack zurücklässt), und zwar während dieser gerade ein NOP ausführt. Da ein NOP nur zwei Taktzyklen benötigt kann man sicher sein das der zweite IRQ mit einer Verzögerung (die durch die Zuende-Abarbeitung des aktuellen Befehls zustande kommt) von null oder einem Zyklus ausgeführt wird.
    2) Die zweite IRQ-Routine wartet nun eine exakt bestimmte Anzahl von Taktzyklen ab, bis der Rasterstrahl fast am Ende der Rasterzeile angekommen ist. Dann wird die Sequenz lda $d012 cmp $d012 beq *+2 ausgeführt. Falls der zweite IRQ zu Beginn 0 Zyklen Verzögerung hatte dann ist der Rasterstrahl während des CMP gerade eben noch in der gleichen Rasterzeile wie beim LDA. Somit haben LDA und CMP die gleichen Werte und der BEQ verzweigt (dazu braucht er drei Zyklen). Falls der zweite IRQ andererseits zu Beginn eine Verzögerung von einem Zyklus hatte dann ist der Rasterstrahl während des CMP gerade in die nächste Zeile gesprungen. LDA und CMP haben dann unterschiedliche Werte, der BEQ verzweigt nicht und verbraucht deshalb nur zwei Zyklen. In beiden Fällen ist der Prozessor nun mit dem Rasterstrahl synchron.

    Der Code dazu (ungetestet, habe ihn gerade aus der Codebase herauskopiert)


    Und was für Probleme gibts eig. bei den sog. Badlines? Also, ich hab recherchiert und herausgefunden, dass bei den Badlines (alle 8 Rasterlines oder?) die Farbinformationen neu geholt werden und somit 40 Cycles "vergeudet" werden.

    Genau. Zu Beginn einer jeden Textzeile holt sich der VIC die entsprechenden 40 Zeichen aus dem Videoram, und dazu benötigt er 40 Taktzyklen extra. Während dieser Zeit wird der Prozessor angehalten, er hat in der Rasterzeile also nur noch 13 Zyklen zur Verfügung. Immerhin merkt sich der VIC die 40 Zeichen solange bis die nächste Textzeile beginnt (in der Regel also 8 Rasterzeilen später), und dann gibt's die nächste Badline.


    Was kann ich "dagegen machen" bzw. dem begegnen bzw. wo bekomm ich damit überhaupt Probleme?

    Probleme gibt es dann, wenn dein Programm in einer Rasterzeile mehr Taktzyklen benötigt als zur Verfügung stehen (z.B. weil du in der Zeile mehrere VIC-Register umschalten musst) oder wenn du einen Befehl exakt zu einem Zeitpunkt ausführen musst, an dem der Prozessor gerade angehalten wird. Letzteres kann zum Beispiel bei Rastersplits oder bei der Kommunikation mit externen Geräten der Fall sein.
    Die wahrscheinlich meist genutzte Methode dem zu begegnen ist das Eintreten der nächsten Badline mittels FLD hinauszuzögern. Da sich der VIC dann natürlich auch keine neuen Daten aus dem Videoram holt muss man das, was der VIC dann anzeigt, halt entsprechend kaschieren (z.B. durch Umschalten des Zeichensatzes)

    So, und jetzt ist glaube ich ein Zitat meinem Prof. angebracht: "Hat das jemand verstanden? Wenn nicht erkläre ich's nochmal." :)
    By the way, in der Magic Disk gab's mal einen IRQ-Kurs, den kann ich dir wärmstens empfehlen.

  • Genau. Zu Beginn einer jeden Textzeile holt sich der VIC die entsprechenden 40 Zeichen aus dem Videoram, und dazu benötigt er 40 Taktzyklen extra. Während dieser Zeit wird der Prozessor angehalten, er hat in der Rasterzeile also nur noch 13 Zyklen zur Verfügung

    .

    naja... eigentlich sind es 43 zyklen die verloren gehen (ehm. naja..so pauschal kann man das auch nicht sagen, da in den ersten 3 zyklen der prozessor die abarbeitung von schreibzugriffen noch erlaubt. z.b. die letzten 2 schreibzugriffe eines INC, DEC...usw befehls. wenn du aber nur NOPs betrachten würdest, gehen wirklich 43 Zyklen verloren).
    und daraus ergibt sich ein rest von 20 zyklen, die in einer badline noch zu verfügung stehen. (denn eine zeile hat ja - bei PAL - 63 zyklen)

  • Find ich echt spitze, dass du mir des so ausführlich erklärst, dann kann ich ja mal schön weiterwerkeln :)

    was ich mich bloß noch frag is, woher diese Verschiebung / dieses Flackern /was auch immer an meinem Splitscreen Rand kommt :/

  • das flackern kommt eben genau durch den oben beschrieben effekt der verzögerungen die beim start des irqs auftreten (und der raster eben noch nicht stabil ist).
    du forderst den irq ja in einer bestimmten zeile an. z.b. in zeile 80. nun"wartet" der processor eben darauf, dass die zeile 80 beginnt um dann ein irq auszulösen. allerdings muss er ja den "anderen befehl" der z.b. im Kernal oder Basic Rom gerade läuft zuende ausführen (0-6 taktzyklen). und dann wird die irq routine aufgerufen die die borderfarbe ändert. und da das farbeändern innerhalb eines sichbaren bereichs passiert, sieht man da auch das flackern (0-6 taktzyklen). würde das farbeändern ein paar zyklen früher durchgeführt, würde es zwar immernoch flackern, allerdings nicht mehr im sichtbaren brereich und man merkt nix mehr davon (der sichtbare bereich ist ja nur ca. 51 chars/zyklen breit... eine zeile hat ja aber 63 zyklen. d.h. wenn man die farbe zum "richtigen" zeitpunkt ändert, kann es ruhig flackern, ohne das man es sieht

  • so gut dann versuch ich wohl erstmal mir nen double interrupt anhand der beschreibung oben zu machen... soll ja doch noch ein bisschen lerneffekt dabei sein :D

  • hmm gibts hier sowas wie nen edit- button?

    naja auf jedenfall hab ich mir das ganze jetzt mal angeschaut und dann versucht den source zu verstehn... doch leider bin ich da dann auf ein paar kleine probleme gestoßen^^


    also:

    Warum "rettet" er den Inhalt von x y und des accus? wird das nicht beim auftreten eines interrupts automatisch gemacht?
    und wenn ich des jetzt richtig verstehe werden auch beim auslösen eines interrupts alle interrupts ausgeschalten (deswegen dann cli oder?). müssen die dann später im 2. interrupt code dann nicht sicherheitshalber wieder ausgeschaltet werden?

    des weiteren wundert mich, warum er die adresse für die interrupts an $fffe und $ffff schreibt und nicht an den interrupt vektor in den enhanced zeropages, oder steht da noch die adresse des "initialisierungs interrupts".

    und das asl $d019 find ich auch komisch :/

    und dann im 2. interrupt muss dann wieder ein dec $d012 stattfinden oder?

  • Zitat

    Warum "rettet" er den Inhalt von x y und des accus? wird das nicht beim auftreten eines interrupts automatisch gemacht?

    nein, nur das statusregister wird automatisch gerettet

    Zitat

    des weiteren wundert mich, warum er die adresse für die interrupts an $fffe und $ffff schreibt und nicht an den interrupt vektor in den enhanced zeropages, oder steht da noch die adresse des "initialisierungs interrupts".

    $fffe/$ffff ist der interruptvektor über den die cpu springt. normalerweise zeigt der auf eine routine im kernal, welche dann wiederum über den vektor bei 0314/0315 springt. wenn man nun das kernal abschaltet muss man da seine irq routine eintragen.

    Zitat

    und das asl $d019 find ich auch komisch :/

    mmmhja, als beispiel doof. was passiert ist folgendes: eigentlich muss man ja den wert der in d019 steht auch wieder da rein schreiben um die flags zurückzusetzen... da aber nun (durch eine eigenart der cpu) jeder "read/modify/write" befehl in wirklichkeit ZWEI werte schreibt, nämlich einmal den originalen wert und direkt danach den modifizierten wert funktioniert asl $d019 genauso wie auch inc/dec/rol/ror etc