Les split-rasters
par roudoudou - cet article est sous licence
modification,reproduction libre, citation de l'auteur

________________________________

Techniquement, le split-raster consiste à changer la couleur d'une encre pendant que le balayage est visible. Ainsi, sur la même ligne, avec la même encre, on observe plusieurs couleurs différentes. La technique demande plus de doigté que des rasters simples (ligne à ligne) car il faut être précis dans le déclenchement du changement d'encre. Nous verrons un peu plus loin que ce n'est pas si facile que ça et impose quelques contraintes.

Cependant, autant vous montrer tout de suite un exemple parlant comme le dessin du rhinocéros en mode 2 de Madram. Pour rappel, le mode 2 est le mode haute-résolution 640x200 en 2 couleurs. Grâce aux splits-rasters, certaines lignes profitent de 3 couleurs. Voici une utilisation pratique et utile des splits-rasters. Je dis utile, pour ne pas paraphraser Sylvestre à propos de toute la floppée de démos qui ont fait des splits-rasters sans qu'on sache vraiment pourquoi, à part gaspiller du temps machine. Pour faire court: C'était moche.


Rhinocéros / Madram

On pourra toutefois noter l'utilisation des splits-rasters pour faire des scroll textes dans certaines démos, dont une particulièrement réussie avec un mélange de rasters ligne à ligne pour créer une belle illusion d'optique dans la Big'O'Full d'Arkos.


Big'O'Full / Arkos

La précision requise pour afficher des splits-rasters impose l'usage de l'assembleur. De façon générale, l'utilisation d'un langage trop lent ou compilé ruinerait tous vos efforts avec la moindre modification dedans. Évitez de perdre du temps. On utilisera donc les couleurs hardware du gate-array dont voici un tableau de correspondance sur cet écran mode 2 qui affiche les 27 couleurs de notre bon vieux CPC, sans trucage évidemment.



Pour réaliser cet écran, je vais changer la couleur du papier et laisser tranquille la couleur du texte, cela va m'éviter de dessiner un rectangle au bon emplacement. Ainsi, je n'aurais qu'à jouer sur la temporisation pour caler mes rectangles de couleur au bon endroit. De même que pour tout effet ligne à ligne, il est impératif d'avoir une durée d'exécution ligne à ligne de 64 nops sous peine de décalages.

Pour l'affichage du texte, j'ai utilisé le BASIC, bien plus souple:

10 MEMORY &3FFF
20 MODE 2:ZONE 2
30 BORDER 0:INK 0,0:INK 1,13
40 DIM ga(26):FOR g=0 TO 26:READ ga(g):NEXT
50 DATA 20,4,21,28,24,29,12,5,13,22,6,23,30,0,31,14,7,15,18,2,19,26,25,27,10,3,11
60 c=0:FOR J=0 TO 8:FOR I=0 TO 2
70 IF i=0 THEN d=0 ELSE d=1
80 LOCATE 5+i*26+d,1+j*2:PRINT"color";c;ga(c)
90 c=c+1
100 NEXT:NEXT
110 LOCATE 25,20:PRINT"color (basic) (gate array)"
120 LOCATE 20,18:PRINT"01234567"
130 CALL &BB18:CALL &9000

Je ne commente pas en détail. J'affiche du texte à une position en rapport avec mes compteurs de boucle. Ce texte contient le compteur c qui s'incrémente ainsi que la valeur associée que j'ai lu au tout début des DATA dans le tableau ga. Enfin j'attends une touche et je lance l'assembleur en &9000 (voir code ci-dessous). L'instruction intéressante et peu connue est ZONE qui redéfinit la tabulation entre variables lors du PRINT.
On remarque visuellement une certaine harmonie naturelle dans les couleurs affichées selon l'ordre du BASIC, ce n'est pas un hasard qu'il y ait 27 couleurs. Mes deux boucles FOR imbriquées correspondent aux 3 colonnes sur 9 lignes, représentatives au passage des niveaux RVB de la palette de l'Amstrad CPC.

Le code assembleur, initialisation et préambule à l'affichage:

org #9000

