Tachibana

Een DIR die het scherm dooft: de jacht op een gedeelde lock

#neo6502 #firmware

Op de Neo6502 doofde in Hercules-modus het eerste DIR-commando na het opstarten het scherm. Twaalf correcties, elf weerlegd door de meting, vier verkeerd geijkte instrumenten — en uiteindelijk een lock van de SDK die beide cores deelden en de video-interrupt één scanlijn vertraagde. Eén lijn was genoeg.

Door bmarty · Tachibana · 25 september 2026

De Neo6502 combineert een 65C02 met een RP2040: die tweede doet al het overige, inclusief de DVI-video-uitgang, via de bibliotheek PicoDVI van Luke Wren. Onze firmware Trinity voegt een «Hercules»-modus toe: 720 × 350 monochroom binnen een 720 × 480-signaal op 60 Hz — een native modus, één schermlijn per beeldlijn, terwijl de 320 × 240-modus elke lijn verdubbelt en dus dubbel zoveel tijd heeft.

De fout was reproduceerbaar en absurd: MODE 1 werkte, en dan doofde de eerste DIR het beeld. De tweede ging ongemerkt voorbij. Het toetsenbord bleef tijdens de storing reageren. Alleen een moduswissel bracht het beeld terug.

1. Wat de meting weerlegde, hypothese na hypothese

Het verschil tussen beide opdrachten was de FatFs-cache: koud leest de eerste tweeëntwintig sectoren van de USB-stick; warm bijna niets. De schijfbelasting was de trigger. Bleef de vraag hoe die de weergave brak.

Eerst vermoedden we geheugentekort, dan te weinig buffers, dan de busarbitrage, dan een monitor die de lock verliest. Elke verklaring werd door de volgende meting weerlegd, en twee correcties die daarop werden uitgeleverd verergerden de fout voordat ze werden teruggetrokken:

We probeerden ook een VGA-timing 720 × 400 op 70 Hz — die van de tekstmodus van MDA-kaarten, precies 720 pixels breed. De monitor meldde eindelijk een modus, wat hij bij 720 × 480 nooit had gedaan (een timing bedoeld voor televisies). Maar het beeld kwam er slecht uit, en het spoor ging in reserve.

2. Het instrument vervalst de meting, vier keer

De debug-seriële poort van de firmware heeft nooit gewerkt: de draad zat niet op de uitbreidingsconnector. Alle instrumentatie liep via de SWD-probe, die het geheugen van de RP2040 leest terwijl die draait. We schreven een hulpmiddel dat op de machine typt door direct in de toetsenbordwachtrij van de firmware te schrijven, en de tellers uitleest — bruikbaar met zwart beeld.

Dat instrument loog vier keer tegen ons, en elke leugen kostte een hypothese:

Aangenomen regel: voor elke meting vertrekken van een stroomonderbreking, nooit van een software-reset. En nooit twee proeven vergelijken die niet vanuit dezelfde toestand starten.

3. Wat de zwarte doos liet zien

Alle metingen werden na de feiten genomen, als de storing al zat. Dus plaatsten we een zwarte doos: op het exacte moment dat de afwijking wordt gedetecteerd, en vóór elke reparatie, kopieert de firmware de volledige toestand van de zes DMA-kanalen en van de videotoestandsmachine.

Het resultaat was eenduidig. Voor elk van de drie TMDS-banen gebruikt PicoDVI een data-kanaal en een control-kanaal dat het herprogrammeert. Tijdens de storing droegen twee datakanalen de configuratie van een andere baan — verkeerd verzoeksignaal, verkeerde FIFO. Twee kanalen schreven naar dezelfde FIFO, een derde kreeg niets meer. De bloklijsten in het geheugen waren daarentegen intact: de fout zat niet in de gegevens, maar in het laden ervan.

Een foto van het scherm maakte het beeld beter compleet dan tien metingen: na een DIR stond de tekst dubbel, één blauwe en één gele kopie, ongeveer veertig pixels uit elkaar. Geel = rood + groen: de andere twee kanalen liepen nog onderling gelijk, alleen de blauwe schoof op. Blauw is de synchronisatiebaan, de enige die vier controleblokken draagt waar de andere twee dragen. Eén rang verschuiving beweegt hem niet evenveel: de drie banen lopen uit de pas.

4. De fout onschadelijk maken, vóór we hem begrepen

Daaruit kwam een correctie die de oorzaak niet wegneemt: alle banen hetzelfde aantal blokken geven, door de blanking van de twee databanen net zo in stukken te knippen als die van de synchronisatiebaan. Een overloop verschuift ze dan allemaal met dezelfde fase, en het beeld blijft uit één stuk.

