Retourner au sommaire

L'ASIC

Les rubriques CPC+ ont eu une vie plutôt chaotique dans les pages de Quasar CPC. Aussi, certains aspects n'ont pas été abordés correctement - voire pas du tout -, alors que d'autres ont été très largement détaillés. En conséquence, un gros travail d'homogénéïsation est à effectuer dans cette section qui regroupe la plupart de ces articles.

A zic alors ! Nous allons ici étudier de façon relativement détaillée de fonctionnement de l'ASIC des CPC+. Il s'agit du chipset spécifique qui équipe uniquement les CPC+ et leur permet d'avoir 4096 couleurs, 16 sprites hard, des DMA audio, une gestion vectorisée des interruptions, etc.

La page I/O ASIC

Basé sur l'article publié dans Quasar CPC numéro 5, CPC plus, par OffseT.

Comme vous le savez certainement déjà, à la différence des autres périphériques du Z80, l'ASIC ne s'adresse pas grâce à un port mais par l'intermédiaire d'une page mémoire R/W qui lui est réservée. Et, qui dit page de mémoire, dit Gate Array. En effet, comme c'était le cas sur CPC, sur CPC+ c'est toujours le Gai Taré qui s'occupe de tout ce qui est flipping mémoire. Comme toute mémoire vive du CPC qui se respecte, la page I/O ASIC se connecte entre &4000 et &8000.

Délockage

Pour permettre la connexion de cette fameuse page I/O ASIC, les ingénieurs d'Amstrad ont utilisé le bit 5 du port d'adressage du Gate Array qui n'était pas utilisé sur CPC old. Mais, copyright oblige, celui-ci est en quelque sorte crypté. Si on tente de l'utiliser comme ça, sans rien demander à personne, on se fait jeter comme un malpropre car le CPC+ se comporte alors exactement comme un CPC classique…

Avant d'accéder à la page I/O ASIC, c'est comme quand vous voulez sortir en boîte de nuit (hem !), il faut avertir les vieux. Et dans le cas qui nous intéresse, le vieux à avertir, c'est notre fidèle CRTC 6845 auquel il faut envoyer une série de codes par l'intermédiaire de son port &BCxx. Pour employer l'expression consacrée par l'inoubliable Longshot, cela revient à “délocker” l'ASIC. Cette manip' peut se faire très simplement depuis le BASIC.

Listing : délockage en BASIC

Télécharger le programme BASIC.

10 RESTORE 
20 FOR x=0 TO 16:READ a:OUT &BC00,a:NEXT 
30 DATA 255,0,255,119,179,81,168,212,98,57,156,70,43,21,138,205,238 
40 PRINT"ASIC déverrouillé !"

Elle est délockée ta face là ?!

Commutation

Une fois l'ASIC délocké, on peut enfin utiliser le bit 5 du port &7Fxx du Gate Array lequel va nous permettre d'accéder à toutes les spécificités du CPC+. Vous connaissez sûrement déjà toutes les fonctions classiques du Gate Array ; celui-ci dispose de 4 modes (PENR, INKR, RMR et MMR) sélectionés par l'intermédiaire des bits 6 et 7, puis, pour chaque mode, les bits 0 à 4 sont les “data-bits”. Pour plus de détails, allez jeter un coup d'oeil ici.

Le bit 5 n'est pas utilisé sur CPC old. Sur CPC+, un cinquième mode vient se greffer aux autres, on y accède en mettant le bit 7 à 1, le 6 à 0 et le 5 à 1. Ce mode, appelé RMR2, dispose comme pour tous les autres modes, de 5 “data-bits” qui permettent la connexion des ROM de la cartouche et, ce qui nous intéresse ici, la connexion de la page I/O ASIC. Je vous en donne le schéma d'adressage puis nous pourrons passer à quelque chose de plus intéressant.

Le mode RMR2 du Gate Array

Les bits de contrôle sont ceux qui permettent de sélectionner le mode actif du Gate Array, pour le RMR, c'est le mode 2 ; le bit d'extension permet (sur CPC+ uniquement) d'accéder au deuxième jeu d'adressage (RMR2) et les bits de commande contiennent les infos à faire ingurgiter au Gai Taré.

Le numéro de ROM correspond en fait au numéro de ROM physique à commuter ; c'est à dire la plage de 16Ko de la cartouche. Ces ROM sont également accessibles en tant que ROM hautes grâce au mode RMR

Tadadap ! Tagadap ! Dans la cartouche d'orgine livrée avec les CPC+, les ROM physiques disponibles sont :

  • ROM 0 : Firmware,
  • ROM 1 : BASIC,
  • ROM 2 : Vierge (inutilisée),
  • ROM 3 : Amsdos,
  • ROM 4 : Burnin' Rubber ROM 0,
  • ROM 5 : Burnin' Rubber ROM 1,
  • ROM 6 : Burnin' Rubber ROM 2,
  • ROM 7 : Burnin' Rubber ROM 3.

Ne soyez pas intrigués par la connexion de ROM couplée à celle de la page I/O ASIC car, en standard, il faut effectivement connecter la ROM cartouche 0 (le firmware) entre &0000 et &3FFF.

Voilà, j'espère que c'est clair, pour ceux qui bloquent un peu, je résume :

OUT &7F00,&B8 ' Connexion de la page I/O ASIC en &4000
OUT &7F00,&A0 ' Déconnexion de la page I/O ASIC

Tout ceci se fait bien sûr après avoir délocké l'ASIC.

Contraintes

Avant de passer à la pratique, il faut que je vous donne quelques détails supplémentaires. Tout d'abord, il faut savoir que la page I/O ASIC ne se comporte pas à 100% comme une RAM car, si on peut écrire ce qu'on veut où on veut, en revanche tous les octets ne sont pas lisibles : ils sont en lecture seule. Ne soyez pas étonnés, dans certains cas, de ne pas relire ce que vous aviez écrit à une adresse donnée.

Par ailleurs, et c'est très très important, la page I/O ASIC est prioritaire sur toutes les autres ! Les RAM et les ROM seront dans tous les cas masquées par l'ASIC qu'elles aient été connectées avant ou après. Faites très attention à cela car, à titre d'exemple, les ingénieurs de Romantic Robot, lorsqu'ils ont conçu leur Multiface Two pour CPC+, se sont contentés de modifier les sauts en ROM1) et n'ont nullement pensé à l'ASIC ! Résultat : la Multiface Two ne peut pas se connecter lorsque l'ASIC est “on” ! Donc, attention.

Les 4096 couleurs

Basé sur l'article publié dans Quasar CPC numéro 5, CPC plus, par OffseT.

Changeons de sujet et passons à la pratique, au menu : les 4096 couleurs. La palette des couleurs de l'ASIC se situe de &6400 à &6421 sur la page I/O ASIC et chaque couleur est codée par deux octets qui sont eux-mêmes codés comme suit :

Le codage RVB des couleurs de l'ASIC

Voilà, c'est plutôt simple non ? Vous trouverez un petit exemple d'utilisation de la palette CPC+ dans le programme suivant qui utilise également le retard vidéo ASIC afin de faire onduler l'écran.

Listing : table de couleurs & ondulation

Télécharger le listing BASIC.

