Der Verwandlungskünstler: DSP-Programmierung auf dem Falcon, Teil 2

I/O-Schnittstellen und I/O-Programmierung des DSP 56001

Fast so bedeutend wie der DSP selbst sind dessen Schnittstellen zum Host-Prozessor, denn: ohne einen effizienten Informationsaustausch zwischen Sub- und Hauptprozessor wäre der DSP im Falcon etwa so belanglos wie Christi Linfords 100-Meter-Sprint-Sieg bei der letzten Leichtathletik-Weltmeisterschaft ohne die Medien, die diese Leistung erst im ganzen Lande publizierten.

Ausgangspunkt aller Bemühungen ist und bleibt natürlich der Hauptprozessor. Da sich vom Desktop aus ausschließlich Host-Applikationen starten lassen, beginnen wir also mit einem Programm in der üblichen Landessprache (hier die Sprache C).

Eine ganze Reihe von neuen XBIOS-Funktionen bildet die Brücke zum DSP. Da derzeit die wenigsten Compiler über eine angepaßte XBIOS-Bibliothek verfügen, befindet sich auf nachfolgenden Seiten eine Header-Datei mit allen neuen DSP-und Sound-Funktionen. Diese sind entsprechend ihrer XBIOS-Nummern aufsteigend sortiert, wobei die erste Funktion Dsp_DoBlock() mit der Nummer 96 beginnt. Wer bisher mit den XBIOS-Nummern aus [3] hantierte, weiß jetzt, wem er die stundenlangen, erfolglosen Rechnersitzungen zu verdanken hat. Dort handelte es sich um eine erste Beta-Version der neuen Funktionen mit noch vorläufigen Funktionsnummern. Mit den richtigen Nummern läßt sich eine geeignete Bibliothek schnell selbst schreiben. Wem hierzu die Zeit fehlt, der besorgt sich einfach die Monatsdiskette der ST-Computer. Neben den Quellen aus diesem Heft liegen die kompletten Assembler-Quellen und eine Library für die neuen XBIOS-Funktionen bei. Soviel zum dringend benötigten Werkzeug.

Host ruft DSP - bitte kommen!

Spätestens das MultiTOS mit seinen parallel laufenden Prozessen weckte auch bei ATARI die Sensibilität im Umgang mit den Rechnerschnittstellen. So reicht das alleinige Starten eines zweiten Prozesses leider nicht aus, um einen zweiten DSP (wenn auch nur virtuell) zu erzeugen. Teilen ist also angesagt. Folglich prüft man mit Dsp_Lock() zum einen, ob der DSP zur Zeit einem anderen Prozess gehört (liefert einen Fehler zurück), und außerdem sperrt man ihn vor fremden Zugriffen, wenn er gerade außer Dienst war.

Zuvor sollte man sich allerdings vergewissern, ob überhaupt ein DSP vorhanden ist. Dazu rät ATARI, die Funktion Dsp_GetWordSize() (liefert die Anzahl der By tes pro Wort) zu benutzen. Weil Motorolas DSP-X6001-Reihe sowohl 16- wie 24-und 32-Bit-Prozessoren umfaßt, lassen Rückgabewerte, die nicht zwischen zwei und vier liegen auf einen fehlenden DSP schließen. Prinzipiell wäre ein intelligenter Programmlader in der Lage, die verschiedenen DSP-Programmcodes auf das Format des gerade eingesetzten DSPs zu konvertierten, die Abwärtskompatibilität scheitert dennoch meistens an einem zu kleinen Wertebereich. Um ganz sicher zu gehen, wird man wohl jeden Wert ungleich drei als Fehler behandeln.

Nächster Schritt: man besorgt sich eine private DSP-Programmkennung mit DSP_GetUniqueProgAbility(). Mit dieser Kennung läßt sich später ermitteln, ob sich ein gewünschtes Programm noch im DSP befindet oder durch ein anderes ersetzt wurde. Jetzt ermittelt man den freien DSP-Speicher mit Dsp_Available(). Denn während das DSP-Hauptprogramm immer wieder überschrieben wird, können noch weitere DSP-Unterfunktionen eventuell benötigten Speicher blockieren. In diesem Fall entfernt Dsp_FlushSubroutines() die Unterfunktionen. Danach läßt sich mit Dsp_Reserve() der benötigte Speicher reservieren.