Op de kaart, met vier DIR achter elkaar: de overloop trad nog steeds op (vijftien keer), maar de drie banen bewogen samen — nul faseverschil — en de tekst was scherp. De fout was onzichtbaar geworden.

Dat is nuttig, het is niet bevredigend. De echte vraag bleef: waarom liepen de controlekanalen een ronde voor?

5. De oorzaak: een lock die beide cores ongemerkt deelden

De latentiemeting, eindelijk goed gefilterd, gaf een bescheiden en beslissend cijfer. Tussen twee actieve lijnen is het nominale interval 32 µs. Tijdens de DIR was de slechtste meting 63 µs — dat wil zeggen één lijn vertraging, nooit meer. Geen lange blokkade, geen kritieke sectie van honderden microseconden: één lijn.

En één lijn is genoeg. Tijdens die vertraging maken de datakanalen hun blok af en ketenen door naar hun controlekanalen, die dan zelf door de lijst lopen — zonder dat iemand erom vraagt. Ze lopen een ronde voor, de banen verschuiven, en de rest volgt.

Restte de vraag wat de interrupt ophield. Het antwoord zat in de lock-nummers. PicoDVI beschermt zijn vier wachtrijen met spinlocks verkregen via next_striped_spin_lock_num(). Die SDK-functie deelt de locks 16 tot 23 bij toerbeurt uit onder alle aanvragers — wachtrijen, mutexen, alarmpools, en wat bibliotheken ook gebruiken. Uitgelezen op de kaart: PicoDVI had de nummers 18 en 19, die FatFs en de USB-stack vanaf de andere core konden nemen.

Het beslissende detail staat in de SDK-documentatie: spin_lock_blocking begint met het uitschakelen van de interrupts van de wachtende core. Toen de applicatiecore de lock hield tijdens een schijfsalvo, wachtte de videocore erop, blind. Zijn lijninterrupt kwam één lijn te laat.

De SDK documenteert de afweging zonder omhaal: die locks zijn gedeeld, en de nummers binnen een bereik verdelen «verkleint de kans» dat twee gebruiken op dezelfde vallen. Kans, geen garantie.

6. De oplossing past op één regel

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

// na
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);

Gereserveerde locks, die niemand anders kan nemen (nummers 24 en 25, buiten het gedeelde bereik). Gemeten op de kaart, koude DIR van tweeëntwintig sectoren, twee proeven waaronder één vanaf een stroomonderbreking:

voorna
kanaaloverlopen9 tot 150
interval tussen twee lijnen63 µs33 µs (het nominale)
vertraagde lijnen tijdens de DIR+10 tot +16+2
beeldzwart, of dubbele tekstperfect

De fout treedt niet meer op. Dit is geen omweg meer.

7. Waarom niemand anders het heeft gezien

We keken hoe de andere PicoDVI-projecten de bibliotheek initialiseren: het origineel, de fork ikjordan, de fork xep80, pico-pacPlus. Allemaal nemen ze het patroon van het oorspronkelijke voorbeeld over, next_striped_spin_lock_num() — en dat past hen. De afweging van de SDK komt alleen in een bijzondere situatie aan het licht, die drie voorwaarden verenigt:

Emulators die programma's van een SD-kaart laden verenigen de eerste twee. Wij weten niet of anderen dit geval zijn tegengekomen: je hebt een probe en een teller voor DMA-kanaalvoorsprong nodig om het te zien, en we hebben het nergens beschreven gevonden. Dat is precies de reden voor dit artikel — als de signatuur iemand bekend voorkomt, spaart het hem de twee dagen die het ons kostte.

8. Wat deze jacht nalaat

Twaalf correcties, elf weerlegd. Vier valse metingen die opnieuw gekalibreerd moesten worden. En twee dingen die de zaak werkelijk openden, geen van beide uit een meting: een foto van het scherm, die liet zien dat maar één baan verschoof, en het uitlezen van de lock-nummers, dat de oorzaak gaf.

Een correctie die op haar eigen tellers wordt beoordeeld, is een niet-beoordeelde correctie. Twee keer op dezelfde avond stonden alle indicatoren op groen terwijl het scherm niets toonde.

De signatuur van deze fout is het onthouden waard, want ze is generiek voor de RP2040: een vertraging van precies één tijdseenheid van het realtimesysteem — hier één lijn — ongevoelig voor de klokfrequentie. Het is geen doorvoerprobleem: het is iemand die een lock heeft.

Bronnen en links

← Neo6502 · Onze projecten →