– Cette série d'articles a été rédigée entre 1992 et 1995. À l'époque les connaissances CRTC de la scène (et les miennes) étaient plus empiriques que techniques, aussi certaines informations sont discutables du point de vue théorique. Cette rubrique peut donc être grandement améliorée ; toutefois, en l'état, elle pose déjà toutes les règles pratiques à connaître pour pouvoir coder tous les types d'overscans, de ruptures et de scrollings hard sur les différents CRTC.
Nous allons donc ici parler du CRTC, le circuit sans lequel 99% des démos CPC n'auraient jamais été. C'est lui qui contrôle tous les signaux vidéos et gère les adresses de mémoire vidéo. Vous voulez faire de l'overscan ? des scrollings ? du page-flipping ? Eh bien c'est à lui qu'il faut s'adresser.
Plutôt que d'aborder le CRTC par une austère fiche technique, nous en découvrirons les fonctionnalités au fur et à mesure des techniques décrites. Pas à pas, nous allons apprendre à nous en servir pour faire de l'overscan1), de la rupture simple2), puis ligne-à-ligne3) et enfin verticale4). En outre, nous n'évoquerons ici les problèmes de compatibilité CRTC qu'au cas par cas en fonction des contraintes imposées par telle ou telle technique. Si vous voulez tous les détails sur les différents types de CRTC et leurs particularités, je vous invite à aller lire cet article, dans lequel vous trouverez également comment tester les CRTC logiciellement.
Vous le voyez, on a du pain sur la planche. Je tiens toutefois à vous avertir que les explications qui suivent nécessitent de votre part une parfaite maîtrise de l'assembleur et une excellente connaissance de l'architecture de la mémoire du CPC5).
— Basé sur les articles publiés dans Quasar CPC numéro 3 et numéro 4, Perfectionnement à l'Assembleur et Concours6), par OffseT.
Comme le laisse deviner le titre de cette première section, nous allons donc parler ici de l'overscan. Pour ceux qui ne savent pas encore ce qu'est l'overscan7), eh bien il s'agit simplement de passer outre l'énorme bordure d'écran qui encercle normalement la zone affichable : faire disparaître purement et simplement le “border” ! D'ailleurs, si l'on traduit “overscan”, on s'aperçoit que ça veut dire “au-delà du bord”. Bref, reprenons nos élucubrations sur l'overscan.
La première chose que vous devez savoir, c'est comment s'adresse le CRTC, car nous allons avoir besoin de titiller quelques uns de ses registres. Je vous rassure tout de suite, c'est très simple. La procédure se fera en deux temps. D'abord il faut préciser au CRTC quel registre on veut modifier, et ensuite, il faut envoyer la sauce ! Ces deux opérations s'effectuent à l'aide de deux ports : &BC00
pour le registre, et &BD00
pour la sauce. Voici, en BASIC pour une fois, le schéma d'adressage du CRTC :
OUT &BC00,Registre OUT &BD00,Valeur
Bon, maintenant, passons aux choses sérieuses.
Il nous faut tout d'abord placer l'écran à gauche. Pour ce faire, nous n'allons pas le soulever pour le déplacer sur le bureau, mais nous servir du registre 2 du CRTC qui permet de choisir la position de la HBL. En standard, il est à 46, ce qui correspond à un écran centré. Nous, nous allons le mettre à 49, ce qui va placer l'écran complètement à gauche.
Comment ça, il n'est pas complètement à gauche ? No problem, si vous y tenez vraiment, mettez-le à 50. Mais attention, tous les types de CRTC ne l'acceptent pas ! Eh oui, il existe sur CPC plusieurs types de CRTC, ce qui conduit à divers problèmes de compatibilité… mais il s'agit d'un vaste sujet que nous n'approfondirons pas ici, pour plus de détail je vous invite à aller lire la rubrique traitant du test CRTC. Pour le cas qui nous intéresse, il faut en fait s'assurer que la somme des registres 2 et 3 soit inférieure à la valeur du registre 0 pour rester compatible avec les CRTC “type 2”… mais bon, je vous conseille donc de laisser le registre 2 à 49 pour le moment, ce qui est largement suffisant.
À présent, il nous faut placer l'écran en haut. C'est là qu'intervient le registre 7 qui agit sur la position de la VBL. En standard, sa valeur est de 30, et nous le mettrons à 34 (ou 35) pour positionner l'écran tout à fait en haut. Maintenant que l'écran est placé, il va nous falloir l'agrandir.
Faites tout d'abord un BORDER 6
pour mieux visualiser l'écran et stigmatiser le border.
Maintenant, nous allons voir comment il faut s'y prendre pour augmenter la hauteur. Eh bien, le registre 6 est là pour ça ! Sa valeur standard étant de 25 (ce qui correspond ni plus ni moins aux 25 caractères de haut de l'écran standard), il nous faut le mettre à 35 pour avoir un écran en pleine hauteur. Et là, horreur malheur ! Ce qui est en haut se répète en bas ! Ne vous affolez pas, c'est normal !
Par défaut, le CRTC est programmé pour afficher un écran de &4000
octets (de &C000
à &FFFF
). Mais lorsque l'on augmente la taille de l'écran, le nombre d'octets à afficher dépasse &4000
… et que fait alors notre brave CRTC, il boucle ! La solution, vous vous en doutez, consiste à indiquer au CRTC qu'il n'a plus &4000
octets à afficher, mais &8000
, on passe alors en “mode overscan” avec un buffer vidéo de 32Ko au lieu de 16Ko ! Pour ce faire, il faut utiliser le registre 12 qui sert également à choisir la page écran à afficher. Ce registre va en fait de paire avec le registre 13 qui permet de définir ce qu'on appelle l'offset et dont nous parlerons plus en détail un peu plus loin. Voici donc le schéma de ces deux registres :
Les registres 12 et 13 du CRTC6845 :
|
|
On voit donc que ce sont les bits 2 et 3 du registre 12 (appelons-les les “overscan bits”) qui permettent de sélectionner le mode overscan. S'ils sont tous les deux à 1, la page écran fait 32Ko, dans tous les autres cas, elle en fait 16.
Bon, il ne nous reste plus qu'à l'élargir l'écran ! Il faut alors utiliser le registre 1 qui est à 40 en standard (comme 40 caractères “mode 1” de large), et le mettre à 46. Eh bien voilà, je crois que je n'ai rien omis de préciser concernant les notions générales sur l'overscan. Bidouillez, testez… et quand vous vous sentirez prêts, passez à la suite pour voir comment utiliser de tels écrans !
Bon, nous avons vu la partie hardware de l'overscan, mais une fois que l'écran est retaillé et positionné, il faut le gérer…
Comme vous vous en êtes sûrement aperçus, la mise en place d'un écran overscan passe obligatoirement par un écrasement total ou partiel du système vu que nous n'avons pas deux pages de 16Ko consécutives totalement libres. Ok, Ok, le petit blond à lunettes a raison, “obligatoirement” est abusif, car si on fait de l'overscan avec des ruptures simples plutôt qu'en utilisant les overscan-bits, on peut ne pas toucher au système… mais nous verrons cela plus tard vu que l'utilisation de ruptures d'écran demande plus de prérequis que l'overscan de base.
Dès lors, lorsque l'on veut faire des programmes avec un overscan via les overscan-bits, il faut soit reconfigurer les interruptions (passer en mode IM 2), soit déplacer la pile et, dans tous les cas, il faut se reprogrammer entièrement tous les vecteurs de gestion de l'affichage (&BC26
8), &BC29
9), etc.). Donc, il y a du boulot !
Je vais traiter ici deux gestions possibles de l'overscan, et, pour chacune d'entre-elles, vous expliquer comment doit se faire la reconfiguration du système.
Tout d'abord, envisageons le cas d'un écran de 32Ko logé de &C000
à &FFFF
et de &0000
à &3FFF
. Dans ce cas là, les avantages sont assez nombreux, surtout si on débute ; en effet, on n'écrase pas les vecteurs (la plupart d'entre-eux, notamment les &BB1B
10), &BB06
et &BB18
11), restent utilisables) ce qui facilite un peu la programmation, la pile reste intacte, l'Amsdos est toujours accessible, etc..
Mais, car il y a toujours un mais, du fait de la partie de l'écran logée de &0000
à &4000
, les pauvres petites interruptions IM 1 en &38
se retrouvent au beau milieu de l'écran… gênant non ? Il y a donc deux solutions pour remédier à ce problème (j'en utilise parfois d'autres mais elles sont encore plus scabreuses) : soit on fait un DI
et on en parle plus (mais là, on devra carrément se passer des interruptions, ce qui est souvent très embêtant !), soit on se met franchement en mode vectorisé (IM 2, c'est le mode d'interruption où l'on peut choisir l'adresse de l'interruption). Mais dans ce dernier cas, il y a un problème de compatibilité entre CPC et CPC+ (qui n'est pas insurmontable du tout) c'est pourquoi je vous invite tout de go à aller lire cet article qui vous expliquera tout cela en détail.
Ensuite, si on a un écran logé de &8000
à &FFFF
(c'est la configuration que je préfère même si elle pose quelques problèmes pendant la programmation). Là, plus de système du tout ! Adieu les vecteurs, ciao l'Amsdos, bye bye la pile ! Vous le voyez, avant de pouvoir utiliser cette configuration, il faut bien maîtriser l'assembleur puisque l'on doit se refaire la gestion du clavier, du FDC (pas toujours en fait), etc..
Maintenant qu'on a fait un rapide tour d'horizon des problèmes que pose l'overscan 32Ko, on va pouvoir y aller ! Pour plus de simplicité commencez par travailler avec un écran logé à partir de &C000
et un DI
.
Nous allons à présent afficher un sprite sur un écran overscan.
La plupart d'entre-vous ont déjà essayé et se sont écriés : “ça foire complètement !” Et pour cause, il faut, dès que l'on a reformaté l'écran en largeur, se refaire des &BC26
et des &BC29
adaptés car ceux du système sont prévus pour un écran de 80 octets de large… si on les utilise malgré tout, on obtient un affichage avec des lignes en décalé. Donc, nous allons voir comment il faut s'y prendre pour refaire les routines de calcul des lignes inférieures et supérieures.
Mais comme je ne sais pas si vous connaissez bien la structure d'un écran (overscan ou standard), nous allons commencer par voir ça ensemble ! Étudions tout d'abord la configuration d'un écran standard (80 octets de large par 200 de haut) et voyons comment ça marche. Allumez votre CPC et faites un petit POKE &C000,&FF
. Résultat des courses : un magnifique petit octet s'est affiché dans le coin en haut à gauche de votre écran ! Imaginons maintenant que nous voulions décaler cet octet vers la droite (qui a dit on fait RRA
?), eh bien il suffit d'incrémenter son adresse. D'où le fameux POKE &C001,&FF
qui nous affiche un autre octet juste à droite du précédent. Bon, jusque là, c'est simple. Maintenant, si on continue à incrémenter l'adresse (allez, disons 78 fois de plus, au hasard), il va bien arriver un moment où on va se retrouver contre le bord droit de l'écran (dans le cas contraire, dites-vous qu'il doit peut-être y avoir un léger problème, si si !). Dès lors, si on ajoute encore un… ça nous donnera un POKE &C050,255
(&C050
=&C000
+80
). Horreur ! L'octet se retrouve 8 lignes en dessous du premier que nous avions affiché. On peut renouveller l'opération et notre petit octet va continuer à divaguer de gauche à droite et à sauter de 8 lignes en 8 lignes, et ce, jusqu'en bas de l'écran. Si on continue alors à incrémenter l'adresse écran, le curseur réapparait en haut à gauche, mais cette fois-ci juste une ligne en dessous du &C000
… etc.. Que du bonheur !
Si vous n'avez pas bien compris tout ça, tapez le programme suivant et vous verrez exactement ce qui se passe :
10 ' L'écran et ses octets... 20 FOR x=49152 TO 65535 30 ' 49152=&C000 et 65535=&FFFF 40 POKE x,255 50 NEXT 60 END
Bon, essayons maintenant de voir ce qu'il faut faire pour passer d'un seul coup d'un seul d'une ligne à celle du dessous. Il se trouve que l'écran fait &4000
octets de long (16384 si vous préférez la barbarie), or, lorsque l'on incrémente notre petite adresse (comme nous l'avons vu juste au dessus), il descend de 8 lignes en 8 lignes d'où le calcul savant du nombre qu'il faut ajouter à l'adresse pour descendre d'une ligne et d'une seule : x=&4000
/8
=&800
. Donc, si vous faites un POKE
en &C800
, vous vous retrouverez à l'adresse située juste en dessous de &C000
. Mais attention ! Ce calcul ne fonctionne que dans le cas où vous n'êtes pas déjà à une adresse correspondant à une ligne multiple de 8 !
En effet, dans le cas où vous êtes sur une ligne qui est multiple de 8, si vous ajoutez &800
, vous sortez de l'écran et vous vous attaquez aux restarts du système… Pour obtenir une adresse correcte, il vous faudra ajouter &C000
(vous vous retrouvez ainsi de nouveau sur l'écran mais 8 lignes au dessus de l'adresse escomptée), puis enfin 80 (pour redescendre de 8 lignes). Oui, je sais, je ne suis pas très clair, mais c'est franchement pas évident à expliquer sans schéma ! Donc, pour plus de clarté, voici la routine du BC26
(pour un écran de 80 octets de large) :
; ; Calcul de la ligne inférieure ; BC26 LD A,H ; HL=adresse écran ADD A,8 ; On ajoute &800 à HL LD H,A RET NC ; A-t-on débordé de l'écran ? LD BC,&C050 ; &C000+80 ADD HL,BC RET
Oups, j'ai oublié de vous dire qu'il s'agit là d'un BC26
sur HL et qui modifie BC, de plus, il ne tourne que pour un écran logé en &C000
puisqu'il utilise la carry pour détecter le débordement. Libre à vous de le modifier selon vos besoins. Ensuite, pour l'adapter à n'importe quelle largeur d'écran, si vous avez suivi, cela ne devrait pas vous poser de problème ; il suffit de remplacer le +80 (&50
) par la nouvelle largeur d'écran en octets (valeur du registre 1 du CRTC * 2).
Enfin, ce qu'il faut savoir, lorsque l'on passe en overscan, c'est qu'on ne gère pas un écran de 32Ko, mais 2 écrans de 16Ko consécutifs (généralement un en haut et un en bas, non, non, je ne blague pas !). Vous voyez donc logiquement qu'il vous faudra deux BC26
différents suivant que vous vouliez afficher un sprite sur une partie de l'écran ou sur l'autre. J'espère que c'est bien clair.
Je sais, cette section est un peu abstraite, mais si vous connaissez déjà un petit peu la gestion des sprites avec les vecteurs système, vous devriez être en mesure d'afficher de superbes pages overscan… je vous assure que ce chapitre contient tous les éléments nécessaires même si c'est un peu éparpillé !
— Basé sur l'article publié dans Quasar CPC numéro 7, Perfectionnement à l'Assembleur, par OffseT.
Découverte par Longshot à la fin des années 80, cette technique permet de changer l'adresse vidéo en cours de balayage. Elle est donc extrêmement pratique pour créer toutes sortes d'effets à base de scrollings ou de page-flipping, et est à la base de toutes les autres techniques vidéo hardware sur CPC.
Comme nous l'avons déjà vu plus haut, on adresse le CRTC via ses 18 registres. Dans le cas qui nous intéresse, nous aurons besoin de 8 d'entre eux seulement. Je vais tout d'abord commencer par faire les présentations ; vous connaissez normalement déjà la plupart de ces registres vu que nous en avons déjà parlé dans la section sur l'overscan mais un petit rappel ne fait jamais de mal.
Registre 1 : |
Largeur d'écran en caractères mode 1. |
Registre 2 : |
Position X de l'écran visible. |
Registre 3 : |
Longueur de synchronisation horizontale. |
Registre 4 : |
Alors là, on rentre dans le vif du sujet car c'est le registre qui permet les ruptures. Il permet de définir le nombre de lignes de caractères total à balayer avant de reboucler. En standard, il est à la valeur 38. Ce qui correspond à 39 (38+1) lignes de caractères balayées, soit un écran de 312 lignes de pixels (dont en pratique 25 sont visibles, voir le registre 6 ci-dessous). |
Registre 6 : |
Nombre de lignes de caractères visibles. |
Registre 7 : |
Position Y de l'écran… ou plus rigoureusement, position de la zone de VBL. |
Registres 12 et 13 : |
Ils définissent l'adresse vidéo. Celle-ci se compose de 3 éléments :
Pour plus de clarté, voici un schéma représentant ces registres : |
Pour que vous compreniez bien comment fonctionne une rupture, il faut que vous sachiez que le CRTC dispose en fait de deux jeux de 18 registres. Il y a les registres à part entière qui sont ceux que nous pouvons adresser grâce au Z80 et des registres internes au fonctionnement du CRTC que nous appellerons les compteurs.
Voici maintenant, de façon simplifiée, comment ça se passe quand notre petit CRTC balaye l'écran.
On commence par le haut de l'écran. Le compteur 412) vient d'être remis à zéro. Le CRTC balaye la première ligne de caractères (8 lignes de pixels en standard), incrémente le compteur 4, le compare au registre 4, si c'est inférieur, on passe à la ligne suivante. Ainsi, notre CRTC continue gaiement à faire ses comptes jusqu'au moment fatidique où le compteur 4 et le registre 4 sont égaux. Le CRTC remet alors calmement son compteur 4 à zéro et lit l'adresse vidéo (le registre 12 et 13) afin d'en charger la valeur dans les compteurs 12 et 13… et reboucle.
Nous y venons. En fait une rupture c'est quoi ? Eh bien ça consiste tout simplement à changer l'adresse vidéo en cours de balayage, ce qui donne accès à quelques techniques hardware sans lesquelles le CPC serait relégué au rang d'Oric. En effet, à partir du moment où on peut changer l'adresse vidéo durant le balayage, on peut faire des scrollings hard, des flippings d'écran partiels, etc.. Mais en plus, tout un tas d'autres avantages viennent se rajouter (avec le registre 5 par exemple donc nous parlerons ci-après dans la section consacrée aux scrollings hard).
Revenons à notre adresse vidéo, comme vous l'aurez remarqué, le problème c'est que celle-ci n'est lue par le CRTC qu'au bouclage du registre 4.. Qu'à cela ne tienne, on va modifier la valeur du registre 4 et celle de l'offset pendant le balayage un peu comme on le fait avec les rasters ! Oui mais voilà : bug ! Soit on n'a plus de VBL du tout (dans le cas où le registre 7 est supérieur au registre 4), soit une vilaine zone noire de VBL vient se coller à chaque bouclage du registre 4 et donc, entre chacun des écrans ainsi créés (dans le cas où le regsitre 7 est inférieur ou égal au registre 4) ! Heureusement, papy Longshot est passé par là et il a trouvé la solution : trifouiller le registre 7 pendant la frame.
En effet, ce registre localise la zone de VBL sur l'écran et notre compère Longshot a eu la bonne idée de la mettre à 255 en haut de l'écran, il s'agit d'une valeur dite “overflow”, toujours supérieur à celle du registre 4 et qui a pour effet d'ordonner de faire la VBL… jamais ! Mais si on laissait ça comme ça, on aurait des problèmes car sans zone de synchro notre rupture ressemblerait plus à un bug qu'autre chose… d'où la suite, on remet ce registre à 0 tout à fait en bas, ce qui signifie : fais la VBL dès que le registre 4 vaut 0, c'est à dire tout de suite, dès le début de l'écran ! Résultat : notre écran est synchrone et la zone de synchro est allée se cacher sagement tout en haut de l'écran (si vous voulez voir cette zone de VBL, elle est en fait tout le temps présente, il vous suffit de tourner un peu le bouton “V-HOLD” à l'arrière de votre moniteur).
Il faut absolument que je vous parle de deux choses essentielles : la bufferisation des registres 12 et 13 et l'overflow du registre 4.
Tout d'abord, comme les registres 12 et 13 sont lus pendant le bouclage du compteur 4, vous comprendrez aisément que ceux-ci doivent impérativement avoir été chargés avec la nouvelle valeur avant ce bouclage (changement d'écran). C'est-à-dire que l'adresse vidéo de la rupture suivante doit être envoyées aux registres 12 et 13 pendant la rupture courante. Mais attention, pas trop vite non plus car sur CRTC 1 les registre 12 et 13 sont lus pendant que le compteur 4 vaut 0 (et non pas seulement quand il passe à zéro), donc il est pris en compte durant toute la première ligne de caractère de chaque rupture ce qui n'est généralement pas souhaitable ; il faut donc attendre un peu cette ligne soit terminée pour rester compatible avec ce CRTC.
Jusque là, ça n'est pas très compliqué mais par contre, pour le chargement du registre 4 se pose un problème. On ne peut pas le charger n'importe quand avant le bouclage car le CRTC ne comprendrait alors plus rien à son affaire. Il était en train de s'appliquer à compter pour arriver au bon résultat et voilà qu'un petit malin vient s'amuser à lui fausser ses calculs. Résultat : overflow dans le cas où la nouvelle valeur spécifiée est plus petite que la valeur courante du compteur 4 ou perte de toutes références (adieu les 50Hz) dans le cas où la valeur n'a pas encore été atteinte (puisque la rupture courante devient la rupture suivante !).
La solution, c'est donc de renouveller le registre 4 après que le bouclage ait eu lieu… Mais pas n'importe quel “après”. En effet, il faut faire attention à l'overflow. Il faut impérativement que lorsque vous chargez votre nouvelle valeur le compteur 4 ne l'ait pas déjà dépassée, car sinon celui-ci va refaire un tour complet sur 7 bits : c'est l'overflow.
Pour l'heure, les bases étant posées, voyez le petit prog' d'exemple qui est généreusement commenté. Pour information, sachez que les interruptions (qui nous servent de synchro via des HALT
) ont lieu toutes les 6,5 lignes de caractères.
Télécharger le listing assembleur au format Maxam 1.5
; ; Programme d'exemple de rupture simple ; par OffseT 1994 pour Quasar CPC 7 ; ; Gestion de 3 ruptures classiques ; Org &8000 Nolist ; ; Initialisations ; di ; On coupe les interruptions ld hl,(&38) ; On lit l'ancienne interruption ld (inter+1),hl ; et on la sauve. ld hl,&c9fb ; On remplace tout ça par ld (&38),hl ; du neuf (EI,RET) ei ; On remet les interruptions ; Formatage de l'écran ld bc,&bc00+3 ; On met la valeur 8 dans out (c),c ; le registre 3 du CRTC ld bc,&bd00+8 ; afin de rester compatible out (c),c ; avec les CRTC type 2 ld bc,&bc00+2 ; pour lesquels la valeur out (c),c ; 50 est autrement impossible ld bc,&bd00+50 ; à mettre dans le registre 2 out (c),c ; On met ensuite le registre 1 ld bc,&bc00+1 ; à la valeur 48 ce qui out (c),c ; nous fait un écran de ld bc,&bd00+48 ; 96 octets de large, out (c),c ; un overscan complet quoi ! ; Offset rupture 1 ld bc,&bc00+12 ; On initialise l'offset out (c),c ; qui sera celui de la ld bc,&bd00+&30 ; première rupture avant le out (c),c ; programme principal afin ld bc,&bc00+13 ; d'etre sur que tout marchera out (c),c ; sur tous les CRTC car certains ld bc,&bd00+0 ; certains refusent de modifier out (c),c ; l'offset pendant la VBL ; Programme Principal ; Attente de la VBL Prog ld b,&f5 ; On attend gentiment Synchro in a,(c) ; que le canon à électron rra ; entame sa remontée vers jr nc,synchro ; le haut de l'écran. ; Préparation des ruptures ld bc,&bc00+7 ; On met le registre 7 en overflow out (c),c ; pour ne pas etre embeter par des ld bc,&bd00+255 ; des zones de VBL intempestives au out (c),c ; milieu de nos ruptures. ld b,255 ; Une petite boucle d'attente pour que djnz $ ; les CRTC qui refusent de travailler ; pendant la VBL ne soient pas genés. ; Gestion des ruptures ; Rupture 1 ld bc,&bc00+4 ; On met le registre 4 à 14 out (c),c ; ce qui signifie que le ld bc,&bd00+14 ; premier écran fera 15 out (c),c ; lignes de haut. ; Offset rupture 2 ld bc,&bc00+12 ; On prépare le nouvel out (c),c ; offset pour la rupture ld bc,&bd00+&20 ; 2 afin que celui-ci out (c),c ; soit pris en compte au ld bc,&bc00+13 ; bouclage du compteur du out (c),c ; registre 4 de notre ld bc,&bd00+0 ; brave petit CRTC out (c),c ; adoré... ; Rupture 2 halt ; On attend que le premier halt ; écran ait été complètement halt ; balayé et que le compteur ld bc,&bc00+4 ; du registre 4 ait bouclé out (c),c ; puis on charge la nouvelle ld bc,&bd00+12 ; hauteur, 13 lignes de out (c),c ; caractères ici. ; Offset rupture 3 ld bc,&bc00+12 ; On prépare le nouvel out (c),c ; offset pour la rupture ld bc,&bd00+&10 ; 3 afin que celui-ci out (c),c ; soit pris en compte au ld bc,&bc00+13 ; bouclage du compteur du out (c),c ; registre 4 de notre ld bc,&bd00+0 ; brave petit CRTC out (c),c ; adoré... ; Rupture 3 halt ; On attend la fin de la halt ; rupture 2 et on commande ld bc,&bc00+4 ; au CRTC la hauteur de out (c),c ; l'écran suivant, ici ld bc,&bd00+10 ; c'est le dernier et il out (c),c ; fait 11 lignes. ; Offset rupture 1 ld bc,&bc00+12 ; On bufferise l'offset out (c),c ; de la première rupture ld bc,&bd00+&30 ; qui sera pris en compte out (c),c ; après la VBL. On le fait ld bc,&bc00+13 ; à l'avance car certains out (c),c ; CRTC un peu fainéants ld bc,&bd00+0 ; refusent de travailler out (c),c ; pendant la VBL. ; Fin des ruptures ld bc,&bc00+7 ; On remet le registre 7 à out (c),c ; 0 pour que le CRTC puisse ld bc,&bd00+0 ; se remettre de ses émotions out (c),c ; sinon plus de synchro ! ; Test clavier ; Barre espace ld bc,&f40e ; Rien à dire ici car out (c),c ; ce n'est pas le sujet ! ld bc,&f6c0 out (c),c xor a out (c),a ld bc,&f792 out (c),c ld bc,&f645 out (c),c ld b,&f4 in a,(c) ld bc,&f782 out (c),c ld bc,&f600 out (c),c rla jp c,prog ; ; Réinitialisations ; ; Ecran en mode standard ld bc,&bc00+12 ; On se remet l'offset out (c),c ; standard avec l'écran ld bc,&bd00+&30 ; logé en &C000 pour ne out (c),c ; pas avoir de gribouillis ld bc,&bc00+13 ; au retour au BASIC... out (c),c ; Puis on enchaine sur la ld bc,&bd00+0 ; remise à l'état standard out (c),c ; de tous les autres ld bc,&bc00+4 ; registre du CRTC. out (c),c ; Registre 4 à 38, dès la ld bc,&bd00+38 ; prochaine VBL tout va out (c),c ; rentrer dans l'ordre. ld bc,&bc00+7 ; Registre 7 à la valeur 30 out (c),c ; pour recader l'écran en ld bc,&bd00+30 ; hauteur. out (c),c ; Puis on remet l'écran ld bc,&bc00+2 ; en place en largeur out (c),c ; avant de remettre le ld bc,&bd00+46 ; registre 3 à 142 qui out (c),c ; nous avait servi à ld bc,&bc00+3 ; faire suivre le CRTC out (c),c ; type 2 qui plante ld bc,&bd00+142 ; autrement avec le out (c),c ; registre 2 à 50. ld bc,&bc00+1 ; Et puis pour finir, out (c),c ; on se remet la bonne ld bc,&bd00+40 ; largeur pour ne pas out (c),c ; perdre ce pauvre BASIC. ; Interruptions standard di ; On coupe tout ! Inter ld hl,0 ; On remet le vieux ld (&38),hl ; modèle et on ei ; remet en marche ! ret ; Heu... ; Surtout, veillez bien à ce que la somme de la hauteur de ; tous les écrans soit égale à 39 (hauteur = registre 4 + 1).
— Basé sur l'article publié dans Quasar CPC numéro 8, Perfectionnement à l'Assembleur, par OffseT.
Vous trouverez ici une application directe des ruptures classiques qui sont expliquées juste au dessus.
Le programme que je vous propose est en fait un des premiers programme de rupture que j'ai fait ; c'est pour vous dire s'il est simple ! Il n'y a qu'une seule rupture et, qui plus est, elle est au milieu de l'écran ! On ne peut pas faire plus simple… Sur le premier écran, il y a un scrolling hard vertical, et sur le second, un bête scrolling horizontal au word près.
Là, c'est très simple ! Comme vous le savez si vous avez bien lu ce qui précède, lorsqu'on configure une rupture d'écran, on spécifie via les registres 12 et 13 du CRTC l'adresse vidéo de l'écran. Dans cette adresse vidéo, nous avons l'adresse écran de base (qui reste fixe ici) et l'offset (qui va être une variable). Le CRTC, lorsqu'il regarde ce qu'il doit afficher sur votre moniteur, se sert de l'offset pour repérer à quelle adresse il doit commencer la lecture de la mémoire écran ; l'adresse haut gauche de l'écran est ainsi celle correspondant à l'adresse écran de base à laquelle on ajoute le double de la valeur de l'offset, d'où la notion de word.
Vous voyez en effet que seules les adresses paires peuvent ainsi être atteintes ! D'où un scrolling offset se déplaçant obligatoirement de deux octets en deux octets. Le seul moyen d'avoir un scroll à l'octet (ou même moins !) est de recourir à la technique du page-flipping. On gère alors x scrollings (si possible sur la même page écran pour simplifier les choses et économiser de la mémoire) décalés les uns par rapport aux autres de 1 x-ième de word et on fait un page-flipping avec tout ça ! Les plus curieux d'entre-vous trouveront des exemples pratiques d'exploitation du page-flipping dans les articles sur les effets de plasma et les scroll-text.
C'est ici un peu plus compliqué même si la technique est tout aussi élémentaire. On peut ici utiliser le registre 5 du CRTC pour avoir un scrolling au pixel près (ce qui nous dispense de la technique du page-flipping). Le registre 5 contrôle le retard vidéo ; il peut prendre des valeurs allant de 0 à 15 qui provoquent un décalage vertical de l'écran de 0 à 15 pixels… mais également une modification de la vitesse de balayage de l'écran, ce que nous allons voir un peu plus loin.
Mais pour l'heure, le principe : on décrémente le registre 5 à chaque synchro, lorsqu'il boucle on le remet à 7, et on décale l'offset de la valeur du registre 1 (la largeur de l'écran visible), ce qui fait monter l'écran de 8 lignes ! Et ainsi de suite… On a alors en théorie un joli scroll vertical au pixel géré totalement en hard.
Oui mais voilà que nos deux écrans gesticulent dans tous les sens, ça clignote, c'est l'horreur ! Eh oui, il se trouve qu'il faut compenser la valeur prise par le registre 5 au niveau de l'écran suivant celui que l'on veut faire scroller ; et ce pour deux raisons : premièrement il faut rattrapper le décalage dû au retard vidéo, et deuxièmement il faut retomber sur une fréquence de balayage de 50Hz. Enfin, voyez le programme et vous comprendrez plus aisément.
Quoi qu'il ne soit pas utile dans cet exemple, j'ai introduit dans le programme le registre 9 qui permet de spécifier au CRTC le nombre de lignes pixel de haut d'un caractère. En standard, sa valeur est à 7 soit un écran composé de caractères de 8 lignes de haut. Dans le chapitre suivant, je vous explique comment on utilise ce registre pour obtenir de la rupture ligne-à-ligne… mais si vous y regardez d'un peu plus près, je suis sûr que vous trouverez comme ça fonctionne sans même aller le lire !
Il ne vous reste plus qu'à éplucher le programme qui suit ; il vous donnera plein de renseignements utiles. Vous constaterez que tout ceci est en fait très simple, mais c'est une bonne base avant de se lancer dans des écrans plus complexes.
ATTENTION: bien que le listing fonctionne visuellement, il est erronée, l'écran n'est pas à 50Hz. Une mise à jour corrigée devrait apparaître d'ici peu
la règle “Nbre ligne de l'écran=(Reg4+1)*(Reg9+1)+Reg5” n'est pas du tout respectée.
Télécharger le listing au format Maxam 1.5
; ; Scrollings multiples avec rupture d'écran ; par OffseT (Futurs') 25/12/93 ; Adapté le 02/04/95 pour Quasar CPC 8 ; Info ; Nbre ligne de l'écran=(Reg4+1)*(Reg9+1)+Reg5 ; Registres utilisés ; ; Reg1=Largeur d'écran ; Reg2=Position X d'écran ; =Position de la HBL ; Reg4=Nbre total de lignes de caractères ; Reg5=Retard vidéo ; Reg6=Hauteur d'écran ; Reg7=Position Y d'écran ; =Position de la VBL ; Reg9=Nbre de lignes des caractères Org &4000 Nolist ; ; Initialisations ; ; Interruptions di ld hl,(&38) ld (inter),hl ld hl,&c9fb ld (&38),hl ei ; Largeur de l'écran ; (Valeurs communes aux deux écrans) ld bc,&bc00+2 out (c),c ld bc,&bd00+49 out (c),c ; Reg2=49 ld bc,&bc00+1 out (c),c ld bc,&bd00+50 ; Reg1=50 out (c),c ; ; Prog. principal ; Prog ld b,&f5 Synchro in a,(c) rra jp nc,synchro ; Format du premier écran ld bc,&bc00+4 out (c),c ld bc,&bd00+19 out (c),c ; Reg4=19 ld bc,&bc00+9 out (c),c ld bc,&bd00+7 out (c),c ; Reg9=7 ld bc,&bc00+5 out (c),c Retard1 ld bc,&bd00+7 out (c),c ; Reg5=variable ld bc,&bc00+6 out (c),c ld bc,&bd00+21 ; Reg6=21 out (c),c ld bc,&bc00+7 out (c),c ld bc,&bd00+255 ; Reg7 hors champ out (c),c ld bc,&bc0c out (c),c Plot1 ld bc,&bd00+%00110000 out (c),c ; Page=&C000 & Taille=16Ko ld bc,&bc0d out (c),c Plot2 ld bc,&bd00+0 out (c),c ; Offset=variable halt halt ; Format du deuxième écran ; On ne touche pas aux registres 4 et 9 ; car c'est comme pour l'écran 1 ld bc,&bc00+5 out (c),c Retard2 ld bc,&bd00 out (c),c ; Reg5=variable ld bc,&bc0c out (c),c Plot3 ld bc,&bd00+%00100000 out (c),c ; Page=&8000 & Taille=16Ko ld bc,&bc0d out (c),c Plot4 ld bc,&bd00+0 out (c),c ; Offset=variable halt halt halt ; Fin du deuxième écran ld bc,&bc00+7 ; On remet le Reg7 out (c),c ; à une valeur ld bc,&bd00+19 ; normale pour la out (c),c ; synchro VBL call reg5 call offset2 di ; Test clavier ld bc,&f40e out (c),c ld bc,&f6c0 out (c),c xor a out (c),a ld bc,&f792 out (c),c ld bc,&f645 ; Ligne 5 out (c),c ld b,&f4 in a,(c) ld bc,&f782 out (c),c ld bc,&f600 out (c),c ei rla jp c,prog ; Test espace ; ; Exit, on remet tout en standard ; di ld bc,&bc04 out (c),c ld bc,&bd00+38 out (c),c ; Reg4=38 ld bc,&bc09 out (c),c ld bc,&bd00+7 out (c),c ; Reg9=7 ld bc,&bc05 out (c),c ld bc,&bd00+0 out (c),c ; Reg5=0 ld bc,&bc0c out (c),c ld bc,&bd00+%00110000 out (c),c ; Page=&C000 ld bc,&bc0d out (c),c ld bc,&bd00+0 out (c),c ; Offset=0 ld bc,&bc06 out (c),c ld bc,&bd00+25 out (c),c ; Reg6=25 ld bc,&bc07 out (c),c ld bc,&bd00+30 out (c),c ; Reg7=30 ld bc,&bc02 out (c),c ld bc,&bd00+46 out (c),c ; Reg2=46 ld bc,&bc01 out (c),c ld bc,&bd00+40 out (c),c ; Reg1=40 ; Restitution des interruptions ld hl,(inter) ld (&38),hl ei ret ; ; Scrolling vertical ; Modif retard vidéo ; (écrans 1 et 2) ; Reg5 ld a,(retard1+1) dec a ld c,a and %00000111 ld (retard1+1),a cp c call nz,offset1 ld a,(retard2+1) inc a and %00000111 ld (retard2+1),a ret ; ; Scrolling vertical ; Modif. offset (écran 1) ; Offset1 ld a,(plot1+1) ld h,a ld a,(plot2+1) ld l,a ld bc,50 add hl,bc ld a,h and %00110011 or %00110000 ld (plot1+1),a ld a,l ld (plot2+1),a ret ; ; Scrolling horizontal ; Modif. offset (écran 2) Offset2 ld a,(plot4+1) ld l,a ld a,(plot3+1) ld h,a inc hl ld a,h and %00100011 ld (plot3+1),a ld a,l ld (plot4+1),a ret ; ; Data ; Inter dw &0000
— Basé sur l'article publié dans Quasar CPC numéro 9, Perfectionnement à l'Assembleur, par OffseT.
Ce coup-ci on passe aux choses sérieuses ! Je vais tenter de vous expliquer la technique de la rupture ligne-à-ligne qui est sans conteste celle qui a le meilleur rapport résultat / prise de tête. Mais ce ne sera pas une mince affaire puisque va se poser le problème qu'on a jusqu'alors pu éviter : la compatibilité CRTC.
Alors là, je préviens tout de suite ceux qui ont un CRTC type 2 : vous pouvez aller vous jeter dans le fleuve le plus proche car votre CPC est 100% incompatible avec cette technique. Je sais, c'est dur, mais on n'y peut rien (merci Amstrad !). Vous pourrez au mieux avoir de la rupture “deux-lignes-à-deux-lignes” et même si ça peut être intéressant dans des cas particuliers, on perd beaucoup des avantages de la ligne-à-ligne.
Si vous avez un type 0, 1, 3 ou 4, pas de problème, ça marchera ! Mais attention, si ça marche sur votre CPC, cela ne veut pas dire que ça marchera sur celui de votre voisin… des adaptations CRTC seront nécessaires.
Le technique est extrêmement simple ! En fait, ce qu'on veut c'est avoir une rupture à chaque ligne pixel et non à chaque ligne caractère afin de pouvoir modifier l'offset quand bon nous semble. Ça y est, le petit blond à lunettes ricane car le registre 4 ne permet que des ruptures par lignes de caractères. Eh bien, qu'à cela ne tienne ! On va dire au CRTC que nos caractères font une ligne de haut !
Comme j'en ai déjà brièvement parlé dans la section précédente, nous allons utiliser le registre 9 qui donne le nombre de lignes d'un caractère. En standard, il est à 7 pour 8 lignes par caractère ; il nous faudra donc le mettre à 0 pour avoir des caractères d'une ligne de haut. Et puis pour avoir une rupture à chaque ligne, il nous faudra également mettre le registre 4 à 0 comme on l'aurait fait en rupture classique.
De plus, comme vous le constaterez, chacune de nos ruptures “ligne” sont identiques ; il ne sera donc pas nécessaire de renouveller les registres 4 et 9 durant notre rupture qui se fera toute seule ! Nous aurons juste à gérer l'offset ou des affichages au fur et à mesure de notre rupture “automatique”. C'est ce qui consitue le principal avantage de cette technique.
En revanche, nous avons un inconvénient en retour : comme nos caractères ne font qu'une seule ligne de haut, seul le bloc 0 est balayé. On n'a donc pas accès à toute la mémoire vidéo et le nombre de lignes différentes disponible est donc limité (à une vingtaine de lignes par écran de 16k si vous comptez), mais c'est largement suffisant pour nombre d'effets !
Voilà, côté théorique, comme vous le voyez, il n'y a aucune difficulté. Mais, va se poser à présent le douloureux problème de l'overflow…
Eh oui, le registre 9 va nous poser problème lorsqu'on va le mettre à zéro. En effet, si notre CRTC était en train de balayer une ligne autre que la ligne numéro 0, on lui donne une borne inférieure à la valeur à laquelle il est déjà… comme je vous l'ai précédemment expliqué, on obtient alors un overflow !
Mettez-vous à la place du CRTC et vous allez mieux comprendre. Vous êtes en configuration standard et vous balayez un des caractères de 8 lignes. Vous commencez à balayer un nouveau caractère :
Tout se passerait pour le mieux si un petit malin ne se mettait pas à changer la valeur du registre 9 en cours de route ! Alors que vous balayiez la ligne 3, on vous met le registre 9 à 0.
Et on peut alors aller très très loin comme ça ! C'est l'overflow ! Heureusement, notre registre 9 boucle à 32 et l'overflow se traduira tout bêtement par la répétition (4 fois) de la première ligne de caractère de l'écran et non par un plantage CRTC… ouf !
Résoudre le problème de l'overflow est au final assez simple. Il suffit d'attendre la ligne précise à laquelle le CRTC est en train de balayer le bloc 0 pour mettre le registre 9 à 0. Ainsi, il n'y a pas d'overflow ! Oui, mais voilà, cette ligne n'est pas exactement au même endroit sur tous les CRTC par rapport aux interruptions qui nous servent de synchro. Cela implique donc qu'il faudra obligatoirement faire une adaptation CRTC en compensant ces décalages de synchro par des boucles de temporisation différentes d'un CRTC à l'autre.
Voici donc l'algorithme général à suivre pour faire de la rupture ligne-à-ligne.
Je pense avoir été clair ! Je vous propose maintenant d'aborder le programme que je vais vous détailler afin d'aborder les problèmes d'adaptation CRTC.
Bon, au début du programme, vous avez les classiques initialisations. On met l'écran en overscan et on met EI:RET
en interruption. Vient ensuite l'attente de la VBL qui est suivie par une boucle d'attente. Cette boucle est là uniquement pour une bête compatibilité CRTC. Elle est par exemple inutile pour les types 1 mais d'autres CRTC sont perdus en son absence.
On met ensuite le registre 7 à 255 pour préparer une rupture classique, puis le registre 4 à 6 pour la programmer à 7 lignes de caractères de haut. Un petit HALT
nous amène à peu de choses près à la fin de notre rupture classique. Ici on va lancer notre rupture ligne-à-ligne ; il nous faut donc temporiser pour tomber sur le bloc 0. C'est le pourquoi de la boucle de 64 NOP
en Loop1
. La valeur 2 est correcte si vous avez un type 1. Si vous avez un type 3 c'est 3 qu'il vous faudra mettre. Il est à noter par ailleurs que le type 3 ne fait pas d'overflow car il fait un “reset block” lorque l'on modifie le registre 9.
Une fois que l'on est sûr d'être où il faut, on met les registres 9 et 4 à 0 pour lancer notre rupture ligne-à-ligne. On la laisse ensuite courir en attendant 4 HALT
+ 20 lignes de telle sorte de n'avoir plus qu'à faire une rupture classique de 4 lignes en bas de l'écran de notre petit CPC (je vous rappelle qu'au total on doit toujours avoir 312 lignes pour conserver un balayage à 50Hz). Mais attention, pour certains CRTC, il vous faudra là aussi temporiser en Loop2
pour que la reprise de la rupture classique se fasse sans problème.
Ensuite, il ne vous reste plus qu'à mettre le registre 7 à 0 pour que tout soit synchronisé correctement. Là encore, une petite temporisation de 8 lignes a été insérée. Elle n'est indispensable que pour le type 1. En effet, il se trouve que le CRTC 1 prend en compte les mises à jours des registres en direct tant qu'il est en train de balayer la première ligne de caractère (ce qui fait de lui le seul CRTC intéressant pour exploiter la RVMB… mais c'est un autre sujet) et il faut donc attendre la fin du premier caractère car sinon, sans se poser de question, il va nous balancer une VBL en plein milieu de notre rupture (quel farceur !). Alors, si vous voulez que votre programme soit compatible avec les types 1, attendez toujours que les 8 premières lignes d'une rupture classique aient été balayées avant de reprogrammer un registre (que ce soit le 7 ou même les registres d'offset) car sinon celui-ci sera pris en compte directement et non bufferisé.
Voilà, je crois que vous êtes maintenant en mesure de faire de la rupture ligne-à-ligne, à vos plasmas, rubber bars et autres aurores boréales !
Télécharger le listing au format Maxam 1.5
; Exemple de rupture ligne-à-ligne ; par OffseT le 25/08/95 ; pour ; Quasar CPC numéro 9 Org &8000 ; On commence ici ! Nolist ; ou à l'adresse ; que vous voulez... ; ; Initialisations ; ld bc,&bc01 ; On définit la out (c),c ; largeur de notre ld bc,&bd00+48 ; écran en la fixant out (c),c ; à 48 words. ld bc,&bc02 ; On positionne notre out (c),c ; écran pour qu'il ld bc,&bd00+50 ; soit bien centré out (c),c ; sur le moniteur. di ; On coupe les inters ld hl,(&38) ; du système après ld (inter+1),hl ; les avoir sauvées en ld hl,&c9fb ; fin de programme par ld (&38),hl ; une auto-modif. ei ; Et hop là ! ; ; Programme principal ; Prog ld b,&f5 ; Attente de la synchro Synchro in a,(c) ; écran, classique quand rra ; on fait des ruptures jr nc,synchro ; ou des rasters. ld b,255 ; Boucle d'attente pour Loop djnz loop ; la compatibilité CRTC ld bc,&bc07 ; Mise en overflow du out (c),c ; registre 7 pour ne ld bc,&bdff ; pas avoir de VBL out (c),c ; entre nos ruptures ld bc,&bc04 ; La première rupture out (c),c ; est classique et fait ld bc,&bd06 ; 7 caractères de haut out (c),c ; Voilà... halt ; Attente de la fin de ld b,2 ; la rupture. Puis Loop1 ds 60 ; positionnement précis djnz loop1 ; avec une boucle à ; adapter suivant ; votre CRTC. ld bc,&bc04 ; Nos écrans feront 1 out (c),c ; ligne de caractère ld bc,&bd00 ; de haut (reg4=0) out (c),c ld bc,&bc09 ; Et nos caractères out (c),c ; feront 1 ligne pixel ld bc,&bd00 ; de haut seulement ! out (c),c ; C'est parti ! halt ; On laisse se faire la halt ; rupture un petit brin halt ; de temps pour bien la halt ; voir... ld b,20 ; Dernière petite attente Loop2 ds 60 ; pour tomber exactement djnz loop2 ; où on veut... ld bc,&bc09 ; STOP ! Plus de rupture out (c),c ; ligne-à-ligne ! On ld bc,&bd07 ; reprogramme une rupture out (c),c ; classique... ld bc,&bc04 ; Vu où on se trouve sur out (c),c ; l'écran, celle-ci doit ld bc,&bd03 ; faire 4 lignes de haut out (c),c ; pour tourner à 50Hz... ds 64*8 ; On attend que notre ; rupture ait commencé ld bc,&bc07 ; puis on met le registre out (c),c ; 7 à 0 pour envoyer ld bc,&bd00 ; la VBL et synchroniser out (c),c ; le tout ! ; ; Test clavier ; ld bc,&f40e ; On tripote un out (c),c ; petit peu le ld bc,&f6c0 ; PPI pour se out (c),c ; faire un test xor a ; clavier de la out (c),a ; toutouche ld bc,&f792 ; espace... out (c),c ; Pfiou ! Que ld bc,&f645 ; c'est long... out (c),c ; Courage, la fin ld b,&f4 ; est proche ! in a,(c) ; On saisit l'état ld bc,&f782 ; de la ligne 5 out (c),c ; et on remet le ld bc,&f600 ; PPI dans son état out (c),c ; normal. rla ; On teste ladite jp c,prog ; toutouche... ; Et on boucle ! ; ; Quit ; ld bc,&bc07 ; On remet tous les out (c),c ; petits registres ld bc,&bd00+30 ; du CRTC auxquels out (c),c ; on a touché à ld bc,&bc04 ; leur état normal out (c),c ; pour que le CPC ld bc,&bd00+38 ; n'ait pas le out (c),c ; mal de mer au ld bc,&bc01 ; retour... out (c),c ; Que c'est laborieux ! ld bc,&bd00+40 ; Largeur standard... out (c),c ; OK ! ld bc,&bc02 ; Position standard out (c),c ; de l'écran... ld bc,&bd00+46 ; OK ! out (c),c ; Ouf ! di ; On récupère les inters Inter ld hl,0 ; du début et on revient ld (&38),hl ; au BASIC... ei ; Attention... ret ; Et voilà !
— À rédiger en exclusivité pour le Quasar Net !