Wie in [1] beschrieben, ist der Programmspeicher in den X- und Y-Speicher eingeblendet. Deshalb müssen die unteren 16 K-Worte des benötigten Programmspeichers zum Y-Speicher hinzugerechnet werden, während die oberen 16 K-Worte dem X-Speicher zufallen. Wie groß die einzelnen Segmente allerdings wirklich sind, läßt sich mit Sicherheit nur über die vom DSP-Linker erzeugte LOD-Datei ermitteln. Den detaillierten Aufbau der LOD-Datei beschreibt der nächste Teil dieses Kurses.

Genauere Kenntnisse über die exakte Speichersegmentiemng benötigt man auch für die nächste Funktion, die aus der LOD-Datei (im ASCII-Format) eine Binärdatei für den DSP berechnet. Dsp_LodToBinary() verlangt nämlich nach einem ausreichend großen Puffer für den DSP-Code. Dazu gibt ATARI für die Berechnung der Puffergröße folgende Formel an:

size = wsize * ( nwords + (wsize * nblocks))

mit

wsize: DSP-Wortgröße in Bytes (von Dsp_GetWordSize())
nwords: Anzahl aller DSP_Wörter des Programms (X-, Y- und P-Speicher)
nblocks: Anzahl der Speicherblöcke (jeder Wechsel zwischen den Speichern, bei HOSTJO.LOD also 2)

Nun endlich kann das Programm mit Dsp_ExecProg() gestartet werden. Die Funktion Dsp_LoadProg() bietet eine einfachere Alternative zu den letzten beiden Befehlen, da hier beide Schritte zusammengefaßt sind.

DSP an Host

Offiziell läuft das DSP-Programm jetzt. Da aber noch die zu berechnenden Daten fehlen, wird der DSP wohl vorläufig in einer Warteschleife seine Runden drehen (siehe auch HOST_IO.ASM).

An diesem Punkt sollte feststehen, ob der DSP Echtzeitaufgaben zu bewältigen hat oder ob einfach nur möglichst schnell möglichst viele Daten zu berechnen sind. Im ersten Fall bezieht der DSP nämlich seine Daten interruptgesteuert und muß spätestens bis zum Auftreten des nächsten Interrupts die vorherigen Daten bearbeitet haben. Auf gleiche Weise exportiert er seine Ergebnisse.

Als Schnittstelle bietet sich der Port C (Synchronous Serial Interface SSI) an, wobei der Multiplexer (Ein-/Ausgabematrix), der auch mit dem Audiosubsystem verbunden ist, den Datentransport auf der Systemseite übernimmt. Ganz nebenbei fungiert der Multiplexer auch als externer Taktgeber und damit als Interrupt-Auslöser.

Andernfalls, also bei „Falschzeitaufgaben“, kümmert sich der Hauptprozessor um den Datenaustausch mit dem DSP, wobei dieser dann via Port B (Host Interface) kommuniziert. Allerdings bedient man sich auch dieses Weges, wenn es darum geht, Parameter für die variable Steuerung von Echtzeitaufgaben an den DSP zu schicken.

Die Host-Seite wird wieder kräftig durch das XBIOS unterstützt. Soll zum Beispiel auf einen Schlag gleich ein ganzer Speicherblock in Richtung DSP, kommt es darauf an, wie die Daten im Speicher angeordnet sind, denn seine bevorzugte Kost sind, wie inzwischen bekannt, die Drei-Byte-Wörter. Diese Aufgabe übernimmt die Funktion Dsp_DoBlock(). Dsp_BlkUnpacked() bereitet Langwörter (32 Bit) DSP-gerecht auf, Dsp_BlkWords() versorgt den DSP mit Integern (16 Bit), und Dsp_BlKBytes() speist ihn folgerichtig mit einzelnen Bytes. Eine Sonderrolle kommt der Funktion Dsp_BlkHandshake() zu. Diese verlangt zwar ebenfalls Drei-Byte-Wörter, wartet aber bei jedem DSP-Wort-transfer auf eine Übertragungsbestätigung seitens des DSP.

