Cette partie peut être optionnelle, car elle nécessite une intervention à l’intérieur de l’ordinateur. En effet les signaux nécessaires à la mémorisation de la vidéo ne sont pas disponibles sur le port d’extension. En l’absence de signaux la carte il faudra fixer CTLA_VT à ‘0’ et se contenter d’un affichage vidéo à 16 couleurs et vous pouvez passer au chapitre suivant.

Si vous êtes toujours là, sachez qu’il n’est pas possible de relire ou d’intercepter les échanges avec la mémoire vidéo car l’image est créée à l’aide d’un générateur de caractères alphanumériques ou et d’un générateur de caractères pseudo graphiques câblé. Voici donc les signaux qu’il sera nécessaire de sortir sur une connectique supplémentaire :

TRS_SHIFT : Horloge graphique du TRS-80 (10,6445MHz)

TRS_GRAPHIC : Signal logique qui permet de générer la vidéo

TRS_HDRV : Signal de lecture horizontale

TRS_VDRV : Signal de lecture verticale

TRS_VID : Signal permettant de détecter un conflit en l’adressage de la mémoire vidéo du TRS-80 par le processeur simultanément avec les circuits de génération vidéo.

Ce dernier signal est à l’origine des bandes noires qui apparaissent à l’écran lorsque l’ordinateur travaille sur l’affichage et que l’on va essayer de supprimer.

Voici où l’on pourra prélever ces signaux :

Nous allons câbler du fil en nappe 10 conducteurs (5 signaux + 5 GND) comme suit :

 

 

 

 

 

 

 

 

Coté gestion des adresses, nous utilisons des compteurs comme pour la lecture de la mémoire mais cette fois cadencés à chaque pixel du TRS-80 (TRS_SHIFT) sachant que le pixel change sur un front montant, les adresses changerons donc sur front montant afin d’être stable lors de l’écriture d’un octet.

CPLD ADRESSES

TCY0.t = 'b'1 ;                                                                          /* COMPTEUR SYNCHRONE UTILISE POUR CHAQUE PIXEL */

TCY1.t = TCY0 ;

TCY2.t = TCY1 & TCY0 ;

TCY0.ck = TRS_SHIFT ;

TCY1.ck = TRS_SHIFT ;

TCY2.ck = TRS_SHIFT ;

TCY0.ar = TRS_HDRV ;                                                               /* RESYNCHRONISE A CHAQUE DEBUT DE TRAME HORIZONTALE */

TCY1.ar = TRS_HDRV ;

TCY2.ar = TRS_HDRV ;

 

TCL0.t = ‘b’1 ;                                                                          /* COMPTEUR DES COLONNES*/

TCL1.t = TCL0 ;

TCL2.t = TCL1 & TCL0 ;

TCL3.t = TCL2 & TCL1 & TCL0 ;

TCL4.t = TCL3 & TCL2 & TCL1 & TCL0 ;

TCL5.t = TCL4 & TCL3 & TCL2 & TCL1 & TCL0 ;

TCL0.ck = !TRS_SHIFT ;

TCL1.ck = !TRS_SHIFT ;

TCL2.ck = !TRS_SHIFT ;

TCL3.ck = !TRS_SHIFT ;

TCL4.ck = !TRS_SHIFT ;

TCL5.ck = !TRS_SHIFT ;

TCL0.ar = TRS_HDRV ;                                                               /* RESYNCHRONISE A CHAQUE DEBUT DE TRAME HORIZONTALE */

TCL1.ar = TRS_HDRV ;

TCL2.ar = TRS_HDRV ;

TCL3.ar = TRS_HDRV ;

TCL4.ar = TRS_HDRV ;

TCL5.ar = TRS_HDRV ;

 

TLG0.t = 'b'1 ;                                                                          /* COMPTEUR DES LIGNES */

TLG1.t = TLG0 ;

TLG2.t = TLG1 & TLG0 ;

TLG3.t = TLG2 & TLG1 & TLG0 ;

TLG4.t = TLG3 & TLG2 & TLG1 & TLG0 ;

TLG5.t = TLG4 & TLG3 & TLG2 & TLG1 & TLG0 ;

TLG6.t = TLG5 & TLG4 & TLG3 & TLG2 & TLG1 & TLG0 ;

TLG7.t = TLG6 & TLG5 & TLG4 & TLG3 & TLG2 & TLG1 & TLG0 ;