10 ' 
20 ' Générateur de la table de 
30 '   sinus pour le source 
40 '      assembleur... 
50 DEG 
60 SYMBOL AFTER 256 
70 MEMORY &2FFF 
80 FOR x=0 TO 720 STEP 3.10344828 
90 t=t+1:PRINT INT((1+SIN(x))*7.5); 
100 POKE &3EFF+t,INT((1+SIN(x))*7.5) 
110 NEXT

Listings : raster et ondulation

Télécharger le listing au format Maxam 1.5

;
; Ondulation d'écran pixel par pixel
;   et raster en couleurs RVB CPC+
;     (en Mode 2 uniquement !!!)
;
; Par OffseT pour Quasar CPC 5
;
	Org &3000
	Limit &3eff
	Nolist
 
Table	Equ &3f00
Taille	Equ 232
 
	di		; Reconfiguration
	ld hl,(&38)	; des interruptions
	ld (inter),hl	; pour pouvoir
	ld hl,&c9fb	; synchroniser nos
	ld (&38),hl	; raster et notre
	ei		; ondulation
 
	ld bc,&7fb8	; Connexion de la
	out (c),c	; page I/O ASIC
 
	ld hl,0		; Encre 1 en noir
	ld (&6402),hl	; c'est plus clean
 
;
; Programme principal
;
 
Prog	ld b,&f5	; On adresse le PPI
Synchro	in a,(c)	; pour se synchroniser
	rra		; avec la VBL
	jr nc,synchro	; Hop là !
 
	ei		; On se prépare pour
	halt		; les rasters histoire
	halt		; de commencer en haut
	di		; de l'écran et au début
	ds 30		; d'une ligne
 
	ld hl,table	; On pointe sur la table
	ld b,taille	; des sinus et des couleurs
Loop	ld a,(hl)	; On lit les valeurs
	ld (&6804),a	; en on envoie la sauce
	inc hl		; À la une !
	ld (&6400),a	; À la deux !
	ld (&6420),a	; À la trois !
	ds 44		; Et hop là,
	djnz loop	; c'est fini !
 
	xor a		; On est arrivé en bas de
	ld (&6804),a	; l'écran, on remet tout
	ld (&6820),a	; bien comme il faut pour
	ld (&6800),a	; que ça soit plus joli et
	ld a,(table)	; on fait scroller la table
	ld hl,table+1	; pour que l'ondulation et
	ld de,table	; les rasters ne restent pas
	ld bc,taille-1	; statiques et parcourent
	ldir		; l'écran de bas en haut
	ld (table+taille-1),a
 
;
; Test clavier
;
 
Key	ld bc,&f40e	; On adresse le PPI
	out (c),c	; pour lire la ligne
	ld bc,&f6c0	; 5 du clavier qui
	out (c),c	; contient la barre
	xor a		; espace qui est la
	out (c),a	; touche exit de ce
	ld bc,&f792	; programme.
	out (c),c	; ...
	ld bc,&f645	; ...
	out (c),c	; ...
	ld b,&f4	; ...
	in a,(c)	; C'est lent, très lent !
	ld bc,&f782	; ...
	out (c),c	; ...
	ld bc,&f600	; ...
	out (c),c	; ...
	rla		; Ouf ! Nous voilà
	jp c,prog	; arrivés...
 
	di		; Si espace alors on
	ld hl,(inter)	; s'en va, mais avant
	ld (&38),hl	; on nettoie tout en
	ld bc,&7fa0	; repositionnant les
	out (c),c	; interruptions standard
	ei		; et en déconnectant la
	ret		; page I/O ASIC.
 
;
; Data
;
 
Inter	dw &0000

Les sprites hard

Basé sur l'article publié dans Quasar CPC numéro 8, CPC plus, par OffseT.

Les sprites hard c'est géant ! Comme le laisse suggérer le titre de cette section, nous allons nous familiariser avec les sprites hard. Et comme l'apprentissage par l'exemple est souvent la méthode la plus facile et la plus rapide de comprendre les choses, je vous propose de travailler sur la base des petits programmes qui suivent : nous allons nous amuser à faire bouger les sprites hard !

Le premier programme BASIC envoie simplement la séquence de délockage de l'ASIC ; il suffit donc de l'exécuter une fois après la mise sous tension de votre CPC plus. Le second programme BASIC génère la table de mouvement de sprites hard puis commande leur affichage en appelant le code assembleur.

Les présentations

Tout d'abord, un peu de théorie. Le CPC plus dispose de 16 sprites hard de 16 pixels par 16. Chaque pixel est codé par un octet ; un sprite hard occupe donc 16×16 soit 256 octets en RAM. Mais le plus important ici est que les données relatives à ces sprites sont à une adresse fixe sur la page I/O ASIC, de &4000 à &5000. C'est en fait leur principal défaut : ils ne sont pas indexables en mémoire, et pour modifier le contenu d'un sprite hard… il faut effectivement modifier son contenu alors qu'un simple changement de pointeur d'adresse aurait été bien plus puissant.

Par ailleurs, ces sprites disposent d'une palette indépendante de la palette de l'écran. Cette palette est logée sur la page I/O ASIC de &6422 à &643E alors que la palette de l'écran est codée de &6400 à &6420. Chaque encre est composée de deux octets que l'on peut diviser en trois quartets dont deux sont sur l'octet de poids fort et le troisième est constitué des 4 bits de poids faible de l'octet de poids faible. Chaque quartet indique la quantité de rouge, vert et bleu dont sera composée la couleur de l'encre considérée ; d'où les 4096 couleurs. Vous constaterez de plus, si vous comptez le nombre de words dans la palette des sprites hard et celui dans la palette de l'écran, que cette dernière a 17 encres alors que la précédente n'en compte que 15… C'est normal car la palette de l'écran inclue le border (le dernier word en &6420), et les sprites hard n'ont que 15 couleurs car l'encre 0 correspond au mode transparent !

Pour en revenir maintenant aux positions… On a là aussi un codage en words pour la position en X et pour la position en Y de chaque sprite. Attention, la position est indiquée au pixel mode 2 près quelle que soit la résolution de l'écran. La liste des positions commence en &6000/&6001 avec la position X du sprite 0, ensuite on a en &6002/&6003 la position en Y ; en &6004 le zoom. On reprend ensuite en &6008 pour le sprite 1, etc.. Il y a des octets vierges entre les données des sprites ; il s'agit en fait de doubles de l'octet du zoom.

À propos du zoom, seul le quartet de poids faible est utilisé : les deux bits de poids fort définissent le zoom en X et les deux autres en Y. La résolution la plus fine pour un sprite hard (zoom %0101) est celle du mode 2 (en 15 couleurs donc, ce qui n'est pas mal du tout !) et la résolution la plus faible (zoom %1111) est celle du mode 0 en X et que 4 lignes écran par pixel en Y (les sprites sont alors énormes, mais très moches). Si le zoom en X ou en Y est à zéro, alors le sprite hard est éteint (invisible à l'écran).

Ah ! Je ris de me voir si belle en ce miroir ! Enfin, voici un petit tableau récapitulatif de tout ce que je viens de dire :

