Anhang B: Implementierung des ISDNLAP

Dieser Anhang beschreibt im Detail die Implementierung des ISDNLAP und seiner Funktionen. Besonderer Wert wird auf die Beschreibung der Installation des Link Access Protocols gelegt, da die Schnittstelle zum AppleTalk Protocol Stack in der vorhandenen Dokumentation zum LAP Manager nicht ausreichend behandelt wird.

Eine Implementierung eines ISDNLAP muß folgende Anforderungen erfüllen:

v   Prozeduren zum Auf- und Abbau von Wählverbindungen nach dem X.21-Protokoll und zur automatischen Verbindungssteuerung müssen bereitgestellt werden.

v   Eine dynamische Node-Adressierung und eine Funktion zur Ermittlung der Ruf­num­mer zu einer Node ID sind zu integrieren.

v   Ankommende Pakete müssen von einem Interrupt Handler entgegengenommen und anhand des Paketkopfes an den LAP Manager weitergereicht werden. Für das weitere Einlesen muß das ISDNLAP die Prozeduren ReadPacket, ReadRest des ALAP durch eigene Prozeduren ersetzen.

v   Der LAPWrite-Code im ATalkHk2 muß durch eine neue Sendeprozedur substituiert werden, die sich nahtlos in den AppleTalk Protocol Stack einfügt.

Das ISDNLAP kennt grundsätzlich zwei Zustände: Den Ruhezustand, der auf die Übertragung eines Paketes oder einen ankommenden Anruf wartet, und die Paketüber­tragungsphase, in der Datenpakete ausgetauscht werden. Der Übergang vom einen in den anderen Zustand geschieht über die Initialisierungsprozeduren InitSCCStuff und InitHDLCStuff.

B.1 Allgemeines

Bevor mit der Implementierung des ISDNLAP begonnen werden kann, ist ein geeignetes Entwicklungswerkzeug zu wählen. Auch über die Speicher­situation bei der Implemen­tie­rung muß man sich klar werden[1]. Wichtig für eine erfolgreiche Implemen­tierung ist zudem die Wahl von geeigneten Testhilfen, wie Debugger und History.

B.1.1 Programmierumgebung und Programmiersprache

B.1.1.1 Programmierumgebung

Für den Macintosh gibt es eine Reihe guter Entwicklungsumgebungen. Favorisiert wurde schließlich der Macintosh Programmers Workshop (MPW) in der Version 2.02, das offizielle Entwick­­lungs­werkzeug der Firma Apple. Seine Vorteile liegen vor allem in der Vollständigkeit und der Aktualität der Interface­files. MPW unterstützt verschie­dene Program­mier­sprachen[2] und erlaubt ein einfaches Zusam­men­binden von Moduln in unter­schiedlichen Sprachen. Pascal- und Assembler-Prozeduren können damit auf einfache Weise mit dem MPW kombiniert werden.

Abb. B.1.1: Die MPW-Shell

Die Entwick­­lungs­umgebung enthält alle Hilfsmittel die zur Erstellung umfang­reicher Programme notwendig sind. Anderen Entwicklungspaketen voraus hat der MPW die hervorragende Unter­stützung der Imple­men­tierung von „stand alone-Code[3], der nicht zu einer Applikation gehört, sondern in das System eingebunden wird. Als größter Nachteil muß die Benutzeroberfläche (MPW-Shell) betrachtet werden (Abb. B.1.1). Der MPW ist in der Bedienung so kompliziert und in seinen Möglichkeiten so umfangreich, daß es einiger Übung bedarf, ein Programm zu übersetzen. Durch die Abarbeitung der Befehle in einer Makrosprache wird zudem die Verarbeitungs­geschwin­dig­keit sehr herabgesetzt. Bedauer­licherweise hat der Pascal-Compiler der vorliegenden Version einige latente Fehler. Beispiels­weise versagt der Compiler beim Ermitteln von Feldadres­sen in gepackten Records, oder wenn gepackte Records über Handles angesprochen werden. Dennoch gibt es keine echte Alternative zum MPW.

B.1.1.1 Programmiersprache

Aus Gründen der besseren Wartbarkeit und Übersichtlichkeit wurde versucht, den Großteil des ISDNLAP in Pascal zu implementieren. Da die Schnittstelle zum LAP Manager „register based“ ist, werden Glueroutinen, die in Pascal entweder nicht imple­mentiert werden können oder in Assembler einfacher zu realisieren sind, in M68000-Assembler codiert. Erhebliche Schwierigkeiten entstehen auch, wenn sich am Anfang eines Codes eine Sprungtabelle befindet. Die Erfahrung hat gezeigt, daß es oft mühsamer ist, Glueroutinen als Inline-Statements oder als „tricky Pascal code“ zu imple­men­­tieren als direkt in Assembler. Zudem erzeugt jeder Compiler anderen Objektcode und belegt andere Register.

Das systemnahe Programmieren mit Pascal birgt einige Gefahren in sich. So sollte man vorsichtig mit der Arithmetik von Adressen sein, besonders dann, wenn innerhalb eines WITH-Statements ein Pointer oder Handle manipuliert wird. Durch das Dereferenzieren des WITH-Statements wird die Basisadresse eines Feldes in einem Register gehalten. Es nützt daher nichts, wenn man die Variable mit der Basisadresse direkt manipuliert. Eine weitere Gefahr, die durch die Optimierung des Compilers entsteht, ist das Pollen von globalen Variablen, die über Interrupts gesetzt werden. Dadurch, daß in einer Schleife nur eine Variable abgefragt wird, kann der Compiler die Variable in einem Register halten. Da eine Interruptroutine nur die globale Variable verändert, nicht aber das Register, in dem die Variable gehalten wird, terminiert die Schleife nicht.

In solchen Fällen hilft es, den vom Pascal-Compiler erzeugten Objektcode zu analysieren. Das ist nicht die Art, in der man Pascal programmieren sollte. In vielen Zweifelsfällen jedoch die einzige Möglichkeit zu verstehen, wie ein Compiler Code erzeugt. Oft genügt es mit {$W-} den Optimizer des Compilers abzuschalten.

B.1.2 Debugging und Tracing

Zu den notwendigen Programmierwerkzeugen gehören auch Test- und Debughilfen. In diesem Abschnitt wird daher der Einsatz von derartigen Hilfsmitteln bei der Implemen­tie­rung von ISDNLAP beschrieben.

B.1.2.1 Einsatz von Debuggern

Trotz vorsichtiger Programmierung und sorgfältigem Durchdenken von Algorithmen kommt es gelegentlich zu Programmabstürzen oder zu Fehlverhalten von Programmen, wobei die Ursachen nicht offensichtlich sind. Um Fehler, wie Adreßfehler[4], „Illegal Instructions“[5] und Busfehler[6], zu lokalisieren und zu beseitigen, wird ein Debugger verwendet.

Abb. B.1.2: Die gebräuchlichsten Debugger

Bevorzugt wird dabei der TMON- oder MacsBug-Debugger eingesetzt (Abb. B.1.2). Beide sind keine Sourcecode-Debugger, sondern arbeiten auf Assembler-Ebene. Sie werden automatisch installiert, wenn sie sich beim Systemstart im System Folder befin­den. Gerufen werden die Debugger in folgenden drei Fällen:

v   Durch einen Debugger-Interrupt (Interruptpriorität 4–7), der beim Drücken des Debugger-Knopfes an der Seite des Macintosh ausgelöst wird.

v   Von einem Programm aus über einen speziellen Debugger-Trap ($A9FF). In Pascal verwendet man entweder eine Inline-Prozedur oder eine Prozedur Debugger.

v   Durch das Auftreten eines System-Fehlers. Am häufigsten handelt es sich dabei um Adreßfehler oder „Illegal Instructions“.

Beide Debugger bieten die Möglichkeit, den Speicher sowohl als „Hexdump“ als auch disassembliert zu betrachten. Während TMON durch veränderbare Fenster komfortabler in der Handhabung ist, bietet MacsBug wesentlich mehr Möglich­keiten. Für welchen Debugger man sich entscheidet hängt von der Situation ab.

B.1.2.2 Tracing mit EavesHist

Um den Programmfluß zu verfolgen, kann in einer normalen Applikation eine Ausgabe auf den Bildschirm erfolgen[7]. Bei der Implementierung des ISDNLAP ist das nicht so ohne weiteres möglich, da der Bildschirm meist von einer anderen Applikation beschrie­ben wird. Zudem benutzen die Ausgaberoutinen der Toolbox den Memory Manager und können daher nicht in Interrupts verwendet werden. In vielen Fällen ist eine Ausgabe auf den Bildschirm ohne Pufferung auch zu langsam, um bei kurz hintereinander ankom­menden Paketen eine Ausgabe auf den Bildschirm zu ermöglichen.

Für die Implementierung des ISDNLAP wurde daher ein History verwendet, das von P. Schulthess zum Monitoren des PDEF10 entwickelt wurde[8]. Bei ISDNLAP kann das History jedoch nicht auf dem Application Heap des Macintosh angelegt werden, da dieser beim Beenden eines Applikationsprogrammes „gespült“ wird. Damit die Aufzeich­nungen im History auch nach dem Beenden eines Programms erhalten bleiben, wird das History auf dem System Heap angelegt. Installiert wird das History bei der Initiali­sie­rung des ISDNLAP durch die Prozedur EavesInit. Da es unter den Pascal-Interfaces des OS keine ‘NewHandleClrSys’-Prozedur gibt, ist für das Anlegen des Histories ein kurzes Stück Assembler als Glueroutine notwendig (Abb. B.1.3).

NewHandleClrSys      PROC  EXPORT      ; create handle with zeros

         Move.L      4(A7),D0          ; get size

         _NewHandle  SYS,CLEAR         ; create handle on sysheap

         Move.L      A0,8(A7)          ; return handle

         Move        D0,MemErr         ; result of trap

         Move.L      (A7)+,(A7)        ; clear stack pointer

         Rts                          ; go home

         DC.B        'NEWHAND '

         ENDP

Abb. B.1.3: Der Assembler-Glue zum Anlegen eines mit Nullen gefüllten Handles.

Beschrieben wird das History durch Aufruf der Prozeduren EavesHist und PtrEavesHist. Während EavesHist eine Marke und ein String als Meldung über­geben wird, erwartet PtrEavesHist die Länge einer Meldung und einen Zeiger darauf.

PROCEDURE EavesHist(mark: Str8; VAR str: Str255);

PROCEDURE PtrEavesHist(marke: Str8; Size: INTEGER; Ptr: charPtr);

Durch bedingte Compilierung kann bestimmt werden, welche Aktionen des ISDNLAP aufgezeichnet werden. Ist das Programm ausgetestet, so läßt sich das Tracen abschalten, wodurch der Code schneller und kleiner wird. Gesetzt werden die Trace-Optionen in der „Make-Datei“ (ISDNLAP.make) des ISDNLAP:

POptions = -d TDEBUG=TRUE -d RDEBUG=FALSE -d DDEBUG=TRUE

 

Mit der Dump-Funktion eines Debuggers kann das History betrachtet werden. Hierzu wird mit der Suchfunktion der Anfang des Histories, der durch die Marke „[ISDNInit]“ gekennzeichnet ist, lokalisiert und das Dumpfenster des TMON mit „(v)“ auf die gefun­dene Speicherposition gesetzt[9]. Einen Auszug aus dem History zeigt Abbildung B.1.4.

  0002E4A6  5B49 5344 4E49 6E69  745D 3534 3100 0000  [ISDNInit]541•••

  0002E4B6  5B49 6E69 7453 4343  5D00 0000 0000 0000  [InitSCC]•••••••

  0002E4C6  5B54 5061 636B 6574  5D32 3535 0000 0000  [TPacket]255••••

  0002E4D6  5B44 6F44 6961 6C5D  0000 0000 0000 0000  [DoDial]••••••••

  0002E4E6  5B46 696E 645D 0000  0000 0000 0000 0000  [Find]••••••••••

  0002E4F6  5B44 6961 6C55 705D  3534 3100 0000 0000  [DialUp]541•••••

  0002E506  5B49 6E69 7453 4343  5D00 0000 0000 0000  [InitSCC]•••••••

  0002E516  5B44 6961 6C54 4F31  5D16 1635 3431 2B00  [DialTO1]••541+•

  0002E526  5B48 616E 6755 705D  0000 0000 0000 0000  [HangUp]••••••••

  0002E536  5B49 6E69 7453 4343  5D00 0000 0000 0000  [InitSCC]•••••••

  0002E546  5B54 5061 636B 6574  5D32 3535 0000 0000  [TPacket]255••••

  0002E556  5B44 6F44 6961 6C5D  0000 0000 0000 0000  [DoDial]••••••••

  0002E566  5B46 696E 645D 0000  0000 0000 0000 0000  [Find]••••••••••

usw.

Abb. B.1.4: Auszug aus dem History im Speicher

Bei der Deinstallation des ISDNLAP muß der Speicher, der durch das History belegt wird, wieder freigegeben werden. Das geschieht in der Prozedur RestoreOldLAP durch den Aufruf der Toolbox-Prozedur DisposHandle.

B.1.2.3 Weitere Werkzeuge zur Entwicklung

Für die Entwicklung von AppleTalk-Applikationen steht eine Vielzahl von Tools zur Verfügung. Hier sollen diejenigen kurz beschreiben werden, die bei der Entwicklung des ISDNLAP von Nutzen waren und frei erhältlich sind. Es gibt viele neuere Hilfsmittel, die aber meist sehr teuer sind und deren Nutzen nicht bewiesen ist. Wo diese Tools nicht ausreichten, wurden eigene kleine Hilfs­programme entwickelt.

Abb. B.1.5: Hilfsmittel zur Entwicklung von Netzwerkapplikationen

v   Peek: Hier handelt es sich um ein Monitor-Programm, das alle Pakete, die über eine AppleTalk-Leitung wandern, anzeigt und aufzeichnet. Wahlweise können auch Kontroll­­pakete des LAP betrachtet werden. Ein Abspeichern von Aufzeichnungen ist ebenfalls möglich.

v   Poke: Das AppleTalk-Poke-Programm erlaubt es, bis zu zehn LAP-, DDP- und ATP-Pakete zu definieren und wiederholt auf das Netzwerk zu senden. In einer Dialogbox werden die Parameter für jedes Paket definiert.

v   NetCheck: Dieses Programm sucht nach Einträgen auf dem Netzwerk. Dabei ist es möglich, zwischen benannten und unbenannten Einträgen zu unterscheiden. Leider zeigt es keine Zonennamen an.

v   ATITools: Das ist eine Eigenentwicklung, welche benannte Einträge auf dem Netzwerk registrieren, suchen und wieder löschen kann. Zusätzlich besteht mit diesem Programm die Möglichkeit zu Testzwecken die Node ID eines Knotens zu verändern.

B.1.3 Speichersituation

ISDNLAP ist ein ADEV-File und als solches keine Applikation, sondern „stand alone“-Code. Somit sind bei der Belegung des Speichers einige Besonderheiten zu beachten.

B.1.3.1 Die Datenstrukturen des AppleTalk im Speicher

Die Datenstrukturen des AppleTalk im Speicher sind stark zerklüftet (vgl. Abb. B.1.6). Manche Datenblöcke sind über globale Variablen erreichbar[10]. Ein großer Teil ist jedoch undoku­mentiert und sollte daher auch nicht verwendet werden. Jeder Treiber besitzt lokale Variablen und Datenblöcke. Für die Implementierung eines alternativen Link Access Protocols sind vor allem die lokalen Variablen des .MPP von Bedeutung.

Abb. B.1.6: Die Verkettung der Datenstrukturen des AppleTalk

Diese lokalen Variablen des .MPP-Treibers sind in der Struktur der .MPP Local Variables abgelegt. Ihre Position wird in der globalen Systemvariablen ABusVars mit der Adresse $2D8 festgehalten. Der Speicher für einen Names Table-Eintrag muß von der jeweiligen Applikation, die den Namen hinzufügt, gestellt werden. Die Applikation ist auch für das Aufräumen nicht mehr benötigter Einträge verant­wortlich.

sysLAPAddr     EQU $0      ; This node's LAP address

toRHA          EQU $1      ; Top of RHA ($18 Bytes )

sysABridge     EQU $19     ; Node address of a bridge [byte]

sysNetNum      EQU $1A     ; This node's network number [word]

vSCCEnable     EQU $1C     ; SR to reenable SCC interrupts [word]

atpVars        EQU $1E     ; ATP variable ptr (high byte is flag)

afterGlobals   EQU $22     ; After the globals

NBPC1          EQU $8A     ; Pointer to NBP Code 1 ( undocumented )

NBPC2          EQU $8E     ; Pointer to NBP Code 2 ( undocumented )

Names Table    EQU $90     ; Pointer to first Names Table Entry

Abb. B.1.7: Bekannte Offsets innerhalb der lokalen .MPP-Variablen

Leider ist über die Struktur der lokalen .MPP-Variablen sehr wenig bekannt. In den Assembler-Equates zum MPW[11] befinden sich einige wichtige Offsets (siehe Abb. B.1.7). Ferner ist mir durch Debugging die Position des Zeigers auf die Names Tables bekannt ($90). Bei Macintosh II hat sich diese jedoch bereits geändert.

B.1.3.2 Globale Variablen für ISDNLAP

Temporärer Speicher wird in Pascal-Programmen auf dem Stack angelegt und nach der Beendigung der Prozedur wieder freigegeben. Globale Variablen, die für die Dauer der Aktivierung des ISDNLAP benötigt werden, bereiten jedoch Schwierigkeiten. Da es sich bei ISDNLAP um „stand alone“-Code handelt, können globale Variablen vom Pascal-Compiler nicht relativ zum A5-Register adressiert werden (vgl. Abb. B.1.8).