All diesen Funktionen ist gemeinsam, daß sie zuerst einen Block senden, um anschließend einen Block zu empfangen. Die Blockgrößen übergibt man den Funktionen. Hier muß der Host-Prozessor also so lange warten, bis das letzte Wort vom DSP empfangen wurde. Dieses Problem läßt sich mit Dsp_HStat() (1: DSP ist bereit zum Senden, 2: DSP ist bereit zum Empfangen) nur geringfügig abschwächen.

Möchte man dagegen den Transfer im Hintergrund erledigen, um den MC68030 derweil mit anderen Aufgaben zu beschäftigen, hat man die Wahl zwischen den Funktionen Dsp_InStream(), Dsp_OutStream() und Dsp_IOStream(). Hier installiert das XBIOS kleine Interrupt-Routinen, die den Transfer übernehmen. Wie die Namen schon verraten, ist die letzte Funktion nichts anderes als eine Aneinanderreihung der ersten beiden Befehle. Alle drei Befehle verlangen allerdings das auf der Host-Seite recht unübliche Drei-Byte-Format.

Eine etwas langsamere Alternative bietet die Funktion Dsp_SetVectors(). Diese überläßt es eigenen Interrupt-Funktionen (jeweils eine für Senden und für Empfangen), in welchem Format Daten vorliegen und wie mit ihnen umzugehen ist. Ein weiterer Vorteil des Hintergrundtransfers: mit Dsp_RemoveVectors() läßt sich eine Übermittlung jederzeit abbrechen.

Bild 1: Der Multiplexer verbindet auf flexible Weise alle Sound-Subsysteme

Abgerundet werden die Kommunikationsleitungen durch vier Signal-Bits. Alle vier Bits lassen sich sowohl vom Host wie vom DSP abfragen. Während sich die ersten beiden Bits („Hf0“ und „Hf1“) nur vom Host setzen lassen, hat der DSP für die verbleibenden beiden Bits („Hf2“ und „Hf3“) den ausschließlichen Schreibzugriff.

Soweit ist die Schnittstelle vom Hauptprozessor zum DSP abgehandelt. Das kleine DSP-Assembler-Listing „HOST_IO.ASM“ erklärt am besten, wie mit den Host-IO-Registern auf DSP-Seite umzugehen ist.

Kommen wir nun zum Audio-Subsystem beziehungsweise dem Port C (SSI) auf DSP-Seite,

Neben dem DSP im Falcon hat ATARI mit der Implementierung des Datenpfadmultiplexers (siehe Bild 1) den Falcon nochmals deutlich wertgesteigert. Mit diesem Multiplexer lassen sich Sound-Daten im speziellen und, abgesehen vom AD/DA-Wandler, auch beliebige andere Daten kreuz und quer über alle Rechnerkomponenten verschicken. Wie Bild 1 zeigt, unterscheidet der Multiplexer zwischen vier verschiedenen Quellen und vier verschiedenen Zielen, die sich mit einer Ausnahme beliebig verbinden lassen: jede Quelle und jedes Ziel darf nur einmal verknüpft werden. Mit dem DSP-Ein- und -Ausgang ist die eben angesprochene SSI-Schnittstelle gemeint. Zum Beispiel lassen sich die Sound-Daten des AD-Wandlers zum DSP schicken, und der wiederum schickt seine geänderten Daten über DMA-Aufnahme in den Speicher.

Doch eins nach dem anderen. Vorher sind noch Routinearbeiten nötig: locksnd() ist das entsprechende Pendant von DSP_Lock() für das Soundsystem. sndstatus(1) „resetet“ den DA/AD-Wandler und den Multiplexer. Mit dem Parameter Null liefert die Funktion nur verschiedene Fehler, die während des Betriebes aufgetreten sind. Interessant sind die Bits 4 und 5, die einen linken bzw. rechten Überlauf signalisieren (z.B. bei Übersteuerung).

