Beiträge von berni im Thema „Mal wieder Raster-Interrupt“

    Die stx/sta's sollten genau zwischen den Zeitpunkten kommen, wo der Rasterstrahl das Innere von 226 verlässt und in das Innere von 227 eintritt. Wenn ich es richtig verstehe, wird das Badline-Verhalten so umgangen,

    Ja, so in etwa. Im Grunde genommen ist das mit der Badline sogar irrelevant. Das Problem hättest du in jeder anderen Zeile auch, da du ja vor dem Inneren, wie du es nennst, schon umschalten willst. Da ist noch gar nichts mit Badline, nur die Sprites stören da.

    Das Ganze ist tatsächlich noch einen kleinen Tick komplizierter, aber wenn es so tut, brauchst du das evtl. gar nicht zu beachten. Ich hatte es nur oben irgendwo schonmal erwähnt: Die einzelnen Register des VIC haben unterschiedliche Verzögerung. Beispielsweise (glaube ich grad zumindest) kannst du den Hintergrund schon drei Zyklen bevor der Rasterstrahl das Innere von 226 verlässt umschalten, weil die Wirkung halt erst später einsetzt. Ich hatte mal ein Demo gesehen, da wurde das ausgenutzt. Das hat dann so gewirkt, als würde in zwei direkt aufeinanderfolgenden Taktzyklen was geändert, was natürlich gar nicht möglich ist.

    Wenn ich ne Zeropage-Adresse statt "byte" verwende, bin ich zu früh.

    Deswegen hätte ich auch das ldy vor das sta vorgezogen. Dann hast du nochmal zwei Taktzyklen mehr Spielraum. Ansonsten sieht das aber schon super aus.

    Ich hab' etliche Ideen, was man machen könnte, weiß aber nicht genau, was am Ende gut funktioniert.

    1. INC $D019 ist nicht zeitkritisch, das kannst du auch noch später ausführen. Das spart dir dann immerhin 6 Taktzyklen.
    2. LDA $D012, CMP #226, BCC L_ELSE kannst du dir auch sparen, wenn du stattdessen den IRQ-Vektor ($314/$315) hin- und herbiegst, also zwischen zwei IRQ-Routinen wechselst. Die eine ist nur für Zeile 226 zuständig, die andere für alle anderen.
    3. "Deinen Tipp mit x, y und a habe ich aber nicht ganz verstanden." Die Idee ist, die drei Schreibzugriffe (im Diagramm oben BG, MC und CH) so nahe wie möglich beieinander zu haben. Wenn du die beiden ersten Tipps umgesetzt hast, ist in der Zeile vermutlich noch genug Zeit, um die drei Register zu laden, bevor du den rechten Rand erreichst. Das Timing sähe dann so aus:


      Bitte melde dich an, um diesen Anhang zu sehen.

      Selbst wenn jetzt das Timing um 6 Taktzyklen nach rechts verschoben sein sollte, ist auch der letzte Speicherzugriff noch rechtzeitig.

    4. "Zusätzlich habe ich noch den Wert, der in $d016 geschrieben werden soll, im Handler für Zeile 0 in die Zeropage bei $06 geschrieben," Wenn du den Wert für $d016 (und $d018) vorher schon kennst (und ich glaube, das ist hier der Fall), kannst du auf die Konstruktion mit dem ORA verzichten und stattdessen einfach ldx #$nn, lda #$nn schreiben. Das spart nochmal viele Taktzyklen ein. Inklusive Schreiben sind es dann nur noch 18 Taktzyklen, drunter geht nicht, wenn du nicht zufällig eine der Zahlen für ein anderes Register wiederverwenden kannst.

    Die meisten Probleme habe ich natürlich, wenn der Mauszeiger (auch zwei Sprites) sich auf der Zeile befindet und/oder das Startmenu offen ist und ich einen Eintrag highlighte. Aber selbst mit dem Mauszeiger weit weg und geschlossenem Startmenu gab es Probleme.

    Mit den Sprites ist alles nochmal etwas komplizierter. Es reicht nämlich nicht die Anzahl der Sprites (0, 2 oder 4 bei dir, wenn ich dich richtig verstanden habe), sondern man muss auch noch wissen, welche das sind. Es macht einen Unterschied, ob du die Sprites 0-3 oder die Sprites 4-7 verwendest, weil die Prozessorabschaltungen für jedes Sprite zu einem anderen Zeitpunkt stattfinden.

    Sodele, jetzt hab' ich wieder Zeit und inzwischen dein Programm auch verstanden.

    Zuerst einmal zu den Badlines: Das sind immer die obersten Zeilen eines 8er-Blocks. Der VIC muss ja wissen, welche Zeichen in dieser Textzeile dargestellt werden sollen, und die normalen Zugriffszeiten reichen dafür nicht aus, weshalb er den Prozessor kurzerhand abschaltet und sich die Zeichen aus dem Speicher holt, wenn eigentlich der Prozessor dran wäre.

    Was dich (und wohl auch ogd oben) vermutlich hierbei verwirrt hat, sind die Sprites: Die werden nämlich eine Zeile weiter unten dargestellt. Ein Sprite mit Y-Position 226 beginnt also erst in Rasterzeile 227.

    Jetzt zum Timing deines Programms. Ich hab' das mal schematisch dargestellt:

    Bitte melde dich an, um diesen Anhang zu sehen.

    Am Anfang von Rasterzeile 126 wird ein IRQ ausgelöst (möglicherweise auch schon einen Taktzyklus vorher, bei den IRQs bin ich mir immer nicht so ganz sicher...) Es dauert aber noch mindestens einen Taktzyklus, bis der Prozessor das bemerkt, also in Taktzyklus 1 in Zeile 126. Dann wird erst noch der aktuell bearbeitete Befehl abgearbeitet. Im Diagramm bin ich davon ausgegangen, dass da gerade in Taktzyklus 1 ein Befehl geendet hat. Das muss aber nicht so sein, im blödsten Fall muss man (ich glaube) noch 8 Taktzyklen warten. Das Maximum erreicht man aber nur mit illegalen Opcodes. Soweit ich weiß kann man in der Praxis davon ausgehen, dass es maximal 6 sind.

    Dann wird ein spezieller Prozessorbefehl ausgeführt, der den IRQ startet (im wesentlichen ist das der BRK-Befehl). Der dauert 7 Taktzyklen und der Prozessor macht dann mit der IRQ-Routine weiter. Wo man die findet, steht in $FFFE und $FFFF. Normalerweise ist das $FF48. Dort werden erst mal die Register auf den Stack gerettet (PHA TXA PHA TYA PHA) und dann geprüft, ob das ein BRK-Befehl war oder ein IRQ (TSX LDA $0104,X AND #$10 BEQ $FF58). Am Ende folgt dann noch ein indirekter Spung an die Stelle, die in $0314 und $0315 steht.

    Erst dann beginnt deine `RasterIRQ`-Routine. In der Grafik habe ich noch markiert, wo die relevanten Speicherzugriffe stattfinden (BG=Hintergrund, MC=Multicolor, CH=Zeichensatz). Wie man sehen kann, wird die Hintergrundfarbe im rechten Rahmen von Zeile 126 geändert und Multicolor im linken Rahmen von Zeile 127.

    Der Wechsel des Zeichensatz erfolgt an einer ganz kritischen Position (Taktzyklus 14). Das ist nämlich der Bereich, wo der Prozessor abgeschaltet wird (Badline). Ich bin mir da nicht ganz sicher, aber ich glaube, das ist gerade schon ein Taktzyklus zu spät und der tatsächliche Schreibzugriff erfolgt erst in Taktzyklus 54 im rechten Rahmen von Zeile 127.


    Wie schon geschrieben, das ist der Idealfall, wenn die IRQ-Bearbeitung frühestmöglich beginnt. Das ganze Diagramm kann aber um einige Taktzyklen nach rechts verschoben sein, dann bist du mit dem letzten Schreibzugriff definitiv in der Badline drin.

    Ich kann dir gerne die gesamte Routine zeigen. Die anderen Handler (bei 150, 80, 40 und 0) sind allerdings eher zeitunkritisch. Deshalb wüsste ich nicht, wo da das Problem liegen soll, wenn nicht genau beim Handler von Zeile 226. Die Routine davor (bei 150) läuft im Extremfall über ca. 10 Rasterzeilen.

    Ich hab's mal ausprobiert, bei mir läuft es stabil (und ich verstehe den Code nicht ganz, heute Abend habe ich dafür mehr Zeit, dann schaue ich es mir nochmal genauer an). Ein paar Annahmen musste ich aber treffen: `std_irq` hatte ich durch $ea81 ersetzt und alle nicht vorhandenen Unterroutinen einfach als `RTS`.

    Ich denke, deine Probleme kommen von den Sprites (die sind bei mir ja nicht da): Sprites machen ja auch so Mini-Badlines - da fallen dann pro Rasterzeile 5 bis 11 Zyklen aus, und zwar ausgerechnet in dem Bereich, in dem man gerne die Änderungen vornehmen würde (also im Rahmen links und rechts).

    Ein paar Dinge, die du ausprobieren kannst:

    1. Die Werte, die du in die Register schreibst, vorher schon berechnen:

    ldy #CL_LIGHTGRAY
    lda $d016
    ora #%00010000
    tax
    lda $d018
    ora #TASKCHARSHI
    sty BKGCOLOR
    stx $d016
    sta $d018

    Dann sind die drei Schreibbefehle direkt hintereinander und die Änderungen passieren innerhalb von 9 Taktzyklen. Weniger geht nicht. (Du kannst allerdings möglicherweise noch die Reihenfolge der ST-Befehle ändern.

    2. Die einzelnen Register der VIC haben eine unterschiedliche Latenz, bis die Änderung wirksam wird.) lda $d012
    cmp #225
    bcc L_ELSE
    lda #226
    - cmp $d012
    bne -

    Damit hast du ein besseres Timing - maximal 7 Taktzyklen Unterschied pro Durchlauf (wobei da die Sprites noch reinspielen, dann kann es noch deutlich mehr werden).

    3. Den Rasterstrahl exakt mit dem Prozessor synchronisieren (siehe Bitte melde dich an, um diesen Link zu sehen.).Mit Sprites ist das allerdings sehr schwierig.

    Das Problem liegt vermutlich nicht in dem geposteten Teilstück des Codes. Die Frage ist, wie du den IRQ auslösen lässt und ob andere IRQ-Auslöser noch aktiv sind. Zudem auch, was du sonst so noch (nach dem BCC-Sprung ans Ende des Listings) machst. Wenn du da mehr Details posten könntest, kann ich dir gerne weiterhelfen. So dürfte es nur stochern im Nebel sein.