Abb. B.1.8: Das Memory Mapping im Macintosh

Für ISDNLAP gibt es zwei Möglichkeiten globale Variablen anzulegen:

v   Zuerst kann beim Öffnen des Treibers ein Handle von der Größe der GlobalVars auf dem Heap reserviert werden. Da das ISDNLAP über mehrere Applikationen hinweg aktiv ist, muß dies auf dem System Heap geschehen. Die übliche Pascal-Prozedur NewHandle kann hierzu nicht verwendet werden, da diese ein Handle nur auf dem Application Heap anlegt[12].

v   Schließlich besteht die Möglichkeit zum ‘atlk’-Code des ISDNLAP einen ausreichend großen Datenblock hinzu­zulinken und die globalen Variablen dort abzulegen.

GLOBALS        PROC

   DC.B '**************************************************'

                           ◊Ç◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊◊

   DC.B '**************************************************'

 

     Vom Assembler-Modul wird der Bezeichner als Prozedur exportiert. Das Pascal-Modul importiert diese Prozedur und greift über die Adresse auf die globalen Variablen zu:

PROCEDURE Globals; EXTERNAL;

 

MyGlobals:=GlobalPtr(@Globals);

WITH MyGlobals^ DO …

 

Ein fester Block von globalen Variablen hat zwei Vorteile: Zum einen erübrigt sich beim Debuggen die Suche nach den Variablen im Heap. Zum anderen ist das Anlegen eines Handles im System Heap von Pascal aus nicht möglich; es muß eine Hilfsprozedur in Assembler implementiert werden.

Da sämtliche Prozeduren des ALAP machen nach der Initialisierung keinen Gebrauch mehr vom Memory Manager, da das Reservieren von Speicher durch das LAP mit Hilfe des Memory Managers Probleme bereitet. Diese treten dann auf, wenn ein Klient des LAP ein Handle dereferenziert und mit dem Pointer arbeitet und dabei das Handle nicht „gelockt“ hat. Durch einen Interrupt, der eine Prozedur des LAP ruft, die den Memory Manager verwendet, könnten so Speicher­blöcke verschoben und die Pointer des unter­brochenen Programmes korrumpiert werden. Daher muß in den Interruptroutinen des ISDNLAP gänzlich auf die Verwendung von Toolbox-Routinen, die Speicher verschieben oder löschen, verzichtet werden[13].

B.1.3.3 Die ‘atlk’-Resource im System Heap

Die ‘atlk’-Resource des ISDNLAP wird, wie alle AppleTalk-Treiber, in den System Heap geladen. Dort herrscht leider bei älteren System-Versionen[14] (< 6.0) akuter Platzmangel. So kann beim Starten des Systems das ISDNLAP nicht geladen werden, da zu diesem Zeitpunkt der Speicher besonders knapp ist. Der einzig mögliche Ausweg ist die Vergrößerung des System Heaps. Eine Aufteilung des Codes ist ebenso nicht sinn­voll, da auch ein eventuell gesonderter Treiber im System Heap Platz finden muß. Außer beim Macintosh 128 wird auch der NBP- und ATP-Code in den System Heap geladen.

Um das ISDNLAP in den System Heap zu laden, muß dieser vergrößert werden. Die Größe des System Heaps wird durch einen Eintrag in den Bootblocks des Macintosh bestimmt. Falls der Eintrag in den Bootblocks nicht ausreicht, setzt zusätzlich ein INIT die Größe auf einen Minimalwert, um die aktuelle System-Version zu unterstützen. Die genaue Position der System Heap-Größe in den Bootblocks ist $86–$89. In jeder System Resource befindet sich eine Kopie der Bootblocks. Diese kann mit dem ResEdit in die Resource Fork des FEdit kopiert und mit der Update-Funktion des FEdit auf die Boot­blocks der Diskette geschrieben werden. Somit lassen sich Modifika­tionen leicht rück­gängig machen.

Globale Variablen werden zusammen mit dem History und dem LAP-Code im System Heap untergebracht. Auch wenn bei neueren System-Versionen dort kein Platzmangel herrscht, muß dennoch darauf geachtet werden, daß der Code des ISDNLAP nicht zu umfangreich wird.

B.1.4 Timeouts und Interrupts

Kommunikationsprotokolle arbeiten mit Zeitlimits um Verklemmungssituationen zu vermeiden. Das Betriebssystem des Macintosh offeriert eine sehr elegante Methode, um Timeouts zu realisieren. Über den Time Manager kann eine Task in die Time Manager Queue eingehängt werden, die nach einer vorgegebenen Anzahl von Millisekunden genau einmal gerufen wird und den Timeout signalisiert. Diese Methode wurde in DialUp verwendet (TimeoutTask). Leider kann eine derartige Task in einer Interrupt­routine nicht als Zeitbegrenzung fungieren, da während eines Interrupts Tasks ausgeschlossen sind. Auch Ticks können nicht verwendet werden, da der Zähler in der globalen Variable Ticks in Interrupts nicht erhöht wird. Es bleibt nur die Möglichkeit, Zählschleifen zu verwenden. Aber auch das ist nicht problemlos, da das LAP unabhängig vom Rechnertyp und dessen Verarbeitungsgeschwindigkeit sein soll. Eine globale Variable TimeDBRA, die angibt wieviele Decrement-and-Branch-Schleifen in einer Millisekunde ausgeführt werden können, ist am Macintosh Plus nicht verfügbar und für Pascal­-Schleifen zu ungenau.


B.2 Die Makrostruktur des ISDNLAP

B.2.1 Struktur des ADEV ISDNLAP

Das ISDNLAP erscheint auf dem Desktop als System-Datei vom Typ ADEV. Dieses ADEV enthält in seiner Resource Fork die Teile des ISDNLAP als einzelne Resourcen (Abb. B.2.1).

Abb. B.2.1: Die Struktur des ADEV ISDNLAP

Im wesentlichen lassen sich dabei die Resourcen in drei Gruppen unterteilen:

v   Die ‘adev’-Resource, die die Wechselwirkungen mit dem CDEV Netzwerk bei der Auswahl übernimmt.

v   Die ‘atlk’-Resource, die den eigentlichen Code des ISDNLAP enthält.

v   Einige weitere Resourcen, die für den Dialog mit dem Benutzer und dem Betriebs­system zuständig sind.

B.2.1.1 Die ‘adev’-Resource im ADEV ISDNLAP

Die ‘adev’-Resource enthält den Code der Prozeduren, die bei der Auswahl des LAP im Kontrollfeld gerufen werden[15]. Der Code der ‘adev’-Resource wurde aufgrund seiner Systemnähe vollständig in Assembler implementiert und besteht im wesentlichen aus einer Sprungtabelle, den Prozeduren GetADEV und SelectADEV sowie der Zeichenkette ‘ISDNLAP’, die unter dem ISDNLAP-Icon im Kontrollfeld erscheint.

Da der Code der ‘adev’-Resource nur zur Auswahl benötigt wird und danach nicht mehr, kann er anschließend wieder aus dem Speicher entfernt werden. Da es sich um Code handelt, darf die Resource allerdings nicht verschoben werden und muß „locked“ sein. Folgende Anweisung veranlaßt den Resource-Compiler den ‘adev’-Code in die Resource des ADEV ISDNLAP einzubinden :

include "ISDNadev"  'adev' (126) AS 'adev' (126, locked);

 

B.2.1.2 Die ‘atlk’-Resource im ADEV ISDNLAP

Der eigentliche Code des ISDNLAP ist in der ‘atlk’-Resource des ADEV enthalten. Er besteht aus einer Sprungtabelle, den Installationsprozeduren für den LAPWrite-Code, dem Assembler-Glue zum Aufruf der Sende- und Empfangsprozeduren in Pascal und dem eigentlichen ISDNLAP-Code in Pascal.

Die Assembler-Prozeduren doAInstall und doAShutDown dienen dabei der Installation. Der Glue für die Pascal-Routinen besteht aus den Prozeduren LAPWrite, ReadDispatch, ReadPacket und ReadRest. Schließlich enthält die ‘atlk’-Resource noch eine Glueroutine NewHandleClrSys, die ein Handle auf dem System Heap anlegt, was in Pascal nicht möglich ist. Am Ende des Assembler-Codes der ‘atlk’-Resource wird ein Datenblock angelegt, der die globalen Variablen des Pascal-Moduls aufnimmt. Der Pascal-Code des ISDNLAP, der eine Vielzahl von Prozeduren enthält, wird zu diesen Assembler-Routinen hinzugebunden (vgl. Abb. B.2.1).

Da der Code der ‘atlk’-Resource über mehrere Applikationen hinweg aktiv ist, muß er im System Heap untergebracht und „festgeschraubt“ werden[16]. Dazu ist jeweils ein Bit in der Resource zu setzen:

include "ISDNatlk"  'atlk' (126) AS 'atlk' (126, sysheap,locked);

 

B.2.1.3 Weitere Resourcen des ADEV ISDNLAP

Neben den Code-Resourcen befinden sich noch weitere Resourcen in der Resource Fork des ADEV (vgl. Abb. B.2.2). Die BNDL-, FREF- und ICN#-Resourcen dienen dem Anzeigen des ISDNLAP-Icons auf dem Desktop. Dieses ist identisch mit dem Icon im Kontrollfeld.

Resource-Typ

Resource ID

    ‘BNDL’

            -4032

    ‘FREF’

            -4032

    ‘ICN#’

            -4032

    ‘STR ’

            -4032

    ‘DLOG’

                 128

    ‘DITL’

                 128

    ‘adev’

                 126

    ‘atlk’

                 126

Abb. B.2.2: Die Resourcen des ADEV ISDNLAP

In einer ‘STR ’-Resource ist die Rufnummer des NameServers abgelegt. Sie wird bei der Auswahl des ISDNLAP vom Benutzer über eine Dialogbox, die sich in der ‘DLOG’- und ‘DITL’-Resource befindet, gesetzt. Ferner ist zu beachten, daß die Resource ID -4032 für die Typen ‘BNDL’, ‘FREF’, ‘ICN#’ und ‘STR ’ für alle ADEVs fest ist und nicht verändert werden darf. Die ID der ‘atlk’- und ‘adev’-Resourcen muß im Bereich von 1–254 liegen, die Nummern der zusätzlichen Resourcen sind dagegen frei wählbar.

B.2.2 Funktionale Aufteilung des ISDNLAP in Prozeduren

Die Prozeduren des ISDNLAP lassen sich nach ihren Aufgaben grob in fünf Bereiche gliedern (Abb. B.2.3): Die Auswahlprozeduren für das Kontrollfeld, die Glue­routinen zu AppleTalk, die Initialisierungs- und Installationsprozeduren des ISDNLAP, die Verbin­dungs­steuerung und die Paketübertragung.

Abb. B.2.3: Das Zusammenwirken der Prozeduren des ISDNLAP

v   Die Auswahlprozeduren im ‘adev’-Code sind vollständig in Assembler geschrieben und übernehmen beim Anzeigen und bei der Auswahl des LAP im Kontrollfeld den Dialog mit dem CDEV Netzwerk und dem Benutzer.

v   Die Installations- und Initialisierungsprozeduren in ‘atlk’-Code übergeben bei der Auswahl die Adresse des LAPWrite-Codes an den LAP Manager und Initialisieren das ISDNLAP.

v   Die Assembler-Glueroutinen bilden das Interface zwischen den Pascal-Routinen des ISDNLAP und den Routinen des LAP Managers und des Betriebssystems, die ihre Parameter in Registern übergeben.

v   Die Pascal-Prozeduren der Verbindungssteuerung übernehmen den Verbindungs­auf- und -abbau, die dynamische Node-Adressierung sowie die Suche nach einer Rufnummer am NameServer.

v   Die Pascal-Prozeduren zur Paketübertragung sind für das Senden und Empfangen von Paketen verantwortlich.

v   Verschiedene kleinere Hilfsprozeduren in Pascal unterstützen die Übertragungs­- und Verbindungssteuerungsprozeduren.

B.2.3 Die Aufteilung in Units und das Linken der Moduln

Die Prozeduren des ISDNLAP sind in drei Units zusammengefaßt. Dabei handelt es sich um zwei Assembler- und ein Pascal-Modul. Im einzelnen besteht das ISDNLAP aus folgenden Quelldateien:

Abb. B.2.4: Die Komponenten des ISDNLAP-Quellcodes

ISDNLAP.p

enthält den eigentlichen LAP-Code. Hier befinden sich die Proze­duren zur Verbindungssteuerung und zur Paketübertragung. Die Reihenfolge der Prozeduren in ISDNLAP.p ist so gewählt, daß wenig FORWARD-Deklarationen notwendig werden.

ISDNatlk.a

integriert die Routinen zur Installation und Deinstallation des LAPWrite-Codes im ATalkHk2 und einige Glueroutinen zum Aufruf der Pascal-Prozeduren aus ISDNLAP.p.

ISDNadev.a

besitzt zwei Routinen zur Auswahl des ISDNLAP im Kontrollfeld.

ISDNLAP.r

enthält die Resourcen des ADEV ISDNLAP.

ISDNLAP.make

übersetzt und bindet das ISDNLAP nach den vorgegebenen Build-Kommandos.

LAPMgrEqu.a

stellt Konstanten zur Verfügung, die von der ISDNadev.a- und ISDNatlk.a-Unit benötigt werden.

Die Assembler- und Pascal-Moduln verwenden Toolbox- und OS-Routinen, die jeweils importiert und vom Linker hinzugebunden werden müssen. Um zu verhindern, daß Code zur Bereichsüberprüfung eingebunden wird, muß mit der Compiler-Option {$R-} das Range Checking des Compilers deaktiviert werden. Damit auch die importierten Moduln mit dieser Option übersetzt werden, muß das vor USES geschehen:

{$R-} USES {$LOAD $$Shell(Plibraries)MemQDOSToolPack}

        Memtypes, Quickdraw, OSintf, Toolintf, Packintf, PasLibIntf;

 

Die einzelnen Teile des ISDNLAP werden durch den Linker schließlich zu den ‘adev’- und ‘atlk’-Resourcen zusammengebunden. Zur Erzeugung der ‘adev’-Resource wird die ‘adev’-Assemblerunit mit der SetNumber-Prozedur aus ISDNLAP.p kombiniert. Aus den restlichen Pascal-Prozeduren in ISDNLAP.p und dem ‘atlk’-Assemblermodul entsteht durch Linken die ‘atlk’-Resource:

ISDNadev ƒƒ ISDNadev.a.o ISDNLAP.p.o

ISDNatlk ƒƒ ISDNatlk.a.o ISDNLAP.p.o

 

Schließlich erzeugt der Resource Compiler (Rez) aus den einzelnen Code-Resourcen das ADEV ISDNLAP und Setfile setzt das Bundlebit, den Typ und den Creator:

ISDNLAP ƒƒ ISDNLAP.r ISDNadev ISDNatlk

   Rez   ISDNLAP.r -o ISDNLAP

   Setfile -a B -t 'adev' -c 'ISDN' ISDNLAP

   duplicate -y ISDNLAP “{SystemFolder}”ISDNLAP

 

Zuletzt wird mit dem Kommando „duplicate“ eine Kopie des ISDNLAP im System Folder abgelegt. Somit ist es sofort für das Kontrollfeld und das CDEV Netzwerk verfügbar.

B.2.4 Konstanten, Datenstrukturen und globale Variablen

B.2.4.1 Konstanten

Die Konstanten des ISDNLAP, wie die Länge von Timeouts, bestimmte Kontrollzeichen und Strings[17], werden zur Zeit noch im Quelltext (hard coded) untergebracht. Das spart vor allem globale Variablen und/oder Zeit für das Lesen von Resourcen aus der Resource Fork. Dadurch können jedoch Konstanten nur durch Änderung im Quell­text modifiziert werden. Für eine endgültige Version des ISDNLAP empfiehlt es sich daher darüber nachzudenken, welche Konstanten modifizierbar sein und somit als Resourcen angelegt werden müssen. Zusätzlich werden verschiedene Konstanten aus den Toolbox- und OS-Interfacedateien verwendet.

B.2.4.2 Datenstrukturen und Typen

Die Verwendung von strukturierten Datentypen, wie Pascal sie anbietet, erlaubt einen höheren Abstraktionsgrad als die Assembler-Ebene und erleichtert damit den Zugriff auf Daten wesentlich. Die Objekte der Datenkommunikation lassen sich durch Pascal-Typen strukturieren und gewinnen somit gegenüber einem unstrukturierten Bitstrom an Anschaulichkeit .

Die wichtigsten Typen, die bei der Implementierung des ISDNLAP Verwendung finden, werden hier kurz erläutert[18]:

Port

Ein Aufzählungstyp, dessen Konstanten die beiden Schnittstellen des Macintosh darstellen (MODEM, PRINTER).

LineStatus

Dieser Aufzählungstyp spiegelt die Zustände wider, die eine Verbindung annehmen kann.

TransmitStatus

Dieser Ergebnistyp gibt die Situationen, die bei der Übertragung eines Paketes auftreten können, wieder.

FrameStatus

Wird von den Empfangsprozeduren verwendet, um anzuzeigen, ob ein Paket fehlerfrei empfangen wurde oder welcher Fehler aufge­treten ist.

IdStatus

Die Zustände der Node ID bei der dynam. Node-Adressierung.

aDataField

