Der Ruf des Falken

Wer mit dieser Lautäußerung nur ein heiseres Krächzen verbindet, darf sich nun von ATARIs jüngstem Sproß eines Besseren belehren lassen. Der Falcon 030 nämlich verwöhnt den geneigten Zuhörer mit 16-Bit-Stereosound und macht den eher spartanischen Soundchip der ST-Serie schnell vergessen. Grund genug also, die Soundhardware und die dazugehörigen XBIOS-Aufrufe genauer unter die Lupe zu nehmen. Zum Abschluß gibt es dann noch ein „Sound-Oszilloskop“ zum Abtippen, in dem viele der in diesem Artikel vorgestellten Aufrufe Verwendung finden.

Falcon-Oszilloskop als Accessory

In Abbildung 1 ist der prinzipielle Aufbau des sogenannten Audio-Subsystems dargestellt. Es besteht aus vier Sendeeinheiten, vier Empfangseinheiten und einem Multiplexer, der die Verbindungen zwischen diesen Komponenten herstellt.

Von Sendern...

Die Sendeeinheiten sind Datenlieferanten. Im einzelnen sind folgende Komponenten zu finden:

16-Bit-Stereo-Analog-Digital-Wandler (ADC)

Der ADC ist mit dem Mikrofoneingang auf der Rückseite des Falcon verbunden. Ein anliegendes Stereosignal wandelt er in zwei vorzeichenbehaftete 16-Bit-Wörter um (jeweils ein Wort für den linken und den rechten Kanal), womit sich der Wertebereich der beiden Werte von -32768 bis 32767 erstreckt. Liegt kein Signal an, liefert der ADC Werte nahe Null. Die Eingangsverstärkung (gain) beider Kanäle ist getrennt von 0 bis 15 einstellbar.

Wahlweise kann auch anstatt des Mikrofonsignals das Ausgangssignal des PSG (Programmable Sound Generator) auf den ADC-Eingang geschaltet werden. Dieser aus dem ST bekannte Soundchip ist im Falcon aus Kompatibilitätsgründen noch vorhanden und kann sich auf diesem Wege Gehör verschaffen.

DMA-Wiedergabe

Der DMA-Wiedergabekanal liest ohne Zutun der 68030-CPU Daten aus dem Speicher. Dies geschieht mit einer Geschwindigkeit von maximal einem Megabyte pro Sekunde. Die Ausgabe kann einmalig oder im Loop-Modus erfolgen; in diesem Fall wird der angegebene Speicherbereich immer wieder von vorne ausgelesen. Die Adresse der Stelle, die gerade ausgelesen wird, kann erfragt werden, auch kann bei Erreichen des Pufferendes ein Timer-A-oder MFP-7-Interrupt ausgelöst werden.

DSP-Ausgang

Der DSP ist über sein SSI-Interface mit dem Audiosubsystem verbunden. Diese serielle Hochgeschwindigkeits-Schnittstelle kann Daten mit einem Megabyte pro Sekunde übertragen. Die Anwendungsgebiete des DSPs können an dieser Stelle nicht ausführlich behandelt werden, eine Einführung dazu finden Sie in [1]. Es soll an dieser Stelle jedoch ausdrücklich darauf hingewiesen werden, daß es sich bei den DSP-Daten nicht zwangsläufig um Audiodaten handeln muß. Man könnte z.B im Signalprozessor einen schnellen Packer/Entpacker realisieren, der auf diesem Weg Daten mit dem System austauscht.

Externer Eingang

Eine weitere serielle Schnittstelle ist der externe Eingabekanal, der über den DSP-Anschluß auf der Rückseite des Falcon erreichbar ist. Auch auf diesem Weg können dem Audiosubsystem Daten zugeführt werden.

... und Empfängern

Die Empfangseinheiten sind Datenkonsumenten. Folgende Komponenten sind vorhanden:

16-Bit-Stereo-DigitaI-Analog-Wandler (DAC)

Der DAC wandelt Digitaldaten zurück in analoge Signale, die er der Kopfhörerbuchse, der Audioleitung des Video-Anschlusses und dem internen Lautsprecher zur Verfügung stellt. Er benötigt je ein vorzeichenbehaftetes Wort für den rechten und den linken Kanal. Die Lautstärke jedes Kanals läßt sich getrennt einstellen. Dies geschieht durch die Wahl der sogenannten Abschwächung (attenuation), die von 0 bis 15 einstellbar ist. Eine Abschwächung von 0 bedeutet größtmögliche Lautstärke, bei einer Abschwächung von 15 ist fast nichts mehr zu hören.

Dem DAC vorgeschaltet ist ein 16-Bit-Hardware-Addierer. Dieser besitzt zwei Eingänge, von denen einer die an den DAC gelieferten Daten aufnimmt. Der andere Eingang ist mit der Ausgabe des ADCs verbunden. So ist es wahlweise möglich, dem Ausgangssignal das Eingangssignal hinzuzumischen. Dies stellt eine einfache Methode dar, das an der Mikrofoneingangsbuchse anliegende Audiosignal direkt auf den Kopfhörerausgang „durchzuschalten“. Addierer, DAC und ADC bilden zusammen den sogenannten CODEC (COder/DECoder).

Abb. 1: Schematischer Auflau des Audiosubsystems
Abb. 2: Multiplexer

DMA-Aufnahme

Eine weitere DMA-Einheit schreibt Daten ohne Benutzung der CPU in den Speicher. Die Geschwindigkeit beträgt ebenfalls maximal ein Megabyte pro Sekunde, auch hier ist ein Betrieb im Loop-Modus möglich. Wie bei der DMA-Wiedergabe kann die aktuelle Schreibposition erfragt und bei Erreichen des Pufferendes ein Interrupt ausgelöst werden. DMA-Aufnahme und -Wiedergabe sind übrigens jeweils mit einem 32-Byte-FIFO-Puffer ausgestattet, der die Daten jeweils in Blöcken von 32 Bytes schreibt oder liest. Dadurch wird der Bus deutlich entlastet.

DSP-Eingang

Über seine SSI-Schnittstelle kann der DSP nicht nur Daten senden, sondern auch empfangen. Man kann beispielsweise die DMA-Lese- und Schreibeinheiten so mit dem DSP verbinden, daß dieser aus einem Speicherbereich Daten empfängt, sie verarbeitet und dann in einen anderen Speicherbereich schreibt.

Externer Ausgang

Der externe Ausgang stellt die andere Hälfte der seriellen Schnittstelle dar, die über den rückwärtigen DSP-Anschluß erreichbar ist. An diese Buchse kann Zusatz-Hardware angeschlossen werden, die auf diesem Weg Daten mit dem Audiosubsystem austauschen kann.

Verschlungene Pfade

