SOMMAIRE
1. INTRODUCTION
2. INTERFACE LOGICIELLE DE BASE
3. INTERFACE LOGICIELLE NORMALISEE
4. LES REGISTRES INTERNES GENERAUX
5. LE REGISTRE INTERNE DE CONVERSION A/N
6. LES REGISTRES INTERNES D'OSCILLATEURS
Rédacteur : Marc BAVANT
Mai 1988
1. INTRODUCTION
L'ordinateur Apple II GS (désigné par 2GS dans ce qui suit) est muni
d'un circuit spécialisé qui lui confère toutes ses possibilités
sonores. Il s'agit du DOC (Digital Oscillator Chip) 5503 de la firme
Ensoniq. Ce circuit, utilisé dans des synthétiseurs professionnels,
contient 32 oscillateurs numériques, divers registres de commande et
un convertisseur analogique-numérique à 8 bits. De plus, ce circuit
est associé à une mémoire vive de 64Ko qui lui est réservée.
Ces exercices de travaux pratiques ont pour but de familiariser
l'étudiant avec les possibilités du DOC et son utilisation dans la
configuration de l'Apple II GS. On se limitera à des opérations
élémentaires du point de vue logiciel et on ne s'aventurera pas trop
dans les dédales de la boite à outils (indispensable pour les
programmes d'application plus ambitieux).
matériel requis
* un 2GS muni d'un clavier et d'un moniteur (lecteur de disquette et
DOS pas indispensables)
* un poste de radio muni d'une sortie casque (de préférence avec
volume et tonalité réglables)
* quelques composants électroniques de base : résistances,
condensateurs, alimentation TTL (piles, alimentation variable ou
récupérer alimentation du 2GS récupérée sur le connecteur de jeux),
le moyen de les implanter (planche à contacts, soudure...), un petit
voltmêtre et un peu de cable électrique gainé souple.
connaissances requises
* programmation de base en 6502 et notions de 65816
* utilisation du moniteur intégré du 2GS (fonctions de base)
* bon sens et prudence pour manipuler l'électronique : vous êtes seuls
responsables de vos actes ! (cet avertissement ne sera pas
renouvelé)
bibliographie de référence
* Apple II GS hardware reference
* Ensoniq DOC ERS
* Sound Tools ERS
2. INTERFACE LOGICIELLE DE BASE
généralités
Le principe de communication entre le processeur 65816 du 2GS et le
DOC est identique à celui employé avec tous les autres organes
d'entrée-sortie, à savoir l'utilisation de "registres" spécialisés
situés dans l'espace $C000-$CFFF.
L'interface est constituée
. d'un registre de commande $C03C qui permet d'une part de passer
certaines commandes au DOC et d'autre part de modifier la
signification du registre d'adresse,
. d'un registre d'adresse $C03E-$C03F qui permet de spécifier
l'adresse de RAM ou le registre interne concerné,
. d'un registre de donnée $C03D qui permet de lire ou d'écrire à
l'endroit pointé par le registre d'adresse.
contenu du registre de commande
b7 : DOC occupé
b6 : accès RAM
b5 : autoincrémentation
b4 : réservé
b3..b0 : volume
DOC occupé : si ce bit est à 1, le circuit ne prend pas en compte les
demandes d'accès à ses registres internes, il faut donc attendre que
ce bit passe à 0 (est-ce que cela arrive vraiment en pratique ?).
accès RAM : si ce bit est à 1, l'adresse présente dans le registre
d'adresse (16 bits) désigne un octet de la RAM privative du DOC sinon,
le premier octet du registre d'adresse sera interprété comme un numéro
de registre interne du DOC pour toute opération de lecture ou
d'écriture.
autoincrémentation : si ce bit est à 1, le registre d'adresse est
incrémentée à chaque fois que le 65816 accède au registre de données
(en lecture ou en écriture).
volume : ces quatre bits indiquent le volume sonore de sortie ; ils
correspondent aux seize graduations du réglage de volume accessible
par le tableau de bord (ce réglage est disponible à l'adresse
$E100CA) mais ils fournissent toujours le résultat $F en lecture.
mise en pratique
Mettez le 2GS sous tension puis appelez le moniteur. Consultez le
contenu de $C03C. Vous trouverez vraisemblablement $1F, ce qui
signifie DOC non occupé, accès aux registres internes, pas
d'autoincrémentation. Ecrivez alors la valeur $1F dans $C03C, en
apparence rien ne devrait avoir changé mais pour percevrez un souffle
plus intense dans le haut-parleur, signe que le volume sonore demandé
($F) est plus important qu'auparavant. Si vous tapez CONTROL-G ou que
vous faites une erreur de syntaxe, le traditionnel BIP se transformera
en un GONG assourdissant. Tout rentrera dans l'ordre si vous modifiez
de nouveau le registre de commande en tenant compte du volume imposé
par le tableau de bord ($E100CA).
***** accès à la RAM
Essayons de transférer quelques octets dans la RAM du DOC.Pour cela
positionnons le registre de commande à $7x (o| x est le volume, par
exemple 3) ce qui signifie accès RAM et autoincrémentation.
Positionnons le registre d'adresse à la valeur $80FE :
C03C : 73
C03E : FE 80 (inversion des octets)
attention : toute erreur de manipulation peut entrainer des résultats
apparemment aberrants ; en particulier, à chaque fois que vous
commettrez une erreur qui occasionne un BIP, il vous faudra
repositionner le registre de commande.
Puis insérons successivement les valeurs $55, $66, $77, $88 dans le
registre de données
C03D : 55
C03D : 66
C03D : 77
C03D : 88
Observons alors le registre d'adresse, on trouve
C03D : 02 81
ce qui illustre bien l'autoincrémentation de ce registre à chaque
accès au registre de données.
Essayons maintenant de relire ces octets, repositionnons donc le
pointeur d'adresse à $80FE puis lisons alternativement le registre de
données et celui d'adresse à l'aide du petit programme suivant :
*!
!300:LDA C03D
! STA 2000
! LDA C03E
! STA 2001
! LDA C03F
! STA 2002
! RTS
!
*
Il vient alors :
* C03C : 73 (deux précautions ...)
* C03E : FE 80
* 300G 2000.2002
=* 00/02000 : xx FF 80 (xx quelconque)
* 300G 2000.2002
=* 00/02000 : 55 00 81
* 300G 2000.2002
=* 00/02000 : 66 01 81
* 300G 2000.2002
=* 00/02000 : 77 02 81
* 300G 2000.2002
=* 00/02000 : 88 03 81
nota : on a fait précéder les réponses du moniteur par le signe = afin
de les différencier des commandes.
Etonnant, non ? En fait, le fonctionnement est plus cohérent qu'il n'y
parait : l'opération de lecture du registre par le 65816 a pour effet
de transmettre la commande de lecture à la logique d'interface puis
d'incrémenter le pointeur d'adresse, mais la donnée recherchée n'est
pas immédiatement disponible et le 65816 obtient la valeur
correspondant à la requête précédente.
Pourquoi avoir utilisé un programme et pas le moniteur, vous
demandez-vous ? Eh bien, faisons-le, on obtiendra quelque chose comme
:
* C03C : 73
* C03E : FE 80
* C03D.C03F
=* 00/C03D : xx FF 80 (xx quelconque)
=* 00/C03D : 66 01 81
=* 00/C03D : 88 03 81
il semble donc que l'on saute une valeur sur deux, peut-être parce que
le moniteur lit ces registres deux fois pour avoir la valeur
recherchée et que double lecture implique doucle incrémentation.
exercice 2.1 : essayez de relire la RAM avec le moniteur mais en
n'utilisant pas l'option d'autoincrémentation; conclusion ?
***** accès aux registres internes
Le fonctionnement est tout à fait analogue pour adresser les 256
registres internes du DOC et nous pouvons nous lancer dans un
programme qui permet de récupérer leur valeur dans la page
$2000-$2100.
*!
!300:LDA #13 ; accès registre + pas autoincrémentation
! STA C03C
! LDX #0
!307:STX C03E ; X est le numéro de registre concerné
! LDA C03D
! LDA C03D ; cette répétition est nécessaire
! STA 2000,X
! INX
! BNE 307
! RTS
!
*300G 2000.20FF
exercice 2.2 : faites la mème chose en utilisant l'autoincrémentation.
3. INTERFACE LOGICIELLE NORMALISEE
généralités
Nous avons réussi à accéder au DOC sans difficulté jusqu'à présent,
mais cette interface est un peu trop proche du niveau physique et pas
assez compacte. C'est pourquoi Apple fournit une interface normalisée
dite "de bas niveau" (au sens des programmes d'applications complexes)
sous la forme de routine en ROM. Les points d'entrée de ces routines
dépendent de la version de ROM utilisée et on doit avoir recours à
l'outil GetTableAdress pour récupérer un pointeur sur une table des
points d'entrée.
mise en pratique
Appelez cet outil grace à la commande moniteur U :
*\4 4 0 0 0 0 B 8\U
le résultat est $00FF407B avec la version 01 des ROM.
routine offset point d'entrée
--------------------------------------------
Read Register 00 $FF40A3
Write Register 04 $FF40B4
Read RAM 08 $FF40A8
Write RAM 0C $FF40B9
Read Next 10 $FF40AF
Write Next 14 $FF40BC
--------------------------------------------
Vous avez tous les éléments pour désassembler ces routines très
simples, comprendre leur mode de fonctionnement et leur interface.
remarque 1 : prêtez attention aux problèmes de gestion de
l'incrémentation et de l'accès séquentiel (Xxxx Next).
remarque 2 : dans l'interface de ces routines, le registre A est court
alors que le registre X est tantot long (accès RAM) tantot indifférent
(accès registres).
remarque 3 : nous utiliserons dans ce qui suit les points d'entrée
trouvés ci-dessus, ce qui n'est pas la manière légale de faire et, à
l'occasion, nous reviendrons à l'interface de base pour les programmes
qui imposent des contraintes sur le nombre de cycles processeur (en
effet, l'inconvénient de cette interface normalisée est qu'elle ne
spécifie pas la durée d'exécution de ces routines).
Nous sommes maintenant à mème de réécrire le petit programme de
lecture de tous les registres sous une forme abrégée :
*!
!300:LDX #0
! JSL FF40A3 ; ReadReg
!306:STA 2000,X
! JSL FF40AF ; ReadNxt
! INX
! BNE 306
! RTS
!
*
Ecrivons maintenant un programme inverse qui nous permette de
positionner tous les registres internes désignés à la valeur voulue à
l'aide d'une table de couples (registre,valeur) terminée par $FF et
commen\ant en $340 par exemple :
!320:LDY #0
!322:LDA 340,Y
! CMP #FF
! BEQ 337
! TAX
! INY
! LDA 340,Y
! INY
! JSL FF40B4 ; WritReg
! BRA 322
!337:RTS
!
*
Vérifions tout \a sur un exemple :
* 340 : 80 88 FF (mettre 88 dans le registre 80)
* 320G 300G 2080
=* 00/2080 : 88 ....
Reste maintenant à connaitre la signification de tous ces registres !
4. LES REGISTRES INTERNES GENERAUX
On a déjà eu l'occasion de dire que le DOC possède 256 registres
internes numérotés de $00 à $FF. Les registres de numéro compris entre
$00 et $DF permettent la commande de chacun des 32 oscillateurs et se
répartissent donc en tranches de 32 ($00,$20,$40,$60,$80,$A0,$C0 et
$D0). Les positions de $E3 à $FF sont inutilisées. Le registre $E2
concerne la conversion analogique-numérique et les registres $E0 et
$E1 font l'objet du présent chapitre.
registre d'autorisation $E1 (oscillator enable)
Ce registre à pour but d'indiquer au DOC combien d'oscillateurs sont
en fonction simultanément à un instant donné. Ce paramêtre à une
influence directe sur la fréquence du son produit par le DOC comme
nous le verrons plus loin. L'utilisation de ce registre n'est pas des
plus évidentes :
en écriture : introduisez la valeur 2*n-2 ou 2*n-1 modulo 64
pour autoriser n oscillateurs (ex : $02, $03, $42 -> 2
oscillateurs),
en lecture : vous obtenez la valeur $C0+2*n-1 lorsque n
oscillateurs sont autorisés (ex : $C3 -> 2, $FF -> 32).
A la mise sous tension ou après un démarrage à froid, les 32
oscillateurs sont prêts à fonctionner comme l'atteste la valeur $FF du
registre $E1. Décidons de restreindre le nombre d'oscillateurs
autorisés à 16. On introduira pour cela la valeur 2*16-2=$1E dans le
registre $E1. Si on exécute à présent notre programme de lecture de
tous les registres, on s'apercevra que tous les registres
correspondant à des oscillateurs non autorisés sont égaux à $FF.
Essayons de modifier la valeur du registre $90=$80+16 qui correspond à
l'oscillateur numéro 16 (c'est-à-dire le 17ème...).
* 340: 90 88 FF
* 320G 300G 2090
=* 00/2090 : FF ....
la valeur qu'on voulait imposer n'a pas été prise en compte. Regardons
les choses un peu plus en détail :
*!
!800:STZ 820
! LDA #88
! LDX #90
! JSL FF40B4 ; WritReg
!80B:JSL FF40A3 ; ReadReg
! CMP #88
! BNE 818
! INC 820
! BRA 80B
!818:RTS
!
* 800G 820
=* 00/820 : nn ... (nn <> 0)
Ce programme nous permet de voir que la valeur donnée est conservée un
certain temps avant que le DOC ne se souvienne que l'oscillateur en
question n'est pas autorisé et efface le registre.
Essayons maintenant l'opération inverse, c'est-à-dire d'augmenter le
nombre d'oscillateurs autorisés d'une unité. Et là une suprise nous
attend :
UNCLAIMED SOUND INTERRUPT
Ce message signifie qu'une interruption issue du DOC est arrivée sur
la ligne IRQ du 65816 et que le vecteur d'interruption correspondant
est resté à sa valeur par défaut, à savoir l'affichage de ce message
et l'arrêt du traitement.Un reset nous sortira provisoirement
d'affaire avant d'approfondir cette notion d'interruption.
registre d'interruptions $E0 (oscillator interrupt)
Chacun des 32 oscillateurs est capable de générer une interruption
dans certaines phases de son fonctionnement afin de permettre au
processeur de modifier les caractéristiques de ce fonctionnement pour
une phase ultérieure.
Les interruptions peuvent être autorisées ou non pour chaque
oscillateur i par l'intermédiaire du registre $A0+i. Nous verrons plus
bas que c'est le bit 3 de ce registre qui autorise l'interruption.
Lorsque qu'un oscillateur est en situation de générer une
interruption, le DOC met à 0 le bit 7 du registre $E0, indique dans ce
mème registre le numéro de l'oscillateur concerné (bits 1 à 4) et
active une interruption sur la ligne IRQ du 65816. Si d'autres
interruptions surviennent avant l'acquittement de la première, le
numéro de l'oscillateur concerné est mis dans une file d'attente
spécifique.
Lors d'une interruption IRQ issue du DOC, le 65816 est censé exécuter
une routine qui acquittera l'interruption grace à une lecture du
registre $E0. Le bit 7 passera alors à zéro s'il n'y a pas d'autre
interruption en attente.
remarque : le reset qui a suivi notre dernière expérience a acquitté
l'interruption "non réclamée" provoquée vraisemblablement par
l'oscillateur 16. Rappelons que ce dernier avait été mis en
fonctionnement alors que tous ses registres valaient $FF ce qui, entre
autres choses, a d| l'autoriser à générer une interruption. La petite
expérience suivante permettra de vérifier cette conjecture : décidons
d'autoriser un nouvelle oscillateur (le numéro 17) en ayant pris soin
de modifier juste auparavant le registre $A0+17=$B1 pour lui interdire
les interruptions (une expérience précédente nous a prouvé que la
modification de registre d'un oscillateur non autorisé restait
effective un certain temps) :
*!
!800:LDA #0 ; interruption inhibée
! LDX #B1
! JSL FF40BA ; WritReg
! LDA #22 ; -> 18 oscillateurs autorisés
! LDX #E1
! JSL FF40BA
! RTS
!
*
Cette fois-ci, on entendra peut-être une court bruit dans le haut
parleur mais on a évitera l'erreur fatale.
5. LE REGISTRE INTERNE DE CONVERSION A/N
Nous commen\ons par cette fonction du DOC car c'est la plus simple et
elle semble totalement indépendante des autres fonctions. Le signal à
convertir est présenté sur deux broches du connecteur réservé au son,
après quoi la lecture du registre interne $E2 déclenche un processus
de conversion qui va durer environ 32 microsecondes (d'o| une
fréquence d'échantillonnage de 31.25 kHz). La valeur trouvée est
alors disponible dans ce mème registre. On note qu'il y a encore un
décalage entre la valeur lue et la valeur recherchée (on lit toujours
la valeur précédente) mais il ne faut pas effectuer deux lectures
consécutives dans l'espoir de résoudre ce problème puisque chaque
lecture réinitialise le processus de conversion (d'o| également
l'impossibilité d'utiliser les routines Read Register et Read Next à
cet effet !).
brochage du connecteur réservé au son
Ce connecteur est un connecteur Molex qui se présente sous la forme
d'une barrette de sept pointes de section carrée et d'un centimêtre de
haut environ. Ce connecteur se trouve à l'avant droit de l'appareil,
tout près du connecteur de haut-parleur. Le brochage est décrit dans
les documents cités en référence, mais il faut savoir que les broches
sont numérotées de l'avant vers l'arrière. Cela donne :
7, 6, 5, 4 indications de canal pour démultiplexage
3 sortie analogique
2 masse
1 entrée analogique
Il est spécifié que l'entrée ne doit pas dépasser 2,5 V et 3000 ohms
d'impédance.
Il peut paraitre difficile à première vue d'utiliser ce connecteur
sans posséder un connecteur femelle correspondant, mais en fait c'est
très simple. Il suffit de prendre un conducteur souple gainé de
plastique, proprement coupé pour présenter une section sans barbes de
cuivre (sources potentielles de court-circuits), et de se servir de la
gaine tubulaire comme d'un gant pour coiffer la broche. Enfoncer
suffisamment pour que cela tienne en faisant attention toutefois à ne
pas tordre les broches. Traiter de cette manière les broches 1 et 2
qui seules nous servent pour le moment.
étalonnage du convertisseur
Nous savons peu de chose sur ce convertisseur sinon qu'il accepte des
tensions inférieures à 2,5 V. Construisons-nous un petit programme qui
se charge d'interroger le convertisseur et de stocker 256 résultats.
*!
!800:LDA #13
! STA C03C
! LDA #E2
! STA C03E
! LDX #0
!80C:LDA C03D
! DEC 0 ; histoire de passer 5 cycles
! DEC 0
! DEC 0
! DEC 0
! DEC 0
! DEC 0
! STA 2000,X
! INX
! BNE 80C
! RTS
!
*
Pour fonctionner comme souhaité, ce programme doit être lancé en
vitesse lente (1 MHz, faites un reset pour être plus s|r). En vitesse
rapide, il faudrait rajouter des délais pour atteindre les 32
microsecondes indispensables.
Vérifier à l'aide d'un voltmêtre que le circuit ne délivre aucune
tension entre les broches 1 et 2 (si c'était le cas, vous vous seriez
trompé de broches !) et court-circuitez-les. La valeur trouvée par le
convertisseur est alors 0. Bricolez une alimentation variable avec une
pile et un potentiomêtre. Reliez la masse au pole moins de la pile et
appliquez prudemment et successivement des tensions de 0 à 2.5 V de
manière à étalonner le convertisseur. Pour ma part, aux erreurs de
mesure près je trouve :
tension (V) valeur numérique
-------------------------------
0.0 00
0.2 16
0.4 2C
0.6 42
0.8 58
1.0 6E
1.2 80
1.4 96
1.6 AA
1.8 BE
2.0 D2
2.2 E8
2.4 FC
Ces résultats montrent de manière flagrante que le convertisseur est
linéaire sur la totalité de son domaine, avec une pente constante
d'environ 110/V.
Cette gamme de tensions admissibles va nous poser un petit problème
car la plupart des équipements de reproduction musicale (radio,
magnétophones) fournissent un signal de valeur moyenne nulle, signal
qu'il faudra donc polariser autour de 1,2 V si on veut l'utiliser en
entrée du convertisseur. Et ceux qui fournissent un signal polarisé
n'ont pas de raison particulière d'avoir la tension moyenne que nous
recherchons. Un simple pont diviseur et un couplage par condensateur
se révéleront très satisfaisants dans tous les cas.
écoute directe du son numérisé et écrêté
Ce paragraphe va certainement rappeler des souvenirs à ceux qui
avaient utilisé l'entrée cassette de leur vieil Apple II pour
numériser des messages qu'on pouvait lui faire restituer d'une voix
nasillarde, mais parfaitement intelligible et naturelle, à tout
moment. La technique est ici la mème : on ne regarde que le signe du
signal d'entrée (par rapport à sa valeur moyenne) et on actionne un
déplacement de la membrane du haut-parleur à chaque changement de
signe.
*!
!800:LDA #13
! STA C03C
! LDA #E2
! STA C03E
!80A:LDA C03D ; 4 cycles
! JSR 830 ; 12 + n*2 (n = nombre de NOP)
! TXA ; 2
! EOR 0 ; 3
! BPL 80A ; 3
! STX 0
! STA C030 ; haut-parleur
! BIT C061 ; bouton ou pomme ouverte
! BPL 80A
!830:NOP
! NOP
! NOP
! NOP
! RTS
une fois encore, les temps sont ajustés pour un fonctionnement à 1
MHz.
Testons tout cela. Choisissez une station de radio, de préférence en
modulation de fréquence à cause de la meilleure qualité du son, et au
moment des informations pour pouvoir évaluer l'intelligibilité.
Récupérez le signal de la prise casque, par exemple grace à un jack
monophonique avec un éventuel adaptateur, et enfin injectez ce signal
à l'entrée du montage polarisateur après avoir pris soin de baisser le
réglage de volume du poste. Il est prudent de garder un voltmêtre à
courant continu en parallèle sur l'entrée du DOC pour surveiller les
tensions appliquées.
Lancez le programme ci-dessus, ajustez le volume juste au-dessus du
seuil limite, ne lésinez pas sur les aigus et appréciez la qualité de
la parole écrêtée c'est-à-dire, dans notre cas, numérisée avec un seul
bit par échantillon. Si tout fonctionne correctement, la parole doit
être clairement intelligible et ressembler à ce qui peut sortir d'un
walkie-talkie, d'un poste CB ou autres gadgets pas précisément
renommés pour leurs qualités musicales. Si \a ne marche pas cherchez
l'erreur !
L'avantage de ce type de numérisation est bien entendu la compacité du
codage qu'il est possible d'atteindre (pour mémoire, les systèmes
similaires sur Apple II permettent de stocker une petite dizaine de
secondes de parole dans 16K octets mais cela dépend beaucoup de la
qualité du son). La fréquence d'échantillonnage est toutefois un peu
faible sur le DOC pour ce genre de technique (l'entrée cassette était
beaucoup mieux adaptée aux échantillonnages rapides sur un bit)
Quand vous en avez assez, frappez la touche pomme ouverte ou faites
reset.
numérisation et stockage du son dans la RAM privative
L'expérience précédente nous aura surtout permis de vérifier que notre
montage fonctionne correctement. Nous allons maintenant préparer
l'étape suivante en stockant les valeurs numérisées dans la RAM
privative du DOC. On peut noter au passage qu'il est dommage que le
processeur soit obligé d'intervenir pour cette opération qu'on
aimerait bien voir le DOC effectuer seul. Le programme qui suit est
une décalque du précédent légèrement compliquée par la valse des
registres.
*
*!800:LDA #00
! STA C03E
! STA C03F
( acquérir l'échantillon précédent et relancer la numérisation)
!808:LDA C03E ; 4 + k cycles
! PHA ; 3
! LDA #13 ; 2
! STA C03C ; 4 + k
! LDA #E2 ; 2
! STA C03E ; 4 + k
! LDX C03D ; 4 + k
! BNE 8xx ; 3 (remplacer les 0 par des 1)
! INX ; 2 - 1
( insérer l'échantillon dans la RAM privative )
!8xx:LDA #73 ; 2
! STA C03C ; 4 + k
! PLA ; 4
! STA C03E ; 4 + k
! STX C03D ; 4 + k
( continuer jusqu'à 64 K échantillons )
! LDA C03E ; 4 + k
! BEQ 8yy ; 3
! NOP ; 2 - 1
! JMP 8zz ; 3 (astuce pour équilibrer les temps de
cycle)
!8yy:LDA C03F ; 4 - 1 + k
!8zz:BNE 80A ; 3
! RTS
Lancez ce programme par 800G, le prompteur du moniteur réapparaitra
après un court instant quand toute la RAM privative aura été remplie.
remarque : la valeur 0 qui correspond à une tension rigoureusement
nulle a la mauvaise propriété d'être également un ordre d'arrêt pour
les oscillateurs qui essaient de la produire, d'o| notre INX.
remarque : on a procédé à une astuce pour éviter que la durée de la
période d'échantillonnage ne diffère selon la branche empruntée par le
programme après le test de début de page. Ceci n'a sans doute pas
grand effet sur la qualité du son et présente l'inconvénient
d'allonger cette période dans tous les cas.
exercice 5.1 : on arrive à une période d'échantillonnage de 59 ou 60
cycles si je ne me trompe pas, ce qui est un peu trop pour un
fonctionnement du microprocesseur à 1 MHz et pas assez en principe
pour 2.8 MHz. Néanmoins, on verra que grace aux ralentissements dus à
l'accès à la mémoire lente, ce programme fonctionne également en
vitesse "rapide". En supposant que l'accès à la mémoire lente se fait
exactement à 1 MHz, calculez le nombre équivalent k de cycles à
2.8 MHz "rajoutés" aux instructions STore et LoaD. (réponse : k=11
environ)
exercice 5.2 : calculez la fréquence d'échantillonnage correspondant à
ce programme tournant à 1 MHz. Calculez le débit binaire moyen de
transfert dans la RAM des données numérisées. Calculez le temps de
remplissage de la RAM.
Mesurez-le et comparez.
exercice 5.3 : mesurez le temps de remplissage pour l'horloge à
2.8 MHz. Déduisez-en la période et la fréquence d'échantillonnage.
Conclusion.
Nous pouvons évidemment faire fonctionner ce programme mais, à ce
stade, nous n'aurons pas d'assurance sonore que tout s'est bien passé.
Contentons-nous d'observer une page de la RAM privative et de vérifier
qu'elle contient des données plausibles.
*!
!300:LDX #0
! JSL FF40A8 ; ReadRam
!306:STA 2000,X
! JSL FF40AF ; ReadNxt
! INX
! BNE 306
! RTS
!
* 300G 2000.20FF
5. LES REGISTRES D'OSCILLATEURS
Voici l'heure du plat de résistance ! Les lecteurs pressés peuvent
sauter directement aux expériences mais il ne comprendront pas tout.
Il ne sera pas question ici de rentrer dans tous les détails, mais
seulement d'essayer d'éclairer la documentation du constructeur sans
la paraphraser. La lecture intégrale de cette documentation (Ensoniq
DOC ERS, 11 pages) n'est pas nécessaire pour comprendre ce chapitre,
mais je la recommande fortement à ceux que le sujet intéresse.
généralités sur les oscillateurs
Le DOC est un processeur synchronisé grace à une horloge à 7.14 MHz
(je ne garantis pas cette valeur sur laquelle la documentation semble
hésitante). Le cycle de traitement interne du DOC est 8 fois plus long
que le cycle d'horloge. Pendant chaque cycle de traitement, le DOC
"donne la parole" à un oscillateur, par ordre de numéro et jusqu'à
concurrence du nombre d'oscillateurs autorisés (si quatre oscillateurs
sont autorisés, le DOC donnera successivement la parole au 0, au 1, au
2, au 3, au 0 etc.). Il consacre ensuite deux cycles au
rafraichissement de la RAM. La durée qui sépare deux interrogations
successives du mème oscillateur dépend donc du nombre d'oscillateurs
autorisés. Par commodité, on lui donnera le nom de période
d'échantillonnage du DOC (ne pas confondre cet échantillonnage de
sortie avec l'échantillonnage de 32 microsecondes réalisé par le
convertisseur d'entrée !)
Chaque oscillateur est en fait un générateur d'adresse, c'est-à-dire
que lorsque le DOC lui "donne la parole", il élabore une adresse de 16
bits à l'aide des informations de commandes que l'utilisateur a
placées dans ses registres. Vous aurez s|rement deviné que cette
adresse désigne un octet de la RAM privative du DOC et que c'est
justement cet octet-là qui va ensuite passer dans le convertisseur
numérique-analogique qui précède la sortie analogique du circuit.
la génération d'adresse
Entrons un peu dans les détails de la génération d'adresse. Tout
d'abord, notons que les calculs d'adresse se font sur 24 bits.
L'accumulateur de l'unité arithmétique du DOC est chargé avec la
valeur courante relative à l'oscillateur courant (ex : 04FF80). On lui
ajoute le contenu concaténé de deux registres de 8 bits dits registres
de fréquence (ex:0002) correspondant à ce mème oscillateur. Il faut
ensuite procéder à un savant mixage pour obtenir l'adresse recherchée.
Cela se fait à l'aide de trois paramêtres :
. la résolution,
. la page de début de la "table d'onde",
. la longueur de la "table d'onde".
Le paramêtre de résolution nous permet déja de nous débarasser de 8
bits superflus dans l'accumulateur (ex : $04FF82). On peut garder les
bits
. de b23 à b8 : $04FF (résolution 7)
. de b22 à b7 : $09FF (résolution 6)
etc.
. de b16 à b1 : $7FA1 (résolution 0)
notez que dans tous les cas, le bit 0 est sacrifié (le bit 1 aussi
d'ailleurs à cause de ce qui suit). Notez tout de mème que le résultat
de l'opération reste bien dans l'accumulateur, dont le contenu est
sauvegardé, et donc que le bit 0 n'est pas du tout inutile !
La "table d'onde" (wavetable) est tout simplement un segment de la
mémoire RAM privative auquel on veut cantonner l'oscillateur.
Autrement dit, on ne l'autorise pas à générer des adresses en dehors
de ce segment. Pour simplifier les calculs du DOC nous sommes
astreints à définir des "tables d'onde" commen\ant impérativement en
début d'une page (i.e avec une adresse divisible par $100) et dont la
longueur est un nombre entier de pages, puissance de deux comprise
entre 1 et 128 (autrement dit des tailles de 2**8 à 2**15 octets).
Notre bonheur ne serait pas complet si on n'ajoutait pas à cela une
restriction liant ces deux paramêtres, à savoir que le début d'une
"table d'onde" doit avoir une adresse multiple de sa longueur.
Illustration :
. un segment d'une page peut commencer à n'importe quel début de page,
. un segment de 16 pages ne peut commencer qu'à une adresse de la
forme $n000,
. un segment de 128 pages ne peut commencer qu'à l'adresse $0000 ou
$8000,
. un segment de 2**L pages ne peut commencer qu'à une adresse de la
forme n*2**(L+8).
Nous avons presque terminé la description du calcul d'adresse. Une
fois déterminés les bits significatifs du registre de début de "table
d'onde" en fonction de sa longueur (ex: les quatres premiers bits de
$A0 pour une "table d'onde" de 16 pages) il nous reste à extraire 12
bits de l'accumulateur qui n'en contient déjà plus que 16 après
l'opération de résolution (ex: la résolution la plus fine $04FF82 ->
$7FA1). On choisit les 12 bits de poids fort ( $7FA0 -> $7FA ). D'o|
l'adresse finale $A7FA. Tout ceci est admirablement résumé dans le
tableau de la page 11 de la documentation.
quelques formules pour mieux saisir
On aura compris que la fréquence du son issu du DOC est une fonction
des nombreux paramêtres décrits ci-dessus. Commen\ons par exprimer la
fréquence d'échantillonnage (inverse de la période d'échantillonnage)
désignée dans la documentation par SR.
SR = CLK / (8*(OSC + 2))
o| l'on retrouve CLK la fréquence d'horloge, 8 le nombre de cycles
d'horloge pour un cycle de traitement, OSC le nombre d'oscillateurs
autorisés et 2 le nombre de cycles de rafraichissement. On peut mettre
ceci en rapport avec la fréquence d'échantillonnage d'entrée :
SRin = CLK / 8*26
o| l'on retrouve 26, le nombre de cycles de traitements par
numérisation.
Imaginons maintenant que nous définissions une "table d'onde" en RAM
contenant une période unique d'un signal périodique quelconque. Nous
pouvons demander à un oscillateur de parcourir cette table
indéfiniment en bouclant. Le son produit sera donc également
périodique et de période égale à la durée de parcours de cette table
(période du fondamental). Calculons cette durée : à chaque période
d'échantillonnage, l'oscillateur incrémente son accumulateur de N
N = FC (mod. 2**(16+RES))
et l'adresse qu'il produit d'une quantité n qui dépend de tous les
paramêtres définis :
n = N / (2**(9+RES-L))
o| FC est le contenu des registres de fréquence, RES est la résolution
et 2**L est la longueur de la table en pages.
La période du fondamental est donc
T = 2**(L + 8) / (n * SR) = 2**(17+RES) / (FC * SR)
Ce n'est pas un hasard si la longueur de la table n'intervient plus
dans cette dernière formule : c'est une conséquence directe des
mécanismes compliqués de calcul d'adresse. C'est mème
vraisemblablement leur cause ! De cette manière, vous avez le choix du
pas d'échantillonnage de votre signal : soit vous choisissez un pas
très fin et alors vous aurez une "table d'onde" très longue, soit vous
choisissez un pas grossier et alors la mème portion du mème signal
tiendra dans une "table d'onde" plus courte. Si les paramêtres FC, RES
et OSC sont identiques dans les deux cas, vous obtiendrez en sortie du
DOC un signal de mème fréquence mais de qualité sonore peut-être
différente.
les registres dits de commande de fréquence $00-$1F et $20-$3F
Le premier contient les bits de poids faible et le second les bits de
poids fort de la valeur ajoutée à l'accumulateur à chaque cycle de
traitement.
ex: $05 contient $40 et $25 contient $02 -> incrément de $240 pour
l'oscillateur numéro 5
les registres de volume $40-$5F
En plus du volume général de la sortie commandé par les 4 bits de
poids faible du registre $C03C, on peut commander individuellement et
sur une échelle de 0 (mini) à 255 (maxi) le volume de sortie de chaque
oscillateur. C'est cela qui permet à la boite à outils d'offrir des
définitions d'enveloppe.
les registres d'échantillon $60-$7F
Chaque registre contient la dernière valeur lue dans la RAM par
l'oscillateur concerné.
les registres d'adresse $80-$9F
Chaque registre contient le numéro de la première page de la "table
d'onde" à laquelle l'oscillateur est rattaché.
les registres de commande $A0-$BF
Chaque registre de cette série est composé de plusieurs champs :
b7 à b4 : canal (ces bits seront présentés sur les broches 4
à 7 du connecteur Molex à chaque fois que
l'oscillateur en question sera actif)
b3 : autorisation de générer une interruption à la fin
de chaque cycle de parcours de la "table d'onde"
associée
b2 à b1 : mode de fonctionnement (voir plus bas)
b0 : arrêt de l'oscillateur (spontané ou commandé par
for\age de ce bit), rappelons que l'oscillateur s'arrête
s'il génère l'adresse d'une mémoire de contenu 0.
Les modes sont 00 (boucle infinie), 01 (une seule itération), 10 (un
oscillateur commande le volume ou la durée de la boucle d'un
oscillateur de numéro adjacent), 11 (deux oscillateurs de numéros
adjacents prennent la main à tour de role et indéfiniment).
les registres de longueur/résolution $C0-$DF
Chaque registre de cette série est composé de plusieurs champs :
b7 à b6 : non utilisés sur 2GS
b5 à b3 : paramêtre L (2**L = nombre de pages de la "table
d'onde")
b2 à b0 : paramêtre RES
Nous ne revenons pas sur leur signification.
mise en pratique
Nous allons nous contenter da déterminer par le raisonnement les
paramêtres à utiliser pour que le DOC nous "rejoue" le contenu de la
mémoire que nous venons d'y insérer grace au programme de
numérisation.
Tout d'abord, on remarque qu'un seul oscillateur ne peut pas parcourir
les 64 K d'un coup. On pourrait penser à lui faire parcourir les 32
premiers K, lui demander de générer une interruption, traiter cette
interruption en modifiant ses registres pour lui faire parcourir le
reste de la RAM. C'est faisable mais il existe une technique beaucoup
plus naturelle pour le DOC, à savoir l'utilisation du mode relais
(swap mode, %11) qui permet d'enchainer le fonctionnement de deux
oscillateurs sans aucune discontinuité. Le premier oscillateur
travaillera donc sur les adresses $0000 à $7FFF et le second sur $7FFF
à $FFFF.
remarque : ce mode nécessite que le premier oscillateur soit de numéro
pair et que le second soit de numéro immédiatement supérieur.
Nous voulons que la période d'échantillonnage en sortie soit identique
à celle que nous avons utilisée en entrée. En supposant que l'on a
échantillonné l'entrée avec la fréquence maximale SRin, on obtient :
OSC = 24
Nous voulons également qu'à chaque période d'échantillonnage,
l'adresse produite augmente de n=1. Comme L=7, il vient :
FC = 2**(2 + RES)
Nous pouvons donc choisir ces deux derniers paramêtres avec un degré
de liberté : RES=0 et FC=$0004 par exemple. Le volume de chaque
oscillateur sera réglé à une valeur identique, $FF par exemple.
*340 : E1 2E (24 oscillateurs )
40 FF 41 FF (volume )
00 04 20 00 01 04 21 00 ("fréquence" )
80 00 81 80 (page de début )
C0 38 C1 38 (longueur/résolution )
A0 06 A1 07 (commande )
FF (fin des directives )
*320G (chargement des registres )
*300G 2000.20FF (pour relire et vérifier )
Normalement, dès l'exécution de la routine de chargement des registres
vous devriez entendre le contenu sonore de la RAM privative répété
sans relache.
Si cela vous convient, vous pouvez continuer à travailler en musique
(un peu lancinant tout de mème) ou arrêter le DOC, soit par un reset,
soit par
*340 : A0 01 A1 01 A0 01 A1 01 FF
*320G
La répétition semble nécessaire dans certains cas.
Remarque : en dépit des savants calculs ci-dessus, la fréquence
restituée par le DOC est trop grande. Ceci est d| au fait que nous
avons supposé que la fréquence d'échantillonnage est égale à la
fréquence max, ce qui est approximatif dans le cas d'une horloge à
2.8 MHz et carrément faux dans le cas de l'horloge à 1 MHz. Ajuster la
fréquence par l'intermédiaire des paramêtres RES ou FC ne peut pas
suffire car la modification de l'un de ces paramêtres revient toujours
à doubler ou diviser par deux la fréquence du son. On peut, en
revanche, jouer sur le nombre d'oscillateurs pour régler finement la
fréquence. Les paramêtres suivants sont satisfaisants :
2.8 MHz OSC=27 RES=0 FC=$0004
1 MHz OSC=22 RES=0 FC=$0002
exercice 6.1 : calculez k (ralentissement) en fonction des résultats
ci-dessus.(résultat : k=10 environ)
exercice 6.2 : essayez différentes valeurs de RES et de FC
satisfaisant à l'égalité FC = 2**(2 + RES).
exercice 6.3 : utilisez quatre oscillateurs (2 paires) en choisissant
leurs numéros assez éloignés pour obtenir un léger décalage entre les
deux "voies" (ex: 0-1 et 13-14). Vous constaterez s|rement une
amélioration de la qualité. Comment produire un décalage plus
important ?
exercice 6.4 : modifier la routine de numérisation pour qu'elle boucle
indéfiniment sur elle mème (prévoir quand mème une sortie par appui
sur pomme ouverte) et lancer cette routine tandis que les oscillateurs
sont en fonctionnement. Qu'obtient-on ? Observez les conséquences du
léger désajustement des fréquences d'échantillonnage de lecture et de
numérisation.