Méthode et leçons
Un projet bas niveau se trompe souvent, et parfois en silence. Eliott65 s’est donné quelques règles pour que les erreurs se voient, et consigne celles qu’il a faites.
Dans cette page
La façon de travailler
- Petites versions. Chaque étape est une version numérotée (de 0.1 à 0.40 entre le 13 et le 14 octobre 2026), avec un journal qui dit ce qui a été fait, appris et mesuré.
- Une définition de « terminé ». Code compilé sans avertissement, tous les tests passants, documentation à jour, version incrémentée, et une mesure physique si le changement touche le bus ou les temps.
- Des décisions écrites. Chaque choix structurant fait l’objet d’une note de décision (un ADR) : le contexte, la décision, les options écartées, les conséquences. Voir Les décisions, en bref.
- Mesurer, puis décider. La MMU dans le bus avait un critère fixé d’avance (au plus 20 % de perte) ; la mesure a dit −34 %, et l’idée a été abandonnée.
Des tests qui ne se croient pas eux-mêmes
- Des outils indépendants : images de disque créées par
mkfs.fatet contrôlées parfsck.fat, plus un lecteur FAT écrit à part ; une référence Python du graphisme, écrite d’après la spécification et non d’après le code. - Des contre-épreuves : on introduit exprès une erreur (un miroir inversé, une couleur mal étendue, un bit faux dans la vidéo, une restitution oubliée) et on vérifie que les tests échouent.
- La carte en dernier juge : une suite d’acceptation tourne sur la carte réelle, et un outil compare l’émulateur à la carte.
Les erreurs, et ce qu’elles ont appris
Le journal du projet consigne ses propres erreurs. En voici une sélection, avec la règle qui en est sortie.
Un échec masqué par un filtre
Un test sur la carte a échoué, mais sa sortie passait par grep, dont le code de retour a masqué
l’échec : la version est passée sans validation. Plus tard, une compilation ratée, cachée de la même
façon, a fait flasher l’ancien firmware, et encore plus tard un filtre qui cherchait « error » en
minuscules a laissé passer une erreur d’assemblage.
Règle : set -o pipefail partout où une sortie est filtrée, filtres insensibles à la casse, et ne
valider qu’après un code de retour nul.
La documentation aussi se teste
Un chapitre du journal a été écrit après le passage des tests, et deux versions ont été commitées sans les relancer : un test de la documentation échouait (un lien d’index manquant).
Règle : relancer toute la suite après avoir écrit la documentation, et ne commiter que sur un code de retour nul.
Des mesures faites sur le mauvais firmware
Quand le flashage échouait en silence (la 6502 écrasait le programme d’OpenOCD), plusieurs mesures avaient été prises sur l’ancien firmware. Elles ont été écartées et refaites.
Règle : le flashage échoue bruyamment ; on ne mesure qu’un firmware vérifié.
Un délai « raisonnable »
Le délai de 50 ms du pilote USB ressemblait à une panne matérielle. La norme accorde 500 ms.
Règle : prendre les délais dans la norme, pas au jugé.
Un bloc « libre » qui ne l’était pas
Le dernier bloc de la clé, qu’on croyait vide, contenait 489 octets non nuls.
Règle : toujours sauvegarder avant d’écrire sur un support qui n’est pas à nous.
Des frappes au mauvais endroit
Des essais semblaient échouer parce que les touches simulées arrivaient dans le mauvais programme, ou au milieu d’une saisie en cours.
Règle : vérifier l’état (quel programme tourne, que contient la saisie) avant de taper.
La retenue de la 6502
Deux fois, une soustraction 32 bits écrite en assembleur 6502 a donné des durées négatives : une
comparaison (CPY, puis CPX) placée dans la boucle modifiait la retenue et cassait la chaîne
d’emprunts de SBC. Les horodatages bruts, relus par la sonde, ont montré que le noyau était juste et
que l’erreur était dans le programme de mesure.
Le compilateur plus malin que prévu
Une fonction marquée pour s’exécuter en RAM avait été intégrée par le compilateur dans une autre, restée en flash : la boucle vidéo du cœur 1 tournait depuis la flash depuis le début. De même, la police du texte avait été rangée en flash, ce qui retardait des lignes vidéo quand le cœur 0 lisait la clé.
Règle : un outil vérifie, après chaque compilation, qu’aucun code du cœur 1 n’est en flash.
Le test qui avait tort
Dans le graphisme, deux fois, le moteur et la référence étaient d’accord entre eux, contre le cas écrit à la main. C’était l’attente qui était fausse (un octet mal placé, des tables posées au mauvais endroit).
Leçon : une référence indépendante protège aussi contre ses propres certitudes.