Sprite Adresse Position X Position Y Zoom
0 &4000 &6000-01 &6002-03 &6004
1 &4100 &6008-09 &600A-0B &600C
2 &4200 &6010-11 &6012-13 &6014
3 &4300 &6018-19 &601A-1B &601C
4 &4400 &6020-21 &6022-23 &6024
5 &4500 &6028-29 &602A-2B &602C
6 &4600 &6030-31 &6032-33 &6034
7 &4700 &6038-38 &603A-3B &603C
8 &4800 &6040-41 &6042-43 &6044
9 &4900 &6048-49 &604A-4B &604C
10 &4A00 &6050-51 &6052-53 &6054
11 &4B00 &6058-59 &605A-5B &605C
12 &4C00 &6060-61 &6062-63 &6064
13 &4D00 &6068-69 &606A-6B &606C
14 &4E00 &6070-71 &6072-73 &6074
15 &4F00 &6078-79 &607A-7B &607C

Position X : [-256,767] (word) Position Y : [-256,255] (word) Zoom : [0,15] (quartet)

J'allais oublier une dernière chose, les sprites hard passent obligatoirement par dessus l'écran et par dessous le border ; de plus, c'est toujours le sprite de plus petit indice qui “écrase” le sprite d'indice plus grand : c'est-à-dire que le sprite 0 passe devant tous les autres.

L'exemple

Il ne vous reste plus qu'à étudier et lancer les programmes ci-après ; ils font tourner gaiement les 16 sprites suivant une jolie ellipse ; il va de soi que le programme assembleur doit être assemblé en mémoire avant de lancer le deuxième programme BASIC qui y fait appel.

Je vous conseille, pour vous habituer à ces charmants lutins, de tatonner avec des pokes sous BASIC. Faites un ”MEMORY &3FFF”, délockez l'ASIC, connectez-le (OUT &7F00,&B8) et le tour est joué ! Vous verrez, c'est enfantin.

Listings

Table de sinus et animation des sprites

Télécharger le programme BASIC.

10 ' 
20 '  Générateur de la table de 
30 ' mouvements des Sprites Hard 
40 ' 
50 SYMBOL AFTER 256 
60 MEMORY &3FFF 
70 DEFINT a-z 
80 adr=&9000 
90 pas=2 
100 ampx=(639-64)/2 
110 ampy=(199-64)/2 
120 decx=0 
130 decy=0 
140 DEG 
150 FOR x=0 TO 360-pas STEP pas 
160 a=(COS(x)+1)*ampx+decx 
170 b=(SIN(x)+1)*ampy+decy 
180 POKE adr,a\256 
190 POKE adr+1,a MOD 256 
200 POKE adr+2,b\256 
210 POKE adr+3,b MOD 256 
220 adr=adr+4 
230 NEXT 
240 POKE adr,255 
250 ' 
260 ' Initialisation du zoom des 
270 '  sprites hard et mise en 
280 '       mouvement... 
290 ' 
300 CALL &8000:OUT &7F00,&B8:FOR x=&6004 TO &607C STEP 8:POKE x,15:FOR t=0 TO 100:NEXT t,x:OUT &7F00,&A0 
310 CLEAR INPUT 
320 WHILE INKEY$<>" ":CALL &8000:FRAME:WEND 
330 OUT &7F00,&B8:FOR x=&6004 TO &607C STEP 8:POKE x,0:NEXT:OUT &7F00,&A0

Affichage des sprites hard

Télécharger le listing au format Maxam 1.5

;
; Gestion du mouvement des sprites hard
;
 
	Org &8000	; Implantation en &8000
	Limit &8fff	; ou comme vous voulez
	Nolist		; du moment que ce n'est
			; pas entre &4000 et &7FFF
Table	Equ &9000	; Adresse où lire la table
			; de mouvement.
	ld bc,&7fb8	; Connexion de la page
	out (c),c	; I/O ASIC en &4000
 
Move	ld ix,&6000	; IX pointe sur le tableau
			; ASIC des positions des
			; sprites hard.
	ld iy,spr	; IY pointe sur le tableau
			; d'adresses de gestion du
			; mouvement.
	ld de,8		; DE est l'incrément pour
			; passer de l'adresse ASIC
			; d'un sprite au suivant.
	ld b,16		; On va faire la boucle 16
LoopMov	ld h,(iy+1)	; fois pour les 16 sprites
	ld l,(iy+0)	; HL reçoit la première
	ld a,(hl)	; adresse et A l'octet de
	cp 255		; poids faible de la position X
	call z,rectif	; On teste si on est en fin de
	ld (ix+1),a	; table sinon on poke A sur la
	inc hl		; page I/O ASIC... On passe
	ld a,(hl)	; ensuite @ l'octet de poids
	ld (ix+0),a	; fort et on le met @ sa place
	inc hl		; sur la page I/O ASIC...
	ld a,(hl)	; On fait de meme pour la
	ld (ix+3),a	; position en Y du sprite
	inc hl		; en lisant l'octet de poids
	ld a,(hl)	; fort puis l'octet de poids
	ld (ix+2),a	; faible et en les pokant sur
	inc hl		; la page I/O ASIC...
	ld (iy+1),h	; On remet ensuite le tableau
	ld (iy+0),l	; d'adresses @ jour pour le
	inc iy		; prochain passage et on
	inc iy		; pointe sur l'adresse pour le
	add ix,de	; sprite suivant...
	djnz loopmov	; On boucle.
 
	ld bc,&7fa0	; On déconnecte la page
	out (c),c	; I/O ASIC et on revient
	ret		; au BASIC...
 
Rectif	ld hl,table	; Si on est en fin de
	ld a,(hl)	; table alors on repointe
	ret		; au début.
 
;
; Table d'adresses de gestion du mouvement
;
 
Spr	dw table,table+16,table+32,table+48
	dw table+64,table+80,table+96,table+112
	dw table+128,table+144,table+160,table+176
	dw table+192,table+208,table+224,table+240

La rupture d'écran

Basé sur l'article publié dans Quasar CPC numéro 14, CPC plus, par OffseT.

La rupture d'écran ASIC

(à développer)

Si la rupture d'écran sur CPC old impliquait quelques manipulations frauduleuse, il n'en est rien sur CPC+ puisque l'ASIC dispose purement et simplement du registre SPLT (&6801).