Mittelpunkt des Audiosystems ist ein Multiplexer, der auf flexible Art Datenpfade zwischen den zuvor erwähnten Sende- und Empfangskomponenten einrichten kann. Er wird als 4x4-Matrix dargestellt, wobei an den Zeilen die Sender und an den Spalten die Empfänger eingetragen sind (siehe Abbildung 2). Eine Verbindung zwischen einer Sende- und einer Empfangskomponente wird einfach als ausgefüllter Punkt auf der entsprechenden Kreuzung dargestellt. Mehrere Sender können gleichzeitig Daten über den Multiplexer senden, es ist nur darauf zu achten, daß ein Empfänger von höchstens einem Sender beliefert werden kann - in jeder Spalte der Matrix darf sich also höchstens ein Verbindungspunkt befinden. Ein Sender dagegen kann seine Daten an mehrere Empfänger gleichzeitig senden, so können bis zu vier Verbindungen in einer Zeile vorhanden sein.Die Flexibilität dieses Ansatzes wird im Beispiel in Abbildung 2 deutlich: Hier schickt der ADC seine Daten (z.B. Sprache aus einem angeschlossenen Mikrofon) sowohl an die DMA-Aufnahme-Einheit als auch an den DSP. Während die DMA-Einheit die Daten einfach im Speicher ablegt, kann der DSP eine Effektberechnung (z.B. Hall) durchführen. Die veränderten Daten gibt er an den DAC weiter, der das Ergebnis über den Lautsprecher hörbar macht.

### locksnd (XBIOS 128)

Setzt ein Flag, welches anzeigt, daß das Audiosubsystem momentan benutzt wird. Benutzt bereits eine andere Applikation das Audiosubsystem, erhält man einen Fehlercode. Jedes Programm sollte vor der Benutzung weiterer Audiofunktionen diesen Aufruf machen.

Aufrufschema in C:

return = locksnd (void)

C-Binding:

#define locksnd() (LONG)xbios(0x80)

Eingabeparameter:

keine

Rückgabewert:

 1 = Aufruf erfolgreich 

-129 = Audiosystem ist bereits gesperrt

unlocksnd (XBIOS 129)

Gibt das Audiosystem wieder frei

Aufrufschema in C:

return = unlocksnd (void)

C-Binding:

#define unlocksnd() (LONG)xbios(0x81)

Eingabeparameter:

keine

Rückgabewert:

   0 = Aufruf erfolgreich 
-128= Audiosystem war nicht gesperrt

soundcmd (XBIOS 130)

Setzen und Abfragen der CODEC-Einstellungen

Aufrufschema in C:

return = soundcmd (WORD mode, WORD data)

C-Binding:

#define soundcmd(a,b) (LONG)xbios(0x82,a,b)

Eingabeparameter:

mode = 0 (LTATTEN) Abschwächung des linken Ausgangskanals 
                   L = 0... 15
data = 0000 0000 LLLL 0000 (Bit-Vektor)
mode = 1 (RTATTEN) Abschwächung des rechten Ausgangskanals 
                   R = 0... 15
data = 0000 0000 RRRR 0000 (Bit-Vektor)
mode = 2 (LTGAIN) Verstärkung des linken Eingangskanals 
                  L = 0... 15
data = 0000 0000 LLLL 0000 (Bit-Vektor)
mode = 3 (RTGAIN) Verstärkung des rechten Eingangskanals 
                  R = 0... 15
data = 0000 0000 RRRR 0000 (Bit-Vektor)
mode = 4 (ADDRIN) Wählt die Eingabequellen für den 16-Bit-Addierer aus 
data = 0000 0000 0000 00MA (Bit-Vektor)
       M: gesetzt -> Multiplexer als Quelle verwenden 
       A: gesetzt -> ADC als Quelle verwenden
mode = 5 (ADCINPUT) Wählt die Eingabequelle für den ADC aus 
data = 0000 0000 0000 00LR (Bit-Vektor)
       L = Quelle linker Kanal 
       R = Quelle rechter Kanal 
       gelöschtes Bit bedeutet Mikrofoneingabe 
       gesetztes Bit bedeutet Eingabe vom PSG
mode = 6 (SETPRESCALE) Dieser Parameter dient der Kompatibilität dem STE/TT-Sound 
                       Er wird nur beachtet, wenn der mit devconnect (siehe dort)
                        eingestellte Vorteiler gleich Null ist. 
data = 0: Sample-Rate = (6.25 KHz, wird nicht unterstützt)
       1: Sample-Rate = 12.50 KHz 
       2: Sample-Rate = 25.00 KHz 
       3: Sample-Rate = 50.00 KHz 
Übergibt man data = -1, erhält man den momentanen Wert zurück.

Rückgabewert:

Es wird jeweils der alte Wert zurückgegeben.

setbuffer (XBIOS 131)

Setzen des Beginns und Endes des Wiedergabe- oder Aufnahmepuffers

Aufrufschema in C:

return = setbuffer (WORD reg, LONG begaddr, LONG endaddr)

C-Binding:

#define setbuffer(a,b,c) (LONG)xbios(0x83,a,b,c)

Eingabeparameter:

reg = 0 : Setzen des Wiedergabepuffers 
      1 : Setzen des Aufnahmepuffers 
begaddr:  Anfangsadresse des Puffers 
endaddr:  Endadresse des Puffers (erste ungültige Adresse)

Rückgabewert:

0 = kein Fehler

setmode (XBIOS 132)

Auswahl des Aufnahme-/Wiedergabemodus

Aufrufschema in C:

return = setmode (WORD mode)

C-Binding:

#define setmode(a) (LONG)xbios(0x84,a)

Eingabeparameter:

mode = 0: 8 Bit Stereo 
       1:16 Bit Stereo 
       2: 8 Bit Mono

Rückgabewert:

0 = kein Fehler

Taktfragen

Bei der Herstellung einer Verbindung müssen neben Sender und Empfänger auch noch Taktrate und Protokoll angegeben werden. Es sind drei mögliche Taktquellen vorhanden:

Jede Sendekomponente muß eine dieser Taktquellen als Haupttakt auswählen, der CODEC kann jedoch nur mit dem 25.175-MHz- oder dem externen Takt betrieben werden.

Der Haupttakt wird dann vom programmierbaren Vorteiler durch einen Wert zwischen 4 und 24 (einstellbar in Zweierschritten) geteilt. Der resultierende Wert ist die Bit-Rate der Verbindung. Verwendet man also z.B. den 32-MHz-Takt und programmiert den Vorteiler auf 4, ergibt sich eine Bit-Rate von 8 MHz, dies entspricht 1 Megabyte pro Sekunde, also genau der Übertragungsrate des SSI-Interfaces.

Handelt es sich bei der Sendekomponente beispielsweise um den ADC, so ist anstelle der Bit-Rate die Sample-Frequenz von größerem Interesse. Da das Audiosystem in der Lage ist, bis zu vier Stereokanäle zu verarbeiten, können in jeder Sample-Periode bis zu 4x2 Datenwörter anfallen, dies entspricht 128 Bit. Die Sample-Frequenz ist also die Bit-Rate geteilt durch 128, bei einem Haupttakt von 25.175 MHz und einem Vorteiler von z.B. 4 ergibt sich damit eine Sample-Frequenz von 25.175 MHz/4/128 = 49170 Hz. Der Haupttakt läßt sich auch von außen über den DSP-Anschluß zuführen (maximal 32 MHz). Mit einem externen Takt von 22.5792 MHz und einem Vorteilerwert von 4 erhält man eine Sample-Frequenz von 44100 Hz (diese findet bei CD-Playern Verwendung).

