Revenir au plan du site
Les DMA audio de la gamme Amstrad Plus / GX-4000
(article très inspiré du site Quasar, voir
[QUASAR])
L'Amstrad Plus dispose de trois DMA (Direct Memory Access pour accès direct à la mémoire) pas comme les autres. Oui, à l'instar des sprites hards "pas comme les autres" de l'ASIC, les DMA du CPC sont aussi un peu particuliers.
N'espérez pas déplacer de la mémoire avec, faire des split-rasters ou charger une disquette en tâche de fond, ils n'ont pas été conçus pour ça. Par contre, ils sont capables tous les trois d'envoyer des valeurs aux registres audio de l'AY en évitant toute la gymnastique propre au CPC à travers le PPI. Terminés donc les 6 OUT incompressibles et coûteux en temps machine, place aux DMA-listes! C'est déjà pas mal.
Toutes les instructions sont dédiées au traitement de l'audio et de synchronisation.
| Instruction | Opcode | Description |
| NOP |
#4000 |
No OPeration, ne fais rien |
| LOAD R,D |
#0RDD |
Envoyer la valeur DD dans le registre R de l'AY
R : 0 à 15 et D : 0 à 255 |
| PAUSE N |
#1NNN |
Attend NNN fois l'unité de pause N : 0 à 4095
PAUSE 0 est équivalent à NOP |
| REPEAT N |
#2NNN |
Initialise le compteur de boucle à NNN
NNN : 0 à 4095
REPEAT 0 est équivalent à REPEAT 1 |
| LOOP |
#4001 |
Reboucler à l'instruction suivant le précédent REPEAT si le compteur est différent de zéro |
| INT |
#4010 |
Générer une interruption DMA |
| STOP |
#4020 |
Arrêter l'exécution de la liste DMA
Le registre DCSR est mis à jour |
Petite particularité des listes DMA. Elles seront toujours lues directement dans la mémoire centrale (les premiers 64K) quel que soit la configuration mémoire active. Chaque instruction doit être placée sur une adresse PAIRE (multiple de 2). Bien que le nombre de listes sous-entende une filiation avec le nombre de canaux audio de l'AY, une seule liste suffit pour envoyer du son à tous les canaux, puisque l'instruction LOAD adresse n'importe quel registre. Il faut plutôt y voir une polyvalence d'usage, par exemple streamer des sons en continu avec deux listes qui font du ping-pong et pourquoi pas une troisième pour la synchro qui déclenche une interruption sur le dernier sample.
Les instructions des listes DMA sont lues lors de la HBL. Une fois les instructions lues, ce qui prend au minimum 3 NOPS, elles sont exécutées. Alors le truc à retenir, c'est surtout ce "retard" de 3 NOPS rapport au début de la HBL, parce que les interruptions classiques ou les interruptions rasters, elles, démarrent en même temps que la HBL! Il n'est donc pas toujours possible de remplacer plusieurs interruptions rasters par une liste DMA d'interruptions (pour ceux qui ont regardé l'article sur le rouleau d'Axelay).
Mais déclencher plusieurs interruptions avec une liste DMA reste une façon pratique pour multiplexer des sprites. En optimisant correctement, on arrivera aussi à faire de beaux rasters avec. La capture ci-dessous montre le déclenchement des interruptions en vert dans les bords (étendus pour l'occasion) ainsi que le contour des sprites hard. J'en compte au moins 28 de visibles, plus ceux planqués dans les côtés en attente d'être utilisés.
Note : Les interruptions de multiplexage ne sont pas matérialisées car elles réactivent les interruptions immédiatement.
L'importance de l'acquittement
En utilisant l'acquittement universel (bit 0 de l'IVR à zéro), il suffit de terminer ses vecteurs d'interruption par
EI:RET. En acquittement spécifique, tant que le bit correspondant dans le DCSR n'est pas mis à 1, l'ASIC cesse totalement d'exécuter le programme principal, appelant le vecteur concerné à l'infini.
Les listes sont pilotables via un lot de registres dont voici le détail :
Les registres de commande des listes DMA
| Registre | Adresse | Description |
| I |
modifiable avec LD I,A
lisible avec LD A,I |
Définir le poids fort de la table des vecteurs d'interruption* |
| IVR |
#6805 |
Bits 0 : définir le mode d'acquitement => 0 : Universel / 1 : Spécifique
Bits 3 à 7 : définir les bits 3 à 7 de la table des vecteurs d'interruption*
|
| SAR0 |
#6C00-#6C01 |
Adresse de la liste DMA 0 |
| PPR0 |
#6C02 |
Unité de pause du DMA 0 |
| SAR1 |
#6C04-#6C05 |
Adresse de la liste DMA 1 |
| PPR1 |
#6C06 |
Unité de pause du DMA 1 |
| SAR2 |
#6C08-#6C09 |
Adresse de la liste DMA 2 |
| PPR2 |
#6C0A |
Unité de pause du DMA 2 |
| DCSR |
#6C0F |
Bits 0 à 2 : Activer/Désactiver le DMA 0,1,2
Bits 4 à 7 : Acquitter le DMA 2,1,0 et la PRI en mettant le bit à 1 |
*table de vecteurs d'interruption pour le mode IM 2
Avec l'aimable autorisation d'Offset, je vous mets les deux schémas un peu plus visuels
Utilisation générique des listes DMA
Le DCSR permet d'activer/désactiver simultanément les trois listes DMA. On met le bit à 1, l'exécution commence à la prochaine HBL.
Dans l'exemple ci-dessous, on lance la liste DMA, les temporisations sont manuelles, c'est l'exemple le plus simple d'utilisation d'une liste
[Télécharger les sources + fichier audio]
include 'system.asm'
buildsna : bankset 0
DCSR equ 0x6C0F
DMA0 equ 1
DMA1 equ 2
DMA2 equ 4
ALLDMA equ DMA0|DMA1|DMA2
IVR equ 0x6805
SAR0 equ 0x6C00
PPR0 equ 0x6C02
SAR1 equ 0x6C04
PPR1 equ 0x6C06
SAR2 equ 0x6C08
PPR2 equ 0x6C0A
org #38
ei : ret
org #100
run #100
ld sp,#100
UnlockAsic (void) ; macro définie dans system.asm
im 1 : RMR2 ASICON
ei
xor a : ld (PPR0),a : ld (PPR1),a : ld (PPR2),a ; prescale pause register NULL
xor a : ld (IVR),a ; acquittement universel !!!
;=====
reloop
;=====
ld hl,sample1 : ld (SAR0),hl
ld a,DMA0 : ld (DCSR),a ; lancer DMA0
ld b,0
halt
djnz $-1
jp reloop
sample1
align 2
defw #073F ; mixeur coupe tout
incbin 'nnntssssstsssss15k.wav',DMA,DMA_CHANNEL_B
|
Notez bien que le résultat audible de samples DMA (ou même de samples CPC classiques) dépend énormément de la source, lire l'article de Targhan dans 64Nops pour plus d'informations à ce sujet.
On entend le son qui se répète régulièrement et pendant que le son joue et la liste DMA déroule, le Z80 attend patiemment. C'est gratuit. La restitution du sample se fait en
variant rapidement le volume d'un canal fermé (voir
[signification de l'initialisation du mixeur])

Après l'exécution d'une liste, si les registres SAR ne peuvent être relus (pas plus que les registres PPR), l'adresse courante de la liste est toujours stockée dans le SAR "interne" de l'ASIC. Il est donc possible de relancer un DMA stoppé qui continuera à l'instruction suivante dans la mémoire. Comme le souligne Offset sur Quasar, l'instruction STOP laisse le pointeur d'adresse sur l'instruction suivante, ce qui peut être source de bug dans vos programmes.
La suite se passe dans
l'article suivant.