Die ersten Falcon-TOS-Versionen haben bei diesem Sound-Reset die externen DSP-Leitungen vom Multiplexer abgekoppelt (nur wichtig, wenn der externe Port direkt den DSP-SSI-Port versorgt). Um den Multiplexer nicht unnötig einzuschränken, sollte man mit dsptristate(ENABLE, -ENABLE) den externen DSP-Port wieder mit dem Multiplexer verbinden.

Mit soundcmd() lassen sich die DA-Wandler-Ausgaben in 1.5-dB-Schritten abschwächen (um Übersteuerungen vorzubeugen) und die AD-Wandler-Eingaben in gleichen Schritten verstärken. Außerdem läßt sich, am Multiplexer vorbei, der Mikrophoneingang direkt zum DA-Wandler-Ausgang (also zum Kopfhörerausgang) über einen Hardware-Addierer hinzumischen. Soll an Stelle des Mikrophons der Sound-Chip an den AD-Wandler gekoppelt werden, führt kein Weg an soundcmd(ADCINPUT,3) vorbei.

Mit setsndmode() bestimmt man den Aufnahme- und Wiedergabemodus (8 Bit Stereo, 16 Bit Stereo und 8 Bit Mono), wobei - und das ist nirgendwo dokumentiert - die DMA-Aufnahme immer im 16-Bit-Stereo Modus erfolgt.

Ist ein DMA-Transfer über die Matrix geplant, müssen zuerst die Aufnahme- und Wiedergabepufferadressen mit setbuffer() gesetzt werden. Die aktuellen Aufnahme-und Abspielpositionen erfährt man dann von buffptr().

Diese Positionen sind zum Beispiel für das Harddisk-Recording wichtig. Installiert man den Aufnahmepuffer mit buffoper(RECREPEAT|RECENABLE) als Ring-Buffer, muß während der Aufnahme der neu beschriebene Pufferinhalt schnell genug auf die Festplatte gebracht werden, bevor dieser wieder überschrieben wird.

Als Höhepunkt ist noch die Ein-/Ausgabematrix zu setzen. Dies erledigt die Funktion devconnect(), wobei sich, wie schon erwähnt, jeder Quelle ein Ziel zuordnen läßt, solange nur ein Ziel nicht von mehreren Quellen angesteuert wird.

Das SSI-Interface auf der DSP-Seite besprechen wir anhand eines Programmbeispiels dann ausführlich im vierten Teil dieses Kurses.

Übersicht DSP-Programmierkurs

Teil 1: Register und Befehlssatz des DSP 56001

Teil 2: I/O-Schnittstellen und I/O-Programmierung des DSP 56001 und des MC68030

Teil 3: Programmiertools des DSP 56001 (Assembler, Linker. Debugger)

Teil 4: Tips und Tricks mit Programmbeispiel

Literatur:

[1] Der Verwandlungskünstler, Teil 1, ST-Computer, 10/93, MAXON Computer

[2] DSP56000/DSP56001 Digital Signal Processor User’s Manual, Motorola, 1990

[3] Das Buch zum ATARI Falcon 030, Dietmar Hendricks, Alexander Herzlinger, Martin Pittelkow, Data Becker, 1. Auflage 1992

/* Dateiname: DSPLIB.H
 * Header for DSP-XBIOS-Library (rev. 3 and rev. 4)
 * Sound- and DSP-IO functions
 * (c)1993 by MAXON-Computer
 * Last update: 28.09.93 by J. Lietzow 
 */

#ifndef __DSPLIB__
#define __DSPLIB___

/*********   DSP IO functions   *******************/

/* sizeof( vdsp ) means Dsp_GetWordSize() */
/* A vdsp-array is organized like a */
/* DSP word array. Actually 3 Bytes */

#define vdsp    char

void Dsp_DoBlock( vdsp *data_in, long size_in,
                  vdsp *data_out, long size_out ); 