Ce registre permet - couplé au registre SSA (&6802-&6803) qui contient l'offset (registres 12 et 13 du CRTC) du nouvel écran - de passer commande d'une rupture d'écran à la ligne désirée. De plus, ce registre se comporte exactement comme le registre PRI dont nous parlerons plus bas ; c'est-à-dire que l'on peut générer autant de ruptures qu'on le désire et que celles-ci peuvent se trouver au-delà de la ligne 256. Toutefois, une restriction (qui est d'ailleurs commune avec le registre PRI) : il est impossible de programmer la ligne 256 car celle-ci correspond à la ligne 0, soit un registre chargé à 0, ce qui signifie pour l'ASIC que la fonction est désactivée… tant pis, on doit faire avec. Pour le reste, mettre le registre à la valeur “n” implique une rupture programmée aux lignes “n” et “n+256” (sous réserve que l'écran visible soit assez haut bien entendu).

La rupture d'écran "old" et l'ASIC

(à saisir et à enrichir)

Le retard vidéo ASIC

Basé sur l'article publié dans Quasar CPC numéro 11, CPC plus, par SNN.

Monsieur Propre et autres Canard WC, voici ma première initiation au CPC+ !

Saviez-vous que j'ai entre les mains un document d'Amstrad PLC classé “Compagny Confidential” avec dessus une explication détaillée des registres du CPC+ ? C'est fou ce qu'Amstrad sait garder les secrets. Prenons un exemple : en Septembre 1989, Computer Express, un canard d'outre-Manche, annonce la sortie d'une nouvelle gamme de CPC (les ”+” donc). Ce secret était tellement bien gardé que Marion Vannier (Amstrad France) elle-même l'ignorait alors que le journaliste vulgus, lui, le savait… et elle a aussitôt démenti2).

Mais je ne suis pas ici pour faire de la “Culture CPC”. Je vais vous parler de plein de trucs plus du tout théoriques ! On démarre avec…

Le scroll hard-soft au pixel

Bip ! Bip ! Rien que ça !

Et c'est tellement simple que je ne donnerai pas d'exemple. Juste le système. En fait, il est basé sur le décalage en X ou en Y de l'écran par un seul LD (je vois des possesseurs de CPC classiques dubitatifs). Remarque importante : ce scrolling n'altèrera pas votre rupture ASIC (ce qui est bien sympathoche pour une barre des scores).

Scrolling horizontal

Pour un scroll soft en X, on utilisera les quatre premiers bits du registre ASIC SSCR (notés D0 à D3) qui n'est autre que l'adresse &6804 du RMR23).

Donc, si on joue sur 4 bits, on a normalement 16 positions intermédiaires avant d'avoir à décaler vraiment l'écran d'un word (2 octets) par le système le plus classique du monde : l'offset vidéo (aka les registres 12 et 13 du CRTC). Or… non. Du moins pas en modes 1 et 0. Comme le SSCR fait tourner les bits, on aura dans ces cas là un infâme pâté de couleurs comme rarement j'ai pu en produire avec OCP4). Pour mieux comprendre, en mode 0, faites tourner par un RRA (ou RLA, je suis pas raciste) tous les bits de tous les octets de l'écran.

Compris ? Bon. Alors la solution, c'est…

Mode 2 Mode 1 Mode 0
Décalage au pixel 1 2 4

Mais si on compte, pour le mode 0, on ne fera que 4 décalages par word… soit 4 pixels pour 2 octets ce qui ô miracle, correspond bien à la réalité. Putain que je suis bon.

Bon, dès qu'on a fait 4 décalages, on scrolle tout ça avec l'offset, on recommence et moi je vais dormir.

Ah ! Non ! Il reste le vertical !

Scrolling vertical

Ce sont les bits 6 à 4 (oui, oui, 3 bits seulement) qui feront scroller vers le haut. Exo de math : calculer 2 exposant 3. Si ça fait 8, lire la suite. Sinon, retourner lire les rubriques de Tony. Vous avez trouvé 8 ? Bravo. Huit donc, ce qui, re-ô miracle, re-correspond à la hauteur d'une ligne texte simplex' (sous réserve bien sûr d'avoir le registre 9 du CRTC à 7), facilitant ainsi le scroll vertical hard.

Mais alors bon, c'est bien joli, mais le bit 7 ? Hein ? Même Longshot n'en a pas parlé5). En fait, ce bit est un bit “sans-moi-c'est-moche” qui cache l'effet inesthétique du scroll horizontal par SSCR. Laissez-le à 1, et on n'en parle pas plus longtemps !

Vaaaouuuuaaaooouumm !

Listing : scrolling hard-soft ASIC

Cet article ne comportait pas d'exemple, il serait judicieux d'en ajouter un.

Les interruptions de l'ASIC

Basé sur les articles publiés dans Quasar CPC numéro 10 et numéro 14, CPC plus, par OffseT.

Peut-être puis-je vous délester de quelque fardeau ? Comme vous l'avez peut-être déjà lu ici6), en standard, tous les CPC sont en mode non vectorisé (IM 1) ; pour être plus précis, c'est le Z80 qui est réglé sur ce mode au boot. Lorsqu'il est dans ce mode, à chaque fois qu'une requête d'interruption est générée par un périphérique, le Z80 interrompt l'exécution du programme en cours, fait un DI (pour ne pas être dérangé par une autre interruption), puis un RST &38 (sauvegarde de PC dans la pile et saut en &0038). Ainsi, il va exécuter la routine située en &0038 (souvent un vecteur, JP &B941 pour le système), avant de revenir au programme principal à la rencontre d'un RET.

Dans ce mode, si on veut gérer ses propres interruptions sans passer par le système, il suffit donc de mettre la routine d'interruption de son choix en &0038. Ceci est bien pratique à utiliser, mais ça impose pas mal de contraintes. Par exemple, lorsqu'on passera en overscan, on ne pourra utiliser un écran logé entre &0000 et &4000 qu'en bidouillant dans tous les sens vu que notre vecteur d'interruption se retrouve en plein milieu de l'écran. C'est ainsi que que l'utilisation du mode vectorisé (IM 2) sur les CPC classiques trouve son intérêt, puisqu'on peut alors choisir le vecteur d'interruption sans passer par &0038 (lisez l'article sur les interruptions à ce sujet).

Vive le mode IM 2 !

Sur les CPC plus, la situation est totalement différente ! En effet, sur les CPC classiques, il n'y avait qu'un seul périphérique générant des interruptions : le Gate Array. En revanche, sur CPC plus, on a quatre générateurs possibles : les interruptions des contrôleurs DMA des trois canaux sonores (via leur instruction INT, voir ci-dessous), et celle du Gate Array émulé par l'ASIC (ou l'interruption raster programmable, PRI, suivant le mode de fonctionnement). Imaginez la panique que c'est en mode IM 1 ! Lorsqu'une routine est appelée en &0038, comment savoir qui a généré l'interruption pour faire le traitement adapté ?!

Il est des lieux où il vaut mieux n'avoir à s'acquitter de rien Le petit blond à lunette va vous rétorquer qu'il suffit de consulter le registre DCSR de l'ASIC dont voici l'architecture :

Le registre DCSR de l'ASIC :

Le registre DCSR de l'ASIC

Certes, mais il y a tellement plus simple et élégant ! En effet, si vous regardez le schéma suivant, vous constaterez qu'en mode vectorisé, le vecteur d'interruption dépend du périphérique qui a généré l'interruption…

Le registre IVR de l'ASIC et le calcul d'un vecteur d'interruption associé

