Faire fonctionner une démo incompatible CRTC2 sur… CRTC2
En ce moment, DManu78 est en train de finaliser la prochaine version d’Amspirit et dans cette optique il effectue un paquet de tests de non-régression. Cela consiste à tester des démos, certaines notoirement complexes comme la 5KB3 (qui est loin de passer sur tous les émulateurs) afin de vérifier que les dernières modifications de son émulateur n’ont rien cassé dans le code. A l’occasion il teste aussi quelques démos pas spécialement complexes, juste pour s’assurer que tout va bien.
Ce matin il a testé la « Scroll Factory » d’Impact. Celle-ci est annoncée comme fonctionnant sur tous les CRTCs, et pourtant…

L’image en haut à gauche et celle en bas à droite correspondent à ce que donne la démo sur CRTC1 et CRTC3. Ca se complique sur CRTC4 (bas à gauche) et c’est complètement planté sur CRTC2 (L’image qu’on voit correspond à la partie précédent les autres écrans, là on a un son fixe qui est diffusé, et l’image défile verticalement.
Panique à bord ! Dmanu aurait-il foiré dans son code ? Après vérifications sur le vrai hardware, son émulation correspond exactement à ce qu’on obtient avec la machine d’origine. Mais que se passe-t’il ?
On va regarder ce qu’il se passe sur le CRTC2. Ce dernier est réputé pour être le plus compliqué, le plus limité et j’avoue que c’est mon petit chouchou. Il est compliqué surtout parce qu’il n’a pas vraiment été étudié : il a été employé dans à peine 10 à 15% des Amstrad CPC.
Regardons l’état des registres du CRTC sur l’écran planté :

J’ai vérifié, l’écran ne comporte pas de rupture. On devrait être face à une image fixe. Est-ce que les paramètres permettent d’afficher une image stable en vertical ? Le Registre 4 est à #26, ce qui correspond à 38 en décimal, c’est la valeur standard. Le registre 7 est à #23, qui est une valeur inférieure à celle du registre 4, un signal VSync (Qui permet de signaler au moniteur qu’on démarre une nouvelle frame) devrait donc être généré par le CRTC dès que C4 (le compteur associé au registre 4) atteint la valeur de R7. Or, sur la capture d’écran on voit que C4 est à #23, et que le seul signal qui est envoyé par le CRTC, c’est… le border. Pas de VSync alors que C4=R7 ! On devrait avoir un signal VSync ! Si les valeurs de synchronisation verticale semblent cohérentes mais qu’on n’a pas de VSync comme prévu, c’est que le problème réside ailleurs.
On peut peut-être regarder la synchro horizontale. On a R0=#3F, c’est la valeur standard sur CPC. R2=#32, qui correspond à 50, c’est la valeur normale quand on fait un écran fullscreen. le signal de synchro horizontale se produira donc 50 microsecondes après le début de la ligne. R0=48, pas de surprise non plus : On a donc bien un écran fullscreen en largeur, 48 caractères seront affichés. R3=#8E, c’est la valeur au démarrage du CPC. Mais… Attendez !? Et la règle de compatibilité CRTC2, qu’on connait depuis les années 80 ? « Sur CRTC2, R2+R3 doit impérativement être inférieur à R0 »
R3 peut être découpé en deux valeurs, dans notre cas #8 et #E. Le #8 correspond sur CRTC0, 3 et 4 à la durée de la VSync, en scanlines. Si vous avez un autre CRTC cette valeur n’est pas prise en compte et le composant impose d’office une VSync de 16 scanlines de haut. Sachant que le Gate Array intercepte le signal et fait sa propre cuisine (4 lignes de VSync suivies de 22 lignes de noir), on n’a pas grand intérêt à jouer avec cette valeur. En pratique on peut la mettre à 0, ça forcera tous les CRTCs à faire 16 scanlines de VSync. Ceci nous évitera un problème d’incompatibilité potentiel. Reste le poids faible, #E. Cela correspond à la valeur 14 : la Hsync (le signal de synchronisation horizontal, qui est produit à la fin de chaque scanline afin que le moniteur sache qu’il doit passer à la ligne) durera 14 microsecondes.
Oui mais voilà : R3+R2=14+50=64, et le registre R0 est à 63. Ca plante. Pour l’explication technique détaillée, je vous renvoie au Compendium, partie 15.4.4.
En pratique, on peut sans problème définir une longueur de 6 microsecondes pour la HSync. C’est la durée du signal fourni par la Gate Array, qui n’hésite jamais à intercepter les signaux sur CRTC. En clair, que vous mettiez 6, 7,8 ou n’importe quelle valeur supérieure, le Gate Array tranche dans le vif et limite le signal à 6. Attention, une valeur inférieure fonctionne aussi mais elle réduira la durée de la HSync fournie par le Gate Array, avec quelques conséquences. Dans tous les cas, 6 est la plus petite valeur de HSync fournie par le CRTC qui n’a aucune incidence visible à l’écran. Et dans ce cas, une valeur de 6 règlerait notre prolème : R3+R2=6+50=56, ce qui est largement inférieur à R0 !
J’ai modifié R3 au vol dans mon émulateur préféré, et à la frame suivante on a :

Un signal VSync est bien produit. Si on arrête le débugger la démo se met à fonctionner.
Quand on a R3+R2>R0 sur CRTC2, le signal de synchronisation vertical est inhibé. Le code de nombreuses démos utilisent l’arrivée de ce signal pour se caler correctement et synchroniser des rasters, de la musique. En l’occurrence, comme il n’y avait pas signal de synchro le code passait son temps à attendre un feu vert qui n’arrivait jamais !
C’est bien beau tout ça, mais est-ce qu’on va être obligés de modifier les valeurs au vol dans un émulateur pour être capables de visionner cette démo sur CRTC2 ? Non ! Il y a une autre solution, et elle fonctionne même sur une vraie machine : Le Basic a la solution !
Comme on l’a vu plus haut, la valeur de R3 présente durant la démo est la valeur standard définie par le firmware. Il y a donc de grandes chances que le programme ne modifie tout simplement pas cette valeur avant d’arriver à la partie problématique. Le Basic nous offre la possibilité de modifier les registres du CRTC, on ne va donc pas s’en priver : Pour mettre le registre 3 à 6 il suffit de taper la ligne suivante :
OUT &BC00,3:OUT &BD00,6
… Puis lancer la démo normalement, avec un RUN « SCROLLF ». Et ça fonctionne !
Il est certain que ce type de problème existe avec d’autres démos. En cas de blocage de la musique avec un défilement vertical sur un CPC CRTC2, ça vaut la peine de tenter la ligne de Basic et de relancer la démo pour voir si cela règle le problème.