Le ZX Spectrum est arrivé sur Hacker News, mais le mode texte révèle les compromis de sa ROM
- Ethan Carter

- il y a 2 heures
- 14 min de lecture
Le ZX Spectrum a refait surface sur Hacker News grâce à une visite guidée de son système publiée en 2026, qui met en lumière un conflit enfoui dans sa ROM de 16K. Afficher un caractère peut être simple. Construire une sortie texte fiable en code machine exige de comprendre des hypothèses non documentées, des variables système modifiables, des attributs persistants et des chemins d’entrée propres au matériel.
Michael Martin a publié la visite guidée en code machine le 30 mai 2026. L’article s’inscrit dans le prolongement d’une précédente exploration de BASIC, mais il ne se contente pas de traduire des commandes familières en assembleur Z80. Il montre où s’arrête l’environnement de programmation pratique de Sinclair et où commence son firmware à la structure plus lâche.
Cette distinction donne à l’article une portée qui dépasse le rétrogaming. Les machines Commodore proposaient des tables de sauts KERNAL stables, tandis que MSX définissait des appels firmware communs à plusieurs fabricants. Le Spectrum encourageait plutôt les programmeurs à combiner quelques points d’entrée de la ROM avec un accès direct à l’état du système. Cette approche économisait des couches d’abstraction, mais transférait aux développeurs le travail de compatibilité et de débogage.
Le résultat n’est ni une fonctionnalité nouvellement découverte ni l’annonce d’un produit moderne. Il s’agit d’un examen attentif d’un ancien compromis d’ingénierie. Le Spectrum mettait des primitives utiles à disposition avec très peu de mémoire, sans jamais les transformer en une plateforme propre pour le code machine.
Ce que la visite guidée du système ZX Spectrum a réellement changé
La nouvelle contribution est un parcours cohérent en code machine, de la sortie texte aux graphismes, aux entrées et à un affichage complet en fonctionnement.
Le Spectrum ne s’est pas soudainement doté d’un mode texte. L’article de Martin enrichit les explications disponibles en réunissant plusieurs mécanismes épars dans une seule séquence pratique. Il commence par une routine compacte de type Hello World, puis aborde les codes de caractères, les contrôles de couleur, les graphismes personnalisés, l’effacement de l’écran, le balayage du clavier et l’entrée joystick.
La première étape utilise RST $10, une entrée de redémarrage de la ROM qui affiche le caractère contenu dans le registre A du Z80. Un redémarrage est un appel compact vers une adresse fixe en mémoire basse. Avant de l’utiliser, l’exemple écrit zéro dans TVFLAG à IY+2, afin de diriger la sortie vers la zone principale de l’écran.
Cette séquence donne à l’opération de base une apparence presque moderne. Un programme charge un pointeur vers un message, récupère un octet, appelle l’imprimante, puis recommence. Martin place le code à l’adresse $7000, laissant de la place en dessous pour BASIC tout en conservant une mémoire utilisable sur une machine 16K.
La simplicité disparaît dès que le texte requiert un état. L’écran du Spectrum affiche normalement 24 lignes de 32 caractères. Son firmware divise ces lignes en une fenêtre supérieure de 22 lignes et une fenêtre inférieure de deux lignes destinée à l’édition et aux messages d’état. Les spécifications d’affichage originales de Sinclair confirment cette séparation et décrivent un affichage de 256 par 192 pixels.
La machine ne dispose pas d’un matériel de caractères distinct, comparable à celui d’un terminal classique. Sa ROM dessine un glyphe de 8 par 8 dans la mémoire bitmap, puis écrit les informations de couleur pour la cellule correspondante. La sortie texte dépend donc déjà de l’agencement graphique, des attributs courants, de la position du curseur et du canal de sortie sélectionné.
Martin étend ensuite ce parcours avec 16 caractères semi-graphiques prédéfinis et des graphismes définis par l’utilisateur. Les semi-graphiques divisent une cellule de caractères en blocs, permettant à des formes simples de passer par l’imprimante texte ordinaire. Les graphismes définis par l’utilisateur occupent les codes de caractères à partir de $90, leurs données bitmap étant localisées via le pointeur système UDG.
L’exemple final de l’article associe ces possibilités dans une bannière colorée avec une image de parapluie personnalisée. Il charge quatre définitions de caractères, émet des octets de contrôle en ligne, attend une entrée, puis restaure l’écran. Martin indique que le package en code machine représente moins de la moitié de la taille de sa précédente version BASIC, même en incluant son chargeur et son en-tête de bande.
Cette comparaison est le véritable intérêt de l’événement. L’article ne se contente pas de présenter des adresses isolées. Il démontre que la ROM du Spectrum peut servir de framework applicatif compact, à condition que le programmeur accepte la responsabilité de son état caché.
Pourquoi une audience Hacker News s’intéresse encore à cette ROM
Le Spectrum condense un problème familier de systèmes dans une machine suffisamment petite pour être comprise presque entièrement.
L’article a atteint Hacker News parce qu’il aborde le matériel rétro comme un système logiciel inspectable. Chaque opération majeure suit un chemin visible. Un caractère passe par une entrée fixe de la ROM, lit un glyphe, touche la mémoire bitmap, applique un octet d’attribut et avance un curseur représenté dans l’état du système.
Les développeurs modernes rencontrent les mêmes catégories de problèmes derrière des interfaces bien plus vastes. Les bibliothèques conservent une configuration. Les flux de sortie ont un état. La compatibilité dépend de comportements que la documentation ne garantit pas toujours. Les abstractions matérielles exposent des échappatoires lorsque les interfaces normales se révèlent trop limitées.
Sur le Spectrum, ces problèmes tiennent dans un espace d’adressage Z80. Le modèle d’origine utilisait un processeur Z80A cadencé à 3,5 MHz, une ROM de 16K et 16K ou 48K de RAM. Ces contraintes rendent chaque abstraction visible dans la carte mémoire.
L’affichage est particulièrement instructif. Un écran Spectrum standard occupe 6 912 octets : un bitmap monochrome de 6 144 octets et 768 octets d’attributs. Chaque octet d’attribut fournit les couleurs de premier plan et d’arrière-plan, la luminosité et le clignotement pour une cellule de 8 par 8.
Cette conception économisait de la mémoire, mais elle liait les pixels voisins à un même choix de couleur. Le résultat bien connu est l’attribute clash, où des objets de couleurs différentes ne peuvent pas traverser la même cellule sans s’influencer mutuellement. L’impression de texte hérite de cette architecture, car chaque glyphe atterrit dans l’une de ces cellules.
L’explication de Martin apporte une seconde leçon. Une petite interface documentée ne crée pas nécessairement une plateforme de programmation stable. Le Spectrum expose une imprimante de caractères efficace, mais les programmes sophistiqués doivent aussi connaître les adresses de variables telles que ATTR-T, MASK-T, P-FLAG, SCR-CT et UDG.
La description officielle des variables système présente la mémoire partagée par BASIC et les routines de ROM. Appeler le firmware tout en modifiant directement ces valeurs est efficace, mais crée un couplage étroit. Un programme dépend à la fois de la routine appelable et de l’état interne attendu par cette routine.
C’est là que le Spectrum diffère des machines conçues autour de frontières firmware plus formelles. Le KERNAL de Commodore utilisait des vecteurs de saut fixes pour les services courants. MSX standardisait les appels afin que les logiciels puissent cibler des machines de différents fabricants. Le BIOS de l’IBM PC a lui aussi établi des services appelables au-dessus du matériel, même si les développeurs les ont ensuite contournés pour gagner en vitesse.
L’approche de Sinclair était moins formelle. Elle convenait bien à BASIC, car Sinclair contrôlait à la fois l’interpréteur et la ROM. Les programmeurs en assembleur recevaient des détails d’implémentation utiles plutôt qu’un vaste contrat de compatibilité.
Ce compromis explique l’intérêt qu’il suscite encore. Le Spectrum offre une étude de cas remarquablement claire de la façon dont une implémentation interne devient une interface publique. Dès lors que les programmeurs bâtissent des logiciels sur des adresses et des particularités, ces détails deviennent difficiles à modifier, que leurs concepteurs aient ou non souhaité ce résultat.
Le véritable adversaire est la commodité face à la stabilité
La ROM du Spectrum rend les programmes simples faciles à écrire, mais chaque raccourci accroît la dépendance à des comportements propres à la machine.
Le conflit central n’oppose pas le ZX Spectrum au Commodore 64. Il oppose la commodité à la stabilité au sein même du Spectrum. L’accès direct au système réduit la taille du code et expose des capacités utiles. Il rend aussi le logiciel responsable d’hypothèses qu’un contrat firmware plus solide aurait contenues.
Prenons les attributs de texte. Les codes de caractères $10 à $17 contrôlent INK, PAPER, FLASH, BRIGHT, INVERSE, OVER, le placement du curseur et la tabulation. Un programme peut intégrer ces octets dans une chaîne, puis envoyer toute la séquence via RST $10.
C’est un mécanisme compact. Il rappelle les séquences d’échappement des terminaux, où des octets non imprimables modifient l’interprétation du texte qui suit. Il permet aux messages d’emporter leur formatage sans appels de dessin distincts.
L’aspect surprenant est la persistance. Ces contrôles en code machine ne se réinitialisent pas après l’équivalent d’une instruction BASIC PRINT. Un retour chariot ne restaure pas non plus l’état précédent. Un auxiliaire qui suppose que le formatage prend fin avec une chaîne peut donc modifier toutes les opérations d’impression ultérieures.
La ROM suit les attributs temporaires et permanents à travers plusieurs variables système. ATTR-T contient les réglages actuels de couleur, de luminosité et de clignotement. MASK-T détermine quels bits doivent rester inchangés. Les équivalents permanents influencent l’effacement de l’écran et établissent les valeurs par défaut.
Cette division fonctionne parce que BASIC la gère dans le cadre d’une opération de langage plus large. Le code assembleur intervient sous cette couche. Il doit reproduire le comportement de préparation et de nettoyage que BASIC fournit habituellement.
L’effacement de l’écran révèle la même tension. Appeler la routine CLS de la ROM efface l’affichage, mais Martin note qu’elle redirige aussi les sorties suivantes vers la fenêtre inférieure. Elle ne coordonne pas entièrement la bordure de l’écran avec les zones supérieure et inférieure.
Son auxiliaire clrto répare ce comportement. Il définit les attributs permanents, dérive la couleur de la bordure, efface les masques et les indicateurs de mode, invoque CLS, puis ouvre le canal deux pour l’écran supérieur. Une opération supposément élémentaire devient un petit protocole de restauration d’état.
La routine CHAN-OPEN à $1601 montre pourquoi la ROM reste utile. Ouvrir un canal est plus clair que de simplement modifier un indicateur. Pourtant, le programme a toujours besoin d’écrire directement dans des variables et d’envoyer une instruction de sortie au port $FE. L’accès au firmware et au matériel reste entremêlé.
Cette combinaison peut être productive sur une cible fixe. Elle évite de dupliquer le rastériseur de caractères et la gestion du curseur de la ROM. Le développeur obtient du texte lisible, des contrôles de couleur, un comportement de fenêtrage et des glyphes personnalisés sans écrire chaque routine de pixels.
Le coût apparaît lorsque la cible change. Martin met en avant le Timex Sinclair 2068, dont la ROM incompatible a nui à la compatibilité des logiciels Spectrum aux États-Unis. Les programmes dépendant de routines fixes ou de dispositions système ne pouvaient pas supposer un comportement équivalent.
Une interface de programmation d’applications classique sépare le comportement pris en charge de l’organisation interne. L’environnement assembleur du Spectrum n’offre qu’une version partielle de cette frontière. Ses appels de ROM sont attrayants parce qu’ils sont déjà présents, mais le contrat qui les entoure est en partie reconstruit par les développeurs.
C’est pourquoi cette explication compte davantage qu’un autre exemple Hello World. Elle rend explicite le contrat caché. Le code documente quel état doit être défini, quelles routines le modifient et quelles valeurs doivent ensuite être restaurées.
Le mode texte est en réalité un bitmap et une machine à états
Parler de mode texte est un raccourci utile, mais l’implémentation est un moteur de rendu bitmap gouverné par un état partagé et modifiable.
L’expression « mode texte » suggère généralement des cellules de caractères dédiées, stockées sous forme de codes de caractères. Le matériel récupère un glyphe pour chaque code et le dessine automatiquement. Modifier une cellule consiste à écrire une valeur de caractère et, éventuellement, une valeur de couleur.
Le Spectrum d’origine fonctionne autrement. Le logiciel invoque l’imprimante de la ROM, qui restitue les pixels des glyphes dans le même bitmap que celui utilisé pour les graphismes. Une zone d’attributs distincte fournit la couleur à la résolution des cellules de caractères. La grille visuelle existe par convention de programmation, et non comme tampon matériel de texte complet.
Cette distinction explique plusieurs mécanismes de la visite de Martin. La ROM peut imprimer des caractères normaux, des graphismes en blocs et des glyphes définis par l’utilisateur via un même chemin, car tous deviennent des motifs de 8 par 8 pixels. L’imprimante n’a pas besoin de savoir si un glyphe représente une lettre ou une partie de parapluie.
La partie haute du jeu de caractères du Spectrum prend en charge cette approche. Les codes $80 à $8F représentent 16 combinaisons de blocs. Les codes commençant par $90 désignent des graphismes définis par l’utilisateur. Les codes suivants encodent des mots-clés BASIC, ce qui permet à l’interpréteur de stocker les commandes de manière compacte.
Les graphismes personnalisés reposent sur une indirection. La variable système UDG pointe vers les bitmaps définis par l’utilisateur actuellement actifs. Chaque caractère occupe huit octets, un octet par ligne. L’exemple de Martin copie 32 octets dans cette zone afin de définir quatre morceaux adjacents du parapluie.
Cette indirection constitue une abstraction modeste mais importante. La routine de dessin n’exige pas une unique adresse graphique codée en dur. Un programme peut découvrir la zone active grâce au pointeur, puis remplacer les formes. Ce comportement ressemble à un atlas de polices configurable, à une échelle bien moindre.
La couleur reste basée sur les cellules. Un octet d’attribut attribue une couleur d’encre, une couleur de papier, un bit de luminosité et un bit de clignotement. Les pixels individuels déterminent si la cellule affiche l’encre ou le papier, mais ne peuvent pas choisir des couleurs indépendantes.
Cette conception économe en mémoire transforme les contrôles de mise en forme en opérations portant à la fois sur l’état et la mémoire écran. Lorsque la ROM imprime un caractère, elle consulte ATTR-T et MASK-T, écrit les pixels et met à jour la cellule d’attribut. Les options d’encre ou de papier transparents fonctionnent en masquant des champs sélectionnés au lieu de remplacer l’octet entier.
Le désassemblage de la ROM reste précieux, car il expose les chemins à l’origine de ces effets. Ce matériau permet de vérifier ce que modifie réellement un point d’entrée, en particulier lorsqu’un programme dépend d’un comportement qui dépasse la description superficielle d’un manuel.
Cet état partagé crée aussi des défaillances subtiles. La première boucle d’impression de Martin utilise zéro comme terminateur de chaîne. Cela fonctionne jusqu’à ce que zéro devienne une donnée significative. La bannière finale doit imprimer des arguments de contrôle dont la valeur est zéro, notamment des réglages pour le papier et la luminosité.
La boucle révisée utilise donc $FF comme sentinelle. Cet octet représente le mot-clé BASIC COPY, que la bannière n’imprimera pas. Le changement est minime, mais il illustre un problème général de protocole : un terminateur dans la bande échoue dès que le format de données s’étend pour inclure cette valeur.
Le même problème apparaît dans les protocoles réseau, les formats de fichiers, les flux de commandes et les bibliothèques de sérialisation. Un octet n’est sûr comme délimiteur que tant que la charge utile l’exclut. Une fois que les données de contrôle et les données d’affichage partagent un même flux, le cadrage mérite une conception explicite.
C’est la leçon moderne la plus forte de l’article. Les limites de la machine sont anciennes, mais ses modes de défaillance sont actuels. Les états partagés, les effets de bord non documentés, les valeurs d’octets surchargées et les hypothèses étroites de compatibilité continuent de façonner les systèmes logiciels.
L’entrée complète le compromis du firmware
La gestion du clavier et du joystick suit le même schéma que la sortie texte : utiliser le firmware lorsque sa logique convient, puis le contourner lorsque le contrôle direct devient essentiel.
La visite de Martin passe de la sortie d’affichage à l’entrée clavier, car un système de texte utilisable nécessite une interaction. Le Spectrum propose là encore deux voies. Les programmes peuvent consommer l’état du clavier préparé par la ROM ou lire directement les ports matériels.
La voie du firmware repose sur l’interruption de trame de la machine. Une interruption est un transfert déclenché par le matériel vers une routine de service. À chaque trame vidéo, le gestionnaire du Spectrum met à jour son minuteur FRAMES et analyse la matrice du clavier.
Lorsqu’il trouve une touche, le gestionnaire décode cette entrée et stocke un caractère dans LAST-K. Il active également le bit cinq de la variable système FLAGS. La routine getkey de Martin attend avec l’instruction Z80 HALT, teste le drapeau, récupère le caractère, efface le drapeau et retourne.
L’utilisation de HALT est importante. La boucle n’a rien d’utile à faire tant que le gestionnaire d’interruptions n’effectue pas une nouvelle analyse du clavier. Attendre cette interruption évite de relire continuellement un drapeau inchangé à pleine vitesse du processeur.
Cette voie offre une interprétation plutôt qu’un état électrique brut. La ROM comprend les combinaisons de touches et les convertit en caractères. Un programme peut accepter du texte sans reproduire le décodeur clavier.
L’entrée directe échange cette commodité contre l’immédiateté. Le clavier du Spectrum est organisé en matrice, accessible par des ports d’E/S. Sélectionner une ligne et vérifier les bits renvoyés révèle quelles touches sont actuellement maintenues.
Le Z80 introduit une particularité historique. Certaines instructions d’entrée et de sortie semblent exposer une adresse de port sur huit bits, mais IN A,(C) et OUT (C),A placent le registre BC complet sur le bus d’adresses. Sinclair a exploité ce comportement lors de la conception de son interface matérielle.
L’exemple de Martin vérifie la touche A via la valeur de port $FDFE. Ce code n’attend pas que la ROM traduise un appui. Il interroge le matériel sur une position de la matrice, ce qui le rend utile pour les jeux qui nécessitent un état directionnel continu.
Le joystick Kempston est plus simple. Il utilise le port $1F, où les bits représentent les directions et le bouton de tir. Le lecteur de Martin traduit ce champ de bits en décalages horizontaux et verticaux, ainsi qu’en valeur de tir.
Ces options montrent pourquoi les programmeurs contournent les abstractions même lorsqu’elles existent. L’entrée clavier du firmware convient bien à la saisie de texte ou à l’attente d’une commande. Les lectures directes de ports sont plus adaptées aux mouvements simultanés, à une faible latence et aux vérifications répétées d’état.
Le coût est la portabilité. Une routine liée à la matrice clavier du Spectrum suppose l’organisation électrique de la machine. Un lecteur Kempston suppose cette interface particulière. Les émulateurs doivent reproduire ces comportements, tandis que du matériel alternatif ou d’autres normes de joystick exigent un code différent.
Martin relève également des incohérences impliquant des touches Maj synthétiques dans les émulateurs. C’est un angle sceptique utile. Une routine techniquement exacte peut tout de même se comporter différemment lorsque l’implémentation environnante interprète autrement les entrées de l’hôte.
L’apparition sur Hacker News ne doit pas être confondue avec une validation étendue de chaque émulateur ou variante du Spectrum. La soumission liée a suscité une réponse modeste et aucune discussion enregistrée dans l’instantané fourni. La valeur technique vient du chemin de code reproductible, et non d’un consensus populaire.
Les lecteurs devraient donc distinguer trois niveaux d’affirmation. La documentation Sinclair d’origine établit les capacités prévues de la machine. L’analyse de la ROM révèle le comportement de l’implémentation. Les exemples de Martin démontrent une voie de développement fonctionnelle, mais ne garantissent pas des résultats identiques sur chaque clone, révision de ROM, interface ou émulateur.
Ce que les développeurs devraient surveiller après le pic Hacker News
Le prochain test consiste à déterminer si cette visite devient une infrastructure technique durable plutôt qu’un lien éphémère.
Le premier signal est la poursuite de la visite du système. Martin termine sur une lacune claire : les exemples peuvent reproduire l’essentiel de l’affichage, des entrées et de l’animation nécessaires à une version en langage machine du jeu précédent, mais le son et son écran-titre restent non résolus. Une suite consacrée aux graphismes ou à l’audio montrerait si la même méthode s’étend au-delà de la sortie orientée caractères.
Le deuxième signal est la reproductibilité entre les cibles. Les développeurs devraient tester les exemples sur du matériel 48K d’origine, des modèles Spectrum ultérieurs, des émulateurs courants et des variantes de ROM. Des sorties identiques renforceraient l’idée de traiter ces routines comme une couche de compatibilité pratique. Des divergences indiqueraient à quel moment l’accès direct à l’état l’emporte sur la stabilité des appels ROM.
Le troisième signal est de savoir si le code devient plus facile à inspecter et à réutiliser. Un exemple téléchargeable, un processus de compilation documenté, une image de test fixe ou une automatisation d’émulateur transformeraient l’article en référence exécutable. Martin nomme déjà l’assembleur, l’outil de création de bandes et l’émulateur FUSE utilisés pour le flux Hello World initial. Préserver ces dépendances importe autant que préserver la liste assembleur.
Il existe aussi une question plus large de documentation. Les plateformes rétro disposent souvent d’informations abondantes, mais d’une autorité fragmentée. Les manuels décrivent le comportement prévu, les désassemblages exposent les mécanismes internes, les références communautaires corrigent les erreurs et les tutoriels modernes relient les éléments. Un guide de plateforme utile peut réduire cette fragmentation s’il distingue clairement les contrats documentés des particularités observées.
Les développeurs qui suivent cette histoire devraient éviter de transformer un exemple élégant en règle universelle. Les appels ROM directs peuvent économiser de la mémoire et des efforts de développement. L’accès direct au matériel peut améliorer la réactivité. Aucun des deux ne garantit la compatibilité en dehors de l’environnement exact dans lequel il a été testé.
Cette incertitude fait partie de la valeur du sujet. Le ZX Spectrum permet de remonter les défaillances à travers une pile complète, d’un octet de chaîne à une routine ROM, une variable système, une adresse mémoire et une cellule d’affichage. Peu de systèmes modernes permettent ce niveau d’inspection.
L’action suivante la plus utile est simple : reproduire la bannière, modifier une hypothèse et observer ce qui casse. Déplacez le code, modifiez le terminateur, laissez un attribut actif, sélectionnez le mauvais canal ou testez une autre ROM. Le moment Hacker News passera, mais ces expériences préservent la véritable leçon : une interface est définie autant par son état et ses effets de bord que par le point d’entrée appelé par un programmeur.


