La vidéo : du HDMI sans puce vidéo
La prise HDMI de la carte est reliée directement aux broches du RP2040. Pas de puce graphique : c’est le logiciel qui fabrique le signal, 270 millions de bits par seconde et par couleur.
Dans cette page
Le signal à produire
Un écran HDMI accepte le DVI, son ancêtre plus simple : trois paires de fils pour le rouge, le vert et le bleu, et une paire pour l’horloge. Chaque pixel de chaque couleur est envoyé sous forme d’un symbole de 10 bits, dit TMDS. Pour un écran 640 × 480 à 60 images par seconde, cela fait 252 millions de bits par seconde sur chaque paire.
Eliott65 a d’abord réglé le RP2040 à 252 MHz : un bit par cycle, exactement. Depuis la v0.52, le signal est un 720 × 480 à 60 Hz, et le RP2040 tourne à 270 MHz (voir plus bas) : toujours un bit par cycle.
Comment le pilote s’y prend
- La PIO sérialise. Un programme PIO de deux instructions envoie les bits un par un sur chaque paire, et un troisième fabrique l’horloge.
- La DMA alimente. Pour chaque couleur, des canaux DMA envoient les mots de la ligne et s’enchaînent seuls d’une ligne à l’autre (deux canaux par couleur à l’origine, trois depuis la v0.46, voir plus bas). Aucun cœur n’a besoin d’intervenir dans le flux.
- Pas d’image en mémoire. Une image 640 × 480 occuperait des centaines de kilo-octets. Le pilote ne garde que deux tampons de ligne déjà encodés et trois lignes de synchronisation constantes : environ 14 Kio en tout dans la première version. Pendant qu’une ligne part, la suivante est préparée.
L’image logique fait 320 × 240 pixels, doublés dans les deux sens. Chaque ligne logique doit donc être prête en 63,5 µs, le temps de deux lignes de l’écran.
L’écran texte « zéro copie »
En mode texte, l’écran fait 40 × 30 caractères. Le programme 6502 indique au noyau où, dans sa propre mémoire, se trouvent ses caractères ; le pilote vidéo les y lit directement, à chaque image. Aucun tampon d’écran n’est réservé : un programme qui n’affiche rien ne perd pas un octet.
Une variante ajoute une page d’attributs : un octet par case, qui donne la couleur du texte et celle du fond parmi 16 couleurs. Pour animer un titre arc-en-ciel, la démonstration ne réécrit que 15 octets d’attributs à chaque battement.

