Post

Un Minitel comme terminal série ?

Un Minitel comme terminal série ?

Génèse

Il y a de cela maintenant deux ans, je me suis intéressé aux microprocesseurs avec l’envie de fabriquer un micro-ordinateur de zéro. Toutes ces recherches m’ont mené à en faire un basé sur le MOS6502, un processeur de 1975 qui a notamment été utilisé dans l’Apple I et II, la NES, le Commodore 64 et PET, etc.

J’étais très fier du résultat, sur lequel j’ai pu adapter Wozmon, le logiciel développé par Steve Wozniak, cofondateur d’Apple, pour l’Apple I, et msbasic (Microsoft BASIC pour 6502). Cependant, une chose ne me plaisait pas forcément : le terminal. Toutes les communications avec l’ordinateur passent par un port série, que j’interface sur un ordinateur avec un adaptateur TTL to USB, ce qui fonctionne bien, permet de facilement écrire et copier-coller du texte, mais qui force une dépendance avec un ordinateur et qui ne reflète pas (et dénature un peu) l’usage des années 1980.

Malheureusement, par manque de motivation, d’idées (et d’argent, je voulais utiliser un terminal “mythique” d’une ère, et un VT100 ne rentrait pas — et ne rentre toujours pas — dans mon budget), j’ai laissé tomber ce projet… avant de regagner en motivation il y a quelques jours, où j’ai eu l’idée d’utiliser un Médium interactif par numérisation d'information téléphonique, plus connu sous son acronyme Minitel.

Le Minitel est un appareil emblématique des années 1980-1990 en France, distribué gratuitement (ou loué pour certains modèles) par les PTT (Postes, télégraphes et téléphones), ancêtre de France Télécom (devenu Orange) et La Poste, aux foyers pour mettre en avant le réseau francais Videotex nommé Télétel, précurseur d’Internet, qui permettait d’accéder à des services numériques via la ligne téléphonique.

Mais ce n’est pas ce réseau qui nous interesse, nous ; c’est une prise que certains modèles (1B et 2, potentiellement d’autres) possèdent à l’arrière : la prise Péri-informatique. C’est une prise qui permet d’interagir avec des périphériques via le protocole série standard. Exactement ce qu’il me faut ; en plus d’avoir une histoire importante en France, c’est juste parfait.

Après quelques recherches sur différents sites d’occasion, je suis l’heureux propriétaire d’un Minitel 2 fabriqué par Philips dans les années 1990 (quinze ans après le 6502 certes, mais j’aime le blanc, et c’est le seul modèle de cette couleur <3).

La prise Péri-Informatique

Bien qu’il s’agisse d’un port série, il n’utilise ni les niveaux TLL (0V-5V) ni les niveaux RS232 (-12V-12V) qui sont les deux supportés par mon circuit. Au lieu de cà, la broche de transmission est à collecteur ouvert et doit donc être “poussée” à 5V, la broche de réception est maintenue à une tension non nulle, dont la documentation n’indique pas la tension exacte : un véritable enfer. Heureusement, avec deux transistors et des résistances, il est facile d’adapter ce système au standard TLL (le schéma est laissé comme exercice au lecteur, par manque d’envie de le faire moi-même <3).

Il reste maintenant à déterminer les paramètres du port série (vitesse et codage d’un multiplet).

En connectant le Minitel à un oscilloscope, on peut déterminer la vitesse en mesurant la durée d’une impulsion transmise.

Image oscilloscope vitesse

On mesure ici 1190 kHz, la valeur standard la plus proche est 1200 bauds , on peut maintenant décoder les bits avec un analyseur logique, ou directement avec mon oscilloscope, puisqu’il le permet.

On prendra la lettre J comme exemple.

Image oscilloscope multiplet

Les bits sont transmis en commençant par celui de poids faible ; on lit ici 0b11001010 soit 202 ou en hexadécimal 0xCA.

Ce qui ne correspond pas à la lettre J en ASCII, pour sur, les caractères imprimables étant inférieurs à 128.

Si on le compare avec la représentation binaire du J (qui est 0b01001010), on remarque que seul le bit de poids fort change. En transmettant la lettre S (en binaire 0b01010011), le bit ne change pas.

La différence entre J et S est le nombre de bits à 1 dans sa représentation : S en contient un nombre pair, J un nombre impair ; c’est un bit de parité (pair de plus).

La configuration finale est 7E1 (7 bits de données, un bit de parité Even, et 1 bit de fin) à une vitesse de 1200 bauds.

La partie logicielle

Dans le reste de l’article, toutes les modifications seront faites sur mon fork de msbasic dans lequel je fais l’intégralité de mes tests. Tout le code qui suit est un diff du projet ; un lien vers le fichier est fourni à chaque fois.

Il ne reste plus qu’à (ou presque) configurer le port série. Pour des raisons de stocks et d’économie, acheter l’interface série 6551 originale n’est pas possible, mais Western Design Center produit toujours un remplacement (presque) compatible avec l’original : le W65C51S et le W65C51N.

On peut configurer sa vitesse et la taille du multiplet via le registre de commande.