TLG0.ck = !TRS_HDRV ;                                                             /* INCREMENTE A CHAQUE TRAME HORIZONTALE */

TLG1.ck = !TRS_HDRV ;

TLG2.ck = !TRS_HDRV ;

TLG3.ck = !TRS_HDRV ;

TLG4.ck = !TRS_HDRV ;

TLG5.ck = !TRS_HDRV ;

TLG6.ck = !TRS_HDRV ;

TLG7.ck = !TRS_HDRV ;

TLG0.ar = TRS_VDRV ;                                                               /* RESYNCHRONISE A CHAQUE DEBUT DE TRAME VERTICALE */

TLG1.ar = TRS_VDRV ;

TLG2.ar = TRS_VDRV ;

TLG3.ar = TRS_VDRV ;

TLG4.ar = TRS_VDRV ;

TLG5.ar = TRS_VDRV ;

TLG6.ar = TRS_VDRV ;

TLG7.ar = TRS_VDRV ;

 

Le compteur de colonnes est large de six bits pour couvrir les 48 colonnes, il est remis à zéro à chaque nouvelle ligne. Le compteur de lignes sur huit bits afin de couvrir les 192 lignes il est incrémenté à chaque nouvelle ligne et mis à zéro à chaque nouvelle synchronisation verticale.

Du coté des données, il faudra échantillonnerons les pixels sur un front descendant de TRS_SHIFT afin qu’ils soient stables lorsqu’ils sont lus. Chaque pixel est inséré dans un registre à décalage qui contiendra un octet à écrire en mémoire au bout d’un cycle de huit pixels.

CPLD DONNEES

NODE [VT7..0] ;

 

VT0.d = !TRS_GRAPHIC ;                                           /* LECTURE D’UN PIXEL */

VT1.d = VT0;

VT2.d = VT1;

VT3.d = VT2;

VT4.d = VT3;

VT5.d = VT4;

VT6.d = VT5;

VT7.d = VT6;

VT0.ck = !TRS_SHIFT ;                                                /* DECALAGE DANS LE REGISTRES D’UN BIT A CHAQUE COUP D’HORLOGE */

VT1.ck = !TRS_SHIFT ;

VT2.ck = !TRS_SHIFT ;

VT3.ck = !TRS_SHIFT ;

VT4.ck = !TRS_SHIFT ;

VT5.ck = !TRS_SHIFT ;

VT6.ck = !TRS_SHIFT ;

VT7.ck = !TRS_SHIFT ;/

 

La vidéo est numérisée à la cadence de l’horloge du TRS-80 soit 10,6445Mhz, elle doit maintenant être écrite dans la mémoire à la cadence de l’horloge de la carte soit 15MHz. Nous allons utiliser un registre tampon afin de mémoriser l’octet obtenu.

CPLD DONNEES

NODE [VS7..0] ;

 

VS0.d = VT0 ;                                                            /* ECRITURE DES 8 PIXELS MEMORISES DANS UN REGSITRE TAMPON */

VS1.d = VT1 ;

VS2.d = VT2 ;

VS3.d = VT3 ;

VS4.d = VT4 ;

VS5.d = VT5 ;

VS6.d = VT6 ;

VS7.d = VT7 ;

VS0.ck = !TCY2 & !TCY1 & !TCY0 ;                              /* ECRITURE A CHAQUE DEBUT DE CYCLE DE 8 PIXELS */

VS1.ck = !TCY2 & !TCY1 & !TCY0 ;

VS2.ck = !TCY2 & !TCY1 & !TCY0 ;

VS3.ck = !TCY2 & !TCY1 & !TCY0 ;

VS4.ck = !TCY2 & !TCY1 & !TCY0 ;

VS5.ck = !TCY2 & !TCY1 & !TCY0 ;

VS6.ck = !TCY2 & !TCY1 & !TCY0 ;

VS7.ck = !TCY2 & !TCY1 & !TCY0 ;

 

La mémorisation est effectuée en fin de cycle de manière à ce que 8 pixels soient écrits dans le registre à décalage. Il ne reste plus qu’à injecter l’écriture de cet octet dans la mémoire dans le cycle correspondant MCY4-5.

Pour la mémoire il suffit d’ajouter le cycle correspondant :

CPLD DONNEES

MEM_D0.d = TRS_D0 & MCY2 & MCY1 # VS0 & MCY2 & !MCY1 ;

MEM_D1.d = TRS_D1 & MCY2 & MCY1 # VS0 & MCY2 & !MCY1 ;