Hier handelt es sich um das Format eines unstrukturierten Frames, d.h. ein Frame wird nur als ein Feld von Zeichen betrachtet.

NumberCache

In dieser Struktur wird die zuletzt gewählte Rufnummer mit zuge­höriger Node ID abgelegt.

ABusVars

Die Struktur der lokalen .MPP-Variablen, soweit bekannt.

SCCReg

Über diesen Record werden die Kontroll- und Daten-Register des SCC adressiert.

Level2IntTable

Diese Struktur besitzt die Level-2 Interrupt Vector Table.

MyWDSEntry

Eine Kurzversion der Write Data Structure, in der Kontroll­pakete für die TransmitFrame-Prozedur verpackt werden.

aFrame

Dieser Variantenrecord spiegelt die Struktur eines LAP-Frames wider. Zum einen kann über ein Feld auf die Zeichen zugegriffen werden, zum anderen ist der Packet Header direkt ansprechbar. Hier ist auch die Struktur der Read Header Area enthalten.

GlobalVars

Ein Record, der den im Assembler-Quellcode angelegten Bereich der globalen Variablen strukturiert.

B.2.4.3 Globale Variablen

Die globalen Variablen des ISDNLAP sind als Datenblock im Assembler-Modul ISDNatlk.a angelegt. Sie enthalten nur die notwendigsten Werte, die für die Dauer der Installation für verschiedene Prozeduren relevant sind. Die Struktur der globalen Variablen wird durch den Typ GlobalVars festgelegt. Die Felder dieses Records haben folgende Bedeutung:

 

MPPVars

ABusVarsPtr

Ein Zeiger auf die lokalen .MPP-Variablen.

Guard

BOOLEAN

Dieses Flag verhindert asynchrone Aufrufe von TransmitPacket und signalisiert, daß ein Paket gesendet wird.

SCCWrCtl, SCCRdCtl, SCCWrData, SCCRdData

Ptr

Über diese Adressen können die Write-, und Read-Kontroll- und Datenregister des SCC erreicht werden.

NameServer-Number

ISDNNumber

Hier wird die Nummer des NameServers aus der Resource abgelegt.

ActualNumber

ISDNNumber

Die ermittelte Rufnummer des Knotens, zu dem eine Verbindung aufgebaut werden soll.

IsUp

LineStatus

Der Status einer Verbindung.

Last Transmission

LONGINT

Die Anzahl der VBL-Task-Aufrufe seit dem zuletzt übertragenen Paket.

TOut

BOOLEAN

Signalisiert einen Timeout durch TimeOutTask.

VBLEntry

VBLTask

Eintrag in der VBLQueue[19] für die VBL-Task Terminator (Abbau einer Verbindung).

MSecQueue

TMTask

Eintrag der Time Manager-Task TimeOutTask in der Time Manager Queue.

AddrStatus

IdStatus

Diese Variable zeigt an, ob die Node ID einer Station gültig ist.

HisAddress

BYTE

Die Node ID der Gegenstation der aktuellen Verbindung.

OldReceive InterruptVector,OldReceive SpecialVector

Ptr

Hier werden die Interruptvektoren gespeichert, die sich vor der Installation des ISDNLAP in der Level-2 Interrupt Vector Table befanden.

Cache

NumberCache

Im Cache befinden sich die Rufnummer und die Node ID derjenigen Station, zu der zuletzt eine Verbindung bestand.

globzeux

globrec

Enthält einen Zeiger auf das History im System Heap.


B.3 Die Verbindungssteuerung nach X.21

B.3.1 Die Initialisierung des SCC für den Verbindungsaufbau

B.3.1.1 Zugriff auf den SCC in Pascal

Der Zugriff auf den SCC des Macintosh ist „memory mapped“, d.h. er er­folgt über logische Adressen. Den 8 Kontrollregistern des SCC sind 8 Adressen im Adreßbereich zuge­ordnet. Für jeden Kanal (Port) gibt es sowohl für Lese- als auch für Schreibzugriffe jeweils ein Daten- und ein Kontrollregister. Die Basisadressen dieser Register haben folgende Werte:

Globale Variable

Macintosh Plus

Macintosh II[20]

SCCRd ($1D8)

$9FFF8

$50F04000

SCCWr ($1DC)

$BFFF9

$50F04000

Abb. B.3.1: Die Basisadressen des SCC bei verschiedenen Macintosh-Modellen

Diese Werte können den globalen Variablen SCCRd und SCCWr des Macintosh-Betriebs­systems entnommen werden. Da sich die Ba­sisadressen mit neuen Betriebs­system­­versio­nen ändern können, empfiehlt es sich, diese stets den globalen Variablen zu entnehmen.

Abb. B.3.2: Adressierung der Daten- und Kontrollregister des SCC

Die Adressen der einzelnen SCC-Register wer­den aus den 2 Basisadressen SCCRBase und SCCWBase und den Offsets der Felder des folgenden Records gebildet[21]:

   SCCReg =    RECORD

                 bCtl: INTEGER;

                 aCtl: INTEGER;

                 bData: INTEGER;

                 aData: INTEGER

               END;

Abb. B.3.3: Die Anordnung der SCC-Register als Pascal-Record

In Pascal erhält man die Adressen der Daten- und Kontrollregister eines Ports, indem man die Adresse von SCCWr bzw. SCCRd als Handle auf die Register des SCC interpre­tiert und dieses Handle entsprechend dereferenziert.

SCCWBasePtr:=SCCHandle($1DC);

SCCWrCtl:=@SCCWBasePtr^^.bCtl;

 

Über die Adressen der jeweiligen Register erfolgt dann der Schreib- und Lesezugriff auf die gewünschten Register:

SCCWrCtl^ := value; value := SCCRdCtl^;

 

Über die Datenregister erfolgt der Austausch von Nutzdaten. Auf sie kann direkt zuge­griffen werden, da für beide Ports jeweils sowohl zum Schreiben als auch zum Lesen ein eigenes Datenregister zur Verfügung steht. Für die Programmierung des SCC verfügt jeder Kanal über einen internen Satz von Write- und Read-Registern, der nicht explizit auf logische Adressen abgebildet ist, sondern auf den über die Kontroll­register zuge­griffen wird. Mit den Write-Registern wird der SCC initialisiert und gesteuert, die Read-Register dienen der Statusabfrage. Die Write-Register können nicht gelesen werden, so daß es leider nicht möglich ist, die programmierten Parameter zu überprüfen. Der Zugriff auf die internen Register geschieht in 2 Stufen:

Schreiben eines Kontrollregisters:

1.   Die Nummer des Write-Registers, auf das zugegriffen werden soll, wird in das Write-Kontrollregister geschrieben.

2.   Der Wert, der geschrieben werden soll, wird im Write-Kontrollregister abgelegt.

Im ISDNLAP wird das Beschreiben von Write-Kontrollregistern, wie es z.B. bei der Initialisierung des SCC notwendig ist, von der Prozedur Write_SCC übernommen:

PROCEDURE Write_SCC(reg, value: BYTE);

 

   VAR MyGlobals: GlobalPtr;

   BEGIN

     MyGlobals:=GlobalPtr(@Globals);

     WITH MyGlobals^ DO BEGIN

         IF reg<>0 THEN SCCWrCtl^:=reg;

         SCCWrCtl^:=value;

     END

   END; { Write_SCC }

 

Ähnlich wie der Schreibzugriff verläuft auch das Lesen eines Read-Kontrollregisters. Da meist das Read-Register 0 gelesen wird, ist im ISDNLAP keine gesonderte Read_SCC Prozedur vorgesehen.

Lesen eines Kontrollregisters:

1.   Die Nummer des zu lesenden Read-Kontrollregisters wird in das Write-Kontroll­register geschrieben.

2.   Der Wert des Read-Registers wird aus dem Read-Kontrollregister gelesen.

Eine Ausnahme bilden die Write- und Read-Kontrollregister 0. Auf sie kann in einem Schritt zugegriffen werden, indem bei einem Schreibzugriff ein Wert direkt in das Kontrollregister geschrieben und bei einem Lesezugriff das Kon­trollregister direkt gelesen wird. Daß Register 0 gelesen werden soll, erkennt der SCC bei Lesezugriffen daran, daß vorher kein an­deres Register gesetzt wurde. Bei Schreib­zugriffen kann der SCC zwischen einer Registernummer und einem Wert für Register 0 unterscheiden, da die möglichen Register­nummern und die möglichen Kommandos für Write-Re­gister 0 disjunkt sind.

B.3.1.2 Verwendung der Kontroll- und Statusregister

Die Beschreibung der einzelnen Register ist dem technischen Handbuch des SCC zu entnehmen[22]. Im folgenden werden wegen ihrer Bedeutung die Read-Regi­ster 0 und 1, die wichtige Statusregister für das Senden und Empfangen von Frames darstellen, sowie das Write-Register 0 beschrieben, über das Kommandos an den SCC gerichtet werden.

Abb. B.3.4: Die Read-Register 0 und 1

Das Read-Register 0 enthält u.a. den Status des Sende- und Empfangspuffers (FIFO) (vgl. Abb. B.3.4). Für die Implementierung der Verbindungssteuerung und einer Paket­übertragung im HDLC-Mode sind lediglich die folgenden Bits von Bedeutung:

RxCharAvailable

zeigt an, daß mindestens ein Zeichen im Empfangspuffer vorhan­den ist.

TxBufferEmpty

Sobald dieses Bit ge­setzt ist, ist das letzte Zeichen übertragen und ein weiteres Zeichen kann in den Sendepuffer ge­schrieben werden.

TxUnderrun/EOM

meldet, daß der SCC einen Frame mit der Prüfsumme und einem Flag abgeschlossen hat. Das Senden eines Frames ist beendet, sobald dieses Bit gesetzt ist. Es kann über das Write-Register 0 zurückgesetzt werden.

 Das Read-Register 1 gibt den Status eines empfangenen Zeichens an:

RxOverrunError

signalisiert einen Überlauf des Empfangspuffers. Zu diesem Fehler kommt es, wenn ankommende Zeichen nicht schnell genug aus dem Empfangspuffer gelesen werden. Das Bit bleibt bei allen nachfolgenden Zeichen solange gesetzt, bis es durch Setzen des ErrorReset-Bits im Write-Register 0 zurückgesetzt wird.

EndOfFrame

Sobald der SCC das schließende Flag eines Frames erkennt, wird beim zuletzt empfangenen Zeichen das EOF-Bit gesetzt.

CRCError

gibt beim letzten Zeichen eines Frames an, ob die Prüfsumme des Frames korrekt ist. Da es den momentanen Zustand des CRC-Checkers widerspiegelt, hat es bei Zeichen innerhalb eines Frames keine Bedeutung und ist nur gültig, wenn gleichzeitig das EOF-Bit gesetzt ist.

Neben den genannten Read-Register 0 und 1 ist vor allem das Write-Register 0 von Bedeutung (Abb. B.3.5). Über dieses Register kann, wie oben beschrieben, ein anderes Register ausgewählt werden. Darüber hinaus werden die Bits 3 bis 7 genutzt, um einige besonders häufige Kommandos auszuführen[23]:

Reset HIUS

(00111000)

Nimmt den höchsten Interrupt wieder zurück.

Error Reset

(00110000)

Löscht die Fehlerbits in Read-Register 1.

Send Abort

(00011000)

Sendet eine Abbruchsequenz.

Reset EOM

(11000000)

Reset des End of Message-Bits.

Abb. B.3.5: Das Write-Register 0

Ankommende Zeichen werden aus einem FIFO-Puffer gelesen. Dabei ist jedem Zeichen in diesem 3 Byte großen Empfangspuffer ein Status im internen Sta­tuspuffer zugeordnet. Parallel zu jedem Zeichen wird der Status gepuffer­t, der über das Read-Register 1 gelesen wird (vgl. Abb. 6.3.1). Der über das Register 1 zurückgelieferte Status bezieht sich stets auf das nächste aus dem Puffer zu entnehmende Zeichen. Da mit dem Lesen eines Zeichens auch der dazugehörige Status aus dem Statuspuffer entfernt wird, muß zuvor der Status des Zeichens betrachtet werden. Erst danach darf das Zeichen selbst abgeholt werden.

B.3.1.3 Initialisierung des Verbindungsaufbaus durch InitSCCStuff

Wird der LAPWrite-Code des ISDNLAP beim Öffnen des .MPP erstmals gerufen, um ein ENQ-Paket zu übertragen, so aktiviert er über die Prozedur InitSCCStuff den SCC für den Verbindungsaufbau. Vor dem ersten Senden eines Paketes, d.h. vor dem Öffnen des .MPP, darf InitSCCStuff nicht gerufen werden, da andernfalls Anrufe und Pakete angenommen werden könnten, ohne daß die lokalen Variablen des .MPP (ABusVars) initialisiert sind.

PROCEDURE InitSCCStuff(First: BOOLEAN);

Über den Parameter First wird der Prozedur InitSCCStuff mitgeteilt, daß sie erstmals gerufen wird. Das ist aus zwei Gründen erforderlich:

v   Zum einen bestimmt die Prozedur, wie unter B.3.1.1 beschrieben, die Basisadressen der SCC-Register, was aber nur einmal geschehen muß.

v   Zum anderen darf das Retten der alten Interruptserviceroutine (ISR) nur beim ersten Aufruf geschehen. Andernfalls werden sie überschrieben.

Nachdem InitSCCStuff die Basisadressen der SCC-Register für den gewünschten Port bestimmt hat, wird der SCC einmal „angelesen“ und so das Write-Kontroll­register zurückgesetzt. Dadurch wird verhindert, daß der SCC einen bereits in Write-Kontroll­register 0 befindlichen Wert irrtüm­licher­weise als Registerauswahl inter­pretiert.

Nun wird über die Prozedur Write_SCC ein Reset des ausgewählten Ports veranlaßt und der SCC für die Übertragung von Zeichen im BSC-Mode initialisiert. Das geschieht nach folgender Initialisierungssequenz:

Register

Wert

Bedeutung der Werte

9

$40/$80

Reset von Port B oder Port A.

4

$1

Synchroner Modus, 1 SYNC-Zeichen und ungerade Parität.

10

$0

NRZ-Kodierung der Zeichen.

6

$FF

Sende binäre Einsen, falls Leitung idle.

7

$16

(SYNC) SYN-Zeichen (00010110).

11

$28

Externer Receive- und Transmit-Takt.

14

$0

DTR zulassen.

5

$2A

7-Bit-Zeichen senden, Transmitter an, RTS-Signal an,  high (Control OFF).

3

$41

7-Bit-Zeichen empfangen, Receiver an.

15

$8

DCD-Interrupts für die Maus zulassen.

1

$9

Receive-Interrupts beim ersten Zeichen und Special Condition, Externe Interrupts für die Maus zulassen.

9

$A

Keinen Interruptvektor ausgeben und Master Interrupts zulassen.

Abb. B.3.6: Initialisierungssequenz des SCC für den Verbindungsaufbau

Anmerkungen zur Initialisierungssequenz der Verbindungssteuerung:

v   Nach dem Reset eines Ports sind verschiedene Kontrollregister bereits passend vorbe­legt und müssen daher nicht mehr initialisiert werden.

v   Das RTS-Signal muß gesetzt werden, damit der 26LS30-Schnittstellentreiber „enabled“ wird und das Sendesignal an den Port weitergibt.

v   Das Kontrollregister 6 wird mit $FF beschrieben. Das hat zur Folge, daß im Leer­zustand, also wenn sonst keine Zeichen übertragen werden, kontinuierlich binäre Einsen gesendet werden.

v   Das DTR-Signal (Control der X.21-Norm) muß kontinuierlich auf logisch eins (OFF-Zustand für X.21) sein. Zusammen mit der Transmit-Leitung signalisiert das die Bereit­schaft der DTE (DTE Ready).

v   Die Zeichen beim Verbindungsaufbau müssen mit 7 Bit und ungerader Parität übertragen werden.

v   Es ist nicht notwendig, daß der SCC während des Interruptzyklus des Prozessors einen Interruptvektor auf den Bus legt, da die Ebene 2 dem SCC fest zugeordnet ist und so der Prozessor den Verursacher des Interrupts aus der Prioritätsebene des Interrupts bestimmen kann. Der Level-2 Interrupt Handler stellt über den modifizierten Interruptvektor, den er aus dem SCC liest, die Ursache des Interrupts[24], die in den niederwertigen 4 Bit des Interruptvektors kodiert ist, fest.

v   Bevor InitSCCStuff die SCC-Interrupts zuläßt, muß die ISR ReceiveCall, die ankommende Anrufe entgegennimmt, in der Level2IntTable installiert werden. Damit bei der Deinstallation des ISDNLAP der alte Zustand wiederhergestellt werden kann, ist es notwendig, den Inhalt der Level2IntTable zu retten, bevor er durch einen Zeiger auf ReceiveCall überschrieben wird.

v   ISDNLAP verwendet in der Verbindungsaufbauphase einen externen Takt von 9600 Hz, der von der GSX-Schnittstelle geliefert wird.

B.3.2 Der Verbindungsaufbau

Der Verbindungsaufbau erfolgt über GSXI-Schnittstellen von Nixdorf und ein modifi­zier­tes X.21-Protokoll. Von den seriellen Ports des Macintosh Plus führen zu wenig Verbindungen zum SCC, so daß das Indicate-Signal der X.21-Empfehlung, das den Status der Verbindung anzeigt, nicht verfügbar ist[25]. Der Zustand der Verbindung kann also nicht direkt an der Schnittstelle abgelesen werden. Dadurch wird es notwendig, daß die Leitung vor dem Senden eines Nutzpaketes durch den Austausch von Kontroll­paketen explizit geprüft wird.

