Tachibana

Ein DIR, das den Bildschirm löscht: die Jagd auf eine geteilte Sperre

#neo6502 #firmware

Auf dem Neo6502 löschte im Hercules-Modus der erste DIR-Befehl nach dem Start den Bildschirm. Zwölf Korrekturen, elf durch Messung widerlegt, vier falsch kalibrierte Instrumente — und am Ende eine SDK-Sperre, die sich beide Kerne teilten und den Video-Interrupt um eine einzige Zeile verzögerte. Eine Zeile genügte.

Von bmarty · Tachibana · 25. September 2026

Der Neo6502 verbindet einen 65C02 mit einem RP2040: Letzterer übernimmt alles Übrige, einschließlich der DVI-Videoausgabe, über die Bibliothek PicoDVI von Luke Wren. Unsere Firmware Trinity ergänzt einen «Hercules»-Modus: 720 × 350 monochrom innerhalb eines 720 × 480-Signals mit 60 Hz — ein nativer Modus, eine Bildschirmzeile pro Bildzeile, während der Modus 320 × 240 jede Zeile verdoppelt und damit doppelt so viel Zeit hat.

Der Fehler war reproduzierbar und absurd: MODE 1 funktionierte, und dann löschte das erste DIR das Bild. Das zweite lief unbemerkt durch. Die Tastatur reagierte während der Störung weiter. Nur ein Moduswechsel brachte das Bild zurück.

1. Was die Messung widerlegt hat, eine Hypothese nach der anderen

Der Unterschied zwischen beiden Befehlen war der FatFs-Cache: kalt liest der erste zweiundzwanzig Sektoren vom USB-Stick; warm fast nichts. Die Plattenlast war der Auslöser. Blieb die Frage, wie sie die Anzeige zerstörte.

Zuerst verdächtigten wir Speichermangel, dann zu wenige Puffer, dann die Busarbitrierung, dann einen Monitor, der die Synchronisation verliert. Jede Erklärung wurde von der nächsten Messung widerlegt, und zwei darauf gelieferte Korrekturen verschlimmerten den Fehler, bevor sie zurückgenommen wurden:

Wir probierten auch ein VGA-Timing 720 × 400 bei 70 Hz — das des Textmodus der MDA-Karten, genau 720 Pixel breit. Der Monitor meldete endlich einen Modus, was er bei 720 × 480 nie getan hatte (ein Timing für Fernseher). Doch das Bild kam schlecht heraus, und die Spur ging in Reserve.

2. Das Instrument verfälscht die Messung, viermal

Die Debug-Schnittstelle der Firmware hat nie funktioniert: die Leitung lag nicht am Erweiterungsstecker. Die gesamte Instrumentierung lief über die SWD-Sonde, die den Speicher des RP2040 im Betrieb liest. Wir schrieben ein Werkzeug, das auf der Maschine tippt, indem es direkt in die Tastaturwarteschlange der Firmware schreibt, und die Zähler ausliest — brauchbar bei schwarzem Bild.

Dieses Instrument hat uns viermal belogen, und jede Lüge kostete eine Hypothese:

Angenommene Regel: für jede Messung von einer Spannungsunterbrechung ausgehen, nie von einem Software-Reset. Und nie zwei Versuche vergleichen, die nicht vom gleichen Zustand starten.

3. Was die Blackbox zeigte

Alle Messungen entstanden danach, wenn die Störung schon saß. Also setzten wir einen Flugschreiber ein: genau im Moment, in dem die Abweichung erkannt wird, und vor jeder Reparatur, kopiert die Firmware den vollständigen Zustand der sechs DMA-Kanäle und der Video-Zustandsmaschine.

Das Ergebnis war eindeutig. Für jede der drei TMDS-Spuren nutzt PicoDVI einen Daten-Kanal und einen Steuer-Kanal, der ihn umprogrammiert. Während der Störung trugen zwei Datenkanäle die Konfiguration einer anderen Spur — falsches Anforderungssignal, falsche FIFO. Zwei Kanäle schrieben in dieselbe FIFO, ein dritter bekam nichts mehr. Die Blocklisten im Speicher waren dagegen unversehrt: der Fehler lag nicht in den Daten, sondern in ihrem Laden.

Ein Foto des Bildschirms vervollständigte das Bild besser als zehn Messungen: nach einem DIR stand der Text doppelt, in einer blauen und einer gelben Kopie, etwa vierzig Pixel auseinander. Gelb = Rot + Grün: die anderen beiden Kanäle liefen untereinander noch gleich, nur der blaue verschob sich. Blau ist die Synchronisationsspur, die einzige mit vier Steuerblöcken, wo die anderen zwei tragen. Eine Verschiebung um einen Rang bewegt sie nicht gleich weit: die drei Spuren laufen auseinander.

4. Den Fehler harmlos machen, bevor man ihn versteht

Daraus entstand eine Korrektur, die die Ursache nicht behebt: allen Spuren gleich viele Blöcke geben, indem die Austastlücke der beiden Datenspuren genauso zerteilt wird wie die der Synchronisationsspur. Ein Überlauf verschiebt sie dann alle mit derselben Phase, und das Bild bleibt aus einem Stück.

Auf der Platine, mit vier DIR hintereinander: der Überlauf trat weiter auf (fünfzehnmal), aber die drei Spuren bewegten sich gemeinsam — null Phasenversatz — und der Text war scharf. Der Fehler war unsichtbar geworden.