MEM_D2.d = TRS_D2 & MCY2 & MCY1 # VS0 & MCY2 & !MCY1 ;

MEM_D3.d = TRS_D3 & MCY2 & MCY1 # VS0 & MCY2 & !MCY1 ;

MEM_D4.d = TRS_D4 & MCY2 & MCY1 # VS0 & MCY2 & !MCY1 ;

MEM_D5.d = TRS_D5 & MCY2 & MCY1 # VS0 & MCY2 & !MCY1 ;

MEM_D6.d = TRS_D6 & MCY2 & MCY1 # VS0 & MCY2 & !MCY1 ;

MEM_D7.d = TRS_D7 & MCY2 & MCY1 # VS0 & MCY2 & !MCY1 ;

MEM_D0.ck = CLK ;

MEM_D1.ck = CLK ;

MEM_D2.ck = CLK ;

MEM_D3.ck = CLK ;

MEM_D4.ck = CLK ;

MEM_D5.ck = CLK ;

MEM_D6.ck = CLK ;

MEM_D7.ck = CLK ;

 

Pour les adresses, comme pour les données il faudra les synchroniser avec l’horloge 15MHz :

NODE [MT13..0] ;

 

MT0.d = TCL0 ;                                         /* SYNCHRONISATION DES ADRESSES CORRESPONDANTE A LA VIDEO ECHANTILLONEE */

MT1.d = TCL1 ;

MT2.d = TCL2 ;

MT3.d = TCL3 ;

MT4.d = TCL4 ;

MT5.d = TCL5 ;

MT6.d = TLG0 ;

MT7.d = TLG1 ;

MT8.d = TLG2 ;

MT9.d = TLG3 ;

MT10.d = TLG4 ;

MT11.d = TLG5 ;

MT12.d = TLG6 ;

MT13.d = TLG7 ;

MT0.ck = !MCY2 & !MCY1 & !MCY0 ;

MTx.ck =  !MCY2 & !MCY1 & !MCY0 ;

 

Et on les ajoute dans le bon créneau MCY4-5.

CPLD ADRESSE

MEM_A0.d = !MCY2 & MCL0 # TRS_A0 & MREQ & MCY2 & MCY1 # MCY2 & !MCY1 & MT0;

MEM_A13.d = !MCY2 & MLG8 # TRS_A13 & MREQ & MCY2 & MCY1 # MCY2 & !MCY1 & MT13;

 

