Atarium: Inside MetaDOS - Audio-Programmierung

So kann es gehen: kaum bequemt man sich mal dazu, etwas Positives über die Firma Commodore zu sagen (siehe Messebericht im letzten Heft), schon geht es ihr schlechter (vgl. Spiegel-Ausgabe vom 11. April 1994). Man darf gespannt sein, wie es dort weitergehen wird...

Die ATARI-Szene hat lange gebraucht, sich für die CD-ROMs zu erwärmen, aber nun scheinen sie zum Selbstläufer geworden zu sein. Der Grund sind sinkende Preise für SCSI-Laufwerke, alternative Interfaces, ausgereiftere Treiber-Software und nicht zuletzt die nun auf den Markt drängenden mit ATARI-Software gefüllten CDs.

Im Zuge dieser Entwicklung tauchen auch immer mehr CD-Player-Programme auf. Dies ist kein großes Wunder, fehlt doch in ATARIs MetaDOS-Paket ein entsprechendes Programm. Und wer wollte nicht ab und zu auch mal eine Audio-CD einschieben und sich berieseln lassen?

Weniger verständlich ist, daß sich offenbar fast alle Autoren die Arbeit machen, SCSI- und ACSI-Routinen neu zu entwickeln. Dabei bringen die MetaDOS-BOS-Treiber schon den nötigen Funktionsumfang mit. Da ja wohl niemand ein CD-ROM ohne funktionstüchtigen Betriebssystemtreiber besitzt (Audio-CDs abspielen geht auch billiger), gibt es wirklich keinen Grund, diese nicht auch zu benutzen.

Dafür sprechen natürlich auch noch andere Gründe: wer die Betriebssystemebene wählt, braucht sich keine Gedanken über Kollisionen beim Zugriff auf Hardware-Register zu machen. Ferner kann man auch gleich solche Laufwerke mitbedienen, die über ein anderes Interface (ROM-Port, IDE-Schnittstelle etc.) angeschlossen sind.

So mancher Leser wird nun nicht ganz zu Unrecht ein wenden, daß MetaDOS ja eigentlich ein ‘auslaufendes’ Modell ist und man doch an CD-ROM-Treiber unter MiNT denken sollte. Dies ist völlig richtig, und daher werden wir auch diese Möglichkeit in Betracht ziehen, wenn es auch bislang noch keine entsprechenden MiNT-Treiber (mit Audio-Funktionen) gibt.

Beginnen wir also mit MetaDOS: Diese Betriebssystemerweiterung installiert sich bekanntlich auf zwei Ebenen, nämlich GEMDOS und XBIOS. Auf GEMDOS-Ebene spiegeln die Dateisystemtreiber (‘*.DOS’) ein ganz normales GEMDOS -Laufwerk vor. Den direkten Zugriff auf die Funktionen des Geräts hat man nur über die XBIOS-Funktionen. Die von ATARI dokumentierten Funktionen entsprechen in etwa dem, was das selige ‘CDAR 504’ leisten konnte. Darunter sind einige Eigenheiten, die man bei ‘normalen’ SCSI-Laufwerken vergeblich sucht, und einige Dinge fehlen einfach. Die Standardfunktionen eines CD-Players lassen sich so allerdings leicht implementieren.

Für einen MiNT-Treiber sieht die Sache allerdings schon anders aus. Wenn man nicht gerade die XBIOS-Erweiterungen von MetaDOS emulieren will - mancher würde das einen ‘üblen Hack’ nennen -muß man die Schnittstellen nutzen, die MiNT dafür bietet. Dies sind die GEM-DOS-Funktionen Dcntl() und Fcntl(), mit denen man spezielle Eigenschaften von Geräten steuern kann. Dabei bekommt Dcntl() den Namen einer Datei und Fcntl() das Handle einer Datei.

Dies entspricht dem Unix-Programmiermodell, und so ist es naheliegend, sich auf Unix-Implementationen nach CD-ROM-spezifischen Opcodes umzusehen und diese entsprechend zu übernehmen. Genau dies wurde zu Redaktionsschluß gerade gemacht.