Das ist nützlich, aber nicht befriedigend. Die eigentliche Frage blieb: warum lagen die Steuerkanäle eine Runde vorn?

5. Die Ursache: eine Sperre, die sich beide Kerne unwissentlich teilten

Die endlich richtig gefilterte Latenzmessung ergab eine bescheidene und entscheidende Zahl. Zwischen zwei aktiven Zeilen liegt das nominale Intervall bei 32 µs. Während des DIR war der schlechteste Wert 63 µs — also eine einzige Zeile Verzug, niemals mehr. Keine lange Blockade, kein kritischer Abschnitt von hunderten Mikrosekunden: eine Zeile.

Und eine Zeile genügt. Während dieses Verzugs beenden die Datenkanäle ihren Block und verketten zu ihren Steuerkanälen, die dann von selbst die Liste durchlaufen — ohne dass jemand darum bittet. Sie liegen eine Runde vorn, die Spuren verschieben sich, und der Rest folgt.

Blieb, was den Interrupt aufhielt. Die Antwort lag in den Sperrnummern. PicoDVI schützt seine vier Warteschlangen mit Spinlocks, die über next_striped_spin_lock_num() beschafft werden. Diese SDK-Funktion verteilt die Sperren 16 bis 23 der Reihe nach an alle Aufrufer — Warteschlangen, Mutexe, Alarm-Pools und was Bibliotheken sonst nutzen. Auf der Platine ausgelesen: PicoDVI hatte die Nummern 18 und 19, die FatFs und der USB-Stack vom anderen Kern aus nehmen konnten.

Das entscheidende Detail steht in der SDK-Dokumentation: spin_lock_blocking beginnt damit, die Interrupts abzuschalten auf dem wartenden Kern. Als der Anwendungskern die Sperre während eines Plattenschubs hielt, wartete der Videokern darauf — blind. Sein Zeileninterrupt kam eine Zeile zu spät.

Das SDK dokumentiert den Kompromiss ohne Umschweife: diese Sperren sind geteilt, und die Nummern innerhalb eines Bereichs zu verteilen «verringert die Wahrscheinlichkeit», dass zwei Nutzungen auf dieselbe fallen. Wahrscheinlichkeit, keine Garantie.

6. Die Korrektur passt in eine Zeile

// vorher
dvi_init(&dvi0, next_striped_spin_lock_num(), next_striped_spin_lock_num());

// nachher
static int slTmds = -1, slColour = -1;
if (slTmds < 0) { slTmds = spin_lock_claim_unused(true); slColour = spin_lock_claim_unused(true); }
dvi_init(&dvi0, slTmds, slColour);

Reservierte Sperren, die niemand sonst nehmen kann (Nummern 24 und 25, außerhalb des geteilten Bereichs). Auf der Platine gemessen, kaltes DIR über zweiundzwanzig Sektoren, zwei Versuche, einer davon nach einer Spannungsunterbrechung:

vorhernachher
Kanalüberläufe9 bis 150
Intervall zwischen zwei Zeilen63 µs33 µs (der Nominalwert)
verspätete Zeilen während des DIR+10 bis +16+2
Bildschwarz oder doppelter Texteinwandfrei

Der Fehler tritt nicht mehr auf. Das ist kein Umweg mehr.

7. Warum es niemand anders gesehen hat

Wir haben nachgesehen, wie die anderen PicoDVI-Projekte die Bibliothek initialisieren: das Original, der Fork ikjordan, der Fork xep80, pico-pacPlus. Alle übernehmen das Muster des Ursprungsbeispiels, next_striped_spin_lock_num() — und es passt ihnen. Der Kompromiss des SDK zeigt sich nur in einer besonderen Lage, die drei Bedingungen zusammenbringt:

Emulatoren, die Programme von einer SD-Karte laden, bringen die ersten beiden zusammen. Wir wissen nicht, ob andere auf diesen Fall gestoßen sind: man braucht eine Sonde und einen Zähler für den Vorlauf der DMA-Kanäle, um ihn zu sehen, und wir haben ihn nirgends beschrieben gefunden. Genau darum gibt es diesen Artikel — wenn jemandem die Signatur bekannt vorkommt, erspart sie ihm die zwei Tage, die sie uns gekostet hat.

8. Was diese Jagd hinterlässt

Zwölf Korrekturen, elf widerlegt. Vier falsche Messungen, die neu kalibriert werden mussten. Und zwei Dinge, die den Fall wirklich geöffnet haben, keines davon aus einer Messung: ein Foto des Bildschirms, das zeigte, dass sich nur eine Spur verschob, und das Auslesen der Sperrnummern, das die Ursache lieferte.

Eine Korrektur, die an ihren eigenen Zählern gemessen wird, ist eine nicht geprüfte Korrektur. Zweimal in derselben Nacht standen alle Anzeigen auf Grün, während der Bildschirm nichts zeigte.

Die Signatur dieses Fehlers ist es wert, behalten zu werden, denn sie ist typisch für den RP2040: eine Verzögerung von genau einer Zeiteinheit des Echtzeitsystems — hier einer Zeile — unabhängig von der Taktfrequenz. Es ist kein Durchsatzproblem: es ist jemand, der eine Sperre hält.

Quellen & Links

← Neo6502 · Unsere Projekte →