— 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.
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.
— 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
.
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.
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é !"
On peut également relocker l'ASIC, pour cela, il suffit d'envoyer n'importe quelle autre valeur à la place du dernier octet 238.
Lorsque l'ASIC est locké, on ne peut plus accéder au registre RMR2, il n'est donc pas possible de connecter (ou de déconnecter!) la page ASIC en mémoire. Cependant, les autres parties de l'ASIC continuent de fonctionner: les sprites restent affichés, les DMAs tournent toujours, etc.
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.
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
Dans la cartouche d'orgine livrée avec les CPC+, les ROM physiques disponibles sont :
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.
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.
Certaines extensions mémoires ne prennent pas en compte la présence de l'ASIC et pourront donc se retrouver en conflit avec ce dernier. Dans ce cas il faudra prendre garde de ne pas connecter en même temps la page ASIC et une page de mémoire étendue à la même adresse. Bien que cela fonctionne sans problème avec la mémoire interne du CPC+.
— 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 :
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.
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
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
— Basé sur l'article publié dans Quasar CPC numéro 8, CPC plus, par OffseT.
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.
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).
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.
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.
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
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
(à 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).
(à saisir et à enrichir)
— 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…
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).
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 !
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 !
— Cet article ne comportait pas d'exemple, il serait judicieux d'en ajouter un.
— Basé sur les articles publiés dans Quasar CPC numéro 10 et numéro 14, CPC plus, par OffseT.
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).
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é ?!
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 :
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…
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'').
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…
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.
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 ; ...
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).
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.
— 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.
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.
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 :
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.
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 :
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
.
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 :
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 :
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 !
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.
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
.
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.
&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
) :xxxx xxxx xxx1
dans le compteur de boucle,REPEAT
rencontrée précédemment),LOOP
sautera à l'instruction suivant le &6003
dans l'exemple suivant).&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
) :xxxx xx1x xxxx
),Si vous n'avez rien compris, ça n'est vraiment pas grave, mon moment de folie est terminé.
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.
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.
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.
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
&6804
sur la page ASIC connectée” ? Non ? Ah, bon !