Weiter sind sämtliche Timeouts der X.21-Norm bei Verbindungsproblemen für eine Übertragung von AppleTalk­-Paketen[26] über eine PBX zu lang. Um dem Benutzer des ISDN-AppleTalk im Fehlerfall lange Wartenzeiten zu ersparen, müssen sie verkürzt und den Vermittlungszeiten einer PBX angepaßt werden. Im öffentlichen ISDN können diese durchaus länger sein. Hier fehlt aber zur Zeit die Vergleichsmöglichkeit.

            LineStatus = (connected, called, inProcess, notConnected, timeout, busy);

Die Zustände einer Verbindung werden durch den Typ LineStatus repräsentiert. In der globalen Variablen IsUp wird der aktuelle Zustand einer Verbindung festgehalten.

B.3.2.1 Der aktive Verbindungsaufbau

Das Wählen beim Verbindungsaufbau übernimmt die Prozedur DialUp. Als einziger Parameter wird der Prozedur die zu wählende Rufnummer übergeben. Über das Funkti­onsergebnis wird der Zustand der Verbindung vom Typ LineStatus geliefert.

FUNCTION DialUp(Number: ISDNNumber): LineStatus;

Eigentlich wäre es sinnvoll, als erstes den Zustand der DCE zu prüfen. Das ist aber durch das Fehlen des Indicate-Signals nicht möglich. Besteht eine Verbindung (HisAddress <> 0), so bricht DialUp diese durch den Aufruf von HangUp ab. Andernfalls wird nur der SCC neu initialisiert. Das ist notwendig, um eventuelle Fehler­zu­stände, wie eine unterbrochene Verbindung, zu beheben[27]. Anschließend werden die Receive-Interrupts gesperrt, damit ankommende Zeichen nicht durch ReceiveCall als ankommender Ruf interpretiert werden. Nachdem die Wahlzeichen (Rufnummer) mit zwei SYN-Zeichen am Anfang und einem ‘+’-Zeichen am Ende versehen wurden, signalisiert die DTE durch Senden von kontinuierlichen 0-Bits und durch Fallenlassen des Control-Signals (DTR) der Schnittstelle den Wunsch zum Verbindungsauf­bau­. Nun muß die Wahlauf­­forderung der X.21-Schnittstelle (‘+’-Zeichen) abgewartet werden. Dabei ist zu beachten, daß die Schnittstelle mit 7 Bit und ungerader Parität sendet. Wurde innerhalb einer bestimmten Zeitspanne keine Wahlauf­­forderung empfangen, so wird der Wählvorgang mit der Fehlermeldung Timeout abgebrochen.

Andernfalls können die Wahlzeichen an die Schnittstelle übermittelt werden, indem in einer Schleife immer dann, wenn der Transmit-Puffer des SCC leer ist, ein Zeichen über­tragen wird[28]. Gleichzeitig werden ankommende Zeichen (‘+’) gelesen. Sind die Wahl­zeichen übertragen, so muß die Transmit-Leitung wieder kontinuierliche 1-Bits senden.

Die Schnittstelle antwortet auf die Wahlzeichen mit der Anschlußkennung, die entweder eine Fehlermeldung oder die Rufnummer der Gegenstation enthält. Um diese einzulesen, muß gewartet werden, bis keine ‘+’-Zeichen mehr ankommen. Dabei ist zu beachten, daß die Anschlußkennung ihrerseits ‘+’-Character enthalten kann und von zwei SYN-Zeichen angeführt wird[29]. Die Anschlußkennung ist beendet, wenn nur noch kontinuierliche 1-Bits gelesen werden. Wurde nach einer bestimmten Zeit noch keine Anschlußkennung empfangen, so wird mit einem Timeout abgebrochen.

Die empfangene Anschlußkennung gibt Auskunft über den Verlauf des Verbindungs­aufbaus. Daher wird getestet, ob die Anschlußkennung ein ‘*’-Zeichen enthält. Ist das nicht der Fall, so wurde nicht die Rufnummer der Gegenseite, sondern eine Fehler­meldung empfangen. Enthält die Anschlußkennung die Zeichenfolge ‘+21+’, so handelt es sich um eine Besetztmeldung. DialUp liefert als Funktionsergebnis busy. Bei allen anderen Fehlercodes ist der Fehler nicht zu beheben und eine Wahl­wieder­holung hat keinen Sinn. Ist in der Anschlußkennung jedoch die Rufnummer der Gegenseite enthal­ten, so wird durch die Prozedur InitHDLCStuff der SCC für die Paketüber­tragung initialisiert. Durch das Fehlen des Indicate-Signals ist nicht erkennbar, ob und wann die Leitung wirklich geschaltet ist und für die Übertragung zur Verfügung steht. Dabei kann es bis zu 1,5 Sekunden dauern, bis eine Verbindung durchgeschaltet ist und die Übertra­gung sich stabilisiert hat. Daher liefert DialUp als Funktionsergebnis höchstens inProcess und noch nicht connected. Im Anschluß muß bei der dynamischen Node-Adressierung mittels AcquireAddress durch die Übermittlung von Enquiry Control Frames geprüft werden, ob eine Verbindung wirklich besteht. Wird innerhalb einer gewissen Zeit kein Acknowledge Control Frame erwidert, so bedeutet das, daß keine Verbindung geschaltet wurde und die Verbindung wird durch Aufruf der Prozedur HangUp zurückgesetzt.

B.3.2.2 Der passive Verbindungsaufbau

Bei der Initialisierung des LAP wird durch InitSCCStuff in der Level2IntTable eine Interruptserviceroutine ReceiveCall installiert, die einen ankommenden Anruf entgegennimmt.

Wird ReceiveCall über die Interrupttabelle gerufen, so prüft sie zunächst, ob von der Schnittstelle zur Signalisierung eines ankommenden Anrufs BEL-Zeichen gesendet werden. Wurde innerhalb einer bestimmten Zeit ein BEL-Zeichen empfangen, so wird über die Funktion IsMPPOpen anhand der globalen Variablen PortBUse geprüft, ob AppleTalk initialisiert ist. Trifft dies zu, dann wird durch Zurücksetzen des DTR-Signals (control) im Write-Kontrollregister 5 der Schnittstelle die Annahme des Anrufs (call accept) signalisiert. Danach sendet die Schnittstelle die Anschlußkennung, die in der Variablen ConnectedTo abgelegt wird.

Ein Auswerten der Anschlußkennung auf Fehlermeldungen ist nicht erforderlich, da die Schnittstelle bei Fehlern normalerweise keine Verbindung aufbaut. Wurde eine Anschluß­kennung erhalten, so wird der Zähler LastTransmission zurückgesetzt und das ISDNLAP und der SCC mit InitHDLCStuff für die Paketübertragung initialisiert. Die Variable IsUp zeigt nun mit dem Wert InProcess an, daß eine Verbindung im Aufbau ist. Da das Indicate-Signal nicht verfügbar ist, muß die Leitung explizit getestet werden. Erst dann erhält IsUp den Wert called. Schließlich wird durch die Prozedur FilterNumber der Anschlußkennung die Ruf­nummer der anrufenden Station entnom­men und in einem Zwischenspeicher festgehalten. Tritt ein Timeout oder ein anderer Fehlerzustand auf, so wird die Verbindung durch HangUp zurückgesetzt. Bevor ReceiveCall beendet wird, müssen durch Beschreiben des Write-Kontrollregisters die Interrupts des SCC wieder zugelassen werden (RHIUS).

B.3.3 Der Verbindungsabbau

Nachdem es nun möglich ist, Verbindungen aufzubauen, muß auch ein Abbruch erfolgen können. Ein Verbindungsabbau ist immer dann notwendig,

v   wenn eine Verbindung zu einem Node besteht, dessen Node ID nicht mit der Ziel­adresse des nächsten zu sendenden Paketes übereinstimmt.

v   wenn ein Fehler beim Verbindungsaufbau oder der Node-Adressierung auftritt.

v   wenn eine bestimmte Zeit kein Paket mehr übertragen wurde.

v   wenn die Gegenseite eine Verbindungsauflösung fordert.

v   wenn eine Verbindung unvorhergesehen unterbrochen wurde und der SCC wieder für einen erneuten Verbindungsaufbau initialisiert werden muß.

Der Umstand, daß der Macintosh das Indicate-Signal nicht auswerten kann, beeinträchtigt nicht nur den Verbindungsaufbau, sondern auch den Abbau einer Verbindung.

B.3.3.1 Der aktive Verbindungsabbau

Ein aktiver Verbindungsabbau wird dann durchgeführt, wenn beim Verbindungsaufbau ein Fehler aufgetreten ist oder eine bestehende Verbindung unterbrochen werden muß, da ein Paket an eine andere Station zu übertragen ist.

Zum Abbrechen einer Verbindung im ISDNLAP dient die Prozedur HangUp. Aufge­rufen wird sie mit einem Parameter quitpacket, der angibt, ob die Gegen­seite durch ein Kontrollpaket über den Verbindungs­abbau informiert werden soll.

PROCEDURE HangUp(quitpacket: BOOLEAN);

Nachdem der SCC angelesen ist und die Master Interrupts abgeschaltet sind, sendet HangUp, falls gewünscht, durch Aufruf von TransmitMessage ein HUP-Paket an die Gegenstelle. Danach wird kurz gewartet, bis das Paket die Gegenseite erreicht hat und gelesen wurde. Schließlich wird die Verbindung abgebrochen, indem kontinuierlich 0-Bits gesendet werden und das DTR (Control) in den OFF-Zustand versetzt wird.

Da das Indicate-Signal fehlt, um feststellen zu können, wann die Verbindung abgebaut und die Schnittstelle wieder bereit (DCE Ready) ist, bleibt nur die Möglichkeit eine bestimmte Zeit zu warten und dann das ISDNLAP und den SCC mit InitSCCStuff neu zu initiali­sieren.

B.3.3.2 Der passive Verbindungsabbau

Ursache für den passiven Verbindungsabbau ist, daß seit der Übertragung des letzten Paketes eine Verbindung bestanden hat und eine bestimmte Zeit kein Paket mehr übertra­gen wurde. Durch den passiven Verbindungsabbau wird auch eine eventuelle Fehlersi­tuation, z.B. eine Unter­brechung der Verbindung, behoben.

Das Testen und Abbrechen einer Verbindung durch einen Timeout übernimmt die Proze­dur Terminator. Hier handelt es sich um eine VBL-Task, die bei der Installation des LAP durch InitLAP in die VBLQueue des Betriebs­systems eingehängt und periodisch gerufen wird.

Unmittelbar nach dem Aufruf besorgt sich Terminator die globalen Variablen des ISDNLAP und erhöht den Zähler LastTransmission, der angibt wieviele Task­auf­rufe seit der letzten Paketübertragung stattgefunden haben. Hat dieser Zähler einen bestimmten Wert TerminateDelay überschritten, und besteht nach der Variablen IsUp eine Verbindung, so löst die VBL-Task die Verbindung mit HangUp(TRUE) aus. Bevor die Task beendet wird, muß in VBLEntry der vblCount wieder auf den Startwert TaskTime gesetzt werden.

Normalerweise erkennt eine DTE anhand des Indicate-Signals, daß von der Gegenseite die Verbindung abgebrochen wird. Da das hier nicht möglich ist, muß eine abbrechende Station ihre Gegenstation durch Senden eines HangUp-Paketes über den bevorstehenden Verbin­dungs­abbau informieren. Ohne dieses Kontrollpaket würde die Verbindung zur Gegenstation einfach abgebrochen und diese wäre erst nach dem nächsten Timeout wieder erreichbar. Erhält also eine Station ein HUP-Paket, so weiß sie, daß die Gegen­seite die Verbindung abzubrechen wünscht und ruft ihrerseits die Prozedur HangUp, um das ISDNLAP für den Verbindungsaufbau neu zu initialisieren. Geht dieses Paket verloren, so wird spätestens nach dem Ablauf des Timers LastTransmission die Initia­lisie­rung für einen erneuten Verbin­dungs­aufbau vorge­nommen.

B.3.4 Die Adressierung im ISDNLAP

B.3.4.1 Dynamische Node-Adressierung und Registrierung der Node ID

Beim Öffnen des .MPP-Treibers muß das ISDNLAP die Node ID des Knotens auf dem Netzwerk registrieren. Das geschieht im ISDNLAP durch Übertragung von ENQ-Paketen an den NameServer, der sich die Rufnummer und die Node ID ankommender ENQ-Pakete merkt. Durch die Verbindungssteuerung des ISDNLAP werden alle Broad­cast-Pakete an den NameServer geleitet. Das RTMP sendet beim Öffnen des .MPP zwei Broadcast-Pakete ($FF) an das Netz (Abb.B.3.7). Dabei werden zum Testen der Verbin­dung auch ENQ-Pakete an den NameServer übertragen und die Eindeutigkeit geprüft. Ein explizites Anwählen des NameServers zur Registrierung der Node ID ist daher nicht notwendig. Zusätzlich werden bei jedem Anwählen des NameServers bei einem Name LookUp ENQ-Pakete übertragen.

FF 07 01 00 06 01 01 05 01                      .........

Abb. B.3.7: Ein RTMP-Paket an den NameServer beim Öffnen des .MPP-Treibers

Nach jedem Aufbau einer Verbindung werden zuerst ENQ-Pakete übertragen. Da durch das fehlende Indicate-Signal nicht erkannt werden kann, wann eine Verbindung verfüg­bar ist, muß die Verbindung explizit getestet werden. Dabei kann gleichzeitig festgestellt werden, ob die eigene Node ID eindeutig ist und auf der Gegenseite die gewünschte Station angetroffen wird. Ein nachträgliches Ändern der Node ID kommt nur dann vor, wenn sich eine Station nicht am NameServer angemeldet hat.

Abb. B.3.8: Dynamische Node-Adressierung im ISDNLAP

Zur dynamischen Node-Adressierung und zum Testen der Verbindung wurde für ISDNLAP die Pascal­prozedur AcquireAddress implementiert. Beim Aufruf wird der Prozedur ein Parameter Destination übergeben, der die Zieladresse der ENQ-Pakete enthält. Bei der Rück­kehr teilt die Funktion mit, ob eine Verbindung zur ange­wählten Station hergestellt werden konnte und ob die lokale Node ID gültig ist.

FUNCTION AcquireAddress(Destination: BYTE): BOOLEAN;

AcquireAddress testet zuerst, ob die Node ID der Station im gültigen Bereich liegt. Wenn nicht, wird die Node ID 1 gesetzt. Nach dem Initialisieren von Statusvariablen beginnt eine Schleife, die terminiert, wenn eine gültige Adresse vorliegt oder die Anzahl der Versuche für die Bestimmung einer Node ID überschritten ist. Innerhalb dieser Schleife wird in einer weiteren Schleife im Abstand von jeweils einer sechzigstel Sekunde ein ENQ-Paket an die übergebene Adresse Destination übermittelt. Die Gegen­sta­tion erhält ein ENQ-Paket und antwortet nur dann, wenn die Destination-Adresse mit ihrer Node ID übereinstimmt oder es sich um ein Broadcast-Paket handelt[30]. Somit kann der Sender feststellen, ob er die gewünschte Station erreicht hat.

Sobald das LAP ein ACK-Paket erhält, wird die Sendeschleife beendet. In der Variablen HisAddress wird dabei die Node ID des Knotens, zu dem eine Verbindung besteht, festgehalten. Wurde nach einer bestimmten Anzahl von Versuchen kein Paket empfan­gen, wird die Schleife unterbrochen. Wenn die Adresse bereits in Gebrauch ist, bestimmt AcquireAddress eine neue Node ID. Dabei wird berücksichtigt, ob es sich bei der ursprünglichen Node ID um eine Server ID handelte. Schließlich wird ein Zähler für die Anzahl der Versuche, eine gültige Node ID zu erhalten, erhöht. Wurde keine gültige Node ID ermittelt, so beginnt AcquireAddress erneut mit dem Senden von ENQ-Paketen und der Vorgang wiederholt sich (Abb. B.3.8). Als Resultat liefert die Funktion, ob eine gültige Node ID vorliegt und eine Antwort von der Gegenseite erhalten wurde.

B.3.4.2 Die Verbindungssteuerung des ISDNLAP

Steht ein LAP-Paket zur Übermittlung an, so ruft der LAP Manager den ‘atlk’-Assembler-Glue LAPWrite und dieser anschließend die Funktion TransmitPacket.

Abb. B.3.9: Die Prozeduren der Verbindungssteuerung

Diese prüft zunächst, ob die Node ID des Knotens, zu dem eine Verbindung besteht, mit der Zieladresse des Paketes übereinstimmt. Ist das nicht der Fall, so muß eine neue Verbindung eingerichtet werden. Da die Node ID des NamesServers im allgemeinen nicht $255 ist, wird zuvor geprüft, ob es sich um ein Broadcast-Paket handelt und ob eine Verbindung zum NameServer existiert. Trifft das zu, so kann nach dem Testen der Übertragungsleitung mit AcquireAddress das Paket übertragen werden. Andenfalls muß zuvor über DoDialing die Nummer zur Node ID ermittelt und eine neue Verbin­dung abgebaut werden.

Tritt in DoDialing oder in AcquireAddress ein Fehler auf, so wird TransmitPacket mit einer Fehlermeldung verlassen. Bevor ein Paket übermittelt wird, ist es notwendig, daß die Adresse der Gegenstation mit der Zieladresse des Paketes übereinstimmt, um unnötigen Verbindungsauf- und -abbau beim Betreiben einer Brücke zu vermeiden. Über TransmitFrame wird das Paket schließlich gesendet.