Wie schon oben erwähnt, unterstützt das Standard-MetaDOS-Interface nicht alles, was man mit einem modernen CD-ROM machen kann (und will). Die offensichtliche Lösung ist, dieselben Funktionen, die auch über das Fcntl()-Interface verfügbar sind, ebenso über einen MetaDOS-XBIOS-Aufruf anzubieten. Und wenn dann auch noch MetaDOS selbst und der DOS-Treiber die Funktion Dcntl() unterstützen und entsprechend an den CD-ROM-Treiber weiterleiten, hat man eine Binärkompatibilität zum MiNT-Interface und kann sich Fallunterscheidungen ganz ersparen.

Nochmal zusammengefaßt:

Diesen Monat will ich aber erst einmal auf das eingehen, was alle existierenden MetaDOS-Treiber schon unterstützen (sollten). Die Abbildungen zeigen entsprechende Bindings für PureC.

Die XBIOS-Ebene von MetaDOS arbeitet mit eigenständigen Laufwerksbuchstaben (von ‘A’ bis ‘Z’), die freilich nichts mit den GEMDOS-Laufwerksbuchstaben zu tun haben. Dies sind die Buchstaben, die in der Datei CONFIG.SYS im Zusammenhang mit den B OS-Dateien angegeben sind. Alle XBIOS-Funktionen mit Ausnahme von Metainit() erwarten diesen Laufwerksbuchstaben als ersten Parameter.

Metainit() initialisiert MetaDOS und liefert Informationen zurück. Dabei hat man ausnahmsweise bei ATARI vorausgedacht und die Funktion so deklariert, daß man auch feststellen kann, ob sie überhaupt ausgeführt wurde (was ja bei XBIOS-Funktionen bekanntlich ein Problem darstellt).

Metainit() füllt eine META_INFO_1-Struktur aus, deren Anfangsadresse man als Parameter übergibt. Das erste Element dieser Struktur (mi_drivemap) ist eine ‘Drivemap’, wie man sie bereits von BIOS [Drvmap()] und GEMDOS [Rückgabewert von Dsetdrv()] kennt. Hier steht sie für die installierten MetaDOS-Geräte. MetaDOS wird nur dann installiert, wenn auch ein Gerät gefunden wurde. Löscht man mi_drivemap vor dem Aufruf, kann man also nachher leicht erkennen, ob MetaDOS installiert ist.

Weiterhin trägt MetaDOS in mi_version_string einen Zeiger auf einen Versions-String sowie in mijnfo einen Zeiger auf eine weitere Informationsstruktur ein. Diese Struktur ist seit Version 2.30 vorhanden und enthält eine Versionsnummer (mi_version), zur Absicherung einen Magic-Wert (mi_magic) sowie einen Zeiger auf ein Feld, das zu jedem GEMDOS-Gerät den dazugehörigen MetaDOS-Laufwerksbuchstaben oder Null enthält. Mit Hilfe dieser Information kann man also feststellen, welcher MetaDOS-Laufwerksbuchstabe zu welchem GEMDOS-Laufwerk gehört.

Metaopen() initialisiert ein Gerät und liefert in der ‘META_DRVINFO’-Struktur einen Zeiger auf den Treibemamen zurück. Wenn dieser mit den Zeichen ‘CD’ beginnt, darf man davon ausgehen, daß es sich um einen CD-ROM-Treiber handelt. Metaclose() gibt das Gerät wieder frei (und wird normalerweise ignoriert).

Metaread() liest Daten vom Gerät. Bei CD-ROMs ist die Blockgröße 2048 Bytes, aber leider kann man dies nicht gezielt erfragen. Aufgrund einer Begrenzung in ATARIs BOS-Treibern kann man maximal 63 Blöcke in einem Rutsch lesen (< 128K).

Metawrite() macht bei CD-ROMs logischerweise gar nichts, könnte aber beispielsweise bei Streamer-Treibern eingesetzt werden.