di ; coupe les interruptions
ld hl,#C9FB ; code EI/RET dans HL
ld (#38),hl ; nouveau code d'interruption pour le RST 38H (désactive le système)

loop: ei ; active les interruptions (nous en avons besoin pour le HALT)
; wait VBL
ld b,#F5
novbl:
in a,(c)
rra
jr nc,novbl

halt ; nous sommes synchros 16 lignes avant l'écran
di ; et maintenant on coupe les interruptions jusqu'au bout

defs 48,0 ; petite pause jusqu'à la position X désirée

ld bc,#7F00 ; Gate Array pinceau 0
out (c),c


Le code assembleur de l'affichage d'une ligne de 3 blocs de couleurs différentes:

Lorsque nous arrivons à cet endroit du code, l'encre zéro (le papier) est déjà sélectionnée. Tout ce qui nous reste à faire est de préparer les quatre couleurs split pour jouer avec. Nous avons besoin pour la première ligne du noir, encore du noir (sur les autres lignes, ça changera), du bleu et du bleu clair, ainsi qu'un compteur de ligne pour répéter l'opération. Comme le premier HALT arrive un peu avant l'écran, la boucle ne fait pas 8 lignes mais 25 lignes. Les 17 premières lignes de split seront hors écran. Ce n'est pas optimal mais pour cette démonstration, on s'en moque!
; line counter
ld e,25 ; premier compteur à 25 mais les suivants seront à 8
; split colors
ld c,20+64 ; 0 black (black)
ld h,20+64 ; 0 black (black)
ld l,4+64 ; 1 blue (dark blue)
ld d,21+64 ; 2 bright blue (blue)

;------ 64 nops loop -------
rastaline1:
out (c),h ; première couleur
out (c),c ; retour au noir
defs 5,0 ; on attend un peu pour la seconde
out (c),l ; deuxième couleur
out (c),c ; retour au noir
defs 5,0 ; etc.
out (c),d
out (c),c

defs 26,0 ; 64 nops filler

dec e ; compteur de ligne
jr nz,rastaline1
;--- end of 64 nops loop ---

; Attendre EXACTEMENT 8 lignes avant de faire à nouveau des splits
ld e,125
tempo1:dec e
jr nz,tempo1
defs 2,0

Ce code assembleur est répété 9 fois avec des valeurs différentes pour les registres h,l,d qui sont les trois couleurs changeantes par lignes. On aurait pu factoriser le code et utiliser une table de couleurs, mais cela aurait rendu le code moins lisible. N'oubliez pas d'optimiser chez vous!

Le code à retenir:
ld bc,#7F00+couleur sélectionée ; sélection de l'encre du gate array
out (c),c

ld a,couleur+64 ; envoi de la couleur
out (c),a

En général, on évite de faire un split sur plusieurs encres car il faut alors faire deux OUT pour changer une couleur (un OUT pour sélectionner l'encre et un OUT pour changer la couleur de l'encre), soit un minimum de 8 nops (nous n'utiliserons pas l'instruction OUT (n),A. Jamais). La longueur minimale d'un split quand on utilise la meme encre est alors de 8 caractères mode 2 comme on le voit sur la capture d'écran, soit 4 mots ou 4 nops.


Il ne faut jamais dire jamais

J'ai écrit "en général" et pas "jamais" car je vais immédiatement vous donner un contre-exemple aux classiques split-rasters qui utilisent la meme encre. En mode 2, l'écran du CPC est assez fin pour que les trames aient un effet presque parfait. Nous allons en profiter pour afficher plein de couleurs sur notre bon vieux CPC.



Une fois de plus, j'utiliserai le basic pour afficher ma trame de départ et redimensionner l'écran. Comme les split-rasters prennent du temps, j'ai cette fois besoin de toute la largeur disponible. Je ne fais pas un overscan mais un fullscreen. Overscan, overscan, est-ce que j'ai une tete d'overscan?



Voici la boucle d'affichage d'une ligne de blocs de couleurs (qui respecte la règle des 64 nops). Nous assumons qu'avant de rentrer dans cette boucle, le registre B vaut #7F (gate array), HL pointe sur une table de couleur, C vaut 0 et D vaut 1, pour nous permettre de sélectionner les encres 0 et 1 alternativement. Pour envoyer les couleurs, nous utiliserons l'instruction OUTI. Son fonctionnement a été détaillé de nombreuses fois dans plein d'articles, OUTI va d'abord décrémenter B, lire la valeur en (HL) et l'envoyer sur le port. Vous me ferez remarquer que du coup, je n'écris pas sur le port #7F mais #7E. Oui, c'est vrai, et ça fonctionne toujours (voir chapitre optimisation). Comme j'utilise aussi le OUT classique en plus du OUTI, il faut choisir. Je ne peux pas utiliser le OUT classique avec une valeur à #80, c'est pourquoi j'utilise #7E lors du OUTI et que je l'incrémente ensuite. Le registre E contient le nombre de lignes à afficher.

Vous noterez le nop intercalaire peu avant la fin de boucle, afin de ne pas remettre trop tot l'encre de départ en noir pour la ligne suivante!

rastaline1:
ld l,c
out (c),c
outi
inc b
out (c),d
outi
inc b
out (c),c
outi
inc b
out (c),d
outi
inc b
out (c),c
outi
inc b
nop
out (c),d
out (c),a
dec e
jr nz,rastaline1


Exécution pas à pas de la boucle de splits

On peut voir sur la capture ci-dessous la trame normale avant que les splits soient exécutés sur les blocs suivants. Le changement de couleur doit impérativement se faire sur une couleur unie qui n'est pas celle qu'on change, sous peine de voir l'effet foirer complètement. Regardez bien sur les captures l'affichage de l'écran avancer progressivement au fur et à mesure que le code déroule. Dans le panneau registers, vous voyez aussi les couleurs 0 et 1 changer à chaque étape.













Aller plus loin dans l'adressage du gate array - les optimisations du goret

L'adresse de port recommandée pour accéder au gate array est #7F mais il est possible d'en utiliser d'autres! Pas de valeur supérieure car le bit de commande du gate array ne serait plus bon, mais toute la floppée de valeurs inférieures fonctionneront plus ou moins bien. En fait, au fur et à mesure qu'on va descendre dans les valeurs, le gate array sera adressé, mais aussi d'autres périphériques! Certaines démos finissent par activer le lecteur de disquette en écrivant en #7A. C'est pour cela qu'il faut éviter au maximum de trop descendre, voir de descendre tout court!

Pour aller plus loin à propos du HALT

Nous avons vu que nous utilisions l'instruction HALT pour nous synchroniser avec le balayage. Concrètement, que se passe-t'il lorsque le Z80 exécute l'instruction HALT?

D'après la documentation technique, le CPU exécute des nops pour maintenir le rafraichissement mémoire et attend une interruption masquable ou non masquable. Par ailleurs il envoie son état sur la broche HALT mais ceci n'a pas grand intéret en programmation. Ce qui nous intéresse, c'est qui génère ces interruptions et quand?

Avec le mode d'interruption par défaut (IM 1) et avec une configuration écran pas trop exotique (pas de rupture verticale par exemple) les interruptions sont générées toutes les 52 lignes. Il y a donc un compteur en interne qui cycle de 0 à 51 sobrement appelé CI. Ce compteur est incrémenté à chaque HBL et une interruption est générée à chaque fois que le compteur arrive à 52, SAUF si une VBL est en cours. Si avec ses rasters on prévoit de changer de mode, attention au bit supérieur qui réinitialise le compteur d'interruption. Son usage peut-etre utile mais vous chamboulerez tout le déroulement des interruptions.
Sur CPC+, avec la page ASIC activée, vous pouvez choisir la ligne écran à laquelle l'interruption aura lieu (POKE en #6800). Comme cette valeur est 8 bits et que la valeur zéro désactive la fonctionnalité, vous ne pourrez choisir que les lignes 1 à 255. Attention, le compteur de ligne boucle donc si vous avez un écran de 280 lignes visibles et que vous réglez l'interruption raster en 10, il y aura deux interruptions par écran! En programmation avancée, vous pouvez meme changer la valeur du registre #6800 en cours de balayage. Et ainsi déclencher presque autant d'interruptions que vous voulez.

Le décalage du mode 2 sur certains CPC, le décalage des encres sur CPC+

Il y a eu plusieurs versions du gate array dans la production d'Amstrad CPC:

- Amstrad 40007 sur CPC 464
- Amstrad 40008 sur CPC 464, 664
- Amstrad 40010 sur CPC 464, 664 et 6128
- Amstrad 40226 sur CPC 464 et 6128, you can detect this one as it comes with a CRTC 4
- Amstrad 40489 sur CPC 464+, 6128+ et GX-4000, you can detect this one as it comes with a CRTC 3

Sur l'Amstrad 40007, 40008 et 40226, le gate array décode tous les modes de la meme façon. Le changement de couleur du split-raster se cale parfaitement sur un mot CRTC
illustration Grimware.org


Sur l'Amstrad 40010, très populaire chez les démomakers avec le CPC 6128, le gate array commence à décoder les pixels mode 2 un peu plus tot (1 pixel de décalage). Le changement de couleur du split-raster donne l'impression d'arriver plus tard mais ce sont les pixels qui arrivent plus tot!
illustration Grimware.org


Sur l'Amstrad 40489, le gate array décode tous les modes de la meme façon. Par contre, le changement de couleur du split-raster se cale sur un demi-mot CRTC
illustration Grimware.org


On peut compenser le décalage du CPC+ avec le SSR (Soft Scroll Register) mais il ne faut pas oublier de le modifier dès qu'on change de mode. Cela peut parfois se révéler trop couteux pour etre mis en place. C'est pourquoi de façon générale, on évitera d'utiliser les splits-rasters avec une trop grande précision dans les graphiques.

Les split-rasters sur CPC+ avec l'ASIC

Sur les deux captures ci-dessous on devine aisément la largeur d'un split-raster CPC+. Le plasma qui utilise un affichage plus complexe (mélange des trois couches RVB) en affiche déjà 16, et le scrolltext du masque en totalise 20. Évidemment, les conditions d'accès à l'ASIC via la mémoire au lieu des ports y sont pour beaucoup.
Cependant, attention aux couleurs qui utilisent 2 octets car le Z80 est incapable de réaliser une vraie écriture 16 bits, meme avec la pile! Chaque octet sera écrit successivement, entrainant un artefact visuel si la couleur modifiée est en train d'etre affichée. C'est pour cette raison qu'on ne modifiera les encres qu'en masqué, ou bien on se contentera de ne travailler que sur le vert, ou sur le duo rouge/bleu comme dans l'Asic demo 1 ou l'Asm demo 1 réalisées par mes soins


ASIC intro 1 (roudoudou) / 16 splits par ligne pour un vrai plasma haut en couleurs


ASM demo 1 (roudoudou) / 20 splits par ligne (et du phong mapping au pixel!)

Illustration d'une écriture pseudo 16 bits dans un registre de couleur ASIC

écriture 16 bits avec LD (mem),reg16
infinite_loop:
ld hl,#0000
ld (#6400),hl
defs 30,0
ld hl,#0FFF
ld (#6400),hl
jr infinite_loop


le rouge/bleu est modifié en premier


écriture 16 bits avec PUSH reg16
infinite_loop2:
ld bc,#0000
ld de,#0FFF
ld sp,#6402
push bc
defs 30,0
ld sp,#6402
push de
jr infinite_loop2


le vert est modifié en premier (car la pile descend dans la mémoire)



Le mot de la fin:
La rapidité de changement des couleurs sur CPC+ permet de modifier 4 couleurs complètes par ligne hors affichage, ce qui n'est pas possible avec la méthode des OUT. Cela ouvre la voie à de nombreux effets. Saurez-vous les programmer?


roudoudou


Annexes:
- Récapitulatif des timings Z80 sur CPC (Quasar/Offset)
- Instructions du locomotive basic


Références:
- Fanzine Amslive n°4 page 3 - La vie s'écoule ; Le cpc c'est cool / Madram
- Fanzine Amslive n°20 page 11/12/13 - Les maitres du temps / Madram
- Grimware.org - Documentation gate array / Grim
- AMSTRAD GX4000 CODING / BDC Iron


Merci à Iron, Madram et Offset pour leur soutien.


© 2016 www.roudoudou.com