void Dsp_BlkHandshake( long *data_in, long size_in,
                       long *data_out, long size_out);
void Dsp_BlkUnpacked( long *data_in, long size_in,
                      long *data_out, long size_out ); 
void Dsp_InStream(vdsp *data_in, long block_size,
                  long num_blocks, long *blocks_done); 
void Dsp_OutStream(vdsp *data_out,long block_size,
                   long num_blocks,long *blocks_done);
void Dsp_IOStream( vdsp *data_in, vdsp *data_out, 
                   long block_insize, 
                   long block_outsize, 
                   long num_blocks, long *blocks_done);
#define RECEIVE 1 
#define TRANSMIT 2

void Dsp_RemoveInterrupts( int mask );
int  Dsp_GetWordSize( void );
int  Dsp_Lock( void ); /* 0 == OK */
void Dsp_Onlock( void );
void Dsp_Available( long *xavailable, long *yavailable ); 
int  Dsp_Reserve( long xreserve, long yreserve ); 
int  Dsp_LoadProg( char *file, int ability, vdsp *buffer ); 
void Dsp_ExecProg( vdsp *buffer, long codesize, int ability ); 
void Dsp_ExecBoot( vdsp *codeptr, long codesize, int ability ); 
long Dsp_LodToBinary( char *file, vdsp *buffer ); 
void Dsp_TriggerHC( int vector ); 
int  Dsp_RequestUniqueAbility( void ); 
int  Dsp_GetProgAbility( void ); 
void Dsp_FlushSubroutines ( void );
int  Dsp_LoadSubroutine( vdsp *ptr, long size, int ability ): 
int  Dsp_InqSubrAbility( int ability ); 
int  Dsp_RunSubroutine( int handle );

#define HF_SET      1
#define HF_CLR      0
#define HF_READ     -1

int  Dsp_Hf0( int flag ); 
int  Dsp_Hf1( int flag ); 
int  Dsp_Hf2( void ); 
int  Dsp_Hf3( void );
void Dsp_BlkWords( int *data_in, long size_in, int *data_out, long size_out ); 
void Dsp_BlkBytes( unsigned char *data_in, 
                   long size_in, 
                   unsigned char *data_out, 
                   long size_out ); 
unsigned char  Dsp_HStat( void ); 
void Dsp_SetVectors(void volatile cdecl
                   (*receiver)(long val), 
                    long volatile cdecl 
                   (*transmitter)(void) );

#define BLKTYPE_LONG    0
#define BLKTYPE_INT     1
#define BLKTYPE_UCHAR   2

typedef struct dspblk 
{
    int     block_type;
    long    blk_size; 
    vdsp    *addr;
} DSPBLK;

void Dsp_MultBlock( long num_send, long num_receive, 
                    DSPBLK *send, DSPBLK *receive );

/************   DSP sound functions   *************/

/*  soundcmd() * /
                    /* data mask: bits of low byte */ 
#define LTATTEN     0 /* LLLL0000   left output */
#define RTATTEN     1 /* RRRR0000   right output */
#define LTGAIN      2 /* LLLL0000   left input */
#define RTGAIN      3 /* rrrr0000   right input */
#define ADDERIN     4 /* 000000MA   M: Muliplexer
                                    A: DA-Coder */ 
#define ADCINPUT    5 /* 000000LR   L:Left-R:Right Sound-Chip */ 
                      /*            otherwise microphone */
#define SETPRESCALE 6 /* 000000VV   1=1/640, 2 = 1/320, 3=1/160 */
                      /* only STE modus */
#define INQUIRE    -1

/*  setsndmode()  */

#define STERE08     0
#define STEREO16    1
#define MONO8       2

/*  buffoper()  */

#define PLAY_ENABLE 1
#define PLAY_REPEAT 2
#define RECORD_ENABLE 4
#define RECORD_REPEAT 8

/*  dsptristate()  */

#define TRISTATE    0
#define ENABLE      1

/*  devconnect()  */

#define DMAPLAY     0
#define DSPXMIT     1
#define EXTINP      2
#define ADC         3