Concrêtement, ça signifie que le saut consécutif à la requête d'interruption se fera alors directement à l'adresse voulue pour un périphérique donné ! Alors que sur CPC classique seul le registre I du Z80 était disponible pour préciser l'adresse du vecteur d'interruption (laissant ainsi 7 bits dans le flou, le bit 0 de l'adresse du vecteur d'interruption étant toujours à 1 en pratique), sur CPC plus, l'ASIC nous offre le registre IVR qui nous permet de préciser les bits 3 à 7 de l'adresse du vecteur d'interruption. Outre ces bits issus de l'IVR, vous pouvez constater que les bits 1 et 2 dépendent directement du périphérique ayant généré l'interruption (d'où les sauts automatiques aux bonnes adresses) alors que le 0 est toujours à 0 (d'où une "totale" incompatibilité CPC - CPC plus en ''IM 2'').

Les acquittements

Il ne vous aura pas échappé que le registre IVR permet également de choisir le mode d'acquittement. Ah, “c'est quoi donc un acquittement”, demande notre inculte petit blond à lunettes. Eh bien c'est la façon dont vous direz au périphérique ayant généré l'interruption qu'il peut se rendormir et que vous avez bien pris en compte sa requête. Sur CPC classique la question ne se pose pas car le Gate Array n'attend pas d'acquittement spécifique ; mais sur CPC plus, vous avez le choix entre acquittements spécifiques et universels via le bit 0 du registre IVR.

En mode d'acquittement universel, pas de problème, on ne se pose pas de question, on plante juste un EI une fois l'interruption traitée ça roule ! En effet, dans ce mode le périphérique ne maintient pas sa requête d'interruption (on a le même comportement avec le Gate Array sur CPC classique). En mode d'acquittement spécifique, c'est un peu plus évolué puisqu'on doit mettre à 1 le bit qui va bien dans le registre DCSR pour que notre périphérique comprenne qu'on a bien pris en compte sa requête. Tant que l'ont aura pas fait cette manipulation, il maintiendra la requête d'interruption et tout EI provoquerait un empilement d'interruption ! La tableau suivant résume la gestion des acquittements en fonction du mode choisi.

Les modes d'acquittement :

Interruption Acquittement d'interruption
Raster / Gate Array EI ou mise à zéro du diviseur d'interruption
Spécifique (bit 0 IVR = 1) Universel (bit 0 IVR = 0)
DMA Son 2 Bit 4 de DCSR à 1 EI
DMA Son 1 Bit 5 de DCSR à 1 EI
DMA Son 0 Bit 6 de DCSR à 1 EI

Si vous êtes bien au fait du fonctionnement des interruptions, l'avantage de l'acquittement spécifique ne vous aura pas échappé : dans ce mode le Z80 ne peut pas rater d'interruption puisque le périphérique maintient sa requête jusqu'à ce qu'elle soit explicitement validée. En revanche, en aquittement universel, si le Z80 était sous DI lors que l'interruption, celle-ci est perdue à jamais… ce qui peut parfois se révéler critique.

Outre cet inconvénient de perte potentielle d'interruptions, en mode d'acquittement universel, les bits 4, 5 6 et 7 du registre ASIC DCSR sont alors gelés… Cela ne devrait en principe pas poser de problème vu qu'en mode vectorisé on n'a pas à se soucier du périphérique qui a généré l'interruption puisque le saut se fait automatiquement à la bonne adresse…

Le bug DMA 0 vs PRI

Parfois les interruptions vont dans le mur ! Oui mais voilà, c'était trop beau, les ingénieurs qui ont pondu les CPC plus n'ont pas pu résister à l'envie d'introduire un bug ! Executez le petit programme que je vous ai préparé et vous comprendrez tout de suite. Normalement, puisqu'on n'utilise pas les DMA son et encore moins leur générateur d'interruptions, seul le Gate Array (émulé par l'ASIC) fait des requêtes d'interruption tous les 300ème de secondes ; donc notre programme devrait sauter en ”Raster” régulièrement… De ce fait on devrait retrouver un 255 en &0000. Tapez simplement la ligne suivante sous BASIC après avoir exécuté le programme assembleur :

WHILE 1:? PEEK(0);:WEND