Den Aufbau einer Verbindung in TransmitPacket übernimmt DoDialing. Über­geben wird dieser Prozedur nur die Rufnummer des NameServers, mit der diese zunächst die Prozedur FindNumber ruft, um die Rufnummer der Zieladresse des anstehenden Paketes zu ermitteln.

FUNCTION DoDialing(Number: ISDNNumber): LineStatus;

Bei erfolgreicher Suche wird durch DialUp eine Verbindung zur ermittelten Rufnummer hergestellt. Andernfalls bleibt die Verbindung zum NameServer bestehen. Ist die Ruf­nummer besetzt, so wird nach kurzer Wartezeit die Anwahl solange wiederholt, bis der Verbindungsaufbau Erfolg hatte, ein Fehler aufgetreten ist oder nach einer Anzahl von Versuchen die Leitung immer noch nicht frei ist. Hat DoDialing keinen Erfolg, so erfolgt eine Meldung an den Klienten.

Stehen nach der Übertragung keine weiteren Pakete zur Übermittlung an, so wird die Verbindung durch die VBL-Task Terminator abgebrochen. Beim Verbindungsabbau wird die Variable LastTransmission wieder auf Null gesetzt.

B.3.4.3 Die Suche nach der Rufnummer

Beim Öffnen des .MPP wurde die Node ID und die Rufnummer jeder Station am NameServer registriert. Wird nun eine Rufnummer zu einer Node ID gesucht, so benutzt die Verbin­dungs­steuerung des ISDNLAP die Funktion FindNumber, um vom Name­Server die Rufnummer zu einer Node ID zu erfahren. Aufgerufen wird FindNumber dabei mit der Nummer des NameServers und der Node ID, für die eine Nummer zu ermitteln ist. Über den VAR-Parameter Number wird die Rufnummer zurückgeliefert. Wurde eine Ruf­nummer gefunden, so liefert FindNumber TRUE.

FindNumber prüft zuerst, ob es sich bei der gesuchten Adresse um die Broadcast-Adresse handelt (Abb. B.3.10). Trifft das zu, so wird die Nummer des NameServers zurück­geliefert. Befindet sich die gesuchte Node-Adresse im Zwischen­speicher Cache, so wird die in Cache gespeicherte Rufnummer zurückgegeben. Andernfalls wird die WDS für ein FND-Paket vorbereitet und das Paket mit TransmitPacket solange über­tragen, bis entweder eine Nummer vom NameServer gesendet oder eine Anzahl von Paketen übertragen wurde. Findet der NameServer die gesuchte Node ID, so sendet er ein RND-Paket mit der zugehörigen Nummer. Anschließend wird die Verbindung zum NameServer abgebrochen, die gesuchte Nummer über den VAR-Parameter zurück­ge­ge­ben und in der globalen Variablen Cache gespeichert. Liefert die unter der Rufnummer des NameServers erreichte Station nicht die gesuchte Nummer, so bleibt die Verbindung bestehen. Dadurch kann bei bekannter Rufnummer eine Station auch direkt, ohne den Umweg über den NameServer, angesprochen werden.

Abb. B.3.10: Der schematische Ablauf der Suche nach einer Rufnummer

B.3.4.4 Zwischenspeichern der Rufnummer

Um die Anzahl der Rufnummern, die beim NameServer gesucht werden, und damit die Anzahl der Verbindungen zu reduzieren, wird in der globalen Variablen Cache die zuletzt gewählte Rufnummer mit zugehöriger Node ID festgehalten. Das Speichern und Auswerten übernimmt bei aktiv aufgebauten Verbindungen die Funktion FindNumber.

NumberCache = RECORD

                StoredID: BYTE;

                StoredNumber: ISDNNumber;

              END;

 

Ebenso wird durch die Prozeduren ReceiveCall und ReceiveHeader die Nummer und Node ID derjenigen Station festgehalten, die zuletzt angerufen hat. Das Speichern der letzten Rufnummer führt zu einer erheblichen Reduzierung des Verkehrs, da es sehr wahrscheinlich ist, daß die gleiche Station mehrmals hintereinander angewählt wird.

PROCEDURE ClearCache(VAR Cache: NumberCache);

Wurde eine Node ID unter einer Rufnummer nicht erreicht, so wird Cache durch die Prozedur ClearCache gelöscht.

Mehrere Nummern im Cache zu halten erscheint nicht sinnvoll, da in den meisten Fällen nur eine Station zur selben Zeit angesprochen wird. Außerdem wird dadurch die Verwal­tung der Nummern im Zwischenspeicher komplexer und steht in keinem Verhältnis zum Nutzen.

Die retardierende objektorientierte Verbindungssteuerung und die Zwischenspeicherung der Rufnummern führen zu einer Minimierung der notwendigen Verbindungen.

B.4 Die Paketübertragung im ISDNLAP

B.4.1 Die Initialisierung des SCC für die Paketübertragung

Nachdem eine Verbindung zwischen zwei Knoten aufgebaut ist, wird mit der Über­tra­gung von (Kontroll-)Paketen begonnen. Dazu wird durch Aufruf der Prozedur InitHDLCStuff der SCC für eine Übertragung im HDLC-Modus initialisiert und eine Interruptserviceroutine ReceiveHeader zum Empfang eines Paketes eingerich­tet.

Bevor der SCC beschrieben werden kann, muß er erneut angelesen werden. Danach kann der Reset des Ports und die Initialisierung für den Verbindungsaufbau erfolgen. Das Beschreiben der Kontrollregister wird über die Prozedur Write_SCC vorgenommen. In Detail lautet die Initialisierungssequenz für die Paketübertragung im HDLC-Mode wie folgt:

Register

Wert

Bedeutung der Werte

9

$40/$80

Reset von Port B oder Port A.

4

$20

Verwendung des HDLC-Mode.

10

$80

CRC-Preset, NRZ-Kodierung.

11

$28

Externer Receive- und Transmit-Takt.

6

Node ID

Nur Pakete mit dieser Node ID empfangen.

7

$7E

Bei idle HDLC-Flag (01111110) senden.

5

$EB

Transmitter aktivieren, RTS an,  low.

3

$DD

8-Bit-Char. empfangen, Enter Hunt Mode (mit der Suche nach Flags beginnen), Receive CRC aktivieren, Adreßsuche beginnen, Receiver aktivieren.

15

$8

DCD-Interrupts für die Maus zulassen.

1

$11

Receive-Interrupts bei jedem Zeichen und Special Condition, Externe Interrupts für die Maus zulassen.

9

$A

Keinen Interruptvektor ausgeben und Master Interrupts zulassen.

Abb. B.4.1: Initialisierungssequenz des SCC für den HDLC-Mode des ISDNLAP

Anmerkungen zur Initialisierungssequenz der Paketübertragung:

v   Der Reset eines Ports belegt verschiedene Kontrollregister des SCC bereits passend vor. Diese müssen daher nicht mehr initialisiert werden.

v   Das RTS-Signal ist zu setzen, damit der 26LS30-Schnittstellentreiber „enabled“ und das Sendesignal an den Port weitergegeben wird.

v   Das Kontrollregister 6 wird mit der Node ID der Station beschrieben. Dadurch werden nur Pakete mit der passenden Zieladresse empfangen.

v   Das DTR-Signal (Control der X.21-Norm) muß kontinuierlich auf logisch Null (ON-Zustand für X.21) sein.

v   Die Zeichen werden mit 8 Bit und ohne Parität übertragen.

v   Im Gegensatz zu AppleTalk verwendet das ISDNLAP die NRZ-Kodierung (Abb. B.4.2). FM0-Signale können von der X.21-Schnittstelle nicht übertragen werden.

Abb. B.4.2: NRZ-Kodierung

v   Während das ALAP einen internen Sendetakt verwendet und den Empfangstakt aus der Kodierung des ankommenden Bitstroms entnimmt, benutzt ISDNLAP sowohl zum Senden als auch zum Empfangen einen externen Takt von 64 kHz.

v   Bevor InitHDLCStuff die SCC-Interrupts zuläßt, muß die Interruptserviceroutine ReceiveHeader, die den Header ankommender Pakete einliest, in der Level2IntTable installiert werden. Erst dann dürfen SCC-Interrupts zugelassen werden.

B.4.2 Paketformate und Pakettypen

B.4.2.1 Paketformat

AppleTalk verwendet für die Übertragung von LAP-Paketen ein HDLC/SDLC-Paket­format[31]. Dieses Format wird auch im ISDNLAP beibehalten. Im Unterschied zum ALAP sendet ISDNLAP zwischen den Paketen (Idle) ständig Flags. Auch wird am Ende eines Paketes keine Abbruchsequenz übertragen.

... FlagFlagFlagHeaderDatenDatenDatenCRCFlagFlagFlag...

Der Unterschied liegt darin begründet, daß beim ISDNLAP der Sender auch zwischen den Paketen aktiv sein darf, da hier keine Kollisionen – wie bei einer Busstruktur – auftreten können. Zudem ist durch den externen Takt und die NRZ-Kodierung keine gesonderte Synchronisation notwendig.

B.4.2.2 Pakettypen

ISDNLAP benutzt ähnliche Kontrollpakete wie das ALAP. Bekannte Typen von Kontroll­­­­paketen, sofern verwendet, behalten ihre Bedeutung. Um die zusätzlichen Funk­tionen des ISDNLAP zur Suche nach Rufnummern und zum Abbau einer Verbin­dung erfüllen zu können, werden drei neue LAP-Kontrollpakettypen hinzu­gefügt[32]:

LAP-Typ

ID

Bemerkung

lapENQ

$81

Wird für die dyn. Node-Adressierung, das Registrieren der Rufnummer und zum Testen der Verbindung verwendet.

lapACK

$82

Im Gegensatz zu ALAP wird im ISDNLAP auf ein ENQ-Paket immer ein ACK-Paket erwidert.

lapRTS

$84

Hat keine Bedeutung für ISDNLAP.

lapCTS

$85

Hat keine Bedeutung für ISDNLAP.

lapHUP

$86

Signalisiert der Gegenseite einen Verbindungsabbau.

lapFND

$87

Pakettyp zur Suche nach der Rufnummer am NameServer.

lapRND

$88

Antwortpaket mit der gesuchten Rufnummer.

B.4.3 Senden eines Paketes mit TransmitFrame

B.4.3.1 Paketübertragung

Wünscht ein Klient des LAP ein Paket zu senden, so ruft er den LAPWrite-Code des LAP, auf den ein Zeiger in ATalkHk2 verweist. Dabei übergibt er unter anderem einen Zeiger auf die WDS des LAP, in der das zu sendende Paket enthalten ist. Die LAPWrite-Prozedur des ISDNLAP ist eine Glueroutine, die die registerbasierenden Parameter für den Aufruf der eigentlichen Pascal-Senderoutinen auf den Stack legt. Schließlich wird von LAPWrite die Funktion TransmitPacket mit einem Zeiger auf die WDS gerufen.

FUNCTION TransmitPacket(WDS: WDSEntryPtr): TransmitStatus;

TransmitPacket prüft vor dem Senden des Paketes die Verbindung zur Gegen­station und stellt sie notfalls her (vgl. B.3.4.2). Ist die Gegenstation erreichbar, so wird die Funktion TransmitFrame gerufen, welche die eigentliche Übertragung übernimmt. Übergeben wird dieser Sende­routine nur ein Zeiger auf die WDS.

FUNCTION TransmitFrame(WDS: WDSEntryPtr): TransmitStatus;

Nachdem die Funktion TransmitFrame das Paket übertragen hat, kehrt sie mit einem positiven Funk­tions­ergebnis zu TransmitPacket zurück (vgl. Abb. B.4.3). Konnte keine Verbindung zur Gegen­station aufgebaut werden oder ist bei der Über­tra­gung des Paketes durch TransmitFrame eine Unregelmäßigkeit aufgetreten (fehlender Takt, Underrun, etc.), so kehrt TransmitPacket mit einer Fehlermeldung über LAPWrite zum Klienten zurück.

Abb. B.4.3: Die Aufrufkette beim Senden eines Paketes

Bevor die Prozedur TransmitFrame mit dem Senden des in der WDS verpackten Paketes beginnt, rettet sie mit der Inline-Prozedur SaveAndSetSR das Statusregister des M68000 und verhindert durch das Setzen des Wertes $2600, daß Interrupts die Übertragung eines Paketes stören. Die Bearbeitung eines Interrupts während der Über­tragung eines Paketes würde eine Verzögerung zwischen dem Senden von zwei Zeichen zur Folge haben. Diese führt zu einem Underrun des SCC, worauf je nach Stand des EOM-Bits erst die CRC-Prüfsumme oder sofort ein Flag übertragen und somit das Paket unterbrochen wird.

Nachdem LastTransmission zurückgesetzt wurde, wartet TransmitFrame bis das EOM-Bit im Read-Kontrollregister 0 gelöscht wird. Erst danach ist das vorange­gan­gene Paket vollständig übertragen. Um zu gewährleisten, daß eine Warteschleife terminiert[33], wird sie mit einem Timeout versehen. Dabei kann in Interrupts oder während die Interrupts gesperrt sind, die Systemzeit (Ticks) nicht verwendet werden, da diese deaktiviert ist. Wird das EOM-Bit bis zum Ablauf des Timeouts nicht zurück­gesetzt, so wird das Statusregister wiederher­gestellt und TransmitFrame mit einer Fehlermeldung verlassen.

Andernfalls wird die Struktur des übergebenen WDS durchlaufen (vgl. Abb. 5.2.2). Dabei werden die einzelnen Zeichen der WDS entnommen und durch Beschreiben des Write-Datenregisters gesendet (SCCWrData^ := BYTE;). Vor dem Senden des nächsten Zeichens muß anhand des TxBufferEmpty-Bits in Read-Kontrollregister 0 jeweils geprüft werden, ob das vorange­gangene Zeichen bereits übertragen wurde.

Enthält das Längenfeld der WDS die Länge 0, so ist die Struktur bis zum Ende durch­laufen und alle Daten sind übertragen. Unmittelbar nach dem letzten Zeichen muß nun das EOM-Bit zurückgesetzt werden. Damit wird der SCC veranlaßt, die CRC zu senden, sobald der Transmit-Puffer leer ist. Erst wenn das Paket vollständig übertragen ist, wird das EOM-Bit vom SCC wieder gesetzt. Danach wird das Statusregister wiederhergestellt und die Funktion TransmitFrame mit dem Resultat TransmitOk verlassen.

FUNCTION TransmitMessage(destAddr: BYTE; Message: BYTE):                                                        TransmitStatus;

Eine weitere Prozedur TransmitMessage übernimmt das Zusammenbauen eines WDS für ein Kontrollpaket und das Senden über TransmitFrame. Als Parameter werden die Zieladresse und der Typ der Meldung übergeben.

B.4.3.2 Fehlerbehandlung beim Senden eines Paketes

Zur Erkennung von Übertragungsfehlern wurde im ISDNLAP eine CRC-Prüfsumme vorgesehen. Der SCC unterstützt eine automatische CRC-Berechnung im Zusammenhang mit HDLC-Paketen. Unmittelbar nach der Übertragung eines Frames wird das EOM-Bit gesetzt, worauf ein Underrun des Transmitters das Senden der CRC veranlaßt. Die Überprüfung der CRC erfordert nur geringen Zeit- und Programmierauf­wand. Bei der Paketübertragung können folgende Fehler auftreten:

EOM-Bit gesetzt

     Das EOM-Bit wird nicht zurückgesetzt, da der vorangegangene Frame nicht beendet wurde (Ursache ist z.B. ein Fehlen des Taktes).

Das Transmit-Puffer wird nicht leer,

     da über die Schnittstelle kein externer Takt empfangen wird[34].

Underrun

     Tritt auf, wenn sowohl der Transmit-Puffer als auch das Transmit-Shift-Register leer wird. Ist das EOM-Bit nicht gesetzt, so werden Flags gesendet. Der Empfänger erhält ein Paket mit falscher Länge und falscher CRC. Ein Underrun des Transmitters kann in TransmitFrame kaum vorkommen, da (bis auf den Debugger) alle Interrupts gesperrt sind. Ein Testen auf einen Underrun und eine gesonderte Fehlerbehandlung[35] ist daher nicht notwendig. Wird nach einem Underrun der Rest des Paketes gesendet, so wird er entweder durch die Adreßsuche der Gegenstation ignoriert oder er wird als fehlerhaftes Paket mit falscher CRC verworfen.

B.4.4 Empfangen eines Paketes mit ReceiveFrame

B.4.4.1 Empfangen eines Paketes

Empfängt der SCC ein anderes Zeichen als ein Flag, so vergleicht er dieses erste Zeichen eines ankommenden Paketes (Zieladresse) mit der in Write-Kontrollregister 6 gesetzten Node ID. Stimmen die Adressen überein oder handelt es sich um ein Broadcast-Paket, so fordert der SCC beim Prozessor einen Interrupt an. Über die Level-2 Interrupt Vector Table wird die Pascal-Prozedur ReceiveHeader des ISDNLAP gerufen.

PROCEDURE ReceiveHeader;