#define DMAREC      0
#define DSPREC      1
#define EXTOUT      2
#define DAC         3

#define CLK25M      0
#define CLKEXTERN   1
#define CLK32M      2 /* not for CODEC */

/* for CLK25M only */

#define CLKSTE      0 /* plus soundcmd(SETPRESCALE,) */
#define CLK50K      1
#define CLK33K      2
#define CLK25K      3
#define CLK20K      4
#define CLK16K      5
#define CLK12K      7
#define CLK10K      9
#define CLK8K       11

#define SHAKE       0
#define NO_SHAKE    1

/* Added SOUND BIOS Errors. */

#define SNDNOTLOCK -128 
#define SNDLOCKED  -129

#define vsnd    char

long locksnd( void );       /* 1 ==> OK */
long unlocksnd( void );     /* 0 ==> OK */
long soundcmd( int mode, int data ); 
long setbuffer( int reg, vsnd *begaddr, vsnd *endaddr ); 
long setsndmode( int mode );
long settracks( int playtracks, int rectracks );
                            /* 0 - 3 */
long setmontrack( int montrack ); /* 0 - 3 */
long setinterrupt( int src_inter, int cause ); 
long buffoper( int mode );
long dsptristate( int dspxmit, int dsprec ); 
long gpio( int mode, int data ); 
long devconnect( int src, int dst, int srcclk, int prescale, int protocol ); 
long sndstatus( int reset ); /* 1: reset CODEC
                                0: inquire */

typedef struct playrec 
{
    vsnd    *playbufptr;
    vsnd    *recbufptr;
    long    reserved1, reserved2;
} PLAYREC;

long buffptr( PLAYREC *pointer );

#endif
/* Dateiname: EXAMPLE.C
 * Rahmenprogramm für die DSP-Steuerung
 * (c)1993 by MAXON-Computer 
 */

#include <stdlib.h>
#include <stdio.h>

#include "dsplib.h"

int     DoDSP ( int ability, char *dsp_lod )
{
    long xmem, ymem; 
    void *buf; 
    long len;

    /* Läuft dieses DSP-Programm noch */

    if ( Dsp_GetProgAbility() == ability ) 
        return ( ability );

    /* hole neue Kennung */

    ability = Dsp_RequestUniqueAbility();

    Dsp_Available( &xmem, &ymem );

    /* hier xmem und ymem überprüfen */
    /* evtl. mit Dsp_FlushSubroutines() */
    /* DSP-Speicher frei machen */

    /* .... */

    /* DSP-Speicher reservieren */

    if ( Dsp_Reserve( xmem, ymem ) != 0 ) 
        return ( -1 );

    /* temp. buffer große berechnen */
    /* nblocks ist die Anzahl der Speicherblöcke in */
    /* der LOD-Datei */

    len = 3 * ( xmem + ymem + ( 3 * nblocks ) );

    if ( ( buf = malloc( len ) ) == NULL ) 
        return ( -1 );

    /* erzeuge im Puffer den DSP-Binär-Code */

    len = Dsp_LodToBinary ( dsp_lod, buf );

    /* Programm zum DSP und ausführen */

    Dsp_ExecProg( buf, len, ability );

    return ( ability );
}