Sowohl der DMA-Wiedergabe- als auch der Aufnahmekanal werden mit Übertragungsraten von bis zu einem Megabyte pro Sekunde konfrontiert. Bei starker Busbelastung (beispielsweise durch „gierige“ andere DMA-Geräte oder hohe True-Color-Auflösungen) kann es jedoch vorkommen, daß diese Anforderung nicht erfüllt werden kann. Was dann geschieht, hängt davon ab, mit welchem Protokoll die Verbindung betrieben wird. Zur Wahl stehen der kontinuierliche oder der Handshake-Modus. Sollen Audiodaten übertragen werden, ist ein gelegentlicher Verlust von Daten nicht tragisch. Viel wichtiger ist es, eine konstante Übertragungsrate zu erzielen. In diesem Fall verwendet man den kontinuierlichen Modus. Handelt es sich bei den Daten jedoch z.B. um DSP-Programme, kann ein Datenverlust nicht geduldet werden. Dann wird der Handshake-Modus verwendet, der eine verlustfreie Übertragung garantiert, dafür aber keine feste Übertragungsrate einhält.

In Reih’ und Glied

Bisher wurde immer nur beschrieben, daß Daten von einem Sender an einen Empfänger übertragen werden. Wie sind aber die Informationen für rechten und linken Stereokanal und mehrkanalige Audiodaten kodiert? Betrachten Sie dazu Abbildung 3. Dort sehen Sie, wie die vorzeichenbehafteten Datenwörter im Speicher abgelegt werden. Das Prinzip ist recht einfach: linker und rechter Teil eines Kanals folgen direkt aufeinander, bei mehr als einem Stereokanal werden auch die Samples für die einzelnen Kanäle verzahnt. Schickt man nun einen Datenstrom, der gemäß Abbildung 3b, 3c oder 3d aufgebaut ist (also die Information für mehr als einen Stereokanal enthält), an den DAC, so ist trotzdem nur ein Stereokanal zu hören, die Informationen für die anderen Kanäle werden ignoriert. Das liegt daran, daß eben auch nur ein DAC vorhanden ist. Wer trotzdem alle Kanäle auf einmal hören will, muß zuvor die digitalen Daten mischen, eine Aufgabe, die von der 68030-CPU erledigt werden könnte, typischerweise jedoch vom DSP in Echtzeit durchgeführt wird. Es ist aber auch möglich, die Daten über den DSP-Anschluß einer externen Hardware zuzuführen, die die Kanäle über vier einzelne Stereo-DACs hörbar macht.

Legt man die Anzahl der Aufnahmekanäle auf mehr als einen fest und bestimmt den ADC zur Datenquelle, so liefert dieser zwar einen Datenstrom gemäß Abbildung 3, da man mit einem Stereo-ADC aber auch nur einen Stereokanal aufnehmen kann, werden in den Feldern aller weiteren Kanäle nur Nullen übergeben.

Systemaufrufe

Soviel Hardware-Leistung verlangt nach einer adäquaten Betriebssystemunterstützung. Sie erleichert nicht nur dem Programmierer die Arbeit, sondern ist schlichtweg die Voraussetzung für einen sinnvollen Einsatz der Hardware in einer Multitasking-Umgebung. So ist es nicht mehr nötig, wie noch beim STE/TT-DMA-Sound direkt auf Hardware-Register zuzugreifen. Vielmehr sollten Sie die folgenden XBIOS-Aufrufe verwenden. Auch zur Verwaltung des DSPs wurde eine Betriebssystemschnittstelle geschaffen, deren Behandlung aber den Rahmen dieses Artikels sprengen würde. Eine ausführliche Beschreibung finden Sie in [1].

Im nebenstehenden Kasten finden Sie zunächst Namen und XBIOS-Nummer der Funktion sowie eine kurze Beschreibung. Danach folgt das Aufrufschema in C, aber auch die Benutzer anderer Programmiersprachen sollten damit keine Probleme haben. Der Ausdruck LONG bezeichnet einen 32-Bit-Wert, WORD ist ein 16-Bit-Wert. Der void-Parameter besagt, daß die entsprechende Funktion keine Eingabeparameter erhält. Alle Funktionen geben einen 32-Bit-Wert (LONG) zurück. Das C-Binding macht dem C-Compiler die neuen Funktionen bekannt. Am besten legen Sie eine Datei sndbind.h an, in der Sie alle Bindings sammeln, dann können Sie diese am Beginn Ihrer Programme mit #include <sndbind.h> einbinden. Eine Erläuterung der Parameter finden Sie im Kasten.

Abb. 3: Anordnung der Audiodaten

Praktisches...

Grau ist alle Theorie, darum soll diese Einführung in das Falcon-Audiosystem nicht ohne ein praktisches Anwendungsbeispiel enden. Es handelt sich um ein „Sound-Oszilloskop“, natürlich vollständig in GEM eingebunden. Wilde Kurven zucken im Takt der Musik, die Heimdisco ist gerettet, ein bleibender Eindruck beim staunenden Betrachter garantiert. Das kleine in C und Assembler geschriebene Programm soll Ihnen nur eine Vorstellung davon geben, was man mit der Audiohardware machen kann. Ihren Ideen sind keine Grenzen gesetzt. Wie wäre es z.B. mit einem modularen „Sample-Studio“, welches Aufnahme und Wiedergabe in und aus dem Speicher (oder Festplatte) erlaubt und schrittweise um weitere (DSP-gestützte?) Effekte bereichert wird?

### settrack (XBIOS 133)

Wählt die Anzahl der Aufnahme- und Wiedergabekanäle

Aufrufschema in C:

return = settrack (WORD playtracks, WORD rectracks)

C-Binding:

#define settrack(a,b) (LONG)xbios(0x85,a,b)

Eingabeparameter:

playtracks = 0-3: Anzahl der Wiedergabekanäle-1 
rectracks  = 0-3: Anzahl der Aufnahmekanäle-1

Rückgabewert:

0 = kein Fehler

setmontrack (XBIOS 134)

Wählt unter allen verwendeten Kanälen den aus, der über den DAC abgespielt wird

Aufrufschema in C:

return = setmontrack (WORD montrack)

C-Binding:

#define setmontrack(a) (LONG)xbios(0x86,a)

Eingabeparameter:

montrack = 0-3: ausgewählter Kanal-1

Rückgabewert:

0 = kein Fehler

setinterrupt (XBIOS 135)

Legt fest, welcher Interrupt bei Erreichen des Pufferendes ausgelöst werden soll

Aufrufschema in C:

return = setinterrupt (WORD src_inter, WORD cause)

C-Binding:

#define setinterrupt(a,b) (LONG)xbios(0x87,a,b)

