Neben den Möglichkeiten der Tonerzeugung mittels DMA stehen im STE noch eine Reihe von Funktionen fĂŒr die LautstĂ€rken- und Klangbeeinflussung zur VerfĂŒgung.
Diese werden mit einem Chip namens LMC 1992 verwirklicht, der ĂŒber eine interne serielle Schnittstelle - das Microwire Interface - vom Programmierer angesprochen wird.
Funktionsvielfalt
Der LMC1992 ermöglicht das Einstellen folgender Parameter:
- GesamtlautstÀrke
 
- LautstÀrke des linken Kanals
 
- LautstÀrke des rechten Kanals
 
- Höhen
 
- Tiefen
 
- Zumischung der Signale des Yamaha-Soundchips (den gibtâs ja auch noch)
 
Die GesamtlautstĂ€rke lĂ€Ăt sich von 0 dB (der höchsten LautstĂ€rke) auf -80 dB absenken. Die LautstĂ€rke der KanĂ€le kann getrennt zwischen 0 dB bis -40 dB eingestellt werden. Die Eckfrequenz der Filter fĂŒr die Höhen bzw. Tiefen betrĂ€gt 15 kHz bzw. 50 Hz. Diese Frequenzen sind leider fest vorgegeben und können vom Benutzer nicht verĂ€ndert werden. Der Regelbereich reicht von -12 dB bis +12 dB. Die Werte fĂŒr die LautstĂ€rken und die Filter können in Schritten zu 2 dB verĂ€ndert werden.
Der LMC1992 ist ĂŒber ein serielles Microwire Interface angekoppelt, welches im STE ĂŒber zwei Hardware-Register angesprochen wird. Der Aufbau des seriellen Bit-Stroms, der ĂŒber das Microwire Interface gesendet wird, ist Abb. 1 zu entnehmen. Das AdreĂfeld muĂ immer mit der Adresse des LMC 1992 gefĂŒllt sein, nĂ€mlich mit â10â. Theoretisch wĂ€re es möglich, mit einem 2 Bit breiten AdreĂfeld 4 verschiedene Bausteine zu adressieren - vielleicht darf man auf Erweiterungen des STE in naher Zukunft hoffen.
Das Datenfeld des Bit-Stroms besteht aus zwei Teilen: dem 3 Bit langen Kommando und den dazugehörenden Daten. Macht insgesamt 11 Bit, die pro Kommando an den LMC 1992 ĂŒbertragen werden. Die Kommandos mit dem dazugehörenden Datenfeld sind in Abb. 2 aufgelistet.
Doch nun zurĂŒck zu den zwei Hardware-Registern. Es sind dies das Microwire-Datenregister (16 Bit, Adresse $FF8922) und das Microwire-Maskenregister(16 Bit, Adresse SFF8924).
Im Datenregister werden die 11 Bits des seriellen Bit-Stroms hinterlegt. Das Maskenregister dient der Kennzeichnung, welches der Bits im Datenregister gĂŒltig ist (zugehöriges Masken-Bit gesetzt) und welches Bit ignoriert werden soll (Masken-Bit gelöscht). Zu ignorierende Bits erscheinen nicht im Datenstrom.
Die Maske kennzeichnet demnach die 11 Bits aus dem 16-Bit-Datenregister, die fĂŒr den seriellen Bit-Strom relevant sind. Ein Wert von $7F im Maskenregister bewirkt beispielsweise, daĂ die unteren 11 Bits des Datenregisters gesendet werden.
Das Maskenregister muĂ einmal zu Beginn gesetzt werden. Erscheint danach ein Wert im Datenregister, wird dieser sofort seriell gewandelt und ĂŒber das Microwire Interface gesendet. Dazu werden die Werte im Daten- und im Maskenregister bitweise nach links geschoben. Ist das Bit des Maskenregisters 1, so wird das Bit des Datenregisters gesendet, andernfalls nicht. Das niederwertigste Bit des Maskenregisters wird anschlieĂend mit dem hinausgeschobenen Bit gefĂŒllt, das niederwertigste Bit im Datenregister wird gelöscht.
Das ganze passiert 16mal und dauert 16”s. Am Ende steht im Maskenregister wieder der alte Wert, das Datenregister ist gelöscht.
Zu beachten ist bei der ganzen Angelegenheit, daĂ vor Beschreiben des Datenregisters das Maskenregister ausgelesen und daraufhin ĂŒberprĂŒft wird, ob eine eventuelle vorige Ăbertragung abgeschlossen ist (16”s sind unter UmstĂ€nden eine lange Zeit). Die Ăbertragung ist noch im Gange, wenn der Wert des Maskenregisters nicht mit dem zu Beginn eingetragenen Wert ĂŒbereinstimmt. FĂŒr diesen Fall muĂ sich das Programm in eine Warteschleife begeben.
Ein kleines Bonbon
Ein Blick auf das deutlich gewachsene Assembler-Modul âSOUT.Sâ zeigt, daĂ hier einige Funktionen dazugekommen sind. Als da wĂ€ren:
- snd_laut : Setzen der GesamtlautstÀrke
Parameter : LautstÀrke in dB 
- snd_links : LautstÀrke des linken Kanals
Parameter : LautstÀrke in dB 
- snd_rechts : LautstÀrke des rechten Kanals
Parameter : LautstÀrke in dB 
- snd_hoehen : Einstellung der Höhen
Parameter : Höhen in dB 
- snd_tiefen : Einstellung der Tiefen
Parameter : Tiefen in dB 
- snd_mix : Zumischung des Yamaha-Soundchips
Parameter : 0, 1, 2 (siehe Konstanten in âSOUT.Hâ) 
- snd_init : Initialisierung der verÀnderbaren Werte
 
Damit die Routinen nicht einfach so in der Luft hĂ€ngen, habe ich im Listing âFKT_GEN.Câ einen Funktionsgenerator mit einstellbarer Kurvenform, Frequenz und LautstĂ€rke realisiert.
Literatur
STE Developer Addendum
	
	Abb. 1: Der Aufbau des 11 Bit langen seriellen Bit-Stroms
	
	Abb. 2: Die Kommandos zur LautstÀrken- und Klangbeeinflussung mit dem LMC1992
p:fkt_gen.prg = 
tcstart.o 
sout.s 
fkt_gen.c 
tcfltlib.lib 
tcstdlib.lib 
tctoslib.lib 
tcextlib.lib 
tcgemlib.lib
Project-Datei zu FKT_GEN.C
/* ----------------------------------------- */
/* FKT_GEN.C: Funktionsgenerator fĂŒr den STE */
/*                                           */
/* In Turbo-C 2.0                            */
/* implementiert von Peter Engler            */
/* ----------------------------------------- */
#include <stdio.h>
#include <stdlib.h>
#include <ext.h>
#include <tos.h>
#include <vdi.h>
#include <aes.h>
#include <math.h>
#include "sout.h"
#define MAX_FREQ 16000      /* Max. Frequenz: 16000 Hz */ 
#define MIN_FREQ 16         /* Min. Frequenz: 16 Hz    */
#define MAX_LAUT 0          /* Max. Lautstarke: 0 dB   */
#define MIN_LAUT -80        /* Min. Lautstarke: -80 dB */
/* Typdeklaration */ 
typedef struct
{
    int wellenform;    /* Wellenform (Sinus, SĂ€gezahn, ... ) */ 
    int freq, sw_freq; /* Frequenz, zug. Schrittweite */ 
    int laut, sw_laut; /* LautstÀrke, zug. Schrittweite */
    int start;
} EINSTELLUNG;
/* Prototypen der im Modul verwendeten Funktionen */
int snd_alloc( SOUND *, unsigned long );
void snd_free( SOUND * );
void saegezahn( SOUND * );
void sinus( SOUND * };
void dreieck( SOUND * );
void rechteck( SOUND * );
void bildschirm_ausgabe( int, EINSTELLUNG * ); 
void frequenz_ausgabe( int, EINSTELLUNG * ); 
void lautstaerke_ausgabe( int, EINSTELLUNG * ); 
int eingabe_bearbeiten( int, EINSTELLUNG *, SOUND * );
int main( void );
/* Anlegen des Arrays fur die zu wandelnden Bytes */
int snd_alloc( SOUND *snd, unsigned long anz )
{
    /* Speicherplatz belegen */
    snd -> s_ptr = (char *) malloc( anz );
    /* Fehler aufgetreten */ 
    if (! snd -> s_ptr)
    {
        snd -> anz_bytes = 0L; 
        return( -1 );
    }
    /* Anzahl Bytes des reservierten Bereichs */ 
    snd -> anz_bytes = anz;
    /* Anzahl Bytes, die pro Sekunde gewandelt werden */ 
    switch( snd -> mode_reg & 0x000F )
    {
        case MOD_FR50K :
            snd -> bytes_pro_sekunde = 50066L; 
            break; 
        case MOD_FR25K :
            snd -> bytes_pro_sekunde = 25033L; 
            break; 
        case MOD_FR12K :
            snd -> bytes_pro_sekunde = 12517L; 
            break; 
        case MOD_FR6K :
            snd -> bytes_pro_sekunde = 6258L; 
            break;
        default : 
            snd -> bytes_pro_sekunde = 0L;
            break;
    }
    return ( 0 );
}
/* Freigeben des Arrays der SOUND-Struktur */
void snd_free( SOUND *snd )
{
    free( snd -> s_ptr );
}
/* Generieren der Werte fĂŒr einen Sinus */
void sinus ( SOUND *snd )
{
    unsigned long bytes_pro_periode, index; 
    char *h_ptr;
    h_ptr = snd -> s_ptr;
    /* Berechnen der Bytes, die pro Periode ausgegeben werden */ 
    bytes_pro_periode = snd -> bytes_pro_sekunde / snd -> frequenz;
    if ((bytes_pro_periode % 2) == 1) 
        bytes_pro_periode++;
    snd -> anz_bytes = bytes_pro_periode;
    /* EintrĂ€gen der Werte fĂŒr den Sinus in die SOUND-Struktur */ 
    for (index = 0; index < bytes_pro_periode, index++)
    {
        *h_ptr++ =
            (char) (127 * sin< 2.0 * M_PI * ((double) index) / (double) bytes_pro_periode) - 1);
    }
}
/* Generieren der Werte fĂŒr einen SĂ€gezahn */
void saegezahn( SOUND *snd )
{
    unsigned long bytes_pro_periode, index; 
    char *h_ptr;
    h_ptr = snd -> s_ptr,
    /* Berechnen der Bytes, die pro Periode ausgegeben werden */ 
    bytes_pro_periode = snd -> bytes_pro_sekunde / snd -> frequenz;
    if ((bytes_pro_periode % 2) = 1) 
        bytes_pro_periode++;
    snd -> anz_bytes = bytes_pro_periode;
    /* EintrĂ€gen der Werte fĂŒr den SĂ€gezahn in die SOUND-Struktur */ 
    for (index = 0; index < bytes_pro_periode; index++)
    {
        *h_ptr++ =
            (char) ( 255 * ((double) index) / ((double) bytes_pro_periode ) - 128);
    }
}
/* Generieren der Werte fĂŒr ein Dreieck */
void dreieck( SOUND *snd )
{
    unsigned long bytes_pro_halbperiode, index; 
    char *h_ptr;
    h_ptr = snd -> sjptr;
    /* Berechnen der Bytes, die pro Halbperiode ausgegeben werden */ 
    bytes_pro_halbperiode = (snd -> bytes_pro_sekunde / snd -> frequenz) / 2L;
    if ((bytes_pro_halbperiode % 2) == 1) bytes_pro_halbperiode++;
    snd -> anz_bytes = bytes_pro_halbperiode * 2L;
    /* Eintragen der Werte fur das Dreieck in die SOUND-Struktur */ 
    for (index = 0; index < bytes_pro_halbperiode,: index++)
    {
        *h_ptr++ =
            (char) ( 255 * ((double) index) /((double) bytes_pro_halbperiode ) - 128);
    }
    for (index = bytes_pro_halbperiode ; index > 0 ; indexâ-)
    {
        *h_ptr++ =
            (char) ( 255 * ((double) index) / ((double) bytes_pro_halbperiode ) - 128);
    }
}
/* Generieren der Werte fĂŒr ein Rechteck */
void rechteck( SOUND *snd )
{
    unsigned long bytes_pro_halbperiode, index; 
    char *h_ptr;
    h_ptr = snd -> s_ptr;
    /* Berechnen der Bytes, die pro Halbperiode ausgegeben werden */ 
    bytes_pro_halbperiode = (snd -> bytes_prc_sekunde / snd -> frequenz) / 2L;
    if ((bytes_pro_halbperiode % 2) == 1) bytes_pro_halbper±ode++;
    snd -> anz_bytes = bytes_pro_halbperiode * 2L;
    /* EintrĂ€gen der Werte fĂŒr das Rechteck in die SOUND-Struktur */ 
    for (index = 0; index < bytes_pro_halbperiode, index++)
    {
        *h_ptr++ = (char) 127;
    }
    for (index = bytes_pro_halbperiode ; index > 0 ; indexâ-)
    {
        *h_ptr++ = (char) -128;
    }
}
int eingabe_bearbeiten( int handle, EINSTELLUNG * einst, SOUND *snd )
{
    /* Definition der Scancodes der verwendeten Tasten */
    #define SINUS       31
    #define DREIECK     32
    #define SAEGEZAHN   21
    #define RECHTECK    19
    #define FR_EING     33
    #define FR_MINUS    75
    #define FR_PLUS     77
    #define SCHR_WEITE  17
    #define LT_EING     38
    #define LT_MINUS    80
    #define LT_PLUS     72
    #define QUIT        16
    #define F1          59
    #define F10         68
    int scan_code; 
    int ret_code;
    ret_code = 0;
    /* Auf Zeichen von Tastatur warten */ 
    while (! Cconis ( ));
    /* Zeichen einlesen (nur Scancode berĂŒcksichtigen) */ 
    scan_code = ( Crawcin( ) >> 16 );
    switch( scan_code )
    {
        case QUIT       : ret_code = -1;
                          break;
        case F1         : einst -> start = 1;
                          break;
        case F10        : snd_stop( );
                          einst -> start = 0; 
                          break;
        case SINUS      : einst -> wellenform = SINUS;
                          break;
        case DREIECK    : einst -> wellenform = DREIECK;
                          break;
        case SAEGEZAHN  : einst -> wellenform = SAEGEZAHN;
                          break;
        case RECHTECK   : einst -> wellenform = RECHTECK;
                          break;
        case FR_EING    :
                    {
                        vs_curaddress( handle, 10, 18 ); 
                        v_eeol( handle );
                        /* Zeile ab Cursor löschen */ 
                        scanf( "%d", &(einst -> freq ));
                        frequenz_ausgabe( handle, einst );
                    }
                    break;
        case FR_MINUS   : einst -> freq -= einst -> sw_freq;
                        frequenz_ausgabe( handle, einst ); 
                        break;
        case FR_PLUS    : einst -> freq += einst -> sw_freq;
                        frequenz_ausgabe( handle, einst ); 
                        break;
        case SCHR_WEITE : /* Bei Schrittweite muĂ noch ein weiteres 
                             Zeichen eingelesen werden */
                    {
                        int sc;
                    /* Auf Zeichen von Tastatur warten */
                        while (! Cconis ( ));
                    /* Zeichen einiesen (nur Scancode berĂŒcksichtigen) */ 
                        sc = ( Crawcin( ) >> 16 );
                        if (sc == FR_EING)
                        {
                            vs_curaddress( handle, 13, 39 ); 
                            v_eeol( handle );
                    /* Zeile ab Cursor loschen */ 
                            scanf( "%d", &(einst -> sw_freq ) );
                            frequenz_ausgabe( handle, einst );
                        }
                        else
                        if (sc == LT_EING)
                        {
                            vs_curaddress( handle, 20, 39 ); 
                            v_eeol( handle );
                    /* Zeile ab Cursor löschen */ 
                            scanf( "%d", &(einst -> sw_laut ) );
                            lautstaerke_ausgabe( handle, einst);
                        }
                        else
                            fputc (7, stdout);
                    }
                    break;
        case LT_EING    :
                    {
                        vs_curaddress( handle, 17, 18 ); 
                        v_eeol( handle );
                    /* Zeile ab Cursor löschen */
                        scanf( "%d", &(einst -> laut ) );
                        lautstaerke_ausgabe( handle, einst );
                    }
                    break;
        case LT_MINUS   : einst -> laut -= einst -> sw_laut; 
                        lautstaerke_ausgabe( handle, einst );
                        break;
        case LT_PLUS    : einst -> laut += einst -> sw_laut; 
                        lautstaerke_ausgabe( handle, einst );
                        break;
        default         : fputc( 7, stdout );
                        break;
    }
    /* Wurde nicht Q gedrĂŒckt, werden jetzt die 
       Werte in die SOUND-Struktur geschrieben 
       und ggf. ausgegeben */
    if ( ! ret_code )
    {
        /* Frequenz in SOUND-Struktur */ 
        snd -> frequenz = einst -> freq;
        /* AbhĂ€ngig von der gewĂŒnschten Wellenform 
           werden jetzt die Werte berechnet */ 
        switch( einst -> wellenform )
        {
            case SINUS      : sinus( snd );
                            break;
            case SAEGEZAHN  : saegezahn( snd ); 
                            break;
            case DREIECK    : dreieck( snd ); 
                            break;
            case RECHTECK   : rechteck( snd ); 
                            break;
            default         : snd_stop( );
                            einst -> start = 0; 
                            break,
        }
        /* Soll Ton ausgegeben werden ? */ 
        if ( einst -> start )
        {
            snd_laut( einst -> laut ); /* LautstÀrke einst. */
            snd_play( snd );           /* Ton ausgeben      */
        }
    }
    return( ret_code );
}
/* Frequenzanzeige auf dem Auswahlbildschirm */
void frequenz_ausgabe( int handle, EINSTELLUNG * einst )
{
    char str[20];
    /* GĂŒltigkeit der Werte in einst prĂŒfen */ 
    if (einst -> freq > MAX_FREQ) 
        einst -> freq = MAX_FREQ;
    else
        if (einst -> freq < MIN_FREQ) 
            einst -> freq = MIN_FREQ;
    vs_curaddress( handle, 10, 18 ); /* Cursor setzen */ 
    v_eeol( handle );                /* Zeile ab Cursor löschen */
    sprintf ( str, "%d Hz", einst -> freq); /* Frequenz in str */ 
    v_curtext( handle, str );
}
/* Anzeige der LautstÀrke auf dem Auswahlbildschirm */
void lautstaerke_ausgabe( int handle, EINSTELLUNG *einst )
{
    char str[20];
    /* GĂŒltigkeit der Werte in einst prĂŒfen */ 
    if (einst -> laut > MAX_LAUT) 
        einst -> laut = MAX_LAUT;
    else
        if (einst -> laut < MIN_LAUT) 
            einst -> laut = MIN_LAUT;
    vs_curaddress( handle, 17, 18 );    /* Cursor setzen */ 
    v_eeol( handle );                   /* Zeile ab Cursor löschen */
    sprintf( str, "%d dB", einst -> laut); /* LautstÀrke in str */ 
    v_curtext( handle, str );
}
/* Anzeige des Auswahlbildschirms */
void bildschirm_ausgabe( int handle, EINSTELLUNG *einst )
{
    char str[20];
    /* Ăberschrift */ 
    v_curhome( handle );    /* Cursor nach links oben */ 
    v_rvon( handle );       /* Text invers */ 
    v_curtext( handle, " Funktionsgenerator mit dem DMA-Soundchip            " );
    /* verfĂŒgbare Wellenformen */ 
    v_rvon( handle );       /* Text invers */
    vs_curaddress( handle, 5, 2 );  /* Cursor setzen */
    v_curtext( handle, " Wellenformen :");
    v_rvoff( handle );
    v_curtext( handle, " S = Sinus, D = Dreieck, Z = SĂ€gezahn, R = Rechteck" );
    /* Frequenzauswahl */
    v_rvon( handle );   /* Text invers */
    vs_curaddress( handle, 10, 2 );     /* Cursor setzen */
    v_curtext( handle, " Frequenz ....:"");
    v_rvoff( handle );  /* Text normal */ 
    frequenz_ausgabe ( handle, einst );
    vs_curaddress( handle, 11, 18 );    /* Cursor setzen */
    v_curtext( handle, "F = Eingabe" ); 
    vs_curaddress( handle, 12, 18 );    /* Cursor setzen */
    v_curtext( handle, "Cursor left, right = schrittweise Ănderung" ); 
    vs_curaddress( handle, 13, 18 );    /* Cursor setzen */ 
    v_curtext( handle, "WF = "); 
    v_rvon( handle );   /* Text invers */
    v_curtext( handle, "Schrittweite :"); 
    v_rvoff( handle );
    sprintf ( str, " %d Hz", einst -> sw_freq); /* Schrittweite in str */ 
    v_curtext( handle, str );
    /* LautstÀrkewahl */
    v_rvon( handle );   /* Text invers */
    vs_curaddress( handle, 17, 2 );     /* Cursor setzen */
    v_curtext( handle, " LaustÀrke ...:"); 
    v_rvoff( handle ); /* Text normal */
    lautstaerke_ausgabe( handle, einst );
    vs_curaddress( handle, 18, 18 );    /* Cursor setzen */
    v_curtext( handle, "L  = Eingabe" ); 
    vs_curaddress( handle, 19, 18 );    /* Cursor setzen */
    v_curtext( handle, "Cursor up, down = schrittweise Ănderung" ); 
    vs_curaddress( handle, 20, 18 );    /* Cursor setzen */ 
    v_curtext( handle, "WL = "); 
    v_rvon( handle );   /* Text invers */
    v_curtext( handle, "Schrittweite ); 
    v_rvoff( handle );
    sprintf ( str, " %d dB", einst -> sw_laut); /* Schrittweite in str */ 
    v_curtext( handle, str );
    vs_curaddress( handle, 24, 3 );     /* Cursor setzen */
    v_rvon( handle );
    v_curtext( handle, " F1 = Start, F10 = Stop " );
    vs_curaddress( handle, 24, 69 );    /* Cursor setzen */
    v_curtext( handle, " Q = Quit " );
    v_rvoff( handle );
}
int main( )
{
    SOUND snd;
    EINSTELLUNG einst = { SINUS, 440, 110, 0, 6, 0 } ;
    int handle, dummy;
    /* VDI-Handle holen und Bildschirm löschen */ 
    handle = graf_handle( &dummy, &dummy, &dummy, &dummy );
    v_clrwk( handle );
    /* Textmodus einschalten */ 
    v_enter_cur( handle );
    /* 'mode_reg' immer vor 1. Aufruf von 'snd_alloc' setzen !! */ 
    snd mode_reg = MOD_FR5OK | MOD_MONO; 
    snd.control_reg = SND_IMMER; 
    snd.frequenz = einst.freq; /* in Hz */
    /* Array fĂŒr den Frame anlegen */ 
    if (snd_alloc( &snd, 65536L)) return(-1);
    snd_init( );
    do
    {
        bildschirm_ausgabe( handle, &einst );
    
    } while (! eingabe_bearbeiten( handle, &einst, &snd ) );
    snd_stop( );
    v_exit_cur( handle );
    snd_free( &snd );
    return( 0 );
}
/* SOUT.H: Headerdatei fĂŒr das Assemblermodul SOUT.S */
/* Konstanten fĂŒr das Sound-DMA-Control Register */
#define SND_STOP    0
#define SND_EINMAL  1
#define SND_IMMER   3
/* Konstanten fĂŒr das Sound-Mode Register */
#define MOD_FR6K    0x0000 /* Samplingfrequenz: 6258 Hz */
#define MOD_FR12K   0x0001                  /* 12517 Hz */
#define MOD_FR25K   0x0002                  /* 25033 Hz */
#define MOD_FR50K   0x0003                  /* 50066 Hz */
#define MOD_STEREO  0x0000 /* Stereo-Wiedergabe */
#define MOD_MONO    0x0080 /* Mono-Wiedergabe   */
/* Konstanten fĂŒr die Zumischung der Signale des GI-Soundchips */
#define GI_MIX      1
#define GI_NOMIX    2
#define VOL_12DB    0
 /* Typdeklaration */
typedef struct
{
    unsigned long anz_bytes;
    unsigned long bytes_pro_sekunde;
    int control_reg;
    int mode_reg;
    int frequenz;
    char *s_ptr;
} SOUND;
/* Prototypen der Assemblerfunktionen aus sout.s */
void snd_stop( void );
void snd_play( SOUND * );
void snd_laut( int );
void snd_links( int );
void snd_rechts( int );
void snd_hoehen( int );
void snd__tiefen ( int );
void snd_mix ( int );
void snd_init( void );
* ------------------------------------------------ *
* --- SOUT.S : Routinen zur Programmierung der --- *
* ---          DMA-Soundchips                  --- *
* â--                                          --- *
* --- Zum Einbinden in Turbo-C 2.0,            --- *
* --- von Peter Engler                         --- *
* ------------------------------------------------ *
* --- Deklaration der Routinen  .
10/1991 /1 129
GLOBL snd_stop 
GLOBL snd_play 
GLOBL snd_laut 
GLOBL snd_links 
GLOBL snd_rechts 
GLOBL snd_hoehen 
GLOBL snd_tiefen 
GLOBL snd_mix 
GLOBL snd_init
* --- Adressen der DMA-Soundchipregister
S_CNTRL EQU     $FF8900
F_BASE  EQU     $FF8903
F_COUNT EQU     $FFS908
F_END   EQU     $FF890F
S_MODE  EQU     $FF8920
* --- Adressen der Microwireregister
MW_DATA EQU     $FF8922
MW_MASK EQU     $FF8924
* --- Konstanten fĂŒr die Microwirekommandos
C_LAUT   EQU     $04C0 
C_LINKS  EQU     $0540 
C_RECHTS EQU     $0500 
C_HOEHEN EQU     $0480
C_TIEFEN EQU     $0440 
C_MIX    EQU     $0400
* --- Daten
    DATA
    EVEN
* --- Heap
    BSS
    EVEN
* --- Speicher fĂŒr Parameter reservieren
SND_ADR:
        ds.l    1
LAUTST: ds.w    1 
LINKS:  ds.w    1
RECHTS: ds.w    1   
HOEHEN: ds.w    1 
TIEFEN: ds.w    1 
MIX:    ds.w    1
* --- Code
    TEXT
    EVEN
* --- Aufruf von s_stop im Supervisormodus
snd_stop:
    pea     s_stop      * Adresse von s_stop auf Stack 
    move.w  #$26,-(sp)  * SUPEXEC
    trap    #14 
    addq.l  #6, sp
    rts
* --- Aufruf von s_play im Supervisormodus 
snd_play:
    move.l  a0,SND_ADR  * Adresse der Struktur retten 
    pea     s_play      * Adresse von s_stop auf Stack
    move.w  #$26,-(sp)  * SUPEXEC
    trap    #14         * Xbios-Aufruf
    addq.l  #6,sp       * Stackpointer korrigieren
    rts
* --- Aufruf von s_laut im Supervisormodus 
snd_laut:
    move.w  d0,LAUTST   * Parameter (LautstÀrke) retten
    pea     s_laut      * Adresse von s_laut auf Stack 
    move.w  #$26,-(sp)  * SUPEXEC
    trap    #14         * Xbios-Aufruf
    addq.l  #6,sp       * Stackpointer korrigieren
    rts
* --- Aufruf von s_links im Supervisormodus 
snd_links:
    move.w  d0,LINKS    * Parameter (LautstÀrke links) retten 
    pea     s_links     * Adresse von s_links auf Stack 
    move.w  #$26,-(sp)  * SUPEXEC
    trap    #14         * Xbios-Aufruf
    addq.l  #6,sp       * Stackpointer korrigieren
    rts
* --- Aufruf von s_rechts im Supervisormodus 
snd_rechts:
    move.w  d0,RECHTS   * Parameter (LautstÀrke rechts) retten 
    pea     s_rechts    * Adresse von s_rechts auf Stack 
    move.w  #$26,-(sp)  * SUPEXEC
    trap    #14         * Xbios-Aufruf
    addq.l  #6,sp       * Stackpointer korrigieren
    rts
* --- Aufruf von s_hoehen im Supervisormodus
snd_hoehen:
    move.w  d0,HOEHEN   * Parameter (Hoehen) retten 
    pea     s_hoehen    * Adresse von s_hoehen auf Stack 
    move.w  #$26,-(sp)  * SUPEXEC
    trap    #14         * Xbios-Aufruf
    addq.l  #6,sp       * Stackpointer korrigieren
    rts
* --- Aufruf von s_tiefen im Supervisormodus
snd_tiefen:
    move.w  d0,TIEFEN   * Parameter (Tiefen) retten
    pea     s_tiefen    * Adresse von s_tiefen auf Stack 
    move.w  #$26,-(sp)  * SUPEXEC
    trap    #14         * Xbios-Aufruf
    addq.l  #6,sp       * Stackpointer korrigieren
    rts
* --- Aufruf von s_mix im Supervisormodus 
snd_mix:
    move.w  d0,LAUTST   * Parameter (Mix) retten
    pea     s_mix       * Adresse von s_mix auf Stack 
    move.w  #$26,-(sp)  * SUPEXEC
    trap    #14         * Xbios-Aufruf
    addq.l  #6,sp       * Stackpointer korrigieren
    rts
* ---- Aufruf von s_init im Supervisormodus
snd_init:
    pea     s_init      * Adresse von s_init auf Stack 
    move.w  #$26,-(sp)  * SUPEXEC
    trap    #14         * Xbios-Aufruf
    addq.l  #6,sp       * Stackpointer korrigieren
    rts
* --- Stoppen der Tonausgabe
s_stop:
    move.w  #0, S_CNTRL * Sound-Control-Register löschen
    rts
* --- Ton ausgeben. Der Ton wird durch die SOUND-
*     Struktur beschrieben
s_play:
    movem.l d3/d4,-(sp) * d3 und d4 retten
    movea.l SND_ADR,a0  * Adresse der Sound-Struktur in a0 
    move.l  (a0)+,d0    * Anzahl Bytes in d0
    lea.l   4(a0),a0    * Auf 'control_reg' positionieren 
    move.w  (a0)+,d3    * 'control_reg' in d3 
    move.w  (a0),d4     * 'mode_reg' in d4
    lea.l   4(a0),a0    * Auf âs_ptr' positionieren 
    move.l  (a0),d1     * Adresse der Bytes in d1
    move.l  d1,d2       * Adresse merken
    move.b  d1,F_BASE+4 * Low-Byte eintragen 
    asr.l   #8,d1       * Mid-Byte holen
    move.b  d1,F_BASE+2 * Mid-Byte eintragen 
    asr.l   #8,d1       * High-Byte holen
    move.b  d1,F_BASE   * High-Byte eintragen
    add.l   d0,d2       * Frame-End berechnen
    move.b  d2,F_END+4  * Low-Byte eintragen
    asr.l   #8,d2       * Mid-Byte holen
    move.b  d2,F_END+2  * Mid-Byte eintragen
    asr.l   #8,d2       * High-Byte holen
    move.b  d2,F_END    * High-Byte eintragen
    move.w  d4,S_MODE   * Mode-Register setzen
    move.w  d3,S_CNTRL  * Sound ausgeben (Control-Register) 
    movem.l (sp)+,d3/d4 * d3 und d4 restaurieren
    rts
* --- Einstelien der Gesamtlautstarke
s_laut:
    move.w  LAUTST,d0   * Parameter in d0
    add.w   #80,d0      * Wert fĂŒr Eintragung in Microwire -
    asr.w   #1,d0       * Datenregister aufbereiten
    add.w   #C_LAUT,d0  * GesamtlautstÀrke setzen 
lt_wait:
    cmp.w   #$07FF,MW_MASK * Warten bis voriger Zyklus
    bne     lt_wait        * abgeschlossen
    move.w  d0,MW_DATA     * Datenregister eintragen
    rts
* --- Einstellen der LautstÀrke des linken Kanals
s_links:
    move.w  LINKS,d0    * Parameter in d0 
    add.w   #40,d0      * Wert fĂŒr Eintragung in Microwire -
    asr.w   #1,d0       * Datenregister aufbereiten
    add.w   #C_LINKS,d0 * LautstÀrke links setzen 
lk_wait:
    cmp.w   #$07FF,MW_MASK * Warten bis voriger Zyklus
    bne     lk_wait     * abgeschlossen
    move.w  d0,MW_DATA  * Datenregister eintragen
    rts
* --- Einstellen der Lautstarke des rechten Kanals
s_rechts:
    move.w  RECHTS,d0   * Parameter in d0 
    add.w   #40,d0      * Wert fĂŒr Eintragung in Microwire -
    asr.w   #1,d0       * Datenregister aufbereiten
    add.w   #C_RECHTS,d0 * LautstÀrke rechts setzen 
rt_wait:
    cmp.w   #$07FF,MW_MASK * Warten bis voriger Zyklus
    bne     rt_wait     * abgeschlossen
    move.w  d0,MW_DATA  * Datenregister eintragen
    rts
* ---- Einstellen der Hoehen
s_hoehen:
    move.w  HOEHEN,d0   * Parameter in d0
    add.w   #12,d0      * Wert fĂŒr Eintragung in Microwire -
    asr.w   #1,d0       * Datenregister aufbereiten
    add.w   #C_HOEHEN,d0 * Hoehen setzen 
ho_wait:
    cmp.w   #$07FF,MW_MASK * Warten bis voriger Zyklus
    bne     ho_wait     * abgeschlossen
    move.w  d0,MW_DATA  * Datenregister eintragen
    rts
* --- Einstellen der Tiefen
s_tiefen:
    move.w  TIEFEN,d0   * Parameter in d0
    add.w   #12,d0      * Wert fĂŒr Eintragung in Microwire -
    asr.w   #1,d0       * Datenregister aufbereiten
    add.w   #C_TIEFEN,d0 * Tiefen setzen 
tf_wait:
    cmp.w   #$07FF,MW_MASK * Warten bis voriger Zyklus
    bne     tf_wait     * abgeschlossen
    move.w  d0,MW_DATA  * Datenregister eintragen
    rts
* --- Mischung mit GI-Soundchip festlegen
s_mix:
    move.w  MIX,d0      * Parameter in d0
    add.w   #C_MIX,d0   * Mischung festlegen
mx_wait:
    cmp.w   #$07FF,MW_MASK * Warten bis voriger Zyklus
    bne     mx_wait     * abgeschlossen
    move.w  d0,MW_DATA  * Datenregister eintragen
    rts
* --- Initialisieren der Microwireregister 
s_init:
    move.w  #0,MW_DATA  * Microwire - Datenregister init. 
    move.w  #$07FF,MW_MASK * Microwire - Maskenregister setzen
    bsr     s_stop      * Ton ausschalten
    move.w  #0,LAUTST   * GesamtlautstÀrke auf 0 dB
    bsr     s_laut      * setzen
    move.w  #0,LINKS    * LautstÀrke links auf 0 dB
    bsr     s_links     * setzen
    move.w  #0,RECHTS   * LautstÀrke rechts auf 0 dB 
    bsr     s_rechts    * setzen
    move.w  #0,HOEHEN   * Höhen auf 0 dB 
    bsr     s_hoehen    * setzen
    move.w  #0,TIEFEN   * Tiefen auf 0 dB 
    bsr     s_tiefen    * setzen
    move.w  #1,MIX      * Keine Mischung mit GI-Chip
    bsr     s_mix       *
    rts
    END