L'écran affichait bien les couleurs du ZX Spectrum. Sauf qu'aucune n'avait l'air juste : tout semblait passé au gris sale, y compris ce qui devait être du blanc éclatant. La cause n'était ni le moniteur, ni le DAC à résistances, ni la bibliothèque vidéo. Il manquait un seul pixel noir à la fin de chaque ligne.
Ce détour par un écran terne est la dernière étape d'une série que publie Kevin depuis juillet, Simplified Pico VGA 1. Son objectif tient en une phrase : sortir du VGA sur Raspberry Pi Pico sans traiter la bibliothèque scanvideo comme une boîte noire. Avec une contrainte personnelle qui complique un peu tout : rester dans l'environnement Arduino 2. Adafruit a relayé la série fin juillet, puis à nouveau le 24 août 3.
Dix-sept broches
Le point de départ officiel ne manque pas de moyens. Le manuel matériel du RP2040 documente une carte d'exemple qui produit du VGA avec trois DAC à résistances, cinq bits par canal rouge, vert et bleu. Cinq broches par couleur, plus HSYNC et VSYNC : dix-sept GPIO pour la seule vidéo 4. Sur un Pico standard qui en expose vingt-six, ça laisse peu de place au reste.
C'est ce design qu'utilise la Pico VGA Demo Base de Pimoroni, qui ajoute autour un lecteur SD et une sortie audio I2S 5. Pour bricoler soi-même sur breadboard, chaque canal se câble avec cinq résistances en échelle — 8K, 4K, 2K, 1K, 500R — et deux résistances de 47R pour les synchronisations 2.

Dix-sept broches, c'est le prix du confort. Tout le reste de la série marchande ce prix.
La chasse aux fils
Avant d'écrire quoi que ce soit, Kevin a passé en revue ce qui existait déjà, et chaque projet raconte un compromis différent.
PicoVGA de Miroslav Nemecek descend à neuf broches en passant au format R3G3B2 : trois bits de rouge, trois de vert, deux de bleu, plus une sync composite. Son auteur assume le calcul : avec 264 Ko de RAM, sortir du 8 bits par pixel n'a guère de sens sur RP2040, et le dithering rattrape une partie de la perte 6. Ce projet est aujourd'hui remplacé par DispHSTX, qui utilise le périphérique HSTX du RP2350 pour sortir du DVI et du VGA 7. Autre voie, autre usage : MCEBlaster lit les signaux TTL de cartes MDA, CGA et EGA d'époque et les convertit pour un moniteur VGA moderne, avec huit broches — deux bits par couleur et les deux syncs 8.
Mais le déclic vient d'ailleurs. En cherchant à recréer la palette du ZX Spectrum, Kevin tombe sur pico-zxspectrum, qui pilote un VGA dans un mode baptisé RGBY1111 : bleu, vert, rouge sur une broche chacun, et une quatrième broche qui joue la luminosité 9. Le Y hérite directement de l'architecture du Spectrum de 1982 : ses attributs de couleur s'appliquent à des blocs de huit par huit pixels, et la brillance frappe tout le bloc. Quinze couleurs réellement distinctes — le noir existe en version « bright », donc en double — suffisaient déjà aux jeux de l'époque.
De dix-sept à six. Ce qu'on paie en retour, ce sont les demi-teintes : ici, pas d'orange doux ni de bleu pâle, seulement quinze teintes franches.
Des pixels en instructions
Restait à comprendre comment scanvideo occupe ces broches, et c'est là que la série devient passionnante.
La bibliothèque vit dans pico-extras 10. Le CPU n'y touche jamais aux broches. Il remplit des buffers de tokens de seize bits, le DMA pousse le tout vers un state machine PIO, et c'est le PIO qui crache les pixels au rythme voulu. Pour le mode par défaut, la doc suppose du 5-5-5 plus une broche optionnelle de transparence, tout étant déplaçable via des définitions de compilation 10.
Le tour de force se cache dans le programme PIO nommé video_24mhz_composable_default 11. Sa première instruction utile est out pc, 16 : seize bits sortent du bus et atterrissent directement dans le compteur de programme. Autrement dit, chaque token choisit la suite des instructions. Un token COLOR_RUN saute vers le bloc qui pose une couleur sur les broches, lit un compte de pixels, boucle dessus, puis reprend un nouveau token. Les aplats deviennent très compacts — la doc parle de run-length encoding pour les zones de couleur plate 10 — et deux paramètres de temporisation sont même réécrits à l'exécution pour étirer les pixels selon la résolution visée 11.
J'ai rarement vu un bus de données jouer le rôle d'un programme aussi littéralement. La mémoire ne contient pas seulement l'image : elle contient les instructions qui la dessinent.
Le détour Arduino
Officiellement, scanvideo se consomme en C avec le SDK Raspberry Pi. Kevin tient à son IDE Arduino, donc il a déplacé la bibliothèque à la main sur le core Arduino-Pico d'Earle Philhower 12.
Les fichiers partent à plat dans le dossier du sketch. Les deux programmes PIO, eux, doivent être assemblés avant : il passe les fichiers .pio dans l'outil pioasm de Wokwi et colle le résultat dans deux headers. Et comme les modes vidéo se configurent par définitions de préprocesseur compilées jusque dans les fichiers C de la bibliothèque, un simple #define avant le include ne suffit pas : il greffe un petit vgamode.h directement dans scanvideo.h. La fonction qui tournait sur le second cœur se découpe entre setup() et loop(), la façon Arduino de dire « initialisation » et « boucle infinie ».
Kevin a aussi dessiné une carte d'essai entre le Pico et la prise VGA, publiée avec son schéma 13. Un détail honnête, relevé dans son billet : le texte imprimé recommande 300R + 100R pour le RGBY1111, mais la bonne valeur est bien 330R, celle gravée à côté des composants.

