U T I L I S A T I O N   D U   D O C

T R A V A U X   P R A T I Q U E S

               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.