Zwischen Mac und Maus

Universelles Interface für Apples Desktop Bus

Wissen | Know-how

Seit vielen Jahren informiert der ADB den Mac über Aktivitäten des Anwenders mit Maus und Tastatur. Doch nicht nur dafür taugt der unscheinbare Anschluß: Mit etwas Intelligenz am anderen Ende lassen sich selbst verzwickte Steuerungsaufgaben lösen, für die dem Mac sonst Schnittstellen fehlen. Unser Projekt liefert die passenden Zutaten.

Aufmacher

[#Display-Codes Display-Codes]
[#Debug-Display Debug-Display]
[#MacInterfaceDevice-Register MacInterface Device-Register]
[#Steckerbelegungen Steckerbelegungen]
[#Stückliste Stückliste]
[#ADB-Geheimnisse ADB-Geheimnisse]
[#ADB-Standardadressen ADB-Standardadressen]
[#Register3 Register 3]
[#ADB-Kommandos ADB-Kommandos]
[#Listing Listing]

'Einen Mac mit einer kompatiblen Schnittstelle? Nur über meine Leiche. Schließlich bauen wir keine PCs!' - So oder ähnlich muß die Diskussion mit Steve Jobs über die Ausstattung des ersten Mac abgelaufen sein. Denn seit diesen Tagen quält der Mac kontaktfreudige Nutzer mit seinen kargen und zum Teil proprietären Schnittstellen. Nicht einmal ein paar frei programmierbare Portleitungen, wie sie beispielsweise am Druckerport eines jeden PC zu finden sind, hat er aufzuweisen. Kleine Basteleien [[#literatur 1, 2, 3]] nutzen deshalb regelmäßig den Modemanschluß - was ein lästiges Umstöpseln erfordert, wenn tatsächlich ein Modem die Post bereichern soll.

Aus dieser Unbequemlichkeit heraus entstand das vorliegende Projekt 'MacInterface'. Es macht sich die Tatsache zunutze, daß praktisch jeder heute genutzte Mac mit dem ADB ausgestattet ist und sich ebendieser über hardwareferne, im Betriebssystem implementierte Routinen befehligen läßt. Unsere 'MacInterface'-Platine bietet 16 oder mehr frei programmierbare I/O-Leitungen, eine RS-232-Schnittstelle, einen Chipkarten-Leser und sogar ein alphanumerisches LC-Display für Statusmeldungen; all diese Einrichtungen lassen sich über einfache ADB-Befehle steuern und auslesen - auf einem Power Mac genauso wie auf einem SE. Aber leider ist der ADB außer zu sich selbst zu nichts kompatibel.

Dennoch ist es mit der von Apple herausgegebenen Dokumentation [4, 5] möglich, selbst ein ADB-Device zu konstruieren, das sich mit anderen Bus-'Fahrgästen' wie Maus, Tastatur und eventuell Digitalisiertablett verträgt und über den ADB-Manager sehr einfach in eigene Applikationen einbinden und steuern läßt. Eine gewisse Mindestintelligenz ist dabei Voraussetzung - wer ADB-Device werden will, darf nicht ohne Register, Timer und Speicher daherkommen. Immerhin gilt es nicht nur, Transfers mit dem recht eigenen Timing und Protokoll des Bus abzuwickeln, sondern auch vier Device-Register abzubilden, über die sämtliche Informationen mit dem Mac ausgetauscht werden - etwa die letzte Mausposition, der letzte Tastatur-Scancode oder halt der Pegelstand an einem der MacInterface-Ports. Der ADB-Manager ist dabei das Bindeglied zwischen Applikation und ADB (siehe dazu umseitigen Abschnitt 'ADB-Geheimnisse').

Mit seinem IQ von nur knapp 60, dafür aber recht pfiffigen Befehlen eignet sich Intels 8051 beziehungsweise dessen programmierbares Pendant 8751 recht gut als Mikrocontroller in einem ADB-Gerät (die Loyalität zu den 68ern in allen Ehren, aber für Motorolas Single-Chips hatte ich kein Entwicklungssystem zur Hand - sorry). Außerdem lassen sich die Portleitungen dieses Bausteins durch ihre genial einfache interne Beschaltung (ohne Konfiguration beispielsweise eines Data Direction Registers wie im VIA 6522 oder im PIO 6821) sowohl als Eingang wie auch als Ausgang betreiben. Die Wahl dieses Chips legt auch gleich die Eckdaten der MacInterface-Ausstattung fest: Ohne externes Programm-EPROM und ohne Display nutzten Schaltung und Controller-Programm P0 als dynamischen I/O-Port mit Strobe, P1 als statischen I/O-Port sowie TxD und RxD zum Senden und Empfangen von RS-232-Daten, getrieben von einem MAX232-Transceiver. Optional ist ein LC-Display, das ebenso wie der serielle Kanal ASCII-Zeichen annimmt und einige Steuerzeichen interpretiert; Port P0 läßt sich bei Einsatz des Displays allerdings nur noch in Verbindung mit dem Strobe-Signal verwenden, weil sich die Datenleitungen beim Einschreiben in das Display ändern können.

BESCHREIBUNG
Vergrößern
Das Demoprogramm in Aktion: Hier kann man die Portbits einzeln an- und ausknipsen ...
BESCHREIBUNG
Vergrößern
... und hier einen ADB-Befehl nach eigenem Gusto zusammenstellen. Das Event-Terminal im Hintergrund listet alle Ereignisse an den MacInterface-Ports numerisch auf.

Am einfachsten ist die Funktion von P1 (Stiftleiste ST2) zu verstehen. Ein Schreib-Befehl ('Listen') mit entsprechend gesetztem Selector-Bit (siehe Tabelle) auf Device-Register 0 legt das empfangene Byte an den Port; high-Pegel werden von chipinternen Pull-up-Widerständen sichergestellt. Zieht man 'von außen' an einer auf high liegenden Portleitung, meldet der 8751 den geänderten Port-Stand an den Mac. Schreibt man also den Wert $FF in den Port, können alle Pins als Eingang herhalten. Nach dem Einschalten und dem ADB-Reset liegt P1 auf $F8, das heißt, Bit 0 bis 2 sind Ausgangspins, der Rest läßt sich als Eingang verwenden. Der Request auf dem ADB zum Abholen geänderter Port-Daten wird etwa eine Sekunde aufrechterhalten; hat der ADB-Controller im Mac bis dahin die Daten nicht abgeholt, beispielsweise weil das Device erst nach dem Hochstarten des Rechners angeschlossen wurde und es noch mit keinem Eintrag in der ADB Device Table (s. u.) vertreten ist, nimmt MacInterface den Request zurück.

P0 (ST1) arbeitet als Ausgang genau wie P1, nur daß ein neu eingeschriebener Wert durch einen negativen, etwa 10 µs langen Kicks an '/Strobe' angezeigt wird. Mit einigen TTL-Treibern läßt sich hier eine einfache parallele Druckerschnittstelle realisieren (man darf nur keine PostScript-MBytes verschicken). Auch P0 läßt sich durch Einschreiben von $FF als Eingang verwenden. Hier unterrichtet das Interface den Mac aber erst vom Pegelstand, wenn die Peripherie selbst einen negativen Impuls an '/Strobe' liefert. Verlangt der Mac von sich aus nach P0-Daten, liefert das Interface den Wert des Ports beim letzten '/Strobe' zurück - dabei ist es egal, ob '/Strobe' durch einen Schreibvorgang in P0 oder von außen ausgelöst wurde. 'Busy' zeigt übrigens durch high-Pegel an, daß sich der Controller gerade mit einem eingehenden ADB-Befehl beschäftigt und deshalb noch keine Zeit zum Auslesen des Ports hat; er holt dies nach, sobald 'Busy' wieder auf 'low' geht.

Ähnliches gilt für den RxD-Eingang: Ist 'Busy' gesetzt, sollte ein Peripheriegerät mit der Übertragung von Daten an MacInterface warten. Ist ein Byte vollständig eingetroffen, erfährt auch der Mac davon. Auf der Platine sind übrigens zwei RS-232-Treiber des MAX232 auf die Stiftleiste ST3 geführt, so daß sie bei Bedarf auch die RS-232-Handshake-Signale bedienen können. Das Schreiben in das Serial-Register führt wiederum zum sofortigen Aussenden des Bytes. Die Geschwindigkeit der seriellen Schnittstelle (im Volksmund 'Baudrate' genannt) kann ebenfalls per ADB-Befehl eingestellt werden. Die maximal erreichbare Geschwindigkeit bei allen Schnittstellen ist durch das ADB-Protokoll auf etwa 80 Bytes pro Sekunde begrenzt; nicht überragend viel, aber doch sicher ausreichend für einen Label-Printer oder ein RS-232-gesteuertes Digitalvoltmeter.

BESCHREIBUNG
Vergrößern
Mehrere Optionen stehen neben der Grundausführung zur Auswahl. Die nicht eingezeichnete zweite ADB-Buchse ist mit der ersten parallelgeschaltet und dient zum Durchschleifen zu weiteren Devices wie Maus und Tastatur.

Das optionale LC-Display, ein Standard-Typ mit einer oder zwei Zeile(n) und 16 Zeichen, läßt sich neben seiner Funktion als Statusanzeige zum Beispiel in Zugangskontrollsystemen auch als wertvolles Debugging-Hilfsmittel einsetzen. Über einen speziellen Befehl zeigt es die aktuellen Port-Daten sowie einige interne Register an. Im Normalbetrieb rückt der Cursor des Displays mit jedem Schreiben in den Display-Port um eine Stelle weiter. Einige SCII-Steuercodes ermöglichen eine einfache Textformatierung (siehe Tabelle).

Weil sowohl auf der europakartengroßen Platine als auch im Controller-ROM noch etwas Platz war, habe ich noch einen Chipkarteneinschub nebst Routine zum Auslesen der Seriennummer von handelsüblichen Telefonkarten implementiert. Damit hat es folgende Bewandtnis: Die Seriennummer einer (auch gebrauchten) Telefonkarte ist recht einmalig - jede der -zig Millionen ausgegebenen Karten hat nur 99 gleiche Brüder, weniger als bei vielen Sicherheitsschlüsseln. Auf Befehl liefert MacInterface die Seriennummer der eingeschobenen Karte an den Mac, der dann nach erfolgreicher Überprüfung beispielsweise den Zugang zu einem heiklen Programm oder einem sicherheitsempfindlichen Server ermöglicht.

BESCHREIBUNG
Vergrößern
Das durch Display und Einschub bedingte luftige Layout dürfte keine Schwierigkeiten bei der Bestückung aufwerfen. Von den Karteneinschüben ST6 und ST7 kann selbstverständlich nur eine Version bestückt werden; ST6 steht für die Luxusausführung mit Aufsetzkontakten.
Platine
Vergrößern

Wie das Display ist auch der Karteneinschub und die ihm verbundenen Bauteile (siehe Stückliste) optional. Die Benutzerführung läuft übrigens ohne Zutun des Rechners: Nach Ausgabe des Kartenlese-Befehls meldet das Display (sofern noch nicht geschehen) 'Karte einsetzen', zeigt alsdann die Seriennummer (sie ist in den Telefonkarten-Chips übrigens in umgekehrter Reihenfolge wie aufgedruckt abgelegt) an und bedankt sich schließlich höflich. Die Seriennummer geht dann ASCII-kodiert an den Mac. Setzt der User keine Karte ein, geht MacInterface nach einem Timeout von 10 Sekunden wieder auf Port-Lauerstellung und nimmt 'Busy' zurück. Eine Erweiterung auf andere Chipkarten, etwa auf das I(2C-Protokoll der Krankenkassenkarten, ist sicher möglich, würde aber den Rahmen eines universellen Interface sprengen. Die Platine selbst ist übrigens keinesfalls auf ADB-Betrieb festgenagelt. Wenn man des 8051-Assemblers kundig ist, macht sie auch am PC eine gute Figur, selbst ein Stand-alone-Betrieb etwa als Smart-Card-Terminal ist denkbar. Wird der Kartenleser verwendet, sind nur noch die Bits 5 bis 7 von P1 frei.

Das wichtigste Register ist bei fast allen ADB-Devices Register 0. Der ADB-Controller im Mac pollt ständig Register 0 des Device, das zuletzt Daten geliefert hat und erspart dem übergeordneten ADB-Manager somit zeitaufwendiges Nachfragen. Alle Datentransfers zum und vom MacInterface laufen daher in der Regel über Register 0. Zusätzlich können die Daten jedes Ports sowie der seriellen Schnittstelle über Register 1 und 2 gelesen werden. Alle MacInterface-Register sind 16 Bit (ein Wort) breit.

Möchte man mit einem 'Listen'-Befehl Daten an einen MacInterface-Port absetzen, stellt man im oberen Byte (Selector) des Register-Wortes die entsprechenden Port-Selector-Bits und im unteren Byte die Port-Daten ein. Diese Methode der Sekundäradressierung (ähnlich wie bei SCSI, IEEE 488 und I(2C) hat den Vorteil, daß man nicht auf die beschränkte Registerzahl angewiesen ist. Die nebenstehende Tabelle zeigt, welche Selector-Bits für welchen Port zuständig sind. Setzt man mehrere Bits gleichzeitig, verteilt der Controller die Daten auch an alle damit selektierten Ports. So ist es beispielsweise möglich, ein Zeichen gleichzeitig an das Display und an eine mit P0 aufgebaute Druckerschnittstelle zu senden.

Bei einem 'Talk' auf Register 0 bis 3 rückt MacInterface sofort mit den zugehörigen Daten heraus. Allerdings verlangt die ADB-Spezifikation, daß ein Device zur Vermeidung von Verstopfungen auf dem Bus nur dann Daten in Register 0 liefern sollte, wenn es tatsächlich etwas zu melden hat, sprich sich die Daten an einem der MacInterface-Ports geändert haben oder ein Zeichen über die 'Serielle' eingetroffen ist.

Da es unter Umständen passieren kann, daß eine Anwendung von einem solchen Event nichts mitbekommen hat, weil die Meldung von irgendeiner Warteschlange verschlungen wurde, der ADB-Controller aber ständig auf der letzten Lieferadresse herumpollt und von dort nichts mehr kommt, liegen die Port-Daten zusätzlich in Register 1 und 2 zur Abholung bereit. Im oberen Byte von Register 2 liegen Kopien der vom Device bei Änderungen gesetzen Selector-Bits, die im Gegensatz zu ihren Pendants in Register 0 erst dann gelöscht werden, wenn man die zugehörigen Daten höchstpersönlich aus Register 1 oder 2 abholt.

Die Schaltung verwendet größtenteils handelsübliche Bauteile. Bei eMedia bekommen Sie die Mac-Sammeldiskette 8 mit dem MacInterface-Demoprogramm samt sorgfältig erklärtem Pascal-Source sowie dem Controller-Code im Intel-HEX-Format, die Platine, das Display und auch fertig programmierte EPROMs und Mikrocontroller. Das EPROM benötigen Sie nur, wenn Sie die (preiswertere) ROM-lose Version 8031 statt eines programmierten 8751 einsetzen wollen; ein gebrauchter 8051, etwa aus einer Waschmaschinensteuerung, tut es übrigens auch, wenn man Jumper J1 steckt und so das ihm eigene 30ƒ-Wolle-Schonprogramm lahmlegt. Port P0 ist bei Einsatz des EPROMs allerdings nur noch sehr eingeschränkt zu verwenden, weil der Prozessor bei jedem Zugriff darauf kurz Daten und Adressen auf P0 legt.

Alle ADB-Transfers erledigt der Controller IC1; ohne besondere Ansprüche können Sie den Rest der Schaltung getrost vergessen. Neben dem schon erwähnten Display, dem Karteneinschub und der RS-232-Schnittstelle ist besonders die Steckleiste ST1 erwähnenswert, die alle 'brauchbaren' Signale des Prozessors führt und für allerlei Erweiterungen (Treiber, externer Speicher, mehr Ports) beispielsweise auf einer Huckepackplatine herhalten kann.

IC2 sorgt für einen sauberen Reset und die Dekodierung der Display-Adressen; letztere ist zwar eher rudimentär, läßt aber noch genügend Lücken für eigene Basteleien rund um ST1. Mit dem Trimmpoti P1 stellt man den Kontrast des Displays ein. IC3 bildet ein Adreß-Latch, das bei Betrieb mit externem EPROM IC4 die von IC1 gemultiplext gelieferten Adressen und Daten trennt und für das EPROM zwischenspeichert. IC5 ist der gemeinhin beliebte, aber ebenfalls optionale MAX232, der die seriellen Signale RxD und TxD auf RS-232-Niveau hievt. Zwei seiner Pegelwandler sind noch frei; ihre Anschlüsse führen auf Steckverbinder ST3. Ihre Versorgungsspannung von 5 V 'klaut' die Schaltung übrigens aus der ADB-Buchse, hier dürfen laut Spezifikation maximal 100 mA gezogen werden.

Selbstverständlich sind beim Aufbau der Platine die üblichen Regeln zu beachten: keinen Kolben aus der Klempnerzunft und kein Lötfett verwenden, liegende Bauteile zuerst bestücken, für die ICs Fassungen verwenden. Das Display wird - so benötigt - mit 10 mm langen Abstandsröllchen und M2,5-Schrauben befestigt.

Werden Display und Karteneinschub auf der MacInterface-Platine nicht benötigt, kann man den unbenutzten Teil in der Höhe des Displays abtrennen; eine Schnittkante ist im Bestückungsdruck angegeben. Bei Verwendung des EPROMs als Programmspeicher ist für IC3 ein 74HCT573 einzusetzen, ansonsten ist der Baustein obsolet. Das auf der Mac-Sammeldiskette verfügbare HEX-File ist sowohl für EPROM als auch 8751 geeignet. Für eigene Erweiterungen ist auf der Platine noch ein Lochrasterfeld vorhanden. *

Da selbst funktionslose Mac-Programme 'dank' ihrer aufwendigen Benutzerschnittstelle schon einige hundert Zeilen lang werden, habe ich auf einen Abdruck des Demoprogramms, das Sie auch auf unserem Server finden, verzichtet; zum Verständnis der ADB-Funktionen sollten Sie es aber unbedingt ansehen. Sämtliche Kommunikation mit MacInterface läuft wie erwartet über die ADB-Manager-Funktion ADBOp. Da diese Routine nicht den üblichen Pascal-Calling-Conventions entspricht, weil sie ihre Daten in Prozessorregistern und nicht auf dem Stack übergeben bekommt, ist ein kleiner Griff in die Assembler-Trickkiste nötig.

Die abgedruckte Maschinenroutine, die in einer eigenen Code-Ressource untergebracht und in den System-Heap geladen wird, übernimmt die vom ADB-Manager gelieferten 68K-Registerinhalte und postet einen App1-Event, der von der Main Event Loop der Demo-Applikation abgefangen und verarbeitet wird. Dieses Verfahren hat den Vorteil, daß es unabhängig von der verwendeten Sprache und Entwicklungsumgebung funktioniert - einfach die Code-Ressource 'ADBs' in die Anwendung kopieren, laden und ihre Adresse dem Pointer siADBServRtPtr beziehungsweise compRout übergeben. Think Pascal, mit dem das Demoprogramm entwickelt wurde, erlaubt nämlich keinen Inline-Code als Argument für Pointer-Variablen.

Die vier Bytes im Langwort theEvent.message im Event Record haben beim von ADBs ausgelösten App1Evt folgende Bedeutung:

 MSB LSB Cmd Cnt Sel Data 

Cmd enthält den ADB-Befehl, der diesen Event verursacht hat. In der Regel wird dies ein Autopolling-Talk auf Register 0 sein, beim MacInterface also $7C ($7 ist die Device-Adresse, $C der Talk-Befehl für Register 0). Cnt zeigt mit dem Wert 2 an, daß die Daten in Sel und Data gültig sind; steht eine 0 in Cnt, hat das Device keine neue Daten anzubieten und Sel und Data sind ohne Bedeutung. Selector und Data enthalten die oben beschriebenen Informationen aus dem Device-Register 0 und liefern somit sowohl Quelle des Events als auch die zugehörigen aktuellen Port-Daten. Das Demoprogramm listet die App1-Events und Antworten des ADB-Managers auf eigene Befehle in einem Fenster auf, so daß man die Aktivitäten an den MacInterface-Ports am Bildschirm verfolgen kann.

Damit der Rechner das MacInterface ordentlich in die De-vice Handler List einträgt und auf Requests reagieren kann, muß es beim Hochstarten bereits angeschlossen sein; alternativ können Sie im Demoprogramm den Menüpunkt 'ADBReInit' aufrufen.

Im Menü 'ADB' finden Sie noch einige Funktionen zum Messen des Bus-Timings, die Sie aber nur brauchen werden, wenn Sie selbst ADB-Devices entwickeln. (cm)

* Korrekturen und Ergänzungen zur Printversion in rot.

Weitere Hinweise und Berichtigungen unter: c't-Projekte

Programme und Firmware siehe Download-Seite.

[1] Carsten Meyer, Prozessor Flimmrich, Die c't-Videotext-Karte am Macintosh, c't 4/92, S. 210

[2] Carsten Meyer, Hör-Hilfe, Macintosh-Mikrofonadapter im Selbstbau, c't 5/92, S. 236

[3] Carsten Meyer, Cardware, Macintosh-HyperCard liest Telefonkarten, c't 10/93, S. 194

[4] Inside Macintosh Vol.V, The ADB Manager, Addison Wesley #17719

[5] Guide to the Macintosh Family Hardware, Addison Wesley #52405

[6] Carsten Meyer, Startautomatik, Macintosh automatisch ein- und ausschalten, c't 10/92, S. 196

[#anfang Seitenanfang]


Display-Codes
ASCII-Code Aktion
$02, STX CTRL-B Cursor ein, blinkt
$03, ETX CTRL-C Cursor aus
$08, BS CTRL-H Cursor ein Zeichen zurück
$09, HT CTRL-I Cursor ein Zeichen vor
$0C, FF CTRL-L Display löschen
$0A, LF CTRL-J Zeile wechseln
$0D, CR CTRL-M Cursor zum Anfang der Zeile
$20-$7F - ASCII-Zeichensatz

Funktionen des optionalen Displays. Backspace ($08, CTRL-H) löscht kein Zeichen, sondern bewegt nur den Cursor, und Linefeed ($0A, CTRL-J) gilt nur bei zweizeiligen Displays.

[#anfang Seitenanfang]


Debug-Display

P0 Port 0 Read-Daten (2 Stellen, hexadezimal)
P1 Port 1 Read-Daten (2 Stellen, hexadezimal)
SR Serial Input ASCII-Zeichen (2 Stellen)
REG3 Register 3 Read-Daten (4 Stellen, hexadezimal)
PSFL 4 Port Service Flags wie in Register 2 (4 Stellen, binär)

[#anfang Seitenanfang]


MacInterface Device-Register

DB Display Debug Mode ein (Data=1) bzw. aus (Data=0)
CD Card Read Trigger, Data ohne Bedeutung
DP Display, Data enthält ASCII-Zeichen
BD Baudrate, Data enthält Baudrate/100 (96=9600 Bit/s)
SR Serial, Data enthält ASCII-Zeichen
XX Reserviert
P1 Port 1 (statisch), Data enthält Port-Daten
P0 Port 0 (strobed), Data enthält Port-Daten

[#anfang Seitenanfang]


Steckerbelegungen
Port 0/ST1
1 Gnd
2 P2.7, A15
3 P2.6, A14
4 P2.5, A13
5 P2.4, A12
6 P3.6, /WR
7 P3.7, /RD
8 P3.5, T1
9 P3.3, /INT1
10 ALE
11 Gnd
12 P0.0, AD0
13 P0.1, AD1
14 P0.2, AD2
15 P0.3, AD3
16 P0.4, AD4
17 P0.5, AD5
18 P0.6, AD6
19 P0.7, AD7
20 +Ub
Port 1/ST2
1 Gnd
2 P1.0, C7 CdData
3 P1.1, C3 CdClk
4 P1.2, C2 CdReset
5 P1.3, C1 /CdUb
6 P1.4, Sk
7 P1.5
8 P1.6
9 P1.7
10 +Ub
Aux/ST3
1 Gnd
2 Reset
3 TxD RS-232
4 RxD RS-232
5 MAX7, RS-232 Out 2
6 MAX8, RS-232 In 1
7 MAX9, TTL Out 1
8 MAX10, TTL In 2
9 ADB 2
10 /P1.3, C1 CdUb

[#anfang Seitenanfang]


Stückliste
Grundbestückung
IC1 87(C)51, programmiert
IC2 74HC132
T2 BC547
D1 1N4148
R3, R5 10k
R4 47k
C1, C2 15p ker.
C10...C12 100n ker.
C8 4µ7 35V Ta.
C9 1µ 35V Ta.
X1 Quarz 11,059 MHz
1 Pfostenleiste zweireihig 20pol.,
2 Pfostenleisten zweireihig 10pol.,
2 Mini-DIN-Buchsen 4pol.,
Platine MacInterface
Option Display
IC6 LM16155 o.ä. LCD
R6 4k7
P1 4k7 Trimmpoti liegend, RM10
2 Abstandsröllchen 10 mm, 2 Schrauben M2,5 x 16 mit Muttern
Option serielle Schnittstelle
IC5 MAX232
C4...7 1µ 35V Ta.
Option Chipkartenleser
T1 BC557
R1 220R
R2 10k
ST6 Amphenol C702 10M008 121 4 oder
ST7 Amphenol C702 10M008 206 4

[#anfang Seitenanfang]


Optimal für Eingabegeräte wie Tastaturen und elektronische Kleinnager geeignet, zeichnet sich der ADB für die meisten Anwender einfach durch klaglose Funktion aus - so klaglos, daß die meisten sich nicht einmal fragen, wie der ADB eigentlich funktioniert. Wir erklären es hier trotzdem.

Der ADB ist ein serieller Bus: Alle Bits einer Übertragung müssen sich eine einzige Leitung teilen, und das sogar für den Hin- als auch für den Rückweg (der Nachrichtentechniker spricht hier von 'Halbduplex', wie früher beim Fernschreiber). Der ADB ist sauber spezifiziert und verkraftet theoretisch 16 parallel angeschlossene Geräte; deren Anschluß-Reihenfolge ist völlig egal.

Insgesamt kommt der Bus mit vier Leitungen aus: +5V und Masse als Versorgungsspannung, das eigentliche Datensignal und eine Leitung, über die die größeren Mac-Modelle per Tastatur eingeschaltet werden können (Kontakt nach Masse). Die Datenleitung wird im 'Open Drain'-Verfahren angesteuert (das CMOS-Äquivalent zu Open Collector), wird also über einen Widerstand im Mac auf high-Pegel gehalten. Die Geräte ziehen zum Signalisieren die Leitung entweder auf 0 Volt oder lassen sie 'driften'. An Power stehen auf dem ADB insgesamt 500 mA zur Verfügung, wobei ein einzelnes Gerät nicht mehr als 100 mA verbrauchen darf. Maximal sollen nach Spezifikation nicht mehr als drei Geräte an einer Buchse angeschlossen werden, in der Praxis ergeben sich aber auch bei sechs Geräten noch keine Probleme, solange die 500 mA nicht überschritten werden.

Das verwendete Protokoll ist speziell auf kleine Mikrocontroller zugeschnitten und damit relativ einfach. Jedes Gerät hat bis zu vier Register, die zwei bis acht Byte groß sein dürfen. Register 3 ist 2 Byte groß und Pflicht, da es vom System benötigt wird. Die Register müssen übrigens nicht physikalisch im Device vorhanden sein; es genügt, wenn der Controller die in ihnen gelieferten Daten interpretiert und auf Anfrage dem Register entsprechende Daten liefert.

Auf dem ADB herrscht eine strikte Rangordnung: Kein Gerät darf von sich aus Daten senden, sondern hat immer auf Anweisung vom Macintosh zu warten. Der beginnt seine Befehle gewissermaßen mit einem 'Räuspern', indem er die Bus-Leitung für rund 800 µs auf low zieht. Es folgt ein acht Bit langes Kommando, das mit einem Stoppbit endet. Das Kommando enthält in den oberen vier Bit die Adresse des angesprochenen Gerätes. Eine Ausnahme bildet der Reset-Befehl, der immer an alle Geräte gerichtet ist. Allerdings wird im Normalfall ein Reset auf dem ADB dadurch ausgelöst, daß die Datenleitung für mehr als 3 ms auf low geht, was ebenfalls nur der Mac selbst initiieren darf.

Bei den Kommandos 'Listen' und 'Talk' ist im Kommando-Byte auch die Registernummer kodiert. Ein 'Listen'-Befehl, der ein Gerät zum Annehmen von Daten überreden will, wird nach einer Pause von etwa 200 µs von einem Datenpaket mit zwei bis acht Byte Länge gefolgt, die in das jeweilige Register geschrieben werden sollen. Das 'Talk'-Kommando liest den Inhalt eines Registers aus oder macht zumindest den Versuch dazu. Ein damit angesprochenes Gerät hat 200 µs Zeit, mit der Übertragung seiner Daten zu beginnen, ansonsten wird der Zugriff als erfolglos abgebrochen. Wenn ein 'Talk' auf das Register 0 eines Gerätes geschickt wird, soll das Gerät nur antworten, wenn es neue Daten anzubieten hat. Register 0 wird standardmäßig verwendet, um Eingabedaten, zum Beispiel Scancodes von einer Tastatur, zu übergeben; deshalb wird es auch, wenn es einmal Daten anzubieten hatte, alle 11 ms vom ADB Manager automatisch abgefragt. So wird gewährleistet, daß das momentan vom Anwender benutzte Gerät auch die bestmögliche Datenrate zur Verfügung hat.

Hat ein Gerät Daten in Register 0 bereit, so darf es den Mac mit dem 'Service Request', einer Verlängerung der Low-Phase des Stoppbits am Ende eines Kommandos, darauf aufmerksam machen, daß es diese gerne loswerden möchte. Der ADB Manager beginnt dann, nacheinander alle Geräte mit einem 'Talk' auf Register 0 anzusprechen, bis der Service Request nicht mehr auftritt. Das letzte angesprochene Gerät wird schließlich wieder kontinuierlich abgefragt.

Nach dem Einschalten des Macintosh kann es auf dem ADB recht wüst aussehen. Verschiedene Geräteklassen haben Standardadressen, auf denen sie nach einem Reset liegen sollen. Das kann dann dazu führen, daß sich zum Beispiel auf der Adresse 3 (relative Eingabegeräte) eine Maus, ein Trackball und ein Joystick drängeln, oder auf Adresse 7 (diverses) könnten Barcodeleser und MacInterface übereinanderliegen.

Das Aufräumen geschieht über ein Kollisions-Erkennungsprotokoll, das sich des Registers 3 bedient. Dieses enthält verschiedene Datenfelder, deren Bedeutung sich ändern kann - je nachdem, ob mit 'Listen' oder 'Talk' darauf zugegriffen wird. Im 'Talk'-Zugriff ist die 'Handler-ID' eine Konstante, die dieses Gerät für den Treiber identifiziert (der Wert für Adresse und Handler-ID wird von Apple zugewiesen; zum Redaktionsschluß lief gerade unser Lizenzverfahren an, so daß wir hier noch keine Angaben über unsere ID machen können). Anstelle der Geräteadresse liefert Register 3 beim Auslesen eine Zufallszahl, um die Kollisionswahrscheinlichkeit zu erhöhen. Durch ein 'Listen' auf Register 3 kann dann im Handler-ID-Feld ein Kommando an das Gerät übergeben werden.

minpic07.gif

Beispiel für die Datenübertragung auf dem ADB. Die Bitzellen beginnen immer mit einem high- nach low-Übergang und sind 100 µs lang. Bei einer '0' bleibt das Signal für 65 µs auf low, bei einer '1' für 35 µs. Das Stoppbit am Ende des Kommandos ist normalerweise eine einfache '0'-Bitzelle. Wenn ein Gerät einen Service-Request erzeugt, wird die Länge der Low-Phase auf bis zu 300 µs gestreckt. Die Zeit zwischen dem Ende des Stoppbits des Kommandos und dem Beginn der Datenübertragung sollte in gewissen Grenzen variieren, um die Kollisionserkennung zu erleichtern.

Die Aufräumaktion des ADB Managers sieht so aus, daß auf jede ADB-Adresse ein 'Talk Register 3' geschickt wird. Antwortet ein Gerät, so sendet der ADB Manager an dieses den Befehl 'Adresse verschieben, wenn keine Kollision erkannt'. Haben mehrere Geräte dieselbe Adresse belegt, so werden alle bis auf eines eine Kollision bemerkt haben und sich nicht verschieben. Das Unwissende legt sich dann auf eine der reservierten freien Adressen (8-15). Diesen Vorgang wiederholt der ADB Manager so lange, bis kein Gerät mehr von dieser Adresse antwortet, danach verschiebt er eines der Geräte zurück an seine Originaladresse.

Nachdem der ADB Manager die Adressen auf dem Bus entwirrt hat, sucht er für die verschiedenen Geräte passende Treibersoftware im System. Nach der klassischen Methode sollen die Treiber in 'ADBS'-Ressourcen (nicht zu verwechseln mit der 'ADBs' des Demoprogramms) mit gleichen IDs wie die Adressen der Devices im System liegen. In der Praxis ist dies jedoch nicht mehr brauchbar, da häufig mehr als ein Gerät des gleichen Grundtyps angeschlossen ist und sich zwei verschiedene ADBS-Ressourcen um die gleiche Resource-ID prügeln müßten. Apple hat daher schon vor einiger Zeit in einer Technote empfohlen, die Installation des Treibers mittels eines INIT selbst in die Hand zu nehmen. Das INIT holt sich dann mittels ADBGetInfo die Originaladressen und die Handler-IDs aller Geräte, sucht sich seines heraus und installiert den Treiber mittels ADBSetInfo.

Der Treiber eines ADB-Device wird vom ADB Manager immer dann angesprungen, wenn es auf ein (auch automatisches) 'Talk Register 0' Daten geliefert hat. Allerdings kann der Treiber auch als 'Completion Routine' bei einem ADBOp angegeben werden.

Das Treiberinterface ist auf Assemblerprogrammierung abgestellt, die Parameter werden in Registern übergeben:

  • A0 zeigt auf die vom ADB empfangenen Daten, die als Pascal String abgelegt sind (beginnend mit einem Längenbyte, gefolgt von den eigentlichen Daten),
  • A1 ist ein Pointer auf den Treiber,
  • A2 ist ein Pointer auf die 'optional data area', die mit ADBSetInfo angelegt werden kann, und
  • D0 enthält das ADB-Kommando, das zum Aufrufen des Treibers geführt hat.

Der Treiber läuft im Interrupt und darf deshalb einen Teil der Toolbox-Funktionen nicht benutzen. Alle Register müssen vor der Benutzung gerettet werden.

Der ADB Manager stellt dem Programmierer sechs Funktionen zur Verfügung, um mit ADB-Geräten zu kommunizieren. ADBReInit sollte man gleich wieder vergessen, denn außer für ADB-Tools wie unserem Demoprogramm, Apples sehr brauchbarem 'ADB Parser' oder beim Systemstart gibt es keine Notwendigkeit, den Bus zu initialisieren.

Um ein ADB-Gerät anzusprechen, wird zunächst dessen ADB-Adresse, oft auch die Handler-ID benötigt. Dafür stehen zwei Funktionen zur Verfügung:

 iDev = CountADBs; 

liefert die Anzahl der vorhandenen ADB-Geräte,

 iAdress = GetIndADB(theADBDataBlock, devTableIndex); 

Informationen über jeweils eines davon. Der gelieferte ADBDataBlock enthält die Originaladresse des Gerätes, die aktuelle Adresse, die bedienende Service-Routine und deren Datenbereich. Die gleichen Informationen liefert auch

 iErr = GetADBInfo(theADBDataBlock, ADBAddr); 

allerdings verlangt diese Routine die ADB-Adresse als Eingangsparameter.

 iErr = SetADBInfo(&info,ADBAddr); 

ist das Gegenstück zu GetADBInfo und legt Service-Routine und Datenbereich für ein Gerät fest. Die Funktion

 iErr = ADBOp(data,compRout,buffer,commandNum); 

schließlich tauscht Daten mit dem Gerät aus. Es erwartet ein Kommando, das sich aus der ADB-Adresse, dem angesprochenen Register und der Operation zusammensetzt. Vier Operationen sind verfügbar:

  • SendReset führt eine Initialisierung des Bus durch,
  • Flush löscht alle Daten, die beim angesprochenen Gerät auf Abholung warten,
  • Listen sendet Daten vom Macintosh zum Gerät, und
  • Talk liest Daten aus dem Gerät aus.

'Listen' und 'Talk' benutzen den Daten-Pointer zum Lesen beziehungsweise Schreiben der Registerinhalte. Die Daten werden wie Pascal-Strings verwaltet, also Längenbyte gefolgt von den Daten. Weitere Parameter sind eine optionale Completion-Routine und dazugehöriger User-Daten-Pointer.

Um ein Überlaufen der ADBOp-Kommando-Queue zu verhindern, sollte man die Aufrufe mit Hilfe der Completion-Routine synchronisieren. Eine typische Vorgehensweise dabei ist, im User-Pointer die Adresse eines mit 0 initialisierten Bytes zu übergeben und als Completion-Routine eine Funktion, die dieses Byte auf einen Wert ungleich 0 setzt. Nach dem ADBOp-Aufruf verweilt das Programm in einer Schleife, bis sich der Wert des Bytes ändert. Aufgrund der weiter oben aufgeführten Registerverwendung muß die Completion-Routine in Assembler geschrieben sein. (cm)

[#anfang Seitenanfang]



 EE             Exeptional Event DS             Device Service Request Enable A0...A3        Device ID X0...X3        Zufallswert zur Kollisionsermittlung 

Der Aufbau von Register 3 ist im Gegensatz zu den anderen Registern von Apple spezifiziert. Das 'Service Request Enable'-Bit aktiviert ('1') oder desaktiviert ('0') den Service Request für dieses Gerät. Das Gerät ist im letzteren Fall auf ein 'Talk Register 0' zwar immer noch aktiv, erzeugt aber von sich aus keinen Service Request mehr. Das 'Exceptional Event'-Bit (bei MacInterface nicht verwendet) ist im Ruhezustand '1' und wird zum Beispiel bei der Apple-Tastatur auf '0' gesetzt, wenn die Einschalttaste gedrückt ist. Die vordefinierten Special-Hander-IDs für Listen-Kommandos sind:

 $FF            Selbsttest starten, bei Fehler wird HandlerID $00 bei 'Talk'                 zurückgeliefert $00            Adresse und Enable-Bits übernehmen $FE            Adresse übernehmen, wenn keine Kollision bei letztem 'Talk' $FD            Adresse übernehmen, wenn 'Activator' gedrückt ist 

Was ein 'Activator' ist, hängt von dem jeweiligen Gerät ab. Bei der Maus ist damit die Maustaste gemeint, bei einer Tastatur die Power-Taste.

[#anfang Seitenanfang]


ADB-Standardadressen
Adresse Gerätetyp
0 System (reserviert)
1 Dongles
2 Tastaturen
3 Relative Devices (Mäuse, Trackballs, Joysticks)
4 Absolute Devices (Grafiktabletts)
5 Low Speed Data Transfer Devices
6 Reserviert (PowerBook Duo Ladegerät)
7 Diverses
8...15 Reserviert für Readressierung

Die Geräteadresse 1 für Sicherheitssysteme verfügt über eine zusätzlich erweiterte Adressierung, bei der das Gerät erst freigegeben wird, wenn in Register 2 eine zwei bis acht Byte lange Schlüsselzahl geschrieben wurde. Allerdings wird diese Methode kaum benutzt.

[#anfang Seitenanfang]


ADB-Kommandos
Kommando C7 C6 C5 C4 C3 C2 C1 C0
Talk A3 A2 A1 A0 1 1 R1 R0
Listen A3 A2 A1 A0 1 0 R1 R0
Flush A3 A2 A1 A0 0 0 0 1
Reset x x x x 0 0 0 0

x = ohne Bedeutung
An = ADB-Adresse
Rn = Registernummer
Talk = Daten von Gerät senden
Listen= Daten an Gerät senden
Flush = Alle vorhandenen Eingabedaten löschen
Reset = Einschaltzustand herstellen

[#anfang Seitenanfang]


   1 **********************************************   2 * Service/Completion-Routine ADBServPostEvent.a         *   3 * für ADBOp (compROut) und                              *   4 * SetADBInfo (ADBSetInfoBlock.siServiceRtPtr)           *   5 * © C.Meyer 9/1994                                      *   6 *                                                       *   7 * MPW Build Commands:                                   *   8 * asm ADBServPostEvent.a -lo ADBServPostEvent.a.lst -l  *   9 * link ADBServPostEvent.a.o -o 'ADBs.rsrc'              *  10 *      -rt ADBs=128 -ra =resSysHeap                     *  11 **********************************************  12   13     STRING  ASIS  14     INCLUDE 'Traps.a'  15     INCLUDE 'SysEqu.a'  16   17 Entry MAIN  18     ALIGN 2     19   20 ; D0 enthält den verursachenden ADB-Befehl und A0    21 ; die Adresse des Pascal-'String'-Datenblocks  22     LSL.L   #8,D0         ; nach links einschieben  23     OR.B    (A0),D0       ; Länge  24     LSL.L   #8,D0  25     OR.B    1(A0),D0      ; 2 Datenbytes  26     LSL.L   #8,D0  27     OR.B    2(A0),D0  28     MOVE.L  #app1Evt,A0   ; ein Applikations-Event      29     _PostEvent            ; Event posten  30     RTS  31       32     END 

Dieses 'Routinchen' macht aus den von ADBOp und SetADBInfoBlock gelieferten Registerwerten einen anständigen Event, den das Demo-Programm auswertet.

Anzeige
Anzeige