Metastatus() dient dazu, den momentanen Status des Geräts zu erfragen. Der Return-Wert ist eine 32-Bit-Zahl mit einem etwas skurrilen Aufbau. Das High-Word ist entweder 0 (OK) oder 0xFFFF (Fehler). Die unteren 16 Bits enthalten verschiedene Flags: Fehler (Bit 15), Timeout (Bit 7), Medien Wechsel (Bit 2) und Busy (Bit 1). Die restlichen Bits sind reserviert. ATARIs DOS-Treiber fragt zunächst auf ‘negativ’ ab und testet ansonsten das Medienwechsel-Bit. Der Medienwechsel-Status wird bis zum ersten Read-Befehl beibehalten.

Metaioctl() ist die bereits erwähnte Funktion, über die man die Opcodes der GEMDOS-Funktionen Dcntl() bzw. Fcntl() an das Gerät absetzen kann, magic muß dabei FCTL sein. ATARIs DOS-Treiber benutzt bereits jetzt den Opcode CDROMREAD-OFFSET (0x4300), über den die Nummmer des ersten Sektors der letzten Session einer Multisession-CD erfragt wird. Wenn der aufgerufene BOS-Treiber diese Funktion unterstützt (CD-ARGEN.BOS tut es nicht, wohl aber andere kommerziell erhältliche Treiber), werden also auch Multisession-CDs korrekt erkannt. buffer ist in diesem Fall ein Zeiger auf einen Long-Wert, in dem die Blocknummer eingetragen wird.

Achtung: Wenn ein BOS-Treiber diese Funktion nicht unterstützt, liefert er entweder EINVFN (-32: dieser Opcode existiert) oder EUNKNOWN (-3: Metaioctl() wird nicht unterstützt) zurück.

Die letzten fünf Funktionen befassen sich mit den Audio-Kommandos eines CD-ROMs und funktionieren auf anderen Gerätetypen logischerweise nicht. CD-ROMs kennen zwei verschiedene Methoden zur Angabe einer Position auf der CD. Eine ‘LBA’ (Logical Block Address) entspricht einer Sektornummer auf einer Festplatte: die Sektoren sind, bei Null beginnend, fortlaufend durchnumeriert. Eine ‘MSF’-Adresse hingegen besteht aus Minute, Sekunde und ‘Frame’, wobei jede Sekunde aus 75 Frames besteht (0...74). Um die Sache noch etwas interessanter zu machen, hat der erste logische Block auf dem Medium (LBA 0) die MSF-Adresse 00:02:00.

Metasetsongtime() startet eine Audio-Wiedergabe. Dabei sind ‘starttime’ und ‘stoptime’ 32 Bit große BCD-Zahlen in MSF-Codierung. Für den Anfang des ersten Lieds würde man also 0x00000200 übergeben (die obersten acht Bits bleiben unbenutzt). Wenn man ‘flag’ auf eins setzt, wird im Repeat-Modus abgespielt. Dieser Modus wird meines Wissens nur von CD-ARGEN.BOS und auch nur für das ‘CDAR504’ unterstützt.

Metagettoc() liefert im angegebenen Buffer das Inhaltsverzeichnis der CD zurück (buffer muß Platz für 128 Einträge haben). Jeder einzelne Eintrag besteht aus Track-Nummer (0: Record hat keine Bedeutung, 0x01...0x99: Track-Nummer in BCD-Codierung, 0xa0: ‘erster’ Track bei programmierter Reihenfolge, 0xa2: Ende der CD) und MSF-Adresse (jeweils BCD-Format). Wiederum ein Relikt aus der CDAR504-Zeit sind der Parameter flag sowie die speziellen Track-Nummern 0xa0 und 0xa1.

Metadiscinfo() liefert eine Informationsstruktur über den aktuellen Status. Die Positionsangaben sind dabei wiederum im BCD-MSF-Format. Vorsicht: index wird von alten BOS-Treibern nicht gesetzt, und disctype ist bei SCSI-CD-ROMs nicht gesetzt.

Metastartaudio() dient der Audio-Wiedergabe. Auf SCSI-Geräten kann man nur einen Modus (flag == 0) benutzen: in diesem Fall übergibt man in bytearray[0] die Anzahl der Lieder und in bytearray[1] die Nummer des ersten Lieds. Metastopaudio() schließlich beendet den Audio-Betrieb.

Soviel für diesen Monat. Demnächst folgt dann die Beschreibung der Opcodes für Fcntl(). Bis dann!