Eingabeparameter:

src_inter = 0: Timer-A-Interrupt auslösen 
            1: MFP-7-lnterrupt auslösen 
cause     = 0000 0000 0000 00RP (Bit-Vektor)
            R: gesetzt -> am Ende des Aufnahmepuffers 
            P: gesetzt -> am Ende des Wiedergabepuffers

Rückgabewert:

0 = kein Fehler

buffoper (XBIOS136)

Starten und Stoppen von Wiedergabe und Aufnahme, dabei kann der Loop-Modus aktiviert werden

Aufrufschema in C:

return = buffoper (WORD mode)

C-Binding:

#define buffoper(a) (LONG)xbios(0x88,a)

Eingabeparameter:

mode = 000000000000 RR RE PR PE (Bit-Vektor)
RR: gesetzt -> Record Repeat (Loop-Modus Aufnahmepuffer)
RE: gesetzt -> Record Enable (Aufnahme starten)
PR: gesetzt -> Play Repeat   (Loop-Modus Wiedergabepuffer)
PE: gesetzt -> Play Enable   (Wiedergabe starten)

Mit mode = -1 erhält man den momentanen Wert zurück.

Rückgabewert:

0 = kein Fehler bzw. alter Wert bei mode = -1

devconnect (XBIOS 139)

Programmierung des Multiplexers. Eine Verbindung zwischen einer Sendeeinheit und einer Empfangseinheit wird hergestellt.

Aufrufschema in C:

return = devconnect (WORD src, WORD dst, WORD srcclk, WORD prescale, WORD protocol) 

C-Binding:

#define devconnect(a,b,c,d,e) (LONG)xbios(0x8b,a,b,c,d,e)

Eingabeparameter:

src:        Sendekomponente:
            0   (DMAPLAY): DMA-Wiedergabe
            1   (DSPXMIT): DSP-Ausgabe
            2   (EXTINP) : externer Eingang
            3   (ADC)    : Analog-Digital-Wandler
dst:        Empfangskomponente 0000 0000 0000 ABCD (Bit-Vektor)
            A: gesetzt-> Digital-Analog-Wandler (DAC)
            B: gesetzt-> externer Ausgang (EXTOUT)
            C: gesetzt-> DSP-Eingabe (DSPREC)
            D: gesetzt-> DMA-Aufnahme (DMAREC) 
srcclk:     Haupttakt
            0: interner 25.175-MHz-Takt 
            1: externer Takt 
            2: interner 32-MHz-Takt 
prescale:   Vorteilerwert (0-11)
            Erhöht man prescale um 1 und verdoppelt das Ergebnis, 
            erhält man den zuvor beschriebenen Vorteiler (von 4 bis 24).yxx 
                0: der bei sndcmd (siehe dort) eingestellte Vorteiler wird benutzt 
                (STE-Kompatibilität).
            Mit dem CODEC darf nur der externe Takt oder der 25.175-MHz-Takt verwendet 
            werden. Im letzteren Fall sind nur folgende Vorteilerwerte erlaubt:
            1:  Sample-Frequenz = 49170 Hz
            2:  Sample-Frequenz = 33880 Hz
            3:  Sample-Frequenz = 24585 Hz
            4:  Sample-Frequenz = 20770 Hz
            5:  Sample-Frequenz = 16940 Hz
            7:  Sample-Frequenz = 12292 Hz
            9:  Sample-Frequenz = 9834  Hz
            11: Sample-Frequenz = 8195  Hz
protocol =  0:   Handshake-Modus 
            1:   kein Handshake  

Rückgabewert

0 = kein Fehler     

Das Programm wurde mit PURE-C entwickelt. Eine kleine Assembler-Routine sorgt für flüssigen Bildaufbau. Das Oszilloskop sollte sich ohne größere Anpassungen mit anderen C-Compilem übersetzen lassen; zu beachten ist lediglich, daß die Parameterübergabe an die Assembler-Routine vollständig über den Stack erfolgen muß. In PURE-C wird dazu das Schlüsselwort cdecl verwendet. PURE-C-Benutzer tippen am besten Listing 1 (das Projekt-File OSCI.PRJ), Listing 2 (das C-Programm OSCI.C) und Listing 3 (die Assembler-Routine CURVE. S) ab, wählen OSCI.PRJ als Projekt aus und aktivieren dann Make.

Das GEM-Programmgerüst ist einfach gehalten und sollte jedem, der schon einmal in GEM programmiert hat, verständlich sein. Eine Einführung in die GEM-Programmierung finden Sie z.B in [2]. Ansonsten finden viele zuvor aufgeführte XBIOS-Funktionen Verwendung. Vor dem Öffnen des OSCI-Fensters wird zunächst mittels locksnd() überprüft, ob das Audiosystem verfügbar ist. Im Fehlerfall wird das Fenster nicht geöffnet. Danach werden die CODEC-Parameter mit soundcmd() gesichert und neu gesetzt. Die Einstellungen für Eingangsverstärkung und Ausgangsabschwächung können Sie gemäß Ihren Anforderungen abändem. Das Audiosystem wird mittels setmode() im 16-Bit-Stereo-Modus betrieben, mit settrack() wird ein Stereo-Aufnahmekanal eingerichtet. Nun wird ein Aufnahmepuffer alloziert und mit setbuffer() der DMA-Einheit bekanntgemacht. Devconnect() stellt die Verbindung vom ADC zur DM A-Aufnahmeeinheit her. Den Handshake-Modus aktiviert man nicht, um eine konstante Aufnahmerate zu erzielen. Die Sample-Rate stellen Sie auf 50 KHz ein. Nun muß nur noch die Aufnahme mittels buffoper() im Loop-Modus gestartet werden. Der angegebene Puffer wird nun zyklisch von der DMA-Einheit mit Daten gefüllt. Gleichzeitig liest das Oszilloskop-Programm die Daten aus und stellt sie als Kurve dar (getrennt für den linken und rechten Kanal). Da dies 25mal in der Sekunde geschehen soll, wurde der entsprechende Programmteil in Assembler geschrieben. Um sicherzugehen, daß nicht die Pufferhälfte ausgelesen wird, die gerade von der DM A-Einheit beschrieben wird, muß die Position des Aufnahmezeigers mit buffptr() erfragt und die Darstellung der Daten solange verzögert werden, bis der Pufferzeiger sich in der jeweils anderen Pufferhälfte befindet. Nach dem Schließen des Fensters muß natürlich der alte Systemzustand wieder hergestellt werden, und auch unlocksnd() darf nicht vergessen werden, sonst läßt sich das Audiosubsystem nicht mehr ansprechen. Das Programm ist übrigens als Accessory oder Applikation zu betreiben (einfach die Datei-Extension auf *.ACC oder *.APP ändern). Es ist selbstverständlich nur auf dem Falcon lauffähig.

... und Handgreifliches