Horreur ! On a à peu près 70% de 0 et 30% de 255 ! Le Z80 confond les interruptions Gate Array et les interruptions DMA Son 0. J'ai fait tout un tas d'essais, il confond de même les interruptions DMA 0 et les interruptions Gate Array (une interruption DMA 0 saute de temps en temps en ”Raster” également). Je me suis alors dit, mettons nous en mode “Raster Interrupt” (valeur non nulle pour le registre PRI de l'ASIC) ; eh bien on observe exactement les mêmes phénomènes que l'on soit en mode “Raster Interrupt” ou en mode émulation Gate Array…

Le mode d'acquittement spécifique devient dès lors un incontournable. Outre le fait qu'il empêche qu'on puisse rater des interruptions, il offre également la possibilité de consulter les bits 4, 5, 6 et 7 du registre DCSR. Or, en les testant, on peut rediriger une interruption qui aurait pris le mauvais chemin en tout début des routines d'interruption ”Raster” et ”Son0”. Certes ça rajoute un peu de lourdeur, mais ça reste somme toute parfaitement gérable.

Enfin, n'oublions pas qu'à côté de ça, on peut placer notre vecteur d'interruption où on veut et que les interruptions des DMA 1 et 2 marchent très bien ! Dès lors, si elles sont suffisantes, le bug de l'interruption DMA 0 est sans conséquence.

Listing : IM 2 sur CPC plus

Télécharger le listing au format Protext 6.10 (ASCII ISO-Latin-1).

;           Gestion du mode d'interruption
;         vectorisé sur 6128 plus et 464 plus
; (mise en évidence du bug du vecteur d'interruption)
;
;                Par OffseT of Futurs'
;              pour Quasar CPC numéro 10
;
;      Attention : pensez à délocker l'ASIC avant
; de lancer ce prog car sinon c'est le plantage assuré !!!
;
 
	Org &8000		; On commence ici !
	Limit &9fff		; On s'arrêtera là !
	Nolist
 
Vector	Equ &a0			; Octet de poids fort
				; de l'adresse des
				; vecteurs d'interruption
Adr	Equ vector*&100
IVR	Equ 0			; Valeur du registre IVR
 
DMA0	Equ adr+ivr+%100	; Déclaration des adresses
DMA1	Equ adr+ivr+%010	; des vecteurs d'interruption
DMA2	Equ adr+ivr+%000	; des DMA son 0, 1 et 2 et
RAST	Equ adr+ivr+%110	; de l'interruption raster.
 
	di			; On interdit les
				; interruptions pour
				; ne pas avoir de surprise !
	ld bc,&7fb8		; On connecte la page
	out (c),c		; I/O ASIC en &4000-&7FFF
				; (comme d'habitude)
	ld a,vector		; On charge l'octet de
				; poids fort du vecteur
	ld i,a			; d'interruption dans I.
	ld a,ivr		; Chargement du registre IVR
	ld (&6805),a		; sur la page I/O ASIC
	ld bc,&7fa0		; Déconnexion de la page
	out (c),c		; I/O ASIC
	ld hl,son0		; Initialisation des vecteurs
	ld (dma0),hl		; d'interruption aux adresses
	ld hl,son1		; calulées lors de la
	ld (dma1),hl		; déclaration des constantes.
	ld hl,son2		; (DMA0)=Son0
	ld (dma2),hl		; (DMA1)=Son1
	ld hl,raster		; (DMA2)=Son2
	ld (rast),hl		; (RAST)=Raster
	im 2			; Passage en mode vectorisé
	ei			; On revient au système comme
	ret			; si de rien n'était...
 
;
; Vecteur d'interruption
;
 
son0	push af			; On poke 0 en &0000
	xor a			; pour dire qu'on a été
	ld (0),a		; appelé puis on lance
	pop af			; le vecteur d'interruption
	jp &b941		; du système
 
son1	push af			; De même mais on poke 1
	ld a,1			; en &0000
	ld (0),a		; ...
	pop af			; ...
	jp &b941		; ...
 
son2	push af			; Idem avec 2 en &0000
	ld a,2			; ...
	ld (0),a		; ...
	pop af			; ...
	jp &b941		; ...
 
Raster	push af			; Idem avec 255 en &0000
	ld a,255		; ...
	ld (0),a		; ...
	pop af			; ...
	jp &b941		; ...

Les interruptions raster

Je vais t'interrompre ta trame moi, tu vas voir ! Celles-ci sont pilotées grâce au registre PRI ; étudions-le en détail. Lorsqu'il est à une valeur nulle (ce qui est la valeur par défaut), l'ASIC est alors en mode compatibilité Gate Array et génère une interruption tous les 300ème de seconde comme le ferait un CPC classique… enfin, pas tout à fait ! En effet, elles arrivent une microseconde plus tard par rapport aux CPC classique ; de manière générale ça ne pose aucun problème, mais dès lors que l'on souhaite faire des split-rasters ou de la rupture verticale, ce sont toutes les synchros qui sont à adapter.

Lorsque PRI est non nul, on demande alors à l'ASIC de générer une interruption dite “Raster” à la ligne spécifiée. Ce mode est très pratique pour faire des synchronisations précises avec l'affichage et poser des rasters, des SPLT ou intervenir sur les sprites hard. De plus, il est important de préciser que bien que le registre PRI soit unique, vous pouvez générer autant d'interruptions raster que vous le désirez pendant un balayage vidéo puisque ce registre est consulté à chaque HBL par l'ASIC. Ainsi, si suite à une interruption à la ligne x vous programmez le registre PRI pour une interruption pour la ligne x+n, celle-ci aura lieu. En outre, il est possible de générer des interruptions au-delà de la ligne 255, même si le registre PRI n'est effectivement que 8 bits. Si le registre est programmé pour la ligne x, une interruption raster sera générée pour toutes les lignes x à 256 lignes près. Un petit exemple pour le petit blond à lunettes : si le registre PRI est programmé à 10, nous allons nous retrouver avec une interruption à la ligne 10 mais aussi à la ligne 266 (si l'écran visible est assez haut, cf. registre 6 du CRTC).

Les interruptions DMA

Il y a peu à en dire ; à chaque fois qu'un DMA rencontre l'instruction INT, il génère une interruption dite “interruption DMA”. Comme il y a 3 DMA, nous avons donc 3 interruptions DMA possibles : DMA 0, DMA 1 et DMA 2 comme vu dans les registres IVR et DCSR. Ces instructions d'interruption n'ont par ailleurs rien de particulier et permettent simplement d'invoquer des actions Z80 durant l'exécution des différents DMA audio ; l'utilisation des DMA audio est détaillée ci-après.

À noter que ces interruptions sont aussi capricieuses que les interruptions “raster” lorsque la configuration du CRTC s'écarte des conditions nominales.

On ne sait jamais quand une interruption peut nous tomber dessus !

Les DMA audio de l'ASIC

Basé sur les articles publiés dans Quasar CPC numéro 10 et numéro 20, CPC plus, par OffseT et Zik.

Cet article est là pour détailler le fonctionnement des canaux DMA son du CPC plus. Toutefois, nous ne traiterons pas ici de la gestion des interruptions déclenchées par les DMA puisqu'elle a été vue précédemment.

Passez commande

Pour les anglophobes sévères, je précise que DMA (Direct Memory Access) signifie “accès direct à la mémoire”. C'est-à-dire qu'un contrôleur (contenu dans l'ASIC) est capable d'accéder à la mémoire du CPC (en l'occurrence il s'agit de lecture en RAM centrale) pour transférer des données avec un périphérique (dans le cas présent vers le processeur sonore, le PSG).

Ce transfert de données se fait sans l'intervention du microprocesseur. Il est nécessaire d'écrire les données à transférer dans un format compréhensible par le contrôleur DMA. Celui-ci est assez évolué sur notre cher CPC plus puisqu'il lit et interprète en fait des commandes (ou instructions, comme vous préférez). En voici la liste :

Instruction Codage Description
LOAD R,D &0RDD Charge la valeur de l'octet D dans le registre R du PSG.
R:0 à 15 et D:0 à 255.
PAUSE N &1NNN Attend N fois l'unité de pause.
N:0 à 4095.
PAUSE 0 est équivalent à NOP.
REPEAT N &2NNN Initialise le compteur de boucle à N (nombre de bouclages).
N:0 à 4095.
REPEAT 0 est équivalent à REPEAT 1.
NOP &4000 No OPeration.
LOOP &4001 Boucle.
Si le compteur de boucle est différent de zéro, alors il est décrémenté et on retourne à l'instruction suivant le REPEAT.
INT &4010 Génère une interruption DMA.
STOP &4020 Fin d'AY-liste.
Mise à 0 du bit correspondant du registre DCSR (bit 0, 1 ou 2 selon le DMA en cours).

Comme vous pouvez le voir dans le tableau précédent, toutes les instructions sont 16 bits. De plus, elles doivent obligatoirement être alignées sur des adresses paires et poids faible en tête (dans le sens des adresses croissantes, c'est-à-dire en little endian). Une succession d'instructions DMA son est appelée AY-liste. Toute AY-liste doit se terminer par l'instruction STOP.

A chaque HBL, une instruction est exécutée. Donc, toutes les 64µs en standard (15625Hz). Le CPC plus dispose de trois canaux DMA son indépendants, autant que de canaux sonores, certainement pas par hasard ! Mais tous les canaux DMA ont accès à n'importe quel registre du PSG (instruction LOAD). Un canal DMA n'est ni limité ni lié à un canal sonore particulier. Pour le détail des registres du PSG, je vous invite à aller jeter un coup d'oeil ici.

Les DMA c'est mortel ! Voyons maintenant les registres de l'ASIC qui servent à contrôler les canaux DMA :

DMA0 &6C00-&6C01 SAR0 : adresse d'AY-liste du canal 0
&6C02 PPR0 : unité de pause du canal 0
DMA1 &6C04-&6C05 SAR1 : adresse d'AY-liste du canal 1
&6C06 PPR1 : unité de pause du canal 1
DMA2 &6C08-&6C09 SAR2 : adresse d'AY-liste du canal 2
&6C0A PPR2 : unité de pause du canal 2

SAR : 16 bit Source Address Register
PPR : 8 bit Pause Prescaler Register




Registre DCSR :

Description détaillée du registre DCSR de l'ASIC

Tout ça est assez clair. Pour utiliser un DMA, il suffit d'indiquer l'adresse de l'AY-liste dans le registre SAR (le bit 0 est ignoré). Si l'AY-liste utilise l'instruction PAUSE, il faut aussi mettre à jour PPR. L'exécution du DMA commence quand on met le bit correspondant de DCSR à 1 (bit 0, 1 ou 2).

L'exécution stoppe dans le cas d'un reset du CPC (!), après une instruction STOP ou si l'on remet le bit de DCSR à 0. Par ailleurs, ce même bit peut-être lu, il indique alors si une AY-liste est en cours d'exécution par le DMA considéré.

Les registres SAR et PPR ne peuvent pas être relus, mais l'adresse de l'AY-liste (oui SAR, il y en a qui suivent) est bel et bien incrémentée à chaque lecture d'instruction. En conséquence, quand un DMA est stoppé, si vous le relancez (en écrivant dans DCSR) sans avoir touché au registre SAR, il enchaînera sur la suite de la mémoire. Ça peut être pratique, mais aussi source de bugs : c'est donc à savoir absolument (interrogation écrire au prochain meeting) ! Marquez simplement sur vos notes de cours le cas le plus courant : “l'instruction STOP laisse le pointeur d'adresse sur l'instruction suivante”.

Le fonctionnement général étant esquissé, passons dès lors à une description plus avancée des fameuses instructions qui composeront délicatement vos subtiles AY-listes futures.

Bouclez-là

Quitte à frustrer les ardeurs de certains à la vue des caractéristiques alléchantes des DMA en question, je commencerai par un triste aveu : on ne peut pas imbriquer les boucles ! Donc, c'est une seule à la fois ; une instruction REPEAT suivant un autre REPEAT sans qu'un LOOP ne soit intercalé, en plus de dénoter de votre lamentable style de programmation porcin, sera tout à fait vain. Le premier est inutile et c'est bien au second que le bouclage aura lieu, le nombre de fois spécifié en cette même instruction. Ceci est valable y compris pour l'instruction REPEAT 0 qui n'est donc en vérité pas tout à fait équivalente à NOP.

Notez par ailleurs qu'en aucun cas le DMA ne modifie la mémoire du CPC. L'ASIC utilise poliment des registres internes. Je pense en particulier aux compteurs de boucle.

Le charmant schéma suivant illustre avec une clarté que mes phrases saugrenues auraient du mal à atteindre à quel endroit le bouclage s'effectue :

Fonctionnement des boucles des AY-listes

Lors de l'exécution d'une boucle, l'AY-liste est bien entendu relue à chaque itération. Il est donc possible de la modifier au fur et à mesure, les changements seront pris en compte. Par contre, le schéma montre bien que le REPEAT n'est pas relu, on ne peut donc pas changer le nombre d'itérations une fois dans la boucle.

Connaître l'endroit du bouclage est important. Dans l'exemple précédent, si l'on veut que le registre 0 reste aussi longtemps à la valeur &0F qu'à la valeur &00, il faut ajouter un NOP après le premier LOAD &080F… ou alors retirer celui qui suit le second LOAD.

La paramètre de l'instruction REPEAT donne le nombre de fois que le contenu de la boucle est exécuté. De plus, vous noterez que REPEAT 0 est en fait équivalent à REPEAT 1.

Prenez la pause

Cheese! L'instruction PAUSE est très pratique, elle permet de générer une attente de 64µs à plus de 67s (encore une fois, avec une fréquence vidéo de balayage horizontal standard). De plus, elle permet souvent un gain de mémoire considérable. Par exemple, pour la reproduction d'un sample, si plusieurs échantillons consécutifs (plus de 2 précisément) ont la même valeur, il est avantageux de remplacer le tout par une séquence LOAD/PAUSE. Seulement deux instructions, n'est-ce pas merveilleux ?

Comme vous l'avez très bien compris en lisant le tableau listant les instructions7), la durée de la pause dépend du paramètre mais aussi de l'unité de pause.

L'unité de pause est une valeur 8 bits (0 à 255) stockée dans le registre PPR du canal. Son unité est la période de la HBL (une fois de plus, 64µs en standard, je la note PHBL). La durée d'une instruction PAUSE N sera :

  • Si N différent de 0 : (PPR + 1) x N x PHBL
  • Si N vaut 0 : PHBL

Pour toute valeur de PPR, PAUSE 0 est équivalent à NOP (ce qui est logique vu le fonctionnement des DMA).

Notez enfin qu'un changement de PPR en cours d'exécution d'une AY-liste est pris en compte immédiatement. Si ce changement a lieu pendant l'exécution d'une instruction PAUSE, même si la valeur de PPR est identique ou supérieure à celle précédemment programmée, la PAUSE passe à l'itération suivante… esssplication :

Fonctionnement des pauses des AY-listes

Par conséquent, si vous voulez que votre changement de PPR passe inaperçu, il vous faut le faire pendant la dernière ligne vidéo d'une itération : bon courage ! D'un autre côté, faire de telles manipulations est assez farfelu. Mais maintenant au moins, vous savez comment le DMA réagit, il n'y a pas de risque d'overflow, vous voilà rassurés !

1 + 1 = ?

1+1=10 ! J'ai bon ? Je profite de ce début de paragraphe pour vous dire que le bit 15 est toujours ignoré, pour toutes les instructions. Voilà, je ne savais pas où le caser, c'est fait.

Cumul d'instructions &4xxx

Les différentes documentations officielles qu'on peut lire sur le DMA son du CPC plus nous disent qu'il est possible de cumuler les instructions &4xxx. Certes, mais pas toutes. Ce qu'il faut savoir tout d'abord, c'est que la plupart des bits sont indifférents dès lors qu'on utilise une instruction &4xxx. Voici :

Instruction &4xxx : ?100 ???? ??ab ???c (? = bit indifférent)

De ceci, il est simple de déduire que &CFCF sera équivalent à &4001. Mais bon, n'utilisez pas ça, c'est mal !

Si vous ajoutez à cela que le bit 0 au moins est ignoré lors d'une instruction STOP (&4021 ne boucle pas) et si vous supposez comme moi que c'est également le cas pour INT (ok j'avoue, je n'ai pas testé ce dernier cas), il reste que le seul cumul valide est &4030 = INT + STOP.

Cumul d'instructions... et sinon ?

Qu'en est-il du cumul d'instruction autres que &4xxx ? Eh bien je vais maintenant vous faire part des résultats des recherches toutes récentes du laboratoire Zik Institute en matière de cumul non documenté (et donc par définition, à ne pas utiliser !). Les personnes sensées peuvent sauter cette partie.

  • Le plus intéressant : &3NNN = PAUSE &NNN suivi de REPEAT &NNN. Cependant, la documentation Arnold 1.5 dit à ce sujet ”3xxxh (reserved) : Do not use”. Tirez-en vos conclusions.
  • 0110 xxxx xxxx xxx1 (cumul LOOP et REPEAT) :
    • place la valeur xxxx xxxx xxx1 dans le compteur de boucle,
    • saute au pointeur de boucle courant (c'est-à-dire après la dernière instruction REPEAT rencontrée précédemment),
    • stocke le nouveau pointeur de boucle (le prochain LOOP sautera à l'instruction suivant le &6003 dans l'exemple suivant).

      Par conséquent, pour tout NNN (y compris 0), l'AY-liste suivante génère deux bips aigus suivis de 3 graves (!) :
      &073F,&0000,&0100,&2NNN,&073E,&0080,&080F,&1400,&0800,&1FFF,&6003,&073E,&00EF,&080F,&1400,&0800,&1FFF,&4001,&4020
  • 0110 xxxx xx1x xxxx (cumul REPEAT et STOP) :
    • met à jour le compteur de boucle (à la valeur xxxx xx1x xxxx),
    • stocke le nouveau pointeur de boucle,
    • stoppe le DMA.

Si vous n'avez rien compris, ça n'est vraiment pas grave, mon moment de folie est terminé.

Foire aux détails

Encore quelques petites chose à savoir. Je m'excuse, cet article est aussi déstructuré que ma fatigue est grande.

Réécrire 1 dans le bit DCSR d'un DMA pendant qu'il est déjà actif est sans effet.

On peut faire des sauts d'AY-liste en écrivant dans le registre SAR (sans toucher à DCSR) alors que le DMA fonctionne (bit à 1 dans DCSR).

L'instruction REPEAT (&2xxx), tel un shampooing moderne, a deux actions. Elle charge le compteur de boucle avec la valeur spécifiée et aussi, elle stocke l'adresse de l'instruction suivante pour permettre de réaliser le bouclage.

Une instruction LOOP (&4001) rencontrée alors que le compteur de boucle vaut déjà 0 est sans effet.

Du fait du fonctionnement des instructions de bouclage, on peut réussir à faire des AY-listes qui ne font pas la même chose à chaque lancement. Je vous laisse chercher (indice : &4001, … &2xxx, &4020) ! C'est assez amusant (remarquez, il m'en faut peu).

Une instruction PAUSE n'est pas interruptible. Ceci peut également avoir des effets surprenants si on n'y prend pas garde.

La rupture verticale au service du DMA

Comme expliqué plus haut, les DMA sont intimement liés à la notion de HBL : ils sont donc synchonisés avec la vidéo. Que se passerait-il si on modifiait la fréquence de balayage de notre écran ? Eh bien figurez-vous que ça marche ! Pour ralentir, on est un peu limité car il est difficile de sauter des HBL sans sacrifier notre balayage vidéo, et ça n'a de toute manière pas grand intérêt vu qu'on peut faire attendre les DMA grace à l'intruction PAUSE. En revanche, si on veut accelérer la vitesse de nos DMA, aucun problème, il nous suffira de faire des ruptures verticales générant autant de HBL par ligne qu'on le souhaite ! Ok, ça n'est pas forcément très élégant ni même souhaitable… mais c'est possible, et si vous coupez simplement vos lignes en deux avec une rupture verticale au milieu, vous montez à 31,2kHz. C'est dit.

Qu'est-ce que je fais là ?

Timing serré

Si vous êtes intéressés par le fonctionnement détaillé des DMA, au niveau timing notamment, vous pouvez lire le document Arnold 1.5 que vous pouvez trouver sur le site de Kevin Thacker. D'ailleurs, ce site regorge de documents intéressants (même si certains comportent quelques erreurs).

Ci-après, vous trouverez un programme qui illustre ce dont le DMA est capable (avec uniquement des instructions tout à fait légales, j'insiste). Le premier exemple génère des sons carrés (de rapport cyclique différent de 1/2 tant qu'à faire). Le second exemple joue des notes par le biais normal, il montre une utilisation subtile et délicate des boucles et pauses. Il démontre par ailleurs que le canal son n'a rien à voir avec le canal DMA puisqu'il utilise le DMA2 et joue des sons sur les canaux A et B. Quel magnifique exemple en somme !

Sur ce, je vous laisse en vous rappelant tout de même qu'en aucune façon le Quasar Net n'est sponsorisé par une marque d'aspirine.

Listing : utilisation des DMA

Télécharger le listing au format Maxam 1.5

; Programme d'exemple d'utilisation
;    des canaux DMA son du CPC+
;
;   Zik pour Quasar CPC 20 (2002)
;
	Org &8000 ; où vous voulez en dehors de &4000-&7fff
	Limit &80ff
 
	Nolist ; pour Maxam
 
DMASAR0 equ &6c00 ; adresse AY-liste
DMASAR1 equ &6c04
DMASAR2 equ &6c08
DMAPPR0 equ &6c02 ; adresse unité de pause
DMAPPR1 equ &6c06
DMAPPR2 equ &6c0a
DCSR    equ &6c0f ; ITraster / ITDMA0 / ITDMA1 / ITDMA2 /
                  ;     x    /  DMA2  /  DMA1  /  DMA0
 
	call delock ; déverrouille l'ASIC
 
	di
	ld bc,&7fb8  ; Connexion page Asic (&4000-&7fff)
	out (c),c
 
	call exemple1
	call &bb18 ; attend une touche
	call exemple2
 
	ld bc,&7fa0  ; Déconnexion page Asic
	out (c),c
	ei
	ret
 
Exemple1
	xor a
	ld (DMAPPR2),a
 
	ld hl,Ay_ListeA
	ld (DMASAR2),hl
 
	ld a,%100 ; lance DMA2
	ld (DCSR),a
	ret
 
Exemple2
	ld a,17
	ld (DMAPPR2),a
 
	ld hl,Ay_ListeB
	ld (DMASAR2),hl
 
	ld a,%100 ; lance DMA2
	ld (DCSR),a
	ret
 
;# Déverrouille l'ASIC
;#####################
Delock	di
	ld a,17
	ld hl,asic
Loop	ld b,&bd
	outi
	dec a
	jr nz,loop
	ei
	ret
 
Asic	db 255,0,255,119,179,81,168,212,98,57,156,70,43,21,138,205,238
 
;# Définition des AY-Listes
;##########################
	Org &8100 ; où vous voulez en RAM centrale
		  ; doit etre une adresse paire
	Limit &81ff ; ne t'éloigne pas trop mon petit...
 
; 1er exemple ; génération de formes d'onde
Ay_ListeA dw &073f,&0000,&0100
	  dw &2040,&080f,&1012,&0800,&100f,&4001
	  dw &2040,&0808,&1011,&0800,&1010,&4001
	  dw &2080,&080f,&1011,&0800,&100e,&4001
	  dw &2080,&0808,&1010,&0800,&100f,&4001
	  dw &2060,&080f,&101b,&0800,&101b,&4001
	  dw &4020
 
; 2ème exemple ; sons classiques
Ay_ListeB dw &073f
	  dw &080f,&090f
	  dw &0000+159,&0100,&0200+213,&0300
	  dw &073c,&1040,&073f,&1050
	  dw &2002
	  dw &0000+142,&0100,&0200+358-256,&0301
	  dw &073c,&1040,&073f,&1050
	  dw &4001
	  dw &2002
	  dw &0000+127,&0100,&0200+319-256,&0301
	  dw &073c,&1040,&073f,&1050
	  dw &4001
	  dw &2003
	  dw &0000+119,&0100,&0200+190,&0300
	  dw &073c,&1040,&073f,&1050
	  dw &4001
	  dw &4020
1) la ROM disque d'origine des CPC+ est légèrement différente de celle des CPC old
2) La pauvre fille ! Depuis on n'en a plus jamais entendu parler !
3) Quelle frime ces noms ! On aurait pas pu dire tout simplement “les quatre premiers bits en &6804 sur la page ASIC connectée” ? Non ? Ah, bon !
4) Et Dieu sait que j'ai pu en produire
5) pourtant, il avait le même document que moi sous les yeux ! N'entend-il rien à l'anglais ?
6) sinon, vous devriez car c'est un préalable !
7) sinon faites semblant
 
assem/asic.txt · Dernière modification: 2017/10/09 10:04 (édition externe)