int main( void )
{
    int  ability;
    long curadder, curadc;
    char *rambuf;

    /* Ist überhaupt ein DSP vorhanden, */
    /* und ist es ein DSP mit Wortgröße 3 ? */

    if ( Dsp_GetWordSize () != 3 ) 
        return ( 1 );

    /* sperre sound system für andere Programme */

    if ( locksnd() != 1 ) 
        return ( -1 );

    /* sperre DSP für andere Programme */

    if ( Dsp_Lock() != 0 )
    {
        unlocksnd(); 
        return ( -1 );
    }

    ability = -1;
    ability = DoDSP( ability, "TEST.LOD" );

    /* keine Abschwächung des Ausgabekanals */

    soundcmd( LTATTEN, 0 ); 
    soundcmd( RTATTEN, 0 );

    /* verstärke den Eingabekanal */

    soundcmd( LTGAIN, 0x80 ); 
    soundcmd( RTGAIN, 0x80 );

    /* sichere und setze Eingabe-Quelle */
    /* (1: D/A-Wandler 2:Multiplexer) */

    curadder = soundcmd( ADDERIN, INQUIRE );

    /* Addierereingang nur von Matrix */

    soundcmd( ADDERIN, 2 );

    /* sichere und setze ADC Eingang auf Mikrofon */

    curadc = soundcmd ( ADCINPUT, INQUIRE );
    soundcmd( ADCINPUT, 0 );

    /* Verbinde Matrix mit ext. DSP-Schnittstelle */

    dsptristate( ENABLE, ENABLE );

    rambuf = malloc( 1000000L );

    /* setze 16 Bit Stereo Aufnahme-/Abspielmodus */

    setsndmode( STEREO16 );

    /* setze DMA Abspiel- und Aufnahmepuffer */

    setbuffer( 0, &rambuf[0], &rambuf[100000L] ); 
    setbuffer( 1, &rambuf[0], &rambuf[100000L] );

    /* setze Abspiel- und Aufnahmemodus */

    buffoper( PLAY_ENABLE   | PLAY_REPEAT |
              RECORD_ENABLE | RECORD_REPEAT );

    /* verbinde jede Quelle mit Ziel oder 0 */
    /* hier zum Beispiel: */
    /* ADC -> DMA -> DSP -> DAC */


    devconnect( ADC, DMAREC, CLK_25M,
                CLK50K, NO_SHAKE );

    devconnect( EXTINP, 0,      CLK_25M,
                CLK50K, NO_SHAKE ); 
    devconnect( DSPXMIT,DAC,    CLK_25M,
                CLK50K, NO_SHAKE ); 
    devconnect( DMAPLAY,DSPREC, CLK_25M, 
                CLK50K, NO_SHAKE );

    /* hier kommt das Hauptprogramm bzw. */ 
    /* eine Endlosschleife */

    /* .... */

    /* setze die Ursprungsdaten */

    buffoper( 0 );
    dsptristate( TRISTATE, TRISTATE ); 
    soundcmd( ADDERIN, curadder ); 
    soundcmd( ADCINPUT, curadc );

    Dsp_Unlock(); 
    unlocksnd();

    return ( 0 );
}
; Beispielprogramm für die 
; DSP-Host Schnittstelle 
; (c)1993 by MAXON-Computer
; Liest ein Wort und shiftet es nach rechts 
; (Hf0 = 0) oder nach links (Hf0 = 1) und 
; sendet das Ergebnis zurück 
; Dateiname: IO_HOST.ASM 
; DSP IO-Adressen

PBC equ $ffe0
HCR equ $ffe8 ; Bit 3 und 4 sind Hf2 und Hf3 
HSR equ $ffe9 ; Bit 3 und 4 sind Hf0 und Hf1 
HTX equ $ffeb

    org P:$0 
    jmp start

    org P:$40

start movep #1,X:PBC        ; selektiere Host

      jclr  #0,X:HSR,start  ; warte auf Eingabe
      move  X:HTX,A1        ; hole DSP-Wort
      jclr  #3,X:HSR,right  ; Hf0 gesetzt ?
      lsl   A               ; shifte links
      jmp   xmit
right lsr   A               ; sonst nach rechts
xmit  jclr  #1,X:HSR,xmrt   ; frei zum senden ?
      move  A1,X:HTX        ; sende Ergebnis
      jmp   start

      end

_START IO_HOST 0000 0000 0000 DSP56000 4.1.1

_DATA P 0000 
0AF080 000040 
_DATA P 0040
08F4A0 000001 0AA980 000040 54F000 00FFEB 0AA983
00004B 200033 0AF080 00004C 200023 0AA981 00004C
547000 00FFEB 0C0040 
_SYMBOL P
start                I 000040
right                I 00004B
xmit                 I 00004C

_END 0000

Jürgen Lietzow
Aus: ST-Computer 11 / 1993, Seite 116

Links

Copyright-Bestimmungen: siehe Über diese Seite