L’énigme des couleurs fausses
Premier affichage : le fond bleu est juste, mais le texte blanc sort bleu clair, une ligne blanche apparaît à droite, et sur la mire de barres l’écran décroche tout à fait.
Les vérifications éliminent une à une les pistes : les tampons réellement émis, relus par la sonde, sont justes ; la polarité aussi (une inversion aurait donné un fond jaune) ; un décalage de bits est exclu par le calcul ; réduire la puissance des sorties ne change rien.
La cause : pour obtenir chaque couleur exactement, la table de symboles choisissait, pour le
blanc, des symboles qui alternent à chaque bit (0101010101), soit un signal à 126 MHz. La
liaison de la carte le transmet mal ; sur une grande zone blanche, l’écran perd le signal.
La correction : choisir des symboles avec le moins de transitions possible (5 au plus au lieu
de 9), quitte à s’écarter de 2 niveaux sur 255. Le blanc devient 1011111111 puis 1000000000.
Affichage juste, confirmé à l’écran.
Du DVI au vrai HDMI (v0.44 – v0.46)
Le DVI ne transporte que l’image. Le HDMI ajoute, dans les intervalles de synchronisation, des îlots de données : la description du mode d’image, et le son. Pour les coder, le projet a lu deux réalisations ouvertes indépendantes (hdl-util/hdmi et PicoDVI-HDMI d’ikjordan), sans en reprendre de code, et le banc vidéo de l’émulateur est devenu un récepteur HDMI qui contrôle chaque paquet.
- v0.44 : un îlot par image décrit le mode (640 × 480, 4:3) ; 65 images par seconde, aucune ligne en retard sur la carte.
- v0.45 : « stable » sur l’écran de l’utilisateur ; le HDMI devient le mode par défaut.
- v0.46 : pour pouvoir placer un îlot sur chaque ligne de l’écran (et non sur chaque ligne logique, envoyée deux fois), la DMA est refondue : trois canaux par couleur, dont un anneau de fins de ligne qui avance seul. La liste de 525 lignes disparaît, ce qui libère environ 2 Kio.
Le son qui passe par ces îlots est raconté dans Le son.
Le texte plus rapide (v0.47)
Le son a pris un peu de temps au cœur 1, et la marge du mode texte était tombée à 1 µs. Le rendu du texte a été réécrit : caractères et attributs lus une fois par ligne au lieu de trois, pixels choisis sans branchement. 61 → 50 µs par ligne.
Un seul signal : 720 × 480 à 270 MHz (v0.50 – v0.52)
Le mode Hercules demande 720 pixels de large. Première étape (v0.50) : un signal 720 × 480 à 60 Hz, le format des modes vidéo de télévision (CEA-861), avec une horloge système de 270 MHz (10 × 27 MHz). C’est le signal que le projet Trinity avait validé sur cette carte et cet écran. Sur la carte : image « stable » selon l’utilisateur, aucune ligne en retard.
Restait une question : ce signal pour le seul mode Hercules, ou pour tout ? L’utilisateur a choisi « tout en 720 » (décision 0027) :
- un seul signal pour tous les modes d’écran ; l’horloge reste à 270 MHz du démarrage à l’arrêt, sans changement en route, donc sans bus, minuteries ni PIO à recaler ;
- les modes 320 × 240 (texte et graphisme) gardent leur image 640 × 480 au centre, avec des bordures noires de 40 pixels ;
- la 6502 va 9 % plus vite, et chaque ligne dispose de 7 % de cycles en plus pour être rendue.
Le HDMI en 720 (v0.51). L’îlot de données (38 mots utiles) ne tient pas dans une synchronisation horizontale de 31 mots. Il commence avec elle et déborde de 7 mots sur le palier qui suit ; chaque caractère de l’îlot porte alors le niveau de synchronisation de sa position. Au banc, le récepteur simulé compte 1 050 paquets justes et une synchronisation intacte à travers l’îlot ; sur la carte, l’image reste stable. Par défaut (v0.52) : le 720 × 480 devient le signal d’Eliott65 ; l’ancien 640 × 480 reste une option de compilation.
Le passage à 270 MHz a eu un prix sur le bus de la 6502 : voir Le bus.
Le mode Hercules (v0.53 à v0.60)
Le mode Hercules reprend l’esprit des cartes monochromes des premiers PC : 720 × 350 pixels, en 1 bit par pixel, avec une encre blanche, ambre ou verte. Comme le texte et le graphisme, l’image (31 500 octets) est lue directement dans la mémoire du programme 6502, sans copie : le programme dessine par de simples écritures. Les 350 lignes sont centrées dans les 480 du signal.
- Un codage choisi pour l’exactitude. Le noir et chaque niveau d’encre ont un symbole TMDS équilibré à lui seul (autant de 0 que de 1) : deux pixels voisins quelconques se codent dans un mot, et une table de 16 entrées par couleur code 4 pixels en 2 mots. L’image sortie au banc est exacte (écart 0, contre 2 niveaux dans les autres modes, voir l’énigme des couleurs plus haut).
- Pas de doublement. Chaque ligne de l’écran a son propre contenu : le cœur 1 prépare la ligne suivante pendant que la ligne courante part. Le mode est figé au début de chaque image.
- Une mémoire comptée. Avec le 720, la mémoire du firmware était presque pleine (504 octets libres). Quelques économies (code de sortie de programme inutile, routines de calcul plus petites, tables rangées avec la police) en ont rendu assez pour le mode : il reste 352 octets.
- La démonstration
HERCULE.E65dessine un cadre, une diagonale et un damier en ambre.
Le texte 80 × 25 (v0.54). Le mode a aussi son écran de texte : 80 colonnes sur 25 rangées, en cases de 9 × 14 pixels. Comme le texte ordinaire, les caractères sont lus sans copie dans la mémoire du programme, avec une page d’attributs facultative : inverse, souligné, clignotant.
- D’abord une police provisoire : la police 8 × 8 du projet (du domaine public), étirée sur 11 des 14 lignes de la case.
- Encore de la mémoire à trouver. Il manquait 976 octets. L’en-tête d’hibernation s’écrit désormais en partie depuis une page de zéros du noyau, et les fins de ligne sont produites par l’interruption au lieu d’être recopiées : il reste 532 octets libres. En relisant, une copie de la police par mots de 32 bits a été remise octet par octet : elle supposait un alignement que rien ne garantissait, et le processeur du RP2040 se serait arrêté.
- La démonstration
HTEXTE.E65affiche un titre en inverse, une ligne soulignée, un mot clignotant, la table des caractères et une règle de 80 colonnes, en vert.
Une police dessinée pour Eliott65 (v0.58). Le relevé publié de la police MDA d’origine est sous CC BY-SA 4.0, une licence que l’EUPL 1.2 du projet ne cite pas parmi ses licences compatibles : il n’a pas été repris. Le projet a donc dessiné sa propre police 9 × 14, sous EUPL 1.2 :
- 95 caractères, de l’espace au tilde, sur 7 colonnes ; les 8ᵉ et 9ᵉ restent vides pour séparer
les caractères. Les majuscules occupent les lignes 2 à 10, les minuscules 5 à 10, et les jambages
(
g j p q y , ;) descendent sur les lignes 11 et 12. - Une source qui se lit et se modifie : un fichier texte où chaque caractère est dessiné en
#et., d’où un outil tire le fichier du firmware. Des tests vérifient que tous les caractères existent, que les marges sont respectées et que la ligne de base est commune. - La police (1 344 octets) est recopiée de la flash en mémoire au passage en texte Hercules ; il reste 4 308 octets de RAM libre dans le firmware.
Le shell en 80 colonnes (v0.57). La commande MODE 80 passe le shell en texte Hercules 80 × 25,
en blanc, sans attributs ; MODE 40 le ramène en 40 × 30 couleur. Voir
Le multitâche.
Sur la carte : trop lent (v0.59), puis accéléré (v0.60). Mesurés sur la carte, les deux modes Hercules dépassent leur budget. Chaque ligne y est préparée pendant l’émission de la précédente : le budget est de 31,75 µs, deux fois moins que dans les modes 320 × 240, où une ligne logique couvre deux lignes de l’écran.
| Mode | Rendu d’une ligne (au plus) | Lignes en retard, par image |
|---|---|---|
| texte 40 × 30 (shell) | 46 µs, pour 63,5 µs | 0 |
| texte Hercules 80 × 25 | 64 à 65 µs | environ 126 sur 350 |
| graphique Hercules | 37 µs | environ 321 sur 350 |
Une ligne en retard part sans être finie, et sa fin peut montrer le contenu de la ligne d’avant ; cela n’a pas été vu, faute d’écran visible. Le banc de l’émulateur vérifiait une image exacte, mais pas le temps : c’est la carte qui a révélé le défaut. La cause est le coût de l’encodage : le graphique écrit 1 080 mots par ligne, et le budget en exige moins de 8 cycles par mot.
La v0.60 réécrit l’encodeur en assembleur, commun au graphique et au texte (deux lectures et une écriture de 4 mots par octet et par voie), et ne lit plus une rangée de texte qu’une fois pour ses 14 lignes. Un nouvel outil arrête le rendu sur des lignes choisies et compare ce que la carte encode vraiment à l’image attendue. Ce n’est pas encore vérifié sur la carte ; il reste 3 640 octets de RAM libre.
Le partage avec le bus
La vidéo et le bus de la 6502 utilisent tous deux la DMA. Allumer la vidéo faisait passer la 6502 de 7,52 à 7,38 MHz (mesure à 252 MHz) : le prix, environ 2 %, d’une image sans mémoire d’image.
Vérifier sans écran
Un outil relit par la sonde les tampons de ligne réellement envoyés, les décode, et reconstruit l’image attendue à partir de la page de texte de la 6502. Une mire de diagnostic (8 barres, puis des rampes rouge, verte, bleue et grise de 16 marches) a été validée sur photo. Dans l’émulateur, un banc fait tourner le vrai pilote et un écran simulé décode sa sortie : 0 pixel différent de l’attendu. Voir L’émulateur et les bancs.
Et le graphisme ?
Depuis la version 0.27, le pilote affiche aussi des tuiles et des lutins en 320 × 240. Le défi est alors de tenir les 63,5 µs par ligne ; c’est l’objet de l’article Tuiles et lutins.