Doch halt! Sie müssen natürlich noch Ihren Falcon mit einer Eingabequelle und evtl, mit einem Verstärker verbinden. Der Mikrofonanschluß ist ein sogenannter Lowgain-Eingang, Sie können also ein einfaches Billigmikrofon ohne eingebauten Verstärker benutzen. Wenn Sie kein solches besitzen und der freundliche Elektronikfachhändler auch keines auf Lager hat, tut es zur Not auch ein herkömmlicher Walkman-Kopfhörer, der kurzerhand zum Mikrofon umfunktioniert wird (einfach in die Mikrofonbuchse stecken!). Liebhaber von sattem Sound, die lieber die Schwingungen ihres CD-Players oder Kassettendecks beobachten wollen, können auch diese mit dem Audio-Eingang des Falcon verbinden, jedoch ist zu beachten, daß der Line-Ausgang dieser Geräte ein viel zu starkes Signal liefert. Hier müssen Sie sich ein kleines Adapterkabel basteln, in dem zwei Widerstände Verwendung finden (siehe Abbildung 4a). Die Dimensionierung dieser Widerstände hängt von der gewünschten Eingangsverstärkung (gain) ab, wie sie mit soundcmd() einstellbar ist. Beim Anschluß an den genormten Line-Ausgang eines HiFi-Gerätes gilt folgende Formel:

R = 15.93 KΩ * 10^(0.075 * N) mit N = Eingangsverstärkung (0-15)

Wer also selbst bei größtmöglicher Eingangsverstärkung (N=15) den Eingang nicht überlasten will, sollte R = 212 KD wählen. Wird ein kleinerer Widerstand verwendet, beispielsweise 127 KD für N=12, sollte man auch die Eingangs Verstärkung nicht größer als 12 wählen, sonst wird der Mikrofoneingang übersteuert. Der Kopfhörerausgang läßt sich gemäß Abbildung 4b mit einem Verstärker verbinden, Widerstände sind hier nicht nötig. Wird der interne Lautsprecher verwendet, sollte er im XControl-Feld auch aktiviert sein, sonst ist nichts zu hören.

Damit sollten Sie nun genug Informationen besitzen, um selbst das Audiosystem des Falcon zu programmieren. Vielleicht fangen Sie ja mit einem kleinen Programm an, das Klänge samplen, abspeichern und später wiedergeben kann? Dann könnten Sie sich auf Ihrer Festplatte eine Soundbibliothek einrichten ... und wer weiß, vielleicht ist ja dann auch der Ruf des Falken dabei!

Literatur:

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

[2] ATARI Profibuch ST-STE-TT, 11. Auflage Hans-Dieter Jankowski, Dietmar Rabich, Sybex Verlag

### sndstatus (XBIOS 140)

Ermittelt den Status des CODEC. Die Parameter des Audiosubsystems werden wahlweise auf definierte Ausgangswerte zurückgesetzt.

Aufrufschema in C:

return = sndstatus (WORD reset)

C-Binding:

#define sndstatus(a) (LONG)xbios(0x8c,a)

Eingabeparameter:

reset = 1:  Das Audiosubsystem wird initialisiert.
            - Die Eingangsverstärkung wird auf 0 gesetzt.
            - Die Ausgangsabschwächung wird auf 0 gesetzt.
            - Alle Multiplexer-Verbindungen werden abgebaut.
            - ADDRIN (siehe soundcmd) wird auf 0 gesetzt.
            - 8-Bit-Stereo-Modus wird aktiviert
            - Anzahl der Wiedergabe- und Aufnahmekanäle wird auf 1 gesetzt.
            - Der aktuelle Kanal (siehe setmontrack) wird auf 1 gesetzt.
            - Alle installierten Interrupts werden deaktiviert
            - Aufnahme/Wiedergabe hält an

Rückgabewert:

0000 0000 00LR SSSS (Bit-Vektor)
L : gesetzt -> Ein Überlauf im linken Kanal ist aufgetreten.
R : gesetzt -> Ein Überlauf im rechten Kanal ist aufgetreten.
SSSS = 1: ungültiges Kontrollfeld 
       2: ungültiges Sync-Format 
       3: ungültige Bit-Rate

buffptr (XBIOS 141)

Gibt die Position des Aufnahme- und Wiedergabezeigers zurück

Aufrufschema in C:

return = buffptr (LONG pointer)

C-Binding:

#define buffptr(a) (LONG)xbios(0x8d,a)

Eingabeparameter:

pointer: muß beim Aufruf auf folgende Struktur zeigen, die dann von buffptr ausgefüllt wird :

struct
{
    LONG playbufptr; /* Zeiger Wiedergabepuffer */
    LONG recbufptr; /* Zeiger Aufnahmepuffer */
    LONG reserviert1;
    LONG reserviert2;
}

Rückgabewert:

0 = kein Fehler
Abb. 4: Verbindung des Falcon mit externer Audioquelle

;************************************************
;* Falcon-Sound-Oszilloskop         Version 1.0 *
;* Autor: Stefan Bock                           *
;* (c)1992 by MAXON-Computer                    *
;* erstellt mit Pure-C Version Feb 26 1992      *
;* CURVE.S : Assembler-Routine für Bildaufbau   *
;************************************************

                .EXPORT zeichne_bild;

NULL_LINKS      EQU 47   ; Nullage linker Kanal
NULL_RECHTS     EQU 127  ; Nullage rechter Kanal
SCHRITTWEITE    EQU 24

                .TEXT

; Register retten, Stackparameter einlesen 
zeichne_bild:   movem.l d0-d7/a0-a6,-(sp)
                move.l 64(sp), a0 ; Zeilenliste
                move.l 68(sp), a1 ; Hintergrund
                move.l 72(sp), a2 ; Audiodaten

; Hintergrundbild ins Gesamtbild kopieren 
kopiere_hntr:   move.l  (a0), a6 
                move.w  #208,d7 
.schleife:      movem.l (a1)+,d0-d6/a5
                movem.l d0-d6/a5, (a6) 
                lea     32(a6),a6
                dbra    d7, .schleife

; Oszilloskopkurve ins Bild odern 
zeichne_krv:    clr.w   d7          ; Wortoffset
.schleife1:     move.w  #$8000, d6  ; linkes Pixel
                move.w  #15,d5      ; 16 Pixel
.schleife2:     move.b  (a2),d2     ; linker Kanal
                move.b  2(a2),d3    ; rechter Kanal
                ext.w   d2          ; vorzeichenrichtig
                ext.w   d3          ; erweitern
                asr.b   #2,d2       ; Wertebereich
                asr.b   #2,d3       ; anpassen
                add.w   #NULL_LINKS, d2 
                add.w   #NULL_RECHTS, d3 
; statt der folgenden vier Befehle ... 
                lsl.w   #2,d2
                lsl.w   #2,d3
                move.l  0(a0, d2.w), a5 
                move.l  0(a0, d3.w), a6 