Diese Prozedur setzt zuerst den Zähler der Taskaufrufe seit der letzten Paketübertragung (LastTransmission) zurück und besorgt sich aus den lokalen .MPP-Variablen einen Zeiger auf die Read Header Area (RHA), in die der Kopf des Paketes gelesen wird. Anschließend ruft sie die Funktion ReceiveFrame, um die ersten 5 Bytes in die RHA einzulesen. Der Parameter NoDiscard gleich TRUE gibt an, daß, falls vorhanden, weitere Zeichen nicht verworfen werden dürfen.

Kehrt ReceiveFrame zurück, so liefert sie über incomingLength die Anzahl der tatsächlich gelesenen Bytes. Das Funktionsergebnis zeigt an, ob in ReceiveFrame ein Fehler aufgetreten ist. Wurden 5 Zeichen fehlerfrei gelesen, so wird anhand des Paket­typs des Headers in der RHA entschieden, wie mit dem Paket weiter zu verfahren ist.

Handelt es sich um ein fortgesetztes Paket (continuedFrame), so kann es sich nur um ein Datenpaket oder ein RND-Paket handeln. Ist die aus dem Packet Header ermittelte Paketlänge sinnvoll, so wird anhand des Pakettyps weiter verzweigt. Liegt der Typ im Bereich von $1–$80, so wird über die Glueroutine ReadDispatch der LAP Manager gerufen, der dann anhand des Pakettyps den passenden Protocol Handler ruft, um das restliche Paket einzulesen. Übergeben wird dabei ein Zeiger auf das 6te, nächste freie Byte in der RHA und die Anzahl der Bytes, die noch zu lesen sind[36]. Handelt es sich bei diesem Header um den Anfang eines RND-Paketes, so befindet sich im vierten Byte des Headers die Node ID und im fünften die Länge der ermittelten Rufnummer. Über die Prozedur ReceiveFrame wird die im Paket enthaltene Rufnummer eingelesen und als aktuelle Rufnummer (ActualNumber) übernommen.

Hat ReceiveFrame das Paket vollständig gelesen (closedFrame), so handelt es sich um ein Kontrollpaket des LAP. Wurde der Kopf eines ENQ-Paketes empfangen, muß nicht weiter gelesen werden, da diese Pakete nur aus der Zieladresse, der Quelladresse, dem Typ und den zwei CRC-Bytes bestehen. ReceiveHeader notiert die Quell­adresse des Paketes in der globalen Variablen HisAddress und im Zwischen­speicher Cache. In IsUp wird festgehalten, daß die Station angerufen wurde. Schließlich wird über TransmitMessage ein ACK-Paket an die Gegenseite übertragen. Gehören die empfangenen Zeichen zu einem ACK-Paket, so merkt man sich die Quelladresse des Paketes. Je nachdem, ob diese mit der eigenen Node ID überein stimmt, d.h. die eigene Node ID eindeutig ist oder nicht, erhält die Variable AddrStatus den Wert InUse oder Valid. Wird ein HUP-Paket empfangen, so handelt es sich dabei um eine Auffor­derung, die Verbindung abzubrechen. Das geschieht über die Prozedur HangUp, wobei der Parameter FALSE bedeutet, daß kein HUP-Paket an die Gegenseite gesendet wird.

Entspricht keiner der hier angeführten Typen dem des empfangenen Headers, so wird der Rest des Paketes durch die Prozedur PurgeRest verworfen. Bevor die Prozedur beendet wird, müssen in jedem Fall durch das Kommando Reset Highest Interrupt Under Service (RHIUS) die Interrupts des SCC wieder zugelassen werden.

FUNCTION ReceiveFrame(incomingPacket:FramePtr;BytesToRead:INTEGER;                     VAR incomingLength: INTEGER; NoDiscard:BOOLEAN):FrameStatus;

Der Funktion ReceiveFrame wird ein Zeiger auf einen Puffer (incoming­Packet), die Anzahl der zu lesenden Bytes (BytesToRead), ein VAR-Parameter incoming­Length, der angibt wieviele Bytes gelesen wurden, und ein Flag NoDiscard, welches anzeigt, ob überschüssige Bytes verworfen werden sollen, übergeben. Als erstes initiali­siert die Funktion die lokalen Statusvariablen framedone und ReceiveFrame.

In einer annehmenden Schleife wird Zeichen für Zeichen gelesen, bis die Statusvariable framedone TRUE wird. Innerhalb der Schleife wird zuerst die Verfügbarkeit eines Zeichens geprüft. Ist kein Zeichen vorhanden, wird die Schleife mit einem Underrun-Fehler beendet. Enthält der FIFO-Puffer des SCC ein Zeichen, dann muß zuerst der Status des Zeichens in Read-Kontrollregister 1 gelesen werden. Signalisiert dieses Register „End of Frame“[37], so wird anhand des CRCErr-Bits festgestellt, ob der Frame unversehrt ist. Bei einem fehlerhaften Frame wird die Schleife mit einer Fehlermeldung verlassen. Andernfalls setzt ReceiveFrame die Statusvariable framedone und beendet beim nächsten Test der Abbuchbedingung die Schleife. Weiter muß anhand des Read-Kontrollregisters 1 geprüft werden, ob ein Overrun aufge­treten ist. Ist das nicht der Fall, so kann das Zeichen aus dem Read-Datenregister gelesen werden. Dazu wird der Zähler mit der Anzahl der gelesenen Zeichen inkrementiert und das Byte gelesen. Ist die gewünschte Anzahl von Bytes noch nicht gelesen, so wird das Zeichen in den überge­benen Puffer geschrieben. Wurden schon genügend Zeichen gelesen, so wird, je nach Wert des Parameters NoDiscard, die Schleife beendet oder mit dem Einlesen und Verwerfen der restlichen Zeichen des Frames fortgefahren.

Wurde die Schleife verlassen, und ist beim Empfangen des Frames ein Fehler aufge­treten, so „spült“ die Prozedur PurgeRest den Receiver des SCC. Schließlich kehrt die ReceiveFrame wieder zu ReceiveHeader zurück.

Die Prozedur ReceiveFrame wird aber nicht nur von ReceiveHeader zum Lesen des Paketkopfes verwendet, sondern auch von ReadPacket und ReadRest, um den restlichen Teil eines Frames zulesen. Dazu müssen lediglich die Parameter passend gesetzt werden. Der Aufruf von ReceiveFrame durch ReadPacket und ReadRest wird in Abschnitt B.5 beschrieben.

B.4.4.2 Fehlerbehandlung beim Empfangen eines Paketes

Bei der Übertragung eines Paketes können zahlreiche Fehler auftreten. Dem Aufrufenden werden sie über das Funktionsergebnis von ReceiveFrame mitgeteilt. Die wichtigsten Fehlersituationen und Maßnahmen zur Behebung werden hier erwähnt:

Frame Size Error

     Werden weniger als 5 Zeichen (Header & CRC) empfangen, so kann es sich nicht um ein gültiges LAP-Paket handeln und es wird verworfen. Zusätzlich ist im 4ten und 5ten Byte eines jeden Datenpaketes die Länge der Nutzdaten enthalten. Ist beim Aufruf von ReadRest die Anzahl der noch im Paket vorhandenen Zeichen länger als die Anzahl der zu lesenden Zeichen, so werden die überschüssigen Bytes verworfen. Anhand der Differenz von angeforderten und gelesenen Zeichen kann der Protocol Handler Fehler erkennen.

Frame Type

     Wurde ein Packet Header mit einem unbekannten Frametyp (Typ > $88) empfangen, so wird der Rest des Paketes durch die Prozedur PurgeRest verworfen.

Overrun/Underrun Error

     Tritt auf, wenn das LAP mit den Daten nicht synchronisiert bleibt, d.h unerwartet kein Zeichen mehr ankommt oder die ankommenden Zeichen nicht schnell genug entgegen­genommen werden können. Dieser Fehler muß durch einen Reset der Statusflags und durch Leeren des FIFO behoben werden (Übernimmt die Prozedur PurgeRest).

Frame Check Sequence

     Ist die Prüfsumme (CRC) eines Paketes falsch, so wird mit PurgeRest der Receiver zurückgesetzt und ReceiveFrame mit einer Fehlermeldung beendet.

Ein Reset des Empfängers geschieht über die Prozedur PurgeRest. Diese setzt als erstes das SYNC/HUNT-Bit, so daß alle Zeichen bis zum nächsten Flag verworfen werden. Anschließend kann das FIFO des SCC geleert werden. Schließlich werden über das Kommando ResetErr die Statusbits in den Read-Kontrollregistern, die einen Fehler signalisieren, explizit zurückgesetzt.

Bei sorgfältiger Programmierung ist die Implementierung einer Paketübertragung mit einer Geschwindigkeit von 64 kbit/s auch mit einer 0.4 MIPS-Maschine, wie dem Macintosh Plus, in einer Hochsprache (Pascal) möglich.


B.5 Das Interface zum AppleTalk Protocol Stack

B.5.1 Installation des ISDNLAP

Die Firma Apple hat für den Austausch des LAP für EtherTalk und andere Implemen­tie­rungen von AppleTalk den LAP Manager geschaffen. Vorgesehen ist zum einen ein CDEV Netzwerk, das sich im System Folder befindet und im Kontrollfeld erscheint, zum anderen eine INIT-Resource (ID 18), die beim Systemstart den ausge­wählten LAP-Code installiert[38].

Abb. B.5.1: Das CDEV Netzwerk und zugehörige ADEV im Kontrollfeld

B.5.1.1 Installation über das CDEV Netzwerk

Das CDEV Netzwerk durchsucht bei der Auswahl durch den Benutzer den System Folder nach Files vom Typ ADEV und zeigt alle alternativen AppleTalk-Implemen­tie­rungen mit ihrem Icon und ihrem Namen im Fenster des Kontrollfelds an (Abb. B.5.1). Dabei fordert das CDEV von jedem ADEV Informationen an. Dies geschieht, indem der ‘adev’-Code eines jeden ADEV im System Folder zuerst vom CDEV durch einen GetADEV-Call mit folgenden Register-Parametern gerufen wird:

D0                            101, Anhand dieses Wertes wird im ‘adev’-Code zu doGetADEV verzweigt.

D1  (long)                 Aktueller Wert der aktiven AppleTalk-Implementierung im Parameter RAM

D2  (long)                 Die Slotnummer, die vom letzten GetADEV-Call zurück­gelie­fert wurde. Beim ersten Aufruf 0.

Anhand des Wertes im D0-Register wird der GetADEV-Call (D0 = 101) vom SelectADEV-Call (D0 = 102) beim Anspringen des ‘adev’-Codes unterschieden[39]. Kehrt diese Routine aus dem ADEV zurück, so enthalten die Register folgende Werte:

D0  (byte)                 Das Statusflag des ADEV zeigt den Zustand an.

D2  (long)                 Die nächste Slotnummer, mit der GetADEV gerufen werden soll. Dieser Wert wird auch von SelectADEV verwendet.

A0  (pointer)             Ein Zeiger auf den Namen des ADEV, der unter dem Icon im Kontrollfeld angezeigt wird.

In der ‘adev’-Resource des ISDNLAP wird der GetADEV-Call etwas anders als üblich behandelt. Eigentlich soll vom GetADEV-Call die Nummer des NuBus-Slots ermittelt werden, in der die Kommunikationskarte steckt. Da ISDNLAP aber ohne Karte arbeitet, wird, damit es beim Macintosh II zu keinen Konflikten kommt, der Wert $F, der außer­halb des Bereiches der möglichen Slot-Nummern ($9-$E) liegt, zurückgeliefert.

doGetADEV     

         Addq        #1,D2             ; increase slot number

         Cmp.B       #$10,D2           ; If it is the second call

         Beq.S       nomore            ; then let's get out of here

         Move.B      #0,D0             ; Mac Plus needs this result

         Move.B      #$F,D2            ; return our slot ID

         Lea         iconString,A0     ; get pointer to LAP name

         Asr.L       #8,D1             ; decode the selected LAP ID

         Cmp.B       D2,D1             ; if it is not ours

         Bne.S       notours           ; then return

         Move.B      #-1,D0            ; else indicate 'not ours'

notours  Rts                          ; go home

nomore   Move.B      #1,D0             ; indicate 'no more LAPs'

         Rts                          ; go home

Abb. B.5.2: Die doGetADEV-Routine im ‘adev’-Code des ISDNLAP

Da ein ADEV unter Umständen mehrere Karten unterstützen kann, wird das ADEV vom CDEV solange gerufen, bis es über den Status im DO signalisiert, daß keine weitere Karte mehr vorhanden ist. Weiter zeigt der Code des ADEV mit $-1 im D0 Register an, daß seine Slot-Nummer mit der des zur Zeit ausgewählten LAP übereinstimmt (vgl. Abb. B.5.2). Dabei ist zu beachten, daß der Wert in den oberen drei Bytes des Parameter RAM-Wertes steht. Im A0-Register wird ein Zeiger auf den String zurückgeliefert, der unter dem Icon des ADEV im Kontrollfeld erscheinen soll. Dieses Icon wird vom CDEV der Resource Fork des ADEV-Files als ‘ICN#’-Resource entnommen und angezeigt.

Wählt der Benutzer durch Anklicken mit der Maus das gewünschte LAP aus, so wird der ‘adev’-Code des ADEV durch einen SelectADEV-Call über die Auswahl informiert. Dieser SelectADEV-Call wird mit folgenden Register-Parametern durchge­führt:

Beim Aufruf:

D0                            102, Wert für den SelectADEV-Call

D2  (long)                 Der Wert, den das ADEV bei GetADEV geliefert hat.

Bei der Rückkehr:

D1  (high 3 bytes)     Der Wert, der im Parameter RAM zu setzen ist; wird auch durch den AInstall-Aufruf an den ‘atlk’-Code übergeben.

Die doSelectADEV-Routine in ISDNadev.a ruft eine externe Pascal-Prozedur SetNumber, welche die Rufnummer des Servers aus einer Resource holt, sie in einer Dialogbox anzeigt und schließlich die geänderte Nummer wieder in die Resource zurück­schreibt (Abb. B.5.3).

Abb. B.5.3: Eingabe der Nummer des NameServers bei der Auswahl

Pascal-Prozeduren retten nur verwendete Non Scratch-Register. Es kann aber durchaus sein, daß sich auch in Scratch-Registern (außer dem D2) Werte befinden, die nach dem Aufruf noch benötigt werden (Abb. B.5.4). Daher werden vor dem Aufruf von Pascal-Prozeduren aus Assembler die Scratch-Register (A0–A1, D0–D2) gesichert. Im D2-Register befindet sich beim Aufruf die Slot-Nummer des ausgewählten ADEV, die für das Parameter RAM in die oberen 3 Bytes des D1-Registers „geshiftet“ werden muß.

doSelectADEV

         MoveM.L     A0-A1/D0/D2,-(SP) ; save some registers

         Jsr         SETNUMBER               ; call SetNumber

         MoveM.L     (SP)+,A0-A1/D0/D2 ; restore registers

         Asl.L       #8,D2                   ; decode slot ID

         Move.L      D2,D1                   ; and return it in D1

         Rts                                ; go home

Abb. B.5.4: Die doSelectADEV-Routine im ‘adev’-Code des ISDNLAP

Bevor allerdings eine neue AppleTalk-Implementierung mit AInstall und LWrtInsert installiert werden kann, muß das alte LAP vom System Heap entfernt werden. Nachdem über SelectADEV die ‘adev’-Resource ausgewählt wurde, löscht das CDEV den ‘atlk’-Code des alten LAP aus dem System Heap. Da der zur Zeit instal­lierte Code von seinem Resource File gelöst (detached) ist, muß sich das CDEV über einen LWrtGet-Call an dem LAP Manager die Adresse des aktiven ‘atlk’-Codes im System Heap besorgen. Durch einen AShutDown-Call an den aktiven ‘atlk’-Code wird dieser dann veranlaßt, sich mit einem LWrtRemove an den LAP Manager aus dem System Heap und dem ATalkHk2 zu entfernen.[40]

B.5.1.2 Installation über das INIT beim Systemstart

Der LAP Manager enthält eine INIT-Resource (ID 18), die beim Systemstart das ausge­wählte LAP installiert. Welches LAP zuletzt aktiv war, entnimmt das INIT dem Parameter RAM (Abb. B.5.5).

Abb. B.5.5: Wert des ADEV im Parameter-RAM

Der ‘atlk’-Code des ausgewählten LAP wird in den System Heap geladen und dann über einen AInstall-Call installiert. Damit der Code nach dem Beenden des INIT im System Heap verbleibt, wird die ‘atlk’-Resource vom Resource File gelöst. Die Routinen GetADEV und SelectADEV in der ‘adev’-Resource werden allerdings nicht gerufen, d.h. es kann keine Rufnummer angegeben werden.

B.5.2 Einbinden der Sendeprozedur

Nach dem SelectADEV-Call an den ‘adev’-Code wird der ‘atlk’-Code des LAP vom CDEV Netzwerk in den System Heap geladen und mit einem AInstall-Call gerufen. Dieser Code enthält ganz am Anfang einen Dispatcher. Wird die ‘atlk’-Resource gerufen, um ein LAP-Paket zu schreiben, so wird ganz zu Beginn des Codes bei atlkStart einge­sprungen und zur LAPWrite-Routine verzweigt (vgl. Abb. B.5.6).

atlkStart     

   Bra.S       LAPWrite          ; first entry is the LAP write code

   Cmpi.L      #AInstall,D0      ; if it is an install call

   Beq.S       doAInstall  ; do the installation

   Cmpi.L      #AShutDown,D0     ; if is a shutdown call

   Beq.S       doAShutDown ; then shut down

   Moveq       #-1,D0            ; If it is an unknown parameter

   Rts                          ; go home

   DC.B        'ATLKSTAR'        ; debugger name