JR

/*
    Bindings for MetaDOS functions
    (c)1994 by MAXON-Computer
    Autor: Julian F. Reschke (jr@ma.maus.de),
    17. April 1994
    Free distribution and usage allowed as long as 
    the file remains unchanged.
*/

#include <metados.h>
#include <tos.h>

void
Metainit (META_INFO_1 *buffer)
{
xbios (0x30, buffer);
}

long
Metaopen (short drive, META_DRVINFO *buffer)
{
return xbios (0x31, drive, buffer);
}

long
Metaclose (short drive)
{
return xbios (0x32, drive);
}

long
Metaread (short drive, void *buffer, long blockno, short blks)
{
return xbios (0x33, drive, buffer, blockno, blks);
}

long
Metawrite (short drive, void *buffer, long blockno, short blks)
{
return xbios (0x34, drive, buffer, blockno, blks);
}

long
Metastatus (short drive, void *buffer)
{
return xbios (0x36, drive, buffer);
}

long
Metaioctl (short drive, long magic, short opcode, void *buffer)
{
return xbios (0x37, drive, magic, opcode, buffer);
}

long
Metastartaudio (short drive, short flag, unsigned char *bytearray)
{
return xbios (0x3b, drive, flag, bytearray);
}

long
Metastopaudio (short drive)
{
return xbios (0x3c, drive);
}

long
Metasetsongtime (short drive, short repeat, long starttime, long endtime)
{
return xbios (0x3d, drive, repeat, starttime, endtime);
}

long
Metagettoc (short drive, short flag, CD_TOC_ENTRY *buffer)
{
return xbios (0x3e, drive, flag, buffer);
}

long
Metadiscinfo (short drive, CD_DISC_INFO *p)
{
return xbios (0x3f, drive, p);
}

/*
    Defines and prototypes for MetaDOS functions 
    (c)1994 by MAXON-Computer
    Autor: Julian F. Reschke (jr@ms.maus.de),
    17. April 1994
    Free distribution and usage allowed as long as 
    the file remains unchanged.
*/

#ifndef _METADOS_H 
#define _METADOS_H

typedef struct
{
    unsigned char trackno, minute, second, frame;
} CD_TOC_ENTRY;

typedef struct 
{
unsigned char disctype; /* 0: audio, 1: data */ 
unsigned char firsttrack, lasttrack, curtrack; 
unsigned char relposz, relposm, relposs, relposf; 
unsigned char absposz, absposm, absposs, absposf; 
unsigned char endposz, endposm, endposs, endposf; 
unsigned char index, res[3]; 
unsigned long reserved[123];
} CD_DISC_INFO;

typedef struct {
unsigned short  mi_version; /* 0x230 == '02.30' */
long            mi_magic;   /* == '_MET' */
/* maps DOS-IDs to MetaDOS XBXOS device numbers */ 
const char      *mi_log2phys;
} META_INFO_2;

typedef struct
{
unsigned long   mi_drivemap;
const char      *mi_version_string;
long            reserved;
META_INFO_2     *mi_info;
} META_INFO_1;

typedef struct
{
char            *mdr_name;
long            res[3];
} META_DRVINFO;

void
Metainit (META_INFO_1 *); 

long
Metaopen (short drive, META_DRVINFO *buffer); 

long
Metaclose (short drive); 

long
Metaread (short drive, void *buffer, long blockno, short blks);

long
Metawrite (short drive, void *buffer, long blockno, short blks);

long
Metastatus (short drive, void *buffer); 

long
Metaioctl (short drive, long magic, short opcode, void *buffer);

long
Metasetsongtime (short drive, short repeat, long starttime, long endtime);

long
Metagettoc (short drive, short flag, CD_TOC_ENTRY *buffer);

long
Metadiscinfo (short drive, CD_DISC_INFO *p); 

long
Metastartaudio (short drive, short flag, unsigned char *bytearray);

long
Metastopaudio (short drive);

#endif

Julian F. Reschke
Aus: ST-Computer 06 / 1994, Seite 63

Links

Copyright-Bestimmungen: siehe Über diese Seite