; ... kann man auch nur zwei Befehle verwenden:
;               move.l  0(a0, d2.w * 4), a5
;               move.l  0(a0, d3.w * 4), a6
; dann ist die Routine nur ab der 68020er CPU 
; verwendbar (dafür aber etwas schneller) 
                or.w    d6, 0(a5, d7.w) 
                or.w    d6, 0(a6, d7.w) 
                lea     SCHRITTWEITE(a2),a2 
                lsr.w   #1, d6 
                dbra    d5, .schleife2 
                addq.w  #2,d7 
                cmp.w   #38,d7
                bne     .schleife1

; das war's schon
fertig:         movem.l (sp)+, d0-d7/a0-a6
                rts

Assembler-Routine für Oszilloskop


/**********************************************/
/* Falcon-Sound-Oszilloskop       Version 1.0 */
/* Autor: Stefan Bock                         */
/* (c)1992 by MAXON-Computer                  */
/* erstellt mit Pure-C Version Feb 26 1992    */
/* OSCI.C : C-Teil                            */
/**********************************************/

#include <stdio.h>
#include <stdlib.h>
#include <tos.h>
#include <time.h>
#include <vdi.h>
#include <aes.h>

/* Die Bindings fur die verwendeten Funktionen */

/* Falls Sie bereits eine Datei "sndbind.h" */ 
/* eingetippt haben (siehe Text), können Sie */ 
/* diese mit #include einbinden und die folgen-*/ 
/* den defines auslassen */

#define locksnd()               xbios(0x80)
#define unlocksnd()             xbios(0x81)
#define soundcmd(a,b)           xbios(0x82,a,b)
#define setbuffer(a,b,c)        xbios(0x83,a,b,c)
#define setsndmode(a)           xbios(0x84,a)
#define settrack(a,b)           xbios(0x85,a,b)
#define setmontrack(a)          xbios(0x86,a)
#define buffoper(a)             xbios(0x88,a)
#define devconnect(a,b,c,d,e)   xbios(0x8B,a,b,c,d,e) 
#define sndstatus(a)            xbios(0x8C,a)
#define buffptr(a)              xbios(0x8D,a)

/* weitere defines */
#define LTATTEN     0
#define RTATTEN     1
#define LTGAIN      2
#define RTGAIN      3
#define ADDERIN     4
#define ADCINPUT    5
#define SETPRESCALE 6
#define ADC         3
#define DMAREC      0x01
#define FENSTER_WORD         19
#define FENSTER_BREITE FENSTER_WORD * 16
#define FENSTER_HOEHE       176
#define NULL_LINKS           47
#define NULL_RECHTS         127
#define BILDER_PRO_SEK       25
#define SAMPLEFREQUENZ   49170L
#define VORTEILER             1
#define AUDIOGROESSE     16000L
#define BILDGROESSE       6688L

#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))

/* Prototyp für die Assemblerroutine */ 
extern void cdecl zeichne_bild (long *zeilen, long *hintergrund, long *samplepuffer);

/* Typvereinbarungen */

typedef struct          /* siehe Xbios-Funktion */
{                       /* buffptr () im Text   */
    long playbufptr; 
    long recbufptr; 
    long resv1; 
    long resv2;
} buffptrtype;

typedef enum            /* Definition eines */
{                       /* Boolean-Typs     */
    FALSE,
    TRUE 
} boolean;

extern int _app;        /* _app = 0 : Accessory     */ 
                        /* app = 1 : Applikation    */

/* die Variablen für die GEM-Initialisierung */ 
int     work_in[103], work_out[57]; 
int     workstation; 
int     gl_apid;
int     gl_hchar, gl_wchar, gl_hbox, gl_wbox;

/* Position der Maus, Zustand der Maustasten */ 
int mx, my, mbutton, mstate, keycode, mclicks;

/* Größe des Desktop, maximale Fenstergröße */ 
GRECT desktop, maxfenster;

/* Fensterparameter */
int     fensterhandle = -1;
int     fensterart = NAME|CLOSER|MOVER;
char    fenstername[14] = " Oszilloskop ";
char    accname[20]     = " Falcon-Oszi V1.0";
GRECT   aussen, innen;




/* Bitmaps und Pufferzeiger */
MFDB    hintergrund, bildschirm, gesamt_abh, gesamt_unabh; 
long    *samplepuffer, audiopuffer[2];
int     index = 0;

/* weitere globale Variablen */
int farben[2] = {0, 1}; /* Vorder- und      */
                        /* Hintergrundfarbe */
long    zeilen[FENSTER_HOEHE];
long    alter_zustand[7];
boolean speicher_da = FALSE;

/* Anmeldung beim GEM */ 
boolean open_vwork (void)
{
    int i, phys;

    if ((gl_apid = appl_init()) != -1)
    {
        for (i = 1; i < 103; work_in[i++] = 1); 
        work_in[10] = 2;
        phys = graf_handle (&gl_wchar, &gl_hchar, &gl_wbox, &gl_hbox); 
        work_in[0] = workstation = phys; 
        v_opnvwk (work_in, &workstation, work_out); 
        return (TRUE);
    }
    else
        return (FALSE);
}

/* Abmeldung beim GEM */ 
void close_vwork (void)
{
    v_clsvwk (workstation); 
    appl_exit();
}

/* Gibt einen Speicherblock nur frei, wenn   */ 
/* dieser vorher erfolgreich alloziert wurde */ 
void testfree (long *speicher)
{
    if (speicher != NULL) 
        free (speicher);
}

/* Gibt allen allozierten Speicher frei */ 
void free_alles (void)
{
    testfree (hintergrund.fd_addr); 
    testfree (gesamt_unabh.fd_addr); 
    testfree (samplepuffer); 
    speicher_da = FALSE;
}

/* Zeichnet das Bild, welches im Hintergrund   */ 
/* des Fensters liegt (Raum zur künstlerischen */ 
/* Entfaltung !)                               */
void zeichne_hintergrund (void)
{
    int z, s, *zeile, distanz;

    zeile = (int *) hintergrund.fd_addr; 
    for (z = 0; z < FENSTER_HOEHE; z++)
    {
        distanz = min(abs(z-NULL_LINKS),abs(z-NULL_RECHTS)); 
        if (z==0 || z==FENSTER_HOEHE - 1 || distanz==0)
        {
            for (s = 0; S < FENSTER_WORD; s++)
                *(zeile+s) = 0xFFFF;
        }
        else if (distanz == 3 || distanz == 4 || 
                ( ( (z+1)%16==0) && (distanz!=0)) )
        {
            *(zeile) = 0x8001;
            for (s = 1; s < FENSTER_WORD; s++)
                *(zeile+s) = 0x0001;
        }
        else if (distanz == 1 || distanz == 2)
        {
            *(zeile) = 0x8101;
            for (s = 1; s < FENSTER_WORD; s++)
                *(zeile+s) = 0x0101;
        }
        else
        {
            *(zeile) = 0x8000;
            *(zeile + FENSTER_WORD - 1) = 0x0001; 
            for (s = 1; s < FENSTER_WORD - 1; s++)
                *(zeile+s) = 0x0000;
        }
        zeile += FENSTER_WORD;
    }
}