Abb. B.5.6: Einsprungstelle und Dispatcher im ‘atlk’-Code des ISDNLAP

Handelt es sich jedoch um einen Kontrollaufruf mit AInstall oder AShutDown, so wird der Dispatcher an der Position atlkStart+2 gerufen. Anhand des D0-Registers wird dann verzweigt. Enthält das D0-Register keinen zulässigen Wert, so kehrt die Routine mit einem negativen Wert als Fehlermeldung zum CDEV zurück. Bei der Implementierung der doAInstall-Routine ist folgende Registerbelegung einzuhalten:

Beim Aufruf:

D1  (long)                 Wert im Parameter RAM, wie er von SelectADEV gesetzt wurde.

D4  (Byte)                 Nummer des Ports, auf welchen sich diese Installation bezieht. Diese ist aber nur von Interesse, wenn mehrere Verbin­dungen gleichzeitig unterstützt werden.

Bei der Rückkehr:

D0 (long)                  enthält den Fehlercode; 0 bedeutet kein Fehler, ein negativer Wert signalisiert einen Fehler.

D1 (high 3 bytes)      Neuer Wert für das Parameter RAM; bleibt unverändert.

Wird nun der ‘atlk’-Code zum Aufruf der AInstall-Routine angesprungen, so verzweigt der Dispatcher zu doAInstall. Dieser sichert zuerst den Wert des Parameter RAM im D1 und reserviert auf dem Stack Platz für das Funktionsergebnis. Danach wird die externe Pascal-Funktion InitLAP gerufen (vgl. Abb. B.5.7).

InitLAP initiali­siert die globalen Variablen des ISDNLAP sowie das History, installiert die Tasks Terminator und TimeoutTask in der VBLQueue bzw. der Time Manager Queue und prüft, ob die Station eine gültige Node ID besitzt.

doAInstall

         Move.L      D1,-(SP)          ; save value of parameter RAM

         Clr.L       -(A7)             ; function result

         Jsr        InitLAP           ; call our LAP init code

         Move.B      (A7)+,D0          ; get result

         Beq.S       error                       

         Move        #LWrtInsert,D0    ; if done call LAP Manager

error    Lea         atlkStart,A0      ; start of our LAP in A0

         Move.B      #PortBInt, D1     ; port B interrupts enabled

         Move        #1,D2             ; one try for a node ID

         Move.L      LAPMgrPtr,A1      ; call the LAP Manager

         Jsr         LAPMgrCall(A1)    ; returns result in D0

         Move.L      (SP)+,D1          ; restore value of Para RAM

         Rts                          ; go home

         DC.B        'DOINSTAL'        ; debugger name

Abb. B.5.7: Die doAInstall-Routine im ‘atlk’-Code des ISDNLAP

Nach der Rückkehr aus InitLAP wird das Funktionsergebnis ausgewertet. Ist bei der Initialisierung ein Fehler aufgetreten, so enthält das D0-Register den Wert 0 (FALSE) und der LAP Manager wird mit diesem Wert gerufen. Dieser liefert dann seinerseits über das D0 einen negativen Wert zurück, da kein LAP Manager-Befehl mit Code 0 vorhanden ist (vgl. Abb. B.5.7). Kehrt die AInstall-Routine mit einem negativem Wert im D0 zum CDEV zurück, so meldet dieses dem Benutzer über einen Stopalert einen Fehler.

Abb. B.5.8: Aufrufe bei der Installation des LAPWrite-Codes

Hat die Funktion InitLAP keinen Fehler entdeckt, so ruft doAInstall zur Installa­tion des LAPWrite-Codes den LAP Manager mit LWrtInsert an der Stelle ATalkHk2+2 (vgl. Abb. B.5.8). Im A0-Register wird dabei die Anfangsadresse atlkStart des ’atlk’-Codes übergeben, an der ein Sprung zum eigentlichen LAPWrite-Code steht. Im einzelnen müssen folgende Parameter in den Registern übergeben werden:

Beim Aufruf:

A0  (pointer)             Hier wird ein Zeiger auf den LAPWrite-Code übergeben.

D1  (byte)                 Das D1 enthält Flags. Für das ISDNLAP wird dem LAP Manager über diese Flags mitgeteilt, daß er die Port B-Interrupts zulassen soll.

D2  (word)                Dieses Register enthält die maximale Anzahl der Versuche, die der LAP Manager unternehmen soll um eine gültige Node ID zu erhalten.

D4  (Byte)                 Nummer des Ports, auf welchen sich diese Installation bezieht. Diese ist nur von Interesse, wenn mehrere Verbin­dungen gleichzeitig unterstützt werden (Normalerweise 0).

Bei der Rückkehr:

D0 (long)                  enthält den Fehlercode; 0 bedeutet kein Fehler, ein negativer Wert signalisiert einen Fehler.

Kehrt der LAP Manager-Aufruf zurück, so enthält er im D0-Register die Fehlermeldung oder 0, falls der Aufruf erfolgreich war. Bevor AInstall mit einem RTS zum CDEV zurückkehrt, wird der gesicherte Wert des Parameter RAM in das D1-Register zurückge­schrieben.

Die eigentliche Senderoutine wurde in Pascal implementiert. Da die Parameter jedoch in Registern übergeben werden, ist es ratsam, eine kurze Glueroutine in Assembler zu verwenden. Dieser LAPWrite-Prozedur werden folgende Registerinhalte übergeben:

Beim Aufruf:

A0  (pointer)             Hier wird ein Zeiger auf den Code übergeben, zu dem die LAPWrite-Prozedur nach dem erfolgreichen Senden eines Paketes zurückspringen soll.

A1  (pointer)             Wird ein ENQ-Paket gesendet, so enthält das A1 das PortBUse-Byte, andernfalls einen Zeiger auf die Write Data Structure (WDS), in der das zusendende Paket enthalten ist.

A2  (pointer)             Das A2-Register zeigt auf die lokalen Variablen des .MPP-Treibers (ABusVars).

D0  (byte)                 Das D0-Register enthält einen Wert ungleich 0, wenn ENQ-Pakete zu senden sind; ansonsten ist der Wert 0.

D1  (pointer)            Im D1-Register steht ein Zeiger auf einen Code, der das Paket überträgt, falls das nicht durch das installierte LAP geschehen soll.

D2  (byte)                 Im niederwertigsten Byte des D2-Registers wird die Ziel­adresse des LAP-Paketes übergeben.

D4  (byte)                 Im D4-Register ist beim Senden von ENQ-Paketen ein Zähler enthalten, der anzeigt wieviele ENQ-Pakete noch zu senden sind; das ist allerdings nicht dokumentiert.

Bei der Rückkehr:

D0 (long)                  enthält den Fehlercode; 0 bedeutet kein Fehler, ein negativer Wert signalisiert einen Fehler[41].

Wird der LAPWrite-Code durch den Einsprung am Anfang des ‘atlk’-Codes gerufen, so sichert er zunächst einige Register. Damit die Maus auch bei mißlungenen Wählver­suchen beweglich bleibt, wenn das ISDNLAP von einer VBL-Task gerufen wird, wird das Taskflag im Header der VBLQueue zurückgesetzt[42]. Anschließend legt er in der globalen Variablen MPPVars einen Zeiger auf die lokalen Variablen des .MPP ab, der im A2-Register enthalten ist. Steht ein ENQ-Paket zur Übertragung an (D0 <> 0), so wird durch InitSCCStuff mit dem Parameterwert TRUE der SCC für den Verbin­dungs­aufbau initialisiert[43] und die globale Variable Guard auf FALSE gesetzt. Der InitSCCStuff-Aufruf darf erst dann geschehen, wenn die globale Variable MPPVars einen gültigen Wert besitzt, d.h. wenn der .MPP-Treiber geöffnet wurde. Andernfalls könnten Pakete empfangen werden, ohne daß AppleTalk initialisiert ist. Das D4-Register wird Null gesetzt, so daß keine weiteren ENQ-Pakete mehr gesendet werden[44]. Nach Wiederher­stellung der Register kehrt LAPWrite zum Anfrufenden zurück.

LAPWrite

         MoveM.L     D1-D2/A0-A1/A3,-(A7)    ; save registers,not D4

         Bclr        #$06, VBLQueue          ; reset VBLTask flag

         Lea         Globals(PC),A3          ; get global variables

         Move.L      A2,(A3)                 ; pointer to MPPVars

         Tst.B       D0                      ; is it an ENQ packet

         Bne.S       Enqs                    ; then jump to the enq

         Tst.B       Guard(A3)               ; if we are still

         Bne.S       task                    ; sending then task

         Clr.W       -(A7)                   ; function result

         Move.L      A1,-(A7)                ; push pointer to WDS

         ST          Guard(A3)               ; lock out async tasks

         Jsr         TransmitPacket          ; perform the Xmission

         SF          Guard(A3)               ; allow calls to LAP

         Tst.B       (A7)+                   ; get result

         Beq.S       itsOk                   ; if Xok then exit

task     Move.L      #CTSErr,D0              ; CTSErr to LAP Manager

         Bra.S       Exit

itsOk    Move.L      #NoErr,D0               ; noErr to LAP Manager

         Bra.S       Exit                        

Enqs     SF          Guard(A3)               ; reset guard

         Move.B      #TRUE,-(A7)       ; check connection

         Jsr        InitSCCStuff            ; call SCCInitStuff

         Move.L      #Zero,D4                ; kill ENQ loop of MPP

Exit     MoveM.L     (A7)+,D1-D2/A0-A1/A3    ; restore registers

         Jmp         (A0)                    ; return to sender

         DC.B        'WRITE   '              ; debugger name

Abb. B.5.9: Die LAPWrite-Glueroutine im ‘atlk’-Code des ISDNLAP

Handelt es sich um ein normales Datenpaket (D0 = 0), so muß anhand der globalen Variablen Guard geprüft werden, ob nicht bereits ein Paket gesendet wird. Das ist notwendig, da andernfalls der Verbindungsaufbau das ISDNLAP durch eine asynchrone Task, die erneut ein Paket sendet (z.B. im Chooser), unterbrochen werden könnte. Das hat jedoch unter Umständen negative Auswirkungen auf die Verbin­dungs­steuerung, da eine Unterbrechung den Status einer Verbindung verändern kann. Ein Paket wird daher nur gesendet, wenn Guard zurückgesetzt ist. Nachdem auf dem Stack Platz für das Funktionsergebnis der Pascal-Funktion TransmitPacket reserviert und ein Zeiger auf die WDS als Parameter auf den Stack geladen wurde (vgl. Abb. B.5.9), setzt LAPWrite das Flag Guard. Hat TransmitPacket das Paket übertragen, so wird das Flag wieder zurückgesetzt. Ist das Funktions­ergebnis ungleich 0, so ist ein Fehler aufgetreten, der dem aufrufenden Klienten als ‘CTS Missing’-Fehler gemeldet wird.

B.5.3 Einbinden der Empfangsprozeduren

B.5.3.1 Aufruf des LAP Managers und Verzweigen zum Protocol Handler

Nachdem von der Interruptprozedur ReceiveHeader der Kopf eines Paketes in die Read Header Area eingelesen wurde, kann anhand des LAP-Typs entschieden werden, welcher Protocol Handler (PH) das weitere Einlesen übernimmt. Um den LAP Manager über ein ankommendes Paket zu informieren und ihn zu veranlassen, nach dem passenden PH zu suchen, wird ein LRdDispatch-Call an den LAP Manager gerichtet. Der Aufruf von LRdDispatch wird nicht direkt von ReceiveHeader vorgenommen, sondern geschieht über einen Assembler-Glue ReadDispatch, der folgendermaßen gerufen wird:

PROCEDURE ReadDispatch(RHA: Ptr; Rest: LONGINT; lapType: BYTE;                                            MPPVars: ABusVarsPtr);

Dafür gibt es mehrere Gründe: Zum einen handelt es sich um eine Registerschnittstelle, die von Pascal aus nur mit Mühe zu bedienen ist. Zum anderen kehrt der LRdDispatch-Call nur dann zur aufrufenden Prozedur ReadDispatch zurück, wenn für das Paket kein passender Protocol Handler gefunden wurde. Ansonsten kehrt der vom LAP Manager gerufene PH direkt zur Prozedur ReceiveHeader zurück, obwohl der LAP Manager nicht von dieser direkt, sondern über die Prozedur ReadDispatch mit LRdDispatch gerufen wurde[45]. Ein weiterer Grund, warum diese Prozedur in Assembler geschrieben wurde ist, daß das Aufrufen des Protocol Handlers zeitkritisch ist[46].

Beim Aufruf von LRdDispatch (D0=1) werden die vom LAP Manager benötigten Parameter in Registern übergeben, die wie folgt belegt sind:

A0  (pointer)             Adresse eines Hardwareregisters; hier handelt es sich um SCCWrite; wird hier nicht benutzt.

A1  (pointer)             Adresse eines Hardwareregisters; hier handelt es sich um SCCRead; wird hier nicht benutzt.

A2  (pointer)             Ein Zeiger auf die lokalen .MPP-Variablen ABusVars.

A3  (pointer)             Ein Zeiger, der auf das nächste freie Byte in der Read Header Area (.MPP RHA) zeigt; das 6te Byte der RHA, da der Header 5 Bytes lang ist.

A4  (pointer)             Ein Zeiger auf die ReadPacket-Prozedur, die vom Protocol Handler gerufen wird, um weitere Teile des Paketes einzu­lesen.

A5                             Wird gesichert und vom PH nach der Rückkehr aus ReadRest wiederhergestellt.

D0 (word)                 Enthält den Code, anhand dessen der LAP Manager erkennt, daß es sich um einen LRdDispatch-Aufruf handelt.

D1  (long)                 Enthält die Anzahl der Bytes des Paketes, die noch zu lesen sind. Entspricht dem Inhalt des Längenfeldes im Paket abzüg­lich der 2 Bytes für die Länge, die ja schon gelesen wurden! Die 2 Bytes für die CRC und die 3 Bytes des LAP-Headers werden dabei nicht mitgerechnet[47]!

D2  (byte)                 Das D2-Register enthält den LAP-Typ des Paketes, dessen Header gelesen wurde. Anhand dieses Typs wird ein Protocol Handler bestimmt, der das weitere Einlesen übernimmt.

Wird die Prozedur ReadDispatch von ReceiveHeader gerufen, so befinden sich die Parameter des Aufrufs auf dem Stack. Die Prozedur ReadDispatch holt diese vom Stack und legt sie in Registern ab. Anschließend wird für eine eventuelle Rückkehr zu ReadDispatch die Rückkehradresse auf den Stack gelegt (siehe Abb. B.5.10). Nun kann der LAP Manager gerufen werden.

ReadDispatch   PROC  EXPORT

   RHA         EQU   14                ; parameters on stack

   Rest        EQU   10

   LAPType     EQU   8

   MPPVars     EQU   4

        

   Lea         Read(PC),A4       ; load address of read

   Move.L      RHA(A7),A3              ; get Ptr to RHA

   Move.L      MPPVars(A7),A2          ; get Ptr to MPPVars

   Move        LAPType(A7),D2          ; get LAPType

   Move.L      Rest(A7),D1       ; get length of stuff to read

   Move.L      (A7),RHA(A7)            ; put return address at                                         position of RHA for return

   Add         #14,A7                  ; get rid of params

   Moveq       #LRdDispatch,D0         ; code for LRdDispatch

   Move.L      LAPMgrPtr,A1            ; code of ATlkHook2

   Jsr         LAPMgrCall(A1)          ; call the LAP Manager

   Jsr         PurgeRest               ; if there is no PH, purge                                      rest of packet

   Rts                                ; go home

   DC.B        'READDISP'              ; debugger name

   ENDP

Abb. B.5.10: Die ReadDispatch-Glueroutine im ‘atlk’-Code des ISDNLAP

Kehrt der LAP Manager zu ReadDispatch zurück, so wurde kein passender Protocol Handler gefunden und das Paket muß verworfen werden. Das geschieht über den Aufruf der Pascal-Prozedur PurgeRest, die den Rest eines Paketes verwirft. Diesen etwas ungewöhnlichen Aufrufmechanismus soll die Abbildung B.5.11 nochmals verdeutlichen.

Abb. B.5.11: Call-Mechanismus zwischen ISDNLAP und LAP Manager

B.5.3.2 Aufruf von ReadPacket und ReadRest durch Protocol Handler

Der LAP Manager ruft bei einem LRdDispatch-Call, falls vorhanden, den zum LAP-Typ des Paketes registrierten Protocol Handler. Dieser übernimmt nun mit Hilfe der Prozeduren ReadPacket und ReadRest das weitere Einlesen.­ Beim Aufruf von LRdDispatch durch ReadDispatch wurde die Adresse einer Sprungtabelle überge­ben. Der PH findet so an der Adresse Read einen Sprung zu ReadPacket und an der Stelle Read+2 die Prozedur ReadRest.

Read           PROC EXPORT      ; this is my Read Dispatch Table

   Bra.S       ReadPacket  ; call the ReadPacket code

   Bra.S       ReadRest          ; ReadRest must start two byte                                  after ReadPacket

Abb. B.5.12: Der Read Dispatcher  im ‘atlk’-Code des ISDNLAP