MEM_A14.d = (MREQ & (TRS_A14 & CTRL_PL0 # !TRS_A14 & CTLA_M0) # GORPL & VIEW & CTRL_PL0) & MCY2 & MCY1 # !MCY2 & MCY0 # MCY2 & !MCY1;

 

MEM_A15.d = (MREQ & (TRS_A14 & CTRL_PL1 # !TRS_A14 & CTLA_M1) # GORPL & VIEW & CTRL_PL1) & MCY2 & MCY1 # !MCY2 & MCY1 # MCY2 & !MCY1;

 

MEM_A16.d = (MREQ & (TRS_A14 & CTRL_PGE # !TRS_A14 & CTLA_M2) # GORPL & VIEW & CTRL_PGE) & MCY2 & MCY1 # !MCY2 & CTRL_PGL # MCY2 & !MCY1 & CTRL_PGE;

 

L’affichage semble fonctionner… Mais non !  Un nouveau problème !


En superposant une image graphique, je constate un décalage de plusieurs pixels au début de l’écran et l’affichage ne va pas jusqu’aux bout des 64 caractères.
Vu de l’oscilloscope il y a en effet un décalage entre le premier pixel visible et le premier octet. Il faut donc décaler l’échantillonnage de quelques coups d’horloge, le décalage est de trois pixels, on déclenchera donc le début de l’échantillonnage en resynchronisant quand TCYx vaudra 3 au lieu de 0.

CPLD ADRESSES

DEB_PXL = !TCY2 & TCY1 & TCY0;

 

TCL0.t = DEB_PXL ;

TCL1.t = TCL0 & DEB_PXL ;

TCL2.t = TCL1 & TCL0 & DEB_PXL ;

TCL3.t = TCL2 & TCL1 & TCL0 & DEB_PXL ;

TCL4.t = TCL3 & TCL2 & TCL1 & TCL0 & DEB_PXL ;

TCL5.t = TCL4 & TCL3 & TCL2 & TCL1 & TCL0 & DEB_PXL ;

 

Bon, mais ce n’est pas tout, impossible d’afficher les deux derniers caractères Comme pour les premiers pixels, je soupçonne un problème lié à une décalage des adresses, mais ça ne donne rien.

Je regarde de plus près sur l’oscilloscope et je m’aperçois que la vidéo est encore présente alors que le signal de trame HDRV n’est plus actif.

Sur la photo, la sortie vidéo du TRS-80 est connectée à l'adaptateur vidéo composite vers VGA

On voit bien ici la présence  d'un signal hors champ de validation (TRS_HDRV est actif au niveau bas). Il faut donc augmenter artificiellement ce signal de validation de manière à pouvoir autoriser l'échantillonnage de la totalité de la vidéo.

CPLD ADRESSES

DLY_START = DLY1 & DLY0;                                        /* retard au démarrage  (3 pixels) */

DLY_STOP = DLY4 & DLY3;                                         /* Rallonge sur la fin de trame (6 pixels) */

 

DLY_SCOPE.d = 'b'1;                                                  /* Nouveau signal de Trame valide entre START et STOP */

DLY_SCOPE.ck = DLY_START;

DLY_SCOPE.ar = DLY_STOP;

 

DLY0.t = DEB_PXL & !DLY_START;                               /* Compteur de début de trame*/

DLY1.t = DLY0 & DEB_PXL & !DLY_START;

DLY0.ck = TRS_SHIFT;

DLY1.ck = TRS_SHIFT;

DLY0.ar = TRS_HDRV;

DLY1.ar = TRS_HDRV;

 

DLY2.t = DEB_PXL & !DLY_STOP;                                /* Compteur de fin de trame */

DLY3.t = DLY2 & DEB_PXL & !DLY_STOP;

DLY4.t = DLY3 & DLY2 & DEB_PXL & !DLY_STOP;

DLY2.ck = TRS_SHIFT;

DLY3.ck = TRS_SHIFT;

DLY4.ck = TRS_SHIFT;

DLY2.ar = !TRS_HDRV;

DLY3.ar = !TRS_HDRV;

DLY4.ar = !TRS_HDRV;

 

Sur le CPLD des données il faut également assurer ces décalages :

CPLD DONNEES

TCY0.t = 'b'1;

TCY1.t = TCY0;

TCY2.t = TCY1 & TCY0;

TCY0.ck = TRS_SHIFT;

TCY1.ck = TRS_SHIFT;

TCY2.ck = TRS_SHIFT;

TCY0.ar = TCY_RAZ;                                                                  /* RESYNCHRONISATION A CHAQUE DEBUT DE TRAME */

TCY1.ar = TCY_RAZ;

TCY2.ar = TCY_RAZ;

 

TCY_RAZ.t = 'b'1;                                                                      /* REMISE A ZERO SUR LE FRONT DESCENDANT DE HDRV */

TCY_RAZ.ck = !TRS_HDRV;

TCY_RAZ.ar = TCY_RAZ;

 

DEB_PXL = !TCY2 & TCY1 & TCY0;

 

DLY_START.d = 'b'1;                                                                  /* AUTORISATION DE L’ECHANTILLONNAGE APRES 3 PIXELS*/

DLY_START.ck = DEB_PXL;

DLY_START.ar = DLY_STOP;

 

DLY_STOP = DLY1;

 

DLY0.t = TRS_HDRV & DEB_PXL & !DLY_STOP;                            /* RALLONGE DU SIGNAL DE VALIDITE DE LA TRAME */

DLY1.t = TRS_HDRV & DLY0 & DEB_PXL & !DLY_STOP;

DLY0.ck = TRS_SHIFT;

DLY1.ck = TRS_SHIFT;

DLY0.ar = TCY_RAZ;

DLY1.ar = TCY_RAZ;

 

VT0.ck = !TRS_SHIFT & DLY_START;

VT7.ck = !TRS_SHIFT & DLY_START;

 

VS0.ck = DEB_PXL;

VS7.ck = DEB_PXL;

 

Sur cette photo à gauche, le PC de développement (XP 32bits), au centre et en bas la carte graphique. Le TRS-80 avec son écran et une image tremblotante réalisée à l’aide d’un convertisseur composite vers VGA. Enfin à droite l’écran VGA connecté à la carte. On voit bien que les vidéos sont bien recopiées. Sur cet écran la vidéo est très stable et très nette.

Et voilà pour la prise en compte de la vidéo texte native 😊 !