/* Ermitteln der Überschneidungsfläche zweier */ 
/* Rechtecke, wird beim Redraw gebraucht      */
int rc_intersect (GRECT *r1, GRECT *r2)
{
    int x, y, w, h;

    x = max(r2->g_x, r1->g_x); 
    y = max(r2->g_y, r1->g_y);
    w = min(r2->g_x + r2->g_w, r1->g_x + r1->g_w);
    h = min(r2->g_y + r2->g_h, r1->g_y + r1->g_h);
    r2->g_x = x;
    r2->g_y = y;
    r2->g_w = w - x;
    r2->g_h = h - y;
    return ((w > x) && (h > y));
}

/* Neuzeichnen der Fensterfläche (Redraw) */ 
void zeichne_fenster (GRECT *flaeche)
{
    GRECT ausschnitt;
    int pxy[8];
    boolean maus_aus = FALSE;

    wind_update (BEG_UPDATE);
    wind_get (fensterhandle, WF_FIRSTXYWH,
              &ausschnitt.g_x, &ausschnitt.g_y, 
              &ausschnitt.g_w, &ausschnitt.g_h); 
    while (ausschnitt.g_w && ausschnitt.g_h)
    {
        if (rc_intersect (&desktop, Sausschnitt))
        {
            if (rc_intersect (flaeche, Sausschnitt))
            {
                pxy[0] = ausschnitt.g_x - innen.g_x; 
                pxy[1] = ausschnitt.g_y - innen.g_y; 
                pxy[2] = pxy[0] + ausschnitt.g_w - 1; 
                pxy[3] = pxy[1] + ausschnitt.g_h - 1; 
                pxy[4] = ausschnitt.g_x; 
                pxy[5] = ausschnitt.g_y; 
                pxy[6] = pxy[4] + ausschnitt.g_w - 1; 
                pxy[7] = pxy[5] + ausschnitt.g_h - 1; 
                if ((maus_aus == FALSE) &&
                    (mx+10 >= ausschnitt-g_x) &&
                    (my+16 >= ausschnitt.g_y) && 
                    (mx<ausschnitt.g_x+ausschnitt.g_w) && 
                    (my<ausschnitt.g_y+ausschnitt.g_h))
                {
                    maus_aus = TRUE; 
                    graf_mouse (M_OFF, NULL);
                }
                vrt_cpyfm (workstation, MD_REPLACE, pxy, &gesamt_abh, &bildschirm, farben);
            }
        }
        wind_get (fensterhandle, WF_NEXTXYWH, 
                  &ausschnitt.g_x, &ausschnitt.g_y, 
                  &ausschnitt.g_w, &ausschnitt.g_h);
    }
    if (maus_aus == TRUE)
        graf_mouse (M_ON, NULL); 
    wind_update (END_UPDATE);
}

/* Initialisierung des Audiosystems, Starten   */ 
/* des Samplens und Öffnen des Fensters, gibt */ 
/* TRUE zurück, wenn Darstellung unmöglich ist */ 
boolean darstellung_an (void)
{
    int i, *hilfspointer;

    if ((FENSTER_BREITE > maxfenster.g.w) || 
        (FENSTER_HOEHE > maxfenster.g_h))
    {
        form_alert (1,"[3][Auflösung zu gering][ Abbruch ]"); 
        return (TRUE);
    }
    if (locksnd () < 0L)
    {
        form_alert (1,"[3][Soundsystem ist gesperrt][ Abbruch ]"); 
        return (TRUE);
    }
    if (speicher_da == FALSE)
    {
        hintergrund.fd_addr  =(long *)malloc (BILDGROESSE); 
        gesamt_unabh.fd_addr =(long *)malloc (BILDGROESSE); 
        samplepuffer = (long *)malloc (AUDIOGROESSE);
    }
    if ((hintergrund.fd_addr == NULL) || (gesamt_unabh.fd_addr == NULL) || (samplepuffer == NULL))
    {
        form_alert (1,"[3][Nicht genug Speicher][ Abbruch ]"); 
        free_alles(); 
        unlocksnd (); 
        return (TRUE);
    }
    else
        speicher_da = TRUE; 
    hilfspointer = (int *) gesamt_unabh.fd_addr; 
    for (i = 0; i<FENSTER_HOEHE; i ++)
    {
        zeilen[i] = (long) hilfspointer; 
        hilfspointer += FENSTER_WORD;
    }
    gesamt_abh.fd_addr = gesamt_unabh.fd_addr; 
    gesamt_abh.fd_w = (gesamt_unabh.fd_w = FENSTER_BREITE); 
    gesamt_abh.fd_h = (gesamt_unabh.fd_h = FENSTER_HOEHE ); 
    gesamt_abh.fd_wdwidth = (gesamt_unabh.fd_wdwidth = FENSTER_WORD); 
    gesamt_abh.fd_nplanes = (gesamt_unabh.fd_nplanes = 1); 
    gesamt_abh.fd_stand   = 0; 
    gesamt_unabh.fd_stand = 1; 
    bildschirm.fd_addr = NULL; 
    audiopuffer[0] = (long) samplepuffer; 
    audiopuffer[1] = (long) (samplepuffer + (AUDIOGROESSE/8L));
    zeichne_hintergrund ();
    fensterhandle = wind_create (fensterart,desktop.g_x, desktop.g_y, FENSTER_BREITE, FENSTER_HOEHE); 
    if (fensterhandle < 0)
    {
        form_alert (1,"[3][Kein Fenster verfügbar][ Abbruch ]"); 
        free_alles (); 
        unlocksnd (); 
        return (TRUE);
    }
    wind_set (fensterhandle, WF_NAME, fenstername); 
    wind_open (fensterhandle,aussen.g_x,aussen.g_y,aussen.g_w,aussen.g_h); 
    for (i = 0; i < 7; i++)
        alter_zustand[i] = soundcmd (i, -1); 
    sndstatus (1);  /* Reset des Audiosystems */
    soundcmd (LTATTEN, 0x00); /*Abschwächung und */ 
    soundcmd (RTATTEN, 0x00); /*Eingangsverst.rk. */ 
    soundcmd (LTGAIN , 0xF0); /*evtl. anpassen ! */
    soundcmd (RTGAIN , 0xF0);
    soundcmd (ADDERIN, 0x01); /*nur Daten vom ADC*/ 
    soundcmd (ADCINPUT, 0x00); /*Mikrofoneingang */ 
    setbuffer (1, (long) samplepuffer, (long) samplepuffer + AUDIOGROESSE); 
    setsndmode (1);     /* 16-Bit-Stereo */
    settrack (0, 0);    /* 1 Kanal für Aufnahme */ 
    setmontrack (0);
    devconnect (ADC, DMAREC, 0, VORTEILER, 1); 
    buffoper(12); /* Aufn. im Loop-Modus starten */ 
    return (FALSE);
}

/* Stoppen des Audiosystems und Wiederher-  */ 
/* stellung des alten Zustands              */
void darstellung_aus (void)
{
    int i;

    buffoper (0);   /* Aufnahme stoppen */
    sndstatus (1);  /* Reset des Audiosystems */
    for (i = 0; i < 7; i++)
        soundcmd (i, alter_zustand[i]); 
    unlocksnd ();   /* Audiosystem freigeben */
}