Image documentation vitesse 65c51

On doit donc le configurer en écrivant 0b000111000 ou 0x38 dans le registre.

Pour la parité, il faut configurer le registre de contrôle, dont la description est la suivante :

Image documentation parité 65c51

Dans lequel on doit écrire… dans lequel on ne peut pas écrire notre parité ?!?!?

La version N que je possède n’a pas de support pour la parité, contrairement à la version S. Ce n’est pas grave, avec un peu de magie côté logiciel, on va pouvoir tricher.

Dans une communication série, le bit de parité est envoyé comme un bit classique ; au lieu de configurer la taille du multiplet à 7, on peut le configurer à 8 et encoder la parité côté logiciel.

Réception

Coté réception, “gérer” la parité est simple : une fois l’octet reçu, on va simplement ignorer la parité, en espérant qu’aucune erreur ne s’est produite durant la transmission.

bios.s L97

1
2
3
4
5
6
7
8
9
10
11
@@ -89,11 +100,25 @@ IRQ_INT:
     AND #$08
     BEQ @not_recv
     LDA ACIA_DATA
+.ifdef CONFIG_7E1
+    AND #$7F
+.endif
     JSR WRITE_BUF
 @not_recv:
     PLA
     RTI

Émission

Pour la partie émission, c’est un peu plus compliqué : il faut compter le nombre de bits et ajuster celui de poids fort en fonction. Faire cette opération est bien trop lent, on va donc choisir l’option de la simplicité : une LookUp Table : un tableau de 128 valeurs contentant, à l’index i, la représentation de i avec son bit de parité.

bios.s L111

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@@ -94,6 +105,17 @@ IRQ_INT:
     PLA
     RTI
 
+.ifdef CONFIG_7E1
+LUT_7E1:
+.byte $00,$81,$82,$03,$84,$05,$06,$87,$88,$09,$0A,$8B,$0C,$8D,$8E,$0F
+.byte $90,$11,$12,$93,$14,$95,$96,$17,$18,$99,$9A,$1B,$9C,$1D,$1E,$9F
+.byte $A0,$21,$22,$A3,$24,$A5,$A6,$27,$28,$A9,$AA,$2B,$AC,$2D,$2E,$AF
+.byte $30,$B1,$B2,$33,$B4,$35,$36,$B7,$B8,$39,$3A,$BB,$3C,$BD,$BE,$3F
+.byte $C0,$41,$42,$C3,$44,$C5,$C6,$47,$48,$C9,$CA,$4B,$CC,$4D,$4E,$CF
+.byte $50,$D1,$D2,$53,$D4,$55,$56,$D7,$D8,$59,$5A,$DB,$5C,$DD,$DE,$5F
+.byte $60,$E1,$E2,$63,$E4,$65,$66,$E7,$E8,$69,$6A,$EB,$6C,$ED,$EE,$6F
+.byte $F0,$71,$72,$F3,$74,$F5,$F6,$77,$78,$F9,$FA,$7B,$FC,$7D,$7E,$FF
+.endif
 
 .include "wozmon.s"

Au moment de la transmission, on retire le bit de poids fort (on ne peut pas le transmettre), puis on cherche dans le tableau la valeur à son index.

bios.s L53

1
2
3
4
5
6
7
8
9
10
11
@@ -49,11 +49,22 @@ CHRIN:
 ; Modifies: 
 CHROUT:
     PHA
+    PHY
+.ifdef CONFIG_7E1
+    AND #$7F
+    TAY
+    LDA LUT_7E1, Y
+.endif
     STA     ACIA_DATA

Une dernière chose à faire : corriger un bug matériel dans la puce. Le registre de status est censé contenir un bit indiquant la fin de la transmission, qu’on doit attendre avant de retransmettre un nouvel octet, mais ce bit n’est jamais mis à jour ; il faut donc attendre manuellement.

bios.s L59

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@@ -56,6 +56,14 @@ CHROUT:
     LDA LUT_7E1, Y
 .endif
     STA     ACIA_DATA
+    LDY     #$0D
+@txdelay:
+    LDA     #$80
+@txdelay_:
+    DEC
+    BNE     @txdelay_
+    DEY
+    BNE     @txdelay
     PLY
     PLA
     RTS

Cycles par instruction

  • DEC : 2 cycles
  • LDA/LDY : 2 cycles
  • BNE : 2 cycles + 1 cycle si le saut est effectué

La boucle @txdelay_ prend donc 2 + (0x7F*(2+3)) + (2+2) soit 641 cycles.

La boucle @txdelay prend donc 2 + 0x0C*(641+2+3) + (641+2+2) soit 8399 cycles.

Mon circuit est cadencé à 1MHz, les boucles prennent donc 8399µs, soit une fréquence de 119 fois par seconde, juste en dessous des 120 maximum (1200 baud, avec 1start+8bit+1stop baud par octet).

Il ne reste plus qu’à tester en lançant msbasic, et tout marche parfaitement.

Image minitel msbasic

This post is licensed under CC BY 4.0 by the author.