L'ordinateur personnel Tandy 1000 EX comporte deux sorties vidéo:
NTSC composite (Sortie télé) et
compatible CGA (Color Graphics Adapter). Pour être exact,
le circuit vidéo du Tandy 1000 est de type TGA, mais électriquement, c'est la même chose que CGA.
Voici quelques exemples d'images obtenues via la sortie NTSC:
La qualité d'image NTSC est bien inférieure à celle qu'il est possible d'obtenir en CGA. Malheureusement,
je n'ai pas d'écran CGA. Par contre, des écrans VGA ce n'est pas ce qui manque! Je me suis donc intéressé
à la possibilité de faire une conversion CGA à VGA.
De CGA à VGA
CGA:
Rafraîchissement vertical: 60Hz (Fixe)
Rafraîchissement horizontal: 15.75 kHz (Fixe)
Signal vidéo digital à 4-bit (Rouge, Vert, Bleu et intensité)
Signal vidéo analogique à 3 canaux (Rouge, Vert, Bleu)
Connecteur HD15
Un moniteur VGA standard n'accepte pas un signal dont la fréquence de rafraîchissement horizontale
est aussi basse que 15 kHz. On ne peut donc pas s'en tirer simplement en faisant passer la synchro
(hsync et vsync) directement et en utilisant un circuit simple de résistances pour générer les voltages
RVB pour le VGA à partir des signaux digitaux CGA.
Par contre, on remarque que la fréquence de rafraîchissement horizontal en VGA est environ le double qu'en CGA.
Il y a donc deux fois plus de lignes dans une trame VGA. En répétant chaque ligne CGA deux fois (à la fréquence
VGA de 31 kHz), nous faisons une pierre deux coups. Nous évitons d'obtenir une image compressée verticalement
n'occupant que la moitié de l'écran, mais en plus nous doublons la fréquence de rafraîchissement de sorte
qu'un écran VGA accepte alors le signal.
Ce principe est connu sous le nom de « scan doubler ».
Le montage
La réalisation d'un scan doubler pour passer de CGA à VGA était l'excuse parfaite pour m'acheter un kit
de développement FPGA (Kit Altera DE1 de TerasIC) pour m'exercer à résoudre des problèmes avec une approche
nouvelle pour moi.
Le kit comporte deux connecteurs à 40 broches permettant d'utilser les entrées/sorties logiques du FPGA. Il
s'agit toutefois de logique fonctionnant à 3.3 volt alors que le système CGA fonctionne à 5 volt. Afin
de ne pas endommager le FPGA, j'ai eu recours à des résistances pour fabriquer un réducteur de tension simple,
comme ceci:
Les résistances utilisées doivent tous être de valeur égale. J'ai utilisé des résistance de 220Ω, mais
c'est peut-être un peu trop bas car l'impédence d'entrée résultante est de 660Ω alors qu'avec un vrai
écran CGA, ce serait plutôt 4.7kΩ... Si c'était à refaire, j'utiliserait des résistances de 1.5kΩ.
Voici quelques photos du montage:
Le kit DE1
Le kit et le diviseur de tension
Zoom sur le diviseur de tension
Développement
Voici quelques images que j'ai obtenues au début alors que je m'amusait à
modifier un exemple de générateur d'image VGA pour affichier des donnés
provenant de l'entrée CGA stockées dans un petit bloc de mémoire. C'était une
impasse et j'ai du tout refaire plus tard, mais cela m'a permit de vérifier
que le montage fonctionnait (i.e. Les signaux passaient).
Aucune synchronsation
Syncho. horizontale seulement
Le kit de développement DE1 est doté de mémoires externes que j'aurais pu utiliser, mais
je voulais essayer de tout faire à même le FPGA. Malheureusement, il n'était pas possible
de créer une instance d'un bloc de mémoire assez volumineux pour contenir une
une trame CGA complète en couleur alors j'ai dû prendre une approche traitant les lignes
individuelles plutôt qu'une image complète.
Il y donc deux blocs de mémoire utilisés en alternance. Pendant qu'une ligne
CGA est stockée dans l'un, l'autre est rejouée au double de la vitesse vers
la sortie VGA. Chaque échantillon stocké en mémoire contient les 4 bits de
couleurs CGA ainsi que les signaux HSYNC et VSYNC. Ainsi je n'ai pas à me
soucier de générer les signaux de synchronisation, mais en revanche ils
ne sont problement pas très standard.
Beaucoup de « jitter »
J'ai eu quelques difficultés. D'abord, comme je n'ai pas accès au signal d'horloge du
générateur vidéo (le «pixel clock») je dois échantilloner à une fréquence très
élevée pour détecteur les fronts «hsync». Par contre, je ne peux pas utiliser la
même fréquence pour le stockage des pixels car à cette vitesse, la taille du
bloc de mémoire requis devient trop grande. Je dois donc pour le stockage employer
une horloge de fréquence plus basse obtenue en divisiant l'horloge principale
pour rester en phase. Je resynchronise toutefois cette horloge dérivée en remettant
les compteurs de division à zéro après chaque «hsync», histoire de garder constant
le temps d'échantillonage des pixels par rapport au front «hsync». Sans cela,
il y a toutes sortes d'effets indésirables (les lignes qui dancent, des caractères
difformes, etc) causés par des battements entre l'horloge du générateur vidéo et celle
d'échantillonnage.
La tentation était très forte de modifier mon Tandy 1000 pour pouvoir en extraire l'horloge
utilisée pour générer l'image (via un connecteur BNC que j'ajouterais à l'arrière) mais
j'ai résisté et finalement obtenu une image stable!
L'image est stable seulement sur un tube cathodique. Si l'on regarde de très très près,
on apperçoit encore un peu de «jitter» mais c'est du domaine de ce qui est acceptable.
Problèmes sur LCD
J'ai eu quelques problèmes à faire accepter le signal VGA par mes écrans LCD. À présent
ça fonctionne mais les imperfections du mon signal vidéo semblent se faire amplifier
par l'écran. Cette fois il semble s'agit de battements entre la fréquence d'échantillonage
du LCD (lui aussi doit travailler très fort sans avoir accès à l'horloge du générateur)
et mon horloge. Comme mon signal n'est pas parfaitement standard, cela ne donne pas
de très bons résultats. Je crois à ce stade que je devrais refaire le projet en
stockant une image complète en mémoire externe, que je pourrai ensuite reproduire
vers l'écran VGA en respectant le standard. L'image à droite montre le problème: Certains
pixels ne s'alignent pas verticalement.
Dans l'ensemble, je suis très satisfait du résultat car j'utilise un écran à tube cathodique. Mais
je vais sans doute revisiter le projet puisque je vois déjà plusieurs choses à améliorer (refaire).
Petits «glitchs» de couleur
L'écran LCD qui refuse le signal...
Les couleurs
Un dernier détail était celui des couleurs. Pour avoir des couleurs le plus rapidement possible,
j'ai d'abord codé la conversion de couleur CGA RGBI vers VGA RGB de manière naïve, c'est à
dire ainsi:
assign oR = {iIBGR[3],{7{iIBGR[0]}}};
assign oG = {iIBGR[3],{7{iIBGR[1]}}};
assign oB = {iIBGR[3],{7{iIBGR[2]}}};
(Exemple: Pour le rouge, si le bit d'intensité I est à 1, la sortie rouge analogique sera à 0xff ou 0x7f selon le bit du rouge côté CGA, si le bit d'intensité est à 0, la sortie sera alors 0x00 ou 0x7f. Même chose pour le vert et le bleu).
Wikipedia m'a apprit que cette
approche était erronée. D'abord il y a une exception pour le jaune. Lorsque les bits rouge et vert sont à 1 mais que le bit d'intensité est
0, ce n'est pas du «jaune foncé» qu'on doit obtenir, mais du brun. De plus, la valeurs analogique doivent être 0x00 ou 0xAA (Intensité basse) et 0x55 et 0xFF (Intensité haute), sinon la différence entre gris foncé et gris pâle (couleurs 7 et 8) est beaucoup trop petite.
J'ai réécrit le code pour obtenir les bonnes couleurs:
always @(iIBGR)
begin
if (iIBGR[3]) // High intensity/ light colors
begin
oR = iIBGR[0] ? 'hff : 'h55;
oG = iIBGR[1] ? 'hff : 'h55;
oB = iIBGR[2] ? 'hff : 'h55;
end
else
begin
if (iIBGR[2:0] == 3'b011)
begin
oR = 'haa; oG = 'h55; oB = 'h00; // Brown is an exception (darker green)
end
else // Regular colors
begin
oR = iIBGR[0] ? 'haa : 'h00;
oG = iIBGR[1] ? 'haa : 'h00;
oB = iIBGR[2] ? 'haa : 'h00;
end
end
end
Voici une comparaison des couleurs obtenues:
Couleurs erronées
Couleurs correctes
Pour comparaison: L'horreur NTSC
Je me suis servi de l'interpréteur de basic inclut dans la disquette de démarrage
Tandy pour générer les tests de couleurs ci-dessus. Ah, que de nostalgie! Voici le listing:
10 CLS
20 FOR I = 0 TO 15
22 FOR J = 0 TO 7
30 COLOR I,J
40 PRINT " ** ";
45 NEXT J
46 COLOR I,0
47 GOSUB 100
48 PRINT
50 NEXT I
60 COLOR 15,0
99 END
100 FOR T = 65 TO 90
101 PRINT CHR$(T);
102 NEXT T
103 RETURN
Effet « scanlines »
Omettre d'afficher les lignes paires du côté VGA donne un bel effet « scanlines ».
Effect «scanlines»
L'effet est contrôlé via l'interrupteur SW0 de la carte de développement DE1:
Il n'y a qu'à comparer les images ci-dessous avec celles du début de cet article pour conclure qu'il est
absolument indiscutable que la qualité d'image résultante est stupéfiante en comparaison.
En fait, la qualité d'image est identique à celle obtenu lorsque ce jeu tourne sur une PC avec
une carte VGA. Je trouve que c'est en fait un peu trop pur alors j'utilise l'effet « scanlines ».
MS-DOS 2.11. Vieux.
Monuments of mars
Monuments of mars
Monuments of mars
Code source
Avertissement:
Le code source est mis à votre disposition tel-quel et je n'offre absolument aucune garantie de quelque
sorte que ce soit. L'ensemble est très certainement de mauvaise qualité et sans doute truffé d'erreurs de débutant
et de mauvaises pratiques de programmation verilog. Mon excuse: Il s'agit de mon tout premier projet FPGA en verilog.
Version v1.0 22 novembre 2015 (Dimanche)
Version initale (Approche «scan doubler» ligne par ligne) sans utilisation de mémoire externe.
Je ne saurais être tenu responsable pour les dommages
que l'utilisation des informations ou la mise en œuvre des instructions présentées
sur cette page pourrait causer à votre équipement,
à vous-même ou à autrui. Aussi, je ne donne aucune garantie quant
à l'exactitude des informations et à leur fonctionnement.