Tout en gris
Premier essai du mode RGBY1111 : les bonnes couleurs apparaissent, mais toutes trop sombres. Le blanc éclatant du bas de mire ressort en gris crasseux.
L'enquête tient en trois actes. D'abord, Kevin soupçonne la structure du buffer : sa ligne était un seul gros aplat là où l'exemple officiel découpait chaque ligne en trente-deux morceaux. Il imite le découpage, l'image devient correcte. Puis l'oscilloscope montre quelque chose de plus net : sur la version qui rate, la broche bleue reste constamment haute, là où la version qui marche retombe à zéro, synchronisée avec chaque ligne 2.
Le vrai coupable n'est pas le découpage. C'est le pixel noir qui accompagne ce découpage : la version fonctionnelle termine sa ligne par un token RAW_1P de couleur noire, l'autre non. Et en relisant la documentation, on tombe sur un avertissement en majuscules : il faut terminer chaque ligne par au moins un pixel noir, sinon « vos couleurs débordent dans le blanking » 10.
Pourquoi ça assombrit tout l'écran ? Là, Kevin avoue ne pas savoir exactement. Un lecteur lui a envoyé une note de Ken Shirriff qui décrit le même symptôme : en oubliant de blanker les zones hors image, son image restait visible mais « très sombre, car le moniteur ne savait plus quelle tension représentait le noir » 14. L'idée la plus probable : le moniteur utilise le porche de blanking pour calibrer ce que « éteint » veut dire. Si cette zone porte une couleur, toute la référence glisse. Reste une zone d'ombre que Kevin partage volontiers : le mécanisme électrique précis n'est pas démontré dans la série.
Le noir retrouvé
Troisième version du code, un seul token noir ajouté en fin de ligne : les quinze couleurs tombent justes, brightness comprise 2.
Au bout du compte, six broches font l'affaire : quatre fils couleur et les deux synchronisations, puisque Kevin garde VSYNC là où pico-zxspectrum s'en passe 12.
Ce que raconte cette série dépasse le plaisir rétro. Un signal vidéo de 1986, conçu pour des téléviseurs analogiques, se négocie encore aujourd'hui en broches, en résistances et en cycles : chaque économie de fil se paie en profondeur de couleur, et un pixel oublié suffit à faire mentir tout l'écran. On peut utiliser scanvideo sans rien savoir de tout ça. Mais celui qui a suivi la descente — tokens, DMA, blanking — peut maintenant inventer son propre mode vidéo, pas seulement recopier un #define. Kevin, en tout cas, a continué : sa série s'est déjà prolongée en sortie vidéo compatible Spectrum pour l'ordinateur modulaire RC2014.