Zwischen dem Lesen des 5ten Zeichens des Paketes durch ReceiveHeader, dem Aufruf des LAP Managers über ReadDispatch und dem Lesen des nächsten Zeichens durch ReadPacket dürfen nicht mehr als 360 µs verstreichen[48]. Andern­falls tritt ein Overrun des FIFO-Puffers im SCC auf. Das eigentliche Einlesen der Pakete übernehmen nicht die Prozeduren ReadPacket und ReadRest, sondern die Pascal-Prozedur ReceiveFrame. Da die Schnittstelle aber wieder „registerbased“ ist und ReadRest über einen Offset erreicht wird, ist eine Glue­routine in Assembler zwischen­ge­schaltet. Ein Protocol Handler ruft die ReadPacket-Prozedur mit folgenden Register-Parametern[49]:

Beim Aufruf:

A3  (pointer)             Hier steht ein Zeiger auf einen Puffer, in den die gewünschte Anzahl von Bytes gelesen werden soll.

D1  (word)                Im D1-Register steht die Anzahl der Bytes, die insgesamt noch im Paket enthalten sind[50].

D3  (word)                Das D3-Register enthält die Anzahl der Bytes, die von ReadPacket zu lesen ist; der Wert darf nicht 0 sein.

Bei der Rückkehr:

D0  (byte)                 enthält den Fehlercode; 0 bedeutet kein Fehler, ein negativer Wert signalisiert einen Fehler[51].

D1  (word)                Das D1-Register enthält die Anzahl der Bytes, die nach ReadPacket noch im Paket verbleiben.

D2                            Der Inhalt muß von ReadPacket gesichert werden.

D3  (word)                Null falls die gewünschte Anzahl von Bytes gelesen wurde. Ist in ReadPacket ein Fehler aufgetreten, so wird ein Wert ungleich Null zurückgeben.

A0-A2                       müssen gesichert werden.

A3  (pointer)             Hier wird ein Zeiger übergeben, der auf ein Byte hinter das zuletzt gelesene Zeichen im Puffer zeigt.

ReadPacket rettet als erstes einige Register und reserviert dann auf dem Stack Platz für einen VAR-Parameter und das Funktionsergebnis von ReceiveFrame. Die Parameter des Aufrufs werden auf den Stack geladen. Zusätzlich übergibt ReadPacket die Adresse der lokalen Variablen und den Parameterwert NoDiscard (Abb. B.5.13). Das bewirkt, daß exakt die Anzahl der gewünschten Bytes gelesen und dann zum Aufrufen­den zurück­gekehrt wird.

ReadPacket

         MoveM.L     D1-D2/A0-A2,-(A7) ; save some registers

         Subq        #4,A7             ; space for BytesGot of                                         ReceivePacket & result

         Move.L      A3,-(A7)          ; push pointer to buffer

         Move        D3,-(A7)          ; push # of bytes to read

         Pea         8(A7)             ; address of local variable

         Move.W      #NoDiscard,-(A7)  ; read exactly the count of                                     bytes and not more

         Jsr         ReceiveFrame      ; read requested # of bytes

         Move.B      (A7)+,D0          ; get result of function

         Move        (A7)+,D2          ; get BytesGot

         Move.L      (A7)+,D1          ; D1 must not be modified

         Sub         D2,D3             ; calculate bytes still there

         Sub         D2,D1             ; rest of bytes - BytesGot

         Add         D2,A3             ; pointer behind last byte

         MoveM.L     (A7)+,D2/A0-A2    ; restore registers

         Sub.B       #1,D0             ; is it a continued frame?

         Beq.S       yes               ; Yes => jump (Z-flag set)

         Move.L      #-1,D3            ; call failed (Z-flag clear)

yes Rts                          ; go home

         DC.B        'READPACK'        ; debugger name

Abb. B.5.13: Die ReadPacket-Glueroutine  im ‘atlk’-Code des ISDNLAP

Zurückgeliefert werden von ReceiveFrame die Anzahl der gelesenen Zeichen und als Funktionsergebnis die Fehlermeldung. Anschließend werden die empfangenen Zeichen von den zu lesenden (D3) und noch zu lesenden Zeichen (D1) subtrahiert. Unbedingt beachten sollte man, daß, falls ReadPacket Erfolg hatte, das D0-Register bei der Rückkehr aus ReadPacket 0 (no error) enthalten muß. Andernfalls ist das D0 ungleich Null. Zusätzlich muß bei Erfolg das Zero-Flag gesetzt und sonst zurückgesetzt werden.

Nachdem so ein Paket Stück für Stück eingelesen wurde, muß der Protocol Handler als letztes die Prozedur ReadRest rufen. Wie der Name bereits suggeriert, liest diese Prozedur den Rest eines Paketes ein. Dabei werden mindestens zwei Zeichen, d.h. die zwei Byte lange Prüfsumme gelesen. Beim Aufruf durch den Protocol Handler und bei der Rückkehr zu diesem befinden sich in den Registern folgende Werte:

Beim Aufruf:

A3  (pointer)             Hier steht ein Zeiger auf einen Puffer, in den die gewünschte Anzahl von Bytes gelesen werden soll.

D1  (word)                Im D1-Register steht die Anzahl der Bytes, die insgesamt noch im Paket enthalten sind[52].

D3  (word)                Das D3-Register enthält die Größe des Puffers, der den Rest des Paketes aufnimmt; kann 0 sein.

Bei der Rückkehr:

D0 (long)                  enthält den Fehlercode; 0 bedeutet kein Fehler, ein negativer Wert signalisiert einen Fehler[53].

D1                            verändert (modified).

D2                            Der Inhalt muß von ReadRest gesichert werden.

D3  (word)                Null, falls genau die gewünschte Anzahl von Bytes gelesen wurde. Negativ, wenn das Paket -D3 Zeichen zu lang für den Puffer war und abgeschnitten werden mußte. Positiv, wenn der Puffer D3 Zeichen zu groß für das Paket war.

A0-A2                       müssen gesichert werden.

A3  (pointer)             Hier wird ein Zeiger übergeben, der 1 Byte hinter das letzte gelesene Zeichen im Puffer zeigt.

Ein Paket wird unter allen Umständen vollständig eingelesen. Schließlich ist diese Proze­dur bei End of Frame (EOF) für das Überprüfen der CRC-Prüfsumme verant­wortlich. Die Parameterübergabe findet wie bei ReadPacket statt. Der einzige Unter­schied besteht darin, daß ReceiveFrame mit Discard gerufen wird, wodurch überschüs­sige Bytes verworfen werden (Abb. B.5.14).

ReadRest

         MoveM.L     D2/A0-A2,-(A7)    ; save some registers

         Subq        #4,A7             ; space for BytesGot of                                         ReceivePacket & result

         Move.L      A3,-(A7)          ; push pointer to buffer

         Move        D3,-(A7)          ; push # of bytes to read

         Cmp         D3,D1             ; buffer size < rest of bytes

         Bge.S       more              ; read buffer size

         Move        D1,(A7)           ; if not read remaining byte

more     Pea         8(A7)             ; addr of var for BytesGot

         Move        #Discard,-(A7)    ; discard the rest of bytes

         Jsr         ReceiveFrame      ; read in requested bytes

         Move.B      (A7)+,D0          ; get result of function,                                     ; must contain result for PH       Move        (A7)+,D2          ; get bytesGot

         Subq        #2,D2             ; length without CRC bytes

         Cmp.W       D2, D3            ; # bytes read > buffer size

         Ble.S       toomuch           ; then add buffer size

         Add.W       D3,A3             ; else

         Bra.S       goon              ; add bytes read

toomuch  Add.W       D2,A3             ; pointer behind last byte

goon     Sub.W       D2,D3             ; bufferSize-bytesGot

         MoveM.L     (A7)+,D2/A0-A2    ; restore register

         Tst.B       D0                ; is it a LAP data frame?

                                       ; (set zero flag)

         Rts                          ; go home

         DC.B        'READREST' ; debugger name

         ENDP

Abb. B.5.14: Die ReadRest-Glueroutine im ‘atlk’-Code des ISDNLAP

Zurückgeliefert werden von ReadRest die Anzahl der gelesenen Zeichen und als Funktionsergebnis die Fehlermeldung. Die empfangenen Zeichen, ohne die zwei CRC-Bytes, müssen von den zu lesenden (D3) und noch zu lesenden Zeichen (D1) subtrahiert werden. Unbedingt beachten sollte man wieder, daß das D0-Register bei der Rückkehr aus ReadPacket die Fehlermeldung enthalten muß. Zusätzlich muß das Zero-Flag entsprechend gesetzt werden.

Schließlich kehrt die ReadRest-Prozedur wieder zum Protocol Handler zurück. Dieser wiederum kehrt nicht zum LAP Manager oder zu ReadDispatch, sondern direkt zu ReceiveHeader zurück. Dort wird der Interrupt beendet und das unterbrochene Programm fortgesetzt (siehe Abb. B.5.11).

B.5.4 Deinstallation des ISDNLAP

Wählt der Benutzer im Kontrollfeld ein anderes LAP als das ISDNLAP aus, so muß der Code des ISDNLAP deinstalliert und der belegte Speicher ordnungsgemäß zurückge­geben werden. Bevor der LAP Manager den ‘atlk’-Code aus den System Heap entfernt, wird deshalb vom CDEV Netzwerk die Routine doAShutDown in der ‘atlk’-Resource des ISDNLAP gerufen, die für die Deinstallation verantwortlich ist[54].

doAShutDown                                 ; quit ISDN AppleTalk

         MoveM.L     A0/D1-D2,-(SP)          ; save some registers

         Clr.W       -(A7)                   ; function result

         Jsr         RestoreOldLAP           ; restore old LAP code       Move.B   (A7)+,D0                ; get the result

         Beq.S       notOk                   ; jump if error

         Move        #LWrtRemove,D0          ; set up an LWrtRemove notOk MoveM.L   (SP)+,A0/D1-D2          ; restore registers

         Move.L      LAPMgrPtr,A1            ; call the LAP Manager

         Jsr         LAPMgrCall(A1)          ; returns D0

         Rts                                ; go home

         DC.B        'DOSHUTDO'

Abb. B.5.15: Die doAShutDown-Routine  im ‘atlk’-Code des ISDNLAP

Nachdem doAShutDown einige Register gerettet hat, ruft es die Pascal-Funktion RestoreOldLAP, die für die Deinstallation des ISDNLAP verantwortlich ist (Abb. B.5.15). Wurde diese Prozedur mit FALSE beendet, so wird der LAP Manager mit dem Wert 0 im D0-Register gerufen, worauf dieser eine Fehlermeldung im D0 liefert. Hatte die Deinstallation Erfolg, so wird der LAP Manager mit dem Code für LWrtRemove im D0-Register zum Entfernen des LAP-Codes veranlaßt.

RestoreOldLAP bricht zuerst eine eventuell bestehende Verbindung ab. Durch den Aufruf der Prozedur RestoreOldInterruptService wird dann der SCC deaktiviert und die Interruptserviceroutinen, die sich vor der Auswahl des ISDNLAP in der Interrupttabelle befanden, werden wieder installiert. Dabei ist zu beachten, daß vor einer Änderung an der Interrupttabelle die Interrupts des SCC ausgeschlossen werden müssen. Anschließend wird von RestoreOldLAP die VBL-Task Terminator aus der VBLQueue entfernt.

Abb. B.5.16: Das PortBUse-Byte

Schließlich muß in der globalen Betriebssystemvariablen PortUsePtr (Abb. B.5.16) der Wert $FF gesetzt werden[55]. Dadurch wird signalisiert, daß der .MPP-Treiber geschlos­sen wurde und bei einer erneuten Verwendung von AppleTalk zuerst geöffnet werden muß, damit der SCC wieder für Standard-AppleTalk initialisiert ist.



[1]      »Wohin muß der Code geladen werden?« und »Wie groß wird das Programm?«

[2]      Ein Pascal- und C-Compiler, sowie ein Macro-Assembler werden mitgeliefert.

[3]      Dabei handelt es sich zumeist um sog. DEV-Files.

[4]      Ein Befehl greift wortweise auf eine ungerade Adresse zu.

[5]      Treten auf, wenn falsche Rückkehradressen auf dem Stack liegen.

[6]      unzulässige Speicherreferenz.

[7]      »Poor Man‘s Debugging«

[8]      Schulthess, Froitzheim (Integration des Zugangs zu abgesetzten Druckern über PBX-Systeme)

[9]      Mit MacsBug wird mit „F 00000000  40000000 '[ISDNInit]' “ nach der Startmarke gesucht und das History mit „DM“ angezeigt.

[10]     Denny, Coad (Programmers Guide to Networking), S. 195.

[11]     Apple Computer (Macintosh Programmers Workshop)

[12]     Zudem muß auch das Handle wieder irgendwo aufbewahrt werden.

[13]     Siehe hierzu Apple Computer (Inside Macintosh III), S. 211ff, „Routines that may move or purge memory“.

[14]     Zum Zeitpunkt der Implementierung stand die Version 6.0.3 des Systems und die Version 6.1 des Finders zur Verfügung. Die aktuelle Version des AppleTalk-Treibers .MPP ist 52, der .XPP-Treiber liegt in der Version 1.2 vor. AppleShare wurde in der Version 2.01 verwendet. Gedruckt wurde mit einem Druckertreiber der Version 5.0.

[15]     Siehe Abschnitt B.5.

[16]     Siehe Abschnitt B.1.3.

[17]     „Hard coded strings“ sollte man in Macintosh-Applikationen eigentlich nicht verwenden!

[18]     Die detailierte Struktur der Datenobjekte kann dem Listing in Anhang F entnommen werden.

[19]     Siehe hierzu Apple Computer (Inside Macintosh II), S. 349ff.

[20]     Die Prozessoren 68020/30 unterscheiden Schreib- und Lesezugriffe durch ein Prozessorpin.

[21]     Apple Computer (Inside Macintosh II), S. 196.

[22]     Zilog (SCC Handbuch)

[23]     Vgl. Zilog (SCC Handbuch).

[24]     Zeichen angekommen, Ende des Frames etc.

[25]     Vgl. hierzu auch Anhang D.

[26]     Es kann bis zu 20 Sekunden dauern, bis eine Verbindung geschaltet ist.

[27]     Eine unterbrochene Verbindung kann normalerweise anhand des Indicate-Signals erkannt werden.

[28]     Der Takt wird von der Schnittstelle geliefert. Daher kann es vorkommen, daß bei Ausbleiben des Taktes der Transmit-Puffer nicht leer wird.

[29]     Vgl. Abschnitt 7.2.1.

[30]     Der SCC arbeitet im Address Search Mode.

[31]     Vergleiche Paketformat des ALAP aus Abschnitt 4.2.4.1.

[32]     Grundsätzlich ist der Bereich $81–$254 für die Pakettypen der LAP-Kontrollpakete reserviert. In Absprache mit Apple können ungenutzte Typen für eigene Zwecke verwendet werden.

[33]     Fehlt der externe Takt, so wird z.B. das Transmit-Puffer niemals leer.

[34]     z.B. durch Leitungsunterbrechung.

[35]     Das Senden einer Abort-Sequenz, um die Gegenstation in den SYNC\HUNT-Mode zu bringen.

[36]     Siehe auch Abschnitt B.5.3.1.

[37]     Bei EOF steht das letzte Zeichen noch im FIFO!

[38]     Vergleiche hierzu auch Abschnitt 6.2.3.

[39]     Siehe Listing des 'adev'-Codes in Anhang F.

[40]     Siehe Abbildung 6.2.5.

[41]     Das ist nicht dokumentiert, aber für die Implementierung von größter Bedeutung.

[42]     Bei der Auswahl eines Servers im Chooser war zu beobachten, daß die Maus bei gescheiterten Wählver­suchen für die Dauer der Timeouts unbeweglich blieb. Da das LAP im Abstand von zwei Sekunden von einer VBL-Task gerufen wird, war das sehr störend. Diese Schwierig­keiten können vermieden werden, indem bei Aufruf von LAPWrite ein Flag im Header der VBLQueue zurück­gesetzt wird. Man sollte sich jedoch darüber im klaren sein, daß unsaubere Programmierung hierbei zu Kompli­ka­tionen führen kann.

[43]     Auch wenn der LAPWrite-Code ersetzt wurde, manipuliert das AppleTalk vor dem Senden von ENQ-Paketen am Port B des SCC. Eine vor dem Senden von ENQ-Paketen aufgebaute Verbindung wird dadurch eventuell unterbrochen. Vgl. Froitzheim, Lupper (PluriLAP).

[44]     Normalerweise werden bis zu 32 ENQ-Pakete gesendet.

[45]     Zur besseren Verdeutlichung betrachte man die Abbildung B.5.11.

[46]     Zwischen dem Lesen des 5ten und 6ten Zeichens dürfen nicht mehr als ca. 360 ms vergehen.

[47]     Das herauszufinden kostete viel Zeit und Nerven.

[48]     Vergleiche hierzu Apple Computer (Inside Macintosh II), S. 329.

[49]     Vgl. Apple Computer (Inside Macintosh II), S. 326–327.

[50]     Das ist zwar nicht dokumentiert, wird aber sowohl vom ursprünglichen ALAP-Code als auch vom Async AppleTalk-Code verwendet.

[51]     Das ist nicht dokumentiert, aber für die Implementierung von größter Bedeutung. Die Dokumentation spricht nur von »modified«.

[52]     Das ist zwar nicht dokumentiert, wird aber sowohl vom ursprünglichen ALAP-Code als auch vom Async AppleTalk-Code verwendet.

[53]     Das ist nicht dokumentiert, aber für die Implementierung von größter Bedeutung. Die Dokumentation spricht nur von »modified«.

[54]     Siehe auch Abschnitt 6.2.3.2.

[55]     Siehe Apple Computer (Inside Macintosh), S. 305.