/* Bringt das Fenster an die Oberfläche     */
void toppe_fenster (void)
{
    wind_set (fensterhandle, WF_TOP);
}

/* Verschiebt das Fenster auf dem Desktop   */
void verschiebe_fenster (GRECT *position)
{
    aussen.g_x = position->g_x; 
    aussen.g_y = position->g_y; 
    aussen.g.w = position->g_w; 
    aussen.g_h = position->g_h; 
    wind_set (fensterhandle, WF_CURRXYWH,aussen.g_x,aussen.g_y,aussen.g_w,aussen.g_h); 
    wind_calc (WC_WORK, fensterart,
               aussen.g_x, aussen.g_y, 
               aussen.g_w, aussen.g_h,
               &innen.g_x, &innen.g_y,
               &innen.g_w, &innen.g_h);
}

/* Schliesst das Fenster */ 
void schliesse_fenster (void)
{
    wind_close  (fensterhandle); 
    wind_delete (fensterhandle); 
    fensterhandle = -1;
}

/* Wartet darauf, daß sich der DMA-Aufnahme-  */ 
/* Zeiger nicht in der Pufferhälfte befindet, */ 
/* auf die audiopuffer[index] zeigt.          */
void warte_dma (void)
{
    buffptrtype aktuell;

    if (index == 0)
    {
        do
        {
            buffptr (&aktuell);
        } while (aktuell.recbufptr<audiopuffer[1]);
    }
    else
    {
        do
        {
            buffptr (&aktuell);
        } while (aktuell.recbufptr>audiopuffer[1]);
    }
}

/* Abfrage und Verwaltung der Fensterelemente */ 
void verwalte_fenster (void)
{
    int     event, msgbuf[0];
    int     ereignisse;
    boolean fertig = FALSE;
    clock_t zwischenzeit, wartezeit;

    if (_app)
    {
        fertig = darstellung_an (); 
        if (fertig == FALSE)
            ereignisse = MU_MESAG | MU_TIMER;
    }
    else
        ereignisse = MU_MESAG;

    zwischenzeit = clock ();
    while ((fertig == FALSE) || (!_app))
    {
        wartezeit = (1000L / BILDER_PRO_SEK) - 5L * (clock () - zwischenzeit); 
        if (wartezeit < 20L)
            wartezeit = 20L; 
        event = evnt_multi (ereignisse,
                            1, 1, 1,
                            0, 0, 0, 0, 0,
                            0, 0, 0, 0, 0,
                            msgbuf,
                            (int) wartezeit, 0,
                            &mx, &my,
                            &mbutton, &mstate, Skeycode, &mclicks); 
        zwischenzeit = clock (); 
        if (event & MU_MESAG)
        {
            switch (msgbuf[0])
            {
                case AC OPEN:
                {
                    if (fensterhandle < 0)
                    {
                        fertig = darstellung_an (); 
                        if (fertig == FALSE)
                            ereignisse = MU_MESAG | MU_TIMER;
                    }
                    else
                        toppe_fenster (); 
                    break;
                }
                case AC_CLOSE:
                {
                    if (fensterhandle >= 0)
                    {
                        darstellung_aus (); 
                        free_alles (); 
                        ereignisse = MU_MESAG; 
                        fensterhandle = -1;
                    }
                    break;
                }
                case WM_CLOSED:
                {
                    if (msgbuf[3] == fensterhandle)
                    {
                        darstellung_aus (); 
                        schliesse_fenster (); 
                        ereignisse = MU_MESAG; 
                        fertig = TRUE;
                    }
                    break;
                }
                case WM_MOVED:
                {
                    if (msgbuf[3] == fensterhandle) 
                        verschiebe_fenster ((GRECT *)&msgbuf[4]);
                    break;
                }
                case WM_REDRAW:
                {
                    if (msgbuf[3] == fensterhandle)
                        zeichne_fenster((GRECT *)&msgbuf[4]); 
                    break;
                }
                case WM_TOPPED:
                {
                    if (msgbuf[3] == fensterhandle) 
                        toppe_fenster ();
                    break;
                }
            }
        }
        if ((event & MU_TIMER) && (fensterhandle>=0))
        {
            warte_dma ();
            zeichne_bild (zeilen, hintergrund.fd_addr, (long *) audiopuffer[index]); 
            vr_trnfm (workstation, &gesamt_unabh, &gesamt_abh); 
            zeichne_fenster (&innen); 
            index = 1 - index;
        }
    }
    free_alles ();
}

/* Es folgt das Hauptprogramm */ 
int main (void)
{
    if (open_vwork () == FALSE)
    {
        printf ("Fehler bei der Initialisierungen"); 
        exit (-1);
    }
    if (!_app)
        menu_register (gl_apid, accname); 
    graf_mouse (ARROW, NULL); 
    wind_get (0, WF_WORKXYWH,
                 &desktop.g_x, &desktop.g_y,
                 &desktop.g_w, &desktop.g_h);
    wind_calc (WC_WORK, fensterart,
                 &desktop.g_x, &desktop.g_y,
                 &desktop.g_w, &desktop.g_h,
                 &maxfenster.g_x, &maxfenster.g_y, 
                 &maxfenster.g_w, &maxfenster.g_h); 
    wind_calc (WC_BORDER, fensterart,
                 maxfenster.g_x, maxfenster.g_y, 
                 FENSTER_BREITE, FENSTER_HOEHE, 
                 &aussen.g_x, &aussen.g_y,
                 &aussen.g_w, &aussen.g_h);
    wind_calc (WC_WORK, fensterart,
                 aussen.g_x, aussen.g_y,
                 aussen.g_w, aussen,g_h,
                &innen.g_x, Sinnen.g_y,
                &innen.g_w, &innen.g_h);
    verwalte_fenster (); 
    close_work (); 
    return (0);
}

Oszilloskop-Hauptprogramm


;************************************************ 
;* Falcon-Sound-Oszilloskop         Version 1.0 *
;* Autor: Stefan Bock                           *
;* (c)1992 by MAXON-Computer                    *
;* erstellt mit Pure-C Version Feb 26 1992      *
;* OSCI.PRJ : Prcjektdatei fur PURE-C (TURBO-C) *
;************************************************

OSCI.APP        ; Name des ausführbaren Programms
=
PCSTART.O       ; Startup (TCSTART.C bei TURBO-C)

OSCI.C  [-A- -H- -Q- -K-]
CURVE.S

PCSTDLIB.LIB    ; (TCSTDLIB.LIB bei TURBO-C) 
PCTOSLIB.LIB    ; (TCTOSLIB.LIB bei TURBO-C) 
PCGEMLIB.LIB    ; (TCGEMLIB.LIB bei TURBO-C)

Projekt-File für PURE-C


Stefan Bock
Aus: ST-Computer 11 / 1992, Seite 104

Links

Copyright-Bestimmungen: siehe Über diese Seite