Dittrich Gelfand Schemmel Amiga Intern DATA BECKER 3. Auflage 1988 ISBN 3^9011-104-1 Copyright e 1987 DATA BECKER GmbH Merowingerstr. 30 4000 Düsseldorf Text verarbeitet mit Word 4.0, Microsoft Ausgedruckt mit Hewlett Packard LaserJet II Druck und Verarbeitung Mohndruck, Gütersloh Alle Rechte Vorbehalten. Kein Teil dieses Buches darf in irgendeiner Form (Druck, Fotokopie oder einem anderen Verfahren) ohne schriftliche Genehmigung der DATA BECKER GmbH reproduziert oder unter Verwendung elektronischer Systeme verarbeitet, vervielfältigt oder verbreitet werden. Wichtiger Hinweis; Die in diesem Buch wiedergegebenen Schaltungen, Verfahren und Programme werden ohne Rücksicht auf die Patentiage mitgeteiit. Sie sind ausschließlich für Amateur- und Lehrzwecke bestimmt und dürfen nicht gewerblich genutzt werden. Alle Schaltungen, technischen Angaben und Programme in diesem Buch wurden von dem Autoren mit größter Sorgfalt erarbeitet bzw. zusammengestellt und unter Einschaltung wirksamer Kontrollmaßnahmen reproduziert. Trotzdem sind Fehler nicht ganz auszuschließen. DATA BECKER sieht sich deshalb gezwun¬ gen, darauf hinzuweisen, daß weder eine Garantie noch die juristische Verant¬ wortung oder irgendeine Haftung für Folgen, die auf fehlerhafte Angaben zurück¬ gehen, übernommen werden kann. Für die Mitteilung eventueller Fehler ist der Autor jederzeit dankbar. Inhaltsverzeichnis 1. Die Hardware des Amiga . 11 1.1 Einleitung . 11 1.2 Die Komponenten des Amiga-Systems . 11 1.2.1 Der 68000er-Prozessor. 13 1.2.2 Das CIA 8520 . 20 1.2.3 Die Custom-Chips und Ihre Einbindung in die Amiga-Hardware . 34 1.2.3.1 Der Grundaufbau des Amiga . 36 1.2.3.2 Der Aufbau von Agnus . 40 1.2.3.3 Der Aufbau von Denise . 45 1.2.3.4 Der Aufbau von Paula . 49 1.2.3.5 Besonderheiten des A500 . 53 1.2.3.6 Besonderheiten des A2000 . 55 1.3 Die Schnittstellen des Amiga . 59 1.3.1 Die Audio-/Video-Schnittstellen . 60 1.3.2 Die RGB-Buchse . 63 1.3.2.1 Der A2000-Genlock-Slot .:. 65 1.3.3 Die Centronics-Schnittstelle . 68 1.3.4 Die serielle Schnittstelle . 70 1.3.5 Die Anschlußbuchse für externe Diskettenlaufwerke . 73 1.3.6 Die Game-Ports . 80 1.3.7 Der Expansion-Port und die A2000-Slots . 83 1.3.8 Stromversorgung über die Schnittstellen . 91 1.4 Die Tastatur . 93 1.4.1 Die Schaltung der Tastaturplatine . 95 1.4.2 Die Datenübertragung . 97 1.4.3 Schwächen der Tastatur... 100 1.5 Die Programmierung der Hardware . 102 1.5.1 Die Speicherbelegung . 103 1.5.2 Grundlagen . 113 1.5.3 Interrupts . 129 1.5.4 Der Coprozessor Copper. 131 1.5.5 Playfields . Hl 1.5.6 Sprites . 172 1 . 5.7 Der Blitter . 187 1.5.8 Die Tonausgabe . 226 1.5.9 Maus, Joystick und Paddies . 256 1.5.10 Die serielle Schnittstelle . 264 1.5.11 Der Disk-Controller. 269 2. Exec . 275 2.1 Grundlagen des Betriebssystems . 275 2.1.1 Einführung in die Programmierung des Amiga . 276 2.1.2 Unterschiede bei C und Assembler . 276 2.2 Aufbau von Knoten (Nodes) . 278 2.3 Aufbau von Listen . 282 2.4 Professionelle Programmierung in Assembler . 291 2.4.1 Hinweise zur Benutzung des ASSEM . 292 2.4.2 Die Verwendung von Macros . 295 2.4.3 Verwenden von Include-Dateien . 301 2.4.4 Hinweise zur "sauberen" Programmierung . 305 2.5 Die Benutzung von Funktionstabellen (Libraries) . 309 2.5.1 öffnen einer Library . 313 2.5.2 Schließen einer Library . 315 2.5.3 Weitere Library-Funktionen . 316 2.6 Multitasking . 317 2.6.1 Die Task-Struktur . 318 2.6.2 Task-Funktionen . 331 2.6.3 Kommunikation zwischen Tasks. 334 2.6.3.1 Die Task-Signale . 335 2.6.3.2 Das Message-System . 340 2.7 Speicherverwaltung des Amiga . 358 2.7.1 Die Funktionen AllocMem() und FreeMem() . 360 2.7.2 Die Memory-List-Struktur . 362 2.7.3 Speicherzuweisung und Tasks . 366 2.7.4 Die interne Verwaltung des Speichers . 366 2.7.5 Die Allocate-, Deallocate- und AddMemList-Funktion.. 369 2.7.6 Beschreibung der restlichen Funktionen . 371 2.8 Der interne Library-Aufbau . 372 2.8.1 Ändern einer bestehenden Library . 374 2.8.2 Erstellen einer eigenen Library . 375 2.9 Interne lO-Handhabung auf dem Amiga . 389 2.9.1 Aufbau der lORequest-Struktur . 389 2.9.2 Aufbau eines Devices . 391 2.9.3 lO-Steuerung über Exec-Funktionen . 395 2.9.4 Schreiben eines eigenen Devices. 400 2.10 Interrupt-Handhabung auf dem Amiga . 418 2.10.1 Aufbau der Interrupt-Strukturen . 419 2.10.2 Soft-Interrupts . 425 2.10.3 Die CIA-Interrupts . 428 2.10.3.1 Die CIA-Resource-Struktur . 429 2.10.3.2 Die Verwaltung der Resource-Struktur . 431 2.10.4 Beschreibung der Interrupt-Funktionen . 436 2.10.5 Beispiel eines Interrupt-Servers . 438 2.11 Semaphoren . 441 2.11.1 Die Semaphore-Strukturen . 442 2.11.2 Die Semaphore-Funktionen . 445 2.11.3 Das Beispielprogramm . 455 2.12 Die RAM-Library . 465 2.13 Die ExecBase-Struktur . 469 2.14 Reset-Routine und resetfeste Programme . 479 2.14.1 Dokumentation der Reset-Routine . 479 2.14.2 Resident-Strukturen . 487 2.14.3 Resetfeste Programme und Strukturen . 493 2.14.4 Ein richtiges NoFastMem . 498 2.15 Booten ohne Disk (Die ROM-Boot-Library) . 499 2.15.1 Die Strukturen... 500 2.15.2 Die bootbare RAM-Disk . 512 3. Das AmigaDOS . 519 3.1 Vom CLI zur Hardware: Die DOS-Hierarchie . 519 3.1.1 Der erste Kontakt: Das CLI . 520 3.1.2 Die DOS-Bibliothek . 520 3.1.3 Händler . 521 3.1.4 File-Systeme . 522 3.1.5 Devices . 523 3.2 Die DOS-Bibliothek . 523 3.2.1 Laden der DOS.LIBRARY . 524 3.2.2 Funktionsaufruf und Parameterübergabe . 525 3.2.3 Die DOS-Funktionen . 526 3.2.3.1 Allgemeine Ein-/Ausgabe-Funktionen . 526 3.2.3.2 Disketten-Operationen . 530 3.2.3.3 Prozeß-Verwaltung . 536 3.2.4 DOS-Fehlermeldungen . 547 3.3 Standard-I/O . 548 3.3.1 Tastatur und Bildschirm . 552 3.3.2 Disketten-Dateien . 559 3.3.3 Serielle Schnittstelle . 560 3.3.4 Parallele Schnittstelle . 561 3.4 Programme . 561 3.4.1 Programmstart und Parameter . 562 3.4.1.1 Aufruf mit CLI . 562 3.4.1.2 Start von der Workbench aus . 565 3.4.2 Programm-Datei-Strukturen . 572 3.4.2.1 Programmsegmente . 572 3.4.2.2 Programmaufbau (Hunks) . 573 3.4.2.3 Das IFF-Format . 585 3.5 Interner Aufbau des AmigaDOS. 593 3.5.1 Die DOS-Strukturen . 593 3.5.2 Aufbau der transienten Befehle. 594 3.5.3 Die interne DOS-Vektoren-Tabelle . 597 3.6 DOS-Handler . 603 3.6.1 Funktion der DOS-Handler . 603 3.6.2 Aufbau und Programmierung eines Händlers . 615 3.7 Devices . 636 3.7.1 Trackdisk-Device: Zugriff auf Disketten . 638 3.7.2 Consol-Device: Editor-Fenster . 649 3.7.3 Narrator-Device: Sprachausgabe . 654 3.7.4 Serial-Device: Die RS232-Schnittstelle . 658 3.7.5 Printer-Device: Drucker-Programmierung . 661 3.7.6 Parallel-Device: Digital-Ein-/Ausgaben . 662 3.7.7 Game-Port-Device: Maus und Joystick . 663 3.8 Disketten. 667 3.8.1 Der Boot-Vorgang . 668 3.8.2 Daten-Verteilung auf Diskette . 669 3.8.2.1 Normales Filing-System . 670 3.8.2.2 Fast-Filing-System . 676 4. Die Expansionsarchitektur . 679 4.1 Die Hardware . 679 4.2 Die Software . 685 5. Das Januskonzept - Amiga und PC . 689 5.1 Der Aufbau der PC-Brückenkarte . 690 Anhang: Bibliotheksfunktionen im Überbiick . 697 Stichwortverzeichnis . 707 Die Hardware des Amiga 11 1. Die Hardware des Amiga 1.1 Einleitung Der Amiga von Commodore bietet dem Anwender Möglichkeiten, von denen noch vor einigen Jahren bei Computern dieser Preisklasse nie¬ mand zu träumen gewagt hätte. Um diese Leistungen zu ermöglichen, arbeiten beim Amiga ein leistungsfähiges Betriebssystem und eine aus¬ geklügelte Hardware eng zusammen. Ein Ziel der Entwickler dieses Computers war eine hohe Be¬ nutzerfreundlichkeit. Man wollte nach dem Vorbild eines Apple Mac¬ intosh mit Hilfe der Maus und der Workbench als grafischer Benut¬ zeroberfläche dem Anwender die Bedienung seines Computers er¬ leichtern. Aber nicht nur dieser sollte leicht mit dem Gerät zurecht¬ kommen, auch der Programmierer wurde entlastet. Für beinahe jede erdenkliche Aufgabe findet man im Betriebssystem eine passende Routine, die die direkte Programmierung der Hardware anscheinend überflüssig macht. Aber eben doch nur anscheinend. Denn trotz all dieser komfortablen Routinen kommt man um die direkte Programmierung nicht immer herum, denn die Geschwindigkeit der Betriebssystemroutinen ist sehr viel niedriger, als man es vom Amiga erwarten würde. Der Grund dafür ist die Programmiersprache C, in der das Amiga-Betriebssystem größtenteils geschrieben wurde. Will man also schnelle und leistungs¬ fähige Programme schreiben oder will man ganz einfach nur seinen Amiga besser kennenlernen, sollte man sich mit der Hardware be¬ schäftigen. Das folgende Kapitel bietet dazu eine Beschreibung der Amiga-Hardware und der Programmierung der einzelnen Chips. 1.2 Die Komponenten des Amiga-Systems Im wesentlichen besteht die Hardware des Amiga aus folgenden Bau¬ steinen, egal ob es sich um einen A500, 1000 oder 2000 handelt: ■ Der 68000er von Motorola als Mikroprozessor. ■ Zwei Schnittstellenbausteine vom Typ 8250. 12 Amiga intern ■ Drei Spezial-Chips von Commodore: Agnus, Denise und Paula. Läßt man das RAM und die unumgänglichen Logikbausteine einmal außer acht, sind die sechs oben genannten Chips für alle Funktionen des Amiga verantwortlich. Auch an Schnittstellen ist beim Amiga alles Nötige vorhanden: ■ Paralleler Drucker-Port (Centronics). ■ Serielle RS232-Schnittstelle. ■ RGB-Monitoranschluß. ■ Composite Video (nicht A2000). ■ Stereo-Audioausgang. ■ Anschlußbuchse für einen HF-Modulator. ■ Commodore-eigener Tastaturanschluß. ■ Anschluß für Floppylaufwerke (Shugart-Bus-kompatibel). ■ Zwei identische Buchsen zum Anschluß verschiedener Eingabe¬ geräte wie Maus, Joystick und Paddle. ■ Anschluß für 256 KByte RAM-Erweiterung. Auf diesen An¬ schluß wird in diesem Buch nicht weiter eingegangen, da hier nur die Original RAM-Erweiterung zur Erweiterung von 256 auf 512 KByte angeschlossen werden kann. Beim A500 und A2000 sind diese 256 KByte schon von Anfang an eingebaut. Der A500 hat dafür einen Anschluß für eine RAM-Erweiterung um 512 KByte, der aber völlig anders geartet ist. ■ Expansion-Port zum Anschluß von Systemerweiterungen aller Art. Dieser Anschluß liegt beim A500 und Al000 an der Gehäuseseite, beim A2000 ist er in Form mehrerer Steckplätze ins Geräteinnere verlegt worden. Um die Zusammenarbeit aller Bausteine im Amiga zu verstehen, muß erst einmal die Funktion der einzelnen Chips geklärt werden. Die Hardware des Amiga 13 1.2.1 Der 68000er-Prozessor Der 68000 von Motorola ist unumstritten einer der leistungsfähigsten 16-Bit-Prozessoren. Obwohl er schon seit 1979 auf dem Markt ist, findet man ihn erst seit kurzem in Computern der Preisklasse eines Amiga. Natürlich kann hier keine ausführliche Beschreibung des 68000 er¬ wartet werden, denn dies würde den Rahmen dieses Buches sprengen. Wer mehr über die Programmierung des 68000 wissen möchte, sei an die entsprechende Fachliteratur verwiesen. An dieser Stelle soll ledig¬ lich die Pinbelegung und eine kurze Beschreibung der einzelnen Si¬ gnalgruppen stehen, da viele Bücher über die Programmierung des 68000 zwar eine gute Einführung in die Softwareseite bieten, aber kaum etwas über die Hardware sagen. Eine grundlegende Kenntnis der Signale des 68000 ist aber für das Verständnis der Amiga-Hardware unumgänglich. 14 Amiga intern Die Pinbelegving des &3000 D4 1 X7 64 D5 D3 2 63 D2 3 62 34—► D7 Dl 4 61 a^iifc T) R D0 -4^C 5 60 34-^ D9 AS c 6 59 34-^ D10 UDS c 7 58 34—► Dil LDS 8 57 34—► D12 R/W ■^c 3 56 34—► D13 DTACK 1 0 55 34—► D14 Bä c 1 i 54 34—► D15 BGACK 1 2 0 53 3^~ GND BR 13 52 3*^ A23 Voc 14 51 3*4- A22 CLK 15 CT 50 3—4- A21 GND 16 49 3^—“ Vcc HALT 1 7 0 48 3*4- A20 RESET 1 3 47 3^^ A19 VMA c 1 9 0 46 3—^ A18 E c 20 45 3^^ A17 VPA 21 44 3*4- Al6 BERR 22 43 3^^ Al5 tPL.2 23 42 3*4- A14 ±PL1 24 41 3—^ A13 iPLe 25 40 3'^- A12 FC2 c 26 39 3-^ All FCl c 27 38 □*4- A10 FC0 23 37 3*4- A9 Ai c 29 36 3-H^ A8 A2 30 35 3—4- A7 A3 c 31 34 3*4 A6 A4 c 32 33 3-^ A5 AnHen) Ei n eil ü bei' de M S isr na 1 naM en bed eu t e t « daß das Si sn 1 ( 0-Al< t i v> . Abb. 1.2.1.1 Man kann die Anschlüsse in folgende Funktionsgruppen aufteilen: Die Stromversorgung: VCC und GND Der 68000 arbeitet mit einer einfachen Versorgungsspannung von 5 Volt. Die Anschlüsse sind doppelt ausgelegt und zentral gelegen, um durch kurze Leitungswege im Gehäuse die Stromverluste minimal zu halten. Die Hardware des Amiga 15 Der Takteingang: CLK Der 68000 benötigt lediglich einen einfachen Takt. Die Frequenz hängt von der gewählten Prozessorversion ab. Im Amiga beträgt die Taktfrequenz für den Prozessor 7.16 MHz. Der Datenbus: DO - D15 Der Datenbus ist als 16-Bit-Bus ausgelegt und kann somit ein Wort (16 Bit) auf einmal übertragen. Beim Transfer einzelner Bytes (8 Bit) ist jeweils nur eine Hälfte der Leitungen an der Datenübertragung beteiligt. Entweder wird das Byte über die unteren 8 Bit oder über die oberen 8 Bit gelesen bzw. geschrieben. Der Adreßbus: Al - A23 Der Adreßbus kann mit seinen 23 Leitungen 8 Megaworte Speicher ansprechen (2*® entspricht 8 Megaworten oder 16 Megabytes). Da er kein AO Adreß-Bit besitzt, kann er diesen Speicher nur wortweise adressieren. Bussteuerleitungen im asynchronen Modus: AS, R/W, UDS, LOS. DTACK Der 68000 kann seine Speicherzugriffe grundsätzlich in zwei verschie¬ denen Modi ausführen. Im asynchronen Modus signalisiert der Pro¬ zessor mit AS (Adress-Strobe/Adresse gültig), daß eine gültige Adresse am Adreßbus anliegt. Gleichzeitig bestimmt er mit R/W (Read- Write/Lesen-Schreiben), ob ein Byte/Wort gelesen oder geschrieben werden soll. Die Wahl zwischen Wort oder Byte treffen die beiden Leitungen UDS und LDS (Upper-, Lower Data Strobe/Obere, Untere Datenbushälfte). Da der Speicher immer wortweise adressiert wird, überträgt der Prozessor bei einem Byte-Zugriff einfach nur die oberen oder die unteren 8 Bits des Datenbusses. Dies signalisiert er durch UDS und LDS. Bei einem Wortzugriff legt der 68000 beide Leitungen auf 0. Will er dagegen auf ein Byte zugreifen, legt er entweder UDS oder ODS auf 0, die andere Leitung bleibt 1. Hat der Prozessor jetzt mit AS, R/W, UDS und LDS den von ihm ge¬ wünschten Zugriff signalisiert, wartet er, bis der Speicher ihm mit¬ teilt, daß die gewünschten Daten bereit sind. Dazu verwendet dieser die Leitung DTACK, die er auf 0 legt, sobald er die Daten bereitge¬ stellt hat. Schreibt der Prozessor Daten, teilt ihm der Speicher durch DTACK mit, daß er die Daten übernommen hat. 16 Amiga intern Im asynchronen Modus paßt sich der Prozessor also immer an die Ge¬ schwindigkeit des Speichers an. Die einzelnen Worte und Bytes liegen dann folgendermaßen im Speichen Beteiligte Datenbusleitungen: D8-15 DO-7 Adresse: UDS=0 LDS=0 0 Wort 0 Byte 0 Byte 1 2 Wort 1 Byte 2 Byte 3 4 Wort 2 Byte 4 Byte 6 6 Wort 3 Byte 6 Byte 7 Bussteuersignale im synchronen Modus: E, VPA, VMA Um den Sinn dieser Signale besser verstehen zu können, muß man die Situation zum Zeitpunkt der Markteinführung des 68000 kennen. Es waren damals keine eigenen Peripherie-Chips für den 68000 verfüg¬ bar. Die vorhandenen Chips von Motorola, die ja für die 6800-Serie (von der auch der 6502 abstammt) gedacht waren, konnten nicht ohne zusätzliche Schaltungen in die asynchrone Bussteuerung eingepaßt werden. Also versah man den 68000 bei Motorola noch mit einem synchronen Busmodus, wie man ihn von den 8-Bit-Prozessoren wie dem 6800 oder 6502 kennt. An der Leitung E liegt dabei ständig ein durch den Faktor zehn ge¬ teilter Prozessortakt an, beim Amiga also 716 KHz, der den Periphe¬ rie-Chips als Takt dient. (Er wird mit dem Phi-2-Eingang der Peri¬ pherie-Chips verbunden.) Die Umschaltung von dem synchronen Mo¬ dus in den asynchronen erfolgt über den Eingang VPA (Valid Peri- phial Adress/Gültige Peripherieadresse). Dieser Eingang muß von ei¬ nem externen Adreßdecoder auf 0 gelegt werden, sobald dieser die Adresse eines Peripherie-Chips erkennt. Der Prozessor antwortet da¬ rauf, indem er die Leitung VMA (Valid Memory Adress/Gültige Speicheradresse) ebenfalls auf 0 legt. Das entsprechende Peripherie- Chip muß jetzt innerhalb eines Taktzyklus von E die Daten überneh¬ men bzw. bereitstellen. Danach verläßt der 68000 automatisch den synchronen Modus, bis das VPA Signal erneut aktiv wird. Dies bedeutet also, daß ein Peripherie-Chip im synchronen Modus gezwungen ist, die Daten an einem bestimmten Zeitpunkt be¬ reitzustellen bzw. zu übernehmen. Die Hardware des Amiga 17 Die Steuersignale des Systems: RESET, HALT, BERR Die wichtigste Aufgabe eines Reset-Signals ist das Zurücksetzen des Systems, so daß alle Systemkomponenten in einen verläßlichen Grundzustand versetzt werden und die weitere Programmausführung an einer festgelegten Adresse beginnt. Um einen solchen Systemreset auszulösen, muß beim 68000 sowohl die HALT- als auch die RESET-Leitung auf 0 gelegt werden. Sobald diese Leitungen dann wieder auf 1 gehen, beginnt der 68000 mit der Programmausführung bei der Adresse, die er in der Speicheradresse 4 vorfindet. Die Leitung RESET kann auch vom 68000 aus auf 0 gelegt werden, um das System zu initialisieren, ohne den Prozessorzustand zu verän¬ dern. Mit der BERR-Leitung (Bus-Error/Bus-Fehler) kann eine externe Überwachungsschaltung dem Prozessor mitteilen, daß irgend etwas nicht in Ordnung ist. Ein Grund für einen Bus-Fehler kann z.B. ein Hardware-Defekt oder ein Zugriff des Prozessors auf eine nicht exi¬ stierende Adresse sein. Tritt ein BERR-Signal auf, springt der 68000 in eine spezielle Routine des Betriebssystems, die dann die weitere Fehlerbehandlung über¬ nimmt (Guru-Meditation läßt grüßen!). Tritt während dieser Fehlerbe¬ handlung ein erneuter Bus-Fehler auf, hält der 68000 jede Programm¬ ausführung an und legt HALT auf 0. Dieser sogenannte doppelte Bus- Fehler ist übrigens der einzige Fall, in dem der 68000 abstürzt, d.h. jegliche Programmausführung einsteilt. Bei sämtlichen anderen Fehlern springt er über spezielle Vektoren in Programmroutinen, die dann die Fehlerbehandlung übernehmen können und eine Weiterarbeit des Sy¬ stems ermöglichen. Beim Amiga hat man hier im Betriebssystem etwas gespart. (Man beachte die Häufigkeit der Guru-Meditationen, die sich beim Amiga getreu Murphy’s Gesetz verhalten: Ein Computer stürzt immer genau dann ab, wenn man wichtige Daten bearbeitet, die man noch nicht gespeichert hat.) Hat der Prozessor die Programmausführung auf Grund eines doppelten Bus-Fehlers angehalten, kann er nur mittels einem Reset wieder ge¬ startet werden (HALT und RESET auf 0). Eine weitere Funktion der HALT-Leitung ist ein Stopp des Prozessors. Legt man HALT auf 0, beendet der 68000 noch den aktuellen 1t Amiga intern Speicherzugriff und wartet dann, bis HALT wieder auf 1 zurückge- teax wird. Es gibt noch einige weitere Details im Zusammenspiel von BERR und HALT, die hier nicht weiter erwähnt werden sollen, da sie im Amiga ohne Belang sind. Der Betriebszustand des Prozessors: FCO, FCl, FC2 Die Leitungen FCO - FC2 signalisieren den Betriebszustand des Pro¬ zessors. Folgende Zustände sind möglich: FCJ FCl FCO 0 0 1 0 10 10 1 110 111 Zustand: Zugriff auf Anwender-Daten Zugriff auf Anwender-Programm Zugriff auf Supervisor-Daten Zugriff auf Supervisor-Programm Signalisierung eines gültigen Interrupts Der Prozessor kann grundsätzlich in zwei verschiedenen Modi betrie¬ ben werden, nämlich im Anwender-Modus und im Supervisor-Modus (Oberwacher-Modus). Ein Programm, das im Supervisor-Modus läuft, hat uneingeschränkten Zugriff auf sämtliche Prozessorregister. Das Betriebssystem arbeitet z.B. immer im Supervisor-Modus. Im Anwender-Modus sind bestimmte Register des Prozessors für das Programm gesperrt. Genaueres darüber finden Sie in der Fachliteratur zum 68000. Die drei FCx Leitungen erlauben es also der Systemhardware, den aktuellen Zustand des Prozessors zu erkennen und möglicherweise darauf zu reagieren. So kann zum Beispiel im Anwender-Modus beim Zugriff auf Speicherbereiche des Betriebssystems ein Bus-Fehler (BERR = 0) erzeugt werden. Die Unterbrechungseingänge (Interrupt-Eingänge): IPLO, IPLl, IPL2 Die Signale an den drei Interrupt-Eingängen (IPL = Interrupt Pending Level) werden vom 68000 als eine 3-Bit-Binärzahl interpretiert. Der 68000 kann also acht verschiedene Interrupt-Signale, die sogenannten Interrupt-Ebenen, unterscheiden, wobei eine 0 bedeutet, daß kein In¬ terrupt anliegt, während eine 7 einen Interrupt höchster Priorität si¬ gnalisiert. Jeder der sieben Interrupt-Ebenen ist ein eigener Interrupt- Vektor zugewiesen, der die Adresse der Routine enthält, in die bei ei¬ nem Interrupt verzweigt wird. Die Hardware des Amiga 19 t ein Interrupt der entsprechenden Ebene erlaubt, legt der Prozessor le FCx-Leitungen auf 1 und signalisiert damit, daß er einen Inter- ipt erkannt hat und jetzt auf eine Bestätigung von Seiten des Verur- chers wartet. Sie kann durch VPA oder DTACK erfolgen. Bei einer jstätigung durch VPA erfolgt eine sogenannte autovektorielle Unter- echung, d.h. der Prozessor springt an die Adresse, die er in dem der iterrupt-Ebene zugewiesenen Vektor findet. D.h. es können sieben irschiedene Adressen angesprungen werden, da es sieben gültige In- rrupt-Ebenen gibt (Ebene 0 bedeutet ja, daß kein Interrupt vorliegt). ibt es nur sieben verschiedene Interrupt-Quellen im System, braucht so keine softwaremäßige Trennung der einzelnen Quellen mehr vor- snommen werden. Man ordnet einfach jeder Interrupt-Ebene eine iterrupt-Quelle zu, und der Prozessor springt dann in das entspre- lende Programm. Der Amiga verwendet nur diese autovektorielle nterbrechungen. och mehr Möglichkeiten zur hardwaremäßigen Trennung von ver- hiedenen Interrupt-Quellen bietet die sogenannte nicht-autovektori- le Unterbrechung. Da sie im Amiga nicht verwendet wird, soll auch icht weiter darauf eingegangen werden. Es sei nur soviel gesagt, daß ;i einer nicht-autovektoriellen Unterbrechung die Bestätigung durch TACK erfolgen muß und der Baustein, der den Interrupt ausgelöst it, einen Interrupt-Vektor auf den Datenbus legen kann, der dann ne hardwaremäßige Unterscheidung von bis zu 192 verschiedenen iterrupt-Vektoren erlaubt. ignale für die Buszuweisung: BR, BG. BGACK iese drei Signale erlauben es einem anderen Chip den Bus zu über- jhmen. Dies kann zum Beispiel bei einem Harddisk-Controller der all sein, der dann die Daten von der Festplatte direkt in den Speicher ;hreibt (sogenannter DMA = Direct Memory Access/Direkter jeicherzugriff). uch diese Signale sind im Amiga nicht benutzt, hier wird der DMA if eine andere Weise realisiert, die am Ende dieses Kapitels noch nä- sr beschrieben wird. 20 Amiga intern 1.2.2 Das CIA 8520 Die Pinbeleguotig des 8520 gnd —►c 1 40 3-4-+- CNT PAe 2 39 □ ■4-^ SP PAl 3 38 □ ■*— AB PA2 •«-►C 4 37 Al PA3 ^-►C 5 36 □ A2 PA4 6 35 □ A3 PAS •^-►C 7 34 3'*— RES PA 6 -«-►C 8 33 □ -4-^ De PA 7 9 32 □ Dl pse 1 0 31 3-4-^ D2 PBl 11 Lft 30 3-4-^ D3 PB2 -4-^C 12 u « 29 3'4-^ D4 PB 3 1 3 28 3-4-»^ CS PB4 -^-►C 14 Cu 27 3-4-^ D6 PB 5 15 26 3-4-4 C7 PB 6 16 25 34—4>2CP>ii 2> PB7 ■^-►C 17 24 3 ♦— FEAG PC c 18 23 3 ♦— CS TOD — 19 22 3 ♦— R/U Vco 28 21 3 IRQ AnMei'kiinsren ; Die PFeile kennzeichnen die Si&na 1 r» i ch tun^. Ein St-rich ubep deM Si^nalnahten bedeut^et.« daß das Siarnal Low—Aktiv ist C0—Aktiva. Abb. 1.2.2.1 Die Hardware des Amiga 21 Abb. 1.2.2.2 Der 8520 ist ein Peripheriebaustein vom Typ eines sogenannten Complex Interfece Adapters (CIA), was soviel wie vielseitiger Schnitt¬ stellenbaustein bedeutet. Dies heißt, daß die Entwickler des 8520 ver¬ sucht haben, möglichst viele Funktionen in einem Chip unterzubrin¬ gen. Betrachtet man den 8520 genauer, fällt einem sofort eine große Ähnlichkeit mit einem alten Bekannten auf. Der 8520 gleicht nämlich dem 6526, der im C64 seine Dienste versah, fast wie ein Ei dem an¬ deren. Lediglich die Funktionsweise der Register 8 bis 11 ($8 bis $B) 22 Amiga intern hat sich etwas verändert. Dies ist sicher eine erfreuliche Mitteilung für alle, die sich in der Programmierung des 6526 auskennen. Der 8520 verfügt im einzelnen über folgende Möglichkeiten: zwei frei programmierbare 8-Bit-Parallel-Ports (PA und PB), zwei 16-Bit-Ab¬ wärtszähler (Timer A und Timer B), einen bidirektionalen seriellen Port (SP) und einen 24-Bit-Zähler (Event Counter) mit Alarmfunktion beim Erreichen eines vorgewählten Wertes. Sämtliche Funktionen sind in der Lage, Interrupts auszulösen. Organisiert sind die Funktionen des 8520 in 16 Registern. Für den Prozessor erscheinen sie als ganz normale Speicherstellen, da sämtliche Peripheriebausteine in einem 68000-System, und damit auch im Amiga, memory mapped sind, d.h. die Register dieser Chips erschei¬ nen als Speicherstellen, die mit den üblichen Befehlen wie z.B. Move gelesen und beschrieben werden können. Da der 8520 vom 6526 abstammt, der ja für die 8-Bit-Prozessoren der 65xx Reihe entwickelt wurde, muß der 68000 ihn im synchronen Mo¬ dus ansprechen (siehe Kapitel 1.2.1). Der E-Takt des 68000 ist deshalb auch mit dem Phi2-Eingang des 8520 verbunden. Die Auswahl der 16 internen Register geschieht mit den vier Adreßeingängen des 8520: AO - A3. Genaue Details über die Einbindung der CIAs in das Amiga-System finden sich am Ende die¬ ses Kapitels. Hier erst einmal die Belegung der 16 Register (Eigentlich sind es nur 15 Register, da das Register 11 ($B) unbenutzt ist): Registerbelegung des 8520 Rtgisttr_Name_Funktion 0 0 PRA Datenrcgiflter für Port A 1 1 PRB Datenregister für Port B 3 3 ODRA Datenrichtungsregister für Port A 3 3 DDRB Datenrichtungsi^gister für Port B 4 4 TALO Timer A Untere 8 Bit 5 5 TAHI Timer A Obere 8 Bit 6 6 TBLO Timer B Untere 8 Bit 7 7 TBHI Timer B Obere 8 Bit 8 8 Event Lo Zähler Bits 0-7 9 9 E. 8-16 Zähler Bits 8-15 10 A Event Hi Zähler Bits 16-23 11 B ... Unbenutst Die Hardware des Amiga 23 IS c SP Datenregister des seriellen Ports IS D ICR Interrupt'Kontrollregister 14 E CRA Kontrollregister A IS P CRB Kontrollregister B Die Parallel-Ports Bagiiter Name D7 D6 D6 D4 DS D2 Dl DO 0 PRA PA7 PA6 PAS PA4 PAS PAS PAl PAO 1 PRB PB7 PB6 PB6 PB4 PBS PB2 PBl PBO S DDRA DPA7 DPA6 DPA5 DPA4 DPAS DPAS DPAl DPAO 3 DDRB DPB7 DPB6 DPB5 DPB4 DPBS DPBS DPBl DPBO Der 8520 verfügt über zwei 8-Bit-ParaIIel-Ports, PA und PB, denen jeweils ein Datenregister zugeordnet ist, PRA und PRB (Port Regi¬ ster). Dementsprechend verfügt das Chip über 16 Portleitungen, PAO - PA7 und PBO - PB7. Jede Portleitung kann sowohl als Ein- als auch als Ausgang verwendet werden. Dies wird als Datenrichtung bezeich¬ net. Der 8320 erlaubt es, die Datenrichtung jeder Leitung getrennt einzustellen. Dazu besitzt jeder Port ein korrespondierendes Datenrichtungsregister, DDRA und DDRB (Data Direction Register). Ist ein Bit im Datenrichtungsregister 0, ist die entsprechende Leitung als Eingang geschaltet. Ihr Zustand kann durch Lesen des entspre¬ chenden Bits des Datenregisters abgefragt werden. Setzt man ein Bit im Datenrichtungsregister auf 1, schaltet man die entsprechende Leitung als Ausgang. Der Pegel an der zugehörigen Portleitung stellt dann direkt den Wert des korrespondierenden Bits des Datenregisters dar. Im allgemeinen gilt, daß beim Schreiben in das Datenregister der Wert dort immer gespeichert wird, während beim Lesen immer der Zustand der Portleitungen auf dem Datenbus erscheint. Die Bits im Datenrich¬ tungsregister bestimmen, ob der Wert des Datenregisters auf die Port¬ leitungen geschaltet wird. Deshalb erhält man beim Lesen eines Ports, der als Ausgang geschaltet ist, den Inhalt des Datenregisters, während beim Schreiben in einen Eingangs-Port der Wert im Datenregister ge¬ speichert wird, aber erst auf den Portleitungen erscheint, wenn man auf Ausgabe umschaltet. Um die Datenübertragung mittels der Parallel-Ports zu vereinfachen, besitzt jeder 8520 zwei Handshake-Leitungen, PC und FLAG. Der PC-Ausgang geht bei jedem Zugriff auf das Datenregister B (PRB, Reg. 1) für einen Taktzyklus auf 0. Der Eingang FLAG hinge- 24 Amiga intern gen reagiert auf solche negativen Flanken. Jedesmal wenn der Zustand an der FLAG-Leitung von 1 auf 0 wechselt, wird im Interrupt-Kon- trollregister (ICR, Reg. $D) das FLAG-Bit gesetzt. Mit diesen beiden Leitungen kann somit auf einfache Weise ein Handshake realisiert werden, indem man die FLAG- und PC-Leitungen zweier CIAs wechselseitig miteinander verbindet. Der Sender braucht lediglich seine Daten in das Portregister zu schrei¬ ben und vor jedem weiteren Byte auf ein FLAG-Signal warten. Da FLAG einen Interrupt auslösen kann, hat der Sender sogar die Mög¬ lichkeit, sich in den Wartepausen anderen Aufgaben zuzuwenden. Für den Empfänger gilt genau dasselbe, nur liest er die Daten vom Port, statt sie auszugeben. Die Abwärtszähler/Timer: Lesezugriff (Read): tUgister Name D7 D6 D5 D4 D3 D2 Dl DO 4 TALO 5 TAHI 6 TBLO 7 TBHI TAL7 TAL6 TAH7 TAH6 TBL7 TBL6 TBH7 TBH6 TALS TAL4 TAH5 TAH4 TBL6 TBL4 TBH5 TBH4 TALS TAL2 TAH3 TAH2 TBL3 TBL2 TBH3 TBH2 TALl TALO TAHI TAHO TBLl TBLO TBHI TBHO Schreibzugriff (Write): Register Nam«_D7 D6 PS D4 D3 D2 Dl DO 4 PALO 5 PAHI 6 PBLO 7 PBHI PAL7 PAL6 PAH7 PAH6 PBL7 PBL6 PBH7 PBH6 PAL5 PAL4 PAH5 PAH4 PBL5 PBL4 PBHS PBH4 PAL3 PAL2 PAH3 PAH2 PBL3 PBL2 PBHS PBH2 PALI PALO PAHI PAHO PBLl PBLO PBHI PBHO Der 8520 verfügt über zwei 16-Bit-Abwärtszähler (Timer). Diese Ti¬ mer sind in der Lage, von einem voreingestellten Wert aus bis auf Null herunterzuzählen. Dabei sind eine Vielzahl verschiedener Modi möglich, die mit Hilfe eines Kontrollregisters gewählt werden können. Es existiert dabei für jeden Timer ein eigenes Kontrollregister (CRA und CRB). Jeder Timer besteht intern aus vier Registern (für Timer A: TALO+TAHI und PALO+PAHI) oder zwei Registerpaaren, da je ein Low- und High-Register zusammen den 16-Bit-Zählerwert bilden. Beide Registerpaare liegen an der selben Adresse, aber das eine kann nur gelesen und das andere nur beschrieben werden. Bei einem Schreibzugriff auf eines der Timer-Register wird der Wert in ^inem Die Hardware des Amiga 25 Latch gespeichert. Dieser Wert wird in das Zählregister geladen und heruntergezählt, bis der Zähler einen Unterlauf, d.h. Null, erreicht hat. Danach wird der Wert aus dem Latch erneut in das Zählregister geladen. Liest man eines der Timerregister, erhält man den aktuellen Stand des Zählregisters. Um hierbei einen korrekten Wert zu erhalten, muß al¬ lerdings der Zähler angehalten werden. Warum, zeigt folgendes Beispiel: Zählerstand: $0100 Ein Lesezugriff auf das Register 5 ergibt das High-Byte des aktuellen Zählerstands: $01. Bevor das Low-Byte (Reg. 4) gelesen werden kann, taucht ein Zählimpuls auf und erniedrigt den Zähler: Stand jetzt $00FF. Das Low-Byte wird gelesen: $FF. Gelesener Zählerstand also: $01 FF! Statt den Zähler anzuhalten, was ja auch Fehler verursacht, da jetzt Zählimpulse übergangen werden, kann man auch folgende elegantere Methode anwenden: Man liest das High-Byte, dann das Low-Byte, anschließend noch einmal das High-Byte. Ergibt der Vergleich beider gelesener High-Bytes Übereinstimmung, ist der gelesene Wert korrekt. Fällt der Vergleich dagegen negativ aus, muß der Vorgang wiederholt werden. Welche Signale den Zähler erniedrigen, bestimmen für Timer A Bit 5 und für Timer B die Bits 5 und 6 des jeweiligen Kon- trollregisters. Bei Timer A sind nur zwei Quellen möglich: 1. Timer A wird mit jedem Taktzyklus erniedrigt, da die CIAs im Amiga am E-Takt des Prozessors hängen, beträgt die Zählfre¬ quenz 716 Khz (INMODE = 0). 2. Jeder High-Impuls auf der CNT-Leitung erniedrigt den Zähler (INMODE = 1). Für Timer B gibt es vier Eingangsmodi: 1. Taktzyklen (INMODE-Bits = 00 (Binärdarstellung, erste Ziffer steht für Bit 6, die zweite für Bit 5). 2. CNT-Impulse (INMODE-Bits = 01). 26 Amiga intern 3. Unterläufe von Timer A, damit kann man beide Timer zu einem 32-Bit-Timer kombinieren (INMODE-Bits = 10). 4. Unterläufe von Timer A wenn die CNT-Leitung gleichzeitig High ist. Damit kann man die Länge eines Impulses an der CNT-Leitung messen (INMODE-Bits = 11). Die Unterläufe eines Zählers werden im Interrupt-Kontrollregister (ICR) registriert. Bei einem Unterlauf von Timer A wird das TA-Bit (Bit-Nr. 0) gesetzt, bei Timer B entsprechend TB (Bit-Nr. 1). Diese Bits bleiben, wie alle Bits im ICR, gesetzt, bis das ICR gelesen wird. Zusätzlich besteht die Möglichkeit, die Unterläufe der Timer auf dem Parallel-Port B auszugeben. Wird das PBon-Bit im Kontrollregister des jeweiligen Zählers (CRA oder CRB) gesetzt, erscheint jeder Unterlauf auf der entsprechenden Portleitung (PB 6 für Timer A und PB 7 für Timer B). Mit dem OUTMODE-Bit kann man zwischen zwei verschiedenen Ausgabearten wählen: OUTMODE = 0 Pulse-Mode Jeder Unterlauf wird als positiver Impuls von einem Taktzyklus Länge an der entsprechenden Portleitung ausgegeben. OUTMODE = 1 Toggle-Mode Bei jedem Unterlauf wechselt die entsprechende Port-Leitung von Low nach High oder von High nach Low. Beim Start des Timers be¬ ginnt die Ausgabe mit High. Gestartet und gestoppt wird der Timer vom START-Bit des Kon- trollregisters. START = 0 hält den Zähler an, START = 1 startet ihn. Mit dem RUNMODE-Bit kann man zwischen dem One-shot-Mode und dem Continuous-Mode wählen. Im One-shot Mode hält der Timer nach jedem Unterlauf an und setzt das START-Bit auf 0 zurück. Im Continuous-Mode beginnt der Zähler nach jedem Unterlauf wieder' beim Startwert. Wie schon erwähnt, wird beim Schreiben in ein Timer-Register der Wert nicht direkt in das Zählregister, sondern in ein Latch geschrieben (auch Vorteiler (Prescaler) genannt, da die Anzahl der Unterläufe pro Sekunde gleich der Zählfrequenz geteilt durch den Wert im Vorteiler Die Hardware des Amiga 27 ist). Um eine Übertragung des Werts vom Latch in den Zähler zu erreichen, gibt es folgende Möglichkeiten: 1. Setzen des LOAD-Bits im Kontrollregister. Dies bewirkt ein sog. Force-Load, d.h. unabhängig vom Zählerzustand wird der Wert des Latchs in den Zähler übertragen. Das LOAD-Bit ist ein sogenanntes Strobe-Bit. Das bedeutet, daß das Bit nicht ge¬ speichert wird, sondern nur einen einmaligen Vorgang auslöst. Um danach erneut ein Force-Load zu bewirken, muß noch ein¬ mal eine 1 in das LOAD-Bit geschrieben werden. 2. Bei jedem Unterlauf des Timers wird das Latch automatisch in den Zähler übertragen. 3. Nach einem Schreibzugriff auf das Timer High-Register bei ste¬ hendem Zähler (Stop = 0) wird er ebenfalls automatisch mit dem Wert des Latchs geladen. Aus diesem Grund sollte man die Rei¬ henfolge immer einhalten: Erst Low-, dann High-Byte. Belegung der Bits des Kontrollregisters A: Register Nr. 14/SE Name: CRA 07 06 05 OA 03 02 01 00 nicht SPMOOE INMOOE LOAO RUNHOOE ; OUTMOOE PBon START ver¬ 0=Eing. 0=Takt 1=force 0=cont. 0=pulse 0=PB6aus 0=aus wen¬ 1=Ausg. 1=CNT load 1=one- 1=toggle 1=PB6an 1=an det (Strobe) shot Belegung der Bits des Kontrollregisters B: Register Nr. I5/SF Name: CRB 07 06+05 OA 03 02 01 00 ALARM INMOOE LOAO RUNMOOE OUTMOOE PBon START 0=700 00=Takt 1=force 0=cont. 0=pulse 0=PB7aus 0=aus 1=Alarm 01=CNT load 1=one- 1=toggle 1=PB7an 1=an 10=Timer A (strobe) shot 11=Tiiner A+ CNT Der Zähler (Event-Counter) Register Name D7 D6 D5 D4 D3 D2 Dl DO 8 $8 LSB Event E7 E6 E5 E4 ES E2 El EO 9 $9 Event 8-15 EIS E14 , EIS E12 Eli ElO E9 E8 10 $A MSB Event E23 E22 E21 E20 E19 E18 E17 E16 Wie schon eingangs erwähnt, unterscheidet sich der 8520 nur durch geringe Änderungen vom 6526. Sämtliche dieser Änderungen betreffen 28 Amiga intern die Funktion der Register 8 - II. Beim 6526 befand sich hier eine Echtzeituhr (englische Bezeichnung TimeOfDay TOD), die die Tages¬ zeit in Form von Stunden, Minuten und Sekunden in einzelnen Regi¬ stern bereitstellte. Beim 8520 wurde diese Uhr durch einen einfachen 24-Bit-Zähler ersetzt, den sogenannten Event-Counter. Dadurch tritt eine leichte Begriffsverwirrung auf, da Commodore in den Datenblät¬ tern teilweise die alte Bezeichnung TOD auch noch beim 8520 wei¬ terverwendet. Die Funktion des Event-Counters ist einfach. Er stellt, wie schon er¬ wähnt, einen 24-Bit-Zähler dar. Das bedeutet, sein Wertebereich reicht von 0 bis 16777215 (SFFFFFF). Mit jedem positiven Impuls (Wechsel von Low nach High) der TOD-Leitung wird der Zählerstand um eins erhöht. Hat der Zähler schon SFFFFFF erreicht, springt er beim nächsten Zählimpuls wieder auf 0 zurück. Der Zähler kann auf einen definierten Stand gesetzt werden, indem man den gewünschten Wert in die Zählerregister schreibt. Das Register 8 enthält die Bits 0-7 des Zählers, das sogenannte LSB (LowestSignificantByte = niederwer¬ tigstes Byte), in Register 9 stehen die Bits 8-15 und in Register 10 ($A) die Bits 16-23, das MSB des Zählerwerts (MostSignificantByte = höchstwertiges Byte). Bei jedem Schreibzugriff stoppt der Zähler, damit keine Fehler durch einen plötzlichen Übertrag von einem Register ins andere auftreten (s. vorangegangenen Abschnitt). Erst nachdem der Wert ins LSB geschrie¬ ben wurde (Reg. 8), läuft der Zähler wieder weiter. Im Normalfall wird man also die Reihenfolge Register 10 (MSB), dann Register 9 und abschließend Register 8 (LSB) einhalten. Damit beim Lesen des aktuellen Zählerstands keine Übertragsfehler auftreten, wird der Zählerwert beim Lesen des MSB (Reg. 10) in ein Latch geschrieben. Jeder weitere Zugriff auf eines der Zählerregister liefert jetzt den Wert des Latchs, das somit in aller Ruhe ausgelesen werden kann, während der Zähler intern weiterläuft. Erst beim Lesen des LSBs wird das Latch wieder abgeschaltet. Will man also den Zäh¬ ler auslesen, sollte man dieselbe Reihenfolge wie beim Schreiben ein¬ halten: erst MSB, dann das Register 9 und am Ende das LSB. Zusätzlich ist noch eine sogenannte Alarm-Funktion integriert. Setzt man im Kontrollregister B das Alarm-Bit (Bit-Nr. 7) auf 1, kann man durch Schreiben in die Register 8-10 einen Alarmwert setzen. Sobald dann der Wert des Zählers mit dem Alarmwert übereinstimmt, wird das Alarm-Bit im Interrupt-Kontrollregister gesetzt. Der Alarmwert Die Hardware des Amiga 29 kann nur gesetzt werden, ein Lesezugriff auf die Adressen 8-10 ergibt immer den aktuellen Zählerstand, egal ob das Alarm-Bit im Kontroll- register B gesetzt ist oder nicht. Der serielle Port Register Name _ D7 _D6_D5_D4_D3_D2_Dl_ DO 12 $C SDR S7 S6 S5 S4 SS S2 S1 SO Der serielle Port besteht im wesentlichen aus dem seriellen Datenregi¬ ster (SDR, Serial Data Register) und einem 8-Bit-Schieberegister, auf das man allerdings keinen direkten Zugriff hat. Mit dem SPMODE-Bit im Kontrollregister A kann man zwischen Eingang (SPMODE = 0) und Ausgang (SPMODE =1) umschalten. Im Eingabemodus werden die se¬ riellen Daten an der SP-Leitung mit jeder positiven Flanke (Wechsel von Low nach High) der CNT-Leitung in das Schieberegister gescho¬ ben. Nach acht CNT-Impulsen ist das Schieberegister voll, und sein Inhalt wird in das serielle Datenregister übertragen. Gleichzeitig wird das SP-Bit im Interrupt-Kontrollregister (ICR) gesetzt. Treten jetzt wieder CNT-Impulse auf, werden die Daten weiter in das Schiebere¬ gister geschoben, bis dieses erneut voll ist. Hat der Anwender inzwi¬ schen das serielle Datenregister (SDR) gelesen, wird der neue Wert ins SDR kopiert, und die Übertragung läuft nach diesem Schema konti¬ nuierlich weiter. Um den seriellen Port als Ausgang verwenden zu können, setzt man SPMODE auf 1. Die Unterlaufrate von Timer A, der im Continuous- Mode laufen muß, bestimmt die Baudrate (Anzahl der Bits pro Se¬ kunde). Die Daten werden immer mit der halben Unterlaufrate von Timer A aus dem Schieberegister herausgeschoben, wobei die maxi¬ male Ausgaberate ein Viertel der Taktfrequenz des 8520 beträgt. Die Übertragung beginnt, nachdem das erste Daten-Byte in das SDR geschrieben wurde. Das CIA überträgt das Daten-Byte in das Schie¬ beregister. Die einzelnen Daten-Bits erscheinen jetzt mit der halben Unterlaufrate von Timer A an der SP-Leitung, das Taktsignal von Timer A liegt dabei an der Leitung CNT an (sie wechselt mit jedem Unterlauf von Timer A ihren Pegel, mit jeder negative Flanke (Wechsel von High nach Low) erscheint das nächste Bit an der SP- Leitung). Die Übertragung beginnt mit dem MSB des Daten-Bytes. Sind alle acht Bits ausgegeben, bleibt CNT auf High und die SP-Lei¬ tung auf dem Pegel des zuletzt ausgegebenen Bits. Außerdem wird das SP-Bit im Interrupt-Kontrollregister gesetzt, um anzuzeigen, daß das Schieberegister mit neuen Daten versorgt werden kann. Wurde schon 30 Amiga intern vor der Ausgabe des letzten Bits das nächste Daten-Byte in das Daten¬ register geschrieben, wird die Datenausgabe ohne Unterbrechung fort¬ gesetzt. Will man also eine kontinuierliche Übertragung, muß man das serielle Datenregister rechtzeitig mit neuen Daten versorgen. Die SP- und die CNT-Leitung sind als Open-Collector-Ausgänge aus¬ geführt, damit mehrere 8520 über diese Leitungen zusammengeschaltet werden können. Das Interrupt-Kontrollregister (ICR): Lesezugriff (read) = Datenregister Register Name _ D7 D6 PS D« D3 D2 Dl DO 13 $0 ICR IR 0 0 FLAG SP Alarm TB TA Schreibzugriff (write) = Maskenregister Register Name _ D7 D6 D5 D4 D3 D2 Dl DO 13 $0 ICR S/C X X FLAG SP Alarm TB TA Das ICR besteht aus einem Datenregister und einem Maskenregister. Jede der fünf Interrupt-Quellen kann ihr korrespondierendes Bit im Datenregisters setzen. Hier noch einmal alle fünf möglichen Interrupt- Quellen: 1. Unterlauf von Timer A (TA, Bit 0). 2. Unterlauf von Timer B (TB, Bit 1). 3. Übereinstimmung des Werts des Event-Counters mit dem Alarmwert (Alarm, Bit 2). 4. Das Schieberegister des seriellen Ports ist voll (Ein-) oder leer (Ausgabe) (SP, Bit 3). 5. Negative Flanke am FLAG-Eingang (FLAG, Bit 4). Liest man das ICR-Register, erhält man immer den Wert des Datenre¬ gisters, welches dabei gelöscht wird (alle gesetzten Bits inclusive des IR-Bits werden zurückgesetzt)! Benötigt man den Wert des Datenregi¬ sters noch, muß er nach dem Lesen im RAM gespeichert werden. Die Hardware des Amiga 31 Das Maskenregister kann nur beschrieben werden. Sein Wert bestimmt, ob ein gesetztes Bit im Datenregister einen Interrupt auslösen kann. Um einen Interrupt zu ermöglichen, muß das entsprechende Bit des Maskenregisters auf I gesetzt werden. Der 8520 legt, sobald ein Bit sowohl im Daten- als auch im Maskenregister gesetzt ist, die IRQ- Leitung auf 0 (da die IRQ-Leitung 0-aktiv ist, wird damit ein Pro¬ zessor-Interrupt ausgelöst) und setzt das IR-Bit (Bit 7) im Datenregi¬ ster, das damit einen Interrupt auch softwaremäßig signalisiert. Erst wenn das ICR gelesen und damit das Datenregister gelöscht wird, springt die IRQ-Leitung wieder auf I zurück. Das Maskenregister kann allerdings nicht wie eine normale Speicher¬ stelle beschrieben werden. Um ein Bit im Maskenregister zu setzen, muß man das gewünschte Bit und das S/C-Bit (Set/Clear, Bit-Nr. 7) setzen. Alle übrigen Bits bleiben unbeeinflußt. Um ein Bit zu löschen, muß man ebenfalls das gewünschte Bit setzen, nur bleibt diesmal das S/C-Bit auf 0. Das S/C-Bit bestimmt also, ob die gesetzten Bits der eigenen Maske die entsprechenden Bits des Maskenregisters löschen (S/C = 0) oder setzen (S/C = 1). Alle gelöschten Bits in der eigenen Maske haben keine Auswirkung auf die Bits im Maskenregister. Dazu ein Beispiel: Es soll ein Interrupt durch die FLAG-Leitung erlaubt werden. Der aktuelle Wert des Maskenregisters soll 00000011 binär betragen, d.h. beide Timer-Interrupts sind erlaubt. Folgender Wert muß ins Maskenregister geschrieben werden: 10010000 binär (S/C = 1, d.h. das gesetzte Bit, in diesem Fall das FLAG-Bit (Bit-Nr. 5) setzt das Bit im Maskenregister). Danach hat es folgenden Inhalt 00010011 . Will man jetzt die beiden Timer-Interrupts verbieten, schreibt man folgenden Wert 00000011 (S/C = 0, d.h. die gesetzten Bits (TA und TB, Bit-Nr. 0 und 1) werden im Maskenregister gelöscht). Jetzt enthält das Maskenregister 00010000, und nur noch der FLAG-Interrupt ist erlaubt. Die Einbindung der CiAs in das Amiga-System Wie eingangs erwähnt, besitzt der Amiga zwei CIAs vom Typ 8520. Die Basisadresse des ersten 8520, kurz 8520-A genannt, ist SBFEOOl. 32 Amiga intern Die Register liegen aber nicht durchgehend im Adreßraum, sondern mit Abständen von je 256 Bytes. D.h. alle Register des 8520-A liegen auf ungeraden Adressen, da der 8520-A mit den unteren 8 Leitungen des Prozessordatenbus verbunden ist (DO-7). Folgende Tabelle listet die Adressen der einzelnen Register mit ihrer Verwendung im Amiga auf (näheres über die Bedeutung der einzelnen Port-Bits im Kapitel Schnittstellen): CIA-Ä: Registeradressen Adran« Name DT D6 D5 D4 PS D2 Dl DO IBFEOOI PRA IBFEIOI PRB IBFEWI DDRA tBFESOl DDRB $BFE401 TALO $BFE601 TAHI tBFE601 TBLO IBFETOl TBHI tBFESOl E. LSB tBFEMl E. 8-lS tBFEAOl tBFEBOl tBFECOl tBFEEOl iBFEFOl /FIRl /Visa /RDY /TKO /WPRO /CHNG /LED OVL Centronici-Parallel-Port 00000011 Je nach Anwendung ale Eingang oder Ausgang Timer A wird vom Betriebssystem sur Kommunikation mit der Tastatur verwendet. Timer B wird vom B. für verschiedene Aufgaben benötigt Der Event-Counter im CLA-A s&hlt die 50 Hs Impulse vom Netsteil (sog. Ticks), die E. MSB von der Netsfrequens abgeleitet werden. SP Empfang der Tasten-Codes von der Tastatur ICR Interrupt-KontroIIregister CRA KontroIIregister A CRB KontroIIregister B Das zweite CIA, CIA-B, wird ab der Adresse SBFDOOO angesprochen. Seine Register liegen auf den geraden Adressen, da der Datenbus des CIA-B mit der oberen Hälfte des Prozessor-Datenbus verbunden ist. CIA-B: Registeradressen Adraiie Name $BFDOOO PRA $BFD100 PRB $BFD200 DDRA tBFDSOO DDRB $BFD400 TALO $BFD500 TAHI tBFDOOO TBLO $BFD700 TBHI $BFD800 E. LSB $BFDd00 E. 8-16 tBFDAOO E. MSB $BFDB00 SP $BFDC00 ICR $BFDE00 CRA tBFDFOO CRB DT D6 D5 D4 D3 D2 Dl DO /DTR /RTS /CD /CTS /DSR SEL POUT BUSY /MTR /SEL3 /SEL2 /SELl /SELO /SIDE DIR /STEP 11000000 11111111 Timer A wird nur bei seriellem Datentransfer benutst. Ansonsten ist er frei. Timer B dient sur Synchronisierung d. Blitters mit dem Bildschirm. Ansonsten ist er frei. Der Event-Counter in CIA-B sShlt die hori- lontalen Sync-Impulse. Im Normalfall sind dies 16626 pro Sekunde (PAL-Zeilenfrequens). Unbenutst Interrupt-Kontrollregister KontroIIregister A KontroIIregister B Die Hardware des Amiga 33 Die Adressen $BFDOOO für CIA-B und SBFEOOl für CIA-A sind die von Commodore angegebenen Basisadressen der CIAs. Bei genauer Untersuchung des Schaltplans stellte sich jedoch heraus, daß die bei¬ den CIAs im gesamten Bereich von AOxxxx bis BFxxxx adressiert werden. Die Wahl zwischen den beiden CIAs treffen die Adreßleitun¬ gen A12 und A13. CIA-A wird selektiert, wenn A12 = 0 ist und CIA- B bei Al3 = 0, immer vorausgesetzt, die Adressen bewegen sich zwi¬ schen AOxxxx und BFxxxx. Da der Datenbus des CIA-A mit den Prozessordatenbus-Leitungen DO-7 (ungerade Adressen) verbunden ist und CIA-B mit D8-15 (gerade Adressen), können beide gleichzeitig in einem Wortzugriff angesprochen werden, wenn A12 und A13 gleich 0 sind. MOVE.W $BF0000,D0 lädt die PA-Register beider CIAs in DO, die unteren 8 Bits in DO enthalten dann den Inhalt des PA-Register von CIA-A und die Bits 9-15 den des PA-Registers von CIA-B. Man kann folgendes Schema für die Adressierung der CIAs aufstellen; CIA-A wird bei folgender Adresse selektiert (binär); lOIx xxxx xxxO rrrr xxxx xxxl und CIA-B bei; lOIx xxxx xxOx rrrr xxxx xxxO Die vier mit rrrr bezeichneten Bits wählen das entsprechende Register aus. Dies gilt allerdings nur für den Al000 mit hundertprozentiger Gewißheit. Es kann durchaus möglich sein, das sich dies bei den neueren Amiga-Modellen geändert hat. Wer sichergehen will, sollte die von Commodore empfohlenen Adressen benutzen. (CIA-A bei SBFEOOl und CIA-B bei SBFDOOO) Folgende Liste stellt die Belegung der verschiedenen Signalleitungen der CIAs im Amiga dar; CIA-A /IRQ /INT2-Eingang von Paula /RES System-Resetleitung D0-D7 Protessordatenbus Bits 0-7 A0-A3 Prosessoradreßbus Bits 8-11 Phi2 E-Takt des Prosessors R/W Prosessor R/W PA7 Game-Port 1 Pin 6 (Feuerknopf) PA6 Game-Port 0 Pin 6 (Feuerknopf) 34 Amiga intern PA5 PA4 PAS PAS PAl PAO SP CNT PBO-PB7 PC FLAG CIA-B /IRQ /RES DO-D7 AO-AS PhiS R/W PA7 PA6 PA6 PA4 PAS PAS PAl PAO SP CNT PB7 PB6 PBB PB4 PB3 PB2 PBl PBO FLAG PC /RDY "di»k ready“ Signal vom Diekettenlaufwerk /TKO "diak track 00" Signal vom Diskettenlaufwerk /WPRO "write protect" Signal vom Diskettenlaufwerk /CHNG "disk change" Signal vom Diskettenlaufwerk LED Steuerung der Power-LED (0 = hell, 1 = dunkel) OVL memory overlay bit (nicht verändern!) KDAT serielle Daten von der Tastatur KCLK Taktfrequens für die Tastaturdaten Cen t ronics - Port - D atenleitungen /drdy Centronics-Handshake-Signal: Daten bereit /ack Centronics-Handshake-Signal: Daten übernommen /INT6-Eingang von Paula System-Resetleitung Prosessordatenbua Bits 8-IS Prosessoradresabus Bits 8-11 E-Takt des Prosessors Prosessor R/W /DTR Serielle Schnittstelle, /DTR-Signal /RTS Serielle Schnittstelle, /RTS-Signal /CD Serielle Schnittstelle, /CD-Signal /CTS Serielle Schnittstelle, /CTS-Signal /DSR Serielle Schnittstelle, /DSR-Signal SEL "select"-Signal tur Centronics-Schnittstelle POUT "paper out"-Signal von der Centronics-S. BUSY "busy'-Signal von der Centronics-S. BUSY Direkt mit PAO verbunden POUT Direkt mit PAl verbunden /MTR “motor" Signal sum Diskettenlaufwerk /SEL3 "drive eelect" für Laufwerk Nr. 3 /SEL2 "drive select" für Laufwerk Nr. 2 /SELl "drive select’ für Laufwerk Nr. 1 /SELO "drive select" für Laufwerk Nr. 0 (intern) /SIDE "side select’-Signal sum Diekettenlaufwerk DIR "direction’-Signal sum Diskettenlaufwerk /STEP "step’-Signal sum Diskettenlaufwerk /INDEX "Index"-Signal vom Diskettenlaufwerk Nicht benutst 1.2.3 Die Custom-Chips und ihre Einbindung in die Amiga-Hardware Die Chips, von denen wir bis jetzt gehört haben, waren eher banal. Auch der 68000 ist doch trotz seiner unbestrittenen Leistungsfähigkeit heute nur noch ein Standard-Chip, das für ein paar Mark im Elektro¬ nikladen um die Ecke zu haben ist. Obwohl auch die^Fähigkeiten des Amiga letztlich von der Geschwindigkeit des Prozessors abhängen, fallen dem staunenden Amiga-Bewunderer doch erst einmal andere Dinge ins Auge. Denn wenn der Computer Grafiken in Fernsehquali¬ tät auf den Bildschirm zaubert und dazu noch Beethovens Neunte so aus den Lautsprechern kommt, daß der staunende Freak unwillkürlich den versteckten CD-Player sucht, dann erregt er die Aufmerksamkeit. Die Hardware des Amiga 35 Ob der Computer dann auch noch sämtliche Primzahlen in Bruchteilen von Sekunden errechnet oder ob er kaum schneller als der alte Ta¬ schenrechner ist, interessiert die möglichen Käufer eines Computers wie der Amiga gewöhnlich erst hinterher. Auch die Entwickler des Amiga richteten sich danach und statteten ihn mit einer für Computer dieser Preisklasse nie gekannten Fülle von grafischen und akustischen Fähigkeiten aus, die der Amiga-Hardware einen geheimnisvollen Flair verliehen. Dies wurde noch dadurch ver¬ stärkt, daß anfangs alle möglichen und unmöglichen Gerüchte über seine Fähigkeiten kursierten. Das Ziel dieses Kapitels ist es, die Hardware, die für die fantastischen Sound- und Grafikmöglichkeiten des Amiga verantwortlich ist, allge¬ meinverständlich zu erklären und damit dem Leser eine fundierte Ba¬ sis für die perfekte Programmierung des Amiga zu geben. Grundlage aller oben erwähnten Fähigkeiten sind lediglich drei Chips. Sie wurden eigens für den Amiga entwickelt und tragen daher die Bezeichnung Custom-Chips. (Custom-Chips nennt man integrierte Schaltkreise, die von einer Halbleiterfirma eigens für ein bestimmtes Gerät oder einen bestimmten Kunden nach dessen Angaben entwickelt wurden, die deutsche Bezeichnung dafür lautet "Kundenspezifische Schaltkreise".) Ihre Typenbezeichnungen sind 8361, 8362 und 8364, allerdings waren den Amiga-Entwicklern diese Nummern zu farblos, und so gaben sie den drei Chips die Namen Agnus, Denise und Paula (Im deutschen Amiga steckt allerdings eine an die PAL-Fernsehnorm angepaßte Agnus-Version mit der Typennummer 8367). Diese Custom-Chips übernehmen die Aufgaben der Tonerzeugung, der Bildschirmdarstellung, des prozessorunabhängigen Diskettenzugriffs und vieles mehr. Allerdings sind diese Aufgaben nicht so auf die ein¬ zelnen Chips verteilt, daß eines die gesamte Tonerzeugung, ein anderes die Grafik und ein weiteres den Diskettenzugriff übernimmt, wie das bei den meisten Konkurrenzprodukten der Fall ist, sondern die Auf¬ gaben sind meistens über mehrere Chips verteilt, so wird z.B. die Grafikdarstellung von zwei Chips zusammen erledigt. Bei einer solchen engen Zusammenarbeit hätte man die drei Chips auch zu einem einzigen zusammenfassen können, aber die Herstellung eines solchen komplexen Schaltkreises wäre dann teurer gekommen als die Herstellung der drei einzelnen. 36 Amiga intern Bevor wir jetzt auf die Funktionen von Agnus, Denise und Paula im einzelnen eingehen, erst einmal eine kleine Einführung in den Aufbau des Amiga. 1.2.3.1 Der Grundaufbau des Amiga 0 sind Leitungen von und zu den verschiedenen Schnittstellen Qep Gpundaufbdu des Anise Abb. 1.2.3 Ein einfaches Computersystem besteht normalerweise aus einem Pro¬ zessor, dem ROM mit dem Betriebssystem, einer gewissen Menge RAM und mindestens einem Peripheriebaustein zur Daten-Ein- und Ausgabe. Sämtliche Bausteine sind mit dem Adreßbus und dem Daten¬ bus verbunden. Der Prozessor kontrolliert das System, nur er kann Adressen auf den Bus legen und damit Daten an bestimmte System¬ komponenten, z.B. das RAM, ausgeben oder von ihnen einiesen. Er kontrolliert auch die Bussteuersignale wie z.B^ die R/W-Leitung. (Sie sind der Einfachheit halber in Abbildung 1.2.3 nicht eingezeichnet; die einzelnen Bussteuersignale des 68000 sind in Kapitel 1.2.1 aufgeführt.) Auch enthält jedes Computersystem noch Steuerschaltungen wie z.B. einen Adreßdecoder, der bei bestimmten Werten auf dem Adreßbus die diesen Adressen zugewiesenen Bausteine aktiviert. Die Hardware des Amiga 37 Doch zurück zum Amiga. Wie man aus der Abbildung 1.2.3 ersehen kann, weicht der Aufbau des Amiga doch etwas vom oben beschriebe¬ nen ab. Auf der linken Seite sehen wir den 68000-Mikroprozessor, dessen Daten- und Adreßleitungen direkt mit den beiden CIAs vom Typ 8520 und dem Kickstart-ROM verbunden sind. Dieser Teil des Amiga-Systems ist also konventionell aufgebaut, nur der Prozessor hat Zugriff auf die beiden CIAs und das ROM. Wie sieht es nun auf der rechten Seite aus? Hier befinden sich die drei Custom-Chips Agnus, Denise und Paula und das Chip-RAM, die alle direkt mit demselben Datenbus verbunden sind, welcher aber vom Prozessordatenbus durch einen Puffer getrennt ist, der wahlweise den Prozessordatenbus mit dem Chip-Datenbus verbinden oder von ihm trennen kann. Die drei Custom-Chips sind untereinander auch noch durch den Regi¬ steradreßbus verbunden, der wie der Datenbus wahlweise mit dem Prozessoradreßbus verbunden werden kann oder nicht. Da das Chip-RAM einen weitaus größeren Adreßbereich hat als die Custom-Chips und außerdem gemultiplexte Adressen benötigt, exi¬ stiert noch ein Chip-RAM-Adreßbus. Für diejenigen, denen der Be¬ griff "gemultiplexte Adressen" nichts sagt, nur soviel: die RAM-Chips, die im Amiga (AlOOO) verwendet werden, haben einen Adreßbereich von 2^® Adressen (65536). Um alle Adressen eines Chips ansprechen zu können, benötigt man also 16 Adreßleitungen. Da die eigentlichen Chips aber sehr klein sind, hätte eine solch große Anzahl von Adre߬ leitungen ein unverhältnismäßig großes Gehäuse zur Folge. Um dieses Problem zu beseitigen, hat man eine sogenannte gemultiplexte Adres¬ sierung eingeführt. Das Gehäuse hat dabei lediglich acht Adreßleitun¬ gen, auf denen hintereinander erst die oberen acht Adreß-Bits und danach die unteren acht übertragen werden. Das Chip speichert die oberen acht und hat dann, wenn die unteren an den Adreßleitungen anliegen, alle 16 Adreß-Bits, die es benötigt. Wieder zum Amiga: Warum diese Trennung der beiden Busse? Nun, der Grund dafür ist, daß die verschiedenen Ein-/Ausgabegeräte eine kontinuierliche Versorgung mit Daten benötigen. So müssen z.B. die Daten für die einzelnen Bildpunkte auf dem Monitor fünfzigmal in der Sekunde aus dem RAM gelesen werden, da ein Fernsehbild nach der deutschen Pal-Fernsehnorm fünzigmal in der Sekunde neu aufge¬ baut wird. Eine hochauflösende Grafik kann auf dem Amiga über 64 KB Bild¬ schirmspeicher benötigen. Dies bedeutet, daß pro Sekunde 50 * 64 KB aus dem Speicher geholt werden müssen. Dies sind etwas über 1.5 38 Amiga intern Millionen Speicherzugriffe in jeder Sekunde! Müßte der Prozessor diese Aufgabe erledigen, wäre er hoffnungslos überfordert. Eine so hohe Datenrate schafft auch ein 68000 nicht. Und dabei ist der Amiga auch noch in der Lage, neben der Grafik digitalisierte Tonausgabe und Diskettenzugriffe durchzuführen, ohne den 68000 zu verwenden. Die Lösung liegt in einem zweiten Prozessor, der all diese Speicher¬ zugriffe selbständig ausführt. Einen solchen Prozessor nennt man auch DMA-Controller (Direct Memory Access/Direkter Speicherzugriff). Er ist beim Amiga in Agnus untergebracht. Aus diesem Grund ist Agnus auch mit dem Chip-RAM-Adreßbus verbunden. Die übrigen beiden Chips, Denise und Paula, und der Rest von Agnus sind wie herkömmliche Peripherie-Chips aufgebaut. Sie besitzen eine gewisse Anzahl von Registern, die vom Prozessor (oder dem DMA- Controller) gelesen oder beschrieben werden können. Die einzelnen Register werden über den Registeradreßbus ausgewählt. Er hat acht Leitungen, kann also 256 verschiedene Zustände annehmen. Es gibt keine spezielle Auswahl der einzelnen Chips. Hat der Adreßbus den Wert 255 oder $FF, sind also alle Leitungen High, ist kein Register ausgewählt. Liegt aber eine gültige Registernummer an, erkennt dies das Chip, welches das gewählte Register enthält, und aktiviert dieses. Diese Aufgabe wird in den einzelnen Chips von dem Register-Adreß- decoder ausgeführt. Dadurch, daß die Auswahl eines Registers nur von seiner Registeradresse abhängt und nicht von dem Chip, in dem es sich befindet, können auch zwei Register in zwei verschiedenen Chips gleichzeitig mit demselben Wert beschrieben werden, wenn diese auch dieselbe Registeradresse haben. Von dieser Möglichkeit wird bei eini¬ gen Registern Gebrauch gemacht, die Daten enthalten, die von meh¬ reren Chips benötigt werden. Jedes Chip-Register kann entweder ein Leseregister oder ein Schreibregister sein. Eine Umschaltung zwischen Lesen und Schreiben mittels einer speziellen R/W-Leitung, wie z.B. beim 8520, gibt es nicht. Die Registeradresse bestimmt gleichzeitig, ob ein Lese- oder Schreibzugriff stattfindet. Register, die sich sowohl lesen als auch be¬ schreiben lassen, sind so realisiert, daß der Schreibzugriff auf einer und der Lesezugriff auf einer anderen Registeradresse stattfindet. Dieser Sachverhalt spiegelt sich deutliclLin der Liste der Chip-Regi¬ ster wieder (siehe Kapitel 1.5.1). Da Agnus den DMA-Controller enthält, kann es auch selber auf Re¬ gister der Custom-Chips zugreifen, d.h. eine Adresse auf dem Regi¬ steradreßbus ausgeben. Die Hardware des Amiga 39 Ein offensichtliches Problem ist aber noch ungelöst. Es gibt nur einen Daten- und einen Adreßbus, auf den sowohl der Prozessor als auch der DMA-Controller zugreifen wollen. Einen Bus kann aber immer nur ein Bus-Controller steuern. Würden zwei Chips gleichzeitig ver¬ suchen, eine Adresse auf den Bus zu legen, würde es zu einer Daten¬ kollision, gefolgt von einem Systemzusammenbruch, kommen. Also müssen sie sich die Buszugriffe teilen und dürfen nur noch abwech¬ selnd auf den Bus zugreifen, wobei natürlich jeder möglichst oft den Bus für sich haben möchte. Dieses Problem wurde beim Amiga auf elegante Art in drei Stufen gelöst: Als erstes hat man im Amiga die beiden normalerweise durchgehenden Busse in zwei Teile geteilt. Der eine (in der Abbildung links) verbin¬ det all die Bausteine, die nur vom Prozessor angesprochen werden. Bei einem Zugriff des 68000 auf einen dieser Bausteine unterbrechen die beiden Puffer (in der Mitte der Abbildung) die Verbindungen des Prozessordaten- und des Prozessoradreßbus mit dem Chip-Daten- und Chip-Adreßbus. Dadurch können sowohl der Prozessor auf seiner Seite und Agnus auf der anderen ungestört auf den Bus zugreifen. Der Prozessor hat dadurch einen ungestörten Zugriff auf das Betriebssy¬ stem und eventuelle RAM-Erweiterungen, die am Expansion-Port angeschlossen werden. Dieses Erweiterungs-RAM wird deshalb auch Fast RAM genannt, da der Prozessor immer ohne Geschwindig¬ keitsverlust darauf zugreifen kann. (Die RAM-Erweiterungen, die beim Al000 vorne und beim A500 an der Gehäuseunterseite einge¬ steckt werden, gehören allerdings noch zum Chip-RAM.) Als zweites hat man die Buszugriffe vom Prozessor und von Agnus ineinander verschachtelt, so daß normalerweise auch bei Zugriffen auf das Chip-RAM oder die Chip-Register der 68000 nicht gebremst wer¬ den muß. Bei einem solchen Zugriff verbinden die Puffer die beiden Bussysteme wieder. Erst als dritter und letzter Ausweg werden die Zugriffe des Prozessors verzögert, d.h. der Prozessor muß warten, bis Agnus seine DMA-Zu- griffe beendet hat und der Bus wieder frei ist. Dieser Fall tritt aller¬ dings nur dann ein, wenn entweder sehr hohe Grafikauflösungen ge¬ wählt wurden oder der Blitter in Betrieb war. Näheres dazu aber spä¬ ter. Jetzt soll erst einmal der interne Aufbau der drei Custom-Chips erläutert werden. 40 Amiga intern 1.2.3.2 Der Aufbau von Agnus Die Piribeleguong von Agnus INT3 DMA Ii BliS DBR ARM RGA8 RGA7 RGA6 RGA5 RGA4 RGA3 RGA2 RGAl •4—► D9 •4—► Die •4-^ Dil ■4-^ D12 •4-^ D13 ■ 4 ^ D14 •4—► D15 GNP ■4—► HSV CSV •4—► VSY LP PRA8 DRA7 PRA6 DRA5 PRA4 PRA3 PRA2 i^- DRAl —^ DRAB ■ 4 ^ GNP CCKQ 4^ CCK Anner^kunaren : Die Pfeile kennzeichnen die Si g na 1 r> i ch tuns. Ein Seriell über den SignalnaMen bedeutet» daß das Signal Lom—A ktiv ist, CB—Aktiv) « Abb. 1.2.3.1 Die Hardware des Amiga 41 Abb. 1.2.3.2 Wie schon oben erwähnt, enthält Agnus die gesamte DMA-Steuerung. Für jede der 6 möglichen DMA-Quellen existiert eine eigene Kon- trollogik. Sie sind alle sowohl mit dem Chip-RAM-Adreßgenerator als auch dem Registeradreßgenerator verbunden. Diese Adreßgeneratoren erzeugen die RAM-Adresse der gewünschten Chip-RAM-Speicher- stelle und die Registeradresse des Zielregisters. Auf diese Weise ver¬ sorgen die DMA-Logiken die entsprechenden Chip-Register mit Daten aus dem RAM oder schreiben den Inhalt bestimmter Register ins RAM. Verbunden mit dem Chip-RAM-Adreßgenerator ist auch noch ein Refreshzähler, der die für den Betrieb der dynamischen RAM-Chips notwendigen Refresh-Signale erzeugt. Agnus steuert auch den zeitlichen Ablauf der einzelnen DMA-Zu- griffe. Als Grundlage dazu dient eine Bildschirmzeile. In jeder Bild¬ schirmzeile finden 225 Speicherzugriffe statt, die von Agnus auf die einzelnen DMA-Kanäle und den 68000 verteilt werden. Da Agnus dafür immer die aktuelle Zeilen- und Spaltenposition benötigt, enthält es auch den Rasterzeilen- und Spaltenzähler. Diese Zähler für die Strahlposition erzeugen auch die horizontalen und vertikalen Synchro¬ nisationssignale, die dem angeschlossenen Monitor den Anfang einer neuen Zeile (H-Sync) und den eines neuen Bildes (V-Sync) signalisie¬ ren. Die horizontalen und vertikalen Synchronsignale können auch von außen in Agnus eingespeist werden und steuern dann die internen 42 Amiga intern Rasterzeilen- und Spaltenzähler. Dadurch kann das Videobild des Amiga mit einem anderen, z.B. von einem Videorecorder, synchroni¬ siert werden. Dieses sogenannte Genlock kann dadurch beim Amiga einfach realisiert werden. (Das Synchronisieren zweier Videobilder be¬ deutet, vereinfacht gesagt, daß die einzelnen Rasterzeilen und die einzelnen Bilder beider Signale gemeinsam anfangen.) Zwei weitere wichtige Elemente von Agnus sind der Blitter und der Coprozessor Copper. Der Blitter ist eine spezielle Schaltung, die in der Lage ist, Speicherbereiche zu manipulieren oder zu verschieben. Er kann dadurch den 68000 entlasten, da er diese Aufgaben weitaus schneller ausführen kann. Außerdem ist der Blitter noch in der Lage, selbständig Linien zu zeichnen und Flächen zu füllen. Der Copper ist ein einfacher Coprozessor. Seine Programme, die sogenannten Copper- Listen, enthalten nur drei verschiedene Befehle. Der Copper ist in der Lage, die verschiedenen Chip-Register zu festgelegten Zeitpunkten beliebig zu verändern. Genauere Informationen über den Blitter und den Copper finden sich in den Kapiteln über die Programmierung der Custom-Chips. Hier noch eine Funktionsbeschreibung der einzelnen Pins: Datenbus: D0-D15 Die 16 Datenleitungen sind direkt mit dem Chip-RAM-Datenbus ver¬ bunden. Intern hängen nach einem Puffer sämtliche Chip-Register an dem Bus. Registeradreßbus: RGA0-RGA8 (ReGisterAdreß) Der Registeradreßbus von Agnus ist bidirektional. Bei einem DMA- Zugriff legt der Registeradreßgenerator die gewünschte Regi¬ steradresse auf diese Busleitungen. Greift der Prozessor dagegen auf die Chip-Register zu, wirken diese Leitungen als Eingänge, und die vom Prozessor gewählte Registeradresse gelangt zum Registeradreßde¬ koder innerhalb von Agnus. Allgemein gilt, daß bei einem Wert von $FF auf dem Registeradreßbus, d.h. alle Leitungen sind auf High, kein Register ausgewählt wurde. Die Adreßleitungen für das dynamische RAM: DRA0-DRA8 (Dynamic RAM Adress/Dynamische RAM-Adresse) Diese Adreßleitungen sind mit dem Chip-RAM-Adreßbus verbunden. Sie sind reine Ausgänge und werden von Agnus immer dann aktiviert. Die Hardware des Amiga 43 wenn es einen DMA-Zugriff auf das Chip-RAM ausführen will. Die Adressen auf diesen Pins sind schon gemultiplext und können direkt mit den Adreßleitungen dynamischer 32-K-Bit-RAM verbunden wer¬ den (Typ 41256). Dies ist beim Amiga 500 und 2000 der Fall. Beim älteren AlOOO besitzen die RAM nur 8 Adreßleitungen. Die neunte DRA-Leitung von Agnus wird wieder demultiplext und zur Um¬ schaltung zwischen verschiedenen RAM-Banks verwendet. Die Taktleitungen des Agnus CCK und CCKQ: (Color Clock und Color Clock Delay) Diese beiden Leitungen sind die einzigen Taktleitungen des Amiga. Die Frequenz beider Signale beträgt 3.58 MHz, das ist die halbe Pro¬ zessor-Taktfrequenz. Das Signal CCKQ ist zu dem Signal CCK um einen viertel Taktzyklus (90 Grad) verzögert. Nach diesen beiden Si¬ gnalen richtet sich die gesamte Zeitsteuerung (das Timing) des Agnus. Die Bussteuerungssignale von Agnus: BLS, ARW, DBR Diese drei Leitungen sind mit der Steuerlogik des Amiga verbunden. Mit der Leitung DBR (Data Bus Request/Datenbusanforderung) teilt Agnus dieser Steuerlogik mit, daß er den Bus im nächsten Buszyklus übernehmen wird. Diese Leitung hat immer Vorrang vor einer Busan¬ frage des Prozessors. Benötigt Agnus den Bus in mehreren aufeinan¬ derfolgenden Buszyklen, muß der 68000 eben warten. Die Leitung ARW (Agnus RAM Write) signalisiert der Steuerlogik, daß Agnus einen Schreibzugriff auf das Chip-RAM ausführen will. Das BLS-Signal (Blitter Slow Down = Blitter verlangsamen) signalisiert Agnus, das der Prozessor schon seit drei Buszyklen auf einen Zugriff wartet. Je nach dem internen Zustand überläßt Agnus dem Prozessor dann den Bus für einen Zyklus. Genaueres über die verschiedenen Buszyklen findet sich im Kapitel über Programmierung der Custom- Chips. Die Steuersignale: RES, INT3, DM AL Das RES-Signal (Reset) ist direkt mit der Reset-Leitung des Prozes¬ sors verbunden und setzt Agnus in einen definierten Grundzustand zurück. Die INT3-Leitung (Interrupt der Ebene 3) ist ein Ausgang und direkt mit der gleichnamigen Leitung von Paula verbunden. Agnus signali- 44 Amiga intern siert damit der Interrupt-Logik von Paula, daß eine Komponente von Agnus einen Interrupt ausgelöst hat. Die DMAL-Leitung (DMA Request Line = DMA-Anforderung) ver¬ bindet ebenfalls Agnus mit Paula. Nur diesmal in umgekehrter Rich¬ tung. Paula signalisiert damit, daß Agnus einen DMA-Transfer einlei¬ ten soll. Die Leitungen: HSY, VSY, CSY und LP Im Normalfall erscheinen an den Leitungen HSY (Horizontal Sync/Horizontales Synchronisationssignal) und VSY (Vertical Sync/Vertikales Synchronisationssignal) die Synchronsignale für den angeschlossenen Monitor. Das Signal an der Leitung CSY (Composite Sync/Kombiniertes Synchronsignal) ist ein Summensignal aus HSY und VSY und dient sowohl dem Anschluß von Monitoren, die ein ge¬ mischtes Synchronsignal benötigen, als auch der Schaltung, die das Vi¬ deosignal erzeugt, dem Videomixer. Die LP-Leitung (Light Pen) ist ein Eingang und erlaubt den Anschluß eines Lichtgriffels oder Lightpens. Bei einer negativen Flanke an die¬ sem Pin wird der Inhalt des Rasterzählerregisters gespeichert, (siehe Kapitel 1.5.2) Die HSY- und VSY-Leitungen können auch als Eingang arbeiten und erlauben dann eine externe Synchronisation von Agnus (Genlock). Die Hardware des Amiga 45 1.2.3.3 Der Aufbau von Denise Abb. 1.2.3.3 46 Amiga intern Abb. 1.2.3,4 Die Funktion von Denise kann man ganz allgemein mit Bilderzeugung umschreiben. Der erste Teil dieser Arbeit wird allerdings schon von Agnus erledigt. Dieser holt die aktuellen Grafikdaten aus dem Chip- RAM und schreibt sie in die für die Bit-Ebenen zuständigen Register von Denise. Ebenso verfährt Agnus mit den Spritedaten. Denise ent¬ hält also immer sämtliche Grafik und Spritedaten für 16 Punkte, da immer ein Bit einem Punkt auf dem Bildschirm entspricht und die Datenregister alle eine Breite von einem Wort, also 16 Bit, haben. Diese Daten müssen von Denise in die entsprechende RGB-Darstellung umgewandelt werden. Als erstes weden die Grafikdaten von einer parallelen 16-Bit-Darstellung mittels des Bit-Ebenen-Sequenzers in einen seriellen Datenstrom umgewandelt. Da maximal 6 Bit-Ebenen möglich sind, gibt es diesen Funktionsblock ebenfalls 6mal. Die seri¬ ellen Datenströme der einzelnen Bit-Ebenen-Sequenzer werden jetzt zu einem maximal 6 Bit breiten Datenstrom zusammengefaßt. Die Prioritäts-Kontrollogik wählt aus den Grafikdaten von den Bit- Ebenen-Sequenzern und den Spritedaten aus den Sprite-Sequenzern anhand der eingestellten Prioritäten die für den aktuellen Punkt gülti¬ gen Daten aus. Nach diesen Daten selektiert die Farbdecodierung eines der 32 Farbregister. Der Wert dieses Farbregisters wird dann als digi¬ tales RGB-Signal ausgegeben. Wenn der Hold-And-Modify (HAM)- oder der Extra-Half-Bright-(EHB)-Modus gewählt wurde, werden die Die Hardware des Amiga 47 Daten aus dem Farbregister noch dementsprechend modifiziert, bevor sie das Chip verlassen. Die Daten von den Sequenzern gelangen auch noch zu der Kol- lisionskontrolllogik, die, wie ihr Name schon sagt, die Daten auf eine Kollision zwischen den Bit-Ebenen und den Sprites überprüft und das Ergebnis dieser Prüfung in dem Kollisionsregister ablegt. Die letzte Funktion von Denise hat nichts mit der Bildschirm¬ darstellung zu tun. Denise enthält auch noch die Maus-Zähler, die die aktuellen X- und Y-Positionen der angeschlossenen Mäuse enthalten. Hier die Funktionsbeschreibung aller Pins von Denise: Der Datenbus: D0-DI5 Die 16 Datenbusleitungen sind wie die von Agnus mit dem Chip-Da¬ tenbus verbunden. Registeradreßbus: RGAI-RGA8 Der Registeradreßbus ist bei Denise ein reiner Eingang. Mit Hilfe des Werts am Registeradreßbus wählt der Register-Adreßdecoder das ent¬ sprechende interne Register aus. Die Takteingänge: CCK und 7M Die Timing von Denise richtet sich nach dem CCK-Signal. Der CCK- Pin ist mit der gleichnamigen Leitung von Agnus verbunden. Das Taktsignal auf der 7M-Leitung (7 Megahertz) hat eine Frequenz von 7.15909 MHz. Der Denise-Chip benötigt diese zusätzliche Frequenz für die Ausgabe der einzelnen Bildpunkte, da die Punktfrequenz über den 3.58 MHz des CCK-Signals liegt. Ein Punkt im niedrig auflösen¬ den Modus (320 Punkte/Zeile) hat genau die Dauer eines 7M-Taktzy- klus. Im hochauflösenden Modus (640 Punkte/Zeile) werden in jedem 7M-Takt zwei Punkte ausgegeben. Einer mit jeder Flanke von 7M. Der 7M-Takt ist übrigens auch der Takt des 68000. Er ist mit dessen Clock-Eingang verbunden. Die Ausgangssignale: RO-3, GO-3, BO-3, ZD und BURST Die Leitungen RO-3, GO-3 und BO-3 stellen das RGB-Ausgangssignal von Denise dar. Denise gibt den entsprechenden RGB-Wert digital aus. Dabei wird jede der drei Farbkomponenten mit Hilfe von vier 48 Amiga intern Bits dargestellt. Das macht 16 Werte pro Komponente und 16* 16* 16 Farben (4096) insgesamt. Die drei Farbsignale laufen, nachdem sie Denise verlassen haben, erst über einen Puffer und werden dann mit¬ tels dreier Digital-/Analog-Wandler in ein RGB-Analogsignal verwan¬ delt, das dann am RGB-Port zur Verfügung steht. Ein zusätzlicher Videomischer macht aus diesem RGB-Analogsignal das Videosignal für den Videoanschluß. Er benötigt dazu auch noch das BURST-Signal von Denise. Das BURST-Signal ist eine Schwin¬ gung mit der Frequenz von CCK, also 3.58 MHz. Näheres über die Funktion des Colorbursts entnehmen Sie am besten einem Buch über Fernsehtechnik. Das letzte Ausgangssignal von Denise ist das ZD-Signal (Zero Detect oder auch Background Indicator/Hintergrund-Erkennung). Es ist im¬ mer dann Low, wenn ein Punkt in der Hintergrundfarbe dargestellt wird, d.h. seine Farbe aus dem Farbregister Nummer 0 stammt. Dieses Signal wird in einem sogenannten Genlock-Adapter benutzt und dient in diesem zur Umschaltung zwischen dem externen Video-Signal, wenn ZD = 0, und dem Videosignal des Amiga, wenn ZD j 1 ist. Das ZD-Signal liegt ebenfalls am RGB-Port an. Näheres dazu im Kapitel Schnittstellen. Die Maus-/Joystick-Eingänge: MOH, MIH, MOV, MIV Diese vier Leitungen entsprechen direkt den Mauseingängen der bei¬ den Game-Ports (oder Joystick-Buchsen). Da der Amiga aber zwei Game-Ports hat, müßten es eigentlich acht Eingänge sein. Anschei¬ nend waren aber nur vier Pins von Denise frei, als man diesen ent¬ wickelt hat, und so wurde bei Commodore folgender Weg gewählt: Die acht Eingangsleitungen der beiden Game-Ports gelangen auf einen Umschalter, der wahlweise die vier Leitungen des vorderen oder des hinteren Ports auf die vier Eingänge von Denise schaltet. Diese Um¬ schaltung geschieht synchron zum Takt von Denise, so daß dieser die vier Leitungen intern wieder auf zwei Register aufteilen kann. Eines für jeden Game-Port. Näheres über die Game-Ports siehe Kapitel Schnittstellen. 01 C Ql Die Hardware des Amiga 49 1.2.3.4 Der Aufbau von Paula Die Pinbelegung von Paula Da 1 V y «a 2 47 □ ^-♦‘Dia D6 -4-^C 3 43 □ 4-^-Dll OS 4 45 □ ^-►D12 D4 s 44 □ ^-►F13 6 43 □ ^-►I>14 03 ♦♦C mt 42 QND O IL .. «i 3 RXD Dl -4-^C 3 ’i'P et< 49 3 TXD Dfl .a^r 1 a ^ 39 □ ^^DKME RES — 11 ^ 1 A 39 □ DKMD DKtAL-4—C 12 NV 37 □ DKRD IFLe-^—C 13 a AA 36 □ 4^F0T1Y C 14 IT m 35 34^F0T1X IFL2'^—C IS A iÄ 34 3 ^^ANAGND tNT2^^C 16 n. LU 33 □ 4^FOT0Y ZNT3— 17 W VW 32 □ 4^FOTeX INT6^^C 13 31 3 AUDL RGAS^^C 19 30 3 AUDR RGA7«^C 20 29 3 CCKQ RGA6m-^C 21 28 3 CCK RGA5««^E 22 27 3 ^*Voo RGA4«-i^C 23 26 3 4— RQAl RGA3«-^C: 24 25 3 4—RGA2 AnMei^kunsren : PFe ile kennzei clinen die Sisrnalr'ichiungi. Streich über den Siffn^ilnanen bedeutet* das Sisrnal Lom—A ktiv ist C6—Aktiv> . Abb. 1.2.3.5 50 Amiga intern Blockschaltbild Paula Abb. 1.2.3.6 Die Aufgaben Paulas fallen hauptsächlich in den Ein-/Ausgabe-Be¬ reich (Input/Output kurz I/O), nämlich die Disketten-Ein-/ Ausgabe, die serielle Ein-/Ausgabe, die Tonausgabe und die Abfrage der Analogeingänge. Zusätzlich unterliegt Paula die gesamte Interrupt- Steuerung. In ihm laufen alle im System aufgetretenen Interrupts ein. Aus diesen vierzehn möglichen Interrupt-Quellen erzeugt Paula die Interrupt-Signale für den 68000. Es werden dabei Interrupts der Ebe¬ nen 1-6 auf den IPL-Leitungen des 68000 generiert. Paula gibt dem Programmierer die Möglichkeit, jede der vierzehn Interrupt-Quellen einzeln zu erlauben oder zu verbieten. Der Diskdatentransfer und die Tonausgabe laufen ebenfalls per DMA ab. Bei den Daten von Diskette läßt sich nicht immer Voraussagen, wann das nächste Datenwort für einen DMA-Transfer von Agnus be¬ reit ist. Ursachen hierfür sind unter anderem die unvermeidlichen Drehzahlschwankungen des Diskettenlaufwerks. Auch bei der Audio- ausgabe weiß Agnus nicht im voraus, wann Daten benötigt werden. Um dennoch einen reibungslosen DMA-Transfer zu ermöglichen, be¬ sitzt Paula die DMAL-Leitung, mit der es Agnus mitteilen kann, daß es einen DMA-Zugriff benötigt. Die Hardware des Amiga 51 Die serielle Kommunikation wird von einem UART-Baustein inner¬ halb Paulas übernommen. UART heißt Universal Asynchronous Re- ceive Transmit was soviel wie universeller asynchroner Sen¬ der/Empfänger bedeutet. Die Funktion des UARTs wird genauso wie die der vier Audiokanäle und der Analog-Ports später im Kapitel über die Programmierung der Custom-Chips beschrieben. Wie üblich zum Abschluß noch eine Beschreibung der Pinfunktionen: Datenbus: DO-15 Wie bei den anderen Chips mit dem Chip-Datenbus verbunden. Registeradreßbus: RGA 1-8 Wie bei Denise Die Taktsignale und Reset: CCK. CCKQ und RES Paula enthält dieselben Taktsignale wie Agnus. Die Reset-Leitung RES versetzt das Chip in einen definierten Einschaltzustand. DMA-Request: DM AL Mittels dieser Leitung signalisiert Paula an Agnus, daß es einen DMA- Transfer benötigt. Audioausgänge: AUDL und AU DR Die Ausgängen AUDL und AUDR (Left Audio und Right Audio) sind analoge Ausgänge, an denen die in Paula erzeugten Tonsignale anliegen. An AUDL liegen die internen Tonkanäle 0 und 3, an AUDR die Kanäle 1 und 2. Die Leitungen der seriellen Schnittstelle: TXD und RXD RXD (Receive Data/Empfangsdaten) ist der serielle Eingang des UARTs, TXD (Transmit Data/Sendedaten) der serielle Ausgang. Diese Leitungen haben TTL-Pegel, das heißt, ihre Ein-/Ausgangsspannun- gen bewegen sich zwischen 0 und 5 Volt. Ein zusätzlicher Pegelwand¬ ler erzeugt erst nachträglich die +12/-5 Volt der seriellen RS232- Schnittstelle des Amiga. 52 Amiga intern Die Analogeingänge: POTOX, POTOY, POTIX, POTIY Die Eingänge POTOX und POTOY sind mit den entsprechenden Lei¬ tungen von Game-Port 0 verbunden, POTIX und POTIY dementspre¬ chend mit Port 1. An diese Eingänge können Paddies oder Analog- Joysticks angeschlossen werden. Diese Eingabegeräte enthalten verän¬ derliche Widerstände, sogenannte Potentiometer, die zwischen +5 Volt und den POT-Eingängen liegen. Paula ist in der Lage, den Wert dieser Wiederstände zu ermitteln und in internen Registern abzulegen. Die POT-Eingänge können allerdings softwaremäßig auch als Ausgang ge¬ schaltet werden. Die Diskettenleitungen: DKRD, DRWD, DKWE Über die DKRD-Leitung (Disk Read/Disketten-Lesedaten) erhält PAULA die Lesedaten von der Diskette. Die DKWD-Leitung (Disk Write/Disketten-Schreibdaten) ist der Ausgang für die Daten zum Dis¬ kettenlaufwerk. Die DKWE-Leitung (Disk Write Enable/Disketten- Schreibfreigabe) dient zum Umschalten des Diskettenlaufwerks von Lesen auf Schreiben. Die Interrupt-Leitungen: INT2, INT3, INT6 und IPLO, IPLl, IPL2 Über die drei INT-Leitungen erhält Paula die Aufforderung, einen Interrupt der entsprechenden Ebene zu erzeugen. Mit der INT2-Lei- tung ist normalerweise der mit CIA-A bezeichnete 8520-Baustein ver¬ bunden. Allerdings ist diese Leitung auch noch zum Expansion-Port und der seriellen Schnittstelle durchgeschleift. Wird sie Low, erzeugt Paula einen Interrupt der Ebene 2, vorausgesetzt, daß ein Interrupt dieser Ebene überhaupt erlaubt ist. Die INT3-Leitung ist mit dem entsprechenden Ausgang von Agnus verbunden und die INT6-Leitung mit dem CIA-B und ebenfalls dem Expansion-Port. Alle restlichen Interrupts treten innerhalb der I/O-Komponenten von Paula auf. Die IPL0-IPL2 Leitungen (Interrupt Pending Level des 68000, s. Kap. 1.2.1) sind direkt mit den entsprechenden Prozessorleitungen verbun¬ den. Über sie erzeugt Paula einen Prozessor-Interrupt der entspre¬ chenden Ebene. Die Hardware des Amiga 53 1.2.3.5 Besonderheiten des A500 Die Beschreibungen der Amiga-Hardware in diesem Kapitel entstan¬ den ursprünglich für den Al000. Sie sind größtenteils auch für den A500 gültig. Im A500 wurde am Amiga-Grundaufbau nichts geändert. Man hat nur versucht, eine billigere Version herzustellen. Die größten Unterschiede zwischen beiden Modellen betreffen die Verteilung der verschiedenen Hardware-Elemente auf die einzelnen Chips. Beim Al000 kommen zu den Custom-Chips noch eine Vielzahl einfa¬ cher logischer Schaltkreise, die der Erzeugung der Taktsignale, der Bussteuerung und der Adreßdekodierung dienen. Beim A500 wurden fast alle dieser logischen Funktionen in den großen Chips zusammengefaßt. Dazu hat man einige neue Baugruppen zum Agnus-Chip hinzugefügt und dem neuen Chip den Namen Fat- Agnüs gegeben (Typenbezeichnung; 8370 NTSC und 8371 PAL). ttt a:a:afieaa;oeactta:ocoe8»xu»j ee N I.» H H H « 2 CHN6 .8 INDEX -10 SELO -18 DIR -20 STEP .22 DKMD -24 DKME -26 TKP -28 MPRO -30 DKRD -32 SIDE .34 RDV -alle ungeraden 34-Pin Connector —•_aiie ungeraaen 1 +5Uolt *1 Stronversorgung Abbildung 1 . 3 . 5.3 Wie man sieht, speichert das mit Fl bezeichnete Flipflop das Signal an der MTRX-Leitung, wenn die SELI-Leitung von high auf low geht. Da das FlipFlop den Wert an seinem Dateneingang mit der positiven Flanke des Taktsignals speichert, muß SELl invertiert werden. Dies erledigt das Nandgatter NI. Der Q-Ausgang ist direkt mit dem MTR- Eingang des Zweitlaufwerks verbunden. 80 Amiga intern Das mit N2 bezeichnete Nand-Gatter hat nichts mit der Motorsteue¬ rung zu tun. Es dient dem oben erwähnten Identifikationsmodus, der von den meisten handelsüblichen Laufwerken nicht unterstützt wird. Immer wenn der Motor abgeschaltet und die SELI-Leitung aktiv, also auf 0, ist, legt das Gatter die RDY-Leitung auf low. Dadurch erkennt der Amiga das Zweitlaufwerk als 3.5 Zoll Standardlaufwerk mit der Nummer DFl: an. Da von beiden verwendeten ICs jeweils nur die Hälfte benötigt wird, reichen sie auch noch für ein zweites Zusatzlaufwerk aus. Die Ein¬ gänge von NI müssen dann allerdings mit SEL2 verbunden werden (Pin 9 am externen Floppystecker). Damit die CHNG-Leitung ein¬ wandfrei funktioniert, müssen bei manchen Laufwerken einige Jumper umgesteckt werden. Näheres dazu entnimmt man am besten dem Handbuch des Laufwerks. Als Beispiel sei die FD 1035 von NEC ge¬ nannt, bei ihr muß man den mit J1 bezeichneten Jumper kurz¬ schließen. 1.3.6 Die Game-Ports Die Pinbelegung der Ganeports d] CS (XI CE CB (XIXXX 3-Pin D-SUB-Stecker Abbildung 1.3.6 Verwendung als: Maus-Port Joystick Paddle LightPen Eingang 1 V-Irrpulse Oben Unbenutzt Unbenutzt Eingang 2 H-Irrpulse Unten Unbenutzt Unbenutzt Eingang 3 VQ-Irrpulse Links Linker Knopf Unbenutzt Eingang 4 HO-Irrpulse Rechts Rechter Knopf Unbenutzt Ein/Aus 5 (Knopf 3) Unbenutzt Rechtes Poti Knopf Ein/Aus 6 Knopf 1 Fetjerknopf Unbenutzt LP-Signal 7 +5 Volt +5 Volt +5 Volt +5 Volt 8 GND GNO GND GND Ein/Aus 9 Knopf 2 Unbenutzt Linkes Poti Unbenutzt Die Hardware des Amiga 81 Die Game-Ports sind Eingänge für die neben der Tastatur üblichen Eingabegeräte wie Maus, Joystick, Trackball, Paddle oder Lightpen. Es gibt zwei Game-Ports, der linke wird mit Game-Port 0 und der rechte mit Game-Port 1 bezeichnet. Die Belegung beider Game-Ports ist identisch. Lediglich die LP-Leitung ist nur bei Game-Port 0 vorhan¬ den. Intern sind die Game-Ports mit CIA-A, Agnus, Denise und Paula verbunden. Im einzelnen sind die Pins folgendermaßen verschaltet: Oame-Port 1: Nr. Chip Pin 1 Denise MOV (über Hultiplexer) 2 Denise HOH (über Hultiplexer) 3 Denise HIV (über Hultiplexer) 4 Denise H1H (über Hultiplexer) 5 Paula POY 6 CIA-A PA6 sowie Agnus LP beim A1000 9 Paula POX Game-Port 1: 1 Denise HOV (über Hultiplexer) 2 Denise HOH (über Hultiplexer) 3 Denise HIV (über Hultiplexer) 4 Denise H1H (über Hultiplexer) 5 Paula PlY 6 CIA-A PA7 sowi e Agnus LP beim ASOO 9 Paula PIX Die Funktion des oben erwähnten Multiplexers ist in Kapitel 1.2.3.2 schon erläutert worden. Die Belegungen für die verschiedenen Einga¬ begeräte wurden so gewählt, daß fast alle handelsüblichen Joysticks, Mäuse, Paddies und Lightpens verwendet werden können. Es ist also z.B. möglich, Lightpens vom C64 weiterzuverwenden. Dabei ist die mit "Knopf bezeichnete Leitung meist ein Schalter, der gedrückt wird, wenn man mit dem Lightpen den Schirm berührt. Die LP-Leitung ist das eigentliche Lightpen-Signal, das von der Elek¬ tronik des Griffels erzeugt wird, wenn der Elektronenstrahl an seiner Spitze vorbeiläuft. Beim Al000 war die Lightpen-Leitung mit Game- Port 0 verbunden. Dies hatte den Nachteil, daß man immer die Maus aus- oder umstecken mußte, wenn man einen Lightpen benutzen wollte. Beim A500 ist sie daher jetzt mit Port 1 verbunden. Die Wahl hat, wer Besitzer eines A2000 ist. Dort kann man mit Jumper J200 wählen, wohin der Lightpen gehört. Voreingestellt ist hier, wie beim A500, Port 1 (Jumper-Pins 2 und 3). 82 Amiga intern Alle mit "Knopr bezeichneten Leitungen und die vier Richtungen bei Joystick-Betrieb sind low-aktiv. In den verschiedenen Eingabegeräten befinden sich Schalter, die mit dem entsprechenden Eingang und Masse (GND) verbunden sind. Ein High-Signal am Eingang bedeutet offener Schalter, ein geschlossener Schalter ruft dagegen low hervor. An die mit POX, POY, PIX und PlY bezeichneten Analogeingänge können veränderliche Widerstände, sog. Potentiometer, angeschlossen werden. Ihr Wert sollte 470 KOhm betragen. Sie werden zwischen +5 Volt und den entsprechenden Eingang geschaltet. Die beiden Feuerknopf-Leitungen, die mit dem CIA-A verbunden sind, können natürlich auch als Ausgang programmiert werden. Aller¬ dings muß man bei einem Schreibzugriff auf das Portregister aufpas¬ sen, daß man das unterste Port-Bit nicht überschreibt, da dies einen Systemzusammenbruch zur Folge hat (PAO:OVL). Die genaue Abfrage der Game-Port-Leitungen wird im Kapitel über die Programmierung der Custom-Chips erläutert. Die +5-Volt-Spannung an den beiden Game-Ports ist nicht direkt mit der Betriebsspannung des Amiga verbunden. Man hat eine Strombe¬ grenzungsschaltung dazwischen geschaltet. Sie begrenzt den kurzzeiti¬ gen Spitzenstrom auf 700 mA und den Dauerkurzschlußstrom auf 400 mA. Das macht diesen Ausgang absolut kurzschlußfest. Damit die Spannung an den beiden +5-Volt-Pins nicht zu stark abfällt, sollte die Stromentnahme an beiden Ports zusammen 250 mA nicht übersteigen. Leider hat man diese sinnvolle Schutzschaltung beim A500 und A2000 eingespart. Die Hardware des Amiga 83 1.3.7 Der Expansion-Port und die A2000-Siots Die Belegung des Expansion-Ports Die Pinbelegung des Expansionport 1357... ... 79 81 83 85 □ □□□□□ ■■■ □□□□□□ Z 4 6 8 ... ...80 82 84 88 86-poliger Platinenstecker 0500 & 01000 Abbildung 1.3.7 1 GNO 3 GNO 5 +5 Volt 7 frei 9 Frei A2000:28Hhz 11 Frei A2000-B:/COPCFG 13 GNO 15 COAC 17 /OVR 19 /INT2 21 A5 23 A6 25 GNO 27 A2 29 AI 31 FCO 33 FC1 35 FC2 37 GNO 39 A13 41 A14 43 A15 45 A16 47 A17 49 GNO 51 /VMA 53 /RES 55 /HLT 57 A22 59 A23 61 GNO 63 P015 2 GNO 4 GNO 6 +5 Volt 8 -5 Volt 10 +12 Volt 12 CONFIG IN, mit GNO verbunden 14 /C3 16 /CI 18 XRDY 20 f. A1000:PALOPE A2000-B:/BOSS 22 /INT6 24 A4 26 A3 28 A7 30 A8 32 A9 34 A10 36 All 38 A12 40 /IPLO 42 /IPL1 44 /IPL2 46 /6ERR 48 /VPA 50 E 52 A18 54 A19 56 A20 58 A21 60 /BR A2000-B: /CBR 62 /BGACK 64 /BG A2000-B: /CBG 84 Amiga intern 65 PDU 66 /DTACK 67 PD13 68 /PRU 69 PD12 70 /LOS 71 PD11 72 /UDS 73 GND 74 /AS 75 PDO 76 PD10 77 PD1 78 PD9 79 PD2 80 PD8 81 PD3 82 PD7 83 PD4 84 PD6 85 GND 86 PD5 Die Beiegung der lOOpoligen Erweiterungs-Slots 1 GND 2 GND 3 GND 4 GND 5 +5 Volt 6 +5 Volt 7 /OWN 8 -5 Volt 9 /SLAVEn 10 ♦12 Volt 11 /CFGOUTn 12 CFGINn 13 GND 14 /C3 15 CDAC 16 /CI 17 /OVR 18 XRDV 19 /INT2 20 -12 Volt 21 A5 22 /INT6 23 A6 24 A4 25 GND 26 A3 27 A2 28 A7 29 AI 30 A8 31 FCO 32 A9 33 FC1 34 A10 35 FC2 36 All 37 GND 38 AI 2 39 A13 40 /EINT7 41 A14 42 /EINT5 43 A15 44 /EINT4 45 A16 46 /BERR 47 A17 48 /VPA 49 GND 50 E 51 /VHA 52 A18 53 /RES 54 A19 55 /HLT 56 A20 57 A22 58 A21 59 A23 60 /BRn 61 GND 62 /8GACK 63 PD15 64 /BGn 65 PDU 66 /DTACK 67 PD13 68 /PRW 69 PD12 70 /LDS 71 PD11 72 /UDS 73 GND 74 /AS 75 PDO 76 PD10 77 PD1 78 PD9 79 PD2 80 PD8 81 PD3 82 PD7 83 PD4 84 PD6 Die Hardware des Amiga 85 85 GND 86 PD5 87 GND 88 GND 89 GND 90 GND 91 GND 92 TNKz 93 DOE 94 /BUSRST 95 /BG A2000-B:/GBG 96 /EINT1 97 Frei 98 Frei 99 GND 100 GND Ein kleines "n" steht für die Nummer des Expansion-Slots von 1 - 5. Am Expansion-Port liegen so gut wie alle wichtigen Steuerleitungen und Bussignale des Amiga-Systems an. Dadurch können an ihm RAM- Erweiterungen, neue Prozessoren, Festplatten-Controller usw. ange¬ schlossen weden. Beim AlOOO liegt dieser Port neben den beiden Ga¬ me-Ports hinter einer leicht abnehmbaren Plastikschutzkappe verbor¬ gen. Beim A500 hat man ihn genau gegenüber plaziert, d.h. auf der von vorne gesehen linken Gehäuseseite. Er ist in Form eines 86-poli- gen Platinensteckers ausgeführt. Der Abstand zwischen den einzelnen Pins beträgt 1/10 Zoll, das sind 2,54 mm. Eine dafür passende Buchse dürfte derzeit nicht leicht zu finden sein. Beim A2000 gibt es zwei verschiedene Busanschlüsse. Einmal den MMU-Connector, der weitgehend obiger Belegung entspricht (siehe Klammern) und die 5 lOOpoligen Amiga-Steckplätze (auch Zorro-Bus genannt). Diese sechs Steckplätze befinden sich im A2000 auf der Hauptplatine innerhalb des Gehäuses. Sie sind als 86polige bzw. lOOpolige Buchsen für Platinenstecker ausgeführt. Der Kontaktabstand beträgt ebenfalls 2,54 mm. Die meisten Signale am Expansion-Port sind direkt mit den entsprechenden Leitungen des 68000 verbunden. Die Funktion der Leitungen kann man im Kapitel 1.2.1 nachlesen. Es sind folgende Signale: AO - A23 PDO - PD15 IPLO - IPL2 FCO - FC2 AS, UDS, LOS, PRU, DTACK, VMA, VPA RES, HLT, BERR, BG, BGACK, BR, E Adreßbus Prozessordatenbus Prozessor-Interrupt-Leitungen Function-Code-Leitungen des 68000 Bussteuersignale Sonstige Steuersignale des 68000 Die übrigen Signale haben folgende Funktionen: 86 Amiga intern INT2 und INT6 Diese beiden Leitungen sind mit den gleichnamigen Pins von Paula verbunden. Mit ihnen kann ein Interrupt der Ebene 2 bzw. 6 hervor¬ gerufen werden. CDAC, CI, C3 und beim A2000 noch 28M 2» nnjuuuuuuuuuuuuuuij cDftc r n_r i_r n_r “i_r ä j 1 1 1 1 1_ ■C3 _ __i 1 _ _ 1 1 _ 7M 1 L_rn L_rn L_rn 1 n CCK 1 1 1 1 1 1 1_I-1_f 28.6 MHz 7.16 MHz 3.58 MHz 3.58 MHz 7.16 MHz 3.58 MHz 3.58 MHz Tak-tsignal« an Expansionpor-t Abbildung 1.3.7.1 Dies sind die verschiedenen Taktsignale des Amiga. Ihre Frequenz und Phasenlage kann man am besten aus der Abbildung entnehmen. Beim A2000 liegt auch noch der Haupttakt des Amiga, die 28.64-MHz- Quarzfrequenz, am Expansion-Port an. Die ebenfalls in der Abbildung eingezeichneten Taktsignale 7M, CCK und CCKQ gibt es nicht am Expansion-Port. 7M ist der Takt des 68000, und CCK und CCKQ sind mit den Custom-Chips verbunden. Die Hardware des Amiga 87 PALOPE Dieses Signal existiert ausschließlich beim Al000 und war für ROM- Erweiterungen vorgesehen. Bei allen neuen Modellen hat man dieses Konzept unter den Tisch fallen lassen. CONFIG IN Diese Leitung signalisiert der angeschlossenen Karte, daß sie mit ih¬ rem Autokonfigurationsprozeß an der Reihe ist. Da eine Karte im Ex¬ pansion-Slot immer die erste Erweiterungskarte ist, liegt ihre CON- FIG-IN-Leitung immer auf Masse. In Kapitel 4 kann man eine Be¬ schreibung der Autokonfigurationsarchitektur des Amiga finden. /OVR Mit /OVR low läßt sich das von Gary erzeugte DTACK-Signal für den Bereich von $200000 bis $9FFFFF abschalten. /XRDY Dieses Signal erfüllt einen ähnlichen Zweck wie /OVR. Legt man /XRDY weniger als 60 Nanosekunden nach dem /AS auf low, verzö¬ gert Gary das /DTACK Signal, bis /XRDY wieder auf high geht. Da¬ durch können auch langsame Erweiterungskarten angeschlossen wer¬ den, die nicht in der Lage sind, ohne Waitstates zu antworten. Die mit "frei" bezeichneten Leitungen sind nicht belegt. Viele der Leitungen, die beim Al000 noch frei waren, haben bei den A2000- Modellen inzwischen Verwendung gefunden. Einige Signale des Expansion-Ports sind beim A2000-B etwas anders verschaltet als sonst. Es sind: /COPCFG (Pin 11), /BOSS (Pin 20), /CBR (Pin 60) und /CBG (Pin 64). /COPCFG steht für COProzessor ConFiGuration. Diese Leitung ermöglicht es, auch im A2000-B eine selbstkonfigurierende Erweiterungsplatine in den Expansion-Slot zu stecken. Beim A2000-A ist eine Autokonfiguration im Expansion-Slot nicht möglich. Näheres dazu wie gesagt im Kapitel 4. Mit den anderen drei Leitungen hat es folgende Bewandtnis: Der Expansion-Slot im Amiga 2000 ist hauptsächlich als Coprozessor-Slot gedacht. Er soll zum Beispiel die von Commodore entwickelte 68020-Karte aufnehmen können. Optimal wäre es nun, wenn ein solcher Coprozessor das Sy¬ stem so vollständig übernehmen würde, daß alle vorhandenen Erwei¬ terungskarten unverändert Weiterarbeiten könnten. Beim A2000-A ist 88 Amiga intern dies bis auf eine Ausnahme möglich: Hat der Coprozessor das System übernommen, sind keine DMA-Zugriffe von Seiten einer Slot-Karte mehr möglich. Woran liegt das? Grund dafür ist das DMA-Konzept des 68000-Prozessors. Will in ei¬ nem 68000-System ein anderer Chip die Kontrolle über Adreß- und Datenbus sowie die Kontrollsignale übernehmen, kann er sich der drei Leitungen /BR (BusRequest), /BG (BusGrant) und /BGACK (Bus- GrantAcknowledge) des 68000 bedienen. Zuerst legt er /BR auf low, um anzuzeigen, daß er den Bus will. Sobald der 68000 meint, daß er den Bus jetzt freigeben könnte, antwortet er mit /BG low. Ist der Bus vollkommen frei, d.h. /AS und /Dtack sind high, setzt der Baustein, der den Bus will, /BGACK auf low und sagt damit, daß er jetzt Bus¬ master ist. Hat ein Coprozessor auf diese Weise den Bus übernommen, kann eine andere Erweiterungskarte keinen DMA mehr ausführen, da diese /BGACK low sieht und daher denkt, daß gerade ein anderer DMA-Baustein arbeitet. Man muß also einen Weg finden, der dafür sorgt, daß der Coprozessor zwar wie ein DMA-Baustein den Bus übernehmen kann, dann aber für die restlichen Erweiterungskarten wie der normale Prozessor, und nicht wie ein DMA-Baustein wirkt. Man hat bei dem A2000-B das Problem folgendermaßen gelöst: Der Coprozessor startet seine Übernahme nicht mit /BR, sondern mit /CBR. Dieses Signal geht über den Buster-Chip, der beim A2000-B die Slots steuert, erst einmal zum 68000. Dieser antwortet wie immer mit /BG, welches vom Buster als /CBG zum Coprozessor weitergege¬ ben wird. Dieser signalisiert seine Busübernahme statt mit /BGACK jetzt mit /BOSS. Diese Leitung ist nicht, wie /BGACK, mit den an¬ deren Slots verbunden, geht aber über eine Oderverknüpfung zusam¬ men mit /BGACK zum Prozessor, dieser erkennt also /BOSS als nor¬ malen /BGACK und schaltet sich ab. Sobald Buster /BOSS low sieht, schaltet er die Datenrichtung für /CBR und /CBG um. Statt vom Co¬ prozessor zum Prozessor geht es jetzt von den Slots zum Coprozessor. Ein DMA-Controller in einem der Slots kann jetzt ganz normal über /BR -> /BG -> /BGACK den Bus vom Coprozessor übernehmen und auch wieder an diesen zurückgeben. Der Coprozessor hat also die Funktion des eingebauten 68000 vollständig .übernommen. Will der Coprozessor die Kontrolle wieder abgeben, braucht er lediglich /BOSS wieder auf high zu legen. Die Hardware des Amiga 89 Die Belegung der lOOpoligen Slots ist ähnlich derjenigen des Expansi¬ on-Ports. Es gibt wieder alle wichtigen Signale vom 68000: AO - A23 Adreßbus PDO - PD15 Prozessordatenbus FCO - FC2 Function'Code-Leitungen des 68000 AS, UDS, LOS, PRU, DTACK, VMA, VPA Bussteuersignale RES, HLT, BERR, E Sonstige Steuersignale des 68000 Allerdings sind außer DTACK, VPA, VMA, RES, HLT, BERR und E sämtliche Signale gepuffert, d.h. sie sind nicht direkt, sondern über Treiber-Bausteine vom Typ 74LS245 mit dem Prozessor verbunden. Die restlichen Signale haben folgende Funktion: /BUSRES Gepuffertes Reset-Signal. Während der Amiga mit der RES-Leitung auch von einer Slot-Karte aus zurückgesetzt werden kann, ist die /BUSRES-Leitung nur zum Zurücksetzen der Slot-Karten gedacht. Normalerweise hat man nicht vor, den Amiga von einer Slot-Karte aus zurückzusetzen und sollte daher die /BUSRES-Leitung benutzen, um die direkt mit dem 68000 verbundene /RES-Leitung nicht unnötig zu belasten. /SLAVEn Jeder Slot hat seine eigene /SLAVE-Leitung. Eine Erweiterungskarte muß /SLAVE auf low legen, sobald sie eine für sie gültige Adresse erkannt hat, damit die Daten- und Adreßpuffer korrekt geschaltet werden können. Wenn mehr als eine Slot-Karte SLAVE auf low legt, erzeugt der Buster einen Busfehler. Das gleiche gilt, wenn sich eine Karte außerhalb des Bereichs von $20000 bis $9FFFFF und $E80000 und SEFFFFF mit SLAVE low meldet. /CFGIN und /CFGOUT Die Config-Out-Leitung eines Slots ist immer mit der Config-In-Lei- tung des nächsten verbunden. Jede Karte wird konfiguriert, sobald die /CFGIN-Leitung ihres Slots low ist. Ist die Autokonfiguration been¬ det, legt sie ihren /CFGOUT-Ausgang auf low, um der Karte im nächsten Slot die Konfiguration zu erlauben (siehe Kapitel 4) 90 Amiga intern DOE Dieses Signal kommt vom Buster und sagt der aktiven Erweiterungs¬ karte, daß sie ihre Datentreiber aktivieren darf. Damit werden Daten¬ kollisionen verhindert. /BRn, /BGn und /BGACK Mit diesen Leitungen kann eine Slot-Karte den Bus übernehmen, also zum DMA-Controller werden. Diese Fähigkeit wird z.B. von einem schnellen Harddisk-Controller benötigt. Der prinzipielle Ablauf der Busübernahme wurde in diesem Kapitel ja schon beschrieben, die Reihenfolge lautet; /BR -> /BG -> /BGACK. Was passiert aber, wenn mehrere Karten /BR zum gleichen Zeitpunkt auf low legen? Um da¬ bei Probleme zu verhindern, hat Jeder Slot seine eigenen /BR- und /BG-Signale. Wenn mehrere Slots ihre /BR-Signale gemeinsam akti¬ vieren, zieht Buster den Slot mit der niedrigsten Nummer vor, also denjenigen, der dem Coprozessor-Slot am nächsten liegt, und gibt den /BR an den 68000 (oder einen mittels /BOSS aktivierten Coprozessor beim A2000-B) weiter. Dessen Antwort in Form des /BG wird vom Buster nur an diesen Slot zurückgegeben, wodurch dieser zum Busma¬ ster avanciert und /BGACK auf low legt. Die anderen Slots, die ebenfalls ihr /BR auf low gelegt haben, müssen warten, bis der gerade aktive seinen DMA beendet hat. /OWN Diese Leitung muß eine Slot-Karte auf low legen, wenn sie, wie oben beschrieben, Busmaster geworden ist. Dies ist notwendig, um die Richtung der Daten- bzw. Adreßpuffer umzukehren, da der Busma¬ ster, der die Adressen erzeugt, jetzt auf der anderen Seite der Puffer sitzt. /BG bzw. /GBG beim A2000-B Dies ist der original /BG vom Prozessor. Beim A2000-B, je nachdem, ob ein Coprozessor die Rolle des 68000 übernommen hat, kommt er entweder vom Prozessor oder vom Coprozessor. Mittels dieses Bus- Grant-Signals kann eine Slot-Karte entscheiden, ob der Prozessor den Bus besitzt oder gerade ein DMA stattfindet. Die Hardware des Amiga 91 Die Interrupt-Leitungen /EINTI, /EINT4, /EINT5 und /EINT7 Diese Leitungen lösen einen Interrupt der entsprechenden Ebene aus. Dieser kann nicht, wie bei den /INT2- und /INT6-Leitungen des Ex¬ pansion-Ports, über Paula abgeschaltet werden. 1.3.8 Stromversorgung über die Schnittsteilen An sämtlichen Anschlußbuchsen liegen eine oder auch mehrere der drei Betriebsspannungen des Amiga an. Es ist dadurch möglich, Peri¬ pheriegeräte über die entsprechende Schnittstelle mit Strom zu versor¬ gen. Allerdings muß man Rücksicht auf die maximale Belastbarkeit der Anschlüsse nehmen. Folgende Tabelle gibt die von Commodore empfohlenen Höchstbelastungen beim Al000 wieder: Schnittstelle +5 Volt ♦12 Volt “5 Volt TV-Hod-Buchse . 60 mA - RGB-Buchse 300 mA 175 mA 50 mA RS232-Buchse 100 mA 50 mA 50 mA Ext. Disk 270 mA 160 mA - Centronics 100 mA - - Expansion-Port 1000 mA 50 mA 50 mA Game-Port 0 125 mA - - Game-Port 1 125 mA - • Diese Tabelle kann allerdings nur als grobe Richtlinie angesehen wer¬ den. Erstens gelten die angegebenen Werte nur dann, wenn auch wirk¬ lich alle Ports gleichzeitig entsprechend belastet werden. Ist z.B. der Expansion-Port unbelegt, stehen die zusätzlichen 1000 mA an den an¬ deren +5-Volt-Anschlüssen zur Verfügung. Ist in einer bestimmten Systemkonfiguration sicher, daß einige Ports frei bleiben, erhöht sich dementsprechend die Belastbarkeit der anderen Ausgänge. Selbstver¬ ständlich kann man auch die Gewaltmethode anwenden und solange Expansion-Port-Platinen anschließen, bis das Netzteil abschaltet. Wie (meist unfreiwillige) Kurzschlußversuche gezeigt haben, schadet ihm dies nicht weiter. Allerdings muß jeder selbst die Verantwortung für solche Experimente übernehmen. Vor allem bei den +5 Volt ist Vor¬ sicht geboten. Bei einem Kurzschluß können Ströme von mehr als 8 Ampere fließen. 92 Amiga intern Zweitens gilt die obige Tabelle nur für den AlOOO. Man kann sie nicht auf den A500 oder A2000 übertragen, da die Netzteile dieser Computer unterschiedlich dimensioniert sind. Beim A500 ist die Be¬ lastbarkeit des Netzteils geringer. Man sollte bei stromfressenden Er¬ weiterungen am Expansion-Port (RAM-Karten etc.) ein Zusatznetzteil benutzen. Die Stromversorgung des A2000 ist kräftiger als die des AlOOO. Sie muß ja auch in der Lage sein, sämtliche zusätzlichen Amiga- und IBM-Steckkarten zu versorgen. Noch ein entscheidender Unterschied des A500-Netzteils gegenüber dem des AlOOO ist die negative Versorgungsspannung. Sie beträgt nämlich -12 statt -5 Volt wie beim AlOOO. D.h. überall, wo in diesem Buch -5 Volt angegeben sind, müssen A500-Besitzer mit -12 Volt rechnen. Die Hardware des Amiga 93 1.4 Die Tastatur 7 8 9 3D 3E 3F 4 s 6 ZD 2E 2F 1 2 3 ID lE IF e 0 F 3C - EHTER 43 Anerikdnische ASCII-Tastatur 7 8 9 3D 3E !F 4 9 6 2D ZE ZF 1 2 3 ID lE IF D GF 31: - ENTER 4D 43 Deutsche Tastatur Abb. 1.4.1 Die Amiga-Tastatur ist eine sogenannte intelligente Tastatur. Sie be¬ sitzt einen eigenen Mikroprozessor, der die zeitaufwendige Abfrage der einzelnen Tasten übernimmt und dem Amiga die fertigen Tasten- Codes liefert. Es gibt inzwischen verschiedene Ausführungen und Be¬ legungen der Amiga-Tastatur, aber sie unterscheiden sich nur durch einige neu hinzugekommene Tasten und damit auch Tasten-Codes. Die Abbildung zeigt die Belegung und die zugehörigen Tasten-Codes sowohl der deutschen als auch der amerikanischen ASCII-Version. Wie man sieht, entsprechen die Codes nicht dem ASCII-Standard. Die Ta¬ statur liefert lediglich sogenannte RAW-Key-Codes ("Rohe Tastatur- Codes"), die dann vom Amiga Betriebssystem mit Hilfe einer Übersetzungstabelle, der Keymap, in ASCII-Codes verwandelt werden. 94 Amiga intern Trotzdem gibt es ein System in der Verteilung der RAW-Key-Codes: $00-$3F Codes der Buchstaben-, Ziffern- und Sonderseichentasten. Verteilung entspricht der Anordnung der Tasten auf dem Keyboard. $40-$4F Codes der üblichen Sondertaaten wie SPACE, RETURN, TAB usw. $50-$5F Funktionstasten und HELP. |60-|67 Tasten sur Tastaturebenenwahl (Shift, Amiga, Alternate und Control). Der Tastaturprozessor kann allerdings noch mehr. Er unterscheidet nämlich zwischen dem Drücken und Loslassen einer Taste. Wie man sieht, sind sämtliche Tastatur-Codes lediglich 7 Bit breit (Wertebereich von $00 bis $7F). Das achte Bit ist das KEYup/down-Flag. Es wird von der Tastatur dazu verwendet, um dem Computer anzuzeigen, ob die Taste gerade gedrückt oder losgelassen wurde. Ist das achte Bit 0, bedeutet dies, daß die Taste gerade gedrückt wurde (KEYdown). Ist es auf I, wurde sie losgelassen (KEYup). Dadurch weiß der Amiga stets, welche Tasten gerade gedrückt sind. Man kann die Tastatur damit auch für Zwecke benutzen, die das gleichzeitige Niederhalten ver¬ schiedener Tasten erfordern. Dies sind z.B. Musikprogramme, die die Tastatur zum polyphonen Spielen verwenden wollen. Eine Ausnahme bildet die CAPS-LOCK-Taste. Die Tastatur simuliert bei ihr einen einrastenden Schalter. Drückt man sie das erste Mal, ra¬ stet sie ein, und die Leuchtdiode geht an. Erst wenn man sie ein zweites Mal betätigt, rastet sie wieder aus, und die Leuchtdiode ver¬ löscht. Diesem Verhalten trägt das KEYup/down-Flag Rechnung. Be¬ tätigt man CAPS-LOCK, leuchtet die LED auf, und der Tasten-Code für CAPS-LOCK wird zusammen mit einem gelöschten 8. Bit zum Computer gesandt, um anzuzeigen, daß die Taste jetzt gedrückt ist. Läßt man die Taste wieder los, wird kein KEYup-Code gesendet, und die LED leuchtet weiter. Erst wenn man CAPS-LOCK ein weiteres Mal niederdrückt, wird ein KEYup-Code ausgegeben (gesetztes achtes Bit), und die LED verlöscht. Die Hardware des Amiga 95 1.4.1 Die Schaltung der Tastaturplatine Di« T«*t«nb«i«aun««o ««tt«)««» Dich auf at« DIH-i CZT) badautvt Sannartaatatu«- Abb. 1.4.2 Kern der Tastaturschaltung ist der Mikroprozessor 6500/1. Der 6500/1 ist ein sogenannter Ein-Chip-Mikrocomputer. Er enthält auf einem Chip alle Komponenten, die ein einfaches Computersystem zum Ar¬ beiten benötigt. Das Herz des 6500/1 ist ein Mikroprozessor vom Typ 6502 (Aha!). Dazu kommen noch 2 KByte ROM mit dem Steuerpro¬ gramm, 64 Bytes statisches RAM, 4 bidirektionale 8-Bit-Ports, ein 16- Bit-Zähler mit eigenem Steuereingang und ein Taktgenerator. Zum Betrieb benötigt der 6500/1 nur noch seine Betriebsspannung von 5 Volt und ein Quarz für die Takterzeugung. In der Amiga-Tastatur wird der 6500/1 mit einem 3-MHz-Quartz betrieben. Da diese Fre¬ quenz intern noch durch zwei geteilt wird, beträgt die Taktfrequenz 1.5 MHz. Das zweite Chip auf der Tastaturplatine ist ein sogenannter Prä¬ zisionszeitgeber vom Typ 556. Genaugenommen befinden sich zwei dieser Präzisionszeitgeber in dem Baustein. Der 556 erzeugt zusammen 96 Amiga intern mit den paar Bauteilen um ihn herum das Reset-Signal für den 6500/1. Die Tasten sind in zwei Gruppen aufgeteilt. Die sieben Sonder¬ funktionstasten (Shift rechts, ALT rechts, Amiga rechts, Control, Amiga links, ALT links und Shift links) sind direkt mit den ersten sieben Portleitungen des PB-Ports verbunden. Alle restlichen Tasten sind in einer Matrix von sechs Zeilen auf 15 Spalten angeordnet. Die Zeilen sind mit Leitungen PA2 bis PA7 von Port A verbunden. Diese sechs Portleitungen sind als Eingänge ge¬ schaltet. Die 15 Spalten werden von Port C und D angesteuert. Die zu PD7 gehörende 16. Spalte ist in den derzeitigen Tastaturversionen nicht beschältet. Wenn der 6500/1 die Tastatur abfragt, legt er der Reihe nach die einzelnen Spalten auf 0. Da die Ausgänge von Port C und D Open- Collector-Ausgänge ohne integrierte Pullup-Widerstände sind, verhal¬ ten sich alle auf 1 gesetzten Ausgänge vollkommen inaktiv. Nachdem der Prozessor also eine Spalte auf 0 gelegt hat, fragt er die 6 Zeilen ab. Die sechs Zeilen-Eingänge sind mit internen Pullup-Widerständen versehen, so daß alle nicht gedrückten Tasten als High interpretiert werden. Jede gedrückte Taste verbindet immer eine Zeile mit einer Spalte. Sind in der vom 6500/1 gerade aktivierten Spalte Tasten ge¬ drückt, werden die entsprechenden Zeileneingänge auf 0 gelegt. Nach¬ dem alle Spalten einmal aktiviert und die zugehörigen Zeilen gelesen wurden, kennt der Prozessor den Zustand sämtlicher Tasten. Hat sich dieser seit der letzten Abfrage verändert, sendet er die ent¬ sprechenden Tasten-Codes an den Computer. Die Hardware des Amiga 97 1.4.2 Die Datenübertragung Aussab« des Datenbytes von Tastaturprozessor Abb. 1.4.3 Die Tastatur ist mit dem Amiga über ein vieradriges Spiralkabel ver¬ bunden. Zwei der Leitungen dienen dabei lediglich der Stromversor¬ gung der Tastaturelektronik mit den nötigen 5 Volt. Die gesamte Datenübertragung läuft über die restlichen beiden Leitungen. Dabei dient die eine als Datenleitung KDAT und die andere als Taktleitung KCLK. Innerhalb des Amiga ist KDAT mit dem seriellen Eingang SP und KCLK mit dem CNT-Pin von CIA-A verbunden (siehe Kap. 1 . 2 . 2 ). Die Datenübertragung ist unidirektional. Sie läuft immer von der Ta¬ statur zum Amiga. Der 6500/1 legt dazu die einzelnen Daten-Bits auf die Datenleitung (KDAT), begleitet von 20 Microsekunden langen Low-Impulsen der Clock-Leitung (KCLK). Zwischen den einzelnen Clock-Impulsen liegen jeweils 40 Microsekunden lange Pausen. Das bedeutet, die Übertragungszeit für jedes Bit beträgt 60 Mikrosekunden (20 + 40). Dies ergibt bei 8 Bit 480 Mikrosekunden pro Byte oder um¬ gerechnet eine Übertragungsgeschwindigkeit von 16666 Baud (Bits/Sekunde). Nach der Ausgabe des letzten Bits erwartet die Tastatur einen Hand- shake-Impuls vom Computer. Der Amiga legt zu diesem Zweck die KDAT-Leitung für mindestens 75 Mikrosekunden auf Low. Den genauen Verlauf kann man gut der Abbildung entnehmen. Allerdings werden die Daten nicht in der üblichen Reihenfolge 7-6-5- 4-3-2-1-0 gesendet, sondern vorher noch um eine Bit-Position nach links rotiert; 6-5-4-3-2-1-0-7. Z.B. wird aus dem Tasten-Code für "J" 98 Amiga intern mit gesetztem achten Bit = 10100110 nach dem Rotieren 01001101. Das KEYup/down-Flag wird dadurch immer zuletzt übermittelt. Zusätzlich ist die Datenleitung noch Low-aktiv. D.h. eine 0 wird durch ein High und eine 1 durch ein Low dargestellt. Das Schieberegister im CIA des Amiga übernimmt mit jedem Clock- Impuls das aktuelle Bit an der SP-Leitung. Nach acht Clock-Impulsen hat das CIA ein komplettes Daten-Byte empfangen. Das CIA erzeugt dann normalerweise einen Interrupt der Ebene 2, der das Betriebssy¬ stem dazu veranlaßt, folgende Schritte zu unternehmen: ■ Lesen des seriellen Datenregisters des CIA. ■ Invertieren und Rechtsrotieren des Bytes, um wieder den ur¬ sprünglichen Tasten-Code zu erhalten. ■ Ausgabe des Handshake-Impulses. ■ Interne Weiterverarbeitung des empfangenen Codes. Synchronisation Um eine fehlerfreie Datenübertragung durchführen zu können, muß das Timing von Sender und Empfänger übereinstimmen. Die Bit-Posi¬ tion bei der seriellen Übertragung muß bei beiden identisch sein. Sonst kann es passieren, daß das Keyboard alle acht Bits ausgegeben hat, während sich der serielle Port des CIAs noch irgendwo mitten im Byte befindet. Ein solcher Verlust der Synchronisation tritt immer dann auf, wenn man den Amiga einschaltet oder die Tastatur in einen bereits laufenden Amiga einsteckt. Der Computer hat keine Möglich¬ keit, eine fehlerhafte Synchronisation zu erkennen. Diese Aufgabe wird von der Tastatur übernommen. Nach jedem ausgegebenen Byte wartet die Tastatur maximal 145 Mil¬ lisekunden auf das Handshake-Signal. Trifft es nicht innerhalb dieses Zeitraums ein, nimmt der Tastaturprozessor an, daß ein Übertra¬ gungsfehler vorliegt, und leitet einen speziellen Modus ein, mit dem er versucht, die verlorene Synchronisation wiederzugewinnen. Dazu gibt er immer eine 1 auf der KDAT-Leitung zusammen mit einem Clock- Impuls aus und wartet dann wieder 145 ms auf das Synchronisationssi¬ gnal. Dies wiederholt er solange, bis er ein Handshake-Signal vom Amiga empfängt. Damit ist die Synchronisation wieder hergestellt. Die Hardware des Amiga 99 Allerdings ist das vom Amiga empfangene Daten-Byte fehlerhaft. Der Zustand der ersten sieben Bits ist ungewiß. Lediglich das zuletzt empfangene Bit ist sicher eine 1, da der Tastaturprozessor während des oben beschriebenen Vorgangs ja nur Einsen ausgibt. Da dieses letzte Bit das KEYup/down-Flag ist, ist der fehlerhafte Tasten-Code immer ein KEYup-Code, also eine losgelassene Taste. Dadurch bleiben die möglichen Programmstörungen geringer, als wenn es sich bei dem fehlerhaften Code um einen KEYdown-Code gehandelt hätte. Aus diesem Grund wird jedes Byte vor der Übertragung um ein Bit links¬ rotiert, damit das KEYup/down-Flag immer das zuletzt gesendete Bit ist. Die Sonder-Codes Es gibt noch einige Sonderfälle in der Übertragung, die die Tastatur dem Amiga mittels spezieller Tasten-Codes mitteilt. Folgende Tabelle enthält alle möglichen Sonder-Codes: Code _ Bedeutung _ $F9 Letzter Tasten-Code war fehlerhaft $FA Tastenpuffer im Keyboard voll $FC Selbsttest der Tastatur war fehlerhaft $FD Beginn der beim Einschalten gedrückten Tasten $FE Ende der beim Einschaiten gedrückten Tasten SF9 Der Code $F9 wird von der Tastatur immer nach einem Verlust der Synchronisation und der darauf erfolgten Resynchronisation gesendet. Dadurch erfährt der Amiga, daß der letzte Tasten-Code fehlerhaft war. Nach diesem Code überträgt die Tastatur den verlorengegangenen Tasten-Code noch einmal. SFA Die Tastatur verfügt über einen internen Tastenpuffer von 10 Zei¬ chen. Wenn dieser Puffer voll ist, sendet sie eine $FA zum Computer, um ihm zu signalisieren, daß er den Puffer leeren muß, wenn keine Tasten-Codes verlorengehen sollen. SFC Nach dem Einschalten führt der Tastaturprozessor einen Selbsttest aus. Man erkennt dies am kurzzeitigen Leuchten der CAPS-LOCK-LED. 100 Amiga intern Entdeckt er dabei einen Fehler, sendet er ein $FC an den Amiga und geht dann in eine Endlosschleife, in der er die LED blinken läßt. $FD & SFE War der Selbsttest erfolgreich, überträgt die Tastatur alle Tasten, die während des Einschaltens gedrückt waren. Um dem Computer dies mitzuteilen, beginnt er diese Übertragung mit dem Code $FD. Es fol¬ gen die Codes der während des Einschaltens gedrückten Tasten, und zum Abschluß wird noch ein SFE zum Amiga geschickt. Danach be¬ ginnt die normale Übertragung. Waren keine Tasten gedrückt, werden $FD und SFE unmittelbar hin¬ tereinander gesendet. Reset über die Tastatur Die Tastatur hat auch noch die Möglichkeit, einen Reset des Amiga auszulösen. Drückt man beide Amiga-Tasten zusammen mit Control, legt der Tastaturprozessor die KCLK-Leitung für etwa 0.5 Sekunden auf Low. Dies veranlaßt die Reset-Schaltung im Amiga dazu, einen Prozessorreset auszulösen. Nachdem man mindestens eine der drei Ta¬ sten wieder losgelassen hat, führt die Tastatur auch selbst einen Reset aus. Man erkennt dies am Aufleuchten der CAPS-LOCK-LED. (In¬ teressant ist, daß dieser Reset über die KCLK-Leitung ausgelöst wird, die ja intern mit der CNT-Leitung des CIA-A verbunden ist. Man kann also durch entsprechende Programmierung dieses CIAs software¬ mäßig einen Hardware-Reset auslösen.) 1.4.3 Schwächen der Tastatur Zum Abschluß noch eine Bemerkung zu den Schwächen des Key¬ boards. Bevor Sie weiterlesen, ein kleines Experiment: Drücken Sie die drei Tasten "A","Q" und TAB gemeinsam. Keine Angst, der Amiga ist nicht kaputt. Aber es ist doch erstaunlich, daß die CAPS-LOCK-LED aufleuchtet, ohne daß diese Taste gedrückt wurde. Dasselbe funktio¬ niert mit allen anderen Tasten. Wenn man z.B. "S", "W" und "D" zu¬ sammen niederhält, wird mit hundertprozentiger Sicherheit auch noch ein "E" auf dem Bildschirm erscheinen. Die Hardware des Amiga 101 /V 0 Produzierte Tastencodes: d-|T]-s-h I Dieser Tastencode entsteht, ohne daD die entsprechende Taste gedrückt Hurde, Die Enstehung zusätzlicher Testertcodes Abb. 1.4.4 Die Abbildung versucht diesen Sachverhalt verständlich zu machen. Jede gedrückte Taste stellt einen Kurzschluß zwischen einer Spalte und einer Zeile dar. Der Tastaturprozessor steuert eine Spalte an und fragt dann die einzelnen Zeilen ab. Die Pfeile zeigen die Richtung dieser Abfrage. Sie bestimmt die Reihenfolge, in der die gedrückten Tasten erkannt werden, wenn man sie simultan niederdrückt. Wenn jetzt die zum "E" gehörige Spalte angesteuert wird und der Prozessor die entsprechende Zeile abfragt, während "D", "W" und "S" betätigt sind, erkennt er einen Kurzschluß zwischen Zeile und Spalte, den er natürlich für eine gedrückte "E"-Taste hält. Tatsächlich wird dieser Kurzschluß aber von den drei anderen Tasten hervorgerufen, doch der Tastaturprozessor hat keine Möglichkeit, dies zu erkennen. Noch schö¬ ner wird es, wenn man fünf Tasten simultan betätigt. Bei der Kombi¬ nation D, S, A, Q, I entstehen vier zusätzliche Codes, nämlich W, 2, Dieser Effekt tritt bei fast allen billigen Matrixtastaturen auf, er ist also nicht nur auf den Amiga beschränkt. Man sollte deshalb keine Programme entwerfen, die solche Ta¬ stenkombinationen benötigen. Die Amiga-, Shift-, Alternate- und Control-Tasten, die meistens gemeinsam mit anderen Tasten betätigt 102 Amiga intern werden, sind aus diesem Grund auch außerhalb der normalen Tasten¬ matrix angeordnet. 1.5 Die Programmierung der Hardware In den vorangehenden Kapiteln wurde der Hardware-Aufbau des Amiga näher betrachtet. Auf den folgenden Seiten geht es nun um die Programmierung der drei Custom-Chips. Nachdem die Hardwareseite klar ist, erfolgt jetzt eine Einführung in die Software, vor allem in die Erzeugung von Grafiken und Tönen. Die Voraussetzung für eine erfolgreiche Programmierung des Amiga auf Maschinenebene ist die Kenntnis der Speicherbelegung und der Adressen der einzelnen Chip-Register. Die Hardware des Amiga 103 1.5.1 Die Speicherbelegung Die Speicherkonfiguration Nornale Konfiguration $000000 $080000 512 KB Chip-RAM Spiegelung des jeweili¬ gen Bereichs von SFCBBDD - SFFFFFF Spiegelung des Ohip-ROHs $100000 Spiegelung des Ohip-ROHs $180000 Spiegelung des Ohip-ROHs $200000 $000000 8 MB Fast-RAM— Bereic h CIAs Basisadresse uon CIA-B $000000 Basisadresse uon CIA-A Asee A A20ee 512 KB Erwei terunsisr an $080000 Leer ■1 Asee « A2eee Echtzeituhr Basisadresse der Echtzeituhr | MjJljljljljl Oustnnchips Basisadresse der Custonchips] Leer $£80000 Bereich der Expansinnslots $F00000 ROH-Hodule $F80000 $F00000 256 KB Spiegelung des Kickstart-ROMs 256 KB Kickstart-ROMs Abb 1.6.1 Die erste Grafik zeigt die normale Speicherkonfiguration des Amiga, wie sie sich nach dem Booten dem Programmierer präsentiert. Der gesamte Adreßbereich des 68000 umfaßt 16 Megabyte (Adressen von 0 bis SFFFFFF). Auf Grund seiner Größe ist es kein Wunder, daß weite Bereiche unbelegt sind oder manche Chips mehrfach an verschiedenen Adressen auftauchen. Es gab ja keinen Grund, mit Speicher zu geizen. 104 Amiga intern Die Zeiten des vielfachen Bankswitchings sind mit dem 68000 glück¬ licherweise vorbei. Das RAM Der mit Chip-RAM bezeichnete RAM-Bereich enthält den normalen Speicher des Amiga. Ist bei einem Al000 die Speichererweiterung nicht eingesteckt, reicht er nur bis $3FFFF. Diese 512 KByte heißen Chip-RAM, da die drei Custom-Chips nur auf diesen Bereich zugrei¬ fen können. Es ist möglich, daß der Prozessor bei Speicherzugriffen auf das Chip- RAM durch die Aktivitäten der Custom-Chips gebremst wird. Will man dies verhindern, muß man seinen Amiga mit sogenanntem Fast RAM erweitern. Dieses liegt dann ab $200000 im Speicher und kann bis zu acht Megabyte einnehmen. Da die Custom-Chips auf diesen Bereich keinen Zugriff haben, kann der 68000 hier mit voller Ge¬ schwindigkeit arbeiten. Daher leitet sich auch der Begriff Fast RAM ab. Im Grundzustand enthält der Amiga allerdings noch keinerlei Fast RAM. Die 512-KByte-Erweiterungskarte des A500 oder A2000 liegt von SCOOOOO bis $C7FFFF. Sie nimmt eine Sonderstellung ein. Sie ist so¬ zusagen weder Fisch noch Fleisch, d.h. weder richtiges Chip-RAM noch Fast RAM. Einerseits haben die Custom-Chips keinen Zugriff auf diesen Speicherbereich, andererseits wird der Prozessor bei Zu¬ griffen auf diesen RAM-Bereich trotzdem von den Custom-Chips ge¬ bremst. Diese RAM-Erweiterung kombiniert also die schlechten Ei¬ genschaften sowohl des Chip-RAM als auch des Fast RAM, ohne de¬ ren positive zu übernehmen. Grund dafür ist allerdings nicht eine Bosheit der Amiga-Entwickler, sondern die Einfachheit dieser RAM- Erweiterung und damit deren billige Herstellungsmöglichkeit. Die CIAs Die verschiedenen Register der CIAs tauchen innerhalb des Bereichs von SAOOOOO bis SBFFFFF mehrfach auf. Genaueres über die Adres¬ sierung der CIAs kann man in Kapitel 1.2 nachlesen. Hier noch einmal die Adressen der einzelnen Register an ihren normalen Positionen: Die Hardware des Amiga 105 CIA-A CIA-B Name Funktion $BFE001 SBFDOOO PA Portregister A $BFE101 SBFDIOO PB Portregister B $BFE201 $BFD200 ODRA Datenrichtungsregister A $BFE301 $BFD300 DDRB Datenrichtungsregister B $BFE401 $BFD400 TALO Timer-A-Lo-Byte SBFESOI SBFOSOO TAHI Timer-A-Hi-Byte $BFE601 $BFD600 TBLO Timer-B-Lo-Byte $BFE701 $BFD700 TBHI Timer-B-Hi-Byte $BFE801 SBFDSOO E. LSB Eventcounter-Bits 0-7 $BFE901 $BFD900 E. MID Eventcounter-Bits 8-15 SBFEAOI SBFOAOO E. MSB Eventcounter-Bits 16-24 SBFEBOI SBFDBOO — Unbenutst $BFEC01 SBFOCOO SP Serielles Portregister SBFEDOl SBFDDOO IRC Interrupt-Kontrollregister SBFEEOl SBFDEOO CRA Kontrollregister A SBFEFOl SBFOFOO CRB Kontrollregister B Die Custom-Chips Die verschiedenen Register der Custom-Chips nehmen einen Bereich von 512 Bytes ein. Jedes Register hat eine Breite von 2 Bytes (ein Wort). Aus diesem Grund liegen alle Register auf geraden Adressen. Die Basisadresse des Registerbereichs liegt bei SDFFOOO. Die effektive Adresse beträgt demnach $DFFOOO + Registeradresse. Die folgende Liste gibt die Namen und die Funktionen der einzelnen Chip-Register wieder. Es ist klar, daß die meisten Registerbeschreibungen unbekannt sind, da ja über die Funktion der verschiedenen Register noch nichts gesagt worden ist. Aber diese Liste soll auch nur einen Überblick vermitteln und zum Nachschlagen von Registeradressen dienen. Es gibt vier verschiedene Registertypen: r (Read) Dieses Register kann nur gelesen werden. w (Wrile) Dieses Register kann nur beschrieben werden, s (Strobe) Ein Zugriff auf ein solches Register löst einen einmaligen Vorgang in dem entsprechenden Chip aus. Dabei ist der Wert des Datenbusses, also das Wort, das in das Register geschrieben werden soll, unerheb¬ lich. Diese Register werden gewöhnlich nur von Agnus angesprochen. 106 Amiga intern er (Early Read) Ein mit Early Read bezeichnetes Register ist ein DMA-Ausga- beregister. Es enthält die Daten, die per DMA ins Chip-RAM ge¬ schrieben werden sollen. Es gibt lediglich zwei solcher Register (DSKDATR und BLTDDAT - Ausgaberegister von Blitter und Disk). Sie werden ausschließlich vom DMA-Controller in Agnus angespro¬ chen, wenn ihr Inhalt ins Chip-RAM geschrieben werden soll. Der Prozessor kann auf diese Register nicht zugreifen. A, D, P Diese drei Buchstaben stehen für die drei verschiedenen Chips Agnus, Denise und Paula. Sie geben an, in welchem Chip das entsprechende Register untergebracht ist. Es kann auch möglich sein, daß ein Regi¬ ster in mehreren Chips untergebracht ist. Bei einem Schreibzugriff wird der Wert dann in beiden oder sogar in allen drei Chips gespei¬ chert. Dies ist der Fall, wenn der Inhalt eines bestimmten Registers von mehreren Chips benötigt wird. Für den Programmierer ist es unwesentlich, in welchem Chip sich ein bestimmtes Register befindet. Er kann den Bereich der Custom-Chips als Ganzes sehen, quasi als ein einziges Chip. Er benötigt nur Adresse und Funktion des gewünschten Registers. P. d Ein kleines "d" bedeutet, daß dieses Register nur vom DMA-Controller angesprochen wird. Register mit einem kleinen "p" davor werden nur vom Prozessor oder dem Copper benutzt. Stehen beide Buchstaben bei einem Register, wird dieses zwar gewöhnlich per DMA angesprochen, aber auch ab und zu vom Prozessor. Registeranzahl: 197 Register, die normalerweise nur vom DMA-Controller angesprochen werden: 54 Basisadresse des Registerbereichs: SDFFOOO Name Regadr. ChipR/W p/d Funktion BLTDDAT 000 A er d Blitter-Ausgabedaten (v. B. ins RAM] DMACONR 002 AP r P DMA-Kontrollregister lesen VPOSR 004 A r P Höchstwertiges Bit d. vertikalen Pos. VHPOSR 006 A r P Vertikale und horisontale Strahlpos. DSKDATR 008 P er d Disk-Lesedaten (von Disk in RAM) JOYODAT OOA D r P Joystick-/Mau8position Game-Port 0 Die Hardware des Amiga 107 JOYIDAT OOC D r P Joy8tick'/Mausposition Game-Port 1 CLXDAT OOE D r P Kol}i8ion8regiBter ADKCONR 010 P r P Audio/Di8k KontrollregiBter lesen POTODAT 012 P r P Potentiometer Game-Port 0 lesen POTIDAT 014 P r P Potentiometer Game-Port 1 lesen POTGOR 016 P r P Pot. Port Daten lesen SERDATR 018 P r P Seriellen Port und Status lesen DSKBYTR OIA P r P Diskdaten-Byte und Status lesen INTENAR OlC P r P Interrupt Enable lesen INTREQR OIE P r P Interrupt Request lesen DSKPTH 020 A w P Disk DMA-Adresse Bits 16-18 DSKPTL 022 A w P Disk DMA-Adresse Bits 1-15 DSKLEN 024 P w P Disk DMA-Blocklänge DSKDAT 026 P w d Disk Schreibdaten (vom RAM auf Disk) REFPTR 028 A w d Refresh-Zähler VPOSW 02A A w P MSB d. vertikalen Strahlpos. schreiben VHPOSW 02C A w P Vert. und horis. Strahlpos. schreiben COPCON 02E A w P Copper-Kontrollregister SERDAT 030 P w P Serielle Daten und Stop-Bits schreiben SERPER 032 P w P Ser.-Port-Kontrollreg. und -Baudrate POTGO 034 P w P Pot.-Port-Daten schreiben und -Start-Bit JOYTEST 036 D w P In beide Maussähler schreiben STREQU 038 D 6 d Horis. Sync mit VB und Equal Frame STRVBL 03A D 8 d Horis. Sync mit Vertical Blank STRHOR 03C DP 8 d Horizontales Synchronsignal STRLONG 03E D 8 d Kennzeichnung langer horiz. Zeile Die nun folgenden Register können vom Copper angesprochen wer¬ den, wenn COPCON = 1 ist. BLTCONO 040 A w p Bluter-Kontrollregister 0 BLTCONl 042 A w p Blitter-Kontrollregister 1 BLTAFWM 044 A w p Maske für erstes Datenwort von A BLTALWM 046 A w p Maske für letztes Datenwort von A BLTCPTH 048 A w p Adresse der Quelldaten C Bits 16-18 BLTCPTL 04A A w p Adresse der Quelldaten C Bits 1-15 BLTBPTH 04C A w p Adresse der Quelldaten B Bits 16-18 BLTBPTL 04E A w p Adresse der Quelldaten B Bits 1-15 BLTAPTH OSO A w p Adresse der Quelldaten A Bits 16-18 BLTAPTL 052 A w p Adresse der Quelldaten A Bits 1-15 BLTDPTH 054 A w p Adresse der Zieldaten D Bits 16-18 BLTDPTL 056 A w p Adresse der Zieldaten D Bits 1-15 BLTSIZE 058 A w p Start-Blit + Größe d. Blitter-Fensters — 05A Unbenutzt --- OSC Unbenutzt — OSE Unbenutzt BLTCMOD 060 A w p Blitter-Modulo für Quelldaten C BLTBMOD 062 A w p Blitter-Modulo für Quelldaten B BLTAMOD 064 A w p Blitter-Modulo für Quelldaten A BLTDMOD 066 A w p Blitter-Modulo für Zieldaten D — 068 Unbenutzt — 06A Unbenutzt — 06C Unbenutzt — 06E Unbenutzt BLTCDAT 070 A w d Blitter-Quelldatenregister C BLTBDAT 072 A w d Blitter-Quelldatenregister B BLTADAT 074 A w d Blitter-Quelldatenregister A 108- - Amiga intern 076 Unbenutzt — 078 Unbenutzt — 07A Unbenutzt — 07C Unbenutzt DSKSYNC 07E P w p Disk-Synchronisationsmuster Die nun folgenden Register können in jedem Fall vom Copper be¬ schrieben werden: COPILCH 080 A w p Adresse der 1. Copper-List Bits 16-18 COPILCL 082 A w p Adresse der 1. Copper-List Bits 1-15 COP2LCH 084 A w p Adresse der 2. Copper-List Bits 16-18 COP2LCL 086 A w p Adresse der 2. Copper-List Bits 1-15 COPJMPl 088 A 8 p Sprung zum Anfang 1. Copper-List COPJMP2 08A A s p Sprung zum Anfang 2. Copper-List COPINS 08C A w d Copper-Befehlsregister DIWSTRT 08E A w p Obere» linke Ecke des Anzeigefensters DIWSTOP 090 A w p Untere, rechte Ecke d.Anzeigefensters DDFSTRT 092 A w p Beginn Bit-PIane-DMA (Horiz. Pos.) DDFSTOP 094 A w p Ende Bit-Plane-DMA (Horiz. Pos.) DMACON 096 ADP w p DMA-Kontrollregister schreiben CLXCON 098 D w p Kollisionskontrollregister schreiben INTENA 09A P w p Interrupt enable schreiben INTREQ 09C P w p Interrupt request schreiben ADKCON 09E P w p Audio, Disk und UART Kontroll- register AUDOLCH OAO A w p Adresse der Audiodaten-Bits 16-18 AUDOLCL 0A2 A w p on Tonkanal 0 Bits 1-15 AUDOLEN 0A4 P w p Kanal 0 Länge der Audiodaten AUDOPER 0A6 P w p Kanal 0 Periodendauer AUDOVOL 0A8 P w p Kanal 0 Lautstärke AUDODAT OAA P w d Kanal 0 Audiodaten (zum D/A-Wandler) — OAC Unbenutzt _ OAE Unbenutzt AUDILCH OBO A w p Adresse der Audiodaten Bits 16-18 AUDILCL 0B2 A w p von Tonkanal 1 Bits 1-15 AUDILEN 0B4 P w p Kanal 1 Länge der Audiodaten AUDIPER 0B6 P w p Kanal 1 Periodendauer AUDIVOL 0B8 P w p Kanal 1 Lautstärke AUDIDAT OBA P w d Kanal 1 Audiodaten (zum D/A-Wandler) — OBC Unbenutzt OBE Unbenutzt AUD2LCH OCO A w p Adresse der Audiodaten Bits 16-18 AUD2LCL 0C2 A w p von Tonkanal 2 Bits 1-15 AUD2LEN 0C4 P w p Kanal 2 Länge der Audiodaten AUD2PER 0C6 P w p Kanal 2 Periodendauer AUD2VOL 0C8 P w p Kanal 2 Lautstärke AUD2DAT OCA P w d Kanal 2 Audiodaten (zum D/A-Wandler) — OCC Unbenutzt _ OCE Unbenutzt AUD3LCH ODO A w p Adresse der Audiodaten Bits 16-18 AUD3LCL 0D2 A w p von Tonkanal 3 Bits 1-15 AUD3LEN 0D4 P w p Kanal 3 Länge der Audiodaten AUD3PER 0D6 P w p Kanal 3 Periodendauer AUD3VOL 0D8 P w p Kanal 3 Lautstärke AUD3DAT ODA P w d Kanals Audiodaten (zum D/A-Wandler) Die Hardware des Amiga 109 — ODC — ODE BPLIPTH OEO A w p BPLIPTL 0E2 A w p BPL2PTH 0E4 A w p BPL2PTL 0E6 A w p BPL3PTH OES A w p BPL3PTL OEA A w p BPL4PTH OEC A w p BPL4PTL OEE A w p BPL5PTH OFO A w p BPL6PTL 0F2 A w p BPL6PTH 0F4 A w p BPL6PTL 0F6 A w p — 0F8 ... OFA — OFC — OFE BPLCONO 100 AD w p BPLCONl 102 D w p BPLCON2 104 D w p — 106 BPLIMOD 108 A w p BPL2MOD lOA A w p — IOC — lOE BPLIDAT 110 D w d BPL2DAT 112 D w d BPL3DAT 114 D w d BPL4DAT 116 D w d BPL5DAT 118 D w d BPL6DAT llA D w d ... IIC — IIE SPROPTH 120 A w P SPROPTL 122 A w P SPRIPTH 124 A w P SPRIPTL 126 A w P SPR2PTH 128 A w P SPR2PTL 12A A w P SPR3PTH 12C A w P SPR3PTL 12E A w P SPR4PTH 130 A w P SPR4PTL 132 A w P SPR5PTH 134 A w P SPR5PTL 136 A w P SPR6PTH 138 A w P SPR6PTL 13A A w P SPR7PTH 13C A w P SPR7PTL 13E A w P SPROPOS 140 AD w dp SPROCTL 142 AD w dp SPRODATA 144 D w dp SPRODATB 146 D w dp SPRIPOS 148 AD w dp SPRICTL 14A AD w dp SPRIDATA 14C D w dp SPRIDATB 14E D w dp Unbenutzt Unbenutzt Adresse von Bit-Plane 1 Bits 16-18 Adresse von Bit-Plane 1 Bits 1-15 Adresse von Bit-Plane 2 Bits 16-18 Adresse von Bit-Plane 2 Bits 1-15 Adresse von Bit-Plane 3 Bits 16-18 Adresse von Bit-Plane 3 Bits 1-15 Adresse von Bit-Plane 4 Bits 16-18 Adresse von Bit-Plane 4 Bits 1-15 Adresse von Bit-Plane 5 Bits 16-18 Adresse von Bit-Plane 5 Bits 1-15 Adresse von Bit-Plane 6 Bits 16-18 Adresse von Bit-Plane 6 Bits 1-15 Unbenutst Unbenutzt Unbenutzt Unbenutzt Bit-Plane Kontrollregister 0 Kontrollregister 1 (Scroll-Werte) Kontrollregister 2 (Prioritätskontr.) Unbenutzt Bit-Plane Modulo f. ungerade Planes Bit-Plane Modulo f. gerade Planes Unbenutzt Unbenutzt Bit-Plane 1 Daten (z. RGB-Ausgang) Bit-Plane 2 Daten (z. RGB-Ausgang) Bit-Plane 3 Daten (z. RGB-Ausgang) Bit-Plane 4 Daten (z. RGB-Ausgang) Bit-Plane 5 Daten (z. RGB-Ausgang] Bit-Plane 6 Daten (z. RGB-Ausgang) Unbenutzt Unbenutzt Sprite-Daten Sprite 0 Bits 16-18 Sprite-Daten Sprite 0 Bits 1-15 Sprite-Daten Sprite 1 Bits 16-18 Sprite-Daten Sprite 1 Bits 1-15 Sprite-Daten Sprite 2 Bits 16-18 Sprite-Daten Sprite 2 Bits 1-15 Sprite-Daten Sprite 3 Bits 16-18 Sprite-Daten Sprite 3 Bits 1-15 Sprite-Daten Sprite 4 Bits 16-18 Sprite-Daten Sprite 4 Bits 1-15 Sprite-Daten Sprite 5 Bits 16-18 Sprite-Daten Sprite 5 Bits 1-15 Sprite-Daten Sprite 6 Bits 16-18 Sprite-Daten Sprite 6 Bits 1-15 Sprite-Daten Sprite 7 Bits 16-18 Sprite-Daten Sprite 7 Bits 1-15 Sprite 0 Startposition (vert. + horiz.) Sprite 0 Kontrollreg. und vert. Stopp Sprite 0 Datenreg. A (z. RGB-Ausg.) Sprite 0 Datenreg. B (z. RGB-Ausg.) Sprite 1 Startposition (vert. + horiz.) Sprite 1 Kontrollreg. und vert. Stopp Sprite 1 Datenreg. A (z. RGB-Ausg.) Sprite 1 Datenreg. B (z. RGB-Ausg.) 110 Amiga intern SPR2POS 150 AD w dp SPR2CTL 152 AD w dp SPR2DATA 154 D w dp SPR2DATB 156 D w dp SPR3POS 158 AD w dp SPR3CTL 15A AD w dp SPR3DATA 15C D w dp SPR3DATB 15E D w dp SPR4POS 160 AD w dp SPR4CTL 162 AD w dp SPR4DATA 164 D w dp SPR4DATB 166 D w dp SPR5POS 168 AD w dp SPR5CTL 16A AD w dp SPR6DATA 16C D w dp SPR5DATB 16E D w dp SPR6POS 170 AD w dp SPR6CTL 172 AD w dp SPR6DATA 174 D w dp SPR6DATB 176 D w dp SPR7POS 178 AD w dp SPR7CTL 17A AD w dp SPR7DATA 17C D w dp SPR7DATB 17E D w dp COLOROO 180 D w P COLOROl 182 D w P COLOR02 184 D w P COLOR03 186 D w P COLOR04 188 D w P COLOR05 18A D w P COLOR06 18C D w P COLOR07 18E D w P COLOR08 190 D w P COLOR09 192 D w P COLORIO 194 D w P COLORll 196 D w P COLOR12 198 D w P COLOR13 19A D w P COLOR14 19C D w P COLORIE 19E D w P COLOR16 IAO D w P COLOR17 1A2 D w P COLOR18 1A4 D w P COLORIQ 1A6 D w P COLOR20 1A8 D w P COLOR21 IAA D w P COLOR22 lAC D w P COLOR23 lAE D w P COLOR24 IBO D w P COLOR25 1B2 D w P COLOR26 1B4 D w P COLOR27 1B6 D w P COLOR28 1B8 D w P COLOR29 IBA D w P COLOR30 IBC D w P COLOR31 IBE D w P Sprite 2 Startposition (vert. + horis.) Sprite 2 Kontrollreg. und vert. Stopp Sprite 2 Datenreg, A (e. RGB-Ausg.) Sprite 2 Datenreg. B (s. RGB-Ausg.^ Sprite 3 Startposition (vert. + horis.) Sprite 3 Kontrollreg. und vert. Stopp Sprite 3 Datenreg. A (s. RGB-Ausg.) Sprite 3 Datenreg. B (c. RGB-Ausg.) Sprite 4 Startposition (vert. + horis.) Sprite 4 Kontrollreg. und vert. Stopp Sprite 4 Datenreg. A (e. RGB-Ausg.) Sprite 4 Datenreg. B (e. RGB-Ausg.) Sprite 5 Startposition (vert. + horie.) Sprite 5 Kontrollreg. und vert. Stopp Sprite 5 Datenreg. A (e. RGB-Ausg.) Sprite 5 Datenreg. B (e. RGB-Ausg.^ Sprite 6 Startposition (vert. + horiE.) Sprite 6 Kontrollreg. und vert. Stopp Sprite 6 Datenreg. A (e. RGB-Ausg.) Sprite 6 Datenreg. B (e. RGB-Ausg.^ Sprite 7 Startposition (vert. + horU.) Sprite 7 Kontrollreg. und vert. Stopp Sprite 7 Datenreg. A (e. RGB-Ausg.) Sprite 7 Datenreg. B (e. RGB-Ausg.) Farbpalettenregister 0 (Color table) Farbpalettenregister 1 (Color table) Farbpalettenregister 2 (Color table) Farbpalettenregister 3 (Color table) Farbpalettenregister 4 (Color table) Farbpalettenregister 5 (Color table) Farbpalettenregister 6 (Color table) Farbpalettenregister 7 (Color table) Farbpalettenregister 8 (Color table) Farbpalettenregister 9 (Color table) Farbpalettenregister 10 (Color table) Farbpalettenregister 11 (Color table) Farbpalettenregister 12 (Color table) Farbpalettenregister 13 (Color table) Farbpalettenregister 14 (Color table) Farbpalettenregister 16 (Color table) Farbpalettenregister 16 (Color table) Farbpalettenregister 17 (Color table) Farbpalettenregister 18 (Color table) Farbpalettenregister 19 (Color table) Farbpalettenregister 20 (Color table) Farbpalettenregister 21 (Color table) Farbpalettenregister 22 (Color table) Farbpalettenregister 23 (Color table) Farbpalettenregister 24 (Color table) Farbpalettenregister 2$ (Color table) Farbpalettenregister 26 (Color table) Farbpalettenregister 27 (Color table) Farbpalettenregister 28 (Color table) Farbpalettenregister 29 (Color table) Farbpalettenregister 30 (Color table) Farbpalettenregister 31 (Color table) Die Hardware des Amiga 111 Die Register ICO bis IFC sind unbelegt. Ein Zugriff auf die Registeradresse IFE hat keinerlei Funktion zur Folge. Die Chips werden dabei nicht angesprochen (siehe Kapitel 1.2.3). Das ROM Die Abbildung 1.5.1 zeigt den ROM-Bereich, wie er nach dem Booten aussieht. Die 256 KByte ROM bei $FC0000 enthalten das Kickstart des Amiga. Der Bereich von $F80000 bis $FBFFFF ist mit dem Be¬ reich von SFCOOOO bis SFFFFFF identisch. In ihm spiegelt sich noch einmal das Kickstart-ROM. Allerdings kann sich diese Konfiguration ändern. Der 68000 holt nach einem Reset die Adresse des ersten Be¬ fehls aus der Speicherstelle 4, dem sogenannen Reset-Vektor. Wäre die Speicherkonfiguration unveränderlich, würde der 68000 den Reset- Vektor aus dem Chip-RAM holen, das ja an der Adresse 4 liegt. Da dessen Inhalt nach dem Einschalten unbestimmt ist, würde der Pro¬ zessor an eine willkürliche Adresse springen. Die Folge davon wäre, daß das System schon nach dem Einschalten abstürzen würde. Die Lösung sieht folgendermaßen aus: Das Chip, das für die Speicherkon¬ figuration zuständig ist, hat einen Eingang, der mit der untersten Portleitung von CIA-A (PAO) verbunden ist. Diese sogenannte OVL- Leitung (Memory Overlay) liegt im normalen Betrieb auf 0, und die Speicherkonfiguration entspricht der Abbildung. Nach einem Reset springt die Portleitung automatisch auf 1. Dadurch wird der ROM- Bereich von $F80000 bis SFFFFFF in den Adresßbereich von 0 bis $7FFFF eingeblendet. D.h. die Adresse 4 (der Reset-Vektor) ent¬ spricht dann der Adresse $F80004. Dadurch findet der 68000 eine gültige Reset-Adresse, die ihn ins Kickstart springen läßt. Im Laufe der Reset-Routine legt dieses dann die OVL-Leitung auf 0 und schaltet damit auf die normale Speicheranordnung zurück. Man muß sehr vorsichtig sein, wenn man mit dieser Leitung experi¬ mentieren will. Läuft das Programm, das die OVL-Leitung auf 1 set¬ zen soll, im Chip-RAM, kann das katastrophale Auswirkungen haben, denn das Programm schaltet sich quasi selber aus dem Speicher heraus, und der Prozessor landet irgendwo innerhalb des Kickstarts, das ja nach dem Umschalten den Platz des Chip-RAM einnimmt. 112 Amiga intern DasAIOOO-WOM Weitere Besonderheiten findet man bei den AlOOO-Modellen. Besitzer dieser Geräte haben sich sicher schon gewundert, daß hier andauernd von einem Kickstart-ROM gesprochen wird, obwohl der Amiga das Kickstart ja am Anfang von Diskette lädt. Nun, die Situation beim Al000 war folgende: Die Hardware war fertig, die Geräte verkaufs¬ bereit, aber mit der Software, in Form des Betriebssystems Kickstart, lag es noch im argen. Es war noch unvollständig und immer noch mit Fehlern behaftet. Also beschloß man, den Amiga mit einem speziellen RAM zu versehen, in welches nach dem Einschalten das Betriebssy¬ stem geladen wurde. Danach verriegelte der Amiga den Schreibzugriff auf dieses RAM. Es verhielt sich nun wie ein 256 KByte großes ROM. Man gab ihm bei Commodore den Namen WOM (Write Once Me¬ mory/Nur einmal beschreibbarer Speicher). Jetzt konnte man die er¬ sten Amiga mit der noch unvollständigen Kickstart-Version (1.0) aus¬ liefern. Nach Fertigstellung neuerer Kickstart-Versionen (1.1 und 1.2) brauchten die Amiga-Benutzer lediglich die neuen Kickstart-Disketten einlegen. Da dieses WOM natürlich teurer ist als ein einfacher ROM-Baustein, rüstete man den A500 und A2000 nicht mehr damit aus, da ja auch inzwischen eine endgültige Kickstart-Version, VI.2, fertig war. Das WOM wirft aber noch einige Fragen auf: Wo ist das Programm, das nach dem Einschalten Kickstart lädt? Wie kann ich Kickstart än¬ dern, wenn es ja in einem RAM liegt? Im Normalfall sieht der Betriebssystembereich im Al000 genauso wie in den neueren Modellen aus. Kickstart von SFCOOOO bis SFFFFFF mit Spiegelung bei $F80000. Versucht man, in das Kickstart zu schrei¬ ben, passiert gar nichts. Es ist kein Schreibzugriff möglich. Auch das sogenannte Boot-ROM, das Kickstart am Anfang lädt, ist nirgendwo eingeblendet. Gesteuert wird das Ganze nämlich über die Reset-Leitung. Nach ei¬ nem Reset, egal ob beim Einschalten, durch Drücken von Amiga & Control oder durch einen Reset-Befehl des 68000, ändert sich die Speicherkonfiguration. Danach liegt das Boot-ROM bei $F80000 (Da bei einem Reset gleich¬ zeitig die OVL-Leitung gesetzt wird, stammt auch der Reset-Vektor aus dem Boot-ROM), und es ist möglich, ins Kickstart zu schreiben! Die Hardware des Amiga 113 Man kann es jetzt nach Belieben verändern. Dieser Zustand bleibt aber nur solange erhalten, bis man versucht, etwas in den Bereich des Boot-ROMs von $F80000 bis SFBFFFF zu schreiben. Dann wird das Boot-ROM wieder ausgeblendet und der Schreibzugriff auf Kickstart verboten. Kurz gesagt: Reset erlaubt das Schreiben ins Kickstart und blendet das Boot-ROM ein. Ein Schreibzugriff auf eine Adresse zwischen $F80000 und SFBFFFF verbietet das Schreiben und schaltet das Boot-ROM aus. 1.5.2 Grundlagen Wie in dem vorangegangenen Kapitel erwähnt wurde, gibt es Register, die vom Prozessor angesprochen werden, und solche, die per DMA gelesen und beschrieben werden. Gehen wir zuerst auf den ersten Fall ein. Die Programmierung der Chip-Register Die Chip-Register können direkt adressiert werden. Beispiel: Der Wert des Hintergrundfarben-Registers soll verändert werden. Das Register hat den Namen COLOROO. Wenn man in der Tabelle aus 1.5.1 nach¬ schlägt, findet man eine Registeradresse von $180. Dazu kommt noch die Basisadresse des Registerbereichs, d.h. die Adresse des ersten Re¬ gisters auf den Adreßbereich des 68000 bezogen. Sie beträgt SDFFOOO. Plus der Registeradresse von COLOROO ergibt $DFF180. Ein einfacher MOVE.W-Befehl genügt zur Initialisierung des Registers: MOVE.U #Uert,$OFF180 ;Uert in COLOROO Sollen mehrere Register angesprochen werden, empfiehlt es sich, die Basisadresse in einem Adreßregister unterzubringen und die indirekte Adressierung plus Offset zu verwenden. Dazu ein Beispiel: LEA SDFF000,A5 ;Basisadresse in A5 speichern MOVE.U #Uert1,$180(A5) ;Uert1 in COLOROO MOVE.U #Uert2,$182(A5) ;Uert2 in COLOR01 MOVE.U ... usu. Normalerweise findet der Zugriff auf ein Chip-Register wie oben gezeigt statt. Allerdings kann man die Register auch als Langwort an¬ sprechen. In diesem Fall werden dann immer zwei Register auf einmal 114 Amiga intern beschrieben. Dies hat seinen Sinn bei den sogenannten Adreßregistern. Diese Register bestehen aus einem Registerpaar, das eine 19-Bit- Adresse enthält, mit der der ganze Chip-RAM-Bereich von 512 KByte angesprochen werden kann. Alle mit den Custom-Chips zusammen¬ hängenden Daten müssen im Chip-RAM liegen. Da die Chips den Speicher immer nur wortweise adressieren, ist das unterste Bit (Bit 0) unwesentlich. Das Adreßregister zeigt nur auf gerade Adressen. Da ein Chip-Register nur ein Wort, d.h. 16 Bit, breit ist, dienen zwei Regi¬ ster an aufeinanderfolgenden Registeradressen gemeinsam zur Auf¬ nahme der 19-Bit-Speicheradresse. Dabei enthält das erste die oberen 3 Bit (Bits 16 bis 18) und das zweite die unteren 16 (Bits 0 - 15). Da¬ durch ist es möglich, mit einem einzigen Langwort-Zugriff beide Re¬ gister zu initialisieren. Beispiel; Es soll der Zeiger für die erste Bit- Plane auf die Adresse $40000 gesetzt werden. BPLIPTH ist der Name des ersten Registers (Bits 16-18) und BPLIPTL (Bits 0-15) der des zweiten. Registeradresse von BPLIPTH: $0E0, BPLIPTL = $0E2. A5 enthält die Basisadresse SDFFOOO. MOVE.L #$40000,$0E0(A5) .-Initialisiert BPLIPTH und BPLIPTL mit den ;korrekten Werten. Es muß nochmals darauf hingewiesen werden, daß man ein Register nicht an ein und derselben Registeradresse lesen und beschreiben kann. Die meisten Register sind sowieso Nur-Schreib-Register. Sie können nicht gelesen werden. Dazu gehören auch die oben verwende¬ ten Register. Andere können ausschließlich gelesen werden. Nur einige wenige können sowohl gelesen als auch beschrieben werden. Sie be¬ sitzen dann aber zwei unterschiedliche Registeradressen. Eine zum Lesen und eine zum Schreiben. Das DMA-Kontrollregister, das nach¬ her noch näher besprochen werden soll, ist beispielsweise ein solches Register. Beschreiben kann man es an der Registeradresse $096 (DMACON). Zum Lesen muß man die Adresse $002 verwenden (DMACONR - Der Zusatz R steht für read = Lesen). DMA-Zugriff Unter DMA versteht man, wie schon in Kapitel 1.2.3 beschrieben, den direkten Zugriff eines Peripherie-Chips, des sogenannten DMA-Con- trollers, auf den Systemspeicher. Dies besagt auch die englische Be¬ zeichnung: DMA - Direct Memory Access/Direkter Speicherzugriff. Beim Amiga ist der DMA-Controller innerhalb von Agnus unterge¬ bracht. Er stellt die Verbindung der unterschiedlichen Ein-/Ausgaber Die Hardware des Amiga 115 Einheiten (engl. Input/Output, kurz I/O) der Custom-Chips mit dem Chip-RAM dar. Egal ob es um Disketten, Bildschirm oder Audiodaten geht, der DMA-Vorgang läuft immer nach dem gleichen Muster ab. Eine beliebige Ein-/Ausgabe-Einheit, z.B. der Disk-Controller, benö¬ tigt neue Daten oder hat Daten bereit, die in den Speicher müssen. Der DMA-Controller wartet dann auf den richtigen Zeitpunkt, an dem der Speicher für diesen DMA-Kanal frei, d.h. nicht von einem ande¬ ren DMA-Kanal oder dem Prozessor belegt ist, und überträgt die Da¬ ten selbständig ins RAM oder liest sie aus. Der Einfachheit halber gibt es keine spezielle Übertragung der Daten von der Ein-/Ausgabeeinheit zum DMA-Controller. Sie läuft ganz normal über Register ab. Jede dieser I/O-Einheiten besitzt zwei unterschiedliche Registertypen. Ein¬ mal die normalen Register, die vom Prozessor angesprochen und in denen die verschiedenen Betriebsparameter gespeichert werden, und andererseits die Datenregister, die die Daten für den DMA-Controller enthalten. Dieser spricht bei einem DMA-Transfer einfach simultan das entsprechende Datenregister und die zugehörige RAM-Speicher- stelle an. Je nach der Richtung des DMA-Transfers entweder ein Le¬ seregister und Chip-RAM auf Schreiben oder ein Schreibregister und Chip-RAM auf Lesen. Da beide dann über den Datenbus miteinander verbunden sind, wandern die Daten automatisch vom Datenregister ins RAM oder umgekehrt. Die Daten werden nicht in irgendwelchen in¬ ternen Registern zwischengespeichert. Durch den DMA-Transfer kommt noch ein dritter Registertyp dazu: Die DMA-Adreßregister, die, je nach den Bedürfnissen der zugehöri¬ gen I/O-Einheit, die Adresse/Adressen der Daten im RAM enthalten. Über all dem stehen dann noch die zentralen Kontrollregister, die nicht einer speziellen Ein-/Ausgabe-Einheit zugeordnet sind, sondern übergeordnete Steuerfunktionen haben. Unter diese Kategorie fällt auch oben erwähntes DMACON-Register. Die Datenregister können natürlich auch vom Prozessor beschrieben werden, da sie ja in Form normaler Register realisiert wurden. Dies hat aber für gewöhnlich keinen Sinn, da der DMA-Controller diese Aufgabe schneller und eleganter erledigt. Einige Ein-/Ausgabeeinheiten besitzen keinen zugehörigen DMA-Ka¬ nal. Bei ihnen muß der 68000 die Daten selber lesen bzw. schreiben. Dazu gehören aber nur jene Einheiten, bei denen von Natur aus keine hohen Datenmengen anfallen, ein DMA also nicht nötig ist, wie z.B die Joystick- und Mauseingänge. 116 Amiga intern Folgende DMA-Kanäle sind vorhanden: Bit-Ebenen-DMA Sprite-DMA Disk-DMA Audio-DMA Copper-DMA Blitter-DMA Über diesen DMA-Kanal werden die Bildschirmdaten aus dem Speicher gelesen und in die Datenregister der einseinen Bit-Ebe¬ nen geschrieben, von wo aus sie in die Bit-Ebenen-Sequenser gelangen, die die Daten für die Ausgabe auf den Bildschirm um¬ wandeln. Übertragung der Sprite-Daten aus dem RAM in die Sprite-Da- tenregister. Daten von Diskette ins RAM oder vom RAM auf die Diskette. Liest die digitalen Tondaten aus dem RAM und schreibt die in die entsprechenden Audiodatenregister. Über ihn erhält der Coprosessor (Copper) seine Befehlsworte. Daten von und sum Blitter. Es gibt also sechs DMA-Kanäle, die alle auf den Speicher zugreifen wollen, plus dem Prozessor, der natürlich auch möglichst oft das Chip-RAM für sich haben will. Um die dadurch entstehenden Schwierigkeiten zu lösen, hat man ein kompliziertes Zeitmuster ent¬ worfen, in dem man den einzelnen Kanälen feste Positionen zugewie¬ sen hat. Da sich dieses an dem Fernsehbild orientiert, müssen wir zu¬ erst kurz auf dessen Aufbau eingehen. Dieser Abschnitt ist so untech¬ nisch wie möglich gehalten, denn es geht in diesem Kapitel ja ums Programmieren der Custom-Chips, nicht mehr um die Hardware. Die Hardware des Amiga 117 Der Aufbau des Fernsehbilds Konplettes Vollbild: Aufbau «incs PAL**F«rns«hbi 1 ds Abb. 1.5.2.1 Das Zeitverhalten der Bildschirmausgabe eines deutschen Amigas richtet sich exakt nach der deutschen PAL-Fernsehnorm. Ein solches Fernsehbild setzt sich aus 625 horizontalen Zeilen zusammen. Jede dieser Zeilen wird von links nach rechts aufgebaut. Nach jeder Zeile folgt eine Pause, die sogenannte horizontale Austastlücke, in der der Elektronenstrahl, der das Bild zeichnet, Zeit hat, wieder von rechts nach links zurückzulaufen. Während dieser Austastphase ist der Elek¬ tronenstrahl dunkel geschaltet, damit der Strahlrücklauf nicht in Form störender Streifen sichtbar wird. Danach beginnt der Vorgang wieder von neuem, und die nächste Zeile entsteht. Damit das Bild flimmerfrei ist, muß es ständig neu gezeichnet werden. Da unser Auge Bildwechsel oberhalb einer bestimmten Frequenz nicht mehr einzeln wahrnehmen kann, hat man die Anzahl der Bilder pro Sekunde über diese Grenze gelegt. Bei der PAL-Norm liegt die Anzahl der einzelnen Bilder auf 50 pro Sekunde. Allerdings kommt jetzt ein Umstand hinzu, der die ganze Sache kompliziert. Würde man nämlich sämtliche 625 Zeilen 50 Mal in der Sekunde Zeichen, käme man auf 31250 Zeilen pro Sekunde. Als die Grundlagen dieses Fernsehsystems geschaffen wurden, war man nicht in der Lage, eine solch hohe 118 Amiga intern Zeilenfrequenz mit preisgünstigen, für jeden erschwinglichen Fernseh¬ apparaten zu verarbeiten. Also behalf man sich mit einem Trick. Ei¬ nerseits sollte die Anzahl der Bilder nicht unter 50 pro Sekunde liegen, da dies ein starkes Flimmern zur Folge hätte, andererseits durfte die, Zeilenzahl eines Bildes nicht zu niedrig liegen. Die Lösung sah folgen¬ dermaßen aus: Es werden weiterhin 50 Bilder pro Sekunde dargestellt. Allerdings werden die 625 Zeilen auf zwei Bilder verteilt. Im ersten Bild werden alle ungeraden Zeilen übertragen (Zeilen 1, 3, 5 ... 625), im zweiten dann alle geraden (2, 4, 6 ... 624). Folgerichtig gab man den 50 Bildern pro Sekunden den Namen Halbbilder, da sie Ja immer nur die Hälfte aller Zeilen enthalten. Zwei Halbbilder zusammen erge¬ ben dann das Vollbild, das Jetzt alle 625 Zeilen enthält. Die Anzahl der Vollbilder pro Sekunde ist natürlich nur halb so groß wie die Anzahl der Halbbilder, nämlich 25 pro Sekunde. Die Zeilenfrequenz beträgt durch diese Technik nur noch 15625 Hz (25*625 oder 50*312.5). Trotz der hohen Auflösung von 625 Zeilen tritt ein Flimmern nur dann auf, wenn eine Kontur lediglich auf eine Zeile beschränkt ist. Sie wird dann nur noch alle 25stel Sekunde einmal dargestellt, was für das Auge ein deutliches Flimmern zur Folge hat. Diesen Effekt kann man beim Fernsehen besonders an den horizontalen Rändern von Flächen beobachten, da diese naturgemäß aus einer einzigen horizon¬ talen Zeile bestehen. Die englische Bezeichnung für diese Technik der abwechselnden Übertragung von geraden und ungeraden Zeilen lautet Interlace. Zwei weitere Begriffe dienen der Unterscheidung der beiden Typen von Halbbildern. Mit Long Frame bezeichnet man Jenes, in dem die un¬ geraden Zeilen dargestellt werden, und mit Short Frame das andere. Long Frame und Short Frame deshalb, weil es eine ungerade Zeile mehr als gerade gibt, und dementsprechend das Halbbild mit den un¬ geraden Zeilen zeitlich gesehen länger ist. (Von 1 bis 625 gibt es 313 ungerade und 312 gerade Zahlen.) Nach jedem Halbbild folgt eine Pause, bevor das nächste Halbbild be¬ ginnt. Diese Schwarzphase zwischen zwei Halbbildern ist die vertikale Austastlücke. Auch das vom Amiga erzeugte Bild richtet sich nach obigen Grundsätzen. Allerdings mit kleinen Abweichungen. Normalerweise beginnt das zweite Halbbild (Short Frame) etwas ver¬ zögert, so daß die geraden Zeilen genau zwischen die ungeraden zu liegen kommen und sich ein geschlossenes Bild ergibt. Die Hardware des Amiga 119 Beim Amiga dagegen sind beide Halbbilder identisch. Dadurch liegt die Bildfrequenz bei tatsächlichen 50 Hz. Als Folge davon ist die Zei¬ lenzahl auf 313 beschränkt. Man erkennt auch deutlich die Zwischen¬ räume zwischen zwei Zeilen auf dem Monitor, da die Halbbilder nicht mehr versetzt zu einander gezeichnet werden. Um die Zeilenzahl zu erhöhen, hat der Amiga aber auch die Möglich¬ keit, sein Bild im Interlace-Modus zu erzeugen. Dann sind volle 625 Zeilen möglich. Man muß aber die Nachteile des Interlace-Betriebs mit in Kauf nehmen. Doch dazu später. 120 Amiga intern Der Aufbau der Amiga-Bildschirmausgabe Aufbau einer Bitplane GröBe: 320*200 Punkte n=Anfangsadre5se n+75fi0 n+7962 n+7964 n+799B Abb. 1.5.J.J Bit-Planes Der Amiga stellt sein Bild immer in einer Art Grafikmodus dar, d.h. jeder Punkt auf dem Bildschirm hat seine Entsprechung im Speicher. Im einfachsten Fall entspricTit ein gesetztes Bit im RAM einem Punkt auf dem Monitor. Dieser einfachste Aufbau eines Bildschirmspeichers heißt beim Amiga Bit-Ebene oder englisch Bit-Plane. Sie ist das Grundelement jeder Bildschirmdarstellung beim Amiga. Sie besteht aus Die Hardware des Amiga 121 einem zusammenhängenden Speicherbereich. Je nach Bildbreite erge¬ ben eine bestimmte Anzahl von Wörtern eine Bildschirmzeile. Ein Wort entspricht 16 Punkten, da ja jedes Bit einen Bildpunkt repräsen¬ tiert. Für eine Bildschirmdarstellung mit 320 Punkten pro Zeile benö¬ tigt man also 320/16 = 20 Wörter. Da man mit einer Bit-Plane ja nur zwei Zustände unterscheiden kann, nämlich Punkt gesetzt oder Punkt gelöscht, gibt es die Möglichkeit, mehrere Bit-Planes zu kombinieren. Dabei werden immer die Bits mit den gleichen Positionen in allen Pla¬ nes zusammengefaßt. Der erste Punkt auf dem Bildschirm entsteht durch Kombination des obersten Bits im ersten Wort sämtlicher Planes. Der aus diesen Bits resultierende Wert bestimmt dann die Farbe des Punkts auf dem Bildschirm. Um von der Bit-Kombination eines Punkts zu seiner Farbe auf dem Bildschirm zu kommen, gibt es ver¬ schiedene Möglichkeiten, auf die in Kapitel 1.5.5 noch detailliert ein¬ gegangen wird. Die verschiedenen Grafikauflösungen Der Amiga kennt zwei verschiedene horizontale Auflösungen. Der hochauflösende Modus hat normalerweise 640 Punkte pro Zeile, der niedrigauflösende 320. Das "normalerweise" im lezten Satz bedeutet, daß dieser Wert veränderlich ist. Besser ist es, wenn man die beiden unterschiedlichen Auflösungen durch die Zeit pro Bildpunkt definiert. Ein Pixel (= Bildpunkt) im hochauflösenden Modus wird 70 Nanose- kunden (Milliardstel Sekunden) lang angezeigt, im niedrigauflösenden Modus 140 Nanosekunden. In der doppelten Zeit legt der Elektronen¬ strahl die zweifache Strecke auf dem Bildschirm zurück. Aus diesem Grund sind die niedrigauflösenden Pixel auch doppelt so breit. Wichtiger für den Programmierer ist es aber zu wissen, daß im hoch¬ auflösenden Modus lediglich vier Bit-Planes gleichzeitig aktiviert sein dürfen, während im niedrigauflösenden bis zu 6 Planes erlaubt sind. Der Aufbau einer horizontalen Rasterzeile Mit dem Begriff Rasterzeile ist eine komplette horizontale Zeile ge¬ meint, d.h. die horizontale Austastlücke und der sichtbare Bereich. Diese Rasterzeile dient als Zeitmaß für alle DMA-Vorgänge, insbe¬ sondere natürlich für die mit dem Bildschirm zusammenhängenden DMAs. Um die Aufteilung der Rasterzeile zu verstehen, muß man wissen, wie die Speicherzugriffe auf das Chip-RAM und die Custom- Chip-Register zwischen dem DMA-Controller und dem Prozessor verteilt werden. Die Zugriffe auf diese beiden Speicherbereiche müs¬ sen sich nach den sogenannten Buszyklen richten. Die Buszyklen be- 122 Amiga intern stimmen das Timing des Chip-RAM. In jedem Buszyklus kann ein Speicherzugriff stattfinden. Ob die Daten gelesen oder beschrieben werden, ist dabei unwesentlich. Will z.B. der Prozessor auf den Bus zugreifen, bekommt er den Bus für einen Buszyklus zugewiesen. Der DMA-Controller kann dann erst wieder in dem darauffolgenden Zy¬ klus auf das RAM zugreifen. Ein Buszyklus dauert 280 Nanosekunden. Innerhalb einer Mikrosekunde sind also knapp vier Speicherzugriffe möglich. Der 68000 kann aber gar nicht so oft auf den Speicher zugreifen. Er ist dazu einfach nicht schnell genug. Bei der Taktfrequenz, mit der er im Amiga betrieben wird, führt er höchstens alle 560 Nanosekunden einen Zugriff aus. In der gleichen Zeit laufen aber zwei Buszyklen ab. Der 68000 kann also maximal jeden zweiten Buszyklus belegen. Diese Zyklen werden gerade Speicherzyklen (Even Cycles) genannt. Die übrigen, ungeraden Zyklen (Odd Cycles) sind damit ausschließlich für den DMA-Controller da. Die Hardware des Amiga 123 Refresh Audio-DMA ög1 1 che pIane*'D Datefetch-Start S28 $30 S4& S48 s s S S S L * L L t; L i li L L E * 5 5 & 6 7 7 4 6 2 3 5 4 4 6 2 3 5 1 4 i 2 L 1 t; L li t; L k L L k L t U L i t: L * 1^ El E 4 6 2 3 5 i 4 6 2 3 5 4 6 2 3 5 4 e 2 3 5 2 4 £ 2 — — > H fl H H H Ü H fl H fl H fl H H H fl H fl H fl Punkte 4 3 2 4 3 I 4 3 2 4 3 2 4 1 2 2 < -► Datafetch-Stop Lege nde Refresh Zg __ Zyklen D - Disk-OMA Zyklen Ax- Audio-DMA Zyklen, x=Kanalnunner Sx- Sprite-DMA Zyklen, x = Spr i tenunner , ,, ^ ^ Lx-* DnA —Zyklus einer n i edr i gauf 1 osenden Bitplane CLowres), x = Nun Hx- DMA-Zyk?us"einer hochauflösenden Bitplane CHires), x=Nunner der Bitplane |- gerader Buszyklus, Prozessor oder DMA 1— ungerader Buszyklus, nur DMA Abb. 1.6.2.3 Die Abbildung 1.5.2.3 zeigt den zeitlichen Ablauf einer Rasterzeile. Sie dauert 63.5 Mikrosekunden. Dies ergibt 227.5 Buszyklen pro Zeile. Davon können die ersten 225 vom DMA-Controller belegt werden. Wie dies geschieht, zeigt die Abbildung: Die Buchstaben innerhalb der einzelnen Zyklen stehen für den entsprechenden DMA-Kanal. Wäh¬ rend die ungeraden Zyklen ausschließlich vom DMA-Controller be¬ nutzt werden, muß sich dieser die geraden mit dem Prozessor teilen. 124 Amiga intern Dabei haben die DMA-Zugriffe immer Vorrang. Der Blitter-DMA und der Copper-DMA finden ausschließlich während gerader Zyklen statt. Allerdings gibt es für die beiden keine bestimmten zeitlichen Festlegungen. Der Copper-DMA belegt alle geraden Speicherzyklen, bis er seine Aufgabe beendet hat. Er hat dabei Vorrang vor dem Blit- ter. Auch der Blitter belegt alle geraden Zyklen, bis er fertig ist. Es gibt hier allerdings die Möglichkeit, einige Zyklen für den 68000 frei zu halten. Wie man sieht, belegen Disketten-, Audio- und Sprite-DMA lediglich ungerade Buszyklen, beeinflussen also nicht die Geschwindigkeit des Prozessors. Die vier mit "R" bezeichneten Buszyklen sind die soge¬ nannten Refresh-Zyklen. Sie dienen dazu, den Speicherinhalt des Chip-RAM aufzufrischen (siehe Ende dieses Kapitels). Etwas komplizierter ist die Verteilung der Bit-Plane-DMA. Um die ersten 16 Punkte auf dem Bildschirm darstellen zu können, müssen alle Bit-Planes gelesen werden. Während diese 16 Pixels auf dem Bildschirm erscheinen, müssen schon alle Bit-Planes für die nächsten 16 Punkte gelesen werden. Ist die niedrige Auflösung eingeschaltet, werden in jedem Buszyklus 2 Punkte ausgegeben. Dies bedeutet, daß alle acht Buszyklen die Bit-Planes gelesen werden müssen. Solange nicht mehr als vier Bit-Planes aktiviert sind, reichen dazu die ungera¬ den Zyklen aus. Werden aber fünf oder sechs Planes benutzt, müssen auch zwei gerade Zyklen verwendet werden, damit alle Daten inner¬ halb der acht Zyklen gelesen werden können. Noch enger wird es bei einer hochauflösenden Darstellung. Hier werden 4 Punkte pro Buszy¬ klus dargestellt. Sollen nur die ungeraden Zyklen belegt werden, dür¬ fen nicht mehr als zwei Hires-Bit-Planes aktiv sein. Bei der maximal möglichen Anzahl von vier Hires-Bit-Planes sind alle Buszyklen be¬ legt. Der Prozessor verliert dadurch mehr als die Hälfte seiner freien Buszyklen! Seine Geschwindigkeit nimmt dadurch etwa um den glei¬ chen Faktor ab, vorausgesetzt, daß sich das aktuelle Programm im Chip-RAM befindet, denn auf eventuelles Fast RAM und auf das Kickstart-ROM hat der Prozessor immer noch ungebremsten Zugriff. Die mit Datafetch-Start und Datafetch-Stop bezeichneten Zeitpunkte stellen den Beginn und das Ende der DMA-Zugriffe für die Bit-Pla¬ nes dar. Sie bestimmen damit die Breite und die horizontale Position des sichtbaren Bildes. Setzt der Bit-Plane-DMA früh ein und hört spät auf, werden mehr Datenwörter gelesen und damit auch mehr Punkte ausgegeben. Die normale Auflösung von 320 bzw. 640 Punkten pro Zeile läßt sich durch Ändern dieser Werte variieren. Setzt man den Die Hardware des Amiga 125 Datafetch-Start Wert kleiner $30, benutzt der Bit-Plane-DMA-Kanal die normalerweise für den Sprite-DMA reservierten Zyklen. Dadurch fallen je nach Wert von Datafetch-Start bis zu sieben Sprites aus. Le¬ diglich Sprite 0, der Ja im allgemeinen als Maus-Zeiger verwendet wird, läßt sich nicht auf diese Weise abschalten. Die obere Zeile in der Abbildung stellt die Verteilung der DMA-Zy- klen bei einem normalen, 320 Punkte breiten, niedrig auflösenden Bildschirm dar. Der Beginn des Bit-Plane-DMA, Datafetch-Start, liegt bei $38, und das Ende, Datafetch-Stop, bei $D0. In den mit "LI" be- zeichneten Zyklen werden die Daten der Bit-Plane Nr. 1 gelesen, in ”L2" dann Bit-Plane 2 usw. Sind die entsprechenden Bit-Planes nicht eingeschaltet, fallen auch die zugehörigen DMA-Zyklen weg. Die zweite Zeile stellt den Ablauf einer Rasterzeile dar, in dem die Datafetch-Punkte nach außen verlegt wurden. Bis zum Datafetch-Start ist der Verlauf mit der oberen Zeile identisch, bei $28 beginnt dann der Bit-Plane-DMA. Als Folge davon fallen die Sprites Nr. 5 bis 7 aus. Die Datafetch-Stop-Position wurde bis an den Maximalwert $D8 nach rechts verschoben. Die dritte Zeile zeigt die Verteilung der DMA-Zyklen in einem hochauflösenden Bildschirm, wobei die Datafetch-Werte mit denen der ersten Zeile übereinstimmen. Während der vertikalen Austastlücke finden keine Bit-Plane-DMA- Zugriffe statt. Das DMA-Kontrollregister Die einzelnen DMA-Kanäle werden über ein zentrales DMA-Kon¬ trollregister, DMACON, ein- und ausgeschaltet. DMACON Registeradr. $096 (schreiben) und $02 (lesen) Bit _ 15 14 13 12 und 11 10 g 8 7 Name SET/CLR BBUSY BZERO BLTPRI DMAEN BPLEN COPEN Funktion (wenn gesetet) _ Bits setzen/löechen Blitter arbeitet gerade (nur lesen) Ergebnis sämtlicher Blitter-Operationen war 0 (nur lesen) Unbelegt Blitter-DMA hat Priotität über Prozessor Gesamt-DMA einschalten (für Bits 0 bis 8) Bit-Plane DMA einschalten Copper-DMA einschalten 126 Amiga intern 6 5 4 3-0 BLTEN Blitter-DMA einschalten SPREN Sprite-DMA einschalten DSKEN Disk-DMA einschalten AUDxEN Audio-DMA für Tonkanal x einschalten (Bit-Nr. entspricht der Nummer des Tonkanals) Das DMACON-Register wird nicht wie ein normales Register be¬ schrieben. Man kann immer nur Bits setzen oder Bits löschen. Dies wird durch Bit 15 in dem Datenwort festgelegt, das man ins DMA¬ CON-Register schreibt. Ist dieses Bit auf 1, werden alle gesetzten Bits des Datenworts auch im DMACON-Register gesetzt. Ist Bit 15 dage¬ gen 0, werden alle gesetzten Bits im DMACON-Register gelöscht. Die übrigen Bits von DMACON bleiben immer unbeeinflußt. Das mit DMAEN bezeichnete Bit 9 dient quasi als Hauptschalter. Ist es auf 0, sind alle DMA-Kanäle inaktiv, ungeachtet der Bits 0 bis 8. Soll ein DMA erlaubt werden, müssen das Bit des entsprechenden DMA-Kanals und das DMAEN-Bit gesetzt werden. Dazu folgendes Beispiel: Es sei nur der Bit-Plane-DMA eingeschaltet (BPLEN = 1), aber ohne DMAEN-Bit. Der Wert des DMACON-Register beträgt also $0100. Jetzt soll der Disk-DMA erlaubt werden. DSKEN und DMAEN müs¬ sen gesetzt und BPLEN gelöscht werden. HOVE.U #$0100,$OFF096 .-Löscht das BPLEN-Bit (SET/CLR = 0) MOVE.U IW8210,$OFF096 ;Setzt DSKEN und DMAEN (SET/CLR = 1) Das DMACON-Register enthält jetzt den gewünschten Wert von $0210. Die Bits 13 und 14 können nur gelesen werden. Sie geben Aus¬ kunft über Zustände des Blitters, näheres darüber im Blitter-Kapitel. Bit 10 steuert die Priorität des Blitters über den Prozessor. Ist es ge¬ setzt, hat der Blitter absolute Priorität über den 68000. Dies kann so¬ weit gehen, daß der Prozessor während der gesamten Blitter-Operation keinen einzigen Zugriff auf ein Chip-Register oder das Chip-RAM durchführen kann. Wenn es gelöscht ist, bekommt der Prozessor jeden vierten geraden Buszyklus vom Blitter. Dies verhindert, daß der Pro¬ zessor länger angehalten wird, wenn eine Betriebssystemroutine oder ein Programm im Fast RAM, die ja beide ungebremst laufen, einmal auf das Chip-RAM zugreifen müssen. Z.B. auf eine Datenstruktur des Betriebssystems oder auf einen der Ausnahmevektoren des 68000. Die Hardware des Amiga 127 Die Abfrage der aktuellen Strahlposition Da sich das gesamte DMA-Timing an der Position innerhalb einer Rasterzeile orientiert, möchte man manchmal wissen, an welcher Stelle der Zeile sich der Elektrohenstrahl gerade befindet. Agnus besitzt dazu einen internen Zähler, der sowohl die horizontale als auch die vertikale Bildschirmposition enthält, nach der sich das gesamte System richtet. Zwei Register ermöglichen dem Prozessor den Zugriff auf diese Zähler: VH POS $006 (lesen, VHPOSR) und S02C (schreiben, VHPOSW) Bit-Nr.: 15 U 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Funktion: V7 V6 V5 V4 V3 V2 VI VO H8 H7 H6 H5 H4 H3 H2 Hl VPOS $004 (lesen, VPOSR) und $02A (schreiben, VPOSW) Bit-Mr.: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Funktion: LOF.V8 Die mit Hl bis H8 bezeichneten Bits stellen die horizontale Strahlposi¬ tion dar, sie entsprechen direkt den Nummern für die einzelnen Bus¬ zyklen in Abbildung 1.5.2.3 und haben damit eine Genauigkeit von zwei niedrig- oder vier hochauflösenden Punkten. Der Wert für die horizontale Position kann zwischen $0 und $E3 (0 bis 227) liegen. Die horizontale Austastlücke fällt in den Bereich von $F bis $35. Die Bits für die vertikale Position, also die aktuelle Bildschirmzeile, sind auf zwei Register verteilt. Die unteren Bits VO bis V7 liegen noch in VHPOS, das oberste Bit, V8, befindet sich in VPOS. Zusammen er¬ geben sie die Nummer der aktuellen Bildschirmzeile. Es sind Zeilen von 0 bis 312 möglich. Die vertikale Austastlücke (der Bildschirm ist in diesem Bereich immer schwarz) reicht von Zeile 0 bis 25. Das LOF-Bit (Long Frame) zeigt an, ob das gerade dargestellte Bild ein Long Frame (Halbbild mit ungeraden Zeilen) oder Short Frame (Halbbild mit geraden Zeilen) ist. Dieses Bit wird nur im In- terlace-Modus benötigt. Normalerweise liegt es auf 1. Man kann die Strahlposition auch setzen. Diese Möglichkeit wird aber kaum benötigt. Eine weitere Funktion kommt den POS-Registern im Zusammenhang mit einem Lightpen zu. Wenn der Lightpen-Eingang von Agnus aktiviert ist (s. Kap. 1.5.5), und der Lightpen gegen den Bildschirm gehalten wird, enthalten sie seine Position. D.h. ihr Inhalt wird eingefroren, sobald der Lightpen durch den an seiner Spitze 128 Amiga intern vorbeifahrenden Elektronenstrahl ausgelöst wurde. Nach Ende der vertikalen Austastlücke, also ab Zeile 26, werden die Zähler wieder freigegeben. Will man die Lightpen-Position lesen, muß man wie folgt Vorgehen: ■ Auf Zeile 0 (Beginn der vertikalen Austastlücke) warten. Dies kann man am einfachsten mittels des Vertical Blanking Inter¬ rupts erreichen (siehe nächstes Kapitel). ■ Beide Zählerregister lesen. Liegt die vertikale Position zwischen 0 und 25, also innerhalb der ver¬ tikalen Austastlücke, wurde kein Lightpen-Signal empfangen. Liegt der Wert dagegen außerhalb, stellt er die Position des Lightpens dar. Zum Abschluß dieses Kapitels noch einige Details zu den Refresh- Zyklen: Agnus besitzt einen integrierten 8-Bit-Refresh-Zähler. Er läßt sich über die Registeradresse $28 beschreiben (Vorsicht! Dabei kann der Speicherinhalt verloren gehen!!!). Zu Beginn jeder Rasterzeile legt Agnus vier Refresh-Adressen auf den Chip-RAM-Adreßbus. D.h. der Inhalt jeder Speicherzelle wird alle 4 Millisekunden einmal aufge¬ frischt. Während die Zeilenadresse auf dem Chip-RAM-Adreßbus ausgegeben wird, legt Agnus die Adressen bestimmter Strobe-Register auf den Registeradreßbus. Diese Strobe-Signale dienen dazu, den anderen Chips, Denise und Paula, den Beginn einer Rasterzeile oder eines Bil¬ des mitzuteilen. Dies ist notwendig, da sich die Zähler für die Bild¬ schirmposition innerhalb von Agnus befinden und keine Leitungen zur Übermittlung der Synchronisationssignale an die anderen Chips exi¬ stieren. Im einzelnen gibt es vier verschiedene Strobe-Adressen: Adr. Chip Funktion $38 D Vertikale Austastlücke eines Shortframes. $3A D Vertikale Austastlücke. $3C D P Diese Strobe-Adresse wird in jeder Rasterseile außerhalb der verti¬ kalen AustastlUcke erseugt- $3E D Kennseichnung einer langen Rasterseile (228 Zyklen) Während des ersten Refresh-Zyklus wird immer eine der drei oberen Strobe-Adressen angesprochen. Normalerweise $3C, innerhalb der Die Hardware des Amiga 129 vertikalen Austastlücke $38 oder $3A, je nach dem, ob es sich um ein Short- oder Longframe handelt. Mit der vierten Adresse hat es folgende Bewandnis; Eine Rasterzeile hat rein rechnerisch eine Länge von 227.5 Buszyklen. Da es aber keine halben Zyklen gibt, wechseln Zeilen mit 227 und 228 Buszyklen ein¬ ander ab. Die Strobe-Adresse $3E signalisiert die 228 Zyklen langen Zeilen und wird während des zweiten Refresh-Zyklus erzeugt. 1.5.3 Interrupts Fast alle Ein-/Ausgabeeinheiten der Custom-Chips und die beiden CIAs sind in der Lage, einen Interrupt auszulösen. Eine spezielle Schaltung innerhalb Paulas übernimmt die Verwaltung der einzelnen Interrupt-Quellen und erzeugt daraus die Interrupt-Signale für den 68000. Dabei werden die sogenannten Autovektor-Interrupts des Pro¬ zessors benutzt. Und zwar die der Ebenen 0 bis 6. Der nicht maskier¬ bare Interrupt (NMI) der Ebene 7 ist nicht vorgesehen. Die beiden Register sind das Interrupt-Anforderungs-Register (INTREQ, Inter¬ rupt-Request) und das Interrupt-Masken-Register (INTENA, Inter- rupt-Enable). Die Belegung der einzelnen Bits ist in beiden Registern identisch. Interrupt-Enable- und Interrupt-Request-Registerbelegung Registeradressen: INTREQ INTREQR INTENA INTENAR = $09C (schreiben) = $01E (lesen) = $09A (schreiben) = $01C (lesen) Bit Name IE Funktion 15 SET/CLR Schreiben/lesen (siehe DMACON-Register) 14 INTEN (6) Interrupts erlauben 13 EXTER 6 Interrupt von CIA-B oder Expansion-Port 12 DSKSYN 5 Disk-Synchronisationswert erkannt 11 RBF 5 Eingabepuffer des Seriellen Ports voll 10 AUD3 4 Audiodaten Kanal 3 ausgegeben 9 AUD2 4 Audiodaten Kana! 2 ausgegeben 8 AUDI 4 Audiodaten Kanal 1 ausgegeben 7 AUDO 4 Audiodaten Kanal 0 ausgegeben 6 BLIT 3 Blitter fertig 5 VERTB 3 Beginn der vertikalen AustastlUcke erreicht 4 copsai 3 Reserviert für Copper-Interrupts 3 PORTS 2 Interrupt von CIA-A oder Expansion-Port 130 Amiga intern 2 SOFT 1 Reserviert für Software-Interrupts 1 DSKBLK 1 Disk-DMA-Transfer beendet 0 TBE 1 Ausgabepuffer des seriellen Ports leer Die unteren dreizehn Bits stehen für die einzelnen Interrupt-Quellen. Die CIA-Interrupts wurden je zu einem einzigen Interrupt zusammen¬ gefaßt. Die Bits im DMAREQ-Register informieren darüber, welche Interrupts aufgetreten sind. Es wird in diesem Fall das zugehörige Bit gesetzt. Um einen Prozessor-Interrupt auszulösen, müssen das korre¬ spondierende Bit im DMAENA-Register und auch das INTEN-Bit gesetzt sein. Das INTEN-Bit agiert dabei als Hauptschalter für die übrigen 14 Interrupt-Quellen, die mit den Bits des INTENA-Registers getrennt ein- und ausgeschaltet werden können. Nur wenn INTEN auf 1 liegt, können überhaupt Interrupts ausgelöst werden. Sind sowohl das INTEN-Bit als auch zwei zusammengehörige Bits im INTENA- und INTREQ-Register gesetzt, wird ein Prozessor-Interrupt ausgelöst. Die entsprechenden Autovektornummern befinden sich in der mit IE (Interrupt-Ebene) bezeichneten Spalte der Tabelle. Zur Erinnerung hier noch einmal die Adressen der 7 Interrupt-Autovek¬ toren: Vektor-Nr. Adresse (Des/Hex) Autovektor Ebene 25 100/$6A Autovektor Ebene 1 26 104/$68 Autovektor Ebene 2 27 108/$6C Autovektor Ebene 3 28 112/$70 Autovektor Ebene 4 29 116/$74 Autovektor Ebene 5 30 120/$78 Autovektor Ebene 6 (31 124/$7C Autovektor Ebene 7) Wie man sieht, wurden den Interrupts, die eine schnellere Bearbeitung erfordern, höhere Interrupt-Ebenen verliehen. Um die Bits in den beiden Registern zu ändern muß man, wie schon beim DMACON-Register beschrieben (1.5.2), mit einem SET/CLR-Bit arbeiten. Nach der Bearbeitung eines Interrupts muß das auslösende Bit im IN¬ TREQ-Register vom Prozessor zurückgesetzt werden. Im Gegensatz zu den Interrupt-Kontrollregistern der CIAs werden die Bits des IN- TREQ-Registers nicht automatisch beim Lesen gelöscht. Die Hardware des Amiga 131 Wenn man ein Bit im INTREQ-Register mittels eines MOVE-Befehls setzt, hat dies dieselbe Wirkung, als wenn der entsprechende Interrupt aufgetreten wäre. Auf diese Weise wird z.B. der Software-Interrupt (SOFT, Bit 2) erzeugt. Auch der Copper kann nur durch Schreiben in INTREQ seinen Interrupt erzeugen. Eine Besonderheit ist das Bit 14 im INTREQ-Register, es hat dort keine spezifische Funktion wie in INTENA. Aber wenn man es durch Schreiben in INTREQ setzt und sich INTEN im INTENA-Register auf High befindet, wird ein Interrupt der Ebene 6 erzeugt. Bei jedem Interrupt von CIA-A wird Bit 3 im DMAREQ-Register gesetzt. Für CIA-B ist es Bit 13. Die Interrupt-Quelle in dem ent¬ sprechenden CIA muß durch Lesen des Interrupt-Kontrollregisters des CIAs ermittelt werden. Die Interrupts Nr. 3 und 13 können auch durch Erweiterungskarten am Expansion-Port ausgelöst werden. Das Interrupt-Bit 5 zeigt den sogenannten Vertical Blank Interrupt an. Dieser tritt immer zum Beginn jedes Halbbilds auf, am Start der ver¬ tikalen Austastlücke (Zeile 0) und damit 50mal in der Sekunde. Die übrigen Interrupts werden in den dazugehörigen Kapiteln behandelt. 1.5.4 Der Coprozessor Copper Der Copper ist ein einfacher Coprozessor. Er hat die Aufgabe, die verschiedenen Register der Custom-Chips zu festgelegten Zeitpunkten automatisch mit bestimmten Werten zu beschreiben. Genauer gesagt ist der Copper in der Lage, an beliebigen Bildschirmpositionen die Inhalte einiger Register zu verändern. Er kann dadurch den Bildschirm in unterschiedliche Bereiche aufteilen, die dann verschiedene Farben und Auflösungen haben können. Diese Fähigkeit wird z.B. bei der Ver¬ wendung mehrerer Screens benutzt. Der Copper wird deshalb als Coprozessor bezeichnet, weil er, wie ein richtiger Prozessor auch, über ein Programm verfügt, das sich im Speicher befindet und von ihm Befehl für Befehl abgearbeitet wird. Allerdings kennt der Copper nur drei verschiedene Befehle, mit denen man aber eine Menge anfangen kann: MOVE Der Move-Befehl schreibt einen unmittelbaren Wert in ein beliebiges Custom-Chip-Register. 132 Amiga intern WAIT Der Wait-Befehl wartet darauf, daß der Elektronenstrahl eine be¬ stimmte Bildschirmposition erreicht. SKIP Der Skip-Befehl überspringt den nächsten Befehl, wenn der Elektro¬ nenstrahl eine festgelegte Bildschirmposition schon erreicht hat. Mit diesem Befehl lassen sich bedingte Verzweigungen programmieren. Das Programm für den Copper nennt man Copper-List. In ihr liegen die Befehle direkt hintereinander, wobei jeder Befehl immer aus zwei Wörtern besteht. Beispiel; Walt (XI,YD Hove #0,$180 Hove #9,$181 Uait (X2,Y2) ;Uartet, bis die Bildschirmposition X1,Y1 erreicht ist ;Schreibt den Wert 0 in das Hintergrundfarbregister ;Schreibt den Wert 1 in das Farbregister 1 ;Uartet, bis die Bildschirmposition X2,Y2 erreicht ist usw. Zum Betrieb des Coppers reicht die Copper-List alleine nicht aus. Es sind noch einige Register notwendig, die die für den Copper notwen¬ digen Parameter enthalten. Die Copper-Register: Reg. Name Funktion $080 COPILCH Diese beiden Register enthalten gemeinsam die $082 COPILCL 18-Bit'Adresse der ersten Copper-List. $084 COP2LCH Diese beiden Register enthalten gemeinsam die $086 COP2LCL IS-Bit-Adresse der sweiten Copper-List $088 COPJMPl Laden der Adresse der ersten Copper-List in den Programmsähler des Coppers $08A COPJMP2 Laden der Adresse der sweiten Copper-List in den Programmsähler des Coppers $02E COPCON Dieses Register enthält nur ein einziges Bit (Bit 0). Ist es gesetst, kann der Copper auch auf die Register von $040 bis $7E sugreifen. (Dies sind die zum Blitter gehörenden Register.) Sämtliche Copper-Register sind Nur-Schreib-Register. Die beiden COPxLC-Register enthalten jeweils die Adresse einer Copper-List. Da diese Adresse 19 Bit lang ist, werden zwei Register pro Adresse benötigt. Sie können, wie in Kapitel 1.5.2 besprochen, mit einem MOVE.L-Befehl in das erste Register gemeinsam beschrieben werden. Die Copper-List muß, wie sonstige Daten für die Custom- Chips, innerhalb der 512 KByte Chip-RAM liegen. Die Hardware des Amiga 133 Der Copper benutzt einen internen Programmzähler als Zeiger auf den aktuellen Befehl. Er wird mit jedem abgearbeiteten Befehl um zwei Worte erhöht. Um den Copper jetzt bei einer bestimmten Adresse be¬ ginnen lassen zu können, muß die Anfangsadresse der Copper-List in den Programmzähler übertragen werden. Dazu dienen die beiden COPJMPx-Register. Sie stellen sogenannte Strobe-Register da, d.h. ein Wert, den man in eines dieser Register schreibt, wird nicht gespei¬ chert, sodern dient lediglich als Auslöser für eine einmalige Aktion. Aus diesem Grund ist der Wert auch völlig gleichgültig, es kommt nur auf den Zugriff auf ein solches Register an. Beim Copper dienen diese beiden Register dazu, den Inhalt des ent¬ sprechenden COPxLC-Registers in den Programmzähler zu übertragen. Schreibt man also in COPJMPl, wird die Adresse in COPILC in den Programmzähler übertragen, was zur Folge hat, das der Copper die Ausführung seines Programms dort fortsetzt. Gleiches gilt für COPJMP2 und COP2LC. Am Beginn der vertikalen Austastlücke, in Zeile 0, wird der Pro¬ grammzähler automatisch mit dem Wert von COPILC geladen. Dies hat zur Folge, das der Copper in jedem Bild das selbe Programm aus¬ führt. Der Aufbau der Befehle MOVE WAIT SKIP Bit BWl BW2 BWl BW2 BWl BW2 15 X DW15 VP7 BFD VP7 BFD 14 X DW14 VP6 VM6 VP6 VM6 13 X DW13 VP5 VMS VP5 VMS 12 X DW12 VP4 VM4 VP4 VM4 11 X DWll VP3 VM3 VP3 VMS 10 X DWIO VP2 VM2 VP2 VM2 9 X VPl VMl VPl VMl 8 RA8 VPO VMO VPO 7 RA7 HP8 HM8 HP8 HM8 6 RA6 ^B HP7 HM7 HP7 HM7 5 RAS DW5 HP6 HM6 HP6 HM6 4 RA4 HP5 HM5 HP5 HM5 3 RA3 HP4 HM4 HP4 HM4 2 RA2 DW2 HP3 HM3 HP3 HM3 1 RAI DWl HP2 HM2 HP2 HM2 0 0 DWO 1 0 1 1 134 Amiga intern Legende: X Dieses Bit ist unbenutst. Sollte mit 0 initialisiert werden. RA Registeradresse DW Daten wort VP Vertikale Strahlposition VM Vertikale Masken-Bits HP Horizontale Strahlposition HM Horizontale Masken-Bits BFD Blitter Finish Disable Der MOVE-Befehl Der MOVE-Befehl wird gekennzeichnet durch eine 0 in Bit 0 des er¬ sten Befehlsworts. Mittels dieses Befehls ist es möglich, ein Custom- Chip-Register mit einem unmittelbaren Wert zu beschreiben. Die Re¬ gisteradresse des gewünschten Registers kommt in die unteren 9 Bits des ersten Datenworts. Dabei muß Bit 0 immer 0 bleiben (Ist bei den Registeradressen sowieso schon auf 0, da die Register ausschließlich auf geraden Adressen liegen). Das zweite Befehlswort enthält das Da- ten-Byte, das in das Register geschrieben werden soll. Bei der Registeradresse gibt es einige Einschränkungen. Normalerweise kann der Blitter die Register im Bereich von $000 bis $07F nicht be¬ einflussen. Setzt man das unterste (und auch einzige) Bit im COP- CON-Register, ist es dem Copper auch möglich, in die Register von $040 bis $07F zu schreiben. Dadurch kann der Copper dann den Blit¬ ter benutzen. Ein Zugriff auf die untersten Register ($000 bis $03F) ist allerdings immer verboten. Der WAIT-Befehl Der WAIT-Befehl wird durch eine 1 in Bit 0 des ersten und eine 0 in Bit 0 des zweiten Befehlsworts gekennzeichnet. Er veranlaßt den Blit¬ ter, mit der weiteren Befehlsausführung bis zum Erreichen der ge¬ wünschten Strahlposition zu warten. Ist sie beim Auftreten eines Wait- Befehls schon größer als die im Befehl angegebene, der Strahl also schon an der gewünschten Position vorbei, macht der Copper sofort mit der nächsten Instruktion weiter. Diese Position läßt sich getrennt für die vertikalen Zeilen und hori¬ zontalen Spalten einstellen. Vertikal beträgt die Auflösung eine Ra¬ sterzeile. Da nur acht Bit für die vertikale Position vorgesehen sind, es aber 313 Zeilen gibt, kann der Wait-Befehl nicht zwischen den ersten 256 und den restlichen 57 Zeilen unterscheiden. Die untersten 8 Bits (mehr lassen sich im Wait-Befehl nicht angeben) sind z.B. sowohl in Zeile 0 und Zeile 256 gleich. Will man gezielt auf eine Zeile im unte¬ ren Bereich warten, muß man sich mit zwei WAIT-Befehlen behelfen. Die Hardware des Amiga 135 1. WAIT auf Zeile 255. 2. WAIT auf gewünschte Zeile, unter Vernachlässigung des 9. Bits. Horizontal gibt es 112 mögliche Positionen, da die beiden unteren Bits der horizontalen Position, HPO und HPl, nicht angegeben werden können. Das Befehlswort des MOVE-Befehls enthält ja nur die Bits HP2 bis HP8. Die horizontale Koordinate eines WAIT-Befehls läßt sich nur in Schritten von vier niedrig auflösenden Punkten angeben. Das zweite Befehlswort enthält die sogenannten Maskenbits. Mit ihnen kann man festlegen, welche Bits der horizontalen und vertikalen Posi¬ tion überhaupt zum Vergleich mit der aktuellen Strahlposition heran¬ gezogen werden. Nur die Positions-Bits, deren zugehörige Masken-Bits gesetzt sind, werden beachtet. Dies eröffnet vielfältige Möglichkeiten: Uait vertikale Position SOF und vertikale Maske SOF bewirkt, daß alle 16 Zeilen die Wait-Bedingung erfüllt wird, nämlich immer, wenn die unteren 4 Bits auf 1 sind, da Bits 4 bis 6 nicht mehr in den Vergleich mit einbezogen werden (Masken-Bits 4 bis 6 sind auf 0). Das 7. Bit der vertikalen Position läßt sich nicht maskieren. Aus diesem Grund funktioniert das obige Beispiel nur im Bereich der Zei¬ len 0 bis 127 und 256 bis 313. Das BFD(Blitter Finish Disable)-Bit hat folgende Funktion; Soll der Copper dazu benutzt werden, eine Blitter-Operation zu starten, muß er wissen, wann der Blitter mit der vorangegangenen fertig ist. Ist das BFD-Bit gelöscht, wartet der Copper bei jedem Wait-Befehl, bis der Blitter seine Operation beendet hat. Erst dann wird die übrige Wait- Bedingung geprüft. Dies kann man verhindern, indem man das BFD- Bit setzt. Dadurch ignoriert der Copper den aktuellen Blitter-Status. Wenn der Copper keine Blitter-Register beeinflussen soll, setzt man dieses Bit daher auf 1. Der SKIP-Befehl Der SKIP-Befehl ist in seinem Aufbau mit dem Wait-Befehl identisch. Lediglich Bit 0 im zweiten Befehlswort ist gesetzt, um den Skip-Be¬ fehl vom Wait-Befehl zu unterscheiden. Der Skip-Befehl prüft, ob die tatsächliche Strahlposition größer oder gleich der im Befehlswort fest¬ gelegten ist. Fällt dieser Vergleich positiv aus, überspringt der Copper den nächsten Befehl. Sonst fährt er in seiner Programmabarbeitung sofort mit dem darauffolgenden Befehl fort. Der Skip-Befehl ermög¬ licht damit den Aufbau bedingter Verzweigungen. So kann der auf 136 Amiga intern Skip folgende Befehl ein Move in eines der COPJMP-Register sein, wodurch dann je nach Strahlposition ein Sprung ausgelöst werden würde. Der Aufbau einer Copper-Liat Eine einfache Copper-List besteht aus einer Abfolge von Wait- und Move-, seltener auch Skip-Befehlen. Ihre Anfangsadresse befindet sich in COPLCl. Um die Copper-List zu beenden, muß zu einem Trick gegriffen werden. Nach der letzten Instruktion folgt noch ein Wait- Befehl, der aber eine unmögliche Strahlposition als Bedingung hat. Dadurch wird die Abarbeitung der Copper-List beendet, bis durch den Anfang eines neuen Bildes wieder die Adresse von COPLCl in den Programmzähler des Coppers geladen und die Copper-List erneut bearbeitet wird. Wait ($0,$fe) erfüllt diese Bedingung, denn eine ho¬ rizontale Position größer $E4 ist nicht möglich. Der Copper-Interrupt Wie man weiß, gibt es in den Interrupt-Registern ein spezielles Bit für den Copper-Interrupt. Diesen Interrupt kann man mit einem Move- Befehl in das INTREQ-Register auslösen; MOVE #$8010,INTREQ ;SET/CLR und COPER setzen Genauso könnte man auch jedes andere Bit dieses Registers be¬ einflussen, aber Bit 4 ist speziell für den Copper vorgesehen. Ein Copper-Interrupt kann dazu dienen, dem Prozessor das Erreichen einer bestimmten Bildposition mitzuteilen. Dadurch lassen sich soge¬ nannte Raster-Interrupts programmieren, d.h. die Unterbrechung des Prozessors in einer bestimmten Bildschirmzeile (und Spalte). Der Copper-DMA Der Copper holt seine Befehle über einen eigenen DMA-Kanal aus dem Speicher. Er belegt die geraden Buszyklen und hat dabei Vorrang vor Blitter und 68000. Jeder Befehl benötigt zwei Zyklen, da ja zwei Befehlsworte gelesen werden müssen. Der Wait-Befehl benötigt noch einen zusätzlichen Zyklus beim Erreichen der gewünschten Strahlposi- Die Hardware des Amiga 137 tion. Während der Wartephase eines Wait-Befehls gibt der Copper den Bus frei. Das COPEN-Bit im DMACON-Register dient dazu, den Copper-DMA ein- und auszuschalten. Löscht man dieses Bit, gibt der Copper den Bus frei und führt keine weiteren Befehle aus. Setzt man es, beginnt er seine Programmausführung bei der Adresse in seinem Programm¬ zähler. Es ist daher unbedingt notwendig, vor dem Einschalten des Copper-DMA für eine verläßliche Adresse zu sorgen. Ein in unge¬ wissen Speicherbereichen laufender Copper kann das System zum Ab¬ sturz bringen. Die übliche Initialisierungssequenz für den Copper sieht daher folgendermaßen aus: LEA $DFFOOO,A5 MOVE.U #$0080,DMACON(A5) MOVE.L #Copperlist,COPlLCH(A5) COPJHPI(AS) MOVE.W #$8080,DHAC0N(AS) ;Basisadresse der Register nach A5 ;Copper-DMA aus ;Adresse der Copper-List setzen ;Adresse in Copper-Programmzähler ;übertragen ;Copper-DHA wieder einschalten Beispielprogramm Zum Abschluß noch ein Beispielprogramm. Es bringt mit Hilfe von zwei WAIT- und drei MOVE-Befehlen die deutsche Fahne auf den Bildschirm. Sie läßt sich schon mit einer einfachen Copper-List erzeu¬ gen und eignet sich daher gut als Beispiel. Geben Sie das Programm mit einem der handelsüblichen Assembler für den Amiga ein (z.B. Profimat Amiga): ;*** Beispiel für eine einfache Copper-List ••• ;Custoin-Chip-Register INTENA = $9A DHACON = $96 COLOROO = $180 ;Interrupt-Enable-Register (schreiben) ;DNA-KontrollregiSter (schreiben) ;Farbpalettenregister 0 ;Copper-Register COP1LC = $80 COP2LC = $84 COPJHP1 = $88 COPJMP2 = $8a ;Adresse der 1. Copper-List ;Adresse der 2. Copper-List ;Sprung nach Copper-List 1 ;Sprung nach Copper-List 2 ;CIA-A Portregister A (Haus Taste) CIAAPRA = $BFE001 138 Amiga intern ;Exec Library Base Offsets OpenLibrary = -30-522 ;L{bNatne,Version/a1,d0 Forbid = -30-102 Permit = -30-108 AUocHem = -30-168 ;ByteSize,Rec|uirenients/d0,d1 FreeMem = -30-180 ;MefnoryBlock,ByteSize/a1,d0 ;graphics base StartList = 38 ;Sonstige Label Execbase = A Chip = 2 ;Chip-RAM anfordern ;*** Vorprogrann *** ;Speicher für Copper-List anfordern Start: nnve.l Execbase,a6 nnveq #Clsize,d0 moveq #chip,di jsr AUocMeni(a6) move.l dO.CLadr beq.s Ende ;Copper-List nach CLadr kopieren lea CLstart.aO nnve.l CLadr,a1 moveq #CLsize-1,d0 CLcopy; move.b {a0)+,{a1)+ dbf dO,CLcopy ;*** Hauptprogranm jsr forbid(a6) lea $dff000,a5 move.w lllS03a0,dmacon(a5) move.l CLadr,copIlcCaS) clr.w copjmpKaS) ;Copper-DMA einschalten ;Parameter für AllocMem setzen ;Chip-RAM verlangen ;Speicher anfordern ;Adresse des RAM-Bereichs speichern ;Fehler! -> Ende ;Schleifenzähler setzen ;Copper-List Byte für Byte kopieren ;Task-Switching sperren ;Basisadresse der Register nach A5 ;DMA sperren ;Adresse der Copper-List nach COP1LC ;In Programmzähler des Coppers laden move.w #$8280,dmacon(aS) ;Auf linke Maustaste warten Weit: btst #6,ciaapra bne.s Weit *** Nachprogranin *** ;Bit testen ;Gesetzt? Dann warten. Die Hardware des Amiga 139 ;Alte Copper-List wieder aktivieren move.l #GRnafne,a1 clr.l dO jsr 0penLibrary(a6) move.l d0,a4 move.l StartList(a4),cop1lc(a5) clr.w copjmpUaS) jgove.w #$83e0,dnacon(a5) jsr permit(a6) ;Parameter für OpenLibrary setzen ;Graphics-Library öffnen ;Adresse von GraphicsBase nach a4 ;Adresse der Startlist laden ;Alle nötigen DMA-Kanäle ein ;Task-Switching erlauben ;Speicher für Copper-List wieder freigeben move.l CLadr,a1 moveq #CLsize,dO jsr FreeHem(a6) Ende: clr.l dO rts ;Variablen CLadr: dc.l 0 ;Konstanten GRname: dc.b "graphics.library",0 even ;Parameter für FreeMem setzen ;Speicher wieder freigeben ;Fehler-Flag löschen ;Progranin verlassen ;Copper-List CLstart: dc.w colorOO.SOOOO dc.w $780f,$fffe dc.w color00,$0f00 dc.w $d70f,$fffe dc.w color00,$0fb0 dc.w Sffff.Sfffe CLend: CLsize = CLend - CLstart ;Ende des Programms ;Hintergrundfarbe schwarz ;Auf Zeile 1Z0 warten ;Auf Rot unschalten .-Zeile 215 ;Gold .-Unmögliche Position: Ende Copper-List Dieses Programm installiert die Copper-List und wartet dann, bis die linke Maustaste gedrückt wird. Leider ist es beim Amiga nicht leicht, dies so zu erledigen, daß man das Programm am Ende ohne einen Reset verlassen kann. Als erstes benötigt man Speicher, in dem man die Copper-List unter¬ bringen kann. Wie alle Daten für die Custom-Chips muß sie im Chip- RAM stehen. Da man nicht sicher sein kann, ob sich das eigene Pro¬ gramm überhaupt im Chip-RAM befindet, ist es notwendig, die Cop¬ per-List in das Chip-RAM zu kopieren. In einem Multitasking-Be- 140 Amiga intern triebssystem wie das des Amiga kann man nicht einfach irgend etwas in den Speicher schreiben. Man muß den Speicher erst anfordern. Dies geschieht in dem Programm mittels der AllocMem-Routine. Diese gibt in DO die Adresse des angeforderten Chip-RAM-Bereichs zurück, in den danach die Copper-List kopiert wird. Als nächstes wird mit dem Aufruf von ’Forbid’ das Task-Switching abgeschaltet, d.h. der Amiga bearbeitet ab sofort nur noch unser Pro¬ gramm. Dies verhindert, daß unser Programm von einem anderen ge¬ stört wird. Jetzt endlich wird der Copper initialisiert und gestartet. Danach testet das Programm die linke Maustaste, indem es das zugehörige Port-Bit von CIA-A abfragt (siehe Kapitel 1.2.2). Drückt man die Maustaste, verläßt der Prozessor die Warteschleife. Um wieder zu der alten Anzeige zurückzukommen, wird eine spezielle Copper-List in den Copper geladen und gestartet. Diese Copper-List heißt Startup-Copper-List und initialisiert den Bildschirm. Ihre Adresse findet man im Variablenbereich des für die Grafikfunktionen zuständigen Teils vom Betriebssystem. Zum Abschluß wird dann noch das Task-Switching mittels ’Permit’ wieder eingeschaltet und der be¬ legte Speicher mit FreeMem freigegeben. Nun, dieses Programm überfällt Sie mit einer Vielzahl unbekannter Funktionen des Betriebssystems. Leider läßt sich dies nicht vermeiden, wenn das Programm ordnungsgemäß funktionieren soll. Aber es macht nichts, wenn Sie jetzt nicht alles verstehen. Es kommt ja hauptsächlich auf den Copper an, und dieser Teil des Programms müßte verständlich sein. In den späteren Kapiteln dieses Buches werden Sie ja in die Ge¬ heimnisse des Betriebssystems und seiner Routinen eingeführt. Tippen Sie also dieses Beispiel ab und experimentieren Sie mit der Copper- List. Ändern Sie die WAIT-Befehle, oder fügen Sie, ganz nach Belie¬ ben, neue hinzu. Wer es sich zutraut, kann es ja auch einmal mit ei¬ nem SKIP-Befehl versuchen. Noch ein Hinweis zur Copper-List: Die beiden WAIT-Befehle enthal¬ ten als horizontale Position jeweils $E. Dies ist der Anfang der hori¬ zontalen Austastlücke. Dadurch vollzieht der Copper die Farbum- schaltung außerhalb des sichtbaren Bereichs. Setzt man 0 als horizon¬ tale Position, kann man die Farbumschaltung am äußersten rechten Rand des Bildschirms beobachten. Die Hardware des Amiga 141 Copperlist Befehl : Copper aktIV1 n n + 2 n + 6 n+6 n-t-8 n + 10 MOUE SSchwarz,COLOROO MAIT 0,120 MOUEttROT,COLORO0 UAIT 0« 215 HOUEIIGQLD« COLOR0O HAIT 254,255 Ablauf der Copperlist aus unseren Beispiel Abb. 1.5.4 1.5.5 Playfields Die Bildschirmausgabe des Amiga besteht aus zwei Grundelementen: Sprites und Playfields. In diesem Kapitel sollen Aufbau und Program¬ mierung sämtlicher Arten von Playfields besprochen werden. Die Sprites folgen dann in Kapitel 1.5.6. Das Playfield ist die Grundlage der normalen Bildschirmdarstellung. Es besteht aus mindestens einer und maximal sechs Bit-Planes. (Der Aufbau einer Bit-Plane wurde in 1.5.2 ja schon erläutert.) Ein Play¬ field stellt also einen Grafikbildschirm dar, der aus einer variablen Anzahl einzelner Speicherbereiche, den Bit-Planes, zusammengesetzt ist. Der Amiga bietet eine große Anzahl unterschiedlicher Möglich¬ keiten, Playfields darzustellen: ■ Zwischen 2 und 4096 Farben gleichzeitig in einem Bild! ■ Auflösungen von 16 auf 1 bis zu 704 auf 625 Punkte! ■ Zwei von einander völlig unabhängige Playfields möglich. ■ Ruckfreies Scrolling in beide Richtungen (Smooth-Scrolling). 142 Amiga intern All diese Möglichkeiten kann man in zwei Gruppen aufteilen. 1. Die Kombination der Bit-Planes zur Errechnung der Farbe der einzelnen Bildschirmpunkte (die Darstellung des Bit-Musters aus den Bit-Planes auf dem Bildschirm). 2. Die Festlegung von Form, Größe und Position des/der Playfields (Aufbau der Playfields). Die verschiedenen Darsteiiungsmögiichkeiten Durch die Verwendung von 1 bis 6 Bit-Planes wird jeder Punkt von ebenso vielen Bits repräsentiert. Dieser Wert muß nun in eine von 4096 Farben umgewandelt werden, da jedes Pixel auf dem Bildschirm natürlich nur eine Farbe haben kann. Der Amiga erzeugt seine Farben durch Mischen der drei Grundfarben Rot, Grün und Blau. Jede dieser drei Komponenten kann 16 verschie¬ dene Intensitätsstufen annehmen. Dadurch entstehen insgesamt 4096 Farbtöne (16*16*16 = 4096). Zur Speicherung der Farbwerte werden pro Komponente 4 Bit benötigt. Dies macht 12 Bit pro Farbe. Wollte man für jeden Punkt eine von 4096 Farben zulassen, bräuchte man 12 Bit pro Punkt. Es sind aber maximal 6 Bit pro Punkt möglich. Aus diesem Grund müssen die 6 Bit umgerechnet werden, um für den sichtbaren Punkt eine der 4096 möglichen Farben zu erhalten. Die Hardware des Amiga 143 Die Farbpalette 1. Farbnahl über Farbtabelle Farbtabeii. Abb. 1.5.S.1 Man verwendet dazu eine sogenannte Farbpalette oder Farbtabelle. Diese enthält beim Amiga 32 Einträge, die je einen 12-Bit-Farbwert aufnehmen können. Der Wert des ersten Farbregisters COLOROO dient gleichzeitig als Hintergrund und Rahmenfarbe. Farbpalettenregister 0-31 (COLOROO bis 31 (nur Schreiben möglich)): Registeradr. _ Farbpalett«nr«gi»ter $180 COLOROO $182 COLOROl ... usw. $1BE COLORSl Aufbau eines Tabellenelements: Bit: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 COLORxx: X X X X R3 R2 RI RO G3 G2 Gl GO B3 B2 Bl BO R0-R3 4-Bit-Wert für den Rot-Anteil G0-G3 4-Bit-Wert für den Grün-Anteil B0-B3 4-Bit-Wert für den Blau-Anteil Die oberen vier mit "x" beeeichneten Bits sind ungenutzt. 144 Amiga intern Der aus den Bit-Planes gewonnene Wert wird nun als Zeiger auf ein Tabellenelement verwendet. Da es nur 32 dieser Farbtabellenregister gibt, können in diesem Modus maximal 5 Bit-Planes miteinander kombiniert werden. Das Bit aus der Bit-Plane mit der niedrigsten Nummer liefert das LSB dieses Eintrags, die Bit-Plane mit der höchsten Nummer das MSB. Diese Art der Farbgewinnung über eine Tabelle ermöglicht maximal 32 Farben in einem Bild, die aber aus der Gesamtanzahl von 4096 ge¬ wählt werden können. Im hochauflösenden Modus dürfen insgesamt nur 4 Planes gleichzeitig aktiv sein. Hier sind dann 16 Farben die Grenze. Es ist bei dieser Darstellungsart egal, wie viele Planes mit¬ einander kombiniert werden. Es bleiben dann eben einige Farbregister unbenutzt: Antahl der Bit-Planes Farben_Benuttte Farbregister 1 2 3 4 5 2 COLOROO - COLOROl 4 COLOROO - COLOR03 8 COLOROO - COLOR07 16 COLOROO - COLOR15 32 COLOROO - COLOR31 Der Extra-Haifbright-Modus Im niedrigauflösenden Modus können maximal 6 Bit-Planes verwendet werden. Dies ergibt einen Wertebereich von 2® oder 0 bis 63. Es sind aber nur 32 Farbregister vorhanden. Man verwendet aus diesem Grund dafür eine spezielle Technik, den Extra-Halfbright-Modus. Die unte¬ ren 5 Bits (Bits 0 bis 4 aus den Planes 1 bis 5) dienen weiterhin als Zeiger auf ein Farbregister. Der Inhalt dieses Farbregisters wird direkt auf den Bildschirm ausgegeben, wenn Bit 5 (aus Bit-Plane 6) = 0 ist. Liegt dieses Bit aber auf 1, wird der Farbwert durch zwei dividiert, bevor er auf dem Bildschirm ausgegeben wird. Durch zwei dividiert bedeutet, daß die Werte der drei Farb- komponenten um 1 Bit nach rechts geschoben werden, was ja einer Division durch zwei entspricht. Da die einzelnen Komponenten danach nur noch halb so groß sind, wird auf dem Bildschirm zwar dieselbe Farbe dargestellt, aber mit halber Helligkeit, daher auch der englische Name Extra-Halfbright, was soviel heißt wie "Besonderer Modus für halbe Helligkeit". Die Hardware des Amiga 145 Beispiel: Bit-Nr.: 543210 Wert aus den Bit-Planes: 10 0 10 0 Ergibt Tabelleneintrag Nr. 8 (binär 00100 ist gleich 8) COLOR08 soll folgenden Wert enthalten (Farbe: Orange): R3 R2 RI RO G3 G2 Gl GO B3 B2 Bl BO 111001100001 Da Bit 5 = 1 ist, werden die Werte um 1 Bit verschoben: R3 R2 RI RO G3 G2 Gl GO B3 B2 Bl BO 011100110000 Dieser Wert entpricht immer noch Orange, aber jetzt nur noch halb so hell. Durch entsprechende Auswahl der Farbwerte in den 32 Registern ist es im Extra-Halfbright-Modus machbar, jedem Punkt eine von 64 möglichen Farben zu geben. In die Farbregister kommen die hellen Farben, die dazugehörigen dunkleren Töne werden durch Setzen von Bit 5 erreicht. Der Hold-And-Modify-Modus Dieser Modus ermöglicht die Darstellung aller 4096 Farben in einem Bild. Er ist wie der Extra-Halfbright-Modus nur bei niedriger Auflö¬ sung möglich, da er ebenfalls alle 6 Bit-Planes benötigt. In diesem Modus macht man sich die Tatsache zunutze, daß die Farben in einem normalen Bild selten sprunghaft wechseln. Meistens benötigt man fließende Übergänge vom Hellen ins Dunkle oder umgekehrt. Im Hold-And-Modify-Modus, kurz HAM genannt, wird die Farbe des vorangegangenen Punkts von dem darauffolgenden modifiziert. Da¬ durch kann man die gewünschten feinen Farbabstufungen entstehen lassen, indem man z.B. den Blauanteil mit jedem Pixel um einen Schritt erhöht. Eingeschränkt ist man allerdings dadurch, daß immer nur eine Komponente beeinflußt werden kann, von einem Punkt zum nächsten läßt sich entweder der Rot-, Grün- oder Blau-Wert verän¬ dern, nie aber mehrere gleichzeitig. Um aber einen fließenden Über¬ gang von Dunkel nach Hell zu erzielen, müssen bei vielen Mischfarben alle drei Farbanteile verändert werden. Dies kann im HAM-Modus nur erreicht werden, indem man mit jedem Punkt eine der Kompo¬ nenten auf den gewünschten Wert setzt. Man benötigt dafür dann drei Punkte. 146 Amiga intern Als Ausgleich kann man die Farbe eines Pixels auch direkt ändern, indem man weiterhin eine von 16 Farben aus der Farbtabelle holen kann. Wie wird der Wert aus den Bit-Planes im HAM-Modus interpretiert? Die oberen beiden Bits (Bits 4 und 5 aus Bit-Planes 5 und 6) be¬ stimmen die Verwendung der unteren vier Bits (Bit-Planes 1 bis 4). Sind Bit 4 und 5 auf 0, werden die restlichen vier Bits wie üblich als Zeiger auf eines der Farbpalettenregister verwendet. Es können also 16 Farben direkt angewählt werden. Bei einer Kombinationen der Bits 4 und 5, die ungleich 0 ist, wird der Farbwert des letzten Punkts ge¬ nommen (links von dem aktuellen Pixel), zwei der drei Farbkompo- nenten bleiben erhalten, die dritte wird durch den Wert aus den unte¬ ren vier Bits des aktuellen Punkts ersetzt. Die Wahl zwischen den drei Farbanteilen übernehmen dabei die oberen beiden Bits. Dies hört sich komplizierter an, als es ist. Folgende Tabelle veran¬ schaulicht die Verwendung der unterschiedlichen Bit-Kombinationen: Bit-Nr.: 5 4 3 2 1 0 Funktion 0 0 C3 C2 CI CO Die Bits CO bis C3 werden als Zeiger auf eines der Farbregister im Bereich von COLOROO bis COLOR15 verwendet. Dies ist identisch mit der normalen Farbwahl. 0 1 B3 B2 Bl BO Der Rot* und Grünwert des letzten (linken) Pixels bleibt erhalten. Der alte Blauwert wird durch den Wert von BO bis B3 ersetst. 1 0 R3 R2 RI RO Der Blau* und Grünwert des leteten Pixels bleibt erhal¬ ten. Der alte Rotwert wird durch RO bis R3 ersetst. 1 1 G3 G2 Gl GO Der Blau- und Rotwert des letsten Pixels bleibt erhalten. Der alte Grünwert wird durch GO bis G3 ersetzt. Beim ersten Punkt einer Zeile wird die Rahmenfarbe (COLOROO) als Farbe des vorangegangenen Punktes verwendet. Die bisher beschriebenen Modi verwendeten lediglich ein einziges Playfield. Der Dual-Playfield-Modus erlaubt die simultane Darstellung von zwei völlig voneinander unabhängigen Playfields. Es existieren dann quasi zwei Bildschirme, die auf einem Monitor übereinander ge¬ legt werden. Sie können (fast) völlig getrennt benutzt werden. Dies ist besonders für Spiele interessant. Z.B. läßt sich damit sehr einfach ein Fernrohreffekt produzieren. Dazu wird das vordere Play¬ field mit schwarzen Punkten gefüllt. Lediglich in der Mitte bleibt ein Loch, durch das man dann einen Ausschnitt des hinteren Playfields sehen kann. Jedes der beiden Playfields bekommt die Hälfte der aktivierten Bit- Planes für seine Darstellung. Playfield 1 wird aus den ungeraden Pla¬ nes (odd planes) gebildet, Playfield 2 besteht aus den geraden (even 148 Amiga intern planes). Ist eine ungerade Anzahl von Bit-Planes in Betrieb, hat Play- field 1 eine Bit-Plane mehr zur Verfügung. Die Farbauswahl erfolgt im Dual-Playfield-Modus wie üblich; Der Wert, der aus den zu einem Punkt gehörigen Bits aller ungeraden (für Playfield 1) bzw. aller geraden (Playfield 2) Planes gebildet wird, dient als Zeiger auf einen Eintrag in der Färb tabeile. Da jedes Playfield aus höchstens 3 Planes bestehen kann, sind maximal acht Farben möglich. Diese werden bei Playfield 1 aus den unteren 8 Einträgen der Farbta- belle geholt (COLOROO bis COLOR07). Bei Playfield 2 wird noch ein Offset von 8 zu dem Wert aus den Bit-Planes dazuaddiert, wodurch sich seine Farben in den Positionen 8 bis 15 befinden (COLOR08 bis eOLORlS). Hat ein Punkt den Wert 0, wird seine Farbe nicht aus COLOROO (bzw. COLOR08) geholt, sondern er wird durchsichtig dargestellt. Die be¬ deutet, daß dahinterliegende Bildschirmelemente dort zu sehen sind. Dies können das andere Playfield, Sprites oder einfach der Hinter¬ grund (COLOROO) sein. Der Dual-Playfield-Modus ist auch in der hohen Auflösung an¬ wendbar. Jedes Playfield hat dann nur noch vier Farben. An der Ver¬ teilung der Farbregister ändert sich nichts, allerdings sind die oberen vier Farbregister jedes Playfields unbenutzt (Plfl: COLOR04 bis 07, Plf2: COLOR12 bis 15). Verteilung der Bit-Planes im Dual-Playfield-Modus: Bit-Planes _ Planes in Playfield 1 1 Plane 1 2 Plane 1 3 Plane 1 und 3 4 Plane 1 und 3 5 Plane 1, 3 und 5 6 Plane 1, 3 und 5 Planes in Playfield 2 keine Plane 2 Plane 2 Plane 2 und 4 Plane 2 und 4 Plane 2, 4 und 6 Die Hardware des Amiga 149 Farbauswahl im Dual-Playfield-Modus; Playfleld 1 Planes 5, 3, 1_Farbreg. 0 0 0 Transparent 0 0 1 COLOROl 0 10 COLOR02 0 1 1 COLOR03 10 0 COLOR04 1 0 1 COLOR05 1 1 0 COLOR06 111 COLOR07 Playfield 2 Planes 6, 4 , 2 Farbreg. 000 Transparent 0 0 1 COLOR09 0 10 COLORIO 0 11 COLORll 100 COLOR12 10 1 COLOR13 110 COLOR14 111 COLOR16 Der Aufbau der Playfields Wie schon erwähnt, besteht ein Playfield aus einer bestimmten Anzahl Bit-Planes. Wie sehen diese Bit-Planes aus? In 1.5.2 wurde schon be¬ schrieben, daß sie als fortlaufender Speicherbereich konzipiert sind, wobei je nach Bildschirmbreite eine Zeile durch eine Anzahl von Worten repräsentiert wird. Im Normalfall sind dies 20 Worte in der niedrigen Auflösung (320 Punkte durch 16 Punkte pro Wort) und 40 (640/16) in der hohen. Um den genauen Aufbau des Playfields festzulegen, sind folgende Schritte notwendig: ■ Definition der gewünschten Bildgröße. ■ Setzen der Bit-Plane-Größe. ■ Anzahl der Bit-Planes wählen. ■ Farbtabelle initialisieren. ■ Gewünschten Modus festlegen (Hires, Lores, HAM usw.). ■ Copper-List aufbauen. ■ Copper initialisieren. ■ Copper- und Bit-Plane-DMA aktivieren. Festlegung der Bildgröße Der Amiga erlaubt eine freie Positionierung der linken oberen und der rechten unteren Ecke des sichtbaren Bereichs des/der Playfields. Da¬ mit läßt sich sowohl die Bildposition als auch die Bildgröße variieren. Die Auflösung beträgt vertikal eine Rasterzeile und horizontal einen niedrig auflösenden Pixel, zwei Register enthalten Werte. DIWSTRT 150 Amiga intern (Display Window Start) legt die horizontale und vertikale Startposition des Bildschirmfensters fest, d.h. die Zeile bzw. Spalte, in der die Dar¬ stellung des Playfields beginnt. DIWSTOP (Display Window Stop) enthält die Endposition + 1. Damit ist die erste Zeile/Spalte nach dem Playfield gemeint. Soll es z.B. bis Zeile 250 gehen, muß man 251 als DIWSTOP-Wert angeben. Außerhalb des sichtbaren Bereiches wird die Rahmenfarbe dargestellt. (Sie entspricht der Hintergrundfarbe und kommt aus dem COLOROO- Register.) DIWSTRT $08E (nur schreiben) Bit-Nr.: 15 U 13 12 11 10 9 8 7 6 5 4 3 2 1 0 V7 V6 V5 V4 V3 V2 Vl VO H7 H6 H5 H4 H3 H2 Hl HO DIWSTOP $90 (nur schreiben) Bit-Mr.: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 V7 V6 V5 V4 V3 V2 VI VO H7 H6 H5 H4 H3 H2 Hl HO Die in DIWSTRT festgelegte Startposition ist auf das obere linke Viertel des Bildschirms beschränkt, dies sind die Zeilen und Spalten 0 bis 255, da die fehlenden MSBs V8 und H8 als 0 angenommen wer¬ den. Gleiches gilt für die horizontale Endposition, nur wird hier H8 als 1 vorausgesetzt, womit sie innerhalb des Bereichs von 256 bis 458 liegt. Bei der vertikalen Endposition wurde ein anderer Weg begangen. Es sollten sowohl Positionen kleiner als auch größer 256 möglich sein. Aus diesem Grund wird das MSB der vertikalen Endposition, V8, durch Invertieren des V7-Bits erzeugt. Dadurch ist eine Endposition im Bereich der Zeilen von 128 bis 312 möglich. Bei Endpositionen von 256 bis 312 setzt man V7 auf 0 und damit V8 auf 1. Legt man V7 auf 1 geht V8 auf 0 und man erhält eine Position zwischen 128 und 255. Das normale Bildschirmfenster hat eine obere linke Eckposition von horiz. 129 und vert. 41. Die untere rechte Ecke liegt bei 448, 296, d.h. DIWSTOP muß auf 449, 297 gesetzt werten. Die zugehörigen Werte für DIWSTRT- und DIWSTOP betragen hexadezimal $2981 und $29C1. Mit diesen Werten wird der normale Amiga-Screen von 640 auf 256 Punkten (bzw. 320 auf 256) in der Mitte des Bildschirms zen¬ triert. Die Hardware des Amiga 151 Warum verwendet man nicht den gesamten möglichen Bild¬ schirmbereich? Dafür gibt es mehrere Gründe. Erstens stellt ein nor¬ maler Monitor nicht das gesamte Bild dar. Sein sichtbarer Bereich be¬ ginnt gewöhnlich erst einige Spalten bzw. Zeilen nach der jeweiligen Austastlücke. Außerdem ist eine Bildröhre nicht rechteckig. Würde man das Bildschirmfenster so hoch und breit wie die Monitorröhre einstellen, verdeckten die Ecken der Röhre ein Teil des Bildes. Eine weitere Beschränkung erfahren die DIWSTRT- und DIWSTOP- Werte durch die Austastlücken. Vertikal fällt sie in den Bereich der Zeilen von 0 bis 25. Damit ist der sichtbare vertikale Bereich auf die Zeilen von 26 bis 312 ($1A bis $138) beschränkt. Innerhalb der Spal¬ ten 30 bis 106 (SIE bis $6A) liegt die horizontale Austastlücke. Es sind horizontale Positionen ab 107 ($6B) möglich. Nachdem man die Position des Bildschirmfensters festgelegt hat, müs¬ sen auch noch Anfang und Ende des Bit-Plane-DMA eingestellt wer¬ den. Damit die Punkte zum gewünschten Zeitpunkt auf dem Bild¬ schirm erscheinen, müssen die Daten aus den Bit-Planes rechtzeitig gelesen werden. Vertikal ist dies kein Problem. Bildschirm-DMA be¬ ginnt und endet in der Zeile wie das in DIWSTRT und DIWSTOP ein¬ gestellte Bildschirmfenster. Horizontal ist es etwas komplizierter. Damit ein Punkt auf dem Bild¬ schirm dargestellt werden kann, benötigt die Elektronik aus jeder Bit- Plane das aktuelle Wort. Bei 6 Bit-Planes in der niedrigen Auflösung sind acht Buszyklen nötig, um alle Bit-Planes zu lesen. In der hohen Auflösung sind es nur vier. (Zur Erinnerung: in einem Buszyklus wer¬ den 2 niedrig- oder 4 hochauflösende Punkte dargestellt.) Zusätzlich braucht die Hardware noch einen halben Buszyklus, bevor die Daten auf dem Schirm erscheinen können. Der Bit-Plane-DMA muß also genau 8.5 Zyklen (17 Punkte) vor dem Anfang des Bild¬ schirmfensters beginnen (4.5 Zyklen oder 9 Punkte in der hohen Auf¬ lösung, siehe Abbildung 1.5.2.3). Der Buszyklus des ersten Bit-Plane-DMA in der Zeile wird in dem Register DDFSTRT (Display Data Fetch Start) abgelegt, der des letz¬ ten in DFFSTOP (Display Data Fetch Stop): 152 Amiga intern DDFSTRT $092 (nur schreiben) DDF STOP $094 (nur schreiben) Bit-Nr.: 15 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Funktion: x x x x x x x H8 H7 H6 H5 H4 H3 x x Die Auflösung beträgt acht Buszyklen im niedrigauflösenden Modus (Lores Modus), wobei H3 immer auf 0 liegt, und vier im hochauflö¬ senden Modus (Hires Modus). Hier dient H3 als unterstes Bit. Der Grund für die beschränkte Auflösung liegt in der Aufteilung des Bit- Plane-DMA. Im Lores-Modus wird jede Bit-Plane alle acht Buszyklen einmal gelesen. Aus diesem Grund muß der DDFSTRT-Wert ein ganzzahliges Vielfaches von Acht sein (Hl bis H3 = 0). Gleiches gilt für den Hires-Modus, nur sind es hier vier Buszyklen (Hl und H2 = 0). Allerdings sollte die Differenz aus DIWSTRT und DIWSTOP immer durch acht teilbar sein, da die Hardware, unabhängig von der Auflö¬ sung, die Zeile in Bereiche zu je 8 Buszyklen aufteilt. Auch im Hires- Modus wird der Bit-Plane-DMA nach DIWSTOP noch 8 Buszyklen lang ausgeführt, dabei werden immer 32 Punkte gelesen. Die korrekten Werte für DIWSTRT und DIWSTOP errechnen sich wie folgt; Berechnung von DDFSTRT und DDFSTOP im Lores-Modus: HStart = Horizontaler Start des Bildschirmfensters DDFSTRT = (HStart/2 - 8.5) AND $FFF8 DDFSTOP = DDFSTRT + (PunkteproZeile/2 - 8) Dies ergibt für HStart = $81 und 320 Punkte pro Zeile: DDFSTRT = ($81/2 • 8.5) AND $FFF8 = $38 DDFSTOP = $38 + (320/2 • 8) = $00 Berechnung von DDFSTRT und DDFSTOP im Hires-Modus: DDFSTRT = (HStart/2 - 4.5) AND $FFFC DDFSTOP = DDFSTRT + (PunkteproZeile/4 - 8) Dies ergibt für HStart = $81 und 640 Punkte pro Zeile: DDFSTRT = ($81/2 - 4.5) AND $FFFC = $3C DDFSTOP = $3C + (640/4 - 8) = $04 DDFSTRT darf nicht kleiner als $18 werden. DDFSTOP ist auf maxi¬ mal $D8 beschränkt. Die Gründe dafür kann man in Kapitel 1.5.2 nachlesen. Ein DDFSTRT-Wert kleiner $28 hat keinen Sinn mehr, da Die Hardware des Amiga 153 die Punkte dann noch innerhalb der horizontalen Austastlücke darge¬ stellt werden müßten, was nicht möglich ist (Ausnahme: Scrolling). Da sich bei DDFSTRT-Positionen kleiner $34 die DMA-Zyklen der Bit- Planes und Sprites überlappen, sind je nach DDFSTRT einige Sprites nicht mehr darstellbar. Verschieben des Bildschirmfensters Will man das Bildschirmfenster mittels DIWSTRT und STOP horizontal verschieben, kann es passieren, daß die Differenz zwischen DIWSTRT und DDFSTRT nicht genau 8.5 Buszyklen (17 Punkte) beträgt, da man DFFSTRT ja nur in Schritten von acht Buszyklen festlegen kann. In solch einem Fall würde ein Teil des ersten Datenwortes im unsichtba¬ ren Bereich links neben der Grenze des Bildschirmfensters verschwin¬ den. Um dies zu beheben, gibt es die Möglichkeit, die gelesenen Daten vor der Ausgabe auf dem Bildschirm soweit nach rechts zu verschie¬ ben, daß sie mit dem Beginn des Bildschirmfensters übereinstimmen. Wie man dies programmiert, wird im Abschnitt über das Scrolling ge¬ nau erläutert. Festlegen der Bit-Map-Adressen Die Werte in DDFSTRT und DDFSTOP bestimmen, wieviele Daten¬ worte pro Zeile dargestellt werden. Für jede Bit-Map muß jetzt die Startadresse gesetzt werde, damit der DMA-Controller weiß, von wo er die Punktdaten lesen soll. 12 Register enthalten diese Adressen. Je zwei Register, BPLxPTH und BPLxPTL, bilden sie gemeinsam für die Bit-Plane x. Zusammen einfach nur BPLxPT (BitPlanexPointer, Zeiger auf Bit-Plane x). Adr. Name Funktion SOEO BPLIPTH Anfangsadreese der Bits 16-18 »OEZ BPLIPTL Bit-Plane 1 Bits 0-15 »0E4 BPL2PTH Anfangsadresse der Bits 16-18 »0E6 BPL2PTL Bit-Plane 2 Bits 0-15 »OES BPL3PTH Anfangsadresse der Bits 16-18 »OEA BPL3PTL Bit-Plane 3 Bits 0-15 »OEC BPL4PTH Anfangsadresse der Bits 16-18 »OFO BPL4PTL Bit-Plane 4 Bits 0-15 »0F2 BPL6PTH Anfangsadresse der Bits 16-18 »0F4 BPL6PTL Bit-Plane 5 Bits 0-15 »0F6 BPL6PTH Anfangsadresse der Bits 16-18 »0F8 BPL6PTL Bit-Plane 6 Bits 0-15 Der DMA-Controller geht bei der Darstellung der Bit-Planes fol¬ gendermaßen vor: Der Bit-Plane-DMA bleibt inaktiv, bis die erste Zeile des Bildschirmfensters erreicht wird (DIWSTRT). Jetzt holt er ab 154 Amiga intern der in DFFSTRT festgelegten Spalte die Datenworte der verschiedenen Bit-Planes. Dabei hält er sich an das Timing in Abbildung 1.5.2.3. Als Zeiger auf die Daten im Chip-RAM verwendet er die BPLxPT. Nach jedem gelesenen Datenwort wird BPLxPT um ein Wort erhöht. Die gelesenen Worte gelangen in die BPLxDAT-Register. Diese Register werden nur vom DMA-Kanal benutzt. Sind alle sechs BPLxDAT-Re¬ gister mit zusammengehörigen Datenworten aus den Bit-Planes ver¬ sorgt worden, gelangen die Daten Bit für Bit zu der Videologik in De- nise, die je nach gewähltem Modus eine der 4096 Farben auswählt und diese dann auf dem Bildschirm ausgibt. Beim Erreichen von DFFSTOP pausiert der Bit-Plane-DMA bis zum DFFSTRT der nächsten Zeile, dann wiederholt sich der Vorgang bis zum Ende der letzten Zeile des Bildschirmfensters (DIWSTOP). Der BPLxPT zeigt jetzt auf das erste Wort nach der Bit-Plane. Da aber im nächsten Bild der BPLxPT wieder auf das erste Wort der zu¬ gehörigen Bit-Plane zeigen soll, muß er wieder zurückgesetzt werden. Die erledigt der Copper schnell und problemlos. Eine Copper-List für ein Playfield mit 4 Bit-Planes sieht im einfachsten Fall wie folgt aus: AdrPlanexH = Adresse der Plane x, Bits 16-18 AdrPlanexL = Adresse der plane x, Bits 0-15 MOVE #AdrPlane1H,BPL1PTH HOVE #AdrPlane1L,BPL1PTL HOVE #AdrPlane2H,BPL2PTH HOVE #AdrPlane2L,BPL2PTL HOVE #AdrPlane3H,BPL3PTH HOVE #AdrPlane3L,BPL3PTL HOVE *AdrPlane4H,BPL4PTH HOVE #AdrPlane4L,BPL4PTL UAIT ($FF,$FE) ;Zeiger auf Bit-Plane 1 ;Initialisieren ;Zeiger auf Bit-Plane 2 .•Initialisieren ;Zeiger auf Bit-Plane 3 ;Initialisieren .-Zeiger auf Bit-Plane 4 ;Initialisieren ;Ende der Copper-List (Warten auf ;umögliche BiIdschirmposition) Das Zurücksetzen der BPLxPT ist unbedingt notwendig. Wenn man keine Copper-List verwenden will, muß man dies den Prozessor im Vertical-Blanking-Interrupt erledigen lassen. Scrolling und extragroße Playfields Die bisherigen Playfields hatten immer genau die Größe des Bild¬ schirmfensters. Oft wäre es aber nützlich, ein großes Playfield im Speicher zu haben, von dem immer nur ein Ausschnitt im Bild¬ schirmfenster zu sehen ist, den man aber ruckfrei in alle Richtungen verschieben kann. Dies ist beim Amiga problemlos möglich. Folgende Die Hardware des Amiga 155 Abschnitte zeigen dies sowohl in Richtung der X- als auch der Y- Achse. 3itplane BreiteMO Morte Höhei400 Zellen n + iee ntl62 1+16080 n+16082 n + 164 n + 166 BPL»PT = n+164 Moduln = 40 Bytes (20 Morte) Sichtbarer Bereich! Breite: 20 Horte Höhe ; 200 Zeilen Ein Srcollwert vor» 0-15 verschiebt den sichtbaren Bereich un 0-15 Punkte nach links. BPL«PT+0e verschiebt den sicht- baren Be¬ reich eine Zei1e nach unten. BPL«*PT+2 verschiebt den sichtbaren Bereich un 1 Mort nach Rechts nsStartadresse der Bitplane GesantgröBe der Bitplanet 32000 Bytes Abb. 1.5.5.3 Überhohe Playfields und vertikales Scrolling Vertikal läßt sich dies sehr einfach realisieren. Man legt dazu wie üb¬ lich die notwendigen Bit-Planes im Speicher an, aber diesmal mit mehr Zeilen als im Bildschirmfenster festgelegt. Bei einem 256 Zeilen hohen Standardfenster hätte z.B. ein doppelt so hohes Playfield im Speicher einfach 512 Zeilen. Um das Bildschirmfenster jetzt ruckfrei über dieses überhohe Playfield zu bewegen, verändert man die Werte der BPLxPT. Soll das Bildschirmfenster z.B. den Bereich von 100 bis 356 darstellen, muß der BPLxPT auf das erste Wort der 100. Zeile zei¬ gen. Bei einer Bildbreite von 320 Punkten belegt jede Zeile 20 Worte (40 Byte) im Speicher. Multipliziert mit den 100 Zeilen ergibt sich eine Adresse von 4000. Plus der Anfangsadresse des großen Playfields erhält man so den gewünschten Wert, der in die BPLxPT muß. Um das Playfield im Bildschirmfenster zu scrollen, verändert man einfach diesen Wert mit jedem Bild um eine ödere mehrere Zeilen, je nach gewünschter Scrollgeschwindigkeit. Da die BPLxPT nur außerhalb des sichtbaren Bereichs verändert werden dürfen, bedient man sich wieder obiger Copper-List. Man kann dann zu einem beliebigen Zeitpunkt 156 Amiga intern die Adressen in der Copper-List verändern, der Copper schreibt sie automatisch zum richtigen Zeitpunkt in die BPLxPT-Register. Man muß lediglich aufpassen, daß man die Copper-List nicht verändert, während der Copper auf seine Befehle zugreift. Sonst kann es passie¬ ren, daß nachdem man erst ein Wort der Adresse geändert hat, der Copper plötzlich die gesamte, dann falsche Adresse liest. Überbreite Playfields und horizontales Scrolling Für das horizontale Scrolling und extrabreite Playfields sind spezielle Register vorhanden (ausschließlich Schreibregister): $108 BPLIMOD Modulo-Wert für die ungeraden Bit-Planes $I0A BPL2MOD Modulo-Wert für die geraden Bit-Planes BPLCONl $102 Bit-Nr: 15-8 7 6 5 4 3 2 1 0 Funktion: Unbet. P2H3 P2H2 P2H1 P2H0 P1H3 P1H2 P1H1 P1H0 PIHO - P1H3 Position der geraden Pianes (vier Bits) P2H0 - P2H3 Position der ungeraden Pianes (vier Bits) Die Modulo-Werte aus den BPLxMOD-Registern erlauben sogenannte rechteckige Speicherbereiche. Dieses Prinzip wird bei der Amiga- Hardware noch öfters verwendet. Es ermöglicht innerhalb eines großen Speicherbereichs, der in Zeilen und Spalten eingeteilt ist, einen klei¬ neren zu definieren, der ebenfalls eine festgelegte Höhe und Breite besitzt. Nehmen wir an, der große Speicherbereich, in diesem Fall unser Playfield, wäre 640 Punkte breit und 256 hoch. Das ergäbe also 256 Zeilen zu je 40 Worten (80 Bytes). Der kleine entspricht dem Bildschirmfenster, es soll die normale Größe von 320 auf 200 Punkten haben, d.h. nur 20 Worte pro Zeile. Das Problem dabei ist, daß der BPLxPT bei der Ausgabe einer Zeile um 20 Worte erhöht wird. Um auf den Anfang der nächsten Zeile unseres Playfields zeigen zu kön¬ nen, müßte er aber 40 Worte weiter sein. Es müßte also nach jeder Zeile ein Wert von 20 Worten zu dem BPLxPT addiert werden. Genau dies kann der Amiga automatisch erledigen. Man schreibt die Diffe¬ renz der beiden unterschiedlichen Zeilenlängen in die Modulo-Regi- ster. Nach der Ausgabe einer Zeile wird dieser Wert automatisch zu dem BPLxPT addiert. Breite des Playfields: 80 Bytes (40 Worte). Breite des Bildschirmfensters: 40 Bytes (20 Worte). Die Hardware des Amiga 157 Notwendiger Modulo-Wert: 40 Bytes (Der Modulo-Wert muß immer in einer geraden Anzahl Bytes angegeben werden). Start = Anfangsadresse der ersten Zeile des Playfields. Ausgabe der 1. Zeile: Wort: 0 1 2 3 ... 19 BPLxPT: Start Start+2 Start+4 Start+6 ... Start+38 Nach der Ausgabe des letztem Worts wird BPLxPT noch um 1 Wort erhöht: BPLxPT = Start+40 Nach dem Zeilenende wird der Modulo-Wert zu BPLxPT addiert: BPLxPT = BPLxPT + Modulo BPLxPT = Start+40 + 40 = Start+80 Ausgabe der 2. Zeile: Wort: 0 1 2 3 ... 19 BPLxPT: Start+80 Start+82 Start+84 Start+86 ... Start+118 usw. Oben wurde die linke Hälfte der großen Bit-Map im Bild¬ schirmfenster dargestellt. Will man bei einer anderen horizontalen Po¬ sition beginnen, addiert man einfach zu dem Anfangswert von BPLxPT die gewünschte Anzahl von Worten, wobei der Modulo-Wert gleich bleibt. Die Startwerte sind wie oben. Lediglich BPLxPT steht nicht auf Start, sondern auf Start+40, damit wird die rechte Hälfte des großen Play¬ fields angezeigt. Ausgabe der 1. Zeile: Wort: 0 1 2 3 ... 19 BPLxPT: Start+40 Start+42 Start+44 Start+46 ... Start+78 Nach Ausgabe des letzten Worts: BPLxPT = start+80 Jetzt wird der Modulo-Wert zu BPLxPT addiert: BPLxPT = BPLxPT+Modulo BPLxPT = Start+80 + 40 = Start+120 158 Amiga intern Ausgabe der 2. Zeile: Wort: 0 1 2 3 ... 19 BPLxPT; Start+120 Start+122 Start+124 Start+126 ... Start+158 usw. Der Modulo-Wert läßt sich für die geraden und ungeraden Bit- Planes getrennt einstellen. Dies ermöglicht im Dual-Playfield-Modus zwei unterschiedlich große Playfields. Arbeitet man nicht mit diesem Modus, setzt man beide BPLxMOD-Register auf den gleichen Mo- dulo-Wert. Mit Hilfe der BPLxPT- und der BPLxMOD-Register läßt sich der Bildschirm horizontal in Schritten von 16 Punkten verschieben. Feines Scrolling in Schritten von einem Punkt wird durch das BPLCONl-Re¬ gister möglich. Die unteren vier Bits enthalten den Scrollwert der ge¬ raden Planes, Bits 4 bis 7 den der ungeraden. Dieser Scrollwert verzö¬ gert die Ausgabe der gelesenen Punktdaten für die entsprechenden Planes. Ist er auf Null, werden die Daten genau 8.5 (Hires: 4.5) Buszy¬ klen nach der DDFSTRT-Position ausgegeben, ansonsten erscheinen sie bis zu fünfzehn Punkte später, je nach dem Scrollwert, d.h. das Bild verschiebt sich also innerhalb des feststehenden Bildschirm¬ fensters um den Wert von BPLCONl nach rechts. Ein fließendes Scrolling des Bildschirminhalts nach rechts kann man erreichen, wenn man den Wert von BPLCONl schrittweise von 0 auf 15 erhöht und dann wieder auf 0 zurücksetzt, wobei man gleichzeitig den BPLxPT wie oben beschrieben um 1 Wort erniedrigt. Ein Scrolling nach links ergibt sich, wenn man den Scrollwert von 15 auf 0 herabzählt und dann BPLxPT um 1 Wort erhöht. BPLCONl sollte nur außerhalb des sichtbaren Bereichs verändert werden. Entwe¬ der erledigt man dies innerhalb des Vertical-Blanking-Interrupts, oder man verwendet wieder den Copper. Dann kann man den Wert in der Copper-List zu jedem beliebigen Zeitpunkt ändern, er wird immer während der vertikalen Austastlücke in das BPLCONl-Register ge¬ schrieben. Verschiebt man aber das Bild mittels des BPLCONl-Werts nach rechts, werden die überschüssigen Punkte zwar an der linken Kante korrekt abgeschnitten, an der rechten erscheinen aber keine neuen Punkte, da dort ja noch gar keine Punktdaten gelesen wurden. Um dies zu verhindern, muß der DDFSTRT-Wert gegenüber seinem nor¬ malen Beginn um 8 Buszyklen (Hires: 4 Buszyklen) vorverlegt werden. Die Hardware des Amiga 159 Man errechnet wie üblich anhand des gewünschten Bildschirmfensters den DDFSTRT-Wert und verringert ihn zusätzlich um 8 (bzw. 4). Aus dem normalen DDFSTRT-Wert von $38 wird dann $30 (Dadurch wird Sprite 7 abgeschaltet). Dieses zusätzlich gelesene Wort ist im Normal¬ fall nicht sichtbar. Erst wenn der Scrollwert ungleich 0 ist, erscheinen seine Punkte in den freiwerdenden Positionen an der linken Kante des Bildschirmfensters. Hat dieses die Breite von 320 Punkten, werden jetzt 21 statt der üblichen 20 Datenworte pro Zeile gelesen. Bei der Berechnung der Bit-Planes und der Modulo-Werte muß man das be¬ rücksichtigen. Mit Hilfe des Scrollwerts kann man auch das Bildschirmfenster hori¬ zontal beliebig positionieren. Beträgt die Differenz aus DIWSTRT und DFFSTRT mehr als 17 Punkte, verschiebt man die gelesenen Daten einfach um eben diesen Mehrbetrag nach rechts. Der Interlace-Modus Obwohl im Interlace-Modus die doppelte Zeilenzahl darstellbar ist, unterscheidet er sich programmtechnisch nur durch einen geänderten Modulo-Wert und eine neue Copper-List von der normalen Darstel¬ lung. Wie im Kapitel 1.5.2 beschrieben, werden im Interlace-Modus mit jedem Bild abwechselnd die geraden oder die ungeraden Zeilen ausgegeben. Damit man ein Interlace-Playfield im Speicher normal darstellen kann, setzt man den Modulo-Wert gleich der Anzahl der Worte pro Zeile. Nach der Ausgabe einer Zeile wird dadurch noch einmal die Länge einer Zeile zum BPLxPT addiert, was gleichbedeu¬ tend mit dem Überspringen der nächsten Zeile ist. In jedem Bild wird nur jede zweite Zeile ausgegeben. Jetzt muß noch der BPLxPT in Übereinstimmung mit dem Halbbildtyp abwechselnd auf die erste oder die zweite Zeile des Playfields gesetzt werden, damit entweder die geraden oder die ungeraden Zeilen angezeigt werden. In einem Long Frame setzt man BPLxPT auf Zeile 1 (nur ungerade Zeilen), in einem Short Frame auf Zeile 2 (nur gerade Zeilen). Die Copper-List für ein Interlace-Playfield ist etwas komplizierter, es sind zwei Listen für die beiden Halbbildtypen nötig, die sich mit jedem Bild abwechseln: Copper-List für ein Interlace-Playfield: Zeilel = Adresse der ersten Zeile der Bit-Plane. ZeileZ = Adresse der zweiten Zeile der Bit-Plane. 160 Amiga intern Copperl: HOVE #Zeile1Hi,BPLxPTH HOVE «ZeilelLo.BPLxPTL HOVE #Copper2Hi,COP1LCH HOVE trCopperZLo.COPILCL UAIT (SFF.SFE) Copper2: HOVE #ZeUeZHi,BPLxPTH HOVE #Zeile2Lo,BPLxPTL HOVE #Copper1Hi,C0P1LCH HOVE «CopperILo.COPILCL UAIT {$FF,$FE) ;Zeiger für BPLxPT auf die Adresse ;der ersten Zeile setzen ;Sonstige Copper-Befehle ;Adresse der Copper-List auf CopperZ ;setzen ;Ende der 1. Copper-List ;Zeiger für BPLxPT auf die Adresse ;der zweiten Zeile setzen ;Sonstige Copper-Befehle ;Adresse der Copper-List auf Copperl ;setzen ;Ende der 2. Copper-List Der Copper wechselt nach jedem Bild selbständig seine Copper-List, indem er am Ende einer Befehlsliste die Adresse der anderen in COPILC lädt. Diese Adresse wird mit dem Beginn des nächsten Bildes automatisch in den Programmzähler des Coppers geladen. Die Initiali¬ sierung des Interlace-Modus sollte mit Sorgfalt erfolgen, damit die Copper-List für die ungeraden Zeilen auch wirklich innerhalb eines Long Frames abgearbeitet wird: ■ COPILC auf Copperl setzen. ■ LOF-Bit (Bit 15) im VPOS-Register ($2A) auf 0 setzen. Dies stellt sicher, daß nach dem Einschalten des Interlace-Modus das erste Halbbild ein Long Frame ist und damit zur Copper-List Copperl paßt. Das LOF-Bit wird im Interlace-Modus mit jedem Halbbild invertiert. Setzt man es auf 0, springt es mit Beginn des nächsten Halbbildes auf 1. Dadurch ist dieses dann sicher ein Long Frame. ■ Interlace-Modus an. ■ Warten auf erste Zeile des nächsten Bildes (Zeile 0). ■ Copper-DMA ein. Alle sonstigen Registerfunktionen bleiben im Interlace-Modus unver¬ ändert. Sämtliche Zeilenangaben (z.B. in DIWSTRT) beziehen sich immer auf die Zeilennummer innerhalb des aktuellen Halbbilds (0 - 311 für ein Short Frame und 0-312 für ein Long Frame). Schaltet man den Interlace-Modus ein, ohne andere Register zu verändern. Die Hardware des Amiga 161 bemerkt man lediglich ein leichtes Zittern des Bildes, da jetzt die Zeilen der Halbbilder gegeneinander versetzt werden, aber beide Bil¬ der dieselben Grafikdaten enthalten. Erst wenn man mittels geeigneter Copper-List, doppelt so großen Bit-Planes und entsprechenden Mo- dulo-Werten dafür sorgt, das in jedem Halbbild andere Daten darge¬ stellt werden, erreicht man die gewünschte Verdoppelung der Zeilen¬ zahl. Der Interlace-Modus bringt ein stärkeres Flimmern mit sich, da jede Zeile nur noch alle zwei Halbbilder und damit 25mal pro Sekunde aufgefrischt wird. Dieses Flimmern kann man auf ein Minimum her¬ absetzen, wenn zwischen den verschiedenen Farben in der Farbtabelle möglichst kleine Kontraste (geringe Helligkeitsunterschiede) bestehen. Denn das menschliche Auge kann bei niedrigem Kontrast schnelle Bildwechsel schlechter erkennen. Die Kontrollregister Um all die verschiedenen Modi zu aktivieren, existieren drei Kon¬ trollregister, BPLCONO bis BPLCON2. BPLCONl enthält die Scroll- Werte. Die beiden anderen sind wie folgt aufgebaut: BPLCONO $100 -Nr. Nam« Funktion lE HIRES Hochauflösender Modus ein (HIRES = 1) 14 BPU2 Die drei BPUx-Bits ergeben susammen eine 13 BPUl S-Bit-Zahl, die die Aneahl der verwendeten 12 BPUO Bit-Planes enthält (0 bis 6). 11 HOMOD Hold-and-Modify ein (HOMOD = 1) 10 DBPLF Dual-Playfield ein (DBPLF = 1) 9 COLOR Videoausgang Farbe (COLOR = 1) 8 GAUD Genlock Audio ein (GAUD = 1) 7-4 — Unbenutst 3 LPEN Lightpen-Eingang aktivieren (LPEN = 1) 2 LAGE Interlace-Modus ein (LAGE = 1) 1 ERSY Externe Synchronisation ein (ERSY = 1) 0 — Unbenutst HIRES Mit dem Hires-Bit wird der hochauflösende Modus (Hires, 640 Punkte/Zeile) eingeschaltet. 162 Amiga intern BPLO - BPL2 Diese drei Bits ergeben zusammen eine 3-Bit-Zahl, die die Anzahl der aktivierten Bit-Planes auswählt. Es sind nur Werte zwischen 0 und 6 erlaubt. HOMOD und DBPLF Diese beiden Bits wählen den entsprechenden Modus aus. Sie dürfen nicht gemeinsam aktiv sein. Der Extra-Halfbright-Modus wird auto¬ matisch aktiviert, wenn man alle 6 Bit-Planes einschaltet, aber weder HOMOD oder DBPLF anwählt. LACE Wenn das LACE-Bit gesetzt ist, wird das LOF-Frame-Bit im VPOS- Register mit dem Anfang jedes neuen Bildes invertiert, wodurch der gewünschte Wechsel zwischen Long- und Shortframes stattfindet. COLOR Das Color-Bit schaltet den Colorburst-Ausgang von Agnus ein. Nur wenn Agnus dieses Colorburstsignal liefert, kann der Videomischer ein Farbvideosignal erzeugen. Ansonsten bleibt es Schwarz/Weiß. Der RGB-Ausgang wird davon nicht beeinflußt. ERSY Das ERSY-Bit schaltet die Anschlüsse für die vertikalen und horizon¬ talen Synchronsignale von Ausgang auf Eingang um. Damit läßt sich das Amigabild durch externe Signale synchronisieren. Das Genlock- Interface nutzt dieses Bit, um das Amigabild mit einem beliebigem Videobild mischen zu können. Auch das GAUD-Bit ist fürs Genlock- Interface gedacht (siehe Schnittstellen 1.3.2). BPLCON2 $104 Bit-Nr.: 15-7 6 5 A 3 2 1 0 Funktion: Unbel. PF2PRI PF2P2 PF2P1 PF2P0 PF1P2 PF1P1 PF1P0 PF2P0-PF2P2 und PF1P0-PF1P2 bestimmen die Priorität der Sprites in Bezug auf die Playfields (siehe nächstes Kapitel). Die Hardware des Amiga 163 PF2PRI: Ist dieses Bit gesetzt, haben die geraden Planes Priorität vor den ungeraden, d.h. sie erscheinen vor den ungeraden Planes. Das Bit hat nur im Dual-Playfield-Modus sichtbare Auswirkungen. Aktivierung der Bildschirmdarstellung Nachdem alle oben beschriebenen Register mit den gewünschten Wer¬ ten versehen wurden, muß noch der DMA-Kanal für die Bit-Planes eingeschaltet werden und, falls der Copper verwendet wird (was ja gewöhnlich der Fall ist), auch dessen DMA-Kanal. Folgender Move- Befehl erledigt dies, indem er die DMAEN-, BPLEN- und COPEN- Bits im DMA-Kontrollregister DMACON setzt: HOVE.U #$a310,$OFF096 Beispielprogramme Programm I: Extra Half Bright Demo Dieses Programm erzeugt ein Playfield mit den Standardmaßen 320 auf 256 Punkten im Lowres-Modus. Dabei werden 6 Bit-Planes ver¬ wendet, wodurch automatisch der Extra-Halfbright-Modus aktiviert wird. Am Anfang belegt das Programm den notwendigen Speicher. Da erst jetzt die Adressen der einzelnen Bit-Planes bekannt sind, wird die Copper-List nicht aus dem Programm herauskopiert, sondern direkt im Chip-RAM erstellt. Sie enthält nur die Befehle zum Setzen der BPLxPT-Register. Damit man auch etwas von den 64 möglichen Farben sieht, zeichnet das Programm an zufälligen Positionen 16*16 Punkte große Blöcke in allen Farben. Als Zufallszahlengenerator wird dabei das VHPOS-Re- gister verwendet. ;*** Demo für den Extra-Halfbright-Modus *** ;Custom-Chip-RegiSter INTENA = S9A DMACON = S96 COLOROO = $180 VHPOSR = $6 ;Copper-Register COP1LC = $80 COP2LC = $84 ;Adresse der 1. Copper-List ;Adresse der 2. Copper-List ;Interrupt-Enable-Register (schreiben) ;DMA-KontroUregister (schreiben) ;Farbpalettenregister 0 ;Strahlposition (lesen) 164 Amiga intern C0PJMP1 = $88 .-Sprung nach Copper-List 1 C0PJMP2 = $8a .-Sprung nach Copper-List 2 ;Bit-Plane-Register BPLCONO = $100 BPLC0N1 = $102 BPLC0N2 = $104 BPL1PTH = $0E0 BPL1PTL = $0E2 BPL1H0D = $108 BPL2HOO = $10A DIUSTRT = $08E DIUSTOP = $090 DDFSTRT = $092 DDFSTOP = $094 Bit-Plane KontrolIregister 0 1 (ScroU-Werte) 2 (SpriteoPlayfield Priorität) Zeiger auf 1. Bit-Plane Modulo-Wert für ungerade Bit-Planes Modulo-Wert für gerade Bit-Planes :Start des Bildschirmfensters :Ende des Bildschirmfensters ;Bit-Plane DMA Start ;Bit-Plane DMA Stop ;CIA-A-Port-Register A (Maustaste) CIAAPRA = $bfe001 ;Exec Library Base Offsets OpenLibrary = -30-522 Forbid = -30-102 Permit = -30-108 AllocMem = -30-168 FreeMem = -30-180 .-graphics base StartList = 38 ;Sonstige Label Execbase Planesize CLsize Chip Clear = 4 = 40*256 = 13*4 = 2 = Chip*$10000 ;*** Vorprogratim *** ;LibMame.Version/a1,d0 ;ByteSize.Requirements/d0.d1 .-MemoryBlock.ByteSize/al ,d0 .-Größe der Bit-Plane: 40 Bytes auf 256 Zeilen .-Die Copper-List enthält 13 Befehle ;Chip-RAM anfordern .-Chip-RAM vorher löschen Start: .-Speicher für die Bit-Planes anfordern move.l Execbase.a6 move.l #Planesize*6.dO move.l #clear.d1 jsr AllocMem(a6) move.l dO.Planeadr beq Ende /Speicherbedarf aller Planes .-Speicher soll mit Nullen gefüllt werden /Speicher anfordern /Adresse der ersten Plane speichern /Fehler! -> Ende /Speicher für Copper-List anfordern moveq iKlsize.dO /Größe der Copper-List moveq #chip.d1 Die Hardware des Amiga 165 jsr Al locMem(a6} move. l dO.CLadr beq FreePlane .-Fehler! -> RAM für Bit-Planes wieder freigeben ;Copper-List erstellen moveq #S,d4 ;6 Planes = 6 Schleifendurchläufe move.l dO.aO ;Adresse der Copper-List nach aO move.l Planeadr.dl move.w #bpl1pth,d3 ;Erstes Register nach d3 MakeCL: : move.w d3,(a0)+ .-BPLxPTH ins RAM addq.w #2,d3 ;Nächstes Register swap dl move.w d1,(a0)+ ;Hi-Uort der Planeadresse ins RAM move.w d3,(a0)+ .-BPLxPTL ins RAM addq.w #2,d3 .-Nächstes Register swap dl move.w d1,(a0)+ ;Lo-Uort der Plane-Adresse ins RAM add.l #planesize,d1 ;Adresse der nächsten Plane berechnen dbf d4,MakeCL move.l #$fffffffe,(aO) ;Ende der Copper-List ;*** Hauptprogramm *** ;DMA und Task-Switching sperren jsr forbid(a6} lea SdffOOO.aS move.w #$03e0,cknacon(aS) ;Copper initialisieren move.l CLsdr,copUc(aS} clr.u copjmpKaS) ;FarbtabeUe mit unterschiedlichen Farben füllen moveq #31,dO lea color00(a5},a1 moveq #1,d1 ;erste Farbe SetTab: move.w d1,(a1)+ mulu #3,dl dbf dO,SetTab ;Zähler für Farbregister ;Farbe in Farbregister ;Nächste Farbe berechnen ;Playfield initialisieren ; Standardwerte für ;BiIdschirmfenster ;und Bit-Plane-DMA move.w #$3081.diwstrtCaS) move.w #$30c1,diwstop(a5} move.w #$0038,ddfstrt(a5) move.w #$00d0,ddfstop(a5} move.w #%0110001000000000,bplcon0(a5} clr.w bplconlCaS) clr.w bplcon2(a5) ;Priorität ;6 Bit-Planes .-Kein Scrolling ist egal 166 Amiga intern clr.w bplImcxKaS) clr.w bpl2nnd(a5) ;Modulo für alle Planes gleich Null ;DHA ein tnove.w #$8380,dniacon(a5} ;B1t-Plane mdlflzleren moveq #40,d5 clr.l d2 ;Bytes pro Zelle ;H1t Farbe 0 beginnen Loop: clr.l dO move.w vhposr(a5},d0 and.w #$3ffe,d0 cmp.w #$2580,dO bcs Weiter and.w #$1ffe,d0 Weiter: move.l Planeadr,a4 add.l d0,a4 moveq #S,d4 move.l d2,d3 ;ZufalIswert nach dO ;überflüssige Bits ausmaskieren ;GröBer als Plane? ;Wenn nein, dann weiter ;Sonst oberes Bit löschen ;Adresse der 1.B1t-Plane nach a4 ;Adresse des Blocks berechnen ;Zähler für Bit-Planes ;Farbe ln Arbeitsregister Block: clr.l dl Isr #1,d3 negx.w dl moveq #15,d0 move.l a4,a3 ;E1n Bit aus Farbnummer ln X-Flag ;d1 an X-Flag anglelchen ;16 Zellen pro Block ;Blockadresse ln Arbeitsregister Flll: move.w d1,(a3} add.l d5,a3 dbf d0,Flll ;Wort ln Bit-Plane /Nächste Zelle errechen add.l #Planes1ze,a4 dbf d4,Block /Nächste Bit-Plane addq.b #1,d2 btst #6,c1aapra bne Loop /Nächste Farbe /Maustaste gedrückt? /Nein -> weitermachen ;••• Nachprogratim *** ;Alte Copper-List wieder aktivieren move.l #GRname,a1 clr.l dO jsr OpenL1brary(a6} ove.l d0,a4 move.l StartL1st(a4},cop1IcCaS) clr.w copjmpKaS) move.w #$8060,chiacon(a5} jsr perm1t(a6} /Parameter für OpenLIbrary setzen /Graphlcs-Llbrary öffnen /Adresse der Start 11st /Abgeschaltete DHA-Kanäle wieder an /Task-SwitchIng wieder erlauben ;Speicher für Copper-List wieder freigeben move.l CLadr.al ;Paranieter für FreeHem setzen moveq #CLs1ze,dO Die Hardware des Amiga 167 jsr FreeHetn(a6) ;Speicher freigeben (-Speicher für Bit-Planes wieder freigeben FreePlane: nnve.l Planeadr.al move.l SPlanesize*6,dO jsr FreeHeni(a6) Ende: clr.l dO ;Progranri verlassen ^Variablen CLadr: dc.l 0 Planeadr: dc.l 0 ;Konstanten GRname; dc.b "graphics.library“,0 ;Progranniende Programm 2: Dual-Playfield & Smooth Scrolling Dieses Programm verwendet mehrere Effekte auf einmal: Erstens schafft es einen Dual-Playfield-Bildschirm mit einer Lowres-Bit-Plane pro Playfield. Dann vergrößert es das normale Bildschirmfenster so, daß keine Ränder mehr zu sehen sind, und zuletzt scrollt es Playfield 1 horizontal und Playfield 2 vertikal. Am Anfang und Ende werden wieder die üblichen Routinen zur Speicherbelegung usw. verwendet. Beide Playfields werden mit einem Schachbrettmuster aus 16*16 Punkten großen Feldern gefüllt. Die Hauptschleife des Programms, die das Scrolling ausführt, wartet zuerst auf eine Zeile innerhalb der vertikalen Austastlücke, in der das Betriebssystem schon alle eventuellen Interrupt-Routinen abgearbeitet und der Copper die BPLxPT gesetzt hat. Danach zählt es den vertika¬ len Scroll-Zähler hoch, errechnet den neuen BPLxPT für Playfield 2 und schreibt ihn in die Copper-List. Die horizontale Scroll-Position entsteht durch Trennen des Scroll- Zählers in die unteren 4 Bits und den Rest. Die unteren 4 Bits werden in den Scroll-Wert für Playfield 1 im BPLCONl-Register geschrieben, aus dem 5. Bit wird der neue BPLxPT errechnet und in die Copper- List kopiert. 168 Amiga intern Sowohl der horizontale als auch der vertikale Scroll-Zähler werden von 0 bis 31 hochgezählt und dann wieder auf 0 zurückgesetzt. Dies reicht für den Effekt eines fließenden Scrollings, da sich der Inhalt der Playfields aufgrund des verwendeten Musters alle 32 Punkte wie¬ derholt. *** Dual-Playfield & ScroU Demo *** ;Custom-Chip-Register INTENA = $9A INTREQR = Sie DNACON = $96 COLOROO = $180 VPOSR = $4 ;Copper-Register COP1LC = $80 COP2LC = $84 COPJNP1 = $88 COPJMP2 = $8a ;Bit-Plane-Register BPLCOHO ° $100 BPLC0N1 = $102 BPLCON2 = $104 BPL1PTH = $0E0 BPL1PTL = $0E2 BPL1HCO = $108 BPL2H00 = $10A DIWSTRT = $08E DIUSTOP = $090 DDFSTRT = $092 DDFSTOP = $094 CIA-A Portregister A (Maustaste) CIAAPRA = SbfeOOl ;Exec Library Base Offsets OpenLibrary = -30-522 ;LibName,Version/a1,d0 Forbid = -30-102 Permit = -30-108 AllocMera = -30-168 ;ByteSize,Requirenients/d0,d1 FreeMera = -30-180 ;MemoryBlock,ByteSize/a1,d0 ;graphics base StartList = 38 .-Sonstige Label ;Bit-Plane-KontrolIregister 0 ;1 (Scroll-Werte) ;2 (SpriteoPlayfield Priorität) ;Zeiger auf 1. Bitplane ;Modulo-Uert für ungerade Bit-Planes ;Hodulo-Uert für gerade Bit-Planes ;Start des Bildschirmfensters ;Ende des Bildschirmfensters ;Bit-Plane DMA Start ;Bit-Plane DMA Stop ;Adresse der 1. Copper-List ;Adresse der 2. Copper-List ;Sprung nach Copper-List 1 rSprung nach Copper-List 2 ;Interrupt-Enable-Register (schreiben) ;Interrupt-Request-Register (lesen) ;DMA-KontrolIregister (schreiben) ;Farbpalettenregister 0 rStrahlposition (lesen) Die Hardware des Amiga 169 Execbase = 4 Planesize = 52*345 ;GröBe der Bit-Plane Planeuidth = 52 CLsize = 5*4 ;Die Copper-List enthält 5 Befehle Chip = 2 ;Chip-RAM anfordern Clear = Chipr$10000 ;Chip-RAM vorher löschen ;*** Vorprogratnrn *** Start: .'Speicher für die Bit-Planes anfordern move.l Execbase,a6 move.l #Planesize*2,dO move.l #clear,d1 jsr Al locMetn(s6} move.l dO,Plsneadr beq.L Ende ;Speicher für Copper-List anfordern moveq IKlsize.dO moveq #chip,di jsr AllocMem(a6} move.l dO.CLadr beq.L FreePlane .'Fehler! -> Speicher der Planes freigeben ,'Copper-List erstellen moveq #1,d4 ;Zwei Bit-Planes move.l dO.aO move.l Planeadr.dl move.u #bpl1pth,d3 HakeCL: move.u cl3,(a0}+ addq.u #2,cl3 suap dl move.u d1,(a0}+ move.u cl3,(a0)+ addq.u #2,d3 suap d1 move.u d1,(a0}+ add.l #planesize,d1 ;Adresse der nächsten Plane dbf d4,MakeCL move.l mSfffffffe.laO) ;Ende der Copper-List ;*** Hauptprogrstim *** ;DMA und Task-Suitching sperren jsr forbid(a6} lea $dff000,a5 move.u #$01e0,ctaiacon(s5} .'Speicherbedarf der Planes .-Speicher anfordern .'Fehler! -> Ende 170 Amiga intern ;Copper initialisieren move.l CLadr,cop1lc(a5) clr.w copjirpKaS) ;Playfield initialisieren move.u #0,color00(a5) move.w jll$0f00,color00+2(a5) move.w jll$000f,color00+18(a5) move.u #$1a64,diustrt(aS) move.w jll$39d1 ,diwstop(a5) move.w lll$0020,ddfstrt(a5) move.w moOdS.ddfstoplaS) move.u #X0010011000000000,bplconOlaS) ;Dual-Playfield an clr.w bplconKaS) ;Scroll-Uert am Anfang auf 0 clr.w bplcon2(a5) ;Playfield 1 vor Playfield 2 move.u #4,bpl1mod(a5) ;Modulo auf 2 Worte move.w #A,bpl2mod(a5) ;26,100 ;313,465 ;Ein zusätzliches Wort lesen ;DMA ein move.w #$8180,dmacon(a5) ;Bit-Planes mit Schachbrettmuster move.l planeadr,a0 move.w #planesize/2-1,d0 move.u #13*16,dl move.l #$ffff0000,d2 move.w d1,d3 fill: move.l d2, weitermachen /*** Nachprogramm •** /Alte Copper-List wieder aktivieren end: move.l #GRname,a1 /Parameter für OpenLibrary setzen clr.l dO jsr OpenLibrary(a6) /Graphics-Library öffnen move.l d0,a4 move.l StartList(a4),cop1lc(a5) clr.w copjmpKaS) move.w #$83e0,cknacon(aS} jsr permitCaö) /Task-Switching einschalten /Speicher für Copper-List wieder freigeben move.l CLadr,a1 /Parameter für FreeHem setzen moveq #CLsize,dO jsr FreeMem(a6) /Speicher freigeben /Speicher für Bit-Planes wieder freigeben FreePlane: move.l Planeadr,a1 move.l #Planesize*2,dO jsr FreeMem(a6) 172 Amiga intern Ende: clr.l dO rts ;Prograiiiii verlassen ;Variablen CLadr: dc.l 0 Planeadr: dc.l 0 test: dc.l 0 (■Konstanten GRname: dc.b ■■graphics.library“,0 ;Progratnnende 1.5.6 Sprites Sprites sind kleine Grafik-Elemente, die völlig unabhängig von den Playfields verwendet werden können. Jedes Sprite ist 16 Punkte breit und kann maximal die Höhe des ganzen Bildschirmfensters haben. Es kann eine beliebige Position auf dem Bildschirm einnehmen. Norma¬ lerweise befindet sich ein Sprite vor dem/den Playfield(s). Seine Punkte verdecken die der dahinterliegenden Grafik. Der Mauszeiger wird z.B. durch ein Sprite dargestellt. Beim Amiga sind maximal acht Sprites möglich. Ein Sprite ist normalerweise dreifarbig, es besteht aber die Möglichkeit, zwei Sprites zu einem neuen Sprite mit fünfzehn Farben zu kombinieren. Der Aufbau der Sprites Farbwahl Die Farbwahl bei den Sprites ähnelt sehr derjenigen eines Dual-Play- field-Bildschirms. Ein Sprite ist sechzehn Punkte breit, die von zwei Datenworten repräsentiert werden, die sozusagen zwei "Mini-Bit-Pla- nes" darstellen. Denn wie bei den Bit-Planes wird die Farbe eines Punktes von zusammengehörigen Bits aller Bit-Planes gebildet. Bei ei¬ nem Sprite wird die Farbe des ersten Punkts (dies ist der am weitesten links stehende Punkt des Sprites) durch die beiden höchstwertigen Bits (Bit 15) beider Datenworte ausgewählt. Die beiden niederwertigsten Bits (Bit 0) bestimmen die Farbe des letzten Punkts. Jeder Punkt wird also von einem 2-Bit-Datenwert repräsentiert, der vier verschiedene Werte annehmen kann. Um aus diesem Wert die tatsächliche Farbe des Punkts zu gewinnen, wird wieder die Farbtabelle verwendet. Es gibt Die Hardware des Amiga 173 keine eigenen Farbregister für Sprites. Allerdings werden die Sprite- Farben aus der hinteren Tabellenhälfte geholt, also aus den Farbregi- stern 16 - 31. Damit kommen Sprite- und Playfield-Farben erst mit¬ einander in Konflikt, wenn Playfields mit mehr als 16 Farben kreiert werden. Diese Tabelle zeigt die Zugehörigkeit von Farbregistern und Sprites: Sprite-Nr. Sprite-Daten Farbregister 0 ic 1 0 0 Durchsichtig 0 1 COLOR17 1 0 COLOR18 11 COLOR19 2 & 3 0 0 Durchsichtig 0 1 COLOR21 1 0 COLOR22 11 COLOR23 4itS 0 0 Durchsichtig 0 1 COLOR25 1 0 COLOR26 11 COLOR27 6 & 7 0 0 Durchsichtig 0 1 COLOR29 1 0 COLOR30 1 0 COLOR31 Je zwei aufeinanderfolgende Sprites haben dieselben Farbregister. Wie schon im Dual-Playfield-Modus wird die Bit-Kombination von zwei Nullen nicht in einer bestimmten Farbe dargestellt, sondern er¬ scheint durchsichtig. An diesen Stellen sieht man die Farbe des da¬ hinterliegenden Bildelements durch, egal, ob es sich nun um ein an¬ deres Sprite, ein Playfield oder nur den Hintergrund handelt. Reichen einem die drei Farben nicht aus, kann man zwei Sprites mit¬ einander kombinieren. Die zwei Bit-Kombinationen beider Sprites er¬ geben zusammen einen 4-Bit-Wert. Es kann immer nur ein Sprite mit einer geraden Nummer mit dem darauffolgenden ungeraden Sprite kombiniert werden. Also Nr. 0 mit Nr. 1, 2 mit 3 usw. Dabei ergeben die beiden Datenworte aus dem Sprite mit der höheren Nummer auch die beiden höchstwertigen Bits des gemeinsamen 4-Bit-Werts. Dieser dient dann als Zeiger auf eines von fünfzehn Farbregistern, wobei der Wert Null wieder transparent erscheint. Die Farbregister sind für alle vier Sprite-Kombinationen dieselben: COLOR16 bis COLOR31. 174 Amiga intern Sprite-Daten Farbregister Sprite-Daten Farbregister 0 0 0 0 Durchsichtig 10 0 0 COLOR24 000 1 COLOR17 10 0 1 COLOR25 0 0 10 COLOR18 10 10 COLOR26 0 0 11 COLOR19 10 11 COLOR27 0 100 COLOR20 110 0 COLOR28 0 10 1 COLOR21 110 1 COLOR29 0 110 COLOR22 1110 COLOR30 0 111 COLOR23 1111 COLOR31 Der Sprite-DMA Die Programmierung der Sprites ist auf dem Amiga sehr komfortabel gelöst worden. Fast die gesamte Arbeit wird von den Sprite-DMA- Kanälen erledigt. Um ein Sprite auf dem Bildschirm darzustellen, benötigt man nur eine spezielle Sprite-Datenliste im Speicher. Sie ent¬ hält fast alle für das Sprite notwendigen Daten. Lediglich die Adresse dieser Liste muß man dem DMA-Contoller noch mitteilen, um das Sprite erscheinen zu lassen. Der DMA-Controller hat für jedes Sprite einen DMA-Kanal. Dieser kann in Jeder Rasterzeile nur zwei Datenworte lesen. Aus diesem Grund ist ein normales Sprite auf eine Breite von 16 Punkten und vier Farben beschränkt. Da diese beiden Datenworte in jeder Zeile gelesen werden können, ist die Höhe eines Sprites nur von dem Bildschirm¬ fenster begrenzt, nach dem sich auch die Sprites zu richten haben. Der Aufbau einer Sprite-Datenliste Eine solche Datenliste besteht aus einzelnen Zeilen, die jeweils zwei Datenworte enthalten. In jeder Rasterzeile wird eine dieser Zeilen per DMA gelesen. Sie kann entweder zwei Kontrollworte enthalten, die das Sprite initialisieren, oder zwei Datenworte mit den Punktdaten, wenn das Sprite gerade dargestellt wird. Die Kontrollworte bestimmen die horizontale Spalte und die erste und letzte Zeile des Sprites. Nachdem der DMA-Controller diese Worte gelesen und in den ent¬ sprechenden Registern plaziert hat, wartet er, bis der Elektronenstrahl die Anfangszeile des Sprites erreicht. Dann werden mit jeder Zeile zwei Datenworte gelesen und von der Hardware in Denise an der entsprechenden horizontalen Position auf dem Bildschirm ausgegeben, bis die letzte Zeile des Sprites vorbei ist. Die nächsten beiden Worte in der Sprite-Datenliste werden wieder als Kontrollworte aufgefaßt. Sind beide gleich 0, beendet der DMA-Kanal seine Aktivität. Es ist aber auch möglich, eine neue Sprite-Position anzugeben. Der DMA-Con- Die Hardware des Amiga 175 troller wartet dann erneut auf die Startzeile, und der Vorgang wieder¬ holt sich solange, bis zwei Kontrollworte mit dem Wert 0 als Endmarkierung der Liste gefunden werden. Aufbau einer Sprite-Datenliste (Start = Anfangsadresse der Liste im Chip-RAM): Adrenc Inhalt Start+4 Start+8 Start+12 Start+4*n Start+4*(n+l) 1. u. 2. Datenwort der 1. Zeile des Sprites 1. u. 2. Datenwort der 2. Zeile des Sprites 1. u. 2. Datenwort der 3. Zeile des Sprites 1. u. 2. Datenwort der n. Zeile des Sprites 0,0 Ende der Sprite-Datenliste Aufbau des ersten Kontrollworts: Bit-Nr.; 15 U 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Funktion; E7 E6 ES E4 E3 E2 El EO H8 H7 H6 H5 H4 H3 H2 Hl Aufbau des zweiten Kontrollworts: Bit-Nr.: 15 U 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Funktion: L7 L6 L5 L4 L3 L2 LI LO AT 0 0 0 0 E8 L8 HO HO bis HB EO bifl ES LO bis LS AT HoriBontale Position des Sprites (HSTART) Erste Zeile des Sprites (VSTART) Letste Zeile des Sprites-fl (VSTOP) Attach-Kontroll-Bit Es sind je 9 Bits für die horizontale und vertikale Position des Sprites vorgesehen. Diese Bits sind allerdings etwas unpraktisch über die bei¬ den Kontrollregister verteilt. Die Auflösung beträgt in horizontaler Richtung einen niedrig- auflösenden Punkt, in vertikaler Richtung eine Rasterzeile. Diese Werte sind unveränderlich, da unabhängig von dem eingestellen Modus des/der Playfields. Die Sprites sind auf das Bildschirmfenster (eingestellt in DIWSTRT und DIWSTOP) beschränkt. Liegen die in den Kontrollworten festge¬ legten Koordinaten außerhalb, werden die Sprites nur noch teilweise oder überhaupt nicht mehr dargestellt, da alle Punkte, die sich nicht innerhalb des Bildschirmfensters befinden, abgeschnitten werden. Die horizontale und vertikale Startposition bezieht sich auf die linke, obere Ecke des Sprites. Die vertikale Stopposition legt die erste Zeile 176 Amiga intern nach dem Sprite fest, d.h. die letzte Zeile des Sprites+1. Die Anzahl der Zeilen eines Sprites ist also VSTOP-VSTART. Folgende Beispielliste stellt ein Sprite an den Koordinaten 180,160 dar, also etwa in Bildschirmmitte. Er soll eine Höhe von 8 Zeilen haben. Die letzte Zeile (VSTOP) ist also 168. Faßt man die beiden Datenworte zusammen, erhält man Zahlen von 0 bis 3, die jeweils eine der drei Sprite-Farben oder die durchsichtigen Punkte repräsentieren. Dadurch kann man sich das Sprite besser vor¬ stellen: 0000002222000000 0000220000220000 0002200330022000 0022003113002200 0022003113002200 0002200330022000 0000220000220000 0000002222000000 In der Datenliste muß man beide Worte getrennt angeben: Start: dc.w $A05A,$A800 ;HSTART = $B4, VSTART = $A0, VSTOP = SA8 dc.u XOOOO 0000 0000 0000,%0000 0011 1100 0000 dc.w XOOOO 0000 0000 0000,XOOOO 1100 0011 0000 dc.w XOOOO 0001 1000 0000,X0001 1001 1001 1000 dc.w XOOOO 0011 1100 OOOO.XOOll 0010 0100 1100 dc.w XOOOO 0011 1100 OOOO.XOOll 0010 0100 1100 dc.w XOOOO 0001 1000 OOOO.XOOOl 1001 1001 1000 dc.w XOOOO 0000 0000 0000,XOOOO 1100 0011 0000 dc.w XOOOO 0000 0000 0000,XOOOO 0011 1100 0000 dc.w 0,0 ;Ende der Sprite-Datenliste Das AT-Bit im 2. Kontrollwort legt fest, ob zwei Sprites miteinander kombiniert werden. Es hat nur in den Sprites mit ungeraden Nummern eine Funktion (Sprites 1, 3, 5, 7). Ist es z.B. für Sprite I gesetzt, wer¬ den dessen Daten-Bits mit denen von Sprite 0 gemeinsam als 4-Bit- Zeiger auf die Farbtabelle interpretiert. Die Reihenfolge der Bits ist dabei folgendermaßen: Sprite 1 (ungerade Nummer), sweites Datenwort: Bit 3 (MSB) Sprite 1, erstes Datenwort: Bit 2 Sprite 0 (gerade Nummer), sweites Datenwort: Bit 1 Sprite 0, erstes Datenwort: Bit 0 (LSB) Sollen zwei Sprites auf diese Weise miteinander kombiniert werden, muß auch ihre Position übereinstimmen. Ist dies nicht der Fall, wird Die Hardware des Amiga 177 automatisch auf die alte, dreifarbige Darstellung zurückgeschaltet. Am einfachsten ist es, in beide Sprite-Datenlisten dieselben Kontrollworte zu schreiben. Nun noch als Beispiel eine Sprite-Datenliste für ein fünfzehnfarbiges Sprite: Der Einfachheit halber soll unser Sprite nur aus 4 Zeilen bestehen. Die Ziffern stellen wieder die Farbe der entsprechenden Punkte da. Um alle fünfzehn Farben plus transparent darstellen zu können, wurden die Hexadezimalziffern "A" bis "F" dazugenommen. 0011111111111100 1123456789ABCD11 11EFEFEFEFEFEF11 0011111111111100 Die Bildung der notwendigen Datenworte kann man gut aus Zeile 2 ersehen: Farben des Sprite: 1123456789ABCD11 Spritei, Datenwort2: 0000000011111100 Spritei, Datenwortl: 0000111100001100 SpriteO, Datenwort2: 0011001100110000 SpriteO, Datenwortl: 1101010101010111 Die Datenlisten für das gesamte Sprite sehen folgendermaßen aus: Horizontale Position (HSTART) sei wieder 180. Erste Zeile des Sprites (VSTART) 160, letzte Zeile (VSTOP) 164. StartSpriteO: dc.u $A05A,$A400 dc.w %0011 1111 1111 dc.w X1101 0101 0101 dc.w X1101 0101 0101 dc.w %0011 1111 1111 dc.w 0,0 ;HSTART=*B4, VSTART=$A0, VST0P=*A4, AT=0 1100,%0000 0000 0000 0000 0111,%0011 0011 0011 0000 0111,%0011 1111 1111 1100 1100,%0000 0000 0000 0000 StartSpritel: dc.w $A05A,$A480 ;HSTART=$B4, VSTART=$A0, VSTOP=$A4, AT=1 dc.w %0000 0000 0000 0000,%0000 0000 0000 0000 dc.w %0000 1111 0000 1100,*0000 0000 1111 1100 dc.w *0011 1111 1111 1100,*0011 1111 1111 1100 dc.w *0000 0000 0000 0000,*0000 0000 0000 0000 dc.w 0,0 178 Amiga intern Mehrere Sprites über einen DMA-Kanal Nachdem ein Sprite dargestellt wurde, ist dieser DMA-Kanal wieder frei. In dem obigen Beispiel werden die letzten Sprite-Daten in der Zeile 163 gelesen. Danach wird der Sprite-DMA-Kanal durch die bei¬ den Nullen in der Datenliste abgeschaltet. Wie schon erwähnt, ist es aber auch möglich, den DMA-Kanal weiter zu benutzen. Man schreibt hierzu zwei neue Kontrollworte anstelle der beiden Nullen in die Da¬ tenliste. Einzige Bedingung hierbei ist, daß zwischen der ersten Zeile des nächsten Sprites und der letzten des vorangegangenen Sprites min¬ destens eine Zeile frei bleibt. Geht der vorherige z.B. bis Zeile 163, kann der nächste nicht vor Zeile 165 beginnen. Der Grund dafür ist, daß in der Zeile dazwischen (164) die beiden Kontrollworte gelesen werden müssen. Der Ablauf des Sprite-DMA ist dann wie folgt: Zeile _ Daten über den DMA-Kanal _ 162 Vorletate Zeile des 1. Sprites Über diesen Kanal 163 Letste Zeile des 1. Sprites 164 Kontrollworte des 2. Sprites 165 Erste Zeile des 2. Sprites 166 Zweite Zeile des 2. Sprites Folgendes Beispiel stellt das dreifarbige Sprite aus unserer ersten Sprite-Datenliste an zwei verschiedenen Bildschirmpositionen dar: .•Erster Sprite iäaer diesen DMA-Kanal an Zeile 160 (SAO) ;Horizontale Position: 180 (SBA) dc.u $A05A,$A800 ;HSTART = $BA, VSTART = SAO, VSTOP = SAB dc.u XOOOO 0000 0000 OOOO.XOOOO 0011 1100 0000 dc.u XOOOO 0000 0000 OOOO.XOOOO 1100 0011 0000 dc.u XOOOO 0001 1000 OOOO.XOOOl 1001 1001 1000 dc.u XOOOO 0011 1100 OOOO.XOOll 0010 0100 1100 dc.u XOOOO 0011 1100 OOOO.XOOll 0010 0100 1100 dc.u XOOOO 0001 1000 OOOO.XOOOl 1001 1001 1000 dc.u XOOOO 0000 0000 OOOO.XOOOO 1100 0011 0000 dc.u XOOOO 0000 0000 OOOO.XOOOO 0011 1100 0000 ;Jetzt folgt der zueite Sprite über diesen DMA-Kanal ;an Zeile 176 (SBO), horizontale Position 300 (S12C} dc.u SB096,SB800 ;HSTART = S12C, VSTART = SBO, VSTOP = SB8 dc.u XOOOO 0000 0000 OOOO.XOOOO 0011 1100 0000 dc.u XOOOO 0000 0000 OOOO.XOOOO 1100 0011 0000 dc.u XOOOO 0001 1000 OOOO.XOOOl 1001 1001 1000 dc.u XOOOO 0011 1100 OOOO.XOOll 0010 0100 1100 dc.u XOOOO 0011 1100 OOOO.XOOll 0010 0100 1100 dc.u XOOOO 0001 1000 OOOO.XOOOl 1001 1001 1000 dc.u XOOOO 0000 0000 OOOO.XOOOO 1100 0011 0000 dc.u XOOOO 0000 0000 OOOO.XOOOO 0011 1100 0000 dc.u 0,0 .-Ende der Sprite-Datenliste Die Hardware des Amiga 179 Aktivierung der Sprites Nachdem man eine korrekte Sprite-Datenliste im Chip-RAM aufge¬ baut und die gewünschten Farben in die Farbtabelle geschrieben hat, muß man dem DMA-Controller noch mitteilen, an welcher Adresse sich diese Datenliste befindet, bevor man den Sprite-DMA einschalten kann. Dafür besitzt jeder Sprite-DMA-Kanal ein Registerpaar, in das man die Anfangsadresse der Datenliste schreiben muß: SPRxPT-Register (SpritexPointer, Zeiger auf Datenliste für Sprite- DMA-Kanal x): Reg. Name_Funktion $120 SPROPTH $122 SPROPTL $124 SPRIPTH $126 SPRIPTL $128 SPR2PTH $12A SPR2PTL $12C SPR3PTH $12E SPR3PTL $130 SPR4PTH $132 SPR4PTL $134 SPR6PTH $136 SPR5PTL $138 SPR6PTH $13A SPROPTL $13C SPR7PTH $13D SPR7PTL Zeiger auf die Sprite-Datenliste Bits 16-18 für Sprite-DMA-Kanal 0 Bits 0-15 Zeiger auf die Sprite-Datenliste Bits 16-18 für Sprite-DMA-Kanal 1 Bits 0-15 Zeiger auf die Sprite-Datenliste Bits 16-18 für Sprite-DMA-Kanal 2 Bits 0-15 Zeiger auf die Sprite-Datenliste Bits 16-18 für Sprite-DMA-Kanal 3 Bits 0-15 Zeiger auf die Sprite-Datenliste Bits 16-18 für Sprite-DMA-Kanal 4 Bits 0-15 Zeiger auf die Sprite-Datenliste Bits 16-18 für Sprite-DMA-Kanal 5 Bits 0-15 Zeiger auf die Sprite-Datenliste Bits 16-18 für Sprite-DMA-Kanal 6 Bits 0-15 Zeiger auf die Sprite-Datenliste Bits 16-18 für Sprite-DMA-Kanal 7 Bits 0-15 Alle SPRxPT können nur beschrieben werden. Der DMA-Controller benutzt diese Register als Zeiger auf die aktuelle Adresse in der Sprite-Datenliste. Zum Beginn jedes Bildes enthalten sie die Adresse des ersten Kontrollworts. Mit jedem gelesenen Daten¬ wort werden sie um ein Wort erhöht, so daß sie am Ende des Bildes auf das erste Wort nach der Datenliste zeigen. Damit in jedem Bild dieselben Sprites dargestellt werden, müssen diese Zeiger vor jedem Bild wieder auf den Anfang der Sprite-Datenliste zurückgesetzt wer¬ den. Wie schon bei den Bit-Plane-Zeigern BPLxPT erledigt dies am einfachsten der Copper innerhalb der vertikalen Austastlücke. Der entsprechende Abschnitt der Copper-Liste kann etwa so aussehen: StartSpritexH = Anfangsadr. d. Sprite-Datenliste für Sprite x, Bits 16-19 StartSpritexL = Bits 0-15 CopperlistStart MOVE #StartSpriteOH.SPROPTH ;Initialisierung von Sprite-DMA- MOVE #StartSpriteOL,SPROPTL ;KanalO MOVE #StartSprite1H,SPR1PTH ;Initialisierung von Sprite-DMA- 180 Amiga intern HOVE #StartSprite1L,SPR1PTL HOVE #StartSprite2H,SPR2PTH MOVE #StartSprite2L,SPR2PTL HOVE #StartSprite7H,SPR4PTH MOVE #StartSprite7L,SPR4PTL UAIT SFFFE ;Kana 11 ;Kana 12 ;Ebenso für Kanal 3 bis 6 ;Zuletzt noch Kanal 7 ;Sonstige Aufgaben des Coppers ;Ende der Copper-List Es gibt keine Möglichkeit, die Sprite-DMA-Kanäle einzeln ein- und auszuschalten. Das SPREN-Bit (Bit-Nr. 5) im DMACON-Register schaltet den Sprite-DMA für alle acht Sprite-DMA-Kanäle ein. Will man nicht alle davon verwenden, muß man die unbenutzten Kanäle leere Datenlisten bearbeiten lassen. Dazu setzt man ihre SPRxPT auf zwei Speicherworte mit dem Inhalt Null. Man kann dazu z.B. die bei¬ den Nullen am Ende einer vorhandenen Datenliste benutzen. Es müssen aber immer alle acht SPRxPT innerhalb der vertikalen Austastlücke initialisiert werden! Auch wenn die Datenliste nichts außer den zwei Nullen enthält, zeigt der SPRxPT des DMA-Kanals am Ende eines Bilds auf das erste Wort nach ihnen. Selbstverständlich kann die Initialisierung der SPRxPT auch vom Pro¬ zessor innerhalb des Vertical-Blanking-Interrupts erledigt werden. Als letzter Schritt muß noch der Sprite-DMA eingeschaltet werden. Dies geschieht mittels des SPREN-Bit des DMACON-Registers für alle acht Sprite-DMA-Kanäle gemeinsam. Folgender Move-Befehl er¬ ledigt dies: MOVE.U #$8220,$0FF096 ;SPREN und DMAEN im DMACON-Register setzen Bewegen von Sprites Die Position eines Sprites bestimmen die Werte der beiden Kon- trollworte in der Sprite-Datenliste. Um ein Sprite zu bewegen, muß man diese Werte schrittweise verändern. Dies kann direkt vom Pro¬ zessor mittels entsprechender Move-Befehle erledigt werden. Man muß allerdings etwas aufpassen. Ändert man die Kontrollworte zu einem beliebigen Zeitpunkt, kann folgendes Problem auftauchen: Der Prozessor modifiziert das erste Kontrollwort. Bevor er das zweite ebenfalls verändern kann, liest der DMA-Controller beide Worte. Da diese jetzt nicht mehr zueinander passen, erscheint kurzzeitig irgend¬ welcher Unsinn auf dem Bildschirm. Die Hardware des Amiga 181 Man kann das am einfachsten vermeiden, indem man die Kon- trollworte lediglich innerhalb der vertikalen Austastlücke ändert (in¬ nerhalb des Vertical-Blanking-Interrupts, nachdem der Copper die SPRxPT initialisiert hat). Die Sprite/Playfield-Priorität Die Priorität eines Playfields oder Sprites bestimmt, ob es vor, hinter oder zwischen den anderen Bildschirmelementen erscheint. Das Sprite mit der höchsten Priorität befindet sich vor allen anderen. Es kann nicht von ihnen verdeckt werden. Die Priorität eines Sprites wird durch seine Nummer festgelegt. Je niedriger, desto höher ist die Prio¬ rität. Sprite 0 steht damit immer vor allen anderen Sprites. ,Bei den Playfields bestimmt ein Kontroll-Bit, ob Nummer 1 oder 2 vorne liegt. Wie aber steht es mit der Priorität der Sprites in Bezug auf die Playfields? Beim Amiga ist es möglich, die Playfields fast beliebig zwischen den Sprites zu positionieren. Bei der Festlegung der Playfield-Priorität ge¬ genüber den Sprites werden immer zwei Sprites zusammengefaßt und gemeinsam behandelt. Es sind dieselben Kombinationen wie schon bei den fünfzehnfarbigen Sprites. Immer ein Sprite mit einer geraden Nummer und sein ungerader Nachfolger: Sprite 0 & 1, Sprite 2 & 3, Sprite A & 5, Sprite 6 & 7 Man kann die vier Sprite-Paare als einen Stapel aus vier Elementen betrachten. Blickt man von oben auf einen solchen Stapel, kann man darunterliegende Elemente nur durch Löcher in darüberliegenden Sta¬ pelpositionen sehen. Die Löcher entsprechen den transparenten Punk¬ ten der Bit-Planes oder Sprites und den Teilen des Bildschirms, die ein Sprite auf Grund seiner Größe nicht verdecken kann. Die Rei¬ henfolge der Elemente auf dem Stapel ist nicht veränderbar. Aber man kann zwei andere Elemente, nämlich die Playfields, an beliebigen Stellen zwischen den vier Sprite-Paaren einordnen. Fünf Positionen sind für jedes Playfield möglich: Pogition_Reihenfolge von vorne nach hinten 0 PLF SPRO&l SPR2&3 SPR4&5 SPR6&7 1 SPRO&l PLF SPR2&3 SPR4&5 SPR6&7 2 SPRO&l SPR2&3 PLF SPR4&5 SPR6&7 3 SPRO&l SPR2&3 SPR4&S PLF SPR6&7 4 SPRO&l SPR2&3 SPR4&S SPR6&7 PLF 182 Amiga intern Das BPLC0N2-Register enthält die Priorität der Playfields in bezug auf die Sprites: BLPCON2 $104 (nur schreiben) Bit-Nr.: 15-7 6 543210 Funktion: Unbel. PF2PRI PF2P2 PF2P1 PF2P0 PF1P2 PF1P1 PF1P0 PF2PRI Ist dieses Bit gesetzt, erscheint Playfield 2 vor Playfield 1. PFIPO bis PF1P2 Diese drei Bits bilden eine 3-Bit-Zahl, die die Position des Playfields Nummer 1 (alle ungeraden Bit-Planes) zwischen den vier Sprite-Paa- ren bestimmt. Es sind Werte von 0 bis 4 möglich (siehe obige Tabelle). PF2P0 bis PF2P2 Diese drei Bits entsprechen in ihrer Funktion den Bits PFIPO bis PF1P2, nur diesmal für Playfield Nummer 1 (alle geraden Bit-Planes). Beispiel: BPLCON2 = $0003 Dies bedeutet Playfield 1 vor Playfield 2, PF2P0-2 = 0, PFlPO-2 = 3 und ergäbe, von vorne nach hinten, folgende Reihenfolge: PLF2 SPRO&I SPR2&3 SPR4&5 PLF1 SPR6&7 Genau betrachtet, etwas paradox. Das PLF2PRI-Bit ist 0, also Play¬ field 1 vor Playfield 2. Trotzdem stimmt die Reihenfolge oben. Wenn sich eines der Sprites 0 bis 5 zwischen Playfield 1 und 2 befindet, steht es, entsprechend seiner Priorität, vor Playfield 1. Da dieses vor Playfield 2 steht, ist an dieser Stelle das Sprite sichtbar, obwohl es sich eigentlich hinter Playfield 2 befinden müßte. Liegen dagegen nur Playfield 2 und das Sprite an einer gemeinsamen Position, verdeckt Playfield 2 aufgrund seiner Priorität das Sprite. Dies liegt daran, daß die Playfield/Playfield-Priorität Vorrang vor der Sprite/Playfield-Priorität hat. Die Hardware des Amiga 183 Verwendet man den Dual-Playfield-Modus nicht, gibt es nur ein ein¬ ziges Playfield, das dann von den geraden und ungeraden Bit-Planes gemeinsam gebildet wird. Das PLF2PRI- und die PL2P0- bis PL2P2- Bits haben dann keine Funktion mehr. Kollisionen zwischen Grafikelementen Es ist häufig sehr nützlich zu wissen, ob zwei Sprites miteinander oder mit dem Hintergrund kollidiert sind. Dies erleichtert z.B. in Spielpro¬ grammen die Erkennung eines Treffers. Wenn sich an einer bestimmten Bildschirmposition die Punkte zweier Sprites überlappen, d.h. beide einen gesetzten Punkt (nicht transpa¬ rent) an denselben Koordinaten besitzen, wird dies als Kollision zwi¬ schen den zwei Sprites aufgefaßt. Auch ein Zusammenstoß der Play- fields miteinander oder mit einem Sprite ist möglich. Jede erkannte Kollision wird im Kollisions-Datenregister, CLXDAT, gespeichert: CLXDAT $00E (nur lesen) Bit‘Nr. Kollision cwischen _ 15 UnbenutEt 14 Sprite 4 foder 5l und Sprite 6 (oder 7) 13 Sprite 2 (oder 3) und Sprite 6 (oder 7) 12 Sprite 2 foder 31 und Sprite 4 (oder 6) 11 Sprite 0 (oder 1] und Sprite 6 (oder 7) 10 Sprite 0 (oder 1) und Sprite 4 (oder 6j 9 Sprite 0 (oder 1] und Sprite 2 (oder 3) 8 Piayfield 2 (ger^e Bit-Planesl und Sprite 6 (oder 7) 7 Playfield 2 (gerade Bit-Planes) und Sprite 4 (oder 5) 6 Playfield 2 (gerade Bit-Planesj und Sprite 2 (oder 3) 5 Playfield 2 (gerade Bit-Planes) und Sprite 0 (oder 1) 4 Playfield 1 ^ungerade Bit-Planes) und Sprite 6 (oder 7) 3 Playfield 1 (ungerade Bit-Planes) und Sprite 4 (oder 5) 2 Playfield 1 ^ungerade Bit-Planes) und Sprite 2 (oder 3) 1 Playfield 1 (ungerade Bit-Planes) und Sprite 0 (oder 1) 0 Playfield 1 und Playfield 2 Während bei einem Sprite jeder nichttransparente Punkt eine Kollision auslösen kann, läßt sich bei den Playfields frei einstellen, welche Farbe zur Kollisionserkennung herangezogen werden soll. Außerdem ist es möglich. Jedes Sprite mit einer ungeraden Nummer wahlweise in die Kollisionserkennung einzubeziehen oder von ihr auszuschließen. Mit den Bits im Kollisions-Kontrollregister, CLXCON, läßt sich dies alles einstellen. 184 Amiga intern CLXCON $098 (nur schreiben) Bit-Nr. Name_Funktion 15 ENSP7 Kollisionserkennung für Sprite 7 erlauben 14 ENSP5 KolliBionserkennung für Sprite 5 erlauben 13 ENSP3 Kollisionaerkennung für Sprite 3 erlauben 12 ENSPl Kollisionserkennung für Sprite 1 erlauben 11 ENBP6 Bit-Plane 6 mit MVBP6 vergleichen 10 ENBP5 Bit-Plane 5 mit MVBP5 vergleichen 9 ENBP4 Bit-Plane 4 mit MVBP4 vergleichen 8 ENBP3 Bit-Plane 3 mit MVBP3 vergleichen 7 ENBP2 Bit-Plane J mit MVBP2 vergleichen 6 ENBPl Bit-Plane 1 mit MVBPl vergleichen 5 MVBP6 Wert für Kollision mit Bit-Plane 6 4 MVBP6 Wert für Kollision mit Bit-Plane 5 3 MVBP4 Wert für Kollision mit Bit-Plane 4 2 MVBP3 Wert für Kollision mit Bit-Plane 3 1 MVBP2 Wert für Kollision mit Bit-Plane 2 0 MVBPl Wert für Kollision mit Bit-Plane 1 Die ENSPx-Bits (Enable Sprite x) bestimmen, ob das entsprechende Sprite mit ungerader Nummer zur Kollisionserkennung herangezogen wird. Ist z.B. das ENSPl-Bit gesetzt, wird eine Kollision zwischen Sprite 1 und einem anderen Sprite oder einem Playfield registriert. Dabei wird dasselbe Bit im Kollisions-Datenregister gesetzt, wie für Sprite 0. Es ist also nicht möglich, anhand des Registerinhalts zu ent¬ scheiden, ob Sprite 0 oder 1 eine Kollision ausgelöst hat. Außerdem werden keine Kollisionen von Sprite 0 und Sprite 1 untereinander er¬ kannt. Bei der Auswahl der Sprites sollten Sie dies beachten. Hat man zwei Sprites zu einem fünfzehnfarbigen Sprite kombiniert, muß das entsprechende ENSPx-Bit gesetzt werden, um eine korrekte Kollisionserkennung zu ermöglichen. Bei den Playfields kann man selber festlegen, welche Bit-Kombi¬ nationen der Bit-Planes eine Kollision auslösen sollen und welche nicht. Die ENBPx-Bits (Enable Bit-Plane x) bestimmen, welche Bit- Planes zur Kollisionserkennung herangezogen werden. Sind alle ENBPx-Bits eines Playfields gesetzt, ist eine Kollision an all den Punkten möglich, deren Bit-Kombination derjenigen der MVBPx-Bits (Match Value Bit-Plane x) entspricht. Die ENBPx-Bits bestimmen, ob die Bits aus Plane x mit dem Wert von MVBPx verglichen werden. Stimmen bei einem Punkt die Bits aller Planes, bei denen ENBPx-gesetzt ist, mit dem zugehörigen MVBPx überein, kann dieser Punkt eine Kollision auslösen. Kompliziert? Ein Beispiel schafft Klarheit; Die Hardware des Amiga 185 Die ENBPx-Bits sind gesetzt, ebenso alle MVBPx-Bits. Jetzt können nur noch diejenigen Punkte des Playfields eine Kollision auslösen, deren Bit-Kombination binär 111111 beträgt. Sind nur die unteren drei MVBPx-Bits gesetzt, ist eine Kollision lediglich möglich, wenn der Punkt des Playfields die Kombination 000111 hat. Soll eine Kollision an allen Punkten mit der Bit-Kombination 000111, 000110, 000100 oder 000101 erlaubt sein, müssen die MVBP-Bits auf 000100 stehen. Die unteren beiden Bits sollen immer der Kollisionsbe¬ dingung erfüllen, dazu werden die entsprechenden ENBPx-Bits ge¬ löscht, ENBP-Wert: 111100. Beispiele für mögliche Bit-Kombinationen: ENBPx MVBPx Kollision mäglich bei Bit-Muster 111111 111111 111111 111111 111000 111000 111100 lllixx 111100, 011111 xOOOOO 000000, 000000 xxxxxx Kollision 111101, 111110, 111111 100000 bei jedem Bit-Muster möglich Der Wert eines mit "x" bezeichneten Bits ist irrelevant. Sind nicht alle sechs Bit-Planes aktiv, müssen die ENBPx-Bits der unbenutzten Planes auf 0 gesetzt werden! Die verschiedenen Kombinationsmöglichkeiten der ENBPx- und MVBPx-Bits erlauben eine Vielfalt unterschiedlicher Kolli¬ sionserkennungen. Man kann z.B. das CLXCON-Register so einstellen, daß die Sprites nur mit den roten und grünen Punkten des Playfields Zusammenstößen können, nicht aber mit den anderen Farben. Oder eine Kollision ist nur an den transparenten Punkten von Playfield 1 möglich, wenn die dahinterliegenden Punkte von Playfield 2 gleich¬ zeitig schwarz sind usw. Sonstige Sprite-Register Für jedes Sprite existieren neben den SPRxPT-Registern noch vier weitere Register. Sie werden normalerweise vom DMA-Controller au¬ tomatisch mit Daten versorgt. Es ist aber auch möglich, sie von Hand, d.h. mit dem Prozessor, anzusprechen. SPRxPOS SPRxCTL SPRxDATA SPRxDATB Erstes Kontrollwort Zweites Kontrollwort Erstes Datenwort einer Zeile (Low*Wort) Zweites Datenwort einer Zeile (High-Wort) 186 Amiga intern X steht wieder für eine Sprite-Nummer von 0 bis 7, die Adressen dieser Register finden sich in der Registerübersicht in Kapitel 1.5.1. Der DMA-Controller schreibt die beiden Kontrollworte eines Sprites direkt in zwei Register, SPRxPOS und SPRxCTL. Wenn ein Wert in das SPRxCTL-Register geschrieben wird, egal ob per DMA oder vom 68000, schaltet Denise die Sprite-Ausgabe ab. Das Sprite wird nicht mehr auf dem Bildschirm ausgegeben. Der DMA-Controller wartet jetzt auf die in VSTART festgelegte Zeile. Dann schreibt er die zwei ersten Datenworte ins SPRxDATA- und SPRxDATB-Register. Damit wird das Sprite angezeigt, denn beim Schreiben in das SPRxDATA-Register, schaltet Denise die Sprite-Aus¬ gabe wieder ein. Es vergleicht ab Jetzt die gewünschte horizontale Po¬ sition aus den SPRxCTL- und SPRxPOS-Registern mit der tatsächli¬ chen Bildschirmspalte und stellt das Sprite an der richtigen Stelle auf dem Monitor dar. Der DMA-Controller schreibt in Jeder Zeile zwei neue Datenworte in SPRxDATA/B, bis die letzte Zeile des Sprites (VSTOP) vorbei ist. Da¬ nach holt er die nächsten Kontrollworte und plaziert sie in SPRxPOS und SPRxCTL. Damit wird das Sprite wieder abgeschaltet, bis die nächste VSTART-Position erreicht ist. Waren beide Kontrollworte Null, beendet der DMA-Controller den Sprite-DMA für den entspre¬ chenden Kanal bis zum Beginn des nächsten Bildes. Am Ende der vertikalen Austastlücke beginnt er wieder an der aktuellen Adresse in SPRxPT. Darstellung der Sprites ohne DMA Man kann ein Sprite auch problemlos ohne den DMA-Kanal darstel¬ len. Man schreibt dazu die gewünschten Kontrollworte einfach direkt in die SPRxPOS- und SPRxCTL-Register. Dabei müssen nur die HSTART-Position und das AT-Bit gültige Werte enthalten. VSTART und VSTOP werden ausschließlich vom DMA-Kanal benutzt. Man kann die Sprite-Ausgabe in Jeder beliebigen Zeile beginnen, in¬ dem man die beiden Datenworte ins SPRxDATA- und SPRxDATB- Register schreibt. Da SPRxDATA die Sprite-Ausgabe einschaltet, ist es besser, wenn man erst SPRxDATB beschreibt. Ändert man die Da¬ ten in beiden Registern nicht, werden sie in Jeder Zeile wieder ange¬ zeigt. Es entsteht ein vertikaler Balken. Die Hardware des Amiga 187 Will man das Sprite wieder abschalten, schreibt man einfach einen be¬ liebigen Wert in SPRxPOS. 1.5.7 DerBlitter Was ist ein Blitter? Der Name Blitter ist eine Abkürzung der engli¬ schen Bezeichnung: "Block Image Transferer", was etwa soviel heißt wie Bild-Datenblock-Kopierer. Genau dies ist nämlich die Hauptauf¬ gabe des Blitters: das Verschieben und Kopieren von Datenblöcken im Speicher, wobei es sich meistens um Grafikdaten handelt. Der Blitter kann auch mehrere Speicherbereiche miteinander logisch verknüpfen und das Ergebnis wieder zurück in den Speicher schreiben. Er erledigt diese Aufgaben sehr schnell. Einfaches Datenverschieben geht mit ei¬ ner Geschwindigkeit von bis zu 16 Millionen Punkten in der Sekunde vonstatten! Zusätzlich kann der Blitter noch Flächen füllen und Linien ziehen. Die Kombination dieser beiden Fähigkeiten ermöglicht das Zeichnen beliebiger, ausgefüllter Vielecke, und das vielfach schneller, als es mit dem 68000 möglich wäre. Das Betriebssystem verwendet den Blitter für beinahe alle Gra¬ fikoperationen. Er erledigt die Textausgabe, zeichnet Gadgets, ver¬ schiebt Fenster usw. Außerdem erledigt er auch noch die Decodierung der Daten von der Diskette. Ein Beispiel dafür, daß sich die vielfälti¬ gen Fähigkeiten des Blitters nicht nur auf die Grafik beschränken. Die Verwendung des Blitters zum Kopieren von Daten Der Blitter arbeitet beim Kopieren von Daten immer nach dem glei¬ chen Muster: Ein bis drei verschiedene Speicherbereiche, die Daten¬ quellen, werden miteinander durch die gewählte logische Bedingung verknüpft, und das Ergebnis wird wieder zurück in den Speicher ge¬ schrieben. Das Spektrum reicht vom einfachen Kopieren bis zum komplexen Verknüpfen mehrerer Datenbereiche. Die Adressen der Quelldatenbereiche, genannt A, B und C, und des Zieldatenbereichs D lassen sich innerhalb des Chip-RAM frei wählen (Adressen von 0 bis $7FFFF). Die Anzahl der Worte, die in einer Blitter-Operation verarbeitet wer¬ den, darf bis zu 65536 betragen. Es können also bis zu 128 KByte Daten in einem Zug durch den Speicher geschleudert werden. 188 Amiga intern Der Blitter unterstützt sogenannte rechteckige Speicherbereiche. D.h. der Speicher ist, wie bei einer Bit-Map, in Spalten und Zeilen aufge¬ teilt. Es ist auch möglich, einen kleinen Bereich innerhalb einer großen Bit-Map zu bearbeiten, indem sogenannte Modulo-Werte ver¬ wendet werden. Vielleicht erinnern Sie sich daran: Solche Modulo- Werte wurden auch schon bei den Playfields verwendet, um Bit-Planes zu definieren, die breiter als das Bildschirmfenster waren. Um Blitter-Operationen zu starten sind folgende Schritte notwendig: ■ Wählen des Blitter-Modus: Daten kopieren. ■ Auswahl der Quelldatenbereiche (Es müssen nicht immer alle drei Quellen benutzt werden.) und des Zielbereichs. ■ Auswahl der logischen Verknüpfung. ■ Definition sonstiger Betriebsparameter (Scrolling, Maskierung, Adressierungsrichtung). ■ Festlegung des Fensters, in dem die Blitter-Operation durchge¬ führt werden soll, und Starten des Blitters. Die Festlegung des Blitter-Fensters Sie werden sich fragen, warum wir mit der Beschreibung des letzten Punkts beginnen. Nun, eigentlich ist die Definition des gewünschten Fensters die Grundlage aller anderen Einstellungen. Aber wenn man den Blitter programmiert, wird dieser Wert erst am Ende in das ent¬ sprechende Register geschrieben, da der Blitter damit gleichzeitig ge¬ startet wird. Aus diesem Grund steht dieser Punkt auch zuletzt in der obigen Liste. Für das Verständnis der anderen Werte ist es aber not¬ wendig, den Begriff "Blitter-Fenster" zu kennen. Das Blitter-Fenster ist der Speicherbereich, den der Blitter in der Blitter-Operation bearbeiten soll. Er ist wie eine Bit-Plane aufgebaut, d.h. in Zeilen und Spalten aufgeteilt, wobei eine Spalte einem Wort (2 Bytes) entspricht. Die Anzahl der Worte im Fenster ist also gleich dem Produkt aus Zeilen und Spalten: Z*S. Durch die Aufteilung des gewünschten Speicherbereichs in Zeilen und Spalten ist der Blitter sehr gut für die Bearbeitung von Bit-Planes ge¬ eignet. Genausogut kann man aber auch lineare Speicherbereiche ansprechen. Die Aufteilung in Zeilen und Spalten ist ja nur eine Hilfe zur einfa- Die Hardware des Amiga 189 cheren Programmierung. In Wirklichkeit liegen die einzelnen Zeilen weiterhin an fortlaufenden Adressen im Speicher. Bei kleinen Daten¬ feldern, die nicht in Zeilen und Spalten eingeteilt sind, ist es auch möglich, die Fensterbreite oder Höhe auf 1 zu setzen. Der Blitter arbeitet das Blitter-Fenster zeilenweise ab. Die Blitter- Operation beginnt mit dem ersten Wort in der ersten Zeile und endet beim letzten Wort der letzten Zeile. Das BLTSIZE-Register enthält die Fenstergröße: BLTSIZE $058 (nur schreiben) Bit-Nr.: 15 U 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Funktion: H9 H8 H7 H6 H5 H4 H3 H2 Hl HO U5 U4 U3 U2 Ul UO H0-H9 Diese zehn Bits stellen die Höhe (Height) des Blitter-Fen- sters in Zeilen dar. Das Fenster kann eine Höhe zwischen 1 und 1024 Zeilen haben (2^® = 1024). Dabei wird eine Höhe von 1024 Zeilen dadurch gewählt, daß man Height auf 0 setzt. Für alle übrigen Werte entspricht Height direkt der Zeilenzahl. Eine Höhe von 0 Zeilen ist nicht möglich. W0-W5 Sechs Bits repräsentieren die Breite (Width) des Fensters. Sie kann von 1 bis 64 Worten (2® = 64) variieren. Rechnet man dies in Grafikpunkte um (1 Wort = 16 Pixels), ergibt das bis zu 1024 Punkte. Wie bei der Höhe erreicht man die maximale Breite von 64 Worten, indem man Width = 0 setzt. Um aus der Höhe und Breite den notwendigen BLTSIZE-Wert zu er¬ mitteln, dient folgende Formel: BLTSIZE = Höhe*64 + Breite. Sollen auch die beiden Extremfälle Height = 1024 und Width = 64 er¬ faßt werden, muß sie etwas modifiziert werden: BLTSIZE = (Höhe AND $3FF)*64 + (Breite AND $3F) Das BLTSIZE-Register sollte immer als letztes Register initialisiert werden. Der Blitter wird beim Schreiben in BLTSIZE automatisch ge¬ startet. 190 Amiga intern Quell- und Zieldatenbereiche Bei einer Blitter-Operation werden Daten aus völlig verschiedenen Speicherbereichen miteinander verknüpft. Auch wenn das Blitter- Fenster die Anzahl und Anordnung der zu bearbeitenden Daten fest¬ legt, muß die Positionierung dieses Fensters innerhalb der drei Quell- und des Zieldatenbereichs noch bestimmt werden. Nehmen wir z.B. an, der Blitter soll eine kleine, rechteckige Grafik, die irgendwo im Chip-RAM gespeichert ist, in den Bildschirmspeicher kopieren. Bei dieser einfachen Aufgabe gibt es nur einen Quellbereich. Die Wahl des Blitter-Fensters ist einfach. Die gesamte Grafik soll ko¬ piert werden, also entspricht die Breite und Höhe des Blitter-Fensters der der Grafik im Speicher. Damit der Blitter auch weiß, wo diese Grafik zu finden ist, schreibt man die Adresse des ersten Worts der obersten Zeile in das entspre¬ chende Register. Wie aber muß man den Zielbereich definieren? Die Grafik soll in den Bildschirmspeicher, sie muß also in die aktuelle Bit-Plane übertragen werden. (Der Einfachheit halber sollen sowohl Grafik als auch Bild¬ schirmspeicher nur aus einer Bit-Plane bestehen.) Nun ist die Bit- Plane aber um ein Vielfaches breiter als die kleine Grafik. Würde der Blitter sie direkt in die Bit-Plane kopieren, sähe das etwas seltsam aus. Es muß dem Blitter also neben der Adresse des Zielbereichs noch dessen Breite mitgeteilt werden. Dies geschieht mittels sogenannter Modulo-Werte. Der Modulo-Wert wird nach jeder abgearbeiteten Zeile des Blitter-Fensters zu dem Adreßzeiger addiert. Dadurch werden die Worte, die nicht beeinflußt werden sollen, übersprungen, und der Zei¬ ger steht wieder auf dem Anfang der nächsten Zeile. Damit Quell- und Zieldatenbereiche unterschiedlich breit sein können, haben sie voneinander unabhängige Modulo-Register. Die Hardware des Amiga 191 Kopieren einer Grafik in eine Bitplane Bitplane Grafik in die Bitplane kopiert ea 02 84 86 88 18 12 14 16 18 28 22 24 26 28 38 32 34 36 33 88 82 84 86 88 48 42 44 46 48 58 52 54 56 53 18 12 14 16 18 68 62 64 66 68 78 72 74 76 73 28 22 24 26 28 88 82 84 86 88 98 92 94 96 93 28 32 34 36 38 188 102 184 186 183 118 112 114 116 118 48 42 44 46 48 128 122 124 126 123 138 132 134 136 138 148 142 144 146 143 158 152 154 156 158 168 162 164 166 163 178 172 174 176 178 188 1B2 184 186 183 198 192 194 196 198 Grafik 88 82 84 86 88 18 12 14 16 18 28 22 24 26 28 38 32 34 36 38 48 42 44 46 48 88 82 84 86 88 18 12 14 16 13 28 22 24 26 28 38 32 34 36 33 48 42 44 46 48 58 52 54 56 53 68 62 64 66 68 78 72 74 76 73 88 82 84 86 88 98 92 94 96 93 188 182 184 186 188 118 112 114 116 113 128 122 124 126 128 138 132 134 136 133 148 142 144 146 148 158 152 154 156 153 168162 164 166 168 178 172 174 176 173 188 182 184 186 188 198 192 194 196 193 6 Abb. 1.5.7.1 Die Abbildung 1.5.7.1 illustriert unser Beispiel. Die Grafik besteht aus 5 Zeilen, jede 10 Worte breit. Die Zahlen stehen für die Adresse des entsprechenden Worts in bezug auf die Anfangsadresse der Grafik. Die Bit-Plane hat eine Höhe von 10 Zeilen zu 20 Worten. Wie müssen jetzt Blitter-Fenster, Anfangsadressen und Modulo-Werte gewählt werden? 192 Amiga intern Das Blitter-Fenster muß der Grafik entsprechen, da diese ja komplett kopiert werden soll, d.h. Höhe des Fensters 5 Zeilen und Breite 10 Worte. Der Wert, der nachher in das BLTSIZE-Register geschrieben werden muß, beträgt also 330 oder hexadezimal $014A. Die Anfangsadresse der Quelldaten ist geich der Adresse des ersten Datenworts der Grafik. Da die Breite einer Zeile der Grafik gleich der Zeilenbreite des Blitter-Fensters ist, bleibt der Modulo-Wert der Quelle auf 0. Für den Zieldatenbereich muß Jetzt der Modulo-Wert bestimmt wer¬ den. Dazu bildet man einfach die Differenz aus der tatsächlichen Zei¬ lenbreite und der des Blitter-Fensters. In unserem Beispiel 20 Worte minus 10 Worte: Der Modulo-Wert für den Zieldatenbereich beträgt 10 Worte. In den Modulo-Registern des Blitters muß der Modulo-Wert in Bytes angegeben weden. Modulo-Wert = Modulo in Worten *2. Als letztes benötigt der Blitter noch die Anfangsadresse der Zieldaten. Diese bestimmt die Position, an der die Grafik in die Bit-Plane ko¬ piert wird, und ist gleich der Anfangsadresse der Bit-Plane plus der Adresse der Worts, an der die linke, obere Ecke der Grafik plaziert werden soll. In unserer Abbildung ist dies die Adresse der Bit-Plane plus 24. Wie läuft die Blitter-Operation ab? Nachdem die Adressen und die Modulo-Werte unseres Beispiels fest¬ stehen, beginnt der Blitter nach der Initialisierung von BLTSIZE mit dem Kopieren der Daten. Er holt das Wort an der Anfangsadresse der Quelldaten und speichert es an der Zieladresse ab. Danach addiert er ein Wort zu beiden Adressen dazu und kopiert das nächste Wort. Dies wird sooft wiederholt, bis die in BLTSIZE eingestellte Anzahl von Worten pro Zeile bearbeitet wurde. Bevor der Blitter jetzt mit der nächsten Zeile fortfährt, addiert er den Modulo-Wert zu den Adreß- zeigern, damit die nächste Zeile an der richtigen Adresse beginnt. Nachdem alle Zeilen kopiert worden sind, schaltet sich der Blitter ab und wartet auf den nächsten Auftrag. Die Adreßregister enthalten nach einer Blitter-Operation die Adresse des letzten Worts plus 2 und plus dem Modulo-Wert. Die Adreßregister heißen BLTxPT, wobei x für eine der drei Quellen A, B, C oder den Zieldatenbereich D steht. Die Adreßregister beste- Die Hardware des Amiga 193 hen wie üblich aus einem Register für die Bits 0-15 und einem für die Bits 16-18: R«g. Name_Funktion 048 BLTCPTH 04A BLTCPTL 04C BLTBPTH 04E BLTBPTL 050 BLTAPTH 052 BLTAPTL 054 BLTDPTH 056 BLTDPTL Anfangsadreue des Bits 16-18 Quelldatenbereich C Bits 0-15 Anfangsadresse des Bits 16-18 Quelldatenbereich B Bits 0-15 Anfangsadresse des Bits 16-18 Quelldatenbereich A Bits 0-15 Anfangsadresse des Bits 16-18 Zieldatenbereich D Bits 0-15 Jeder der vier Bereiche besitzt ein eigenes Modulo-Register: 060 BLTCMOD Modulo-Wert für Quelle C 062 BLTBMOD Modulo-Wert für Quelle B 064 BLTAMOD Modulo-Wert für Quelle A 066 BLTDMOD Modulo-Wert für Zieldaten D Kopieren mit aufsleigenden oder absteigenden Adressen In unserem Beispiel arbeitete der Blitter mit aufsteigenden Adressen, d.h. er beginnt bei der Anfangsadresse und erhöht diese schrittweise bis zur Endadresse. Die Endadresse ist dabei logischerweise höher als die Anfangsadresse. Es gibt aber einen Fall, in dem eine solche Adressierung zu Fehlern führt: Das Kopieren eines Speicherbereichs an eine höhere Adresse, wobei sich Quell- und Zielbereich teilweise überlappen. Dazu ein Beispiel: Ergebnis: Adresse_Quelldaten Zieldaten Gewünscht Tatsächlich 0 Quelll 2 Quell2 4 Quells 6 QuelU Ziell Quelll Quelll 8 Quells Ziel2 Quell2 Quell2 10 Ziels Quells Quells 12 Ziel4 QuelU IQuelll! 14 Ziels Quells !Quell2l Die fünf Quelldatenworte sollen an die Adresse der Zieldaten ge¬ schrieben werden. Wenn der Blitter bei Quell 1 beginnt, überschreibt er Quell4, wenn er Quelll an der gewünschten Zieladresse Ziell abspei¬ chert, da Quell4 und Ziell dieselbe Adresse haben (beide Bereiche überlappen sich). Das gleiche passiert bei Quell2 und Ziel2. 194 Amiga intern Wenn der Blitter dann bei der Adresse von Quell4 ankommt, befindet sich dort statt dessen Quell 1. So landet Quell 1 anstellt von Quell4 in Ziel4 und Quell2 in Ziel5, während Quell4 und 5 verlorengehen. Als Lösung dieses Problems besitzt der Blitter neben dem Ascending Mode (aufsteigende Adressierung) noch den Descending Mode (abstei¬ gende Adressierung). In dieser Betriebsart beginnt er bei den Adressen in BLTxPT und erniedrigt sie nach jedem kopierten Wort um 2 Bytes. Auch der Modulo-Wert wird nicht addiert, sondern subtrahiert. Die Endadresse liegt also vor der Anfangsadresse. Dies muß man natürlich bei der Initialisierung der BLTxPT be¬ rücksichtigen. Normalerweise setzt man sie auf die linke, obere Ecke des Blitter-Fensters im jeweiligen Datenbereich (A, B, C oder D). Im Descending Mode verläuft die Adressierung rückwärts. Entsprechend muß BPLxPT auf die rechte, untere Ecke zeigen. Die Modulo- und die BLTSIZE-Werte sind mit denen des Ascending Mode identisch. Man kann über die Wahl des Mode folgende Aussagen machen: 1. Keine Überlappung zwischen Quell- und Zielbereich: Entweder Ascending oder Descending Mode, beide arbeiten in diesem Fall korrekt. 2. Quell- und Zielbereich überlappen sich teilweise, wobei der Zielbereich vor dem Quellbereich liegt: Nur der Ascending Mode arbeitet korrekt. 3. Quell- und Zielbereich überlappen sich teilweise, wobei der Zielbereich hinter dem Quellbereich liegt (siehe Beispiel): Nur der Descending Mode arbeitet korrekt. Die Auswahl der logischen Verknüpfungen Wie erwähnt, gibt es drei Quelldaten, die zu den Zieldaten verknüpft werden. Diese logischen Verknüpfungen laufen immer nur bitweise ab, d.h. aus den drei Daten-Bits A, B und C muß das Ziel-Bit D ge¬ wonnen werden. Der Blitter kennt 256 verschiedene Verknüpfungen. Diese entstehen in zwei Stufen: Die Hardware des Amiga 195 1. Es werden acht verschiedene boolesche Gleichungen auf die drei Daten-Bits angewandt. Jede davon liefert bei einer anderen Kombination aus A, B und C eine 1 als Ergebnis. 2. Die acht Ergebnisse obiger Gleichungen werden wahlweise mit¬ einander durch ein logisches ODER verknüpft. Das Ergebnis ist das Ziel-Bit D. Der Begriff "Boolesche Gleichung" steht für einen mathematischen Ausdruck, der eine Kombination logischer Verknüpfungen darstellt. Diese Rechenweise nennt man Boolesche Algebra, nach dem engli¬ schen Mathematiker George Boole (1815 bis 1864). Die weiteren Er¬ läuterungen der logischen Funktionen des Blitters sind auch ohne Kenntnisse der Booleschen Algebra zu verstehen. Die Booleschen Gleichungen werden aber mit aufgeführt. Bei drei Bit gibt es 8 mögliche Kombinationen. Jede der acht Glei¬ chungen ist bei einer davon wahr (ihr Ergebnis ist 1). Mittels acht Kontroll-Bits LFO bis LF7 kann man wählen, ob das Ergebnis der Gleichung zur Bildung von D herangezogen werden soll. Alle Ergeb¬ nis-Bits, deren zugehöriges LFx-Bit auf 1 ist, werden miteinander lo¬ gisch ODER-verknüpft. Eine ODER-Verknüpfung bedeutet, daß das Ergebnis 1 ist, wenn mindestens ein Eingangs-Bit ebenfalls auf 1 steht. Anders ausgedrückt, ein logisches ODER liefert nur eine 0, wenn alle Eingänge 0 sind. Mittels der acht LFx-Bits kann man also wählen, bei welchen Kombi¬ nationen der drei Eingangs-Bits A, B, C das Ausgangs-Bit D gleich 1 ist. Die englische Bezeichnung für die acht Booleschen Eingangsglei¬ chungen lautet "Miniterms". Folgende Tabelle gibt einen Überblick über die zu jedem LFx-Bit gehörige Eingangskombination. In der Spalte Miniterm steht ein kleiner Buchstabe für eine NOT-Ver- knüpfung des entsprechenden Eingangs-Bits. Üblicherweise wird dies durch einen Querstrich über dem Buchstaben dargestellt. Die Spalte "Eingangs-Bits" enthält die Bit-Kombination, für die die entsprechende Gleichung erfüllt ist. Die Reihenfolge der Bits ist gleich ABC. LF7 LF6 LF6 LF4 LF3 LF2 LFl LFO Miniterm: ABC ABc AbC Abc bBC aBc abC abc Eingangs-Bits: 111 110 101 100 011 010 001 000 196 Amiga intern Die Auswahl der einzelnen Miniterme ist einfach. Man setzt alle LFx- Bits, bei deren zugehöriger Eingangskombination das Ausgangs-Bit D gleich 1 sein soll. In unserem ersten Beispiel sollten die Quelldaten von A direkt nach D kopiert werden. Die Quellen B und C werden nicht verwendet. Welche Miniterme müssen dafür ausgewählt werden? D darf nur 1 sein, wenn A = 1 ist. Es kommen also nur die oberen vier Terme LF4 bis LF7 in Frage, da nur bei ihnen A = 1 ist. Damit B keine Rolle spielt, wählt man zwei Terme, in denen B einmal 1 und einmal 0 ist, die sonst aber identisch sind. Jetzt hat B keine Auswir¬ kung mehr auf D, denn der Rest der Gleichung bleibt für beide mög¬ lichen Fälle von B unverändert, und ihr Ergebnis ist nur noch von diesem Rest abhängig. Das gleiche gilt für C. Betrachtet man die Ta¬ belle der Eingangskombinationen, sieht man, daß LF4 bis LF7 akti¬ viert sein müssen. Dann hängt das Ergebnis nur noch von A ab, denn je nach Kombination von B und C ist immer eine dieser vier Gleichungen für A = 1 wahr und D damit gleich 1. Ist A = 0, sind alle vier unwahr und D = 0. Wer sich mit der Booleschen Algebra auskennt, kann auch ganz formal zu den notwendigen Minitermen kommen. Der benötigte Ausdruck ist A = D. Da B und C beim Blitter immer mit dabei sind, müssen sie entsprechend in die Gleichung integriert werden: A*(b»B)*(c+C)=0 Der Term x+X ist immer wahr (gleich 1) und wird verwendet, wenn der Wert von x für das Ergebnis D gleichgültig sein soll. Um jetzt zu den notwendigen Minitermen zu kommen, muß man nur noch aus¬ multiplizieren: 1. A*(b+B)*(c+C)=D 2. (A*b+A*B)*(c+C)=D 3. A*b*c+A*B*c+A*b»C+A*B*C=D Wie Üblich kann man die Mal-Zeichen weglassen: Abc+ABc+AbC+ABC=D Jetzt muß man nur noch die LFx-Bits der entsprechenden Miniterme setzen. Wie man sieht, kommt man mit der Booleschen Algebra eben¬ falls zum Ziel. Einige Beispiele häufig benötigter Blitter-Operationen und der zugehörigen LFx-Bits: Die Hardware des Amiga 197 ■ Invertieren eines Datenbereichs: a = D. Notwendige LFx-Kombination: 00001IIJ. Boolesche Algebra: a = d a*(b+B)*(c+C) = D (ab+aB)*(c+C) = D abc+aBc+abC+aBC = D ■ Kopieren einer Grafik in eine Bit-Plane, ohne deren Inhalt zu verändern. Entspricht einem logischen ODER der Grafik A mit der Bit-Plane B: A + B = D. Notwendige LFx-Kombination: 11111100. Boolesche Algebra: a + b = d A(b+B)(c+C)+B(a+A)(c+C) = D (Ab+AB)(c+C) + (Ba+BA)(c + C) = D Abc+ABc+AbC+ABC+Bac+BAc+BaC+BAC = D Abc+ABc+AbC+ABC+aBc+aBC = D Noch einmal die Regeln zum Heraussuchen der notwendigen LFx-Bits: 1. Bei welchen der acht Kombinationen von ABC soll D gleich 1 werden? 2. Die LFx-Bits der entsprechenden Kombinationen setzen. 3. Werden nicht alle drei Quellen benötigt, müssen sämtliche Kom¬ binationen gewählt werden, in denen die unbenutzten Bits Vor¬ kommen und die gewünschten Bits den richtigen Wert besitzen. Verschieben der Eingangswerte Bei manchen Aufgaben stört es, daß der Blitter an Wortgrenzen ge¬ bunden ist. So kann es z.B. Vorkommen, daß ein bestimmter Bereich innerhalb einer Bit-Map um einige Punkte verschoben werden soll. In diesem Fall müssen die Daten-Bits nur um den Teil eines Wortes be¬ wegt werden. Oder der Blitter soll eine Grafik an einer bestimmten Koordinate in den Bildschirmspeicher schreiben, die nicht mit einer Wortgrenze übereinstimmt. Um diese Probleme bewältigen zu können, hat der Blitter die Fähig¬ keit, die Datenworte aus den Quellen A und B um bis zu 15 Bits nach rechts zu verschieben. Damit ist er in der Lage, die Daten an jede ge¬ wünschte Punktposition zu bringen. Alle Bits, die bei dem Verschiebe¬ vorgang nach rechts hinausgeschoben werden, gelangen beim nächsten Wort in die dort freiwerdenden Bits. Es wird quasi die gesamte Zeile 198 Amiga intern bitweise verschoben. Das Verschieben übernimmt im Blitter ein soge¬ nannter Barrel-Shifter. Er benötigt für einen Verschiebevorgang keine zusätzliche Zeit, egal, um wie viele Bits verschoben werden soll. Eine zusätzliche Verschiebung der Daten schränkt die Geschwindigkeit des Blitters also in keiner Weise ein! Beispiel für eine Verschiebung der Daten um 3 Bit: Vorher: Datenwort 1 Datenwort 2 Datenwort 3 00011111 10011100 00010101 01111111 11100001 11100101 Nachher: Daten wort 1 xxxOOOll 11110011 Datenwort 2 Datenwort 3 10000010 10101111 11111100 00111100 Die drei xxx-Bits hängen von dem vorangegangenen Datenwort ab, aus dem sie hinausgeschoben wurden. Maskierung Es ist möglich, daß der Blitter eine Grafik aus dem Bildschirmspeicher herauskopieren soll, deren Ränder nicht auf Wortgrenzen liegen. Die Daten, die sich links vom dem Rand der Grafik, aber noch innerhalb des ersten Datenworts befinden, sollen nicht mitkopiert werden. Um dies zu ermöglichen, hat der Blitter die Fähigkeit, das erste und letzte Datenwort einer Zeile mit einer Maske zu behandeln. Dies bedeutet, daß man wählen kann, welche Bits dieser Wörter übernommen werden. Dadurch kann man unerwünschte Daten an Rändern der Zeile löschen. Die Maskierungsmöglichkeit existiert nur für Quelle A. Zwei Register enthalten die Masken für die beiden Ränder. Nur wenn ein Bit im Maskenregister gesetzt ist, wird es bei der Blitter-Operation über¬ nommen. Alle übrigen werden gelöscht. $044 BLTAFWM Blitter Source A First Word Mask Maske für das erste Datenwort in der Zeile. Die Hardware des Amiga 199 $046 BLTALWM Blitter Source A Last Word Mask Maske für das letzte Datenwort in der Zeile. Die Bits 0-15 enthalten die entsprechenden Masken-Bits. Beispiel ("1" steht für ein gesetztes Bit, für ein gelöschtes); Grafikdaten in der Bit-Plane: Spalte 1 Spalte 2 Spalte 3 11111111 1111111111111111 111111 . . 1111 11 . ...1111 1111 . 1111 _ 11 . . 11 1111 .... ....111 11111....1111111 _ 11 . . 1 11111 ... . 11 1111111111111111 _ 11 . . 1 11111 ... . 11 1111111111111111 11111....1111111 1111 . 1111 _ 11 , . 11 1111 .... ....111 111111 . . 1111 11 . ...1111 11111111 1111111111111111 FintWordMaak: LastWordMask: 0000000011111111 1111110000000000 Ergebnis: Spalte 1 Spalte 2 Spalte 3 .11111111 1111111111111111 1. .1111 11.1111 1111. .11 1111.111 11111. .1 11111.11 111111. .1 11111.11 111111. .11 1111.111 11111. .1111 11.1111 1111. .11111111 1111111111111111 1. Durch Ausmaskieren der unerwünschten Bildelemente an den Rändern erhält man die gewünschte Grafik. Achtung! Wenn die Breite des Fensters lediglich ein Wort (BLTSIZE.Width = 1) beträgt, fallen beide Masken zusammen. Sie wirken gemeinsam auf das Eingangswort. Nur die Eingangs-Bits, deren Masken-Bits in beiden Masken gesetzt sind, werden durchgelassen. 200 Amiga intern Die Blitter-Kontrollregister Der Blitter besitzt zwei Kontrollregister, BLTCONO und BLITCONl. In diesen Registern befinden sich verschiedene Kontroll-Bits zur Blitter-Steuerung: BLTCONO $040 Bit-Nr. Name Funktion 15 ASH3 ASHO-3 enthalten den Wert für die Verschiebung 14 ASH2 der Eingangsdaten von Quelle A 13 ASHl ASHO-3 = 0 bewirkt keine Verschiebung 12 ASHO 11 USEA Schaltet den DMA-Kanal für Quelle A ein 10 USEB Schaltet den DMA-Kanal für Quelle B ein 9 USEC Schaltet den DMA-Kanal für Quelle C ein 8 USED Schaltet den DMA-Kanal für Ziel D ein 7 LF7 Wählt Miniterm ABC (Bit-Komb. von ABC: 111) 6 LF6 Wählt Miniterm ABc (Bit-Komb. von ABC: 110) 5 LF5 Wählt Miniterm AbC (Bit-Komb. von ABC: 101) 4 LF4 Wählt Miniterm Abc (Bit-Komb. von ABC: 100) 3 LF3 Wählt Miniterm aBC (Bit-Komb. von ABC: 011) 2 LF2 Wählt Miniterm aBc (Bit-Komb. von ABC: 010) 1 LFl Wählt Miniterm abC (Bit-Komb. von ABC: 001) 0 LFO Wählt Miniterm abc (Bit-Komb. von ABC: 000) BLTCONI $042 Bit-Nr. Name Funktion 15 BSH3 BSHO-3 enthalten den Wert für die Verschiebung 14 BSH2 der Eingangsdaten von Quelle B 13 BSHl BSHO-3 = 0 bewirkt keine Verschiebung 12 BSHO 1-5 Unbelegt 4 EFE Exclusive Fill Enable 3 IFE Inclusive Fill Enable 2 FCI Fill Carry In 1 DESC DESC = 1 schaltet auf Descending Mode 0 LINE LINE = 1 aktiviert den Linienmodus Das LINE-Bit schaltet den Blitter auf das Zeichnen von Linien um. Will man den Blitter zum Kopieren von Daten benutzen, muß LINE = 0 sein. Mit dem DESC-Bit kann zwischen aufsteigenden und absteigenden Adressen ungeschaltet werden. Ist DESC = 0, arbeitet der Blitter im Ascending-, bei DESC = 1 im Descending Mode. Die EFE- und IFE-Bits aktivieren die Betriebsart "Flächen füllen" des Blitters. Sie müssen beide auf 0 sein, wenn der Blitter normal betrie- Die Hardware des Amiga 201 ben werden soll. Das FCI-Bit hat nur im Zusammenhang mit dem Füllmodus eine Funktion. Der Blitter-DMA Die Daten der Quellbereiche A, B und C und die Ausgangsdaten D werden mittels vier DMA-Kanälen aus dem Speicher gelesen bzw. in diesen hineingeschrieben. Diesen Blitter-DMA kann man mit dem BLTEN (Bit 6) des DMACON-Registers für alle Kanäle gemeinsam einschalten. Der Blitter besitzt für seine DMA-Transfers vier Datenre¬ gister: Adr. Name Funktion 000 BLTDDAT Ausgangsdaten D 070 BLTCDAT Datenregister von Quelle C 072 BLTBDAT Datenregister von Quelle B 074 BLTADAT Datenregister von Quelle A Der DMA-Controller liest die benötigten Eingangswerte aus dem Speicher und schreibt sie in die Datenregister. Hat der Blitter die Ein¬ gangsdaten verarbeitet, enthält BLTDDAT das Ergebnis. Der DMA- Controller überträgt den Inhalt von BLTDDAT in das Chip-RAM. Mittels der drei USEx-Bits läßt sich der DMA-Transfer über diese vier Register ein- und ausschalten. USEA = 0 schaltet z.B. den DMA- Kanal für das Datenregister A ab. Der Blitter greift aber weiterhin auf den Wert in BLTADAT zu, d.h. mit jedem neuen Wort der aktiven Quellen wird immer dasselbe Wort aus Quelle A geholt. Aus diesem Grund muß man bei nicht benutzten Quellen USEx = 0 setzen und zusätzlich noch durch eine entsprechende Auswahl der Miniterme je¬ den Einfluß dieser Quelle auf das Ergebnis ausschließen (s. oben). Eine andere Möglichkeit ist die bewußte Ausnutzung der Tatsache, daß nach Abschaltung des DMA-Kanals immer dasselbe Datenwort verwendet wird. Man kann damit z.B. den Speicher mit einem Muster füllen, das man direkt mit dem Prozessor in BLTxDAT hineinge¬ schrieben hat. Außer BLTEN gehören noch drei weitere Bits im DMACON-Register zum Blitter: Bit 10 BLTPRI Dieses Bit wurde schon im Kapitel über Grundlagen erklärt. Ist es 1, hat der Blitter absolute Priorität über den Prozessor. 202 Amiga intern Bit 14 BBUSY (Dieses Bit kann man nur lesen.) BBUSY signalisiert den Zustand des Blitters. Ist es auf 1, führt er ge¬ rade eine Operation aus. Nach dem Setzen des Blitter-Fensters in BLTSIZE beginnt der Blitter mit seinem DMA und setzt BBUSY, bis das letzte Wort des eingestell¬ ten Blitter-Fensters bearbeitet und wieder zurück in den Speicher ge¬ schrieben wurde. Er beendet dann seinen DMA und löscht BBUSY. Gleichzeitig mit dem Löschen von BBUSY wird auch noch das Blitter- Finished-Bit im Interrupt-Request Register gesetzt. Bit 13 BZERO Das BZERO-Bit zeigt an, ob bei einer Blitter-Operation sämtliche Er¬ gebnis-Bits null waren. Mit anderen Worten, BZERO ist gesetzt, wenn bei allen Datenworten keine der gewählten Verknüpfungen eine 1 als Ergebnis lieferte. Mit Hilfe des BZERO-Bits kann man z.B. eine Kol¬ lisionsüberprüfung durchführen. Man setzt die Miniterme so, daß D nur 1 wird, wenn beide Quellen ebenfalls 1 sind. Überschneiden sich die Grafiken in beiden Quellen auch nur an einem Punkt, ist das Er¬ gebnis 1, und BZERO wird gelöscht. Am Ende der Blitter-Operation kann man so feststellen, ob eine Kollision vorlag oder nicht. USED setzt man dabei auf 0, damit die Ausgangsdaten nicht in den Speicher geschrieben werden. Die Verwendung des Blitters zum Füllen von Flächen Was hat man sich unter dem Begriff "Füllen von Flächen" vorzu¬ stellen? Unter einer Fläche versteht der Blitter einen zweidimensiona¬ len Speicherbereich, den er mit Punkten füllen soll. Normalerweise gehört diese Fläche zu einer Grafik oder einer Bit-Plane. Um eine Fläche auffüllen zu können, muß der Blitter ihre Be¬ grenzungen kennen. Eine für den Blitter verständliche Definition der Grenzlinen ist notwendig. Vielseitige Füllfunktionen existieren in den meisten Malprogrammen und auch im AmigaBASIC beim PAINT-Be- fehl. Bei ihnen wird ein Bereich des Bildschirms, ausgehend von ei¬ nem Startpunkt, ausgefüllt, bis das Programm auf eine Begrenzungsli¬ nie stößt. Damit lassen sich völlig frei gewählte Flächen ausmalen, vorausgesetzt, daß sie von einer durchgehenden Linie eingeschlossen sind. Der Blitter ist nicht in der Lage, eine solch komplexe Füllopera¬ tion auszuführen. Er arbeitet immer nur zeilenweise und füllt dabei die freien Plätze zwischen zwei gesetzten Bits aus, die die Grenzen der Die Hardware des Amiga 203 gewünschten Fläche festlegen. Zwei Beispiele zeigen den Ablauf eines Füllvorgangs des Blitters: Fehlerfreie Fülloperation: Vorher: Nachher: . 1 . 1 . . 111111111 ... . 1 ,, . 1 . .. 1111111111111 . . 1 ... 1...1 _ 1 . . 111111 .. .111111 . 1 ... 1...1 _ 1 . . 111111 .. .111111 . 1 „ . 1 ...... . 1111111111111 . . 1 . 1 .. . 111111111 ... . 1 .. . 1 . . 1111111111111 . Fehlerhafte Fülloperation auf Grund falsch gewählter Begrenzungs- Bits: Vorher: Nachher: .111.11111111111111. ... 111...111 . 111111111 ... . 11 ... 111...11 . 11111111111111 ... 11 . . 1 ..., . 1 ... 1 .... 1 . . 111111...111111 . 1 .... . 1 ... 1 .... 1 . . 111111...111111 . 11 .. .. 111 ... 11 .. .... 11111111111111 ... 11 . . 1 . . 1 .... . 111111111 ... .1111111111111.1111111111111111111. Beim ersten Beispiel wurde eine Fläche Blitter-gerecht abgegrenzt und auch korrekt gefüllt. Anders dagegen Nummer 2. Hier hat man eine geschlossene Umgrenzungslinie um die Figur gezeichnet. Versucht man eine solche Grafik mit dem Blitter zu füllen, entsteht ein Chaos. Grund dafür ist der Algorithmus des Blitters. Er ist äußerst einfach. Der Blitter beginnt an der rechten Seite der Zeile. Um zu bestimmen, ob ein Bit gesetzt werden muß, benutzt er das sogenannte FillCarry- Bit (FC). Normalerweise ist es am Anfang gleich 0. Der Blitter prüft jetzt den Wert des Bits rechts außen. Ist es auf Null, bleibt der Wert des FC-Bits erhalten. Es bildet das entsprechende Bit der Ausgangs¬ daten. Der Blitter fährt in dieser Weise mit den Nachbar-Bits fort, bis er auf ein gesetztes Eingangs-Bit stößt. Dadurch wird das FC-Bit auf 1 gesetzt. Da die Ausgangs-Bits dem aktuellen Wert des FC-Bit ent¬ sprechen, sind sie ebenfalls 1. Erst wenn der Blitter erneut ein gesetz¬ tes Eingangs-Bit findet, löscht er das FC-Bit wieder. Auf diese Weise wird immer der Bereich zwischen zwei gesetzten Bits aufgefüllt. Wie 204 Amiga intern man aus dem zweiten Beispiel sehen kann, kommt die Füllogik bei ei¬ ner ungeraden Anzahl von Bytes etwas durcheinander. Den Startwert des FC-Bits bestimmt das FCI-Bit (FillCarryln) im BLTCONl. Ist FCI gelöscht, läuft alles wie oben beschrieben ab. Ist FCI = I, beginnt der Blitter vom Rand her zu füllen, bis er auf das erste gesetzte Eingangs-Bit stößt. Der Füllvorgang läuft umgekehrt ab. Beispiel der Auswirkung des FCI-Bits: Ausgwgsgrafik ...1 . 1 ... .1 . 1 . 1_1.1_1 1 .... 1 . 1....1 .1 . 1 . ...1 . 1 ... FCI=0 FCI=1 _1111111. 1111111 ....111111 ..11111111111... 11111.. .1111 .111111.111111.. 1111... 111....111 .111111.111111.. 1111... 111....111 ..11111111111... 11111.. .1111 ....1111111. 1111111 ....111111 Bei den bisherigen Beispielen blieben die Eingangs-Bits, also die Randbegrenzungen, im aufgefüllten Bild erhalten. Dies ist immer der Fall, wenn man den Füllmodus durch Setzen des ICE-Bits (Inclusive Fill Enable) im BLTCONl-Register aktiviert. Im Gegensatz dazu steht der ECE-Modus (Exclusive Fill Enable), der durch das Setzen des gleichnamigen Bits in BLTCONl eingeschaltet wird. Bei ihm werden die Begrenzungs-Bits am linken Rand einer gefüllten Fläche (also immer, wenn das Fill-Carry-Bit von 1 auf 0 wechselt) nicht in das Ausgangsbild übernommen. Damit werden sämtliche Flächen um einen Punkt schmaler. Nur im ECE-Modus ist es möglich, Flächen mit der Breite von nur einem Bit zu erhalten. Dies ist im ICE-Modus nicht möglich, denn zur Markierung einer Fläche, sei sie auch noch so schmal, sind mindestens zwei Begren¬ zungs-Bits notwendig, die beide im Ausgangsbild erscheinen. Unterschied zwischen dem ICE- und ECE-Modus: Ausgangsbild . 11 .. ....11 . 1 ... 1 . ... 1.1 ... 1 ... 11..1 .. 1..1 1 . 11 ... 11...1 ..1 . . 1 _ 1 . _ 1 . . 1 ... 1 . .. 11 .. ICE ECE .11... .1... ...1 .11111.. ..111 .1111.. ..11 ...111111111. .1111 _1111.111. .111 1111111111111 11111 .1111111.1111 1111 ..11111111111 11111 ...111111111111111 ....111111111 1111. .111111111111. .11111.. .11.. .1111.. .1.. Die Hardware des Amiga 205 Bitweiser Ablauf der verschiedenen Fülloperationen; Eingangsmuster: 11010010 Bit-Nr. Eing.-Bit FC FCI = ICE i 0 ECE FC FCI = ICE ■ 1 ECE - 11010010 FC= FCI FC= FCI 0 0 0 0 0 1 1 1 1 1 1 10 10 0 11 01 2 0 1 110 110 0 011 001 3 0 1 1110 1110 0 0011 0001 4 1 0 11110 01110 1 10011 10001 5 0 0 011110 001110 1 110011 110001 6 1 1 1011110 1001110 0 1110011 0110001 7 1 0 11011110 01001110 1 11110011 10110001 FC=FCI bedeutet, daß das FC-Bit vor Beginn des Füllvorgangs den Wert des FCI-Bit aus BLTCONl annimmt. Wie wird eine Fülloperation des Blitters gestartet? Der Blitter kann diesen Füllvorgang zusätzlich zu einem normalen Kopiervorgang aus¬ führen. Eingeschaltet wird er, indem man je nach gewünschtem Mo¬ dus entweder das ICE- oder ECE-Bit in BLTCONl setzt. Der Blitter bildet wie immer aus den drei Quellen A, B, C über die gewählten Miniterme die Ausgangsdaten D. Ist keiner der beiden Füllmodi aktiv, übernimmt der Blitter diese Daten direkt in sein Ausgangsdatenregister (BLTDDAT, $000), von wo sie per DMA in den Speicher gelangen, wenn USED = 1 ist. Im Füllmodus dagegen werden die Ausgangsdaten D als Eingangsdaten der Füllschaltung verwendet. Das Ergebnis der Fülloperation wird dann ins Ausgangsdatenregister BLTDDAT geschrieben. Um eine Fülloperation auszuführen, sind folgende Schritte notwendig; ■ Die BLTxPT, BLTxMOD und die Miniterme so auswählen, daß die Ausgangsdaten D die korrekten Begrenzungs-Bits der zu füllenden Fläche enthalten. ■ Descending Mode wählen. Der Blitter füllt von rechts nach links, dies funktioniert nur, wenn auch die Wörter mit absteigenden Adressen angesprochen werden. ■ Den gewünschten Füllmodus wählen: ICE oder ECE setzen. FCI nach Wunsch setzen oder löschen. 206 Amiga intern ■ LINE = 0 (Linienmodus aus). ■ BLTSIZE auf die Größe der zu füllenden Grafik setzen. Der Blitter beginnt jetzt mit dem Füllvorgang. Wenn er damit fertig ist, setzt er wie üblich BLTBUSY auf 0. Die Geschwindigkeit des Blitters wird durch Aktivieren des Füllmodus nicht eingeschränkt. Im Maximalfall ist der Blitter in der Lage, Flächen mit einer Ge¬ schwindigkeit von 16 Millionen Punkten pro Sekunde zu füllen. Hauptanwendungsgebiet des Füllmodus ist das Zeichnen ausgefüllter Vielecke. Dazu wird in einem leeren Speicherbereich mittels des Li¬ nienmodus das gewünschte Vieleck eingezeichnet. Der Blitter füllt dies dann mit hoher Geschwindigkeit auf. Die Verwendung des Blitters zum Zeichnen von Linien Der Blitter ist ein extrem vielseitiges Hilfsmittel. Neben den hervorra¬ genden Fähigkeiten beim Kopieren von Daten und Füllen von Flächen besitzt er einen leistungsfähigen Modus zum Zeichnen von Linien. Wie auch die anderen Blitter-Modi ist er außerordentlich schnell: bis zu einer Million Punkte pro Sekunde. Selbst mit einem 68020 Prozessor kann man diese Geschwindigkeit softwaremäßig nur mit Mühe errei¬ chen. Für den eingebauten 68000 ist es völlig unmöglich. Was bedeutet eigentlich "Zeichnen von Linien"? Beim Zeichnen von Linien sollen zwei Punkte durch eine Gerade miteinander verbunden werden. Da die Auflösung einer Computergrafik begrenzt ist, kann nicht immer der optimale Punkt gewählt werden. Die tatsächlichen Punkte liegen immer etwas über oder unter der gedachten Idealgera¬ den. Eine solche Linie ähnelt meist mehr oder weniger einer Treppe. Je höher die Auflösung, desto kleiner sind die Stufen, aber man kann sie niemals vollkommen beseitigen. Beispiel für eine Linie in einer Computergrafik: Die beiden Punkte werden durch eine Gerade verbunden 1 1 . 111 ... . 111 . . 1111 . ...111. 111 . □ o Die Hardware des Amiga 207 Nummer des Oktanten Wert der SUD/SUL/AUL-Bits Abb. 1.6.7.2 208 Amiga intern Endpunkt A= DeltaY Yl <= Y2 XI <= X2 1 1 DeltaX <= DeltaY Yl <= Y2 X1>=X2 2 3 DeltaX <= DeltaY Yl <= Y2 XI >= X2 3 7 DeltaX >= DeltaY Punktkoordinaten Oktant Code Yl >= Y2 XI >= X2 4 S DelttOC >= DeltaY Yl >= Y2 XI >= X2 5 2 DeltaX <= DeltaY Yl >= Y2 XI <= X2 6 0 DeltaX <= DeltaY Yl >= Y2 XI <= X2 7 4 DeltaX >= DeltaY 210 Amiga intern Die Ziffern in der Spalte "Code" entsprechen den eingekreisten Ziffern in Abbildung 1.5.7.2. Der Blitter benötigt je nach dem Oktanten, in dem sich der Endpunkt der Linie befindet, eine Kombination dreier Bits. Sie heißen SUD (Sometimes Up or Down), SUL (Sometimes Up or Left) und AUL (Always Up or Left). "Code" ist die von diesen Bits gebildete 3-Bit-Zahl (SUD = MSB und AUL = LSB). Beim Programmieren der Linie muß man also erst den Oktanten des Endpunktes ermitteln und danach den zugehörigen Code-Wert in den Blitter schreiben. Linien mit Mustern Der Blitter verwendet beim Zeichnen einer Linie eine Maske, um zu bestimmen, ob die Punkte der Linie gesetzt, gelöscht oder mit einem Muster versehen werden sollen. Die Maske hat eine Breite von 16 Bit, daher wiederholt sich das Muster alle 16 Punkte. Den Zusammenhang zwischen Muster und Aussehen der Linie kann man am besten durch ein paar Beispiele verdeutlichen; ("." = 0, "1" “ 1, A = Startpunkt und B = Endpunkt) Ausgangsbild: Maske = "1111111111111111": B .111 . 111 .. ...11. ..11. ..11_11 ... 11111 .. ... 11111 .. All....111 ...11B 111 . 11 . .11 .11 11 . Null-Bits in der Maske bewirken, daß Linienpunkte gelöscht werden: Ausgangsbild; Maske = "0000000000000000"; 1111111111..B. 111111111 11111..B. ....1111111 ....11 111111111 11. ....1111111 1111111111.... ....11 111111111 ..111.... ....1111111 1111111111.... ....11 111111... 11111.... ....1111111 1111111111.... ....11 111...111 11111.... ....1111111 1111111111.... ....11 1..111111 11111.... 1111111111.... .11111111 11111.... ..A.1111111 ..A..11 111111111 11111.... Die Hardware des Amiga 211 Kombiniert man Nullen und Einsen in der Maske, bekommt die Linie ein Muster: Maske = "1111111000111000 .A111111. .111 1 . .111111. .111 11 . ..11111 111...111. .1111...B Zeichnen von Begrenzungslinien In dem Kapitel über das Füllen von Flächen mit dem Blitter wurde erklärt, daß die Begrenzungslinien dieser Flächen immer nur ein Pixel breit sein dürfen. Zeichnet man diese Linien mit dem Blitter, kann es Vorkommen, daß mehrere Linienpunkte in derselben horizontalen Zeile liegen. Um dies zu verhindern, läßt sich der Blitter so umschal¬ ten, daß er beim Ziehen von Linien immer nur einen Punkt pro Zeile zeichnet; Normal« Linie: .1111 . 1111 .... . 1111 . _ 1111 . 1111. Linie mit einem Punkt/Zeile: . 1 ... . 1 . . 1 . _ 1 . 1 . Die Definition der Steigung Damit der Blitter weiß, wohin er seine Linie zeichnen soll, benötigt er eine Blitter-gerechte Definition der Liniensteigung. Im einzelnen sind es die Ergebnisse dreier Terme, die alle auf den DeltaX und DeltaY- Werten basieren, wie sie im Abschnitt über die Oktanten erklärt wur¬ den (DeltaY und DeltaX stellen die Breite und Höhe des Rechtecks dar, dessen Diagonale die Linie bildet (siehe Abbildung 1.5.7.3)). Zuerst muß man beide Werte miteinander vergleichen, um den größe¬ ren bzw. kleineren von beiden zu kennen. Nennen wir das kleinere Delta "Kdelta" und das größere "Gdelta". Dann lauten die drei vom Blitter benötigten Ausdrücke folgendermaßen: 212 Amiga intern 1. 2*Kdelta 2. 2*Kdelta - Gdelta 3. 2»Kdelta - 2*GdeUa Außerdem besitzt der Blitter noch ein sogenanntes SIGN-Flag, welches auf 1 gesetzt werden muß, wenn 2*Kdelta < Gdelta ist. Registerfunktionen im Linienmodus Der Blitter verwendet beim Zeichnen von Linien dieselben Register wie beim Kopieren von Daten (er hat ja nicht mehr), ihre Funktion ändert sich allerdings: BLTAPTL In BLTAPTL muß der Wert des Ausdrucks "2*Kdelta- Gdelta” geschrieben werden. BLTCPT Sc BLTDPT Diese beiden Registerpaare (BLTCPTH und BLTCPTL, BLTDPTH und BLTDPTL) müssen mit der Startadresse der Linie initialisiert werden. Das ist die Adresse des Wortes, in dem der Startpunkt der Linie liegt. BLTAMOD In BLTAMOD muß "2*Kdelta-2*Gdelta" stehen. BLTBMOD "2*Kdelta" BLTCMOD Sc BLTDMOD In diese beiden Modulo-Register muß die Breite des ge¬ samten Bildes, in das die Linie soll, geschrieben werden. Dies geschieht wie üblich in Form einer geraden Ansahl Bytes. Bei einer normalen Bit-Plane mit 320 Punkten (40 Bytes) in X-Richtung wäre der Wert für BLTCMOD bsw. BLTDMOD = 40. Die Breite (Width, Bits 0 bis 5) muß fest auf 2 gesetst wer¬ den. Die Höhe (Height, Bits 6 bis 15) enthßlt die Länge der Linie in Punkten. Eine Höhe von 0 entspricht einer Linie mit einer Länge von 1024 Punkten. Die korrekte Linienlänge ist immer genau gleich dem Wert von Gdelta. Das Zeichnen der Linie wird durch Schreiben ins BLTSIZE- Register gestartet. Es sollte daher immer als letztes Register gesetzt werden. Dieses Register muß mit $8000 initialisiert werden. BLTBDAT enthält die Maske, mit der die Linie gezeichnet wird. In dieses Maskenregister kommt $FFFF. BLTSIZE BLTADAT BLTBDAT BLTAFWM BLTCONO Bit-Nr. Name Funktion 15 START3 Der 4-Bit-Wert STARTO-3 enthält die Position des Startpunkts 14 START2 der Linie innerhalb des Worts 13 STARTl an der Startadresse der Linie (BLTCPT/BLTDPT) 12 STARTO Normalerweise sind dies die vier unteren Bits der X-Koordinate des Startpunkts. 11 USEA = 1 Diese Kombination der USEx-Bits ist im 10 USEB = 0 Linienmodus notwendig. 9 USEC = 1 8 USED = 1 Die Hardware des Amiga 213 7 LF7 Die LFx’’BiU müseen mit $CA initialisiert bis 0 LFO werden (D = aC + AB) BLTCONl Bit-Nr. Name Funktion 15 Texture3 Dies ist der Wert für die Verschiebung der 14 Texture2 Maske. Normalerweise setst man TextureO-3 13 Texturei = StartO-3. Das Muster im Maskenregister BLTBDAT 12 TextureO beginnt dann mit dem ersten Punkt der Linie. 11-7 = 0 Unbenutzt, immer auf 0 setzen 6 SIGN Wenn 2*Kdelta Y2 und DeltaX > DeltaY. Dies ergibt den Ok¬ tanten Nr. 7 und einen Wert für den SUD/SUL/AUL-Code von 4. 2. Schritt: Adresse des Startpunkts Diese errechnet sich wie folgt; Anfangsadresse der Bit-Plane + (Anzahl der Zeilen-Y1-1) * Bytes pro Zeile + 2*(X1/16) Die Nachkommastellen der Division werden abgeschnitten. Nach dem Einsetzen der Zahlen; $40000 + (200-185-1)»40 + 2 = $40232 Dieser Wert kommt in BLTCPT und BLTDPT. Die Anzahl der Bytes pro Zeile wird außerdem noch in die BLTCMOD- und BLTDMOD- Register geschrieben. 214 Amiga intern 3. Schritt: Anfangspunkt der Linie in STARTO-3 Nötige Rechnung: XI AND $F. in zahlen: STARTO-3 = 20 AND $F = 4 4. Schritt: Werte für BLTAPTL, BLTAMOD und BLTBMOD DeltaY < DeltaX, d.h. Kdelta = DeltaY und Gdelta = DeltaX. BLTAPTL = 2*ICdelta-Gdelta = 2*150-190 = 110 BLTAMOO = 2*Kdelta-2*Gdelta = 2*150-2*190 = -80 BLTBHOD = 2*Kdelta = 300 2*Kdelta>Gdelta Daher ist SIGN = 0. 5. Schritt: Länge der Linie für BLTSIZE Länge = Gdelta = DeltaX = 190. Der Wert des BLTSIZE-Registers ergibt sich aus der üblichen Formel; Länge*64 + Breite. "Breite" muß beim Ziehen von Linien immer auf 2 gesetzt werden. BLTSIZE = DeltaX*64-F2 =12162 oder $2F82. 6. Schritt: Zusammenfassung der Werte für beide BLTCONx-Register In BLTCONO muß der START-Wert an die richtige Position gebracht werden, dazu kommt $CA für die LFx-Bits und 1011 für USEx. In unserem Beispiel ergibt dies zusammen SABCA. BLTCONl enthält den Code für den Oktanten und die Kontroll-Bits. Unsere Linie soll ganz normal gezeichnet werden, also ist SING = 0. LINE muß natürlich auf 1. SIGN wurde schon ausgerechnet und ist in unserem Beispiel ebenfalls 0. Zusammen macht das $0011. In der Assembler-Sprache könnte die Initialisierung der Register wie folgt aussehen: LEA $DFF000,A5 MOVE.L #$40232, BLTCPTH(A5) MOVE.L #$40232, BLTDPTH(A5) MOVE.W #40, BLTCM0D(A5} HOVE.U #40, BLTDH0D(A5) MOVE.U #110, BLTAPTL(A5) HOVE.U #-80, BLTAH00(A5) HOVE.U #300, BLTBH0D(A5} HOVE.U #$ABCA, BLTCONO(A5} HOVE.U #$11, BLTC0N1(A5) ;Basisadresse der Custom-Chips nach A5 ;Startadresse nach BLTCPT ;und nach BLTDPT ;Breite der Bit-Plane nach BLTCHCn ;und nach BLTDHOO Die Hardware des Amiga 215 HOVE.U #12162, BLTSIZE(A5) ;Jetzt beginnt der Blitter mit ;dem Zeinchen der Linie Andere Zeichenmodi Bis jetzt wurde als Wert für die LFx-Bits immer $CA angegeben. Dies hat zur Folge, daß die Punkte der Linie in Abhängigkeit von der Maske gesetzt oder gelöscht werden, während die übrigen Punkte er¬ halten bleiben. Aber es sind auch andere sinnvolle LFx-Kombinationen denkbar. Um dies zu verstehen, muß man wissen, wie die LFx-Bits im Linienmodus interpretiert werden: Der Blitter kann den Speicher immer nur wortweise adressieren. Im Linienmodus gelangen die Eingangsdaten über den Quellkanal C in den Blitter. Dazu kommt die Maske im B-Register. Welcher Punkt in¬ nerhalb des gelesenen Worts der Linienpunkt ist, bestimmt das A-Re¬ gister. Es enthält ja immer genau ein gesetztes Bit, das vom Blitter an die richtige Position geschoben wird. Der normale LFx-Wert von $CA bewirkt, daß alle Bits, bei denen das A-Bit gleich 0 ist, direkt von Quelle C übernommen werden. Ist A dagegen gleich 1, wird als Ziel- Bit das entsprechende Masken-Bit verwendet. Weiß man, wie die LFx-Bits verwendet werden, kann man auch an¬ dere Zeichenmodi anwählen. $4A bewirkt beispielsweise, daß alle Li¬ nienpunkte invertiert werden. Die Blitter-DMA-Zyklen Wie schon im Kapitel Grundlagen besprochen, belegt der Blitter nur die geraden Buszyklen. Da er dabei Priorität über den 68000 hat, ist es interessant zu wissen, wie viele Zyklen dem Prozessor noch bleiben. Dies hängt von der Anzahl der aktiven Blitter-DMA-Kanäle (A, B, C und D) ab. Folgende Tabelle zeigt den Ablauf einer Blitter-Operation für alle fünfzehn möglichen Kombination aktiver und inaktiver Blit- ter-DMA-Kanäle. Die Buchstaben A, B, C und D stehen dabei für den entsprechenden DMA-Kanal. Die Ziffer "1" dahinter steht für das erste Wort der Blitter-Operation, "2" für alle mittleren und "3" für das letzte Datenwort. Ein Strich "—" bedeutet, daß dieser Buszyklus nicht vom Blitter belegt ist. 216 Amiga intern Belegung der geraden Buszyklen durch den Blitter: Aktive DWA-Kanäle Belegung der geraden Buszyklen Keine D DO -- Dl -- D2 •• C CO -- CI •• C2 -- C D CO .. .. CI DO -- C2 Dl -- D2 -- -- •• B BO -- Bl -- -- B2. -- B D BO Bl DO -- B2 Dl -- D2 -- -- •• B C BO CO -- Bl CI -- B2 C2 . -- B C D BO CO -- •- Bl CI DO -- B2 C2 Dl -- D2 A AO -- Al -- A2 -■ A D AO -- AI DO A2 Dl -- D2 . -- A C AO CO AI CI A2 C2. A C D AO CO -- AI CI DO A2 C2 Dl -- D2 -- -- A B AO BO -- AI Bl -- A2 B2 . A B D AO BO -- AI Bl DO A2 B2 Dl -- D2 -- -- A B C AO BO CO AI Bl CI A2 B2 C3 . -- A B C D AO BO CO -- AI Bl C1 DO A2 B2 B3 Dl D2 Anmerkungen: Obige Tabelle ist nur gültig, wenn folgende Bedingungen erfüllt sind: 1. Der Blitter wird nicht durch Copper- oder Bit-Plane-DMA-Zu- griffe gestört. 2. Der Blitter läuft im Normalmodus (Weder werden Linien ge¬ zeichnet, noch werden Flächen gefüllt). 3. Das BLITPRI-Bit im DMACON-Register ist gesetzt, und der Blitter hat absolute Priorität über den 68000. Erklärungen: Wie man sieht, kommt es vor, daß die Ausgangsdaten DO erst ins RAM gelangen, nachdem schon die Al, Bl und CI Daten gelesen wurden. Dies kommt von dem sogenannten "Pipelining" innerhalb des Blitters. Pipelining bedeutet, daß die Verarbeitung der Daten innerhalb des Blitters in mehreren Stufen abläuft, die voneinander unabhängig arbeiten. Jede Stufe ist mit dem Ausgang der vorangehenden und dem Eingang der nachfolgenden verbunden. Die erste Stufe bekommt die Eingangsdaten (z.B. AO, BO und CO), verarbeitet sie und gibt sie an die zweite weiter. Während sie in dieser Stufe weiterverarbeitet wer¬ den, gelangen schon die nächsten Eingangsdaten in die Eingangsstufe (Al, Bl und CI). Wenn die ersten Daten dann zur Ausgangsstufe ge¬ langen (DO), hat der Blitter schon längst die nächsten Daten gelesen. Es befinden sich während einer Blitter-Operation zu jedem Zeitpunkt Die Hardware des Amiga 217 immer zwei Datenpaare in unterschiedlichen Verarbeitungsstufen im Blitter. Die Tabelle ermöglicht auch die Berechnung der Ablaufdauer einer Blitter-Operation. In jeder Mikrosekunde hat der Blitter zwei Buszy¬ klen zur Verfügung. Soll z.B. ein Bereich von 64 KByte (32768 Worte) von A nach D kopiert werden, benötigt der Blitter 2*32768 Zyklen. Wenn derselbe Bereich aber noch mit Quelle C kombiniert werden soll, ergeben sich 3*32768 Zyklen, da für jedes Wort noch ein Daten¬ wort aus Quelle C gelesen werden muß. Die Tabelle zeigt auch, daß der Blitter nicht in der Lage ist, jeden Buszyklus zu nutzen, wenn nur ein Blitter-DMA-Kanal aktiv ist. Beispielprogramme Programm 1: Linien mit dem Blitter Dieses Programm stellt eine universelle Routine zum Zeichnen von Li¬ nien mit dem Blitter zur Verfügung. Sie zeigt, wie man die notwendi¬ gen Werte berechnen kann. Ansonsten ist das Programm einfach; Im Vorprogramm wird der Speicher angefordert und die Copper-List aufgebaut. Unbekannt dürfte nur die OwnBlitter-Routine sein. Wie ihr Name schon sagt, kann man mit ihr den Blitter für sich beanspruchen. Entsprechend steht daher am Ende des Programms das Gegenstück dazu, der DisownBlitter-Aufruf, mit dem der Blitter wieder unter die Kontrolle des Betriebssystems gestellt wird. Das Programm verwendet nur eine einzige Hires-Bit-Plane, mit den Standardmaßen 640 auf 256 Punkte. In der Hauptschleife zeichnet das Programm Linien, die immer von einer Bildschirmkante durch den Bildmittelpunkt zur gegenüberliegenden Seite gehen. Immer, wenn ein Bildschirm auf diese Weise gefüllt wurde, verschiebt das Programm die Maske, mit der die Linien gezeichnet werden, und beginnt wieder von vorne. Anmerkung: Die Koordinatenangaben in dem Programm gehen von ei¬ nem Punkt 0,0 in der oberen, linken Bildecke aus, sind also keine mathematischen Koordinaten, wie sie in den vorangegangenen Erklä¬ rungen benutzt wurden. Praktisch hat das die Auswirkung, daß in Vergleichen der Y-Werte das Größer/Kleiner-Zeichen umgedreht werden muß. 218 Amiga intern ;*** Linien mit dem Blitter ;Custoin-Chip-Register INTENA » $9A DMACON = $96 DHACONR = $2 COLOROO = $180 VHPOSR = $6 ;Copper-Register COP1LC = $80 COP2LC = $84 C0PJHP1 = $88 C0PJHP2 = $8a ;Bit-Plane-Register BPLCONO - $100 BPLCON1 = $102 BPLCON2 = $104 BPLIPTH = $0E0 BPL1PTL = $0E2 BPL1H00 = $108 BPL2M00 » $10A DIWSTRT = $08E DIUSTOP = $090 DDFSTRT = $092 DDFSTOP = $094 ;Blitter-Register BLTCONO = $40 BLTCONI » $42 BLTCPTH = $48 BLTCPTL 3 $4a BLTBPTH 3 $4c BLTBPTL 3 $4e BLTAPTH 3 $50 BLTAPTL 3 $52 BLTDPTH 3 $54 BLTDPTL 3 $56 BLTCHOO 3 $60 BLTBHCO 3 $62 BLTAMOD 3 $64 BLTDHOO 3 $66 BLTSIZE 3 $58 BLTCOAT 3 $70 BLTBDAT 3 $72 BLTAOAT 3 $74 BLTAFWM 3 $44 BLTALUM 3 $46 ;Interrupt-Enable-RegiSter (schreiben) ;DMA-KontroUregister (schreiben) ;DMA-KontroUregister (lesen) ;Farbpalettenregister 0 ;Strahlposition (lesen) .-Adresse der 1. Copper-List ;Adresse der 2. Copper-List ;Sprung nach Copper-List 1 .-Sprung nach Copper-List 2 Bit-Plane-KontrolIregister 0 1 (Scroll-Werte) 2 (SpriteoPlayfield Priorität) Zeiger auf 1. Bit-Plane Modulo-Uert für ungerade Bit-Planes Hodulo-Wert für gerade Bit-Planes Start des Bildschirmfensters Ende des Bildschirmfensters :Bit-Plane DMA Start :Bit-Plane DMA Stop ;Blitter-KontrolIregister 0 (ShiftA.Usex.LFx) ;Blitter-Kontrollregister 1 (ShiftB.ver. Bits) ;Zeiger auf Quelle C ;Zeiger auf Quelle B ;Zeiger auf Quelle A .-Zeiger auf Zieldaten 0 ;Modulo-Wert für Quelle C ;Hoduto-Wert für Quelle B ;Hodulo-Wert für Quelle A ;Hodulo-Uert für Ziel D .-Höhe und Breite des Blitter-Fensters ;Dstenregister Quelle C ;Dstenregister Quelle B ;Dstenregister Quelle A .-Maske für das erste Datenwort von Quelle A ;Haske für das erste Datenwort von Quelle B ;CIA-A Portregister A (Maustaste) CIAAPRA 3 $bfe001 Die Hardware des Amiga 219 ;Exec Library Base Offsets ;LibNa(ne,Version/a1,dO ;ByteSize,Requirements/dO.dl ;MeflnryBlock,ByteSize/al,d0 (-Graphics Library Base Offsets OwnBlitter = -30-426 DisounBlitter = -30-432 ;graphics base StartList = 38 OpenLibrary = -30-522 Forbid = -30-102 Permi t = -30-108 AllocMem = -30-168 FreeMem = -30-180 (-Sonstige Label ;Größe der Bit-Plane: 80 Bytes auf 256 Zeilen (-Die Copper-List enthält 3 Befehle ;Chip-RAN anfordern ;Chip-RAH vorher löschen ;*** Vorprogramm *** Start: Execbase = 4 Planesize = 80*256 Planewidth = 80 CLsize = 3*4 Chip = 2 Clear = Chipt-SIOOCO ;Speicher für die Bit-Plane anfordern move.l Execbase(a6 move.l #Planesize(dO move.l #clear(d1 jsr AllocHem(a6) move.l dO(Planeadr beq En^ ;Speicherbedarf der Plane ;Speicher anfordern (-Fehlerl -> Ende (-Speicher für Copper-List anfordern moveq )IIClsize(d0 moveq #chip(d1 jsr AllocMem(a6) move.l dO(CLadr beq FreePlane /Fehler! -> FreePlane ;Copper-List erstellen move.l dO(aO move.l Planeadr(dO move.w #bpl1pth((a0)+ swap dO move.w dO((aO)+ move.w #bpl1ptl((a0)+ swap dO move.w dO((aO)+ /Adresse der Copper-List nach aO /Adresse der Bit-Plane /Ersten Copper-Befehl ins RAM /Hi-Uort der Bitplaneadresse ins RAM /Zweiten Befehl ins RAM /Lo-Wort der Bit-Plane-Adresse ins RAM 220 Amiga intern move.l #Sfffffffc,(aO) ;Blitter anfordern move.l #GRname,a1 clr.l dO jsr 0penLibrary(a6} move.l a6,-(sp} move.l d0,a6 move.l a6,-(sp} jsr 0unBlitter(a6} ;*** Hauptprograoin *** ;DHA und Task-Suitching sperren move.l 4(sp),a6 jsr forbid(a6) lea SdffOOO.aS move.u #S03e0,dnacon(a5) ;Copper initialisieren move.l CLadr,copilc(a5) clr.H copjmpKaS) ;Farben festlegen move.w #$0000,color00ve.l Planeadr,aO move.u lllPlaneuidth,a1 move.u #255,a3 nK>ve.u #639,a4 move.u #S0303,d7 Loop: rol.u #2,d7 ;Konstante Parameter für DrauLine ;ln die entsprechenden Register ;MaBe der Bit-Plane in Register ;Startmuster ;Muster verschieben Die Hardware des Amiga 221 move.u d7,a2 ;Huster in Register für DrawLine clr.w d6 .‘Schleifenvariable löschen SchleifeX: clr.w dl ;Y1 = 0 move.w a3,d3 ;Y2 = 255 move.w d6,d0 ;X1 = Schleifenvariable move.w a4,d2 sub.w d6,d2 ;X2 = ö39-Schleifenvariable bsr DrawLine ;Linie zeichnen addq.w #4,d6 ;Schleifenvariable erhöhen cmp.w a4,d6 ;Test ob schon gröBer als 639 ble.s SchleifeX ;Uenn nein, mit der Schleife weitermachen clr.w d6 ;Schleifenvariable löschen SchleifeY: move.w a4,d0 ;X1 = 639 clr.w d2 ;X2 = 0 move.w d6,d1 ;Y1 = Schleifenvariable move.w a3,d3 sub.w d6,d3 ;Y2 = 255-Schleifenvariable bsr DrawLine ;Linie zeichnen addq.w #2,d6 ;Schleifenvariable erhöhen cmp.w a3,d6 .-Test ob schon gröBer als 255 ble.s SchleifeY .-wem nein, mit der Schleife weitermachen btst #6,ciaapra .-Maustaste gedrückt? bne Loop .-Nein •> weitermachen ;*** Nachprogramm *** ;Uarten, bis Blitter fertig Ist Walt: btst #14,dnaconr(aS) bne Uait ;Alte Copper-Llst wieder aktivieren move.l (sp)+,a6 ;GraphicsBase vom Stack holen move.l StartL1st(a6),cop1lc(a5) clr.w copjmpl(aS) ;Startup-Copper-List aktivieren move.w #$8020,dbiacon(a5) jsr DisownBl1tter(a6) .-Blitter frei geben move.l (sp)+,a6 ;ExecBase vom Stack jsr Permit(a6) ;Task-Switching wieder erlauben .-Speicher für Copper-List wieder freigeben move.l CLadr.al ;Parameter für FreeMem setzen moveq #CLsize,dO jsr FreeMem(a6) .-Speicher freigeben 222 Amiga intern .-Speicher für Bit-Plane wieder freigeben FreePlane: move.l’ Planeadr.al move.l iVPlanesize.dO jsr FreeHem(a6) Ende: clr.l dO rts .-Variablen CLadr: dc.l 0 Planeadr: dc.l 0 ;Progrannt verlassen ;Adresse der Copper-List ;Adresse der Bit-Plane .-Konstanten GRname: dc.b "graphics.library".0 even ;••• DrawLine Routine *** ;DrauLine zeichnet eine Linie mit dem Blitter. ;Folgende Parameter werden benötigt: ;d0 = XI X-Koordinate des Startpunkts .-dl = Y1 Y-Koordinate des Startpunkts ;d2 = X2 X-Koordinate des Endpunkts ;d3 = Y2 Y-Koordinate des End^kts ;a0 muss auf das erste Wort der Bit-Plane zeigen .-a1 enthält die Breite der Bit-Plane in Bytes ;a2 wird direkt in das Maskenregister geschrieben .-d4 bis d6 werden als Arbeitsregister verwendet DrawLine: .-Berechnung der Anfangsadresse der move.l a1.d4 mulu d1.d4 moveq #-$10.dS and.w dO.dS Isr.w #3.d5 add.w dS.dA add.l a0.d4 ;d4 enthält jetzt die Startadresse .-Oktant und Deltas errechnen Linie ;Breite in Arbeitsregister ;Y1 • Bytes pro Zeile ;Vorzeichenlos: $f0 ;Untere vier Bits von XI ausmaskieren ;Rest durch 8 teilen ;Y1 * Bytes pro Zeile + Xl/8 ;Plus Anfangsadresse der Bit-Plane der Linie clr.l dS sub.w dl.cB roxl.b #1.d5 tst.w d3 bge.s y2gy1 neg.w d3 yZgyl: sub.w d0.d2 roxl.b «l.dS tst.w d2 ;Arbeitsregister löschen ;Y2-Y1 DeltaY nach D3 ;Vorzeichen von DeltaY in dS schieben ;N-Flag restaurieren ;Wenn DeltaY positiv, nach y2gy1 ;DeltaY invertieren (wird dadurch positiv) ;X2-X1 DeltaX nach D2 ;Vorzeichen von DeltaX in dS schieben ;N-Flag restaurieren Die Hardware des Amiga 223 bge.s x2gx1 ;Uenn DeltaX positiv, nach x2gx1 neg.u x2gx1: d2 ;DeltaX invertieren (uird dadurch positiv) move.u d3,d1 ;DeltaY nach dl sub.u d2,d1 ;DeltaY-DeltaX bge.s dygdx ;Uenn DeltaY > DeltaX, nach dygdx exg d2,d3 ;Kleineres Delta nach d2 dygdx: roxl.b #1,dS ;d5 enthält das Ergebnis der 3 Vergleiche move.b Okttabelle(pc,dS),dS ;Zugehörigen Oktanten holen add.u d2.d2 ;Kleines Delta * 2 .•Test, ob der Blitter die letzte Operation schon beendet hat UBlit: btst #14,dmaconr(aS) ;BBUSY-Bit testen bne.s UBlit ;Uarten, bis es gleich 0 ist move.u d2,bltbnK)d(aS) ;2* kleines Delta nach BLTBMOO sub.u d3,d2 ;2* kleines Delta - grosses Delta bge.s signnl .-Wenn 2*Kdelta > Gdelta nach signnl or.b #S40.d5 ;Sign-Flag setzen sigml: : move.u d2,bltaptl(aS) ;2*Kdelta-Gdelta in BLTAPTL sub.u d3,d2 ;2* kleines Delta - 2* grosses Delta move.u d2,bltamod(aS) ;Nach BLTAHOO ;Restliche Register initialisieren move.u «SSOOO.bltadatCaS) move.u a2,bltbdat(a5) ;Ma8ke aus a2 in BLTBDAT move.u #tffff,bltafum(a5) and.u «$000f,d0 ;Untere 4 Bits von XI ror.u #4,d0 ;An STARTO-3 schieben or.u «$0bca,d0 ;USEx und LFx setzen move.u dO,bltconO(aS) move.u dS,bltcon1(aS) ;Oktsnt in Blitter move.l d4,bltcpth(a5} ;Startadresse der Linie nach move.l d4,bltdpth(aS) ;BLTCPT und BLTDPT move.u a1.bltcmodCaS) ;Breite der Bit-Plane in die move.u a1,bltdmod(a5) ;beiden Hodulo-Register ;BLTSIZE initialisieren und Blitter starten Isl.u Ill6,d3 ;Länge * 64 addq.u #2,d3 ;plus (Uidth = 2) move.u d3,bltsize(a5} rts ;OktantentabeUe mit LINE =1: ;Die Oktantentabelle enthält für jeden Oktanten den zuge- ;hörigen Code-Wert, der schon an die richtige Position ge ;schoben ist. AuBerdem ist noch das LINE-Bit gesetzt. Okttabelle: dc.b 0 »A+l ;y1dy = Okt7 dc.b 2 *4*1 ;y1x2, dxx2, dx>dy = Okt4 224 Amiga intern dc.b 1 *4+1 ;y1>y2, x1y2, x1dy = OktO dc.b 3 *4+1 ;y1>y2, x1>x2, dxy2, x1>x2, dx>dy = 0kt3 Programm 2: Flächen füllen mit dem Blitter Dieses Programm ähnelt sehr dem ersten Programm. Es zeigt, wie man durch Zeichnen von Begrenzungslinien und Füllen mit dem Blitter ausgemalte Vielecke erzeugen kann. Da es größtenteils mit dem ersten Programm identisch ist, haben wir hier lediglich die Teile abgedruckt, die in Programm 1 ausgetauscht werden müssen, um Programm 2 zu erhalten. Dies ist einmal der Abschnitt von dem Kommentar "Linien zeichnen" bis zum Kommentar "*** Nachprogramm ***". Dieser Bereich muß gegen den nachfolgenden Abschnitt mit der Bezeichnung "Teil 1" aus¬ getauscht werden. Außerdem muß die alte Oktantentabelle am Ende des Programms durch die neue nach der Überschrift "Teil 2" ersetzt werden. Die neue Oktantentabelle ist notwendig, da der Blitter zum Füllen der Flächen ja Begrenzungslinien mit nur einem Punkt pro Zeile benötigt. In der neuen Oktantentabelle ist daher zusätzlich zum LINE-Bit auch noch das SING-Bit gesetzt. Das mit "Teil 1" bezeichnete Programm zieht zwei Linien und läßt den dazwischenliegenden Bereich vom Blitter auffüllen. Danach wartet es auf die Maustaste. ;*** Flächen füllen mit dem Blitter *** Teil 1: ;Ausgefülltes Dreieck Zeichen ;Startwerte festlegen move.l Planeadr.aO .-Konstante Parameter für move.w (»Planewidth.al ;Die LineDraw-Rout ine setzen move.w #$ffff,a2 .-Maske auf SFFFF ■> kein Muster ;* Begrenzungslinien zeichnen * .-Linie von 320.10 nach 600.230 move.w #320.dO move.w #10.dl move.w #600.d2 move.w #230.d3 Die Hardware des Amiga 225 bsr.L drawline ;Ltnie zeichnen ;Linie von 319,10 nach 40,230 move.u #319,d0 move.w #10,dl move.w #40,d2 move.w #230,d3 bsr.L drawline ;Linie zeichnen ;* Fläche auffüllen * ;Uarten, bis Blitter die letzte Linie gezeichnet hat Ul ine: btst #14,dnaconr(a5) ;BBUSY teste bne.S Uline add.l #Planesize-2,aO move.w #$09f0,bltco^(a5) move.w #$000a,bltcon1(a5) move.w #$ffff,bltafwm(a5) move.w #$ffff,bltalwm(a5) move.l a0,bltapth(a5) move.l a0,bltdpth(a5) move.w #0,bltamod(a5) move.w #0,bltdmod(a5) move.w #*ff*64+40,bltsize weitertnachen Ende von Teil 1. Teil 2: ;Oktantentabelle mit SING =1 und LINE =1: Okttabelle: dc.b 0 *4+3 ;y1dy = 0kt7 dc.b 2 *4+3 ;y1x2, dxx2, dx>dy = Okt4 dc.b 1 *4+3 ;y1>y2, x1y2, x1dy = OktO dc.b 3 *4+3 ;y1>y2, x1>x2, dxy2, x1>x2, dx>dy = Okt3 ;Adresse des letzten Uorts ;USEA und D, LFx: D = A ;Inclusive Fill plus Descending ;First- und Lastword-Mask setzen ;Adresse des letzten Uorts der Bit- ;Plane in die Adreßzeiger-Register ;Kein Modulo ;Blitter starten 226 Amiga intern 1.5.8 Die Tonausgabe Grundiagen eiektronischer Musik Wenn wir etwas hören, egal ob Musik, Geräusche oder Sprache, ge¬ schieht dies in Form von Schwingungen der Luft, den Schallwellen, die unser Ohr erreichen. Ein normales Musikinstrument erzeugt diese Schwingungen entweder direkt, wie z.B. eine Flöte, bei ihr wird die Luft durch Hineinblasen in Schwingungen versetzt, oder indirekt, in¬ dem erst ein Teil des Instruments den Ton erzeugt und ihn dann an die Luft weitergibt. Dies geschieht beispielsweise bei allen Saitenin¬ strumenten. Ein elektronisches Instrument erzeugt in seinen Schaltkreisen elektri¬ sche Schwingungen, die in ihrem Verlauf dem gewünschten Ton ent¬ sprechen. Hörbar werden sie erst, wenn sie mittels eines Lautsprechers in Schallschwingungen umgewandelt werden. Beim Amiga verwendet man dazu meistens den im Monitor eingebauten Lautsprecher. Leider ist dieser aufgrund seiner Größe und Qualität nicht in der Lage, die elektrischen Schwingungen in identische Schallwellen umzusetzen. Auf deutsch gesagt Er klingt schlecht. Man sollte daher den Amiga unbe¬ dingt an eine gute Verstärkeranlage anschließen, um in den vollen Ge¬ nuß seiner musikalischen Fähigkeiten zu kommen. Doch welche Para¬ meter bestimmen den Ton, der aus dem Computer kommt? Frequenz Als erstes ist da die Frequenz eines Tons. Sie bestimmt, ob er hoch oder tief klingt. Physikalisch gesehen ist die Frequenz die Anzahl der Schwingungen in der Sekunde, gemessen wird sie in Hertz (Hz). 1 Schwingung pro Sekunde ist 1 Hz, folgerichtig entspricht ein Kilohertz dann 1000 Hertz. Das menschliche Ohr kann Töne zwischen 16 und 16000 Hertz wahrnehmen. Wer etwas von Musik versteht, weiß, daß das eingestrichene "a" eine Frequenz von 440 Hz hat. Der Zusammen¬ hang zwischen Frequenz und Tonhöhe ist wie folgt: Mit jeder Oktave verdoppelt sich die Frequenz. Das zweigestrichene "a" hat demzufolge eine Frequenz von 880 Hz, während das kleine "a" (eine Oktave unter dem eingestrichenen "a") mit einer Frequenz von 220 Hz erklingt. Die Frequenz eines Tones muß nicht unbedingt konstant sein. Sie kann z.B. periodisch einige Hz um die eigentliche Tonhöhe schwanken. Dieser Effekt heißt Vibrato. Die Hardware des Amiga 227 Lautstärke Der zweite Parameter eines Tones ist seine Lautstärke. Unter der Lautstärke versteht man, vereinfacht gesagt, die Amplitude einer Schwingung. Gemessen wird die Lautstärke eines Tones in Dezibel (dB). Der Bereich menschlichen Hörens reicht von 1 dB bis ca. 120 dB. Etwa 10 dB bewirken eine Verdoppelung der Lautstärke. Die Lautstärke des Schalls bezeichnet man auch als Schalldruck. Die Lautstärke kann durch viele Parameter beeinflußt werden. Am einfachsten natürlich durch den Lautstärkeregler am Monitor bzw. Verstärker. Dieser macht nichts anderes, als die Amplitude der elek¬ trischen Schwingung zu verändern. Aber auch der Abstand zwischen Zuhörer und Lautsprecher hat eine Auswirkung auf die Lautstärke. Je weiter man sich vom Lautsprecher entfernt, desto leiser wird der Ton. Aber auch die Raumeinrichtung, offene oder geschlossene Türen usw., all dies kann sich auf die Amplitude der Schallwellen auswirken. Aus diesem Grund ist die absolute Lautstärke auch nicht so wichtig. Ent¬ scheidend ist vielmehr die relative Lautstärke der Töne untereinander, z.B. ob der nachfolgende Ton lauter oder leiser als sein Vorgänger ist. Zwischen der Lautstärke eines Tones und seiner Frequenz besteht ein Zusammenhang. Der Grund dafür ist die Empfindlichkeit des men¬ schlichen Ohres. Hohe und tiefe Töne werden leiser empfunden als die mittleren, auch wenn sie physikalisch denselben Schalldruck in Dezibel haben. Dieser mittlere Tonhöhenbereich reicht ganz grob von etwa 1000 bis 3000 Hz. Innerhalb dieses Frequenzbereichs befinden sich die Schwingungen der menschlichen Sprache, was vielleicht der Grund für die dort höhere Empfindlichkeit ist. Auch die Lautstärke eines Tones kann sich innerhalb eines gewissen Rahmens periodisch verändern, diesen Effekt nennt man Tremolo. Dazu kommt noch der Lautstärkeverlauf vom Beginn bis zum Ende des Tones. Ein Ton kann laut beginnen und dann langsam ausklingen. Er kann aber auch laut beginnen, dann auf ein bestimmtes Maß absin¬ ken und zum Schluß abrupt abbrechen. Oder er beginnt leise und stei¬ gert dann langsam seine Lautstärke. Es gibt hier fast unendlich viele Kombinationsmöglichkeiten. 228 Amiga intern Klangfarbe typiscKe Wellenfonmen Sinuts Rechteck n y Dr'eieck Sägezahri Abb. 1.5.8.1 Der dritte und letzte Parameter eines Tones ist etwas komplizierter. Es handelt sich dabei um die Klangfarbe. Sie spielt eine wichtige Rolle. Die Hardware des Amiga 229 Es gibt hunderte verschiedener Instrumente, die alle einen Ton mit gleicher Frequenz und Lautstärke spielen können, aber trotzdem klingt jedes davon anders. Der Grund dafür liegt in der Form der Schwin¬ gung. Die Abbildung 1.5.8.1 zeigt vier häufig vorkommende Wellen¬ formen. Warum klingen sie unterschiedlich? Jede Wellenform, egal wie sie auch aussieht, besteht immer aus einem Gemisch von Sinusschwingungen, die untereinander in einem festen Freuquenzverhältnis stehen. Bei einem Rechteck z.B. hat der erste Teilton die Grundfrequenz des Tons, der zweite hat die dreifache Fre¬ quenz, aber nur noch ein Drittel der Amplitude. Der dritte Teilton hat die fünffache Frequenz und ein Fünftel der Amplitude usw. 230 Amiga intern Ztj^ammensetzxing ver'sctiiedener' Wellenfor'men aus Sinusschwing'ungen Schon 3 Sinuskui'ven ergeben ein brauchbares Rechteck oder einen Sägezahn sin 2x . sin 3 h y = sin H <- - 2 - - 3 - Abb. 1.5.8.2 Abbildung 1.5.8.2 zeigt dies sowohl für eine Rechteckschwingung als auch für einen Sägezahn. Der Einfachheit halber sind jeweils nur die ersten drei Teiltöne angegeben. Die Hardware des Amiga 231 Wie gesagt, setzen sich alle periodischen Wellenformen aus Sinus¬ schwingungen zusammen. Man nennt sie die Obertonreihe eines Tons. Die reine Sinusschwingung besteht logischerweise nur aus dem 1. Teilton. Eine Rechteckschwingung enthält unendlich viele Obertöne. Die Anzahl der Obertöne und ihr Frequenz- und Amplitudenverhältnis untereinander bestimmen allein über die Klangfarbe eines Tones. Die Obertonreihe ist deshalb so wichtig, weil das menschliche Ohr nur auf Sinusschwingungen reagiert. Ein Ton, dessen Wellenform vom reinen Sinus abweicht, wird schon im Ohr, also noch vor dem eigentlichen Hören, in seine Teiltöne zerlegt. Man sollte diese Tatsachen während den folgenden Erläuterungen im Hinterkopf behalten. Geräusche Außer Tönen gibt es auch noch Geräusche. Während man einen Ton sehr genau definieren und auch elektronisch erzeugen kann, ist dies bei den Geräuschen sehr viel schwieriger. Sie besitzen weder eine be¬ stimmte Frequenz noch einen definierten Lautstärkeverlauf und auch keine einheitliche Wellenform. Sie stellen eine willkürliche Kombina¬ tion von Schall-Ereignissen dar. Die Grundlage vieler Geräusche ist daher das Rauschen, denn Rauschen ist eine Mischung unendlich vieler Schwingungen, deren Frequenzen und Phasenlagen in keinem bestimmten Verhältnis zueinander stehen. Der Wind rauscht beispiels¬ weise, weil jedes der abermillionen Luftmoleküle beim Zusammenstoß untereinander oder mit den Gegenständen auf dem Erdboden in Eigenschwingungen versetzt wird, die alle zusammen ein undefinier¬ bares Klanggemisch bilden, eben das typische Windrauschen. 232 Amiga intern Die Tonerzeugung beim Amiga Digitalisierung einer Wellenform Das Hauptkriterium für die Beurteilung der akustischen Fähigkeiten eines Computers ist seine Vielseitigkeit. Optimal wäre es, wenn sich alle drei Parameter eines Tons, Frequenz, Lautstärke und Klangfarbe, völlig frei einstellen ließen. Beim Amiga hat man versucht, sich diesem Ziel möglichst weit zu nä¬ hern. Um nicht an vorgegebene Wellenformen gebunden zu sein, wird das digitale Äquivalent der gewünschten Wellenform im Speicher ab¬ gelegt und dann mittels eines Digital/Analog-Wandlers in die ent¬ sprechende elektrische Schwingung umgewandelt. Anders ausgedrückt. Die Hardware des Amiga 233 die Schwingung wird digitalisiert und im Computer gespeichert. Bei der Ausgabe werden die digitalisierten Daten wieder analogisiert und an den Verstärker ausgegeben. ln der Abbildung 1.5.8.1 sieht man unterschiedliche Wellenformen. Wenn diese in eine für den Computer verständliche Form umgewan¬ delt werden sollen, muß ihr Verlauf durch Zahlen dargestellt werden. Dazu teilt man eine Schwingung der gewünschten Wellenform in eine gerade Anzahl gleich großer Abschnitte ein. Man beginnt damit mög¬ lichst beim Nulldurchgang der Kurve. In jedem dieser Abschnitte überträgt man den zugehörigen Y-Wert in den Speicher. Man hat da¬ nach eine Zahlenfolge, deren Elemente quasi Momentaufnahmen der Schwingung zu bestimmten Zeitpunkten darstellen. Der englische Be¬ griff für diese digitalisierten Werte lautet "sample”, was auf deutsch soviel wie "Probe" heißt. Bei der Ausgabe wandelt der Amiga die Zahlenwerte aus dem Speicher wieder in die entsprechenden Ausgangsspannungen um. Da die Schwingung bei der Digitalisierung aber nur in eine begrenzte Anzahl Samples zerlegt wurde, kann die Ausgangskurve auch nur mit dieser Anzahl rekonstruiert werden. Dadurch entsteht der treppenförmige Verlauf dieser Schwingung, den man in Abbildung 1.5.8.3 sehen kann. Die Qualität der so reproduzierten Klänge im Vergleich zu ihren Ur¬ sprungsschwingungen ist im wesentlichen von zwei Größen abhängig: Die eine ist die Auflösung der digitalisierten Signale. Damit ist der Wertebereich eines Samples gemeint. Beim Amiga beträgt er acht Bit, reicht also von -128 bis +127. Jeder Eingangswert kann einen von 256 Werten im Speicher annehmen. Da die Auflösung des analogen Ein¬ gangssignals theoretisch unendlich, die der einzelnen Samples aber be¬ grenzt ist, entstehen hier Fehler. Man nennt sie Quantisierungs- oder Rundungsfehler. Denn wenn der Eingangswert irgendwo zwischen zwei Zahlen liegt, also nicht genau einer der 256 Digitalisierungsstufen entspricht, wird er auf- oder abgerundet. Der maximal mögliche Quantisierungsfehler beträgt 1/256 des digitalisierten Wertes (man sagt auch: der Fehler beträgt 1 LSB). Mit dem Quantisierungsfehler ist das sog. Quantisierungsrauschen ver¬ bunden. Wie der Name schon sagt, äußert es sich als Rauschen, das mit der Größe des Quantisierungsfehlers zunimmt. 234 Amiga intern Ein Wertebereich von acht Bit erlaubt schon eine sehr gute Re¬ produktion der Ursprungsschwingung. Für Hifi-Qualität benötigt man aber eine höhere Auflösung. Ein CD-Plattenspieler arbeitet z.B. mit 16 Bit. Der zweite Parameter für die Qualität digitalisierter Klänge ist die so¬ genannte Sampling-Rate. Damit ist die Anzahl der Samples pro Se¬ kunde gemeint. Eine höhere Anzahl Samples bringt natürlich auch eine bessere Wiedergabe mit sich. Die Sampling-Rate läßt sich beim Amiga innerhalb gewisser Grenzen frei einstellen. Vorher muß man sich aber überlegen, wie viele Samples man pro digitalisierter Schwingung ver¬ wenden will. In unserem Beispiel in Abbildung 1.5.8.3 sind es 16 Werte. Zwischen der daraus resultierenden treppenförmigen Sinus¬ schwingung und einem normalen Sinussignal kann man kaum mehr einen hörbaren Unterschied feststellen. Die Ausgabe der digitalisierten Töne Hat man die gewünschte Wellenform in die entsprechenden Zahlen umgewandelt und in den Speicher geschrieben, will man sie natürlich zum Erklingen bringen. Der Amiga besitzt vier Tonkanäle, die alle nach dem gleichen Prinzip arbeiten; Eine digitalisierte Schwingung wird per DMA aus dem Speicher gele¬ sen und mittels eines Digital/Analog-Wandlers ausgegeben. Dieser Vorgang wiederholt sich kontinuierlich, so daß aus der einzelnen Schwingung ein dauerhafter Ton wird. Die Kanäle 0 und 3 werden zum linken, 1 und 2 zum rechten Stereokanal zusammengefaßt. Jeder Audiokanal besitzt einen zugehörigen DMA-Kanal. Da der DMA-Zugriff beim Amiga immer wortweise abläuft, faßt man zwei Samples zu einem Datenwort zusammen. Aus diesem Grund benötigt man immer eine gerade Anzahl Samples. Die obere Hälfte des Worts (Bits 8-15) wird immer vor der unteren (Bits 0-7) ausgegeben. Die Datenliste für unsere digitalisierte Sinusschwingung sieht dann im Speicher wie folgt aus, wobei "Start" die Anfangsadresse der Liste im Chip-RAM ist: Start: dc.b 0,49 dc.b 90,117 dc.b 127,117 dc.b 90,49 dc.b 0,-49 dc.b -90,-117 1. Datenwort, Sairples 1 und 2 2. Datenwort, Sairples 3 und 4 3. Datenwort, Sairples 5 und 6 4. Datenwort, Sairples 7 und 8 5. Datenwort, Sairples 9 und 10 '6. Datenwort, Sairples 11 und 12 Die Hardware des Amiga 235 dc.b -127,-117 ;7. Datenuort, Samples 13 utxl 14 dc.b -90,-49 ;8. Datenuort, Sairples 15 und 16 End: Der Digital-/Analog-Wandler benötigt die Samples als vorzei¬ chenbehaftete 8-Bit-Zahlen. Wie in der gesamten Digitaltechnik üblich, müssen sie in Form eines Zweierkomplements angegeben werden. Diese Umrechnung erledigt für uns der Assembler, so daß man die negativen Werte direkt in die Datenliste schreiben kann. Jetzt muß man einen der vier Audiokanäle wählen, über den man den Ton ausgeben will. Danach wird der zugehörige Audio-DMA-Kanal initialisiert. Fünf Register pro Kanal legen die Betriebsparameter fest. Die ersten beiden bilden ein Adreßregisterpaar, wie man es schon von anderen DMA-Kanälen kennt. Sie heißen AUDxLCH und AUDxLCL, oder gemeinsam AUDxLC, wobei x der Nummer des DMA-Kanals entspricht: Rtg. Name_Funktion $0A0 AUDOLCH $0AJ AUDOLCL lOBO AUDILCH $0B2 AUDILCL $0C0 AUD2LCH $0C2 AUD2LCL lODO AUD3LCH $0D2 AUDILCL Zeiger auf die Audiodaten Bits 16-18 von Kanal 0 Bits 0-15 Zeiger auf die Audiodaten Bits 16-18 von Kanal 1 Bits 0-15 Zeiger auf die Audiodaten Bits 16-18 von Kanal 2 Bits 0-15 Zeiger auf die Audiodaten Bits 16-18 von Kanal 3 Bits 0-15 Die Initialisierung dieser Adreßzeiger kann wie üblich mit einem MOVE.L-Befehl erfolgen: LEA SDFFOOO, AS ;Basisadresse der Custom-Chips nach AS MOVE.L #start, AU00LCH(A5) /"Start" in AUDOLC schreiben Als nächstes muß man dem DMA-Controller die Länge der di¬ gitalisierten Schwingung mitteilen, d.h. aus wie vielen Samples sie be¬ steht. Die entsprechenden Register sind die AUDxLEN-Register: Reg. Name_Funktion $0A4 AUDOLEN $0B4 AUDILEN $0C4 AUD2LEN $0D4 AUD3LEN Ansahl der Audiodatenworte von Kanal 0 Ansahl der Audiodatenworte von Kanal 1 Ansahl der Audiodatenworte von Kanal 2 Ansahl der Audiodatenworte von Kanal 3 Die Länge wird aber nicht in Bytes, sondern in Worten angegeben. Daher muß die Anzahl der Bytes noch durch 2 geteilt werden, bevor man sie in das AUDxLEN-Register schreibt. 236 Amiga intern Die Initialisierung des AUDxLEN-Registers kann mit folgendem MOVE-Befehl erfolgen. Um das Abzählen der Worte zu sparen, wur¬ den zwei Labels definiert: "Start" ist die Anfangsadresse der Datenliste, "End" die Endadresse+1 (siehe Beispieldatenliste oben). In A5 befindet sich die Basisadresse der Custom-Chips (SDFFOOO): HOVE.U #(End-Start)/2, AUD0LEN(A5) Jetzt kommt die Lautstärke des Tons. Beim Amiga kann man die Lautstärke für jeden Kanal getrennt einstellen. Dabei stehen 65 Stufen zur Verfügung. Von 0 (unhörbar) bis zu 64 (volle Lautstärke) reicht der Einstellbereich. Die entsprechenden Register heißen AUDxVOL: Reg. Name_Funktion ♦0A8 AUDOVOL *0B8 AUDIVOL ♦0C8 AUD2VOL »0D8 AUD3VOL Lautstärke des Audiokanals 0 Lautstärke des Audiokanals 1 Lautstärke des Audiokanals 2 Lautstärke des Audiokanals 3 Setzen wir unseren Tonkanal auf halbe Lautstärke: NOVE.W 1*32, AUDOVOKAS) Der letzte Parameter, der noch fehlt, ist die Sampling-Rate. Sie be¬ stimmt, in welchen Zeitabständen jeweils ein Daten-Byte, also ein Sample, an den Digital/Analog-Wandler ausgegeben wird. Die Sampling-Rate bestimmt damit die Frequenz des Tons. Wie ganz am Anfang erwähnt wurde, ist die Frequenz gleich der Anzahl der Schwingungen pro Sekunde. Eine Schwingung besteht aus einer frei wählbaren Anzahl Samples. In unserem Beispiel sind es 16. Wenn die Sampling-Rate die Anzahl der gelesenen Samples pro Sekunde dar¬ stellt, entspricht die Frequenz des Tons der Sampling-Rate dividiert durch die Anzahl der Samples pro Schwingung: Tonfrequenä = Sampiing-Rate Samplea pro Schwingung Leider läßt sich die Sampling-Rate nicht direkt in Hertz angeben. Der DMA-Controller will statt dessen die Anzahl der Buszyklen wissen, die zwischen der Ausgabe zweier Samples liegen sollen. Ein Buszyklus dauert genau 279.365 Nanosekunden (Milliardstel Sekunden), in Se¬ kunden ausgedrückt: 2.79365 * 10'^ oder ausgeschrieben: 0.000000279365 Sekunden. Die Hardware des Amiga 237 Um von der Sampling-Rate zu der Anzahl der Buszyklen zu kommen, bildet man den Kehrwert der Sampling-Rate. Dadurch erhält man die Dauer eines Samples. Dividiert man diesen Wert dann durch die Dauer eines Buszyklus in Sekunden erhält man die Anzahl der Buszyklen zwischen zwei Samples, die sogenannte Sampleperiod: Sampleperiod = 1 Sampling-Rate * 2.79365 * 10-7 Nehmen wir einmal an, wir wollten unseren Beispielton mit einer Fre¬ quenz von 440 Hz spielen, also ein eingestrichenes "a". Die Sampling- Rate errechnet sich dann wie folgt: Sampling-Rate = Frequens * Samples pro Schwingung Sampling-Rate = 440 Hb * 16 = 7040 Hb Die notwendige Sampleperiod ist auch schnell da, wenn man die Werte in die zugehörige Gleichung einsetzt: 1 Sampleperiod = -= 508.4583 7040 • 2.79366 * 10’’ Da man als Sampleperiod nur ganzzahlige Werte angeben kann, rundet man das Ergebnis auf 508 ab. Damit ist die Ausgangsfrequenz nicht mehr exakt 440 Hz, die Abweichung bleibt jedoch minimal: 0.4 Hz. Die Sampleperiod kann theoretisch Werte zwischen 0 und 65535 an¬ nehmen. Der tatsächliche Bereich ist aber nach oben hin begrenzt. Wie man der Abbildung 1.5.3.2 im Kapitel "Grundlagen" entnehmen kann, ' besitzt jeder Audiokanal einen DMA-Slot pro Rasterzeile, d.h. in je¬ der Rasterzeile können ein Datenwort bzw. zwei Samples aus dem Speicher gelesen werden. Daher ist der kleinstmögliche Wert für die Sampleperiod gleich 124. Die Samplefrequenz beträgt dann 28867 Hz. Verkürzt man die Sampleperiod über 124 hinaus, kann es Vorkommen, daß ein Datenwort zweimal ausgegeben wird, da das nächste nicht mehr rechtzeitig gelesen werden konnte. Die Sampleperiod-Register heißen AUDxPER: Reg. Name _ Funktion _ S0A6 AUDOPER Sampleperiod für Audiokanal 0 S0B6 AUDIPER Sampleperiod für Audiokanal 1 S0C6 AUD2PER Sampleperiod für Audiokanal 2 $0D6 AUD3PER Sampleperiod für Audiokanal 3 238 Amiga intern MOVE.W #508,AUD0PER(A5) befördert die von uns errechnete Sampling-Rate in das AUDOPER-Register. Damit sind alle Register des Audiokanals Nummer 0 mit den richtigen Werten für unseren Ton versehen. Um ihn zum Erklingen zu bringen, muß man noch den DMA-Zugriff für den Audio-DMA-Kanal 0 einschalten. Vier Bit im DMACON-Register sind für die Audio-DMA-Kanäle zuständig: DMACON-Bit-Nr. Name Audio-DMA-Kanal Nr. s AUDSEN S 3 AUD2EN 2 1 AUDIEN 1 0 AUDOEN 0 Soll der Audio-DMA für Kanal 0 eingeschaltet werden, muß das AUDOEN-Bit auf 1 gesetzt werden. Sicherheitshalber sollte man auch das DMAEN-Bit mitsetzen (siehe Kapitel "Grundlagen"): HOVE.W )ltt8201, DMAC0N(A5) ;AUD0EN und DMAEN setzen Jetzt beginnt der DMA-Controller, die Audiodaten aus dem Speicher zu holen und über den Digital/Analog-Wandler auszugeben. Der Ton ist im Lautsprecher zu hören. Will man ihn wieder abschalten, setzt man einfach AUDOEN = 0. Immer wenn man AUDxEN auf 1 setzt, beginnt der DMA bei der Adresse in AUDxLC. Es gibt allerdings eine Ausnahme: War der DMA-Kanal an, also AUDxEN = 1, und man setzt das Bit nur kurz auf 0 und dann wieder auf 1, ohne daß der DMA-Kanal in der Zwi¬ schenzeit ein neues Datenwort gelesen hätte, fährt der DMA-Control- 1er an der alten Adresse fort. Audio-Interrupts Der Audio-DMA beginnt immer mit dem Daten-Byte an der Adresse in AUDxLC. Sind soviel Datenworte aus dem Speicher geholt und aus¬ gegeben worden, wie in AUDxLEN festgelegt, beginnt der DMA wie¬ der bei der AUDxLC-Adresse. Im Gegensatz zu den Adreßregistern des Blitters oder der Bit-Planes wird der Inhalt des AUDxLC-Regi- sters während des gesamten Audio-DMA nicht verändert. Es existiert vielmehr für jeden Audiokanal noch ein zusätzliches Adreßregister. Bevor der DMA-Controller das erste Daten-Byte aus dem Speicher holt, kopiert er den Wert des AUDxLC-Registers in dieses interne Adreßregister. Auch das AUDxLEN-Register überträgt er in einen internen Zähler. Ist dies geschehen, wird ein Interrupt ausgelöst. Wie man im Kapitel "Interrupts" nachlesen kann, existiert für jeden der Die Hardware des Amiga 239 vier Audiokanäle ein eigenes Interrupt-Bit. Der Prozessor-Interrupt der Ebene 4 ist ausschließlich für diese Bits reserviert. Während der DMA-Controller jetzt Datenwort um Datenwort aus dem Speicher holt, kann der Prozessor AUDxLC und AUDxLEN schon mit neuen Daten versorgen, da beide Register ja intern gespeichert sind. Erst wenn der Zähler, der am Anfang mit dem Wert von AUDxLEN initialisiert wurde, bei 0 ankommt, werden die Daten aus AUDxLC und AUDxLEN erneut gelesen. Der Prozessor hat dadurch genügend Zeit, die Werte der beiden Register, falls notwendig, zu ändern. Da¬ durch ist eine unterbrechungsfreie Tonausgabe möglich. Nach jeder kompletten Schwingung wird also ein Interrupt ausgelöst. Bei hohen Tonfrequenzen treten dadurch sehr häufig Interrupts auf. Man sollte die Interrupt-Enable-Bits (INTEN) der Audio-Interrupts nur setzen, wenn man diese Interrupts auch wirklich benötigt. Der Prozessor kann sich sonst vor lauter Interrupt-Anforderungen kaum mehr retten. Modulation von Lautstärke und Frequenz Um bestimmte Klangeffekte zu erzeugen, besteht die Möglichkeit, Frequenz und/oder Lautstärke zu modulieren. Dabei arbeitet immer ein DMA-Kanal als Modulator, der die entsprechenden Parameter ei¬ nes anderen Kanals verändert. Dies geschieht auf eine sehr einfache Weise: Der Modulationsoszillator holt wie üblich seine Daten aus dem Speicher. Aber statt sie an den Digital/Analog-Wandler auszugeben, schreibt er sie in das Lautstärke- oder Frequenzregister des Oszillators, den er modulieren soll (AUDxVOL oder AUDxLEN). Er kann auch beide Reigster gleichzeitig beeinflussen. In diesem Fall werden die Datenworte aus seiner Datenliste abwechselnd in das AUDxVOL- oder AUDxLEN-Register geschrieben. Die Datenworte haben das gleiche Format wie ihre Zielregister: Volume: Bits 7-15 Unbenutzt Bits 0-6 Lautstärkewert zwischen 0 und 64 Frequens: Bits 0-15 Sampleperiod Folgende Tabelle zeigt die Verwendung der Datenworte des Mo¬ dulationsoszillators für alle drei möglichen Fälle: 240 Amiga intern Datenwort Nr. Frequenz Oscillator moduliert: Lautstärke Frequenz & Lautstärke 1 Period 1 Volume 1 Volume 1 2 Period 2 Volume 2 Period 1 3 Period 3 Volume 3 Volume 2 4 Period 4 Volume 4 Period 2 Um einen Audiokanal als Modulator zu aktivieren, muß man das/die entsprechenden Bits in dem Audio-Disk-Control-Register (ADKCON) setzen. Jeder Kanal kann nur seinen Nachfolger modulieren, d.h. Ka¬ nal 0 moduliert Kanal 1, 1 moduliert 2 und 2 moduliert 3. Auch Ka¬ nal 3 kann man als Modulator schalten, seine Datenworte werden aber nicht zur Modulation eines anderen Kanals verwendet und gehen ver¬ loren. Benutzt man einen Audio-Kanal als Modulator, wird sein Au- dioausgang abgeschaltet. Das ADKCON-Register enthält, wie sein Name schon sagt, auch Steuer-Bits für den Disk-Controller, sie sind hier nicht aufgeführt und werden in dem entsprechenden Kapitel näher erklärt. ADKCON-Register $09E (schreiben) $010 (lesen) Bit-Nr. Name 15 SET/CLR 14-8 7 USE3PN 6 USEJP3 6 USE1P2 4 USEOPl 3 USE3VN J USE2V3 1 USE1V2 0 USEOVl Funktion _ Bits werden gesetst (SET/CLR=1) oder gelöscht Steuer-Bits des Disk-Controllers Audiokanal 3 moduliert nichts Audiokanal 2 moduliert Period von Kanal 3 Audiokanal 1 moduliert Period von Kanal 2 Audiokanal 0 moduliert Period von Kanal 1 Audiokanal 3 moduliert nichts Audiokanal 2 moduliert Volume v. Kanal 3 Audiokanal 1 moduliert Volume v. Kanal 2 Audiokanal 0 moduliert Volume v. Kanal 1 Um es noch einmal zu wiederholen: Verwendet man einen Kanal zur Modulation, werden lediglich seine Datenworte in die entsprechenden Register des zu modulierenden Kanals geschrieben. Sonst arbeiten beide weiterhin völlig unabhängig voneinander. Die Hardware des Amiga 241 Probleme der digitalen Tonerzeugung des Amiga Digitalisierung mehrerer Schwingungen zur Verbesserung der Qualität hoher Töne S = Sample Abb. 1.6.8.4 In unserem Beispiel hatten wir eine Schwingung mittels 16 Samples definiert. Die maximale Sampling-Rate ist 28867 Hz. Dies ergibt eine Höchstfrequenz von 28867 durch 16 = 1460.4 Hz, Das entspricht etwa einem dreigestrichenen Fis (1480 Hz). Will man höher hinaus, muß man die Anzahl der Samples pro Schwin¬ gung verringern. Definieren wir unseren Sinus mit der halben Anzahl Samples, verdoppelt sich die Grenzfrequenz auf 3020.8 Hz. Acht Da- ten-Bytes sind aber etwas wenig für einen schönen Sinus. Für noch höhere Töne schrumpft die Anzahl der Samples weiter. Bei 6041.6 Hz sind es nur noch vier. Da lassen sich dann kaum mehr unterschiedliche Wellenformen darstellen. 242 Amiga intern Allerdings fällt das beim Hören kaum auf. Denn unserem Ohr geht es in dieser Beziehung genauso. Je höher die Frequenz, desto schwieriger wird es, verschiedene Klänge voneinander zu unterscheiden. Trotzdem kann es die Klangqualität verbessern, wenn man bei hohen Frequenzen mehrere Schwingungen zur Definition der gewünschten Wellenform verwendet, wie es in Abbildung 1.5.8.4 zu sehen ist. Der' Tiefpaßfilter- Fr e qf u enzg ang LsiutstÄnke [ db ] Aliasing— Dist.ort.lon Lsiu tstärke Sanpl i nsrrat te Abb. 1.5.8.5 Die Hardware des Amiga 243 Die Grenzfrequenz der Amiga-Tonausgabe wird aber noch von einem anderen Umstand eingeschränkt. Beim Analogisieren der digitalen Klangdaten entstehen nämlich zwei unerwünschte Störfrequenzen durch Wechselwirkungen zwischen der Sampling-Rate und der ge¬ wünschten Tonfrequenz. Die eine davon ist die Summe und die andere die Differenz aus Sampling-Rate und Tonfrequenz. Dieses Phänomen trägt die Bezeichnung "Aliasing Distortion". Bei einem Ton von beispielsweise 3 kHz und einer 12 kHz Sampling- Rate liegt die Differenz bei 9 und die Summe bei 15 kHz. Um diese Störfrequenzen zu beseitigen, hat man einen sogenannten Tiefpaßfilter zwischen den Ausgängen der Digital/Analog-Wandler und den Audiobuchsen eingebaut. Seine Funktionsweise ist in Abbil¬ dung 1.5.8.5 dargestellt. Alle Frequenzen bis 4 kHz können ungestört passieren. Zwischen 4 und 7 kHz wird das Signal immer mehr abge¬ schwächt, bis oberhalb der 7 kHz keine Frequenzen mehr durchgelas¬ sen werden. Nehmen wir unser Zahlenbeispiel von oben: Der 3 kHz- Ton wird von dem Tiefpaß nicht abgeschwächt, aber sowohl Summen¬ ais auch Differenzfrequenz liegen mit 9 bzw. 15 kHz über der Grenz¬ frequenz des Filters von 7 kHz und können ihn nicht mehr passieren. Damit sind sie auch nicht mehr im Lautsprecher zu hören. Versucht man aber, denselben 3 kHz Ton mit einer Sampling-Rate von 9 kHz auszugeben, ist die Differenzfrequenz 9 kHz - 3 kHz = 6 kHz und wird damit zwar vom Filter abgeschwächt, aber noch durchgelassen. Will man sichergehen, daß die Differenzfrequenz oberhalb der Grenz¬ frequenz des Filters bleibt, muß man sich an folgende Regel halten: Sampling-Rate > höchste Frequenzkomponente + 7 kHz Es genügt nicht, wenn man dafür sorgt, daß die Differenz aus Samplingfrequenz und gewünschter Ausgangsfrequenz größer als 7 kHz ist. Wenn man eine Wellenform verwendet, die sehr viele Ober¬ töne enthält, produziert jeder davon seine eigene Differenzfrequenz mit der Sampling-Rate. Aus diesem Grund muß man in den obigen Ausdruck immer die höchste Frequenzkomponente der verwendeten Wellenform einsetzen. Der Tiefpaßfilter hält nicht nur die Störfrequenzen zurück, er be¬ schränkt leider auch den Frequenzgang des Amiga. Zwar treten in ei¬ nem Musikstück selten Töne mit einer Grundfrequenz zwischen 4 und 7 kHz auf, aber die Obertöne bestimmter Wellenformen liegen schon bei viel niedrigeren Grundfrequenzen innerhalb dieses Bereichs. Be- 244 Amiga intern sonders deutlich wirkt sich das auf eine Rechteckschwingung aus. In Abbildung 1.5.8.2 kann man sehen, daß ein Rechteck aus der Kombi¬ nation mehrerer Sinuswellen entsteht, die zueinander in einem festen Frequenzverhältnis stehen. In der Abbildung setzte sich das Rechteck lediglich aus den drei ersten Teiltönen zusammen. Ein tatsächliches Rechteck besteht dagegen aus unendlich vielen Teiltönen. Werden die hochfrequenten Obertöne durch den Filter abgeschnitten oder be¬ grenzt, entsteht solch ein deformiertes Rechtecksignal wie in Abbil¬ dung 1.5.8.2. Im Extremfall, wenn die Grundfrequenz des Rechtecks der Grenzfrequenz des Filters nahekommt, kann es passieren, daß nur noch der erste Teilton übrigbleibt. Dann ist aus dem ursprünglichen Rechteck ein Sinus geworden. Lautstärkeverlauf eines Tones Der Klang eines Instruments wird außer von der Wellenform auch noch von seinem Lautstärkeverlauf bestimmt. In Punkto Wellenformen ist der Amiga ja fast zu allem fähig. Wie sieht es nun mit der Pro¬ grammierung eines bestimmten Lautstärkeverlaufs aus? Den Lautstärkeverlauf eines Tons kann man in drei Abschnitte auf¬ teilen: den Anschlag, die Haltephase und das Abklingen. Sobald der Ton gespielt wird, beginnt die Anschlagphase. Sie be¬ stimmt, wie schnell sich die Lautstärke von null auf den Haltewert einpendelt. Während der Haltephase erklingt der Ton dann mit dieser Lautstärke. Wenn der Ton beendet wird, geht die Haltephase in die Abklingphase über, in der die Lautstärke dann vom Haltewert wieder auf null zurückgeht. Diesen Verlauf kann man in Form einer Kurve, der sogenannten Hüllkurve, in ein Achsenkreuz eintragen. Wie setzt man eine solche Hüllkurve auf den Amiga um? Grundsätzlich gibt es drei Möglichkeiten: Lautstärkemodulation Man verwendet einen zweiten Tonkanal, um die Lautstärke des Tons zu modulieren. Z.B. Kanal 0 als Modulator von Kanal 1. Kanal 1 läßt man dabei ständig den gewünschten Ton ausgeben, seine Lautstärke setzt man aber auf null. Oie Hardware des Amiga 245 Die gewünschte Hüllkurve wird in zwei Abschnitte eingeteilt: An¬ schlagphase und Abklingphase. Ihr Verlauf wird digitalisiert (funktio¬ niert genauso wie bei den Wellenformen) und in zwei Datenlisten im Speicher abgelegt. Soll der Ton gespielt werden, setzt man Kanal 0 auf die Adresse der Anschlagdaten und startet ihn. Da er die Lautstärke von Kanal 1 moduliert, folgt die Lautstärke des Tons genau der ge¬ wünschten Anschlagphase. Hat die Anschlagphase den Haltewert er¬ reicht, ist die Datenliste von Kanal 0 abgearbeitet. Er erzeugt jetzt einen Interrupt und würde normalerweise die Datenliste noch einmal von vorne beginnen. Daher muß der Prozessor auf den Interrupt rea¬ gieren und mittels des AUDOEN-Bits im DMACON-Register Kanal 0 abschalten. Kanal 1 bleibt damit auf der gewünschten Haltelautstärke. Soll der Ton wieder abgeschaltet werden, setzt man Kanal 0 auf die Abklingdaten und startet ihn erneut. Wieder wartet man auf den In¬ terrupt, der anzeigt, daß die Abklingphase beendet ist, und schaltet Kanal 0 ab. Die Register von Kanal 0 müssen bei diesem Vorgang wie folgt initia¬ lisiert werden: USEOVl Dieses Bit im ADKCON-Register setst man auf 1, damit Kanal 0 die Lautstärke von Kanal 1 moduliert. AUDOLC Setst man zuerst auf die Datenliste der Anschlag* und danach auf die der Abklingphase. AUDOLEN Enthält je nach der Adresse in AUDOLC einmal die Länge der An¬ schlag- und einmal die der Abklingdaten. AUDOVOL Hat keine Funktion, da der Audioausgang von Kanal 0 abgeschaltet ist. AUDOPER Der Inhalt des AUDOPER-Registers bestimmt die Geschwindigkeit, mit der die Daten für die Lautstärke aus dem Speicher geholt werden. Man kann damit die Dauer der Anschlag- bsw. Abklingphase einstellen. Mittels dieser Methode läßt sich die gewünschte Hüllkurve perfekt nachbilden. Leider ist sie aber mit einem großen Nachteil verbunden: Man benötigt zwei Audiokanäle für einen Ton. Will man weiterhin vier verschiedene Tonkanäle benutzen können, muß man die zweite Methode anwenden: Steuerung der Lautstärke mittels des Prozessors Die gewünschte Hüllkurve wird genau wie oben in den Speicher übertragen. Allerdings verändert man die Lautstärke diesmal mit dem Prozessor. Dieser holt in regelmäßigen Zeitabschnitten den aktuellen Lautstärkewert aus dem Speicher und schreibt ihn in das Lautstärkere¬ gister des entsprechenden Tonkanals. Man muß das entsprechende 246 Amiga intern Programm als Interrupt-Routine ablaufen lassen. Dies kann innerhalb des Vertical-Blanking-Interrupts geschehen, oder man verwendet einen der Timer-Interrupts von CIA-B. Der Nachteil dieser Methode ist der Bedarf an Rechenzeit, da die Steuerung der Lautstärke diesmal nicht per DMA geschieht. Da sich dieser Bedarf allerdings in Grenzen hält, ist diese Methode für die meisten Anwendungsfälle am geeignetsten. Einbau der Hüllkurve in die Schwingungsdaten Diese Methode ist vor allem bei kurzen Klängen oder Ge¬ räuschimitationen vorteilhaft. Statt nur eine Schwingung der ge¬ wünschten Wellenform zu digitalisieren, schreibt man den gesamten Ablauf in den Speicher. Dieser kann entweder von einem Programm berechnet werden, oder man verwendet einen Audiodigitalisierer. Da¬ mit kann ein Gräusch mit Mikrofon und Analog/Digital-Wandler hardwaremäßig digitalisiert werden. Solche Geräte werden von ver¬ schiedenen Firmen für den Amiga angeboten. Hat man die Daten dann im Amiga, kann man sie in jeder Tonhöhe bzw. Geschwindigkeit ab¬ spielen. Auf diese Weise kann man auch komplexe Effekte wie Ge¬ lächter oder Schreie täuschend echt mit dem Amiga nachahmen. Auch diese Methode hat ihre Nachteile: Entweder erfordert sie schwierige Berechnungen oder zusätzliche Hardware, um den kom¬ pletten Klang in digitalisierter Form im Speicher abzulegen. Außerdem ist der Speicherplatzbedarf sehr groß. Dauert der Klang z.B. 1 Sekunde bei einer Sampling-Rate von 20 kHz, fressen die Klangdaten 20 KByte Speicher! Tips, Tricks und Sonstiges Klangqualität Der Wertebereich der digitalen Daten reicht von -128 bis 127. Diesen Bereich sollte man möglichst ganz ausnutzen. Es ist am besten, wenn die Amplitude der digitalisierten Schwingung gleich 256 ist. Die Klangqualität kann sonst hörbar abnehmen, da die Größe des Quantisierungsfehlers mit der Verringerung des Wertebereichs zu¬ nimmt und mit ihm auch das Quantisierungsrauschen, das sehr schnell störende Ausmaße erreicht. Die Hardware des Amiga 247 Man sollte es aus diesen Gründen vermeiden, die Amplitude der digi¬ talisierten Schwingung zur Steuerung der Lautstärke zu verwenden. Dafür besitzt jeder K.anal sein AUDxVOL-Register. Verringert man damit die Lautstärke, bleibt das Verhältnis zwischen gewünschtem Klang und Störgeräuschen voll erhalten und damit auch die hohe Klangqualität des Amiga. Störungsfreie Übergänge beim Wechsel der Wellenform Um Tonstörungen wie Knackser oder Lautstärkesprünge beim Wechsel der Wellenform zu vermeiden, muß man folgende Regeln beachten: Jede Schwingung sollte immer von Nulldurchgang zu Nulldurchgang digitalisiert werden, d.h. man beginnt bei einem Schnittpunkt der Schwingung mit der X-Achse, die Daten in den Speicher zu übertra¬ gen. Hält man sich an diese Regel, ist sichergestellt, daß alle Wellen¬ formen im Speicher mit demselben Wert beginnen und enden, nämlich null. Dann können beim Aneinanderreihen unterschiedlicher Schwin¬ gungen keine plötzlichen Pegelsprünge auftreten, die als Knackser zu hören wären. Als zweites muß man darauf achten, daß die Gesamtlautstärke der beiden Schwingungen annähernd gleich ist. Damit ist der sogenannte Effektivwert der Schwingung gemeint. Der Effektivwert einer Schwingung ist gleich der Amplitude eines Rechtecksignals, dessen Fläche unter der Kurve genauso groß wie diejenige der Schwingung ist. Dieser Effektivwert bestimmt die Lautstärke einer Schwingung. Nur beim Rechteck ist er gleich der Amplitude. Wechselt man von einer Wellenform auf eine mit einem höheren Effektivwert, klingt diese lauter als ihre Vorgängerin. Der Effektivwert einer Schwingung läßt sich aus ihren digitalisierten Daten einfach berechnen: Man addiert die Beträge sämtlicher Bytes und dividiert sie an¬ schließend durch die Anzahl der Daten-Bytes. Will man bei allen Wellenformen den vollen 8-Bit-Wertebereich der Digital-/Analog-Wandler ausnützen, können sich ihre Effektivwerte nicht immer entsprechen. Man muß bei einem Wechsel der Wellenform die Lautstärke im AUDxVOL-Register entsprechend anpassen. 248 Amiga intern Spielen von Noten Normalerweise wird ein Musikstück in Form von Noten aufge¬ schrieben. Will man ein solches auf dem Amiga spielen, muß man die Notenwerte in die entsprechende Sampleperiod umwandeln. Um sich umständliche Rechnungen zu ersparen, verwendet man am besten eine Tabelle, die die Sampleperiod-Werte für sämtliche Halbtöne einer Oktave enthält: Tabelle der Sampleperiod-Werte von Musiknoten: Note Frequenz (Hz] Sampleperiod bei AUDxLEN — 16 c 261.7 427 (262.0) # c 277.2 404 (276.9) d 293.7 381 (293.6) d* 311.2 359 (311.6) e 329.7 339 (330.0) f 349.3 320 (349.6) 370.0 302 (370.4) S 392.0 285 (392.5) # g 415.3 269 (415.8) a 440.0 254 (440.4) # a 466.2 240 (466.0) h 493.9 226 (495.0) c 523.3 214 (522.7) Werte in Klammern stellen die tatsächliche Frequenz der entsprechen-:^^ den Samplingperiod dar. Noch eine Anmerkung zur Berechnung obiger Werte. Die Frequenz, eines Halbtons ist immer um den Faktor "zwölfte Wurzel aus 2" höher als die des Vorgängers. 440 (a) * = 466.2 (a*), 466.2 (a^) * 2^^/^*^ = 493.9 (h) usw. Eine Oktave entspricht immer einer Frequenzverdoppelung. Will man jetzt eine Note aus einer Oktave spielen, die nicht in der Tabelle enthalten ist, gibt es zwei Möglichkeiten: 1. Man ändert die Samplingperiod. Für jede Oktave nach oben muß man den Wert halbieren. Eine Oktave tiefer entspricht der dop¬ pelten Samplingperiod. Dies ist zwar einfach, man stößt aber schnell an gewisse Grenzen. Bei einem Datenfeld von 32 Byte (AUDxLEN = 16), wie in unserer Tabelle, ist die kleinstmögli- che Samplingperiod (124) schon mit dem zweigestrichenen "a" erreicht. Man muß also die Datenliste verkleinern. Die Hardware des Amiga 249 In diesem Fall bekommt man aber Probleme im Bereich der tie¬ fen Töne, da die Störfrequenzen der Aliasing Distortion (siehe entsprechenden Abschnitt) hörbar werden. Eine bessere Lösung stellt daher Verfahren Nr. 2 dar: 2. Man erstellt für jede Oktave eine eigene Datenliste. Der Samplingperiod-Wert bleibt dabei für jede Oktave konstant. Er dient nur zur Wahl des Halbtons. Soll ein Ton eine Oktave über demjenigen in der Tabelle liegen, verwendet man eine Datenli¬ ste, die nur noch halb so lang ist. Entsprechend eine doppelt so lange für die nächsttiefere Oktave. Der normale Tonumfang beträgt etwa 8 Oktaven, d.h. man benötigt acht Datenlisten pro Wellenform. Als Ausgleich für den höheren Aufwand erhält man mit diesem Ver¬ fahren unabhängig von der Tonhöhe immer den optimalen Klang. Erzeugen hoher Frequenzen Die minimale Samplingperiod beträgt normalerweise 124. Der Grund dafür ist, das der Audio-DMA innerhalb einer noch kürzeren Samplingperiod nicht mehr in der Lage wäre, die Datenworte recht¬ zeitig zu lesen. Das alte Datenwort wird dann mehrfach ausgegeben. Diesen Effekt kann man sinnvoll nutzen. Da das gelesene Datenwort zwei Samples enthält, kann mit ihm ein Rechtecksignal von hoher Frequenz erzeugt werden. Bei einer Samplingperiod von 1 ergibt sich eine Samplingfrequenz von 3.58 MHz und eine Ausgangsfrequenz von 1.74 MHz! Um dieses hochfrequente Ausgangssignal auch nutzen zu können, muß man es vor dem Tiefpaßfilter abgreifen. Dazu kann man den AUDIN-Eingang (Pin 16) der seriellen Buchse (RS232) ver¬ wenden. Er ist direkt mit dem rechten Audioausgang von Paula ver¬ bunden (siehe Kapitel Schnittstellen). Um solch hohe Frequenzen erzeugen zu können, muß AUDxVOL auf volle Lautstärke gesetzt werden (AUDxVOL = 64). Spielen mehrstimmiger Musik Da der Amiga vier unabhängige Audiokanäle besitzt, ist es problemlos möglich, vier verschiedene Töne gleichzeitig zu erzeugen. Damit kann man alle vierstimmigen Musikstücke direkt spielen. 250 Amiga intern Aber es geht noch mehr. Denn vier Audiokanäle heißt noch lange nicht, daß vier Stimmen das Maximum sind. Es wurde ja schon er¬ wähnt, daß jede Wellenform in Wirklichkeit eine Kombination aus Si¬ nussignalen darstellt. Genauso wie diese Obertöne zusammen die Wel¬ lenform ergeben, kann man durch Kombination mehrerer Wellenfor¬ men einen mehrstimmigen Klang erzeugen. Die Ausgangssignale der Audiokanäle 0 und 3 werden ja auch innerhalb von Paula zu einem Stereokanal zusammengemischt. Dabei werden die Wellenformen bei¬ der Kanäle zu einem einzigen, jetzt zweistimmigen Signal kombiniert. Was mit den Analogsignalen elektronisch möglich ist, läßt sich aber auch mit den digitalen Daten rechnerisch erledigen! Man addiert die digitalen Daten zweier völlig verschiedener Wellenformen und gibt die neuen Daten wie üblich über den Audiokanal aus. Schon hat man zwei Stimmen pro Audiokanal. Auf diese Weise lassen sich theoretisch be¬ liebig viele Stimmen über einen Tonkanal abspielen. Praktisch ist diese Anzahl allerdings von der Rechengeschwindigkeit begrenzt, aber 16 Stimmen sind durchaus machbar! Die Errechnung des Summensignals aus den Teilsignalen ist sehr ein¬ fach. Zu jedem Zeitpunkt werden die aktuellen Werte aller Töne aufaddiert und das Ergebnis durch ihre Anzahl dividiert. Auf ähnliche Weise entsteht auch ein Rechtecksignal, wenn man Sinussignale im richtigen Frequenzverhältnis addiert (Abbildung 1.5.8.2). Audioausgabe ohne DMA Wie bei allen DMA-Kanälen, existieren auch beim Audio-DMA Da¬ tenregister, in die der DMA-Kanal die Daten überträgt und die ebenso vom Prozessor beschrieben werden können: Die Audiodatenregister lUg. Name _ Funktion _ $0AA AUDODAT Diese vier Register enthalten immer das aktuelle $OBA AUDIDAT Audiodatenwort, bestehend aus swei Samples. $0CA AUD2DAT Das Sample im oberen Byte (Bits 8-15) SODA AUD3DAT wird immer suerst ausgegeben. Um die Audiodatenregister mit dem Prozessor beschreiben zu können, schaltet man den DMA mit AUDxEN = 0 ab. Dadurch ändert sich auch die Erzeugung der Audio-Interrupts. Sie treten jetzt immer nach der Ausgabe der beiden Samples im AUDxDAT-Register auf, statt wie früher zum Beginn jeder Audiodatenliste. Die Hardware des Amiga 251 Lädt man nicht rechtzeitig ein neues Datenwort in AUDxDAT, wer¬ den die beiden letzten Samples entgegen dem DMA-Betrieb nicht wie¬ derholt, sondern der Ausgang bleibt auf dem Wert des letzten Daten- Bytes (der unteren Hälfte des Worts in AUDxDAT) stehen. Die direkte Programmierung der Audiodatenregister kostet sehr viel Rechenzeit. Außer in Spezialfällen sollte man lieber den Audio-DMA verwenden. Ein paar Fakten AUDxVOL-Werte in Dezibel (0 dB = volle Lautstärke): AUDxVOL dB AUDxVOL dB AUDxVOL dB AUDxVOL dB 64 0.0 48 -2.6 32 -6.0 16 -12.0 63 -0.1 47 -2.7 31 -6.3 16 -12.6 62 -0.3 46 -2.9 30 -6.6 14 -13.2 61 -0.4 46 -3.1 29 -6.9 13 -13.8 60 -0.6 44 -3.3 28 -7.2 12 -14.6 59 -0.7 43 -3.6 27 -7.6 11 -16.3 58 -0.9 42 -3.7 26 -7.8 10 -16.1 57 -1.0 41 -3.9 26 -8.2 9 -17.0 56 -1.2 40 -4.1 24 -8.5 8 -18.1 55 -1.3 39 -4.3 23 -8.9 7 -19.2 54 -1.6 38 -4.6 22 -9.3 6 -20.6 53 -1.6 37 -4.8 21 -9.7 6 -22.1 52 -1.8 36 -6.0 20 -10.1 4 -24.1 51 -2.0 36 -6.2 19 -10.5 3 -26.6 50 -2.1 34 - 6.6 18 -11.0 2 -30.1 49 -2.3 33 -6.8 17 -11.6 1 -36.1 AUDxVOL = 0 entspricht einem dB-Wert von minus unendlich. Wenn AUDxVOL = 64 ist, dann entspricht ein digitaler Wert von 127 einer Ausgangsspannung von etwa 400 Millivolt, -128 entsprechen -400 Millivolt. Eine Änderung um 1 LSB bewirkt eine Schwankung der Ausgangsspannung von ca. 3 Millivolt. Beispielprogramme Programm I: Erzeugen eines einfachen Sinustons Dieses Programm erzeugt einen Sinuston mit einer Frequenz von 440 Hz. Dabei wird die gleiche Sample-Tabelle wie im Text benutzt. Der größte Teil des Programms dient wieder der Anforderung von Chip- RAM für die Audiodatenliste. 252 Amiga intern Der Ton wird über Kanal 0 ausgegeben, bis die Maustaste gedrückt wird. Danach gibt das Programm den belegten Speicher zurück. ;•** Erzeugen eines einfachen Sinustons •** ;Custom-Chip-RegiSter INTENA = S9A ;Interrupt-Enable-Register (schreiben) DHACON = *96 ;DMA-KontroUregister (schreiben) ;Audio-Register AUDOLC = $A0 AUDOLEN = $AA AUDOPER = SA 6 AUDOVOL = $A 8 (‘Adresse der Audiodatenliste ;Länge der Audiodatenliste ;Sanplingperiode (■Lautstärke ADKCON = S9E (•Steuerregister für Modulation ;CIA-A Portregister A (Maustaste) CIAAPRA = $bfe001 ;Exec Library Base Offsets AllocMem = -30-168 ;ByteSize(Requirenients/d0(d1 FreeMem = -30-180 ;Memoryßlock,ByteSize/a1(d0 ;Sonstige Label Execbase = A Chip = 2 (-Chip-RAM anfordern .**• vorprogramn *** Start: (■Speicher für Audiodatenliste anfordern move.l Execbase (06 moveq #ALsize(dO moveq #chip(d 1 jsr Al locMein(a 6 ) beq Ende ;GröBe der Audiodatenliste (•Speicher anfordern ;Fehler -> Progranm beenden (•Audiodatenliste ins Chip-RAM kopieren move.l d 0 (a 0 move.l #ALstart(a1 moveq #ALsize-1(d1 ;Adresse im Chip-RAM ;Adresse im Programm jSchleifenzähler Loop: move.b (a1)+((a0)+ ;Datenliste ins Chip-RAM dbf dl(Loop ;**• Hauptprogramm ;Audioregister initialisieren Die Hardware des Amiga 253 lea $OFFOOO,a5 move.u #$000f.dnaconCaS) move.l d0,aud0lc(a5) move.u #ALsize/2,aud0len(a5) move.u #32,aud0vol(a5) move.u #508,aud0per(a5) move.u #$00ff,adkcon(a5) ;Audio-DMA einschalten move.u #$8201 ,ckiiacon(a5) ;Auf Maustaste uarten uait: btst #6,ciaapra bne ua i t ;Audio-DMA ausschalten move.u #$0001 ,ckiiacon(a5) *** Nachprogramm *•* move.l d0,a1 moveq #Al.size,dO jsr FreeHem; Auf die Bedeutung der Struktur wollen wir an dieser Stelle noch nicht eingehen. Sie dient lediglich zur Demonstration der Unterschiede zwi¬ schen C und Assembler. Das Initialisieren der Struktur dürfte kein Problem darstellen. Beispielsweise: ln_Pri = 20; Mit dieser Zuweisung wird der Wert 20 als ein 1-Byte-Wert mit Vor¬ zeichen in der Struktur vermerkt. In Assembler besteht eine solche C-Struktur aus einer Tabelle. Die Werte sind in gleicher Reihenfolge, die in der Struktur angegeben ist, in ihrer zugehörigen Länge abgelegt. Für eine solche Tabelle muß ihre 278 Amiga intern Basisadresse bekannt sein, damit man sie entsprechend ansprechen kann. Für dieses Beispiel sieht das dann wie folgt aus: Basis + 0 $00000000 Zeiger auf Nachfolger ln_Succ Basis + 4 $00000000 Zeiger auf Vorgänger ln_Pred Basis + 9 $00 ln_Pri Basis + 10 $00000000 Zeiger auf Name ln_Name Die Nullen stehen für beliebige Werte. Um In_Pri auf 20 zu setzen, wie wir es zuvor in C gemacht haben, muß die Basisadresse der Struktur bekannt sein. Ist dies der Fall, so steht dem Setzen des ln_Pri-Feldes nichts mehr im Wege. LEA.L Basis+9,A0 ;Adresse von ln_Pri holen (bez. Basis) MOVE.B #20,; In Assembler: Basis + 0 »00000000 Basis + 4 $00000000 Basis + 8 $00 Basis + 9 $00 Basis + 10 $00000000 Zeiger auf Nachfolger ln_Succ Zeiger auf Vorgänger ln_Pred ln_Type ln_Pri Zeiger auf Name ln_Name Diese Strukturen kann man in zwei Teile aufteilen. Zum einen besteht sie aus. einem Verkettungsteil (ln_Succ und ln_Pred) und einem Da¬ tenteil (Type, Priority und Name). *ln_ßucc Dies ist ein Zeiger auf die nächste Node (Successor = Nachfolger). *ln_Pred Das ist ein Zeiger auf die vorherige Node (Predecessor = Vorgänger). ln_Type In diesem Byte werden die verschiedenen Typen der Node entspre¬ chend kodiert gespeichert. ln_Pri Hier wird die Priorität der Node vermerkt. Dieses Feld auf einen Wert ungleich null zu setzen, ist nur in manchen Fällen, wie zum Beispiel bei einer Task-Node, sinnvoll. Doch dazu kommen wir später. *ln_Name In diesem Langwort wird ein Zeiger auf einen mit Null abgeschlosse¬ nen String gespeichert. Es ist der Name der Node, der möglichst so 280 Amiga intern gewählt werden sollte, daß man schon anhand des Namens erkennt, um welche Node es sich handelt, was die Fehlersuche vereinfacht. Diese Node-Struktur gibt es auch in einer "abgespeckten" Version. Sie heißt MinNode und sieht wie folgt aus: struct HinNode { struct HinNode *mln_Succ; /* Zeiger auf Nachfolger */ struct HinNode *mln_Pred; /• Zeiger auf Vorgänger */ >; Die Einträge der Struktur gleichen denen der Node-Struktur. In Assembler: Basis + 0 $00000000 Zeiger auf Nachfolger ln_Succ Basis + 4 $00000000 Zeiger auf Vorgänger ln_Pred Initialisieren einer Node Bevor eine Node in eine Liste eingehängt wird, sollte sie ordentlich initialisiert sein. Zu diesem Zweck muß der Typ gesetzt werden. Hier¬ bei hat man eine Auswahl zwischen mehreren standardisierten Typen, die folgend aufgeführt sind. Node-Typ Code NT UNKNOUN 00 nt“task 01 nt“interrupt 02 NT DEVICE 03 NT HSGPORT 04 NT~HESSAGE 05 NT FREEHSG 06 NT~REPLYHSG 07 nt“resource 08 NT LIBRARY 09 NT'VtEHORY 10 nt“softint 11 NT~F0NT 12 NT~PR0CESS 13 NT SEHAPHORE 14 NT~SIGNALSEH 15 NT BOOTNOOE 16 (Kick 1.3) Den Node-Typ anzugeben ist somit sehr leicht. Man sucht sich den zu seiner Node passenden Typ aus der Tabelle und trägt ihn ein. 281 Exec Nehmen wir einmal an, es soll die Node, die sich in einer TaskStruk- tur befindet, initialisiert werden. Um zu verstehen, wie diese Initiali¬ sierung dann aussieht, zeigen wir erst, wie eine TaskStruktur mit den für uns bis jetzt wichtigen Teilen aufgebaut ist. struct Task { struct Node tc_Node; >; Die Initialisierung des Node-Type sieht in C also wie folgt aus: struct Task melntask; /* meintask ist der Name der */ /* zugeuiesenen TaskStruktur */ meintask.tc_Node.ln_Type = NT_TASK; Im Anschluß die gleiche Initialisierung noch in Assembler. LEA.L meintask,AO ;Basisadresse des Tasks nach AO HOVE.L #01,8(A0) ;Type = Task (Wert 01) setzen Nachdem der Typ festgelegt wurde, wird die Priorität der Node im Vergleich zu den anderen Nods angegeben. Sie kann einen Wert zwi¬ schen -128 und +127 annehmen. Ein größerer positiver Wert steht für eine höhere Priorität, somit ist +127 die höchste und -128 die niedrig¬ ste Priorität. Einige Exec-Listen werden nach der Höhe der Priorität der sich in ihr befindlichen Einträge geordnet, wobei der Eintrag mit der höchsten Priorität am Anfang steht. Die meisten Exec benutzen den ln_Pri- Eintrag nicht. Es ist sinnvoll, die Priorität solcher Listeneinträge (Nodes) auf Null zu setzen. Das Setzen der Priorität geschieht wie folgt meintask.tc_Node.in_Pri = 5; Und in Assembler: LEA.L meintask,AO ;Basisadresse des Tasks nach AO (CVE.L #05,9(A0) ;Pri auf 5 setzen Zum Schluß müssen wir nur noch den Namen der Node und somit auch des Tasks angeben. 282 Amiga intern In C: ineintBsk.tc_Node.in_Name = "Beispiel Task"; In Assembler: LEA.L meintask.AO ;Basisadresse des Tasks nach AO LEA.L Name,AI .-Adresse, an der der Name steht, nach AI MOVE.L A1,10{A0) .-Zeiger auf Namen eintragen Name: DC.B "Beispiel Task",0 Der String muß, wie schon gesagt, mit Null abgeschlossen werden. Anhand dieser Beispiele sehen Sie, daß Sie lediglich die Position des entsprechenden Eintrags wissen müssen, um die Struktur aus As¬ sembler initialisieren zu können. Sie brauchen also einen sogenannten "Offset" (zu deutsch Ausgleich), von der Basisadresse ausgehend, um die richtige Position zu erreichen. Dieser Offset ist in unserem letzten Beispiel 10. Das Initialisieren von ln_Succ und ln_Pred wird im nächsten Kapitel besprochen. 2.3 Aufbau von Llaten Was ist eigentlich eine Liste und was steht in ihr? Diese Fragen sollen jetzt beantwortet werden. Eine Liste ist eine Reihe von Node-Struk- turen, die vor- und rückwärts miteinander verkettet sind (doppelt ver¬ kettet). Wie schon gesagt, steht an erster Stelle der Node-Struktur ein Zeiger (ln_Succ) auf die nächste Node. An zweiter Position ist ein Zeiger (ln_Pred) auf die vorausgegangene Node. Um eine verkettete Liste besser verwalten zu können, hat man noch einen Listen-Kopf eingeführt, mit dem sich sofort der Anfang oder das Ende der verketteten Liste finden läßt. Abgesehen von der Infor¬ mation, wo der Anfang und das Ende der Liste zu finden ist, ist in der Struktur auch eingetragen, um welche Art von Einträgen es sich in der Liste handelt. Die List-Struktur hat folgendes Aussehen: Die Zahlen vor den Strukturgliedern in der folgend aufgeführten List- Struktur sind deren Offsets, um so auch mit Assembler Zugriff auf sie zu haben. Die Offsets gehören nicht zur C Struktur, dürfen folglich Exec 283 nicht mit eingegeben werden. Sie dienen nur als Gedankenstütze für Assembler-Programmierer. struct List { 0 struct Node •lh_Head; 4 struct Node •lh_Tail; 8 struct Node *lh_TaiIPred; 12 UBYTE ih_Type; 13 UBYTE lh_pad; >; *lh_Head Zeiger auf den ersten Eintrag (erste Node) der Liste. *lh_Tail Immer auf Null. *lh_TailPred Zeiger auf den letzten gültigen Eintrag in der Liste. lh_Type Gibt an, welcher Sorte von Nodes in der Liste verkettet sind. lh_Type wird genau so gesetzt, wie der Typ der Nodes. Ih _pad Pad-Byte, damit die Struktur an einer geraden Adresse endet. Auch bei der List-Struktur gibt es eine verkleinerte Version; struct HinLIst { struct MinNode *mlh_He8d; struct MinNode *mlh_Tail; struct MinNode *mlh_TaiIPred; *mlh_Head Zeiger auf den ersten Eintrag (erste Node) der Liste. 284 Amiga intern *mlh_Tail Immer auf Null. *mlh_TailPred Zeiger auf den letzten gültigen Eintrag in der Liste. Der Zeiger ln_Succ des letzten Eintrags der Liste zeigt auf lh_Tail (zweiter Eintrag der List-Struktur). lh_Tail ist null (NIL) und zeigt dadurch an, daß die "vermeintlich" gelesene Node (es handelt sich je¬ doch in diesem Fall um die List-Struktur) nicht mehr gültig ist. Der ln_Fred Zeiger der ersten Node zeigt auf Ih_Succ (also auch auf die List-Struktur). Wird nun der "vermeintliche" ln_Pred Zeiger gele¬ sen, so ist dieser null (es handelt sich ja hier um lh_Tail), was wie¬ derum anzeigt, daß der zuvor gelesene Eintrag der Liste der erste dieser war. Beispiel einer verketteten Liste Bild 3.3 Exec 285 Initialisierung einer Liste Nachdem wir uns angesehen haben, wie eine verkettete Liste aussieht, wollen wir sie jetzt erstellen. Zu diesem Zweck muß zuerst eine neue List-Struktur erzeugt werden und als leer gekennzeichnet werden. Das Initialisieren einer Task-Liste sieht in C wie folgt aus: #include "exec/lists.h" main () { struct List liste; liste. lh_Head = (struct Node *)S;liste.lh_Tail; liste.lh_Tail = 0; liste.lh_TaiIPred = (struct Node *)&liste.lh_Head; liste.lh_Type = NT TASK; > In Assembler hätte es dieses Aussehen: LEA.L liste,A0 MOVE.L A0,(A0) ADDO.L #04,(AO) CLR.L #04(A0) MOVE.L A0,8(A0) MOVE.B #01,12(A0) Die soeben erzeugte Liste ist selbstverständlich leer. Das Einfügen von Nodes in die Liste wird zu einem späteren Zeitpunkt besprochen. An dieser Stelle soll jedoch gezeigt werden, wie eine leere Liste auch als solche erkannt wird. Zur Feststellung gibt es zwei unterschiedliche Methoden. Zum einen kann man untersuchen, ob lh_Head auf Null (NIL) zeigt, zum anderen, ob lh_TaiIPred auf den Anfang der Liste (lh_Head) zeigt. Ist das der Fall, so ist die betreffende Liste leer. Die Abfrage sieht in C wie folgt aus: if (liste.lh_TaiIPred == &l1ste) { printf ("liste ist leer"); > oder die andere Möglichkeit: if (liste.lh_Head -> ln_Succ == 0) { printf ("liste ist leer"); > 286 Amiga intern Exec-Routine zur Listenverwaltung Zur Verwaltung von Listen stellt Exec eine Reihe sehr nützlicher Funktionen zur Verfügung, mit denen sich nahezu alle sinnvollen Operationen bequem durchführen lassen. Die erste Funktion ist die Insert()-Funktion. Sie dient zum Einfügen von Nodes in eine Liste mit Angabe der Position, an der die neue Node eingefügt werden soll. Insert Funktion: Insert (Liste.Node,Vorgänger); AO AI A2 Offset: -234 Beschreibung Diese Funktion dient zum Einfügen einer Node in eine Liste. Parameter Liste Zeiger auf die Liste, in die die Node eingefügt werden soll. Node Zeiger auf die Node, die in die Liste eingefügt werden soll. Vorgänger Zeiger auf die Node, nach der die einzufügende Node eingefügt wird. Ist dieser Zeiger auf einen Wert ungleich null gesetzt, so ist der Zei¬ ger, der im Parameter "Liste" gesetzt ist, nicht mehr relevant, denn die angegebene Liste wird nicht nach der als Position angegebenen Node durchsucht (um festzustellen, ob die gewünschte Node überhaupt vor¬ handen ist), sondern setzt den Vorgänger-Parameter als richtig voraus und fügt die Node ein. Steht der Parameter auf null, so wird die Node an erster Position eingefügt. Die zweite Möglichkeit, eine Node an er¬ ster Position einzufügen, besteht darin, "Vorgänger" auf "lh_Head" zu setzen. Ein Einfügen an letzter Position erreicht man durch Setzen des Vorgänger-Parameters auf "lh_TailPred". Für das Einbinden einer Node an erster oder letzter Stelle gibt es jedoch eigene Funktionen. Exec 287 Remove Funktion: Remove (Node) AI Offset: -252 Beschreibung Wie aus dem Namen schon ersichtlich, dient diese Funktion zur Ent¬ fernung von Nodes aus einer Liste. Parameter Node Zeiger auf die Node, die entfernt werden soll. Sollte es sich bei dem Zeiger nicht um einen Zeiger auf eine Node handeln, erkennt Exec dies nicht und "entfernt" trotzdem, wodurch es zum Informationsver¬ lust oder zum "Absturz" des Rechners kommen kann. AddHaad Funktion: AddHeadtliste,Node) AO AI Offset: -240 Beschreibung Die Funktion wird benutzt, um Nodes am den Kopf einer Liste ein¬ zufügen. Parameter Liste Zeiger auf die Liste, in die die Node eingefügt werden soll. Node Zeiger auf die Node, die eingefügt werden soll. 288 Amiga intern RemHead Funktion: RemHead (Liste) AO Offset: -258 Beschreibung Entfernt die erste Node der Liste, die angegeben wird. Parameter Liste Zeiger auf die Liste, aus der die erste Node entfent wird. AddTaif Function: AddTail (Liste,Node) AO AI Offset: -246 Beschreibung Einfügen einer Node als letztes Glied in der Liste. Parameter Liste Zeiger auf die Liste, in die eingefügt werden soll. Node Zeiger auf die Node, die eingefügt werden soll. RemTaii Funktion: RemTaii (Liste) AO Offset: -258 Beschreibung RemTaii entfernt den letzten Eintrag der Liste. Exec 289 Parameter Liste Zeiger auf die Liste, aus der der letzte Eintrag entfernt werden soll. Enqueue Funktion: Enqueue (Liste,Mode) AO A1 Offset: -270 Beschreibung Die Funktion wird verwendet, um Einträge in der Liste nach ihrer Priorität zu ordnen. Wie schon im Kapitel über Nodes gesagt, werden Nodes mit höherer Priorität an den Anfang der Liste gestellt. Sollten mehrere Nodes mit der gleichen Priorität in einer Liste vorhanden sein, so wird die neu eingefügte Node hinter die vorhandenen gestellt. Parameter Liste Zeiger auf die Liste, in die die Node eingetragen werden soll. Node Zeiger auf die Node, die eingefügt werden soll. FiitdNamn Funktion: Eintrag = FindName (Liste,"Name") DO AO A1 Offset: -276 Beschreibung Mit FindName kann man eine angegebene Liste nach einer Node mit entsprechendem Namen suchen. 290 Amiga intern Parameter Liste Zeiger auf die Liste, die durchsucht werden soll. Name Zeiger auf den Namen, nach dem gesucht wird. Dieser String muß mit Null abgeschlossen werden. Beim Aufruf der C-Funktion handelt es sich bei dem Parameter nicht um einen Zeiger auf den Namen, son¬ dern um den String selbst. Rückgabeparameter In "Eintrag" (DO) wird von der Funktion ein Zeiger zurückgegeben, der auf die gefundene Node zeigt. Sollte ein Eintrag mit entsprechen¬ den Namen nicht gefunden werden, wird eine Null zurückgegeben. Das folgende Beispiel zeigt, wie man feststellen kann, ob ein Name ei¬ ner Node doppelt in einer Liste vorkommt. Es kann in der Form noch nicht gestartet werden, die Liste muß vor dem FindName()-Aufruf initialisiert werden, da es sonst zu einem Absturz des Rechners kommt. #include mainO { _ struct Node •FindName<),*node; struct List »liste; if ((node = FindName (liste,"testnode"))!=0) if((node = FindName (node,''testnode"))!=0) printf ("Xn der Name testnode wurde 2 mal gefundenXn"); > Nach der Auflistung der für die Bearbeitung der Liste zur Verfügung stehenden Funktionen sollen diese teilweise anhand eines Beispiels verdeutlicht werden. Unser Beispiel zeigt, wie man eine Liste erstellt, diese ausgibt und ihre Einträge löscht. Exec 291 Beispiel für Listen: Dinclude eher *naine[] = {''node1","node2“,"node3">; struct List liste; struct Node node[3],*np; main () { int i; char n; liste.lh_Head = (struct Node •) (liste.lh_Tail; liste.lh_Tail = 0; liste.Ih TailPred = (struct Node *) tliste.lh_Head; liste.lhltype = NT_TASIC; for (i=0;i<=2;i++) { nodeli] .ln_Type = NT_TASIC; node[i].ln_Naine = nameCil; AddTail (&liste,&node[i]); > ausgabe(); printf ("\n Ausgabe der fertigen Liste\n''); np = liste.lh_Head->ln_Succ; Remove (np); ausgabe(}; printf ("\n 2. Node wurde entferntXn"); ausgabe () < for (np = liste.lh_Head; np != &liste.rh_Tail; np = np->ln_Succ) printf ("\n Xs \n",np->ln_Name); > 2.4 Professionelle Programmierung in Assembler Auf die generelle Frage, ob man besser in Assembler oder in einer Hochsprache wie C programmiert, soll in diesem Kapitel keine Ant¬ wort gegeben werden. Es ist vielmehr der Versuch, denen, die lieber oder gezwungenermaßen in Assembler programmieren, zu zeigen, wie sie sich dieses erleichtern. 292 Amiga intern Generell kann zu dem Konflikt jedoch dies gesagt werden: Assembler- Code ist um einiges kürzer und schneller als der einer Hochsprache. Die Fehlersuche (welches Programm ist auf Anhieb fehlerfrei?) ist in Assembler jedoch schwerer. Außerdem kann ein Assembler-Programm nur schwer auf einen anderen Rechner exportiert werden. Wenn jedoch ein Amiga-spezifisches Programm geschrieben werden soll, fällt der letzte Punkt nicht ins Gewicht. Als Entscheidungshilfe für die Benutzung von Assembler oder einer Hochsprache hier einige Beispiele: Interrupt* Device* Händler Sehr lange Programme sollten immer in Assembler sein sind meistens in Assember Assembler oder Hochsprache besser in einer Hochsprache mit Assembler-Einschüben Jetzt aber zum eigentlichen Thema des Kapitels. Für die professionelle Programmierung ist natürlich auch ein entspre¬ chender Assembler notwendig. Hierfür sehr gut geeignet, ist der von der Firma Metacomco entwickelte "ASSEM", der beim Entwicklungs¬ paket mitgeliefert wird. Der Assembler verfügt über lokale Variablen, Macros und die Möglichkeit, die Commodore Include-Files zu benut¬ zen. Darüber hinaus können mit ihm eigene Libraries erstellt und hinzu gelinkt werden. Die Assembler des Lattice- und Aztec-C-Compilers verfügen abgese¬ hen von den sehr nützlichen lokalen Variablen ebenfalls über die be¬ schriebenen Fähigkeiten. Der Nachteil bei diesen drei Assemblern ist jedoch, daß das Programm erst mit einem Editor erstellt und im nachhinein assembliert und ge¬ linkt werden muß, was nicht überragend schnell geschieht. Alle hier verwendeten Beispiele beziehen sich auf den ASSEM, sind jedoch prinzipiell auch auf die oben genannten Assembler sowie den Profimat umsetzbar. 2.4.1 Hinweise zur Benutzung des ASSEM Alle Label müssen immer am Anfang einer Zeile stehen. Vor einem Befehls-Code muß mindestens ein Leerzeichen stehen. Die Zeichen ; und * leiten einen Kommentar ein. Exec 293 Um einer Konstanten einen bestimmten Wert zuzuweisen, wird der EQU-Befehl benutzt. Die Konstante muß immer am Anfang einer Zeile stehen. Beispiel: MeinUert EQU 20 Um einer Variablen vorübergehend einen Wert zuzuweisen, wird der Befehl SET benutzt. Beispiel: MeinUert SET 10 MeinUert SET MeinUert + 10 Das Einfügen von Bytes in den Code geschieht mit dc.x. Für x kann b (für Bytes), w (für Worte) und 1 (für Langworte stehen). Beispiel: dc.b $12,5401001101,10,'hallo* dc.w $1234,100 dc.l $12345678,1202621,LABEL Das Einfügen von mit Null gefüllten Blöcken geschieht mit ds.x. Für x kann b, w oder 1 stehen. Wenn Worte oder Langworte eingefügt wer¬ den, so beginnen diese immer automatisch auf einer geraden Adresse. Beispiel: ds.b 10 ds.w 10 ds.l 10 ds.w 0 ;Einfügen von 10 Leer-Bytes ,-Einfügen von 10 Leerworten ;Einfügen von 10 Leerlangworten ;PC auf gerade Adresse bringen (Align) Zum Einfügen von Blöcken mit bestimmtem Inhalt dient dcb.x. Für x kann b, w, 1 stehen. Beispiel: dcb.b 10,$ff ;Einfügen von dcb.w 10,$ffff ;Einfügen von dcb.l 10,$ffffffff ;Einfügen von 10 $FF Bytes 10 $FF Uorten 10 $FF Langworten Lokale Label Lokale Label werden mit einer Zahl und nachfolgendem $ gekenn¬ zeichnet. Sie sind im Bereich zwischen zwei globalen Labein lokal. Beispiel: Start: ;Globales Label 2$: subq.l #1,d1 move.l #100,dO 1$: subq.l #1,d0 294 Amiga intern bne.s 1$ tst.l dl bne.s 2$ rts Fill: move.l #100,d1 1$: move.b d0,(a0)+ subq.l #1,d1 bne.s 1$ rts ;Lokales Label jLokales Label ;Globales Label ;Lokales Label Die Funktionen XREF und XDEF Allgemein betrachtet importiert die XREF-Funktion während des Link-Vorganges Definitionen und Adressen aus anderen Modulen. Diese Module sind entweder gelinkte Libraries (Linker-Libraries, nicht zu verwechseln mit den Amiga-Libraries wie die Exec-Library) oder andere gelinkte Objektmodule. Anhand späterer Beispiele wird die Benutzung deutlich. Die XDEF-Funktion stellt anderen Modulen die angegebenen Defini¬ tionen oder Adressen zur Verfügung, was nur beim Zusammenlinken mehrerer Module von Wichtigkeit sein kann. Um Funktionen aus Libraries wie Exec, DOS oder Intuition zu be¬ nutzen, müssen deren Offsets bekannt sein. Eine Methode besteht darin, den Offset im Anhang dieses Buches nachzuschlagen und eine Konstante zu definieren. Die andere, einfachere Methode ist es, sich die Offsets durch den Linker "heraussuchen" zu lassen. Hierfür niuß dem Linker jedoch mitgeteilt werden, welche Offsets erwünscht sind. Um dies zu bewerkstelligen, müssen die Funktionsnamen importiert werden. Dies geschieht mit dem Befehl XREF. Vor jeder so impor¬ tierten Library-Funktion muß ein _LVO stehen, damit der Linker die Offsets in seinen Definitionen findet. Beispiel: XREF _LVOAllocHem,_LVOFreeMeni jsr _LV0Al locHein(a6) jsr _LVOFreeMem(a6) Durch den Linker, werden die entsprechenden Offsets geholt. Das lä¬ stige Eintippen des _LVO kann man sich ersparen, wie Sie bei der Beschreibung der Macros sehen werden. Exec 295 Assemblieren mit dem ASSEM Das Erstellen eines lauffähigen Programms erfolgt in zwei Schritten. Als erstes muß das geschriebene Programm assembliert werden. Da¬ nach wird es mit den gewünschten Libraries und anderen Objekt-Da¬ teien gelinkt. Es hat sich eingebürgert, den Source des Assembler-Programms mit der Endung ".asm" zu versehen. Das vom Assembler erstellte Objekt¬ modul erhält die Endung ".o" oder ".obj". Nach dem Linken entfällt diese Endung. Folgende Aufrufe können dazu dienen, ein Source-Programm zu ei¬ nem ausführbaren Programm zu machen. assein fUe.asm *o RAMifile.o *i fnclude blink RAM:file.o to file library Itb/aniga.lib Die "-o"-Option in der ersten Zeile gibt dem Assembler an, daß er das Objekt-File im RAM unter dem Namen "file.o" ablegen soll. Mit "-i" wird ihm "gezeigt", wo entsprechende Include-Dateien zu finden sind. Als zweites wird der Linker auf gerufen, dem das zuvor erstellte File "RAM:file.o" übergeben wird. Dieser linkt es mit der Amiga-Library des Linkers zusammen und legt das fertige Programm unter dem Na¬ men "file" ab. Sofern Sie mit der XREF-Funktion Libraries importiert haben, ist das Linken der Amiga-Library unbedingt vonnöten. 2.4.2 Die Verwendung von Macros Die meisten für den Amiga erhältlichen Assembler verfügen über die Möglichkeit, Macros zu verwenden. Ein Macro besteht aus ein oder mehreren zu einer Einheit zusammen¬ gefaßten Instruktionen (Assembler-Befehle oder Daten-Bytes), die in¬ nerhalb des Programms durch alleinigen Aufruf des Macronamens ein¬ gefügt werden können. Wenn innerhalb eines Programms eine immer gleichbleibende Be¬ fehlsfolge öfter auf taucht, so kann diese zu einem Macro zusammen¬ gefaßt werden. Durch Einträgen des Macronamens in das Programm 296 Amiga intern wird die Befehlsfolge beim Assemblieren eingefügt. Man erspart sich einige Tipparbeit. Ein Beispiel dazu: Nehmen wir an, diese Befehlsfolge würde häufiger auftreten: NOVE.L A2,A0 NOVE.L A4,A1 BSR WILLSOSEIN TST. DO ;Erster Parameter nach AO ;Zueiter Parameter nach AI ;Funktionsauf ruf .-Rückgabe testen In diesem Fall könnte es sich lohnen, ein Macro zu definieren. Ein Macro hat immer den gleichen Aufbau: Nacroname MACRO Befehle ENDN MACRO ist das Code-Wort, das dem Assembler signalisiert, daß ein Macro beginnt. ENDM signalisiert das Ende des Macros. Beispiel: ;Erster Parameter nach AO ;Zueiter Parameter nach A1 ;Funktionsauf ruf ;Rückgabe testen Jedesmal, wenn diese Befehlsfolge im Programm stehen müßte, kann nun der Name des Macros (FUNKAUFRUF) verwendet werden. Das einfache Zusammenfassen von Befehlsfolgen ist jedoch nur ein Bruchteil der Möglichkeiten, die sich durch Macros ergeben. Durch Parameterübergabe sind sie wesentlich vielseitiger als in unserem er¬ sten Beispiel ersichtlich wurde. Ein solcher Parameter kann beispiels¬ weise der Name einer Funktion sein, die aufgerufen werden soll. Der übergebene Parameter wird mit einem \ und einer nachfolgenden Zahl, die die Nummer des Parameters angibt, angesprochen. FUNKAUFRUF MACRO MOVE.L A2,A0 MOVE.L A4,A1 BSR WILLSOSEIN TST. DO ENDM Beispiel: FUNKAUFRUF MACRO ;1. Parameter = Name der Funktion NOVE.L A2.A0 NOVE.L A4,A1 JSR \t ;Aufruf der Funktion 297 Exec TST. DO ENDH Mit FUNKAUFRUF FunktionXY wird anstelle des "JSR \1" "JSR FunktionXY" eingetragen und ent¬ sprechend assembliert. Werden mehrere Parameter angegeben, müssen diese beim Aufruf des Macros mit Kommas getrennt werden. Innerhalb des Macros werden sie in der Reihenfolge der Übergabe mit "\r, ''\2", "\3" usw. entge¬ gengenommen. Sollen innerhalb eines Macros Schleifen Vorkommen, muß eine Klei¬ nigkeit zusätzlich beachtet werden. Beispiel-Macro zum Füllen von Speicherbereichen; FRL HACRO ;FütIwert,Anzahl,Regt ster move.u #\1,d0 move.l #\2,d1 LoopVa move.w d0,<\3)+ subq.l #1,d1 bne ENDH Loop\a Die Verwendung des \@ nach dem Label "Loop" ist unbedingt erfor¬ derlich. Stellen wir uns vor, das Label innerhalb des Macros würde ohne diesen sonderbaren Zusatz benutzt, und das Macro sollte dreimal verwendet werden. Jedesmal würde der angegebene Macro-Code vom Assembler in den Programm-Code eingefügt. Somit stünde an drei Stellen des Programms das Label "Loop" und der Befehl "bne Loop". Der Assembler würde sofort reklamieren, daß das gleiche Label mehrmals benutzt wurde. Aus diesem Grund wird an den Labelnamen \@ angehängt. Bei jedem erneuten Macro-Aufruf wird für \@ eine andere Zahl eingesetzt, wo¬ durch beim ersten Aufruf das Label "LoopO.OOO", beim zweiten "LoopO.OOl", beim dritten "LoopO.003" usw. heißt. Zusätzlich zu den Übergabeparametern kann innerhalb eines Macros bedingt assembliert werden. Das heißt, aufgrund bestimmter Bedin¬ gungen hat das Macro beim "Einbau" in das Programm ein veränder¬ bares Aussehen. 298 Amiga intern Eine Bedingung beginnt mit einer IF-Anweisung und wird mit ENDC (End Condition = Ende der Bedingung) abgeschlossen. Bei den Bedin¬ gungen wird das Ergebnis einer arithmetischen Operation auf größer, kleiner, größergleich, gleich oder ungleich null geprüft. Folgende IF-Bedingungen stehen zur Verfügung; IFEQ AG1-AG2 Fahre mit fort, wenn Argumente gleich sind (equal). IFNE AG1-AG2 Fahre mit fort, wenn Argumente ungleich sind (not equal). IFGT AGI-AG2 Fahre mit fort, wenn AGl > AG2 ist (greater than). IFGE AG I-AG 2 Fahre mit fort, wenn AGl >= AG2 ist (greater or equal). IFLE AG1-AG2 Fahre mit fort, wenn AGl <= AG2 ist (lower or equal). IFC STR1,STR2 Fahre mit fort, wenn String 1 = String 2 ist (copy of). IFNC ST RI,STR 2 Fahre mit fort, wenn String 1 <> String 2 ist (no copy of). IFD Label Fahre mit fort, wenn Label bereits definiert ist (if defined). IFND Label Fahre mit fort, wenn Label nicht definiert ist (not defined). Exec 299 Es sind auch Ausdrücke wie "IFGE {2*\l)+10-(3*\2)" erlaubt. Im Zu¬ sammenhang mit den Bedingungen kann mit NARG die Anzahl der übergebenen Argumente abgefragt werden. Mit MEXIT kann das Macro vorzeitig beendet werden. Zum Ende der Erkärung der Macros soll deren Verwendung noch anhand einiger Beispiele gezeigt werden. Beispiele für Macros Macro für Aufruf von Library-Funktionen ohne die Eingabe von _LVO; dies wird durch das Macro erledigt. CALLSYS HACRO jsr LV0\1(a6) ENDH Aufruf: tnove.l $4,a6 CALLSYS AllocHem Aufruf einer Library-Funktion, wobei in A6 nicht der Zeiger auf die Library steht. Hier muß dem Macro mitgeteilt werden, wo es die Ba¬ sisadresse holen soll. LINKSYS HACRO move.l a6,-(a7) move.l \2,a6 jsr _LV0\1{a6) move.l (a7)+,86 ENDH Aufruf: CALLSYS AUocHeffl,ExecBase Macro, um sich das lästige _LVO beim Importieren einer Library- Funktion zu ersparen. XLIB Aufruf: HACRO XREF LV0\1 ENDH “ 300 Amiga intern XLIB AllocMem XLIB CreateProc anstelle von XREF _LVOAUocMein,_LVOCreateProc Testen, ob es sich bei einer Liste um eine leere Liste handelt. Wird beim Aufruf des Macros ein Parameter (ein Register) übergeben, wird angenommen, daß in diesem Register der Zeiger auf die Liste steht. Wenn kein Parameter angegeben wird, muß der Zeiger auf die Liste in AO stehen. TSTLIST HACRO IFC CMP.L ENDC IFNC CMP.L ENDC ENDM Aufruf: TSTLIST oder TSTLIST A3 Das BITDEF-Macro Um bei einer Konstanten ein bestimmtes Bit setzen zu können, muß folgende Instruktion eingeben werden: BitNunmer EQU 3 HyByit_Bit EQU 1 « BitNunner Das Include-File "exec/type.i" stellt hierfür ein brauchbares Macro (BITDEF) zur Verfügung. Beispiel: BITDEF MyBit,Bit,3 * [listl LH_TAIL+LN_PRED(AO>,AO LH_TAIL+LN_PRED(\1>,\1 zurück erhält man: Exec 301 MyBitB_Bit = 3 MyBitF_Bit = X00001000 BITDEF HACRO * prefix,&naine,&bitnun BITDEFO \1,\2,B_,\3 \aBITDEF SET 1«\3 BITDEFO \1,\2,F AMITDEF ENDM BITDEFO HACRO * prefix,&naine,&type,&value \1\3\2 EQU \4 ENDM 2.4.3 Verwenden von Include-Dateien Include-Dateien sind Dateien, in denen eine Vielzahl von Definitionen vorgenommen wurden, die durch das Einbinden dieser Dateien (Files) dem eigenen Programm zur Verfügung stehen. Das Verwenden von Include-Dateien ist für jeden, der schon in C programmiert hat, selbstverständlich. In Assembler besteht ebenfalls diese Möglichkeit. Das heißt nicht, daß die Includes des C-Compilers für den Assembler genutzt werden können. Es können jedoch ver¬ gleichbare Dateien genutzt werden. Die aus C bekannten vordefinierten Strukturen haben in den As- sembler-Includes zwar ein etwas verändertes Aussehen, sind prinzipiell jedoch gleich. Zu ihrer Benutzung existieren einige sehr hilfreiche Macros in dem Include-File "exec/types.i", die die Benutzung von Strukturen erst ermöglichen. Sehen wir uns zum Vergleich die C-Struktur "List" an. struct List { struct Node *lh_Head; struct Node *lh_Tail; struct Node *lh TailPred; UBYTE lh~Type; UBYTE >; Mit Hilfe unserer Macros ist es kein Problem, diese Struktur nach¬ zubilden. Sie sieht für Assembler so aus: STRUCTURE LH,0 APTR LH HEAO APTR LH TAIL APTR LH~TAILPRED 302 Amiga intern UBYTE LH_TYPE UBYTE tH_pad LABEL LH_SIZE Wie ist dies möglich? STRUCTURE, APTR, UBYTE und LABEL sind Macroaufrufe, denen entsprechende Übergaben gemacht werden. Mit Hilfe der Macros wird dem entsprechenden Label ein Wert zuge¬ wiesen. Dieser Wert entspricht dem Offset des Eintrages innerhalb der Struktur. Sehen wir uns einmal diese Macros an: STRUCTURE MACRO \1 EOU 0 SOFFSET SET \2 ENDM Als erstes wird das Macro STRUCTURE aufgerufen. Diesem Macro werden zwei Werte übergeben. Zum einen ist dies ein Label (LH) und weiterhin eine Zahl. Dem Label LH wird durch das Macro der Wert 0 zugewiesen. Zusätzlich wird dem Label SOFFSET der Wert des zwei¬ ten Übergabeparameters zugewiesen. Da hierbei der Befehl SET (siehe Hinweise zur Benutzung des ASSEM) benutzt wird, kann der Wert von SOFFSET später wieder verändert werden. Halten wir noch einmal fest: LH = 0 SOFFSET = 0 Als nächstes wird das Macro APTR aufgerufen: APTR MACRO \1 EOU SOFFSET SOFFSET SET SOFFSET+4 ENDM Dem Macro wird der Label-Name LH_HEAD übergeben. Wie aus dem Macro ersichtlich, wird diesem Label jetzt der Wert Null zugewiesen (SOFFSET = 0). SOFFSET wird um vier erhöht. Beim erneuten Aufruf von APTR mit der Übergabe des Labels LH_TAIL erhält dieses den Wert 4. SOFFSET wird auf 8 erhöht. Als nächstes erhält das Label LH_TAILPRED den Wert 8 und SOFFSET den Wert 12. Exec 303 Das folgende Macro, das aufgerufen wird, heißt UBYTE: UBYTE MACRO \1 EQU SOFFSET SOFFSET SET SOFFSET+1 ENDH Durch den Aufruf erhält LH_TYPE den Wert 12 und SOFFSET den Wert 13. *LH_pad" bekommt den Wert 13 und SOFFSET den Wert 14. Als letztes sehen wird uns noch das Macro LABEL an: LABEL HACRO \1 EQU SOFFSET ENDH Hier wird dem Label nur der SOFFSET-Wert übergeben, ohne ihn zu ändern. Nachdem alle Macros aufgerufen wurden, ergibt sich folgen¬ des Bild; LH s 0 LH HEAO X 0 LH"tAIL » 4 LH TAILPREO > 8 LH TYPE = 12 LHjjad = 13 LH SIZE = 14 Hier wird klar, wozu diese Macros dienen. Jedem an das Macro über¬ gebene Label ist der entsprechende Offset zugewiesen worden, den der dazugehörende Eintrag in der Struktur hat. Der Wert, um dem SOFFSET erhöht wurde, ist jeweils die Länge des entsprechenden Eintrages. Bei APTR (einem 4-Byte-Zeiger) ist der addierte Wert so¬ mit 4, beim Byte (Länge 1) der Wert 1. Bleibt nur noch zu klären, wozu das Macro LABEL benutzt wird. Mit LABEL wird der aktuelle SOFFSET-Wert an das angegebene Label übertragen. Geschieht das am Ende einer so definierten Struktur, ist dies automatisch die Länge der Struktur. Der Zugriff auf diese Struktur stellt jetzt kein Problem mehr dar. In C sähe er wie folgt aus: finclude main () 304 Amiga intern struct List HyList; MyList.lh_Type = 2; In Assembler somit: include "exec/lists.i" lea Listadresse,aO ;Adresse, ab der die Struktur steht move.b #2,LH_TYPE(aO) ;Typ eintragen Wenn beispielweise Speicher für eine Struktur belegt werden soll, so muß nicht die Länge mühsam nachgeschlagen werden, es kann jetzt wesentlich einfacher gehen: move.l #LH_SIZE,dO ;Länge der List-Struktur nach DO CALLSYS AUÖcMem ;Siehe Nacro-Beispiele Nehmen wir an, es sei eine Struktur Unterstruktur einer anderen. Auch dies läßt sich problemlos lösen: STRUCTURE MyStruct.O APTR UerUeiBUoHinn UBYTE EinUert STRUCT HeineListe.LH SIZE UBYTE Zähler LABLE HyStruct.SIZE Innerhalb der Struktur haben wir wieder einen neuen Macro-Aufruf: STRUCT HACRO \1 EQU SOFFSET SOFFSET SET SOFFSET+\2 ENDH An dieses Macro werden zwei Parameter Obergeben. Zum einen ist dies das Label, dem der Offset zugewiesen wird, zum anderen die Länge der einzufOgenden Struktur. SOFFSET wird um die Länge der Struktur erhöht, was bedeutet, daß dem nächsten Label der Offset hinter der Struktur übergeben wird. In unserem Beispiel wird eine List-Struktur eingefügt, was durch die eingetragene Länge von 14 Bytes (LH_SIZE = Länge der List-Struktur > 14) erkennbar ist. Will man jetzt auf die List-Struktur zugreifen, geschieht dies wie folgt 305 Exec lea MeineStruktur.aO lea MeineListe(aO),aO move.b #2,LH_TYPE(aO) .-Zeiger auf Basisadresse ;Zeiger auf Listanfang ;Typ eintragen Oder durch: lea HeineStruktur+MeineListe.aO move.b #2,LH_TYPE(aO) Es Stehen folgende Macros zur Strukturbildung zur Verfügung: STRUCTURE Label,Offset STRUCT Label,Struct_SIZE BYTE Länge = 1 Byte UBYTE Länge = 1 Byte WORD Länge = 2 Byte UUORD Länge = 2 Byte SHORT Länge = 2 Byte USHORT Länge = 2 Byte BOOL Länge = 2 Byte LONG Länge = 4 Byte ULONG Länge = 4 Byte FLOAT Länge = 4 Byte APTR Länge = 4 Byte CPTR Länge = 4 Byte RPTR Länge = 2 Byte (für Offsets) In den Include-Files sind alle für C nutzbaren Strukturen auch als Assembler-Struktur umgesetzt. Die Namen der Einträge haben sich oft in der Groß-/Kleinschreibung geändert. Abgesehen von den Strukturen sind auch weitere sehr hilfreiche Macros und Definitionen in den Includes enthalten. 2.4.4 Hinweise zur "sauberen" Programmierung Die meisten in diesem Abschnitt gegebenen Ratschläge kommen von den Programmierern des Amiga-Betriebssystems, die sicherlich über den Verdacht erhaben sind, keine professionellen Programmierer zu sein und nicht über entsprechende Erfahrung zu verfügen. Beim Programmieren sollte man die Prozessorregister in zwei Gruppen aufteiien. Dies sind zum einen die Register DO, Dl, AO und Al, die in eine Gruppe fallen. Alle restlichen gehören in die andere Gruppe. Die Gruppen kommen immer dann zur Geltung, wenn in eine Unterrou¬ tine verzweigt wird. 306 Amiga intern Das Betriebssystem ist immer so programmiert, daß die Register der ersten Gruppe (DO, Dl, AO, Al) als Übergabeparameter für die auf¬ zurufende Funktion verwendet werden können. Sollten diese nicht ausreichen, werden notgedrungen auch andere Register benutzt. Das aufrufende Programm kann nicht davon ausgehen, daß diese Re¬ gister der ersten Gruppe nach dem Funktionsaufruf ihren alten Wert beibehalten haben. Im Gegensatz hierzu stehen die Register der zwei¬ ten Gruppe, deren Wert unverändert bleibt. Sollte das Unterprogramm die Register der zweiten Gruppe benutzen, müssen deren Inhalte vor der Benutzung gerettet und vor dem Verlassen des Unterprogramms zurückgegeben werden. Man kann somit beispielsweise Schleifen mit den Registern D2 bis D7 aufbauen, ohne sich darum kümmern zu müssen, ob eventuelle Un¬ terroutinen hier "dazwischenfunken". Wenn man sich strikt nach diesem Prinzip richtet, kann es nicht pas¬ sieren, daß durch eine vor längerer Zeit geschriebene Routine, deren genauer Aufbau nicht mehr präsent ist, Register zerstört werden, die noch vonnöten sind. Es kommt beim Programmieren öfter vor, daß eine Funktion von ei¬ nem anderem Programmteil nur zum Teil verwendet werden kann. Manche Programmierer greifen dann zu einer recht unsauberen Me¬ thode. Sie definieren ein zweites Label innerhalb der Funktion und springen dadurch mitten in diese Funktion ein. Sicherlich istxdies machbar, gehört jedoch nicht zum guten Stil. Das Programm wird so¬ mit für andere, aber auch für einen selbst nach einiger Zeit sehr un¬ übersichtlich. Die Funktion ist nicht klar definiert. Beispiel: jsr Hauptfunktfon jsr Unterfunktion rts Hauptfunktion: ;Aufgerufene Funktion Exec 307 Unterfunktion: ;Eingefügtes Label rts ;Funkt ionsende Eine wesentlich elegantere Methode ist es, die Haupt-Funktion aus zwei kleineren Funktionen bestehen zu lassen, wovon die eine separat aufgerufen werden kann. Beispiel: jsr Hauptfunktion jsr Unterfunktion rts Hauptfunktion: jsr Unterfunktion rts Unterfunktion: rts Dieser Programmaufbau hat den Vorteil, daß jede Funktion ihre klar definierbare Aufgabe hat und das Programm somit leicher zu über¬ blicken ist. In nahezu jedem Assembler-Programm müssen globale Werte und Adressen abgelegt werden. Ein Beispiel hierfür sind Zeiger auf Libra¬ ries, Windows oder ähnliche Strukturen. Hierfür können entsprechende Label am Ende des Programms abgelegt werden. Soll bei einem dieser Label z.B. der Zeiger auf die DOS-Library gespeichert werden, ist es nur mit Aufwand möglich, das Programm relokatibel zu gestalten. Eine gute Möglichkeit ist es, eine Struktur (sie werden in diesem Ka¬ pitel besprochen) mit allen globalen Zeigern zu erstellen. Eine solche Struktur könnte folgendes Aussehen haben: STRUCTURE Globals,0 APTR SysBase APTR DosBase APTR UindowPtr WORD Counter 308 Amiga intern LABEL Globals_SIZE Der Speicherbereich, der für diese Struktur benötigt wird, ist meistens nicht übermäßig groß, so daß sie bequem im Stapel Platz findet. Ihr Programm kann nur folgendermaßen beginnen: LEA -Global_SIZE(a7),a7 ;Schaffen von Platz in Stapel move.l a7,a5 ;Zeiger auf Struktur nach A5 In A5 steht jetzt der Zeiger auf den Anfang unserer Struktur. Wird jetzt der Zeiger auf die DOS-Library geholt, kann er mit: move.l dO,DosBase(aS) gespeichert werden. Der Inhalt von A5 darf jetzt jedoch nicht mehr achtlos verändert werden. Durch den Zugriff auf globale Variablen indirekt über A5 wird der Programm-Code automatisch relokatibel. Das Einfügen einer neuen Variablen stellt keine Schwierigkeiten dar (eintragen in die Struktur, fertig). Soll das Programm beendet werden, muß der Stapel wieder in den Ursprungszustand versetzt werden. Dafür reicht: LEA Global_SIZE(a7),a7 Sollte die globale Struktur zu lang werden, ist das Abspeichern dersel¬ ben im Stapel nicht zu empfehlen. In diesem Fall muß mit der Alloc- Mem-Funktion der entsprechende Speicher zu Beginn des Programms belegt werden. Beispiel: move.l #MEMF_PUBLIC!MEMF_CLEAR,d1 move.l #Global_SIZE,dO CALLSYS AllocHem tst.l dO beq Fehler_AllocMem move.l d0,a5 In A5 befindet sich wieder der Zeiger auf die Struktur der globalen Variablen. Exec 309 2.5 Die Benutzung von Funktionstabellen (Libraries) Dieses Kapitel ist sowohl für C als auch für Assembler-Programmierer gedacht. Das Verständnis des Kapitels ist unerläßlich, wenn man den Amiga mit all seinen Möglichkeiten nutzen will. Was ist eigentlich eine Library? Eine Library ist laut Übersetzung eine Bibliothek. Es handelt sich hierbei um eine Bibliothek von Funktio¬ nen, die vom Programmierer genutzt werden können. Sie ist, um ge¬ nau zu sein, eine größere Sprungtabelle, über die die Funktionen auf¬ gerufen werden. Libraries werden gebraucht, damit der Programmierer die Funktionen benutzen kann, die nicht in C und erst recht nicht in Assembler vorhanden sind, jedoch vom Betriebssystem zur Verfügung gestellt werden. Ein Beispiel dafür ist die Funktion OpenScreen() zum Erstellen eines eigenen Screens. C stellt die Funktion nicht zur Verfü¬ gung, sie muß mit Hilfe einer Library des Amiga aufgerufen werden. Eine Library hat folgendes Aussehen; 00060A JMP $FC2FD6 000610 JHP $FC0B28 000616 JMP SFCOACO Die Libraries des Amiga sind in die verschiedenen Aufgabenteile un¬ terteilt. Die meisten von ihnen sind schon im ROM-Bereich des Rech¬ ners enthalten; einige wenige müssen noch bei Bedarf von Diskette nachgeladen werden. Diese Libraries stehen zur Verfügung: clist.lib diskfont.lib dos.lib expansion.lib exec.lib graphics.lib icon.lib intuition.lib layers.lib mathffp.lib inath i eeedoubbas. l i b mathtrans.lib potgo.lib ram.lib translator.lib 310 Amiga intern Darunter nimmt die Exec-Library eine Sonderstellung ein, denn ihre Funktionen sind sofort nach einem Reset erreichbar. Wenn man eine Funktion einer anderen Library benutzen will, muß man dies dem Sy¬ stem mitteilen, das daraufhin die entsprechende Library erstellt, auf die man dann zugreifen kann. Sollte diese Library schon erstellt wor¬ den sein, wird dem Programmierer lediglich mitgeteilt, wo er sie er¬ reichen kann. Von einer Library ist normalerweise lediglich ihre Basisadresse be¬ kannt, von der aus mit negativen Offsets (Ausgleich) die gewünschten Funktionen aufgerufen werden. Ein Library-Aufruf sieht, für alle Libraries geltend, wie folgt aus: move.l libeasis,a6 ;Basisadresse der Library jsr 0ffset(a6) ;Aufruf der Funktion mit negativem Offset Der Offset der entspechenden Funktion muß aus einer Tabelle ent¬ nommen werden, die Sie im Anhang dieses Buches finden. Vor dem Funktionsaufruf müssen die Parameter (sofern welche benötigt wer¬ den) den entsprechenden Registern übergeben werden. Wie schon gesagt, sind die Funktionen der Exec-Library schon gleich nach einem Reset erreichbar. Um die Funktionen der Library aufru- fen zu können, muß ihre Basisadresse bekannt sein. Die Basisadresse der Exec-Library steht ab Speicherstelle S04, von wo aus sie ohne Probleme ausgelesen werden kann. Von C aus erreicht man sie durch Lesen der Standardvariablen SysBase. Ein Aufruf einer Exec-Library-Funktion aus C heraus ist sehr ein¬ fach. Als Beispiel zeigen wir den Aufruf der FindName-Funktion. finclude struct ExecBase *SysBase; struct Library »FindHameO, *library; mainO C Library = FindHame(&(SysBase->LibList),"dos.Library"); printf ("\n Xx \n",Library); ) Dieses kleine C-Programm durchsucht die Liste aller zur Verfügung stehenden Libraries nach der DOS-Library. Wird diese gefunden, so 311 Exec wird die Adresse, an der sie liegt, ausgegeben, ansonsten wird eine Null ausgegeben. Sie sehen, daß es außer der für Sie vielleicht etwas verwirrenden Zu¬ weisung innerhalb des Funktionsaufrufes bei Exec-Funktionen nichts zu beachten gibt. Wir wissen bereits, daß der FindName-Funktion ein Zeiger auf eine Liste und der Name der Node mitgeteilt werden muß. Warum die Zuweisung der Liste dieses Aussehen hat, braucht hier noch nicht zu interessieren. Dies wird im Kapitel über ExecBase er¬ läutert. Doch sehen wir uns einmal an, wie der C-Compiler dieses Programm in Assembler umsetzt. Wenn man sich auf das Wesentliche beschränkt, hat das umgesetzte Programm dieses Aussehen: global _Sy8Base,4 global _library,4 main: pea *Node move.l _SysBase,a6 pea 378(a6) jsr _FindNaine move.l dO,_library move.l _library,-(a7) jsr jarintf rts ;Zeiger auf Node in Stapel schieben ;Ze1ger auf ExecBase (aus $4) ;Ze1ger auf Liste in Stapel schieben ;FindName Funktion aufrufen .■Rückmeldung in Library merken ;und im Stapel an die ;j3rintf Routine übergeben _FindName movem.l 4(sp),a0/a1 .-Parameter für FindName aus Stapel in ;die entsprechenden Register laden move.l _SysBase.a6 .-Basisadresse der Exec-Library holen jiRP -276(a6) ;Funktion FindName aufrufen Das komplette vom Compiler erzeugte Assembler-Listing ist etwas umfangreicher, trägt jedoch nicht zum Verständnis des Library-Funk¬ tionsaufrufs bei und wurde deshalb weggelassen. _SysBase ist ein Zeiger auf die Exec-Library, dessen Funktionen mit negativen Offsets aufgerufen werden. Wenn man mit positiven Offsets von _SysBase aus auf den Speicher zugreift, erhält man Werte von ExecBase (der Hauptstruktur des Betriebssystems), auf die wir zu ei¬ nem späteren Zeitpunkt noch eiiigehen werden. Unter den Einträgen in der ExecBase befindet sich auch eine Liststruktur, in der die Li¬ braries verzeichnet sind. 312 Amiga intern Das Compilat ist zwar ein wenig umständlich, aber trotzdem recht gut zu verstehen. Das Hauptprogramm bringt die Zeiger auf die Liste und die Node in den Stapel und ruft daraufhin das Unterprogramm auf. Dort werden die eben gespeicherten Parameter in die dafür vorgesehenen Register geladen. Nun wird die Basisadresse der Exec-Library geholt und in A6 geschrieben. Dann ist alles vorbereitet, um die eigentliche Funktion FindName aus der Exec-Library aufzurufen. Der Offset dieser Funk¬ tion ist, wie sich aus dessen Beschreibung im Kapitel über die Listen entnehmen läßt, -276. Folglich heißt der eigentliche Funktionsaufruf "jmp -276(a6)". Der zurückgegebene Parameter wird in "library" ge¬ speichert, in den Stapel geschoben und mit "_printP auf dem Bild¬ schirm ausgegeben. Wie schon gesagt, ist dem System der Standort der Exec-Library be¬ kannt, weshalb ein Funktionsaufruf ohne Probleme stattfinden kann. Sollte man eine Funktion aus einer anderen Library aufrufen wollen, so weiß weder das Betriebssystem noch der C-Compiler, wo er diese Library zu finden hat. Zu diesem Zweck stellt die Exec-Library eine Funktion zur Verfügung, mit der sich die Basisadressen anderer Li¬ braries ermitteln lassen. Diese Funktion nennt sich OpenLibrary und hat folgendes Aussehen: OpanUbraiy LibPtr = OpenLibrary (LibName, Version) DO AI DO Parameter LibName Zeiger auf den mit Null abgeschlossenen Namen der Library, die ge¬ öffnet werden soll, z.B. "intuition.library". Version Gibt die Library-Version an, die der Benutzer zu öffnen wünscht. Sollten mehrere Libraries desselben Namens zur Verfügung stehen, werden sie anhand der Versionen unterschieden. Sollte eine neue Ver¬ sion verlangt werden, die noch nicht zur Verfügung steht, so ist der OpenLibrary-Aufruf gescheitert. Exec 313 LibPtr Beinhaltet nach dem Aufruf der Funktion die Basisadresse der ge¬ suchten Library, sofern diese gefunden wurde. Ist die gesuchte Li¬ brary nicht gefunden worden, wird eine Null als Fehlermeldung zu¬ rückgegeben. Der Variablen-Name, in der die Basisadresse der Library gespeichert wird, kann nicht beliebig gewählt werden. Damit C die Basisadresse der Library weiß, aus der eine Funktion aufgerufen werden soll, muß diese zuvor in einer dafür vorgesehenen Variablen gespeichert werden. Wird diese Variable zwar deklariert, jedoch nicht auf den richtigen Wert gesetzt, führt der Aufruf der Library-Funktion zum Absturz des Programms oder sogar des ganzen Rechners. Auflistung der festgelegten Variablen: Library clist.library diskfont.library dos.library excansion.library exec.library graphics.library icon.library intuition.library layers.library mathffp.library mathieeedoubbas.library mathtrans.library potgo.library translator.library Variable CListBase DiskFontBase DosBase ExpansionBase SysBase GfxBase IconBase IntuitionBase LayersBase MathBase MathIeeeDoubBasBase MathtransBase PotgoBase TranslatorBase Es gibt mehrere Möglichkeiten, diese festen Variablen zu deklarieren. Am einfachsten ist es jedoch, sie als ULONG (Langwort ohne Vor¬ zeichen) zu deklarieren, weil man sich so die Pointer-Umwandlung erspart. Natürlich muß bei der Deklaration als ULONG "exec/types.h" als Include-File hinzugenommen werden, da der Compiler sonst nicht weiß, was ULONG bedeutet. 2.5.1 öffnen einer Library Sehen wir uns den Gebrauch und das Öffnen einer Library einmal an¬ hand eines Beispiels an. Das folgende Beispiel öffnet die Intuition-Li¬ brary und daraufhin einen eigenen Screen. 314 Amiga intern #include #include struct NewScreen ns = { 0 . 0 . 640.256, 2 . 0 . 1 . HIRES, CUSTOMSCREEN, NULL. (UBYTE *) "Hy Screen", NULL, NULL }; ULONG IntuitionBase; mainO ULONG screen; if(!(IntuitionBase=OpenLibrary("intuition.library"))) exit(IOO); screen ^ OpenScreen (&ns); > Nachdem exec/types.h und die Intuition-Strukturen eingebunden sind, wird die Screen-Struktur initialisiert und IntuitionBase als ULONG deklariert. IntuitionBase muß global deklariert werden, da sie in den von C eingebundenen Prozeduren (OpenScreen) verwendet werden muß. Dann wird die OpenLibrary()-Funktion der Exec-Library auf¬ gerufen und die Intuition-Library geöffnet. In IntuitionBase wirrf die Basisadresse der Library gespeichert. Daraufhin wird die OpenScröen- Funktion aufgerufen. Sehen wir uns wieder an, was der Compiler aus diesem Programm macht. Das eigentliche Compiler-Programm ist etwas umfangreicher und wurde hier bis auf die wichtigsten Teile verkürzt. ns: ;Initlalisierung der Screen-Stuktur "dc.w 0 dc.w 0 dc.w 640 dc.w 256 dc.w 2 dc.b 0 dc.b 1 dc.w -32768 dc.w 15 dc.l SOOOO dc.l .1+0 dc.l $0000 Exec 315 dc.l $0000 . 1 : dc.b "My Screen",0 global _lntujtionBase,4 main: movem.l Version,-(sp) ;Lib. Version in den Stapel pea *naine ;Zeiger auf name in Stapel jsr _OpenLibrary ;OpenLib-Funktion aufrufen move.l dO,_lntuitionBase ;RUckmeldung speichern tst.l dO ;Auf Null testen bne .5 ;Nicht Null (ok) pea 100 ;N(jiiTier bei Exit jsr _exit . 5: ;Exit pea _ns ;Zeiger auf Screen-Struktur jsr ^OpenScreen ;Open-Screen-Funktion aufrufen rts ;Rücksprung _OpenScreen; move.l 4(sp),a0 ;Zeiger auf Screen-Struktur move.l _IntuitionBase,a6 ;Basisadresse aus der festen jmp -198 extern ExecBase *SysBase; inain() t struct Task ‘waiting, ‘ready, ‘running; waiting=TaskUait.lh_Head; ready=TaskReady.lh_Head; running=(struct Task *)SysBase->ThisTask; usw. } Dieses Programm übergibt in WAITING und READY je einen Zeiger auf die erste Task-Struktur der jeweiligen Liste. Bei RUNNING han¬ delt es sich um einen Zeiger auf den RUNNING-Task. D.h. Ta^kWait und TaskReady sind Listen-Köpfe, wobei es sich bei ThisTask nur um einen Zeiger handelt. Da es immer nur einen einzigen RUNNING- Task geben kann, existiert hier natürlich keine Liste. Task-Switching An dieser Stelle sind jetzt erst einmal ein paar Worte zu dem zentralen Vorgang nötig, der dafür verantwortlich ist, daß mehrere Tasks den¬ selben Prozessor benutzen können: dem Task-Switching. Wie der Name schon sagt, handelt es sich dabei um das Umschalten der verschie¬ denen Tasks. Befindet sich ein Task einmal in einer der beiden Task- Listen, wird er automatisch in diesen Vorgang mit auf genommen. Frage Nr.l beim Task-Switching ist 321 Exec Wie werden die Tasks umgeschaltet? Ein Task weiß nicht, wann er den Prozessor erhält und wieder abge¬ ben muß. Prüft er das ThisTask-Feld in ExecBase, findet er dort im¬ mer den Zeiger auf seinen Task, da er dieses Feld ja nur abfragen kann, wenn er gerade die CPU besitzt. In Wirklichkeit kann er aber zu jedem beliebigen Zeitpunkt unterbrochen werden. Ausgelöst wird der pnze Vorgang von einem Interrupt, der auftritt, wenn der Task den ihm zustehenden Anteil an Rechenzeit verbraucht hat oder wenn ein wichtiger Task sofort abgearbeitet werden muß. Die Switch-Routine des Betriebssystems erledigt das eigentliche Umschalten. Im Gegensatz zum Task, der immer im User-Mode des 68000er läuft, wird die Switch-Routine im Supervisor-Modus abgearbeitet. Zuerst werden die Prozessorregister D0-D7 und A0-A6 auf dem Task-Stack gesichert. Der User-Stack-Pointer kommt dann in das tc_SPReg-Feld der Task- Struktur, und zuletzt kommen noch Statusregister und Programmzähler auf den User-Stack. Sie wurde vom 68000 durch den Interrupt auto¬ matisch auf den Supervisor-Stack gelegt. Dann wird der Task in die READY-Liste übernommen. Der neue Task stammt entweder aus der READY- oder WAITING- Liste. Er wird jetzt dort entfernt und statt dessen in das ThisTask- Feld eingetragen. Danach werden erst sein Stackpointer aus seinem tc_^SPReg-Feld und anschließend seine Register von seinem Stack ge¬ holt. Exec verläßt die Switch-Routine am Ende mit einem RTE. Da¬ mit wird automatisch der neue Task bearbeitet. Dies war selbstverständlich nur eine grobe Zusammenfassung des Ab¬ laufs der Switch-Routine. Tatsächlich werden noch Daten ausgetauscht und Sonderfälle überprüft. Wenn sich der Task im EXCEPTION-State befindet, kann es Vorkom¬ men, daß die Switch-Routine vor bzw. nach dem Umschalten noch die Routinen aufruft, deren Adressen in den tc_Launch- und tc_Switch- Einträgen der Task-Struktur gespeichert sind. Aber wie gesagt, der gesamte Umschaltvorgang läuft automatisch ab. Sie brauchen sich darum nicht zu kümmern. Wann werden die Tasks umgeschaltet? Formuliert man die Frage anders, lautet sie: Welchen Anteil der Re¬ chenzeit bekommt ein bestimmter Task? Das Verteilen der Rechenzeit heißt Task-Scheduling. Als Grundlage dafür verwendet Exec das der List-Node-Struktur am Anfang der Task-Struktur. 322 Amiga intern Allgemein gilt, je höher die Priorität eines Tasks, desto mehr Re¬ chenzeit bekommt er, d.h desto schneller wird er abgearbeitet. Exec beginnt mit dem Task mit der höchsten Priorität. Er bekommt für eine bestimmte Zeitspanne den Prozessor. Danach wird die Rechenzeit, die er verbraucht hat, von seiner relativen Priorität im Vergleich zu den READY-Tasks abgezogen. Damit ist er Jetzt nicht mehr der Task mit der höchsten Priorität, und so kommt der nächste Task an den Pro¬ zessor. Dieses Verfahren stellt sicher, daß auch Tasks mit einer niedri¬ gen Priorität ab und zu einmal den Prozessor bekommen und zeitkri¬ tische Tasks, die den Prozessor zwar sofort, aber nur für eine kurze Zeitspanne benötigen, den anderen vorgezogen werden. Die Priorität kann wie üblich Werte zwischen -128 und 127 annehmen. Ein normaler Task sollte die Priorität 0 erhalten. Dies ist auch die Priorität eines CLI-Tasks. Die übrigen Tasks des Betriebssystems be¬ wegen sich zwischen -20 und 20, es ist also nicht nötig, extreme Werte in das tc_Node.ln_Pri-Feld einer Task-Struktur zu schreiben. Ein weiteres Feld der Task-Struktur ist tc_Node.ln_Name. Es enthält einen Zeiger auf den Namen des Tasks (siehe Kapitel über Listen). E>amit wird das Auffinden eines bestimmten Tasks erleichtert. Das tc_State-Feld enthält den Task-State. Folgende Werte sind den verschiedenen States zugeordnet UNGÜLTIG = 0 ADDED = 1 RUNNING = 2 READY = 3 UAITING = 4 EXECPTION = 5 RENOVED - 6 Um besser verstehen zu können, wie das Task-Switching arbeitet, folgt an dieser Stelle die Dokumentation der Wait-Funktion, von der aus ein anderer Task gestartet wird. >- DO * Bit-Maske für Signal-Bits >s A6 = Zeiger auf ExecBase fdedO move.l 276(A6},A1 fc1ed4 move.l D0,22(A1} fclecB move.w #$4000,$dff09a fcleeO addq.b #1,294(A6) fcleeA bra.s Sfcifla fc1ee6 fflove.b #$04,15(A1) fcleec lea 420(A6),A0 fclefO lea 4(AO),AO fc1ef4 move.l 4(A0),D0 Zeiger auf eigenen Task Einträgen der Signal-Bits Disable- Hacro Unbedingter Sprung Task-Status auf Uait Zeiger auf Uait-Liste Zeiger auf lh_Tail Zeiger auf letzte Node holen Exec 323 fcIcfB move.l A1,4(A0) fclefcmovc.l A0,(A1) fclefe move.l D0,4(A1) fc1f02 move.l D0,A0 fc1f04 move.l A1,(A0) fc1f06 move.l A5,A0 fcifoa lea -54(A6),A5 fcIfOc jsr -30(A6) Task ans Ende der Liste legen und richtig verketten AS retten Zeiger auf Switch-Funktion SupervisorO Nach dem Aufruf der Supervisor-Funktion wird die Switch-Routine auf gerufen. feif10 move.l A0,A5 A5 zurückholen feif12 move.l 276(A6},A1 Jetziger Task nach AI fc1f16 move.l 22 struct ExecBase *SysBase; mainO < register struct Task •a_task; APTR tun, ttx>des[50], utask, l.task; void o1(),o2(); register APTR Anode; DisableO; Anode = tnodes; Exec 327 run = (APTR}SysBase->ThisTasl(; for(a_task=(struct Task *}SysBase->TaskReady.lh_Head; a_ta8k->tc_Node.ln_Succ; *Anode=(APTR)a_task,Anode++, a_task=a_task->tc_Mode.ln_Succ); utask=Anode; for(a_task=(struct Task *)SysBase->TaskWait.lh_Head; a_task->tc_Mode.ln_Succ; *Anode=(APTR)a_task,Anocte++, a_task=a_task->tc_Mode.ln_Succ); ltask=Anode; EnableO; printfCXnTask in the running state:\n");o1();o2(rijn); printf("\nTask(s) in the ready state:\n“);o1(); for(Anode=tnodes; Anode! =wtask; o2(*Anode),Anode+'i'); printf(''\nTask(s) in the waiting state:\n'');o1(); for{;Anode!=ttask;o2(‘Anode),Anode++); > void o1() C printf C'Stackadress Stacksize Priority Signals NameW); printf <".\n"); > void o2(at} register struct Task *at; { printf("X10lxX10lxX8ldX1Ux Xs\n",at->tc SPLower, {ULONG)at->tc_SPUpper - tc_SPLower -2L, {LONG)at->tc_Hode.ln_Pri, at->tc SigWait,at->tc_Node.ln_Hame); > Da das Programm lediglich die Zeiger auf die Task-Strukturen sichert, nicht aber den Inhalt dieser Strukturen, besteht immer noch eine po¬ tentielle Fehlermöglichkeit, wenn ein Task während der Ausgabe der Werte gelöscht wird. Da dies aber so gut wie nie vorkommt und es ein riesiger Mehraufwand wäre, alle Task-Strukturen zwischenzuspeichern, wurde im Programm darauf verzichtet. Durch Abändern der ol()- und o2()-Routinen kann man auch andere Felder der Task-Strukturen aus¬ geben. Experimentieren Sie ruhig. 328 Amiga intern Erzeugen neuer Tasks Nachdem soviel über Tasks und Task-Strukturen geredet wurde, soll jetzt ein neuer Task kreiert werden. Was wird dafür benötigt? Zuerst eine Task-Struktur, dann ein Stack, der Task-Name, denn die Task- Struktur enthält ja nur einen Zeiger auf den eigentlichen Namen, und zuletzt noch ein Programm, den eigentlichen Task. Hier tauchen jetzt einige Probleme auf. Es wird eigener Speicher für den Task benötigt, denn er soll ja auch nach Beendigung des Pro¬ gramms, das ihn erzeugt hat, im Speicher bleiben. Um das Proramm nicht komplizierter als nötig zu machen, werden Task-Struktur, Task- Name und Stack zum neuen Strukturtyp "alltask" zusammengefaßt, für den dann ein gemeinsamer Speicherblock belegt wird. Der eigentliche Task ist als normale C-Funktion mit dem Namen "code" geschrieben. Er macht nichts weiter als einen Zähler hinaufzu¬ zählen, bis dieser den Wert SFFFFFF erreicht hat. Dafür benötigt er allerdings einige Minuten. Danach endet der Task. Seine Anwesenheit bemerkt man an der Verlangsamung des Amiga, da unser Task ja auch seinen Anteil an Rechenzeit bekommt. Oder man verwendet das vor¬ angegangene Beispielprogramm, das alle Tasks auflistet. Unser Task erscheint als READY-Task mit dem Namen "Beispiel-Task". Um die code-Funktion an ihre engültige Speicherposition zu kopieren, wird ihr Name als Zeiger auf ihre Adresse verwendet. Die Funkiton "end" dient nur dazu, die Endadresse der code-Funktion zu ethalten. Da es in C keine Möglichkeit gibt, den Speicherbedarf einer Funktion zu ermitteln, wird er durch Bildung der Differenz aus Anfangs- und Endadresse errechnet. Auf eine Freigabe des belegten Speichers wurde verzichtet, um das Programm nicht noch komplizierter zu machen. Es handelt sich dabei auch nur um knapp ein KByte. /•••• Erzeugen eines Tasks ****/ #include #include #include ry.h> dldefine STACK_SIZE 500 /• Größe des Stacks */ mainO void code(),end(); APTR m/code, AUocMemO; Exec 329 static eher TasknameH = "Beispiel-Task”; register APTR c1,c2; struct alltask { struct Task tc; char Naine[sizeof(Tasknaine)], Stack[STACK_SIZE]; } "mytask; mytask = AllocMein((ULONG)sizeof(*inytask), MEMF_PUBLIC|MEMF_CLEAR); if(mytask==0) {printfC'Kein Speicher fuer die AUTask-Struktur!\n"); return(O); > / mycode = Al locHein((ULONG)end-(ULONG)code,HEMF_PUBLIC); if(mycode==0) {FreeMemlmytask,(ULONG)sizeof("mytask)); printfC'Kein Speicher fuer den Task-Co^!\n"); return(O); } strcpy(mytask->Name,Taskname); inytask->tc.tc_SPLower=niytask->Stack; mytask->tc.tc_SPUpper=mytask->Stack+STACK_SI2E; fflytask->tc.tc_SPReg=mytask->tc.tc_SPUpper; niytask->tc.tc_Hode. ln_Type=NT_TASK; mytask->tc.tc_Node. ln_Name=niytask->Ranie; for(c1=code,cZ=niycode;c1<=end;*c2++="c1++); AddTaskCmytask,mycode,OL); > /••• Die Funktion "code" ist der eigentliche Task •••/ void codeO { ULONG count; for(count=0;count void endOO Wie man sieht, müssen nur wenige Felder der Task-Struktur initiali¬ siert werden: tc_SPLower mit der unteren Stsck-Grenie tc_SPUpper und tc_SPReg mit der oberen Stack-Grenze tc_Node.in_Type mit dem Lietentyp NT_TASK Auf den Namen kann man auch verzichten. Eine wesentliche Funktion wurde in diesem Programm zum erstenmal verwendet: 330 Amiga intern AddTaskCtask, initialPC, finalPC) Diese Funktion fügt einen neuen Task zum System hinzu. Normaler¬ weise wird der neue Task sofort zur READY-Liste hinzugefügt. Sie benötigt folgende Parameter: task Zeiger auf eine Task-Struktur, in der mindestens die vier oben er¬ wähnten Felder initialisiert sein sollten. initialPC Adresse, bei der die Abarbeitung des Task-Programms beginnen soll, in unserem Beispiel die Anfangsadresse der code-Funktion an ihrer endgültigen Adresse, die in mycode gespeichert ist. finalPC FinalPC enthält die Adresse, die angesprungen wird, wenn dür Task einen RTS-Befehl ausführt. Man kann hier die Adresse einer Routine einsetzen, die den belegten Speicher zurückgibt, geöffnete Files schließt usw. Gibt man einfach 0 als finalPC an (wie in unserem Bei¬ spiel), verwendet Exec seine Standard-FinalPC-Routine. Diese gibt den Speicher frei, auf den das tc_MemEntry-Feld der Task-Struktur zeigt. Danach entfernt sie den Task aus den Systemlisten. Beenden eines Tasks Die Beschreibung von finalPC bringt uns auf das nächste Thema. Wie wird ein Task beendet? Folgende Möglichkeiten existieren: 1. Der Task erreicht einen RTS-Befehl, der nicht den Rücksprung eines Task-eigenen JSR oder BSR darstellt. Dann läuft der unter finalPC geschilderte Vorgang ab. 2. Es tritt eine öSOOOer-Exception auf, die nicht vom Task ge- handhabt wird, z.B. ein Bus- oder Adreß-Error, Division by Zero usw. Exec erzeugt dann seine "Software Error - Task Held"-Meldung oder eine Guru-Meditation. Am Ende dieses Kapitels wird übrigens gezeigt, wie man solche Fehler auffangen kann. 3. Ein Aufruf der RemTask-Funktion, die den Task aus dem Sy¬ stem entfernt. Exec 331 2.6.2 Task-Funktionen Für das Erzeugen und Löschen von Tasks und die Verwaltung der Task-Listen gibt es einige Funktionen im Exec: AddTask Funktion: AcldTask(task, initialPC, finalPC) AO AI A2 Offset: -282 Beschreibung AddTask fügt einen neuen Task zum System hinzu. Parameter lask \ Zeigeriauf eine Task-Struktur. Die Felder Tc_SPUpper, tc_SPLower, tc_SI^eg und tc_Node.ln_Type sollten vorher korrekt initialisiert werden. initialPC Adresse, bei der Abarbeitung des Task-Programms begonnen wird. finalPC Rücksprungadresse, die vor Beginn des Tasks auf den Stack gelegt wird. Führt der Task ein überzähliges RTS aus, wird diese Adresse angesprungen. Ist finalPC auf 0, setzt Exec die Adresse seiner Stan- dard-finalPC-Routine ein. 332 Amiga intern FindTask Funktion: Task = FindTask(Naine) DO A1 Offset: -294 Beschreibung FindTask durchsucht die Task-Listen nach einem Task mit dem ange¬ gebenen Namen und gibt, wenn es einen solchen findet, einen Zeiger auf dessen Struktur zurück. Gibt man 0 als Name an, bekommt man in Task einen Zeiger auf die Task-Struktur des eigenen Tasks. Parameter Name Zeiger auf den Namen des zu suchenden Tasks oder 0. Ergebnis Task Zeiger auf die Task-Struktur des gesuchten Tasks. döitiTa^ Funktion: RemTasktTask) AI Offset:-288 Beschreibung RemTask entfernt einen Task aus dem System. Zeigt das tc_MemEntry-Feld auf eine MemEntry-Liste, wird diese freigegeben. Alle sonst noch vom Task belegten Systemressourcen müssen vorher dem System zurückgegeben werden. Parameter Task Zeiger auf die Task-Struktur des zu entfernenden Tasks. Ist Task = 0, wird der eigene Task gelöscht. Nachdem der eigene Task gelöscht wurde, wird in die Switch-Funktion von Exec verzweigt. Exec 333 SetTaskPrr Funktion: altePriorität = SetTaskPriCTask, neuePriorität) DO A1 DO Offset: -300 Beschreibung SetTaskPri gibt die alte Priorität eines Tasks zurück und setzt sie dann auf den neuen Wert. Dabei wird noch ein sog. RESCHEDULING aus¬ geführt, dies bedeutet, daß die Rechenzeiten der einzelnen Tasks auf Grund der geänderten Priorität neu verteilt werden. Setzt man mit SetTaskPri einen Task auf eine hohe Priorität, bekommt er meistens den Prozessor sofort. Parameter task \ Zeiger auf die Task-Struktur des Tasks. neuePriorität ) NeuexPrior^t des Tasks (in den unteren 8 Bits von DO). Ergebnis altePriorität Alte Priorität des Tasks (in den unteren 8 Bits von DO). Forbid Funktion: ForbidO Offset: -132 Beschreibung Verbieten des Task-Switching und erhöhen des TDNestCnt. 334 Amiga intern Pormit Funktion: PertnitO Offset: -138 Beschreibung Herunterzählen des TDNestCnt und Task-Switching wieder erlauben, wenn TDNestCnt < 0 ist. DIsabl« Funktion: DisableO Offset: -120 Beschreibung Verbieten aller Interrupts und Erhöhen des IDNestCnt. EnaNa Funktion: EnableO Offset: -126 Beschreibung Herunterzählen des IDNestCnt und Interrupts wieder erlauben, wenn IDNestCnt < 0 ist. 2.6.3 Kommunikation zwischen Tasks Nicht jeder Task ist dafür geschaffen, sein Dasein als Einzelgänger zu fristen. Er will Daten mit anderen Tasks austauschen. Meistens handelt es sich dabei um Ein-/Ausgabe-Vorgänge, denn die Routinen, die die verschiedenen Ein- und Ausgabegeräte wie Tastatur, Bildschirm oder Diskette steuern, sind als eigene Tasks ausgelegt. Es wurde ja schon erwähnt, daß manche Tasks nur auf Signale ande¬ rer Tasks warten, um in Aktion zu treten. Und diese Signale sind auch das Thema des nächsten Abschnitts. Exec 335 2.6.3.1 Die Task-Signale Jeder Task besitzt 32 Signal-Bits, damit ist er in der Lage, verschie¬ dene Ereignisse zu unterscheiden. Jeder Task kann diese Signale belie¬ big verwenden. Ein bestimmtes Signal mag für zwei verschiedene Tasks eine völlig andere Bedeutung haben. Allerdings benutzen einige Systemfunktionen, z.B. aus Intuition, bestimmte Signale für ihre Mit¬ teilungen. Will ein Task eines seiner Signale benutzen, muß er es daher zuerst besetzen. Dies geschieht mit der AllocSignal-Funktion. Norma¬ lerweise sind die unteren 16 Bits für Systemfunktionen reserviert, da¬ mit bleiben noch 16 weitere für die freie Benutzung übrig. Man kann mit AllocSignal() |ein bestimmtes Signal besetzen, indem man die Nummer des gewirschten Signals als Argument übergibt, oder man läßt AllocSignal selbst das nächste freie Signal suchen, in¬ dem man -1 angibt Als Ergebnis erhält man immer die Nummer des gewünschten Signals, falls es vorher noch nicht besetzt war, oder aber -1 als Fehlermeldung. AllocSignal(-l) gibt nur -1 zurück, wenn überhaupt kein Signal mehr frei war. Das folgende kleine C-Programm besetzt das nächstbeste freie Signal mit der AllocSignal-Funktion: S{gnal=AUocSignaU-1L); SignaUO ? printfCKeine freien Signale mehr vorhanden!"): printfC'Das Signal Nunner Xld wurde besetzt!”,(long)Signal); Es gibt zwei Möglichkeiten, ein bestimmtes Signal anzugeben: Entwe¬ der man gibt seine Nummer an, dies ist eine Zahl zwischen 0 und 31, die der Bit-Nummer des entsprechenden Signals entspricht, oder man gibt das gesamte Signallangwort an. In dieser sogenannte Signalmaske spiegelt der Zustand eines Bits dann den des entsprechenden Signals wider. Der Vorteil bei der Angabe der gewünschten Signale in Form einer Signalmaske ist, daß man mehrere Signale auf einmal wählen kann. AllocSignal gibt die Signalnummer zurück. Um von ihr zur Si¬ gnalmaske zu kommen, muß man das Bit an der durch die Signal¬ nummer bezeichneten Bit-Posiiton setzen. Dies kann in C folgender¬ maßen geschehen: Signalmaske=1«Sign8lnuniner; In Maschinensprache genügt: 336 Amiga intern HOVE.U Signalnuimer.DO MOVE.L Signalmaske,D1 BSET DO,Dl wobei Signalmaske und Signalnummer jeweils die Adresse des jeweili¬ gen Wertes darstellen (nicht den Wert selbst). Die besetzten Signale eines Tasks werden im tc_SigAlloc-Feld der Task-Struktur in Form einer Signalmaske gespeichert. Warten auf Signale Die wesentliche Funktion eines Signals ist, daß ein Task darauf warten kann. Dieser Ausspruch hört sich seltsam an, ist aber korrekt. Nehmen wir an, ein Task wartet auf eine bestimmte Taste. Prinzipiell könnte er dies in Form einer Schleife tun. Dann würde er aber unnötig Rechenzeit verbrauchen, ohne tatsächlich etwas getan zu haben. Um dies zu verhindern, kann ein Task auf ein Ereignis, z.B. eine Taste, mit Hilfe der Task-Signale warten. Jeder Task kann auf seine Signale warten. Während er wartet, kommt er in die schon er¬ wähnte WAITING-Liste und benötigt so keine Rechenzeit. Um einen Task auf eines seiner Signale warten zu lassen, gibt es die Wait-Funktion. Sie benötigt nur ein einzigen Parameter, nämlich eine Signalmaske, die alle Signale enthält, auf die gewartet werden soll. Es ist somit möglich, auf mehrere Signale gleichzeitig zu warten. Sobald eines der angegebenen Signale von einem anderen Task gesetzt wird, kehrt die Wait-Funktion zurück und übergibt als Ergebnis eine Si¬ gnalmaske, die das (oder auch die) aufgetretene(n) Signal(e) enthält. Mittels AND-Verknüpfungen zwischen den gewünschten Signalen und der von Wait() zurückgegebenen Signalmaske kann man feststellen, welches der Signale, auf die man gewartet hat, tatsächlich aufgetreten ist. Das folgende hypothetische Beispielprogramm in C soll dies demon¬ strieren: unsigned long Signale; Signale=Uait(TastejMausknopf|Henue); iftsignale & TasteK iftSignale & Mausknopfx; iftsignale & Henue){ /• Taste gedrückt •/ > /• Mausknopf gedrückt •/ > /• Henuepunkt aktiviert •/ > 337 Exec Ist eines der gewünschten Sigale schon vor dem Aufruf gesetzt, kehrt Wait() sofort ins Programm zurück. Die Sigale, auf die ein Task war¬ tet, werden in seinem tc_SigWait-Feld gespeichert, diejenigen, die er empfangen hat, in tc_SigRecvd. Hat man vor dem Aufruf einer Wait-Funktion das Task-Switching oder die Interrupts abgeschaltet, werden sie durch die Wait-Funktion wieder erlaubt. Erst wenn Wait() wieder ins Programm zurückkehrt, wird der verbotene Zustand erneut hergestellt. Bewerkstelligt wird dies, indem bei jeinem Wait()-Aufruf die Inhalte der beiden Zähler TDNestCnt und lÖNestCnt in die zugehörigen Fel¬ der der Task-Struktur übeiftragen^erden: tc_TDNestCnt und tc_IDNestCnt. Es gibt fünf wesentliche Routinen im Zusammenhang mit den Tasksi¬ gnalen. Wobei SetSignalsO vom normalen Anwender nicht benötigt wird. Die Signal-Funktionen ÄllocSIgnai Funktion: Signalnunner= AUocSignaUSignalnunmer} DO DO Offset: -330 Beschreibung Mittels der AllocSignal-Funktion kann man eines der Task-Signale besetzen. Gibt man statt der Nummer des zu besetzenden Signals -1 an, sucht AllocSignal das nächste freie Signal und belegt dieses. Ist das gewünschte Signal schon besetzt, gibt AllocSignal -1 zurück. AllocSignal läßt sich nur auf die Signale des eigenen Tasks anwenden und sollte nicht innerhalb einer Exception aufgerufen werden. 338 Amiga intern Parameter Signalnummer Nummer des zu besetzenden Signals (0 - 31) oder -1 für das nächste freie Signal. Ergebnis Signalnummer Nummer des besetzten Signales oder -1, wenn das gewünschte Signal (oder alle Signale bei AllocSignal(-l)) schon belegt war. FroeSignal Funktion: FreeSignaUSignalnuimer) DO Offset: -336 Beschreibung FreeSignalO ist die Umkehrfunktion zu AllocSignal. Das Signal mit der angegebenen Signalnummer wird freigegeben. Wie AllocSignal() sollte FreeSignalO nicht aus einer Exception heraus aufgerufen werden. Parameter Signalnummer Nummer des freizugebenden Signals (0-31). SelSIgnaia Funktion: AlteSignale = SetSignalstMeueSignale,Maske) DO 00 01 Offset: -306 Beschreibung SetSignalsO überträgt den Zustand derjenigen Signale, deren zugehö¬ rige Bits in der Maske gesetzt sind, aus NeueSignale in die Task-Si¬ gnale (tc_SigRecvd). Ist ein Bit in NeueSignale und der Maske gleich 1, wird das zugehörige Task-Signal gesetzt. Ist es in NeueSignale 0 339 Exec und die Maske I, wird es gelöscht. Ist ein Masken-Bit = 0, bleibt das entsprechende Task-Signal unbeeinflußt. Parameter NeueSignale Enthält den neuen Zustand det Signi Maske Bestimmt, welche Signal-Bits geändert werden. Ergebnis AlteSignale Gibt den alten Zustand der Task-Signale wieder. Set Signal s( OL.OL ) Liefert die Signale ohne sie zu verändern. Signal Funktion; SignaUTask, Signale) AI DO Offset: -324 Beschreibung Mit dieser Funktion kann man die Signale eines anderen Tasks setzen. Diese Funktion ist die Kernfunktion des Signalsystems, denn mit ihr ist die Signalübermittlung von Task zu Task möglich. Hat der Emp¬ fänger-Task auf eines der gesendeten Signale gewartet, kehrt er wie¬ der zum Ready- oder Running-State zurück. Diese Funktion wird hauptsächlich vom Message-System benutzt, das im nächsten Kapitel besprochen wird. Parameter Task Zeiger auf die Task-Struktur des Empfänger-Tasks. 340 Amiga intern Signale Signalmaske, die die zu übermittelnden Signal-Bits enthält. Walt Funktion: Signale^ Uait(Signalmaske) DO DO Offset: -318 Beschreibung Wait wartet auf die Signale in der angegebenen Signalmaske. Dies be¬ deutet, daß der Task solange in den Waiting-State versetzt wird, bis eines der Signale von einem anderen Task oder einem Interrupt gesetzt wird. War eines der Signale schon vor Aufruf der Wait-Funktion ge¬ setzt, kehrt Wait sofort wieder ins Programm zurück. Als Ergebnis übergibt Wait eine Signalmaske, die alle aufgetretenen Signale enthält, auf die gewartet wurde. Achtung; Die Funktion darf nur im USER-Mode aufgerufen werden. Parameter Signalmaske Auf die Signale in dieser Signalmaske wird gewartet. Ergebnis Signale Die Signale aus der Signalmaske, die empfangen wurden. 2.6.3.2 Das Message-System Die Task-Signale bilden die Grundlage für ein weiteres Kommunikati¬ onssystem zwischen Tasks, dem sogenannten Message-System. Dieses erlaubt nicht nur die Übergabe von Signalen, sondern auch das Senden und Empfangen von Mitteilungen, die beliebige Daten enthalten kön¬ nen. Auch die automatische Bildung von Warteschlangen, falls der Empfänger nicht schnell genug auf eine Nachricht reagieren kann, ist bei diesem System implementiert. Als Basis für diese Art der Kommu- Exec 341 nikation dient ein sogenannter MeSsage-Port. Dies ist wieder eine Da¬ tenstruktur. In C hat sie folgendes Format (exec/ports.h): /•(14) Flags für Aktionsmodus */ /•{15) Signal-Bit des Tasks */ /•{16) Zeiger auf Empfänger-Task •/ /•{20) Listenkopf der Hessage-List*/ Ein Message-Port ist eine Sammelstelle für Nachrichten (engl. Messa¬ ges) an einen Task (oder Software-Interrupt). Jeder Task kann Daten an einen Message-Port senden, aber nur ein Task wird von den ange¬ kommenen Mitteilungen informiert. Die Felder einer Message-Port-Struktur haben folgende Bedeutung: struct HsgPort { struct Node mp Ndde; UBYTE mp FlagsJ V ^ UBYTE mp_SigBit; struct Task *mp_SigTask; struct List mp MsgList; >; mp_Node Node-Struktur, wie sie in Kapitel 1 vorgestellt wurde. In ihrem ln_Name-Feld wird wieder ein Zeiger auf den Namen des Message- Ports gespeichert. Dies erleichtert das Auffinden eines bestimmten Message-Ports. Der Node-Typ in ln_Typ ist bei einem Message-Port immer NT_MSGP0RT. Die anderen Felder der Node-Struktur werden nur verwendet, wenn man einen Message-Port in eine Liste aufnehmen will. Dies kann entweder eine private Liste sein oder die Liste der sog. Public-Ports. Dies ist die Liste aller Message-Ports, die Exec bekannt sind. mp_Flags Die unteren beiden Bits in diesem Feld bestimmen, was passiert, wenn der Message-Port eine Nachricht empfängt. Es stehen folgende Mög¬ lichkeiten zur Auswahl (die entsprechenden Bit-Kombinationen sind ebenfalls im Includefile "exec/ports.h" enthalten): PAJGNORE (2) Diese Kombination der Flag-Bits bestimmt, daß gar nichts geschieht, wenn eine Nachricht empfangen wird. 342 Amiga intern PA_SIGNAL (0) Jedesmal, wenn der Message-Port eine Nachricht empfängt, wird das Signal aus dem Feld mp_SigBit an den Ziel-Task gesandt. PA_SOFTINT (1) Bei jeder empfangenen Nachricht wird ein Software-Interrupt ausge¬ löst. Näheres über Software-Interrupts können Sie im zugehörigen Ka¬ pitel erfahren. mp_SigBit In diesem Feld ist die Nummer des Signal-Bits gespeichert, das an den Task gesendet wird, wenn mp_Flags = PA_SIGNAL ist. Es handelt sich dabei um eine Signalnummer zwischen 0 und 31, es kann immer nur ein Signal-Bit von einem Message-Port beeinflußt werden. mp_SigTask Dieses Feld muß einen Zeiger auf die Task-Struktur des Tasks ent¬ halten, dem das Signal in mp_SigBit übermittelt werden soll. Ist als Modus PA_SOFTINT gewählt, enthält mp_SigTask statt dessen einen Zeiger auf die Interrupt-Struktur des entsprechenden Software- Interrupts. mp_MsgList Dies ist der Listenkopf für die Liste aller eingetroffenen Nachrichten (Messages). Jede empfangene Nachricht wird an das Ende dieser Liste angehängt, und je nach Modus in mp_Flags löst dies entweder nichts (PA_IGNORE), einen Software-Interrupt (PA SOFTINT) oder ein Signal an den Empfänger-Task (PA_SIGNAL) aus. Dieser Listenkopf muß entsprechend initialisiert werden. Dies kann z.B. mit NewList() geschehen. Aufbau einer Nachricht (Message) Jede Nachricht besteht aus einer Message-Struktur und einer beliebi¬ gen Nachricht, deren Länge maximal 64 KB betragen darf. Die Nach¬ richt wird direkt an die Message-Struktur angehängt. Diese Struktur ist ebenfalls im Include-File "exec/ports.h" untergebracht: 343 Exec - struct Message { / struct Nod3 ran_Nod^ struct MsgPöt^^jp^plyPort; /* (U) Antwort-Port */ UUORD mn_Lengiti; /• <18) Länge der Message in Bytes */ mn_Node Normale Node-Struktur. Sie dient dazu, die Message in die Liste der empfangenen Messages des Message-Ports einzugliedern. Das ln_Typ- Feld ist auf den Node-Typ NT_MESSAGE zu setzen. Ob man seiner Message einen Namen geben will, bleibt einem selbst überlassen. mn_Length Länge der Nachricht in Bytes. Wie schon erwähnt, schließt diese sich direkt an das mn_Length-Feld an. Senden einer Nachricht Eine Nachricht wird mittels der PutMsg-Funktion an einen beliebigen Message-Port gesendet. PutMsgtMessageport, message) Sendet die Message zum Message-Port. Beide müssen als Zeiger auf die zugehörigen Strukturen angegeben werden. Folgendes Beispiel sen¬ det einen Text als Message zu einem hypothetischen Message-Port; t extern APTR Port; /• Zeiger auf den Message-Port */ static char text[] = "Dies ist eine Beispielnachricht! static struct < struct Message msg; char inhaltCsizeofttext)]; > nachricht; nachricht.msg.iin_Node. ln Type=NT_MESSAGE; strcpy(nachricht.inhalt,text); PutMsg(Port,&nachricht}; > Beim Senden einer Message wird diese lediglich an die Liste der einpfangenen Messages, die mp_MsgList, angehängt. Dies geschieht wie üblich mit den ln_Succ- und ln_Pred-Feldern in der Node- Struktur der Message, mn_Node. Die Message wird nicht kopiert! Dies 344 Amiga intern bedeutet, daß die gesamte Message weiterhin Teil des Tasks ist, der sie gesendet hat. Das Senden einer Message erlaubt also dem Empfän- ger-Task, einen Speicherbereich des Sender-Tasks zu benutzen. Empfangen einer Nachricht Das Empfangen von Nachrichten besteht gewöhnlich aus zwei Schrit¬ ten. Erst wartet man auf das Signal des Message-Ports, und wenn dieser signalisiert, daß er eine Nachricht empfangen hat, holt man sie dort ab. Vorausgesetzt, daß der Message-Port ordentlich initialisiert ist, gibt es zwei Möglichkeiten für einen Task, auf die Ankunft einer Message an dem Message-Port zu warten: Entweder verwendet er die Wait()-Funktion oder WaitPort(). message = UaitPort(Port); Diese Funktion wartet auf die Ankunft einer Message am Message- Port "Port". Sind dort schon eine oder mehr Messages vorhanden, kehrt WaitPortO gleich ins Programm zurück. Ansonsten versetzt sie, wie die Wait-Funktion, den Task in den Waiting-State, bis eine Message an¬ kommt. Als Ergebnis erhält der Task bei WaitPortO einen Zeiger auf die Message-Struktur der ersten Message, die Message wird aber nicht vom Message-Port entfernt. Wann soll man Waitf) oder WaitPortO verwenden? WaitO hat den Vorteil, daß es damit möglich ist, auf mehrere Signale zu warten. Damit ist diese Funktion in Fällen, in denen man auf mehrere Ereignisse warten will, am geeignetsten. Will man aber wirk¬ lich nur auf eine Nachricht an einem bestimmten Message-Port war¬ ten, ist WaitPortO günstiger. Denn diese Funktion wartet ja nur, wenn der Message-Port leer ist. WaitO hingegen richtet sich nach den Si¬ gnalen. Hat beispielsweise der Message-Port schon zwei Messages vor dem WaitO empfangen, tritt folgendes Problem auf: Der erste WaitO-Aufruf kehrt sofort zurück, da die beiden Messages schon das entsprechende Signal gesetzt haben. Will man jetzt mit ei¬ nem WaitO auf die nächste Nachricht warten, kann dies zu einem Warten ohne Ende werden, denn die gewünschte Message ist ja schon lange vorher angekommen. Die Ursache dieses Problems liegt darin. Exec 345 -L / daß ein Signal nur einmal gesetzt wird, auch wenn mehrere Messages angekommen sind. Man muß daher vor dem zweiten Wait() testen, ob die nächste Nachricht nicht doch schon angekommen ist. Aus diesem Grund ist es besser, WaitPort() zu verwenden, wenn man nur auf einen Message-Port wartet. Um eine Message vom Port zu holen gibt es die GetMsg-Funktion; message = GetMsg(Port); Diese Funktion holt die erste Nachricht vom angegebenen Port und übergibt einen Zeiger auf ihre Message-Struktur. Die Message wird dann aus der Liste des Message-Ports entfernt. GetMsgO holt die Message an der ersten Position in dieser Liste. Da neue Messages hin¬ ten angehängt werden, stellt die Liste der empfangenen Messages eine sog. FIFO-Warteschlange dar. FIFO heißt "First In First Out" und be¬ deutet, das Element, das zuerst in die Liste aufgenommen wurde, wird auch zuerst wieder ausgegeben. Also kann man mit GetMsgO der Reihe nach alle empfangenen Messa¬ ges vom Port holen. Sind keine weiteren Messages mehr vorhanden, kehrt GetMsgO mit einer 0 zurück. Folgendes Beispiel benutzt WaitPortO und GetMsgO, um eine Nach¬ richt aus einem hypothetischen Port zu holen: extern struct MsgPort «Port; struct Message «GetMsgO; int Signal; if((signal=AllocSignaU-lL}}<0} fprintfCKeine freien Signale mehr übrig! ");return(0);> Port->mp_Flags=PA_SIGNAL; Port->mp_SigBit=signal; Port->mp_SigTask=FindTask(0); /• Eigener Task */ WaitPort(Port); message = GetMsg(Port); Antworten auf eine Nachricht Hat ein Task eine Nachricht ausgesandt, will er natürlich auch wissen, ob sie angekommen ist. Grund dafür ist, daß die gesamte Message einschließlich der Message-Struktur, ja weiterhin zu seinem Task ge¬ hört. Indem er sie sendet, gibt er dem Empfänger die Erlaubnis, den 346 Amiga intern Speicherbereich, in dem die Message steht, auszulesen. Außerdem kann der Empfänger noch irgendwelche Rückmeldungen oder Ergeb¬ nisse in der Message unterbringen. Da der Sender den Speicherbereich meistens wieder für andere Zwecke, z.B. eine neue Nachricht, ver¬ wenden will, muß er wissen, wann der Empfänger die Nachricht empfangen und verarbeitet hat. Er kann sie ja nicht einfach löschen, ohne zu wissen, ob sie schon gelesen wurde. Aus diesem Grund wurde ein sogenannter Reply-Port (Antwort-Port) eingerichtet. Ein Reply- Port kann ein beliebiger Port des Sender-Tasks sein. Um dem Emp¬ fänger die Adresse dieses Ports mitzuteilen, gibt es noch ein Feld in der Message-Struktur, das wir noch nicht erwähnt haben: mn_ReplyPort Enthält die Adresse der Reply-Ports des Sender-Tasks. Um eine Nachricht zu beantworten, sendet der Empfänger sie einfach an den Reply-Port zurück, nachdem er sie gelesen und eventuell verändert hat. Der Sender weiß damit, daß die Message empfangen und bear¬ beitet wurde. Er kann jetzt den Speicher der Message neu verwenden oder einfach dem System zurückgeben. ReplyMsg( message); Erledigt dieses Rücksenden der Message. Schaffen neuer Message-Ports Um einen Message-Port zu schaffen, muß man eigentlich nur eine korrekt initialisierte Message-Port-Struktur im Speicher unterbringen. Am einfachsten geschieht dies durch eine C-Struktur mit der Spei¬ cherklasse static. Allerdings kann man sich auch den nötigen Speicher mittels AllocMemO vom System holen und initialisieren. Hat man eine fertige Message-Port-Struktur, muß man sich noch ent¬ scheiden, ob man sie zur Liste der öffentlichen Message-Ports hinzufügen will. Dies kann mit der AddPort-Funktion geschehen. Diese Funktion übernimmt auch das Initialisieren des mp_MsgList- Felds der Message-Struktur und macht so einen Aufruf von NewList() überflüssig. AddPort benötigt als einzigen Parameter die Adresse der Message-Struktur. Der Vorteil eines Message-Ports in der Liste der öffentlichen Ports ist, daß ein anderer Task diesen Port schnell anhand seines Namens auffinden kann. Fügt man einen Port nicht zu dieser Exec - 347 Liste hinzu, muß man jedem Task, der mit ihm kommunizieren soll, die Adresse des Message-Ports mitteilen. Zum Auffinden eines Ports in der Liste anhand seines Namens gibt es die FindPort-Funktion. Port=FindPort( Name ); Sucht einen Message-Port mit dem angegebenen Namen und übergibt, falls er ihn findet, dessen Adresse. Fügt man einen Port mit AddPort() zum System hinzu, sollte man vorher überprüfen, ob nicht schon ein Port dieses Namens vorhanden ist. Benötigt man einen Port nicht mehr, kann man ihn einfach löschen, nachdem man auf alle noch ausstehenden Messages gewartet und diese mit ReplyMsgO bantwortet hat. Gehört der Message-Port zu den öffentlichen Ports, muß man ihn mit RemPort(Port) aus dem System entfernen, bevor man ihn löscht (bzw. seinen Speicher wieder freigibt). Um das Erstellen neuer Message-Ports zu vereinfachen, gibt es die CreatePort-Funktion. Diese Funktion gehört nicht zum Betriebssystem, sondern findet sich in der Runtime-Library eines Amiga-C-Compilers (amiga.lib). Ihr C-Source-Code lautet wie folgt: #include extern APTR AUocHemO; extern UBYTE AUocSignaU); extern struct Task »FindTaskO; struct MsgPort »CreatePort (Name, Pri) char *Naine; BYTE Pri; { BYTE Signal; struct MsgPort »Port; if ((Signal=AUocSignaU-1))==-1) returnt(struct MsgPort *)0); Port=AUocMem((ULONG)sizeof(*Port),MEMF_CLEAR|MEMF_PUBLIC); if (Port==0) { FreeSignal (Signal); return((struct MsgPort *)0); 348 Amiga intern Port->mp_Node.ln_Name = Name; Port->mp_Node.ln_Pri = Pri; Port->mp_Node.ln_Type = NT_MSGPORT; Port->n\p_Flags = PA_SIGNAL; Port->mp_SigBit = Signal; Port->mp~SigTask = FindTask(O); if (name != 0) AddPort(Port); eise NeuList (&(Port->mp_MsgL{st)); return(Port); > Diese Funktion schafft einen Message-Port mit dem angegebenen Na¬ men und Priorität. Als Ergebnis erhält man die Adresse des neuen Ports oder 0, wenn kein Speicher oder kein Signal mehr frei war. Ist der Zeiger auf den Namen ungleich null, wird der Port der Liste der öffentlichen Ports hinzugefügt. Mit dieser Funktion kann man z.B schnell und einfach einen ReplyPort aufbauen. Auch zum Löschen eines Ports gibt es eine Funktion im amiga.lib: OeletePort(Port) struct MsgPort ‘Port; { if ((Port->inp_Nocle.ln_Naine) != 0) ReraPort(Port); Port->rap_Node.ln_Type = OxFF; Port->mp_MsgList.lfi_Head=(struct Node •)-1; FreeSignal(Port->inp_SigBit); FreeMemtPort,(ULONG) sizeof(«Port)>; > DeletePortO löscht den angegebenen Port. Hat er einen Namen, wird er auch aus der Liste der öffentlichen Ports gelöscht. Task-Exceptions (Ausnahmezustände) Es ist manchmal unbefriedigend, daß der Task beim Warten auf ein Signal nicht weiterlaufen kann. Nehmen wir einmal an, ein Task zeichnet eine mathematische Funktion. Da dies sehr lange dauert, soll die Möglichkeit bestehen, den Zeichenvorgang durch Tastendruck abzubrechen. Dieser soll über einen Message-Port übermittelt werden. Nun müßte man innerhalb der Zeichenschleife ständig die Signale te- — Exjic 349 sten, ob der Port eine Message empfangen hat. Damit würde man aber die Zeichenschleife verlangsamen. Mit diesem unbefriedigenden Zu¬ stand braucht man sich aber beim Amiga nicht abzufinden. Es gibt da nämlich die sogenannte Task-Exception. Dies ist ein Ausnahmezustand des Tasks, der, wie könnte es anders sein, von einem Signal ausgelöst wird. Ähnlich einem Interrupt wird der Task durch das Auftreten eines Si¬ gnals unterbrochen. Dies kann an jeder beliebigen Stelle geschehen. Dann wird der sogenannte Exception-Handler aufgerufen. Dieser ist Teil des Ursprungs-Tasks und hat deshalb Zugriff aud dessen Daten (vorausgesetzt, sie sind nicht lokal). In unserem Beispiel könnte er die Schleifenvariable auf den Endwert setzen und damit das Zeichnen beenden. In Maschinensprache hat man noch mehr Möglichkeiten. Man kann z.B. den Task direkt manipulieren. Um eine Task-Exception zu ermöglichen, sind folgende Schritte not¬ wendig; 1. Die Startadresse des Exception-Handler muß im zugehörigen Feld der Task-Struktur, tc_ExceptCode, untergebracht werden. Es besteht die Möglichkeit, auch noch einen Zeiger auf gemein¬ same Daten in tc_ExceptData zu schreiben. 2. Es muß festgelegt werden, welche Signale eine Exception auslö- sen dürfen. Das tc_SigExcept-Feld in der Task-Struktur be¬ stimmt dies. Jedes dort gesetzte Signal löst eine Exception aus, wenn es vom Task empfangen wird. Um das Setzen und Löschen der Bits in diesem Feld zu vereinfachen, gibt es eine spezielle Funktion: SetExcept. Ihre genaue Beschreibung findet man in der Funktionsübersicht am Ende dieses Kapitels. Tritt eine Exception auf, legt Exec zuerst die aktuellen Inhalte der Prozessorregister (PC, SR, D0-D7 und A0-A6) auf den Task-Stack, um eine störungsfreie Fortsetzung des Tasks nach dem Ende der Ex¬ ception zu ermöglichen. Dann kommt eine Signalmaske in DO, die alle aufgetretenen Excep- tion-Signale enthält. Die Adresse im tc_ExceptData-Feld wird nach Al kopiert. Anschließend beginnt die Abarbeitung des Exception-Co- des an der Adresse in tc_ExceptCode. Am Ende einer Exception muß ein RTS stehen. Dann holt Exec den alten Registerinhalt wieder vom Stack und fährt mit dem Task fort. 350 Amiga intern Während einer Exception verhindert Exec das Auftreten weiterer Ex- ceptions. Um nach dem Ende einer Exception ihr erneutes Auftreten zu erlauben, muß man in DO denselben Wert zurückgeben, der einem dort am Anfang übergeben wurde. D.h. man ändert einfach nicht den Inhalt von DO. Tritt während einer Exception ein Exception-auslösendes Signal auf, wird sie nach dem Ende der gerade ablaufenden erneut gestartet. War ein Signal schon gesetzt, bevor man ihm mittels SetExcept er¬ laubte, eine Exception auszulösen, tritt diese sofort auf. Prozessor-Traps Eine andere Art von Ausnahmezuständen sind die Traps. Mit ihnen sind bestimmte Ausnahmezustände des 68000er-Prozessors gemeint. Diese werden bei Motorola (dies ist die Herstellerfirma des 68000) Exceptions genannt, was man nicht mit oben beschriebenen Task-Ex- ceptions verwechseln darf. Folgende 68000-Exceptions werden von Exec als Traps bezeichnet: Traps: 2 Busfehler (Bus error) 3 Adreßfehler (Adress error) 4 Illegaler Befehl (Illegal instruction) 6 Division durch null (Zero divide) 6 CHK-Befehl (CHK instruction) 7 TRAPV-Befehl (TRAPV instruction 8 Privilegverletsung (Privilege violation) 0 Ablaufverfolgung (Trace) 10 Befehl mit 1010 am Anfang (Line 1010 emulator) 11 Befehl mit 1111 am Anfang (Line 1111 emulator) 32-37 Trap-Befehle (Trap instructions) Ein Trap ist immer eine direkte Folge einer Anweisung im Programm. Dies kann entweder gewollt (CHK, TRAP, TRAPV, 1010, 1111 oder Trace) oder aufgrund eines Programmfehlers (Bus- oder Adreßfehler, Division durch null oder Privilegverletzung) passieren. Tritt also ein Prozessor-Trap auf, springt Exec in einen sogenannten Trap-Handler. Die Adresse dieses Händlers wird aus dem tc TrapCode-Feld geholt. Normalerweise befindet sich dort ein Zeiger auT den Standard-Trap-Handler von Exec. Dieser erzeugt den (leider) nur allzu bekannten "Software Error - Task held -Reguester oder gar eine Guru-Meditation. Exec 351 Man kann aber die Adresse in tc_TrapCode auf einen eigenen Händ¬ ler umbiegen. Dieser Händler kann dann entweder auf alle Traps rea¬ gieren oder nur einige bestimmte behandeln und ansonsten in den Standard-Handler weiterspringen. Ein Trap-Handler wird beinahe direkt angesprungen. Exec legt ledig¬ lich noch die Trap-Nummer (siehe obige Liste) auf den Stack. Dies hat folgende Auswirkungen: Erstens befindet man sich im Supervisormodus und arbeitet mit dem Supervisorstack! Während des Ablaufs des Trap-Handler ist daher das Task-Switching gesperrt. Zweitens kann der Inhalt des Stacks variie¬ ren. Normalerweise hat er folgendes Format: Stackpointer (SSP) Trap-Nummer (Langwort) Stackpointer +4 Statusregister (Wort) Stackpointer *2 Rücksprungadresse (Langwort) Bei einem Adreß- oder Busfehler werden aber weitere Informationen auf den Stack gelegt. Obendrein sind diese bei neueren Vertretern der 68xxx-Familie, wie dem 68010 und 68020, wieder anders. Um voll¬ ständige Kompatibilität zu wahren, muß man also einiges beachten. Am besten nehmen Sie ein gutes 68000-Buch zu Hilfe, um alle Mög¬ lichkeiten einzuschließen. Oder man springt bei einem Adreß- bzw. Busfehler in den Trap- Handler von Exec, da bei diesen Ausnahmen meist doch nichts mehr zu machen ist. Um den Trap-Handler wieder zu verlassen, nimmt man die Trap- Nummer vom Stack (Achtung: Langwort) und verläßt ihn mit einem RTE-Befehl. Da keine zusätzlichen Register von Exec auf dem Stack gesichert werden, darf man den Inhalt der Prozessorregister nicht dauerhaft verändern! Folgendes Beispiel verwendet einen Trap-Handler zum Auffangen ei¬ nes "Division durch nulP-Fehlers. Da man einen Trap-Handler schlecht in C schreiben kann, wurde er mittels #asm und #endasm direkt in Maschinensprache in den Source-Code integriert. Da nicht alle C-Compiler diese Preprozessoranweisungen kennen (das Programm wurde mit Aztec C erstellt), muß man bei anderen Produkten C- und Maschinenspracheteil getrennt compilieren bzw. assemblieren und an¬ schließend gemeinsam linken. 352 Amiga intern /*••• Auffangen einer 68000er-Exception ****/ #include extern struct ExecBase *SysBase; mainO < /••• Hinzufügen des Trap-Handlers •••/ extern APTR Trap; APTR oldtrap; USHORT zahl1,zahl2; struct Task *ThisTask; oldtrap=SysBase->ThisTask->tc_T rapCode; SysBase->ThisT ask->tc_T rapCode=&T rap; SysBase->ThisTask->tc_TrapOata=(APTR)0; /*•* Auslösen eines “Division by zero“ Traps •••/ zahll = 10; zshl2 = 0; zahll = zahl1/zahl2; if((ULONG)SysBase->ThisTask->tc_TrapOata==0) printfC'Oiese Stelle wird nie erreicht!"); eise printf ("Exception erkannt, da tc_TrapOata = Trap-Nunnier:Xld\n", SysBase->ThisTask->tc_TrapÖata); /•*• Trap-Handler wieder entfernen •••/ SysBaseT>ThisTask->tc_TrapCode=oldtrap; > /*•* Trap-Handler **•/ /•• Mur mit Aztec-C sicher lauffaehig **/ /•• TAB vor Opcode ist notwendig! **/ #ssm Trap move.l a0,-(sp) move.l 4,a0 ;SysBase move.l 276(a0),a0 ;SysBase->ThisTask move.l 4(sp),46(a0) .-Trap-Munmer nach ;SysBasc->ThisTask->tc_TrapOata move.l (sp),a0 add.l #8,sp rte #endasm Ohne den Trap-Handler würde dieses Programm mit einem "Software Error - Task held" abstürzen, da zahll durch 0 dividiert wird. Als Beweis dafür, daß dies wirklich einen Trap ausgelöst hat, dient der if- Exec 353 Befehl. Denn nur der Trap kann tc_TrapData ungleich 0 setzen, nachdem es gerade vorher vom T^Jc^elöscht wurde. Aber direkt nach der illegalen Division springt Ex€c in dfen Trap-Handler. Dieser nimmt die Trap-Nummer vom Sufiervisor-Stkck und schreibt sie in das tc_TrapData-Feld. Daher g^bt der printf()-Aufruf auch die Zahl 5 auf dem Bildschirm aus, denn dies ist die Trap-Nummer einer Divi¬ sion durch null. Die Trap-Befehle Auch ein Trap, der durch einen der 16 Trap-Befehle ausgelöst wurde (Trap-Nummern 32 bis 47), springt in den Trap-Handler. Allerdings gibt es bei den Traps die Möglichkeit, bestimmte Trap-Nummern vor¬ her zu belegen, ähnlich wie bei den Signalen mittels einer AllocTrap- und einer FreeTrap-Funktion (genaue Erklärung siehe anschließende Funktionsliste). Das Belegen und Freigeben von Traps dient aber lediglich der Ordung, damit immer klar ist, welche Traps gerade benutzt werden und welche nicht. Tritt ein Trap-Befehl auf, wird immer in den aktuellen Trap- Handler verzweigt, unabhängig davon, ob ein Trap-Befehl mit Al- locTrap belegt wurde oder nicht. Funktionen des Message-Systems, der Traps und Exceptions AddPort Funktion: AddPortCPort) A1 Offset: -354 Beschreibung AddPortO fügt die angegebene Message-Portstruktur in die Liste der öffentlichen Ports ein. Sie wird dort nach ihrer Priorität eingereiht. Der Listheader dieser Liste kann über SysBase->PortList angesprochen werden. AddPortO initialisiert auch die mp_MsgList Struktur inner¬ halb des Message-Ports. 354 Amiga intern Parameter Port Zeiger auf eine Message-Portstruktur. AI)oeTra|i Funktion: Trap-Mirmer = AUocTrap(Trap-Muimer) 00 DO Offset: -342 Beschreibung AllocTrap besetzt einen der Trap-Befehle des 68000. Man kann als Trap-Nummer eine Zahl zwischen 0 und 15 angeben, um den ent¬ sprechenden Trap-Befehl zu besetzen, oder -1, dann sucht AllocTrap den nächsten freien Trap-Befehl. Parameter Trap-Nummer Zahl zwischen 0 und 15 für einen bestimmten Trap-Befehl, oder -1 für den ersten unbesetzten. Ergebnis Trap-Nummer Enthält den tatsächlich besetzten Trap-Befehl oder -1 als Zeichen dafür, daß der gewünschte Trap-Befehl nicht frei war bzw. daß über¬ haupt kein Trap-Befehl mehr frei war. FlndPorl Funktion: Port = FindPorttMsme) DO A1 Offset: -390 Beschreibung FindPortO sucht den nächsten Message-Port mit dem angegebenen Na¬ men in der Liste der öffentlichen Ports und gibt, falls ein Port mit dem angegebenen Namen existiert, einen Zeiger auf den Port zurück. Parameter Name Name des zu suchenden Ports. Ergebnis Port Zeiger auf einen Message-Port mit dem angegebenen Namen oder null, wenn ein solcher nicht existiert. FreeTrap Funktion: FreeTrap(Trap-NLiniier) DO Offset: -348 Beschreibung FreeTrap gibt den Trap-Befehl mit angegebener Nummer wieder frei. Parameter Trap-Nummer Nummer des Trap-Befehls (0 bis 15). FutMsg Funktion: PutMsg(Port,inessage) AO AI Offset: -366 Beschreibung PutMsg sendet eine Nachricht an einen Message-Port. Dort wird sie an die Liste der empfangenen Nachrichten angehängt und die dem Inhalt des mp_Flags-Feld entsprechende Aktion wird ausgelöst. 356 Amiga intern Parameter Port Adresse der Message-Portstruktur des Ziel-Ports. message Zeiger auf die Message-Struktur der Message. Funktion: Re»rPort $«tixeept Funktion: AlteS1gnale= SetExcepKNeueSignale, Maske) DO DO D1 Offset: -312 Beschreibung SetExcept bestimmt, welche Signale eine Exception auslösen können. Das genaue Verhalten dieser Funkiton entspricht demjenigen von Set- Signals(). Parameter NeueSignale Die neuen Zustände der Exception-Signale. Maske Bestimmt, welche Exception-Signale beeinflußt werden sollen. Ergebnis AlteSignale Zustände der Exception-Signale vor Änderung, WaitPort Funktion: message = UaitPorttPort) AO Offset: -384 Beschreibung WaitPort wartet auf den Empfang einer Message an einem bestimmten Port. Trifft dort eine Message ein oder war schon vor dem Aufruf von 358 Amiga intern WaitPortO eine Message vorhanden, kehrt WaitPort mit der Adresse dieser Message zurück. Die Message wird nicht aus der Liste der emp¬ fangenen Messages gelöscht. Dazu muß GetMsgO verwendet werden. Parameter Port Adresse des Ports. Ergebnis message Zeiger auf erste Message in der Liste. 2.7 Speicherverwaltung des Amiga Der Amiga verfügt über eine dynamische' Speicherverwaltung, was be¬ deutet, daß Bildschirmspeicher, Diskspeicher usw. sowie die zu laden¬ den Programme in keinem bestimmten, sich nie ändernden Speicher¬ bereich geladen werden, sondern bei jedem Laden einen anderen Be¬ reich zugewiesen bekommen können. Diese dynamische Speicherver¬ waltung macht es möglich, daß mehrere Programme gleichzeitig im Speicher abgearbeitet werden können, da kein Programm auf einen bestimmten Speicherbereich angewiesen ist, wie es bei anderen Com¬ putern, beispielsweise dem C64, der Fall ist. Dem System muß lediglich mitgeteilt werden, daß Speicher gebraucht wird, worauf dieses ihn, sofern er noch frei ist, dem Anwender zu¬ weist. Vom System wird nicht gemerkt, welches Programm (Task) welchen Speicher belegt hat, sondern nur vermerkt, daß er für andere Tasks nicht mehr zur Verfügung steht. Um genau zu sein, wird nicht vermerkt, welcher Speicher nicht erreichbar, sondern umgekehrt, welcher noch erreichbar ist. Wenn ein Task einen bestimmten Speicherbereich nicht mehr benötigt, sollte er dies dem System mitteilen, damit der Speicher einem anderen Task, sofern er ihn benötigt, zugewiesen werden kann. Wird der nicht benötigte Speicher nicht an das System zurückgegeben, bleibt er be¬ legt, bis ein Reset erfolgt. Die Folge ist natürlich eine drastische Ver¬ ringerung der Speicherkapazität. Wenn Speicher belegt werderi soll, ist dies nur in 8-Byte-Schritten möglich. Anderenfalls rundet aas System die angegebene Speicher¬ größe bis zum nächsten 8-Byte-Schritt auf. Somit ergibt sich eine mi¬ nimale Bereichzuweisung von acht Bytes. Zum Belegen und Freigeben des gewünschten Speicherbereichs exi¬ stieren in der Exec-Library mehrere Funktionen, von denen zwei am häufigsten Verwendung finden. Die Funktionen sind AllocMem() und FreeMem(). Bei der Belegung von Speicher müssen Sie dem System mitteilen, welche Art von Speicher Sie zugewiesen haben möchten und ob dieser noch besondere Eigenschaften haben soll. Diese Bedingungen, die gewählt werden können, sind: MEMFjCHIP Der zuzuweisende Speicherbereich muß Chip-Memory sein. Chip- Memory ist der untere 512-KB-Bereich, der von den Chips wie dem Blitter angesprochen werden kann. Grafik, Sound usw. muß in diesem Bereich abgelegt werden. Auch wenn Sie zur Zeit nur 512 KB Spei¬ cher besitzen und somit diese Bedingung immer erfüllt sein muß, sollten Sie aus Kompatibilitätsgründen zu Geräten mit größerem Speicher nicht auf die genaue Bestimmung verzichten. Der Code für diese Bedingung ist $02 und wird in C, wie auch die anderen Bedin¬ gungen im Memory-Include-File, definiert. MEMF_FAST Der zuzuweisende Speicher muß sich außerhalb der unteren 512 KB befinden. Die Zuweisung führt jedoch nur mit der Verwendung einer RAM-Erweiterung zu einem positiven Ergebnis. Der Code der Bedin¬ gung ist $04. MEMF_PUBLIC Der zuzuweisende Speicher wird für Strukturen benötigt, die von mehreren Tasks verwendet werden (z.B. Messages und Packets). Der Code für diese Bedingung ist $01. 360 Amiga intern MEMFCLEAR Der zuzuweisende Speicher soll für die Übergabe mit Nullen gelöscht werden. Der Code hierfür ist $10000. MEMF_LARGEST Der zuzuweisende Speicher soll der größte zur Verfügung stehende Speicherblock sein. Der Code hierfür ist $20000. Sollen mehrere Bedingungen wie Chip und Clear erfüllt sein, so müs¬ sen die Codes miteinander ODER-verknüpft werden. Sollte weder die Angabe Chip- oder Fast-Memory erfolgt sein, wird zuerst versucht, Fast-Memory zu belegen. Erst nach dem Mißlingen des Versuchs wird Chip-Memory belegt. Vorsicht: Es sollte vermieden werden, aus einem Interrupt Speicher zu belegen oder wieder zurückzugeben, da die Routinen des Amiga, die für diese Tätigkeiten gedacht sind, die Interrupts nicht sperren. Sollte sich ein Task gerade in einer Routine zur Speicherverwaltung befinden und von einem Interrupt unterbrochen werden, der ebenfalls mit den Speicherroutinen arbeitet, so kann das System in große Schwierigkeiten geraten. Das gleiche gilt auch für den Aufruf von Routinen aus einem Interrupt heraus, die nicht unterbrochen werden dürfen, jedoch nicht die Interrupts abschalten. 2.7.1 Die Funktionen AllocMemO und FreeMemQ Aiiodiliöm Funktion: Speicher = AllocMemCSpeichergöBe, Bedingungen) DO DO D1 Offset: -198 Beschreibung Die Funktion sucht einen freien Speicherbereich, der den angegebenen Bedingungen entspricht und kennzeichnet ihn als belegt. In DO wird die Anfangsadresse des gefundenen Speichers angegeben. Sollte es Exec 361 nicht möglich sein, den gewünschten Speicher zu belegen, wird in DO als Fehlermeldung eine Null übergeben. Speichergröße Gibt an, wie groß der Speicher ist, der zugewiesen werden soll. Bedingungen Sind die zuvor beschriebenen Bedingungen, die der AllocMem()- Funktion übergeben werden und nach denen der Speicherbereich aus¬ gesucht wird (z.B. Chip-Memory). Mit der AllocMem()-Funktion kann man sich keinen zuvor genau be¬ stimmten Speicherbereich zuweisen lassen, sondern bekommt einen gerade zur Verfügung stehenden zugewiesen. Wie es eine Funktion zum Zuweisen und somit zum Belegen des Speichers gibt, existiert ebenso eine Funktion zum Freigeben des Speichers. FrdoMöm Funktion: FreeMemtSpeicherblock, GröBe) AI DO Offset: -210 Beschreibung Die Funktion gibt den zuvor belegten Speicher wieder dem System zurück und ermöglicht somit eine erneute Belegung durch andere Tasks. Die übernommenen Parameter werden gerundet. Speicherblock Zeiger auf den Anfang des Speicherbereichs, den man dem System zurückzugeben wünscht. Der Zeiger wird auf das nächste Vielfache von 8 abgerundet. Größe Gibt an, wieviel Speicher man freizugeben gedenkt. Die Größe wird auf das nächste Vielfache von 8 aufgerundet. 362 Amiga intern Vorsicht Sollte versucht werden, bereits freien Speicher erneut als "frei" zu kennzeichnen, führt dies zu einem Absturz mit der Guru-Nummer; 81000009 Das folgende C-Programm zeigt, wie man sich Speicher zuweisen und wieder freigeben lassen kann. #include #include #define SIZE 1000 mainO ULONG Speicher; Speicher = AllocMem (SIZE,MEHF_CHIP | HEHF_PUBLIC>; if (Speicher = 0) { printf <"\n Speicher nicht erreichbar\n"); exit <0); > printf <"\n Speicher zugewiesen\n''); FreeMem (SIZE,Speicher); ) In "Speicher" wird die Anfangsadresse des Speichers vermerkt, der zu- gewiesen werden konnte. 2.7.2 Die Memory-List-Struktur Oft ist es nötig, mehrere verschiedene Speicherbereiche zu belegen. Zu diesem Zweck könnte man für jeden einzelnen Bereich jedesmal die AllocMem()-Funktion aufrufen. Sie sehen sicher ein, daß dies ein recht aufwendiges Unterfangen ist. Aus diesem Grund stellt die Exec- Library zwei Funktionen zur Verfügung, die uns diese Aufgabe er¬ leichtern. Die Funktionen heißen AllocEntry() und FreeEntry(). Um eine der Funktionen aufrufen zu können, muß zuvor, wie könnte es beim Amiga auch anders sein, eine Struktur initialiliert werden, aus der sich die Funktionen ihre Werte nehmen. Die Struktur heißt MemList und sieht wie folgt aus: Exec 363 struct HemList { 0 struct Node inl_Node; 14 UUORD ml_NLiiCntries; 16 struct MemEntry ml_ME >; ml_Node Node-Stniktur, um mehrere MemList-Strukturen miteinander ver¬ knüpfen zu können. ml_NumEntries Gibt an, wie viele Speicherbereiche man zu belegen gedenkt. ml_ME[I] Eigene Struktur, in der eingetragen wird, welche Bedingungen der zu reservierende Speicher hat und wie groß der Speicherbereich sein soll. Die Struktur hat folgendes Aussehen; struct MeitEntry < Union < ULONG meu.Reqs; APTR meu_Addr; ) me_Un; ULONG me Length; >; #define me_un me_Un #define me_Reqs me_Un.meu_Reqs #define me Addr me Un.meu Addr me_Un Ist aufgrund der union-Bedingung geteilt. Zum einen können in me_Un die Bedingungen (Requests) für die Speicherzuweisung ent¬ halten sein, andererseits den Zeiger auf den reservierten Speicher ent¬ halten. Bei der Erstellung der MemEntry-Struktur zum Aufruf der AllocEntryO-Funktion befinden sich hier die Bedingungen (z.B. MEMF_CHIP) für die Speicherzuweisung, nach dem Aufruf der Funktion jedoch die Anfangsadresse des zugewiesenen Speichers. me_Length Länge des zuzuweisenden Speichers an. 364 Amiga intern AllooEnfry Funktion: Liste = AUocEntry(HetnList) DO AO Offset: -222 Beschreibung Der Funktion wird ein Zeiger auf eine MemList-Struktur in AO über¬ geben. Die Funktion versucht nun, alle in der Struktur eingetragenen Speicherbereiche zu belegen. Ist ein Bereich belegt, wird an der Stelle, an der sich zuvor die Bedingungen (Requirements) befanden, der Zei¬ ger auf den gefundenen Speicher abgelegt. Liste Zeiger auf die neuerstellte MemList-Struktur. Sollte ein Fehler wäh¬ rend der Zuweisung aufgetreten sein, wird in DO die Bedingung des nicht erreichbaren Speichers zurückgegeben, wobei das oberste Bit (Bit 31) gesetzt ist. Es wird in dem Fall kein Speicher belegt, auch wenn andere Entries hätten ausgeführt werden können. FraeEntry Funktion: FreeEntry(Liste) AO Offset: -228 Beschreibung Die Funktion gibt alle in der MemList-Struktur vermerkten Speicher¬ bereiche dem System zurück. Weder mit der AllocEntryO- noch mit der FreeEntryO-Funktion ist es möglich, mehrere verkettete MemList- Strukturen gleichzeitig zu bearbeiten. Parameter Liste Zeiger auf die MemList-Struktur, die von der AllocEntry()-Funktion übergeben wurde. Sie werden sich vielleicht fragen, wie es möglich ist, mehrere Entries mit einer MemList-Struktur zu initialisieren, da doch in der MemList- Struktur eindeutig nur eine MemEntry-Struktur eingebunden ist 365 Exec (ml_Me[l]). Um mehrere Entries zu initialisieren, muß zu einem Trick gegriffen werden. Man muß eine'^^ene Struktur erstellen, die beispielsweise folgendes Aussehen hat: ( \ struct { struct Henlist me_Kopf; struct HemEntry me_Zusat 2 [3]; } HeineListe; Statt des Zeigers auf eine MemList-Struktur übergibt man der Alloc- Entry-Funktion jetzt den Zeiger auf seine eigene initialisierte Struk¬ tur, mit der es möglich ist, mehrere Entries zu initialisieren. In dem soeben gegebenen Beispiel werden vier Speicherbereiche von der Al- locEntry-Funktion belegt, da die MemList-Struktur ja noch über eine eigene MemEntry-Struktur verfügt. Sehen wir uns die Verwendung der AllocEntry()-Funktion einmal an¬ hand eines Beispiels an: #include #include struct Henliste { struct Menlist me_Kopf; struct HenCntry ine_Zusatz [2]; >; mainO struct MemList »Speicherliste, »AUocEntryO; struct MemListe HeineListe; HeineListe.ine_Kopf .inl_NunEntries = 3; MeineListe.nie~Kopf,ml~me[0] .iiie_Reqs = MEMF_CLEAR; HeineListe.me_Kopf .nil_nie[0] .ine_Length = 100; HeineListe.me~Kopf.nil_me[1].iiie_Reqs = HEHF_CLEAR j HEHF_FAST; HeineListe.ine_Kopf.nil_me[1] .ine_Length = 19Ö0; HeineListe.me“Kopf.ml“me[2].me~Reqs = HEHF_PUBLIC | HEHFCHIP; HeineListe.me_Kopf.ml_nie[2] .iiie_Length = 300; Speicherliste = AllocEntry (SHeineListe); if { ((ULONG)Speicherliste) » 30) t printf{"\n Nicht alle Einträge konnten mit Erfolg zugewiesen werden\n''); exit (0); >; > 366 Amiga intern 2.7.3 Speicherzuweisung und Tasks Wenn von einem Task aus Speicher belegt werden soll, so empfiehlt sich, dies mit der AllocEntry()-Funktion zu machen. Der Grund hierfür ist, daß in der Task-Struktur eine List-Struktur eingebunden ist (tc_MemEntry), in die der belegte Speicher in Form von MemList- Struktur in einer Liste verknüpft werden kann. Der Speicher, der in diesen Listen verknüpft ist, kann dann leicht von Ihrer Routine, die den Task auflöst, wieder an das System zurückge¬ geben werden. Ein weiterer Vorteil beim Einträgen des verwendeten Speichers in die Liste ist, daß der Task jederzeit nachsehen kann, welche Bereiche er belegt hat. Es ist natürlich auch möglich, einen Speicherbereich mit der Alloc- Mem()-Funktion zu belegen und diesen daraufhin in eine solche Liste einzutragen. 2.7.4 Die interne Verwaitung des Speichers Nachdem jetzt bekannt sein dürfte, wie man für seine Zwecke Spei¬ cherplatz reserviert, wollen wir uns einmal ansehen, wie der Speicher intern verwaltet wird. Wie man sich denken kann, wird die Verwaltung des Speichers wieder über eine Struktur erledigt. Diese Struktur sieht wie folgt aus: struct HemHeader { 0 struct Node mh_Node; 14 UUORD mh_Attributes; 16 struct HemChunk •mh_First; 20 APTR inh_Lower; 24 APTR mh_Upper; 28 ULONG inh_Free; >; #define HEHF_PUBLIC <1L«0) #definc HEHF_CHIP <1L«1) #dcfinc HEHF_FAST <1L«2) #definc HEHF CLEAR <1L«16) #define HEHf“largest <1L«17) #define MEH_BLOCKSIZE 8L #define HEH BLOCKHASK 7L Exec 367 mh_Node Node-Struktur, um die verknüpfen zu können. MemHeader-Struktur einer List-Struktur mh_Attnbutes Bedingungen der verwalteten Speichers, z.B. MEMF_FAST. *mh_First Zeiger auf die erste MemChunk-Struktur. Aufbau und Sinn dieser Struktur werden noch besprochen. mh_Lower Zeiger auf den Speicheranfang, der von dem Header verwaltet wird. mh_Upper Zeiger auf das Speicherende, das von dem Header verwaltet wird. mh_Free Speicher, der durch diesen Header erreichbar ist. Wie schon gesagt wurde, ist dem System nicht bekannt, welcher Spei¬ cher belegt, sondern nur, welcher Speicher unbelegt ist. Die unbeleg¬ ten Speicherbereiche sind mit Hilfe einer MemChunk-Struktur mit¬ einander verknüpft. Mit diesen Strukturen läßt sich problemlos die Größe sowie die Position der freien Speicherblöcke bestimmen. Die Struktur hat folgendes Aussehen; struct HenChunk { 0 struct HenChunk *mc_Next; 4 ULONG mc_Bytes; >; *mc_Next Zeiger auf die nächste MemChunk-Struktur. 368 Amiga intern mc_Bytes Anzahl Bytes, die in diesem Speicherblock frei sind. Der mh_First-Eintrag in der MemHeader-Struktur zeigt auf die erste MemChunk-Struktur, die am Anfang des ersten freien Speicherblocks steht. Die ersten vier Bytes dieses freien Blocks sind somit der Zeiger auf den nächsten freien Speicherbereich (auf die nächste MemChunk- Struktur). Die nächsten vier Bytes geben an, wie groß der hiesige Speicherbereich ist. In der letzten MemChunk-Struktur ist der mc_Next-Zeiger auf Null gestellt, und mc_Bytes gibt somit an, wie¬ viel Bytes noch zwischen der jetzigen Speicherposition und dem Wert, der in mh_Upper vermerkt ist, liegen. Eine MemHeader-Struktur verwaltet das gesamte Chip-Memory und eine weitere das Fast-Memory, sofern vorhanden. Diese Strukturen sind in einer Liste verkettet, die in der ExecBase-Struktur enthalten ist. Die Liste hat den Namem "MemList” und befindet sich bei Offset 322. Die MemHeader-Priorität für den Fast-Memory-Bereich ist Null, und die Priorität für den Chip-Memory-Bereich -10, was auch der Grund dafür ist, daß versucht wird, immer zuerst Fast-Memory zu belegen und danach Chip-Memory. Soll jetzt Speicher belegt werden, so wird in der MemListe der ExecBase-Struktur nachgesehen, ob die angegebenen Bedingungen in der AllocMemO-Funktion auch den Bedingungen der MemHeader- Struktur entsprechen. Als zweites wird geprüft, ob der freie Speicher, der in mh_Free vermerkt ist, ausreicht, damit die angegebene Spei¬ chermenge reserviert werden kann. Sollte eine der beiden Bedingungen nicht erfüllt sein, so wird nachgesehen, ob noch eine weitere Mem- Head-Struktur vorhanden ist. Wenn keine weitere erreichbar sein sollte, wird eine negative Rückmeldung von der AllocMem()-Funktion übergeben. Ansonsten wird der Zeiger mh_First geholt und nachgese¬ hen, ob der in der ersten MemChunk-Struktur angegebene Speicher ausreicht. Wenn nicht, wird zur nächsten MemChunk-Struktur (zum nächsten freien Speicherblock) verzweigt und wiederum verglichen. Wenn kein genügend großer, zusammenhängender Speicher erreichbar ist, wird eine negative Rückmeldung übergeben. Wird hingegen ein ausreichend großer Speicherbereich gefunden, wird errechnet, wieviel von dem freien Speicherblock übrigbleibt, und an der Position, an der der freie Speicher wieder beginnt, wird eine MemChunk-Struktur ein¬ gefügt und diese entsprechend verkettet. Der neubelegte Bereich wird Exec 369 aus der Verkettung entfernt und die Anzahl der belegten Bytes von der Gesamtanzahl abgezogen. \ / 2.7.5 Die Allocate-, Deallocate- und AddMemList-Funktion Es besteht die Möglichkeit, eine eigene MemHead-Struktur zu erstel¬ len und einen separaten Speicherbereich selbständig mit den Funktio¬ nen AllocateO und Deallocate() zu verwalten. Mit den Funktionen kann man jedoch nur Speicher belegen und wieder freigeben und hat nicht die Möglichkeit, irgendwelche Bedingungen anzugeben. AUocate Funktion: Speicher = Allocate (MemHeader.ByteSize) DO AO DO Offset: -186 Beschreibung Die Funktion belegt den angegebenen Speicher, der von der angegebe¬ nen MemHeader-Struktur verwaltet wird. MemHeader Zeiger auf eine MemHeader-Struktur. ByteSize Gibt an, wieviel Speicher belegt werden soll. Speicher Zeiger auf den belegten Speicher. Sollte kein Speicher gefunden wer¬ den, wird eine Null zurückgegeben. 370 Amiga intern D#alloeate Funktion: Deallocate (MemHeader,Speicher,ByteSize); AO AI DO Offset: -192 Beschreibung Die Funktion gibt die belegten Speicher wieder an die MemHeader- Struktur zurück. MemHeader Zeiger auf die MemHeader-Struktur. Speicher Zeiger auf den Anfang des freizugebenden Speichers. ByteSize Gibt an, wieviel Speicher freigegeben werden soll. AddMemUat Funktion: error » AdtSiemList (size.req.pri.basis.name) QO DO Dl D2 AO Al Offset: -618, -$26A, $FD96 Beschreibung Die Funktion erstellt einen Memory-Header und fügt diesen in die Exec-Memory-Liste ein. Parameter size Größe des zu verwaltenden Speichers. reg Angabe, welcher Speichertyp über die MemHeader-Struktur verwaltet werden soll. Beispiel: Exec 371 MEMF_PUBL ICI MEMF_FAST I Pri . J Gibt an, welche Priorität die Strt^ktur Mben soll. Speicher, der über eine MemHeader-Struktur mit hoheT^riorität verwaltet wird, wird vor dem mit niedriger Priorität belegt. Fast-Memory hat, sofern seine MemHeader-Struktur vom Betriebssystem erstellt wurde, die Priorität Null, Chip-Memory die Priorität -10. basis Zeiger auf den Anfang des Speichers. name Zeiger auf Namen des Speichers (z.B. hyprafast.memory). 2.7.6 Beschreibung der restlichen Funktionen AvailMom Funktion: AvailMem (Bedingungen) Dl Offset: -216 Beschreibung Die Funktion gibt die Größe des Speichers bezüglich der Bedingungen an, Z.B. MEMF CHIP. AlIocAbs Funktion: Speicher = AUocAbs (ByteSize,Position) DO DO AI Offset: -204 Beschreibung Die Funktion ermöglicht die Belegung eines bestimmten Speicherbe¬ reichs, der nicht von Exec gesucht wird, sondern vom Programmierer angegeben werden kann. 372 Amiga intern Bytes ize Größe des zu belegenden Speichers. Position Zeiger auf den zu belegenden Speicher. Speicher Zeiger auf den belegten Speicher, der auch dem angegebenen ent¬ spricht. Sollte es nicht möglich sein, den gewünschten Speicher zu be¬ legen, wird eine Null als Fehlermeldung zurückgegeben. 2.8 Der interne Library-Aufbau Die Library-Struktur ist im C-Include-File wie folgt festgelegt: «define LIB VECTSIZE «define L1B~RESERVE0 «define LIB~BASE «define L1B_USERDEF «define LIB NONSTD 6L 4L (-LIB VECTSIZE) (LIB MSE- (LIB RESERVED*LIB VECTSIZE)) (lib"userdef) «define LIB_OPEN (-6L) «define LIB_CLOSE <-12L) «define LIB_EXPUNGE (-18L) «define LIB_EXTFUNC (-24L) «define LIBF_SUMHING (1L«0) «define LI BF CHANGED (1L«1) «define LIBF“sUHUSED (1L«2) «define LIBF_OELEXP (1L«3) extern struct Library ( 0 struct Node lib_Node; 14 UBYTE lib_FLags; 15 UBYTE lib_pad; 16 UUORO lib_NegSize; 18 UUORO lib_PosSize; 20 UUORO lib_Version; 22 UUORO lib_Revision; 24 APTR lib_ldstring; 28 ULONG lib_SLin; 32 UUORO lib_OpenCnt; >; Erklärung der Einträge der Struktur: 373 Exec Lib_Node Struktur der Sorte Node, wie wir sie sbhon kennengelernt haben. Mit Hilfe dieser Struktur sind die Libraries in einer Liste verkettet. Der Typ der Library ist in diesem Fall n^ürlich NT_LIBRARY, und der Name der Node gibt den bfamen dej^^ibrary an. lib_Flags lib _pad Unbenutztes Byte, um die folgenden Worte und Langworte der Struk¬ tur wieder auf gerade Adressen zu bringen. lib_NegSize Bereich für die negativen Offsets. Lib_PosSize Gibt an, wie groß der Bereich der Library von der Basisadresse an ist. Dieser Wert sowie die Angabe über die Größe des negativen Bereichs ist für die Entfernung der Library aus dem System wichtig, da so die Länge derselben festgestellt werden kann. Lib_yersion Library-Version. Lib_Revision Überarbeitung der Library. Lib_IdString Zeiger auf Text, der mehr Informationen über die Library enthält. Lib_Summ Prüfsumme über die Library. Wenn Sie die Library ändern, sollten Sie die Prüfsumme neu berechnen. 374 Amiga intern Lib_OpenCnt Gibt an, von wie vielen Tasks die Library geöffnet ist. Es kommt auf den Typ der Library an, ob sie, wenn kein Task auf diese Library zu¬ greifen will, entfernt wird. Handelt es sich um eine von Disk geladene Library, besteht die Möglichkeit, sie wieder zu entfernen. Die Funktionen, die eine Library dem Benutzer zu Verfügung stellt, beginnen mit Offset -30, obwohl sie theoretisch schon bei -6 beginnen könnten. Wenn man sich die ersten Offsets einmal genauer ansieht, stellt man fest, daß sie auf Funktionen zeigen, die von Exec benutzt werden, um die Library zu verwalten. Hierbei handelt es sich um Funktionen, die zum öffnen und Schließen der Library gebraucht werden und von den entsprechenden Exec- Funktionen angesprungen werden. Jede Library hat somit ihre eigene öffnungs- und Schließroutine. In diesen eigenen Routinen wird auch entschieden, ob die Library, wenn sie von keinem Task mehr benötigt wird, entfernt werden darf oder nicht. Wie auch aus den Defines des Includefiles erkennbar ist, haben die besagten vier Offsets folgende Bedeutung: LlB OPEN -6 Library öffnen LIB~CLOSE -12 Library schließen LIB~EXPUNGE -18 Library entfernen LIB EXTFUNC -24 Offen rär Erweiterungen Die Libraries, die nicht entfernt werden, wenn sie zur Zeit nicht mehr benötigt werden, benutzen den Einsprung LIB_EXPUNGE nicht. Alle nicht verwendeten Einsprünge sollten auf eine Routine zeigen, die DO löscht und daraufhin wieder zurückkommt. 2.8.1 Andern einer bestehenden Library Zum Ändern einer bestehenden Library existiert in der Exec-Library eine Funktion, mit der sich bestimmte Offset-Einsprünge modifizieren sollen. Die Funktion sieht wie folgt aus: Exec 375 SetFunktion Funktion: SetFunktion (Library, Offyet, Einsprung} A1 ÄO DO Offset: -420 Beschreibung Die Funktion verändert den Einsprung der mit einem negativen Offset ausgesuchten Funktion so, daß nun der Einsprung der Funktion auf die neue Routine zeigt. Prüfsumme der Library wird neu berechnet. Library Zeiger auf die zu ändernde Library. Offset Offset der Funktion, die geändert werden soll. Einsprung Zeiger auf die eigene Routine. 2.8.2 Das Erstellen einer eigenen Library Nachdem besprochen wurde, wie man Libraries nutzt, soll nun gezeigt werden, wie man sich seine eigene Library erstellt. Das Erstellen einer eigenen Library ist dann sinnvoll, wenn mehrere parallel arbeitende Tasks gebraucht werden, die gemeinsam eine An¬ zahl von Funktionen benutzen, welche von einem der Tasks zur Ver¬ fügung gestellt werden. Es ist auch ratsam, eine eigene Library zu er¬ stellen, in der einige Funktionen enthalten sind, die immer wieder ge¬ braucht werden. Wenn eine solche eigene Library einmal besteht, kann sie nach Bedarf geöffnet werden, um ihre Funktionen zu benutzen. Zu diesem Zweck muß sie sich jedoch im LIBS-Ordner der Systemdiskette befinden. Zum Aufbau einer Library stellt die Exec-Library einige Funktionen zur Verfügung, die vor der Erstellung der eigenen Library erklärt werden sollten. 376 Amiga intern intfSiruot Funktion: InitStructCinitTabelle, Speicher, GröBe) AI A2 DO Offset; -78 Beschreibung Die Funktion initialisiert eine Struktur ab dem angegebenen Speicher¬ bereich nach einer angegebenen Tabelle. initTabelle Zeiger auf die Tabelle, mit der eine Struktur erstellt wird. Speicher Zeiger auf den bereits zugewiesenen Speicher. Größe Größe der zu initialisierenden Struktur. Der Speicher, in der die Struktur erstellt wird, braucht nicht gelöscht worden zu sein, denn dies wird von der InitStruct-Funktion übernommen. Eine Tabelle, anhand der die Struktur erstellt wird, hat ein etwas ver¬ wirrendes Aussehen. Sie besteht aus einem Befehls-Byte, worauf Daten folgen, die je nach Aussehen des Befehls-Bytes verschieden verarbei¬ tet werden. Nach den Daten folgt wieder ein Befehls-Byte und Daten. Die Länge der Daten hängt wieder von Befehl-Byte ab. Die Tabelle ist zu Ende, wenn das Befehls-Byte den Wert Null hat. Das Befehls-Byte ist in High- und Low-Nibble unterteilt. Die Bit- Kombinationen des High-Nibbles (die oberen vier Bits) geben den Befehl an und das Low-Nibble die Anzahl der Befehlsausführungen. Wenden wir uns zuerst dem High-Nibble zu. Dieses ist wiederum in die zwei oberen und unteren Bits eingeteilt. Die obersten zwei Bits ge¬ ben den eigentlichen Befehl und die unteren zwei des High-Nibbles die Datengröße an, mit der operiert werden soll. Als Datengröße steht Langwort, Wort oder Byte zur Verfügung. Mit den obersten zwei Bits lassen sich vier verschiedene Befehle ko¬ dieren: 377 Kombination: 00 l Die Kombination gibt an, daß die Daten, die nacK dem Befehls-Byte beginnen, in die zu erstellende Struktur übertragenwverden. Was für Daten verarbeitet werden (Langwort, Wort oder Byte), wird in den nachfolgenden zwei Bits entschieden. Kombination: Öl Die Kombination gibt an, daß das Datum der angegebenen Länge entsprechend oft in die zu erstellende Struktur kopiert wird. Kombination: 10 Die Kombination gibt an, daß das nach dem Befehlswort stehende Byte als Offset für die zu erstellende Struktur dient. Der Offset wird zur Startadresse der Struktur addiert und die nach diesem Byte stehen¬ den Daten werden in die zu erstellende Struktur kopiert. Kombination: 11 Die Kombination gibt an, daß die drei Bytes nach dem Befehls-Byte als 24-Bit-Offset verwendet werden sollen. Ansonsten ist dieser Befehl mit dem vorherigen identisch. Die nächsten zwei Bits des Befehls-Bytes (die Bits 4 und 5) geben an, um welchen Datentyp es sich handelt. Korbination: 00 Korbination: 01 Korbination; 10 Korbination: 11 Languort (nur auf geraden Adressen) Wort (nur auf geraden Adressen) Byte Nicht verwenden, sonst Absturz Das Low-Nibble des Befehl-Bytes gibt an, wie oft eine bestimmte Funktion ausgeführt werden soll, sie dient als Zähler. Da der Zähler bis -1 heruntergezählt wird, wird die Funktion somit einmal mehr ausgeführt als im Zähler angegeben. Das Befehl-Byte darf immer nur auf einer geraden Adresse liegen. Zur Verdeutlichung zwei Beispiele: dc.b X00010010,$00 dc.u $FFFF,$FFFF,$1234 378 Amiga intern Das Befehls-Byte ist $12 = %00010010 und legt somit fest, daß die folgenden drei Worte in die Struktur kopiert werden. Das Null-Byte nach dem Befehls-Byte ist notwendig, da die Worte nur auf einer ge¬ raden Adresse, beginnen dürfen. dc.b X10000001,(10 dc.l $12341234,(ffff1111 Das Befehls-Byte gibt an, daß zwei Langworte in die Struktur ab Po¬ sition 16 kopiert werden. Sie werden sicher zustimmen, daß das Erstellen einer solchen Tabelle mehr Zeit in Anspruch nimmt als die Initialisierung einer Struktur "von Hand". Aus diesem Grund werden in den Commodore-Include- Dateien vier sehr hilfreiche Macros zur Verfügung gestellt, die den Aufbau einer solchen Tabelle zum Vergnügen werden lassen. Die Macros sind im File "exec/initializers.i" enthalten. INITBYTE dient zum Einträgen eines Bytes mit dem angegebenen Offset in die Struktur. INITBYTE HACRO * &offset,&value DC.B SeO DC.B 0 DC.U \1 DC.B \2 DC.B 0 ENDM Dieses Macro erfüllt denselben Zweck wie INITBYTE, bezieht sich jedoch auf ein Wort: INITUORD HACRO * &offset,&value DC.B $d0 DC.B 0 DC.W M DC.W \2 ENDM Dieses Macro gleicht dem INITBYTE-Macro, bezieht sich jedoch auf Langworte: IHITLONG MACRO • &offset,&value DC.B $c0 DC.B 0 DC.U M DC.L \2 ENDH 379 Exec Das vierte Macro kann genutzt werden, um mehrere Werte in die Struktur zu kopieren. Es werden die nach dem Befehl stehenden Daten ab dem angegebenen Offset in die Struktur kopiert. Die Anzahl der zu kopierenden Werte wird dem Macro übergeben. Als erstes wird die Größe erlaubten Werte sind: 0 für Langworte 1 für Worte 2 für Bytes der zu bearbeitenden eben. Die Die nächste Angabe bestimmt den Offset, ab der die Eintragungen in die Struktur erfolgen. Von dem Macro wird erkannt, ob es sich hierbei um ein Byte oder 24-Bit-Offset handelt. Den dritten Wert hat man offensichtlich vergessen. Bei diesem Macro muß ein dritter Parameter angegeben werden, auch wenn sein Wert ohne Belang ist (er wird nicht verwendet). Die vierte Übergabe gibt an, wie viele Werte in die Struktur übertra¬ gen werden sollen. Das Maximum ist 15. INITSTRUCT MACRO • Ssize,Soffset,&value,Scount DS.U 0 ITC '\4'," C0UNT\a SET ENDC 0 IFNC ■\4'," C0UNT\a SET ENDC \4 CMD\a SET {{{\1)«4)IC0UNT\a) IFLE (\2)-255 DC.B (CMD\a}!$80 DC.B HEX IT ENDC \2 DC.B CHD\a!$0C0 DC.B (((\2)»16)&$0FF) DC.U ENDM ((\2)&$0FFFF) Das aufwendigere vierte Macro kann wie folgt verwendet werden: INITSTRUCT 1,10,0,5 ds.u 0 dc.w $1111,$2222,$3333,$4444,$5555 INITBYTE .... 380 - - Amiga intern - Programmbeispiel In diesem Beispiel wird der Speicherplatz für eine Library-Struktur reserviert und diese teilweise initialisiert. include "exec/types.i" include "exec/nodes.i" include "exec/libraries.i“ include "exec/initializers.i“ include "exec/memory.i' STRUCTURE MyLib.LIB SIZE STRUCT myl Feld,10 LABEL MyLib_SIZE XREF AbsExecBase XREF “LVOInitStruct XREF _LVOAllocMe=' signalisiert, daß das nachfolgende Register, ;dessen Inhalt beschrieben ist, ein Eingabeparameter ist. ;Oie Zeichen '=>' markieren einen Ausgabeparameter include "exec/types.i" include "exec/initializers.i" include "exec/libraries.i" include "exec/lists.i" include "exec/nodes.i" include "exec/resident.i" include "libraries/dos.i" include "exec/alerts.i" CALLSYS HACRO jsr _LV0\1(a6) ENOM XLIB MACRO XREF _LV0\1 ENDM ;Struktur unserer eigenen Library STRUCTURE HyLib,LIB_SIZE ULONG ml_SysLib ULONG ml_0osLib ULONG ml_SegList 384 Amiga intern UBYTE iiil_Fla9S UBYTE ml_pad LABEL MyLib_Sizeof XLIB OpenLibrary XLIB CloseLibrary XLIB FreeMem XLIB Retnove XLIB Alert Version equ 1 Revision equ 0 Pri equ 0 ;Version der Library ;Überarbeitung der Library ;Pri. der Library (unwichtig) ;Dainit kein Absturz des Systems erfolgt, wenn die Library versehentlich ;als Programm geladen wird, stehen die beiden folgenden Befehle. Start: moveq #0,d0 rts ;Die Resident-Struktur wird von der InitResident-Funktion benutzt, ;um unsere Library aufzubauen. Die Exec-Funktion InitResident wird ;von der Routine zum Laden einer Library aus der RAM-Library aufgerufen. Resident: dc.w RTC_MATCHWORD ;Code für Resident de. l Resident ;Zeiger auf Anfang der Struktur dc.l CodeEnde ;Zeiger auf Ende der Struktur dc.b RTF_AUTOINIT ;Flag für automatischen Aufruf dc.b Version ;Version der Library dc.b NT_LIBRARY ;Typ der Residen-Str. =Library dc.b Pri ;Priorität der Resident-Strukt. dc.l LibName ;Zeiger auf den Namen der Library dc.l idstring ;Erläuterungs-String für Library dc.l Init ;Zeiger auf die Initialisierungs- ;tabelle LibName: dc.b 'test.library',0 idstring: dc.b ■test.library 1. 0 (10 Mai 88)',13,10,0 DosName: dc.b ■dos.library',0 ds.w 0 ;End€ der Resident-Struktur CodeEnde: ;Initialisierungstabelle, die von der InitResedent-Funktion verwendet ;wird, un die entsprechenden Parameter an die MakeLibrary-Funktion ;zu übergeben. Init: dc.l MyLib_Sizeof dc.l FuncTable dc.l DataTable dc.l InitRoutine ;GröBe der Library-Struktur ;Zeiger auf Tabelle der ;Lib-Funktionen ;Zeiger auf Tabelle für InitFunktion ;Zeiger auf die eigene Erstell- Exec 385 ;Routine (Aufruf von MakeLibraryO) FuncTable: -- System Routinen de.l Open de.l Close de.l Expunge de.l Null Eigene Routinen de.l BildBlink de.l LEDBlink Endmarkierung de.l -1 ;Tabelle, die der Funktion InitStruet übergeben wird. ;INITBYTE, INITWORD und INITLONG sind Hacros, die in dem Include^File ;"exee/initiali 2 ers.i" zu finden sind. ;(siehe Besehreibung im Library-Kapitel des Buches) DataTable: INITBYTE LH TYPE,MT_LIBRARY INITLONG LN_NAME,LibName INITBYTE LIB FLAGS.LIBF SUMUSED!LIBF_CHANGED INITWORD LIB_VERSION,Version INITWORD LIB_REVISION,Revision INITLONG LIB_IDSTRIHG,idString de.l 0 ;Die folgende Routine wird von der MakeLibrary-Funktion aufgerufen. ;Sie dient der Initialisierung weiterer Library-Einträge, die nicht ;über den DataTable erfolgen können. ;>= DO = Zeiger auf die Library-Struktur ;>= AD = Zeiger auf die Segment liste der geladenen Library ;>= A6 = Zeiger auf Exeebase ;=> DO = Zeiger auf die Library-Struktur InitRoutine: move.l a5,-{a7) move.l dO,aS move.l a6,ml_SysLib= a 6 = Zeiger auf die eigene Library-Struktur ;>= DO = Zeiger auf die eigene Library-Struktur Open: addq.w #1 ,LIB_0PENC>IT(a6) bclr #LIB8_DELEXP,ml_flags(a6) move.l a6,d0 rts (■Zähler für Anzahl der (■Zugriffe auf die Library (■erhöhen (■Flag für Entfernen (■der Library (■Library löschen (■Rückgabeparameter setzen (■Rücksprung (■Wenn die Library von einem Task nicht mehr gebraucht wird, so (■schließt er sie, um dem Betriebssystem die Möglichkeit zu geben, (■die unbenutzte-Library aus dem System zu entfernen. (■Die Library wird erst entfernt, wenn die AllocMem-Funktion (■feststellt, daß nicht genügend Speicher zur Verfügung steht und ;kein Task auf die Library zugreift. ;Sie können erzwingen, daß die Library entfernt wird, wenn Sie vor :dem Aufruf der CloseLibrary-Funktion das LI88 DELEXP-Flag setzen (■Zeiger auf Segment liste (■löschen (wichtig) ;(Include-File "exec/libraries.i"). Close: clr.l dO subq.w #1,LIB_0PENCNT(a6) bne.s 1$ btst #LIBB_DELEXP,ml_Flags(a5) beq.s 1$ bsr Expunge (■Zähler für Öffnung der (■Library -1 (■Springe, wenn Library (■noch verwendet wird (■Ist das LIBB_DELEXP (■Flag gesetzt? (■Ende, wenn nicht gesetzt (■Library entfernen Exec 387 ’*■ '■*8 .-Rücksprung .-Routine zum Entfernen der Library aus dem Speicher. ;>= A6 = Zeiger auf Library ;=> DO = Zeiger auf Segmentliste der geladenen Library Expunge: movem.l d1/a5-a6,-(a7) move.l a6.a5 move.l ml SysLib(a5).a6 tst.u LIB_OPENCNT(A5) beq IS bset #LIBB_DELEXP.ml_Flags(a5) clr.l dO bra.s Expunge_end IS: move.l ml_SegList(a5),cß move.l aS.ai CALLSYS Remove move.l ml DosLib(a5),a1 CALLSYS cToseLibrary clr.l dO move.l a5,a1 move.u LlB_NEGSIZE(aS}.dO sub.l dO.aT add.w LIB_POSSIZE(a5).dO CALLSYS FreeHem move.l dZ.dO Expunge_end: movem.l (a7)+.d2/a5-a6 rts ;Register retten ;Zeiger auf Library nach A5 .-ExecBase nach A6 ;Wird Library noch gebraucht? .-Springe, wenn nicht gebraucht ;Anzeigen. daB ein Entfernen ;der Library gewünscht ist .-Zeiger auf ;Segmentliste löschen .-Unbedingter Sprung .-Zeiger auf Segment liste .-nach D2 ;Zeiger auf Library nach AI ;Library aus Exec-Lib-Liste ;löschen .-Zeiger auf DOS-Library .-Library schlieBen ;D0 löschen .-Zeiger auf Library ;Zeiger auf Anfang des von .-der Library belegten .-Speichers holen .-Länge des belegten Speichers ;ermitteln .-Speicher frei geben .-Zeiger auf Segment liste .-nach DO .-Register zurückholen ;Rücksprung .-Die folgende Funktion kann mit Offset -24 erreicht werden. Sie .-wird in der jetzigen Kickstart-Version nicht verwendet. Null: moveq #0,d0 ;D0 löschen Ffs ;Rücksprung .-Ab hier beginnen die eigenen Library-Funktionen. .-Die hier abgedruckten Funktionen sind lediglich als Beispiel .-für das Erstellen eigener Libraries gedacht und erfüllen .-keinen besonders nützlichen Zweck ;Blinken des Bildschirms BildBlink: 1$: move.l «SZOOOO.dO move.w d0.$dff180 .-Zähler für Schleife nach DO ;Uert in 388 Amiga intern subq.l #1,dO bne 1$ rts .•Blinken der LED LEDBlink: bchg #1,$bfe001 ;Blinken der LED rts ;Rücksprung END ;Farbregister schreiben ;Zähluert verringern ;Springe, wenn Zähler <> 0 ;Rücksprung Exec 389 2.9 Interne lO-Handhabung auf dem Amiga In diesem Kapitel soll weniger auf die Benutzung der verschiedenen Etevices eingegangen werden, als vielmehr gezeigt werden, wie Exec die IO-Verwaltung vornimmt. Wie man die Ein- und Ausgabesteue- rung des Amiga in seinen eigenen Programmen verwendet, wird im DOS-Kapitel dieses Buches gezeigt. Die Kenntnis dieses Kapitels ist zu empfehlen, um das folgende besser verstehen zu können. 2.9.1 Aufbau der lORequest-Struktur Zum Erledigen von Ein- und Ausgabeprozessen brauchen wir eine lORequest-Struktur, über die wir unsere Befehle an das Device über¬ geben können. Es gibt zwei Arten von lORequest-Stukturen, die sich lORequest und lOStdReq (lO-Standard-Request) nennen. Die lOStdReq-Struktur ist eine Erweiterung der lORequest-Struktur. Die Strukturen haben fol¬ gendes Aussehen: sttuet lORequest { 0 struct Message io_Message; 20 struct Device •io_Device; 24 struct Unit *io_Unit; 28 UUORD io_ConiTiand; 30 UBYTE io_Flags; 31 BYTE io_Error; >; io_Message Eine Message-Struktur, wie sie in Kapitel 2.4 beschrieben wurde. Sie wird gebraucht, damit das Device uns mitteilen kann, daß es mit der Bearbeitung des lO-Commands fertig ist. Die Message-Struktur muß korrekt initialisiert werden, bevor die Ein- und Ausgabe funktionieren kann. *io_Device Zeiger auf die zu benutzende Device-Struktur, die noch beschrieben wird. 390 Amiga intern *io_Unit Zeiger auf eine Unit-Struktur, deren Beschreibung ebenfalls folgt. io_Command Wort, in dem der auszuführende Befehl übergeben wird. io_Flags Wird benötigt, um Device-spezifische Statusmeldungen oder Befehle übergeben zu können. Das Byte ist in High- und Lownibble unterteilt. Die unteren vier Bits werden von Exec für interne Zwecke benutzt. Die oberen vier Bits können vom Programmierer benutzt werden, um mit dem Device zu kommunizieren. io_Error Wird benötigt, um dem Programmierer Fehlermeldungen zu übergeben. Oft reicht diese Struktur nicht aus, um ein Device benutzen zu kön¬ nen. Für den Fall existiert noch eine weitere Struktur, die dem Be¬ nutzer mehr Möglichkeiten bietet. Sie sieht wie folgt aus: struct lOStdReq { struct Message io_Hessage; 20 struct Device ‘io. 24 struct Unit ‘io. 28 UUORD io_Conniand; 30 UBYTE io_Flags; 31 BYTE io_Error; 32 ULONG io_Actual; 36 ULONG io_Length; 40 APTR io_Data; 44 ULONG io_0ffset; >; io_Actual Gibt beispielsweise die Anzahl der tatsächlich übertragenen Bytes an. Der Wert kann erst nach der Beendigung der Übertragung ausgelesen werden. Die Nutzung von io_Actual ist Device-abhängig. Exec 391 io_Length Gibt die Anzahl der zu übertragenden Bytes an. Dieser Wert muß vor der Übertragung initialisiert werden. Oft wird der Wert auf -1 gesetzt, um eine variable Anzahl von Bytes zu übertragen. io_Data Zeiger auf den Datenpuffer, in den die Daten übertragen werden sol¬ len oder aus dem Daten zur Übertragung gelesen werden. io_Offset Gibt den Offset an, der Device-spezifisch benutzt wird. Beim Track¬ disk-Device wird im Offset der Block angegeben, der benutzt werden toll. Der Block wird nicht, wie man vermuten könnte, als Blocknum¬ mer, sondern als Byte-Offset übergeben (Blocknummer * 512). 2.9.2 Aufbau eines Devices Die Device-Struktur hat das Aussehen einer Library: struct Device { struct Library dd_Library; >; #define DEV BEGINIO (-30L) #define DEV~ABORTIO (-36L) #define IOB_OUICK OL #define 10 f“quICK (1L«0) #define CMD_INVALID OL #define CMD_RESET 1L #define CMD_READ 2L #define CMD URITE 3L #define CMd“update 4L #define CMD_CLEAR 5L #define CMD_STOP 6L #define CMD START 7L #define CMD~FLUSH 8L #define CMD NONSTD 9L Um ein Device benutzen zu können, müssen Sie es zuvor öffnen. Der Befehl zum Öffnen eines Devices lautet: 392 Amiga intern OpenDavIce Error = OpenOevice (Name.Unit.IORequest.flags) DO AO DO AI D1 Vor der Verwendung der OpenDevice()-Funktion muß die lORequest- Struktur initialisiert worden sein. Jedes Device, wie auch die Libraries, verfügt über eine Sprungtabelle, deren Einsprünge mit negativen Offsets erreicht werden. Die so er¬ reichbaren Funktionen dienen zum öffnen und Schließen eines Devi¬ ces, sowie zum Ausführen eines lOs. Eine solche Routine ist nötig, damit eine Funktion wie beispielsweise OpenDevice() jedes Device öffnen kann, auch wenn die zu erledigenden Tätigkeiten von Device zu Device verschieden sind. In der OpenDevice()-Funktion wird dann für die Device-spezifischen Prozesse in die Routine des entsprechen¬ den Devices eingesprungen. Jedes Device stellt folgende Funktionen zur Verfügung: Offset Funktion -36 AbortIO -30 BeginlO (10-Ausführen) -12 CI ose -6 Open Sehen wir uns doch anhand der Assembler-Routine eimal genauer an, was geschieht, wenn ein Device geöffnet werden soll. Die hier gezeigte Routine ist der wichtigste Teil der OpenDevice()- Funktion, wird jedoch nicht von der Exec-Library direkt, sondern letztendlich von einer Routine über die RAM-Library aufgerufen. In die Routine wird eingesprungen mit: DO = Unit D1 = Flags AO = Zeiger auf Device-Name AI = Zeiger auf lORequest A6 = Zeiger auf ExecBase fc0666 move.l A2,-{A7) fc0668 move.l A1,A2 fc066a clr.b 31(A1} fc066e movem.l D1-D0,-{A7) fc0672 move.l A0,A1 fc0674 Lea 350(A6},A0 fc0678 addq.b #1,295{A6) A2 retten Zeiger auf lORequset nach A2 Error-Flag löschen DO und D1 retten Zeiger auf Name nach A1 Zeiger auf DeviceList nach AO Forbid Exec 393 fc067c bsr.l »fclöSa fc0680 move.l D0,A0 fc0682 movem. l (A7)+.D1-D0 fc0686 inove.l A0,20(A2) fc068a beq.s SfcOöac fc068c clr.l 24(A2) fc0690 move.l A2,A1 fc0692 move.l A6.-(A7) fc0694 inove.l A0,A6 fc0696 jsr -6(A6) fc069a move.l (A7)+,A6 fc069c move.b 31(A2},D0 fcOöaO ext.u DO fc06a2 ext.l DO fc06a4 jsr -138(A6) fcOöaS move.l- (A7)+,A2 fcOöaa rts Namen in DeviceList suchen (FindNameO) Zeiger auf Device nach AO DO und D1 zurückholen Zeiger auf Device in lOReqest eintragen Fehler, Device nicht vorhanden Zeiger auf Unit löschen Zeiger auf lORequest nach A1 A6 retten Zeiger auf Device nach A6 Spring in OpenDevice ein A6 zurückholen Error-Flag nach DO Fehler Vorzeichen-erweitern Fehler Vorzeichen-erueitern PermitO A2 zurückholen Rücksprung Einsprung bei Device nicht gefunden (Fehler): fcOöac Rioveq Mff.DO fcOöae Riove.b D0,31(A2) fcOöbZ bra.s $fc06a4 Fehleruert nach DO In Error-Flag schreiben Unbedingter Sprung Wie auch bei der eben beschriebenen Routine ist die nun folgende Routine nur der wichtigste Auszug aus der CloseDeviceO-Funktion. Die Routine wird angesprungen mit: A1 - Zeiger auf lORequest A6 = Zeiger auf ExecBase fc06b4 addq.b fcOöbS inove.l fcOöba inove.l fcOöbe jsr fc06c2 inove.l fc06c4 jsr fc06c8 rts #1,295(A6} A6,-(A7) 20(A1),A6 -12(A6) (A7)+.A6 -138(A6) Forbid A6 retten Zeiger auf Device nach A6 Einspringen in CloseDevice A6 zurückholen Permit Rücksprung Als Beispiel für die Routine OpenDevice, die über Offest -6 vom De¬ vice ausgehend aufgerufen wird und Device-spezifische Aufgaben beim öffnen des Devices übernimmt, wird jetzt die OpenDevice- Routine des Trackdisk-Devices näher erläutert. Die Unitnumber gibt beim Trackdisk-Device die Laufwerknummer des Laufwerks an, das angesprochen werden soll. Für jedes der vier möglichen Laufwerke ist ein Zeiger in der Device-Struktur reserviert, der, sofern das Laufwerk vorhanden ist, auf einen entsprechenden 394 Amiga intern Message-Port für das Laufwerk zeigt. Dieser Port hat wie auch das Device selbst zusätzlich zu den standardmäßigen Einträgen, die in der C-Struktur festgelegt sind, noch eigene, nicht standardisierte Einträge, wie beispielsweise einen Zähler für die Anzahl der Zugriffe in der Message-Port-Struktur. Die Routine wird aufgerufen mit DO = Unitnijit>er Dl * Flags AI - Zeiger auf lORequest A6 - Zeiger auf Device fe9f42 movein.l A4/A2/D2,-(A7) D2, A2, A4 retten fe9f46 move.l A1,A4 Zeiger auf lORequest nach A4 fe9f48 move.l D0,D2 Laufwerknuimer nach D2 fe9f4s cmpi.l #$00000004.DO Nuimer zu groB? fe9f50 bcs.s Sfe9f56 Verzweige, wenn Nuimer ok fe9f52 moveq #$20.D0 Sonst Fehlernunmer nach DO fe9f54 bra.s Sfe9f82 Fehler übergeben, Ende fe9f56 Isl.w #2,DO Nuimer *4 für Offset fe9f58 lea 36(A6),A2 Zeiger auf Laufwerks-Port fe9f5c adda.l D0,A2 Offset addieren fe9f5e move.l (A2),A0 Laufwerks-Port nach AO fe9f60 move.l AO.DO Ist Laufwerk vorhanden? fe9f62 bne.s $fe9f70 Verzweige, wenn alles ok fe9f64 bsr.l Sfe9d3e Sonst Laufwerks-Port bestinmen fe9f68 tst.l DO Port gefunden? fe9f6a bne.l Sfe9f82 Verzweige, wenn nicht gefunden fe9f6e move.l A0,(A2) Port in Device eintragen fe9f70 move.l A0,24(A4} Port in lORequest eintragen fe9f74 addq.w #1,32(A6} Anzahl der Zugriffe beim Device erhöhen fe9f78 addq.w #1,36(A0} Anzahl der Zugriffe beim Laufwerks-Port erhöhen fe9f7c movem.l fe9f80 rts (A7)+,A4/A2/D2 D2, A2, A4 zurückholen Rücksprung Einsprung bei Fehler bei der Laufwerks-Port-Zuweisung: fe9f82 move.b D0,31(A4} Fehlernunmer ins Error-Flag fe9f86 moveq #$ff,D0 Zeichen für Fehler nach DO fe9f88 move. l D0,24(A4) Zeiger auf Unit löschen fe9f8c move.l D0.20(A4) Zeiger auf Device löschen fe9f90 bra.s $fe9f7c Unbedingter Sprung Die Funktion CloseDevice() springt mit dem Offset -12 vom Device aus in eine Device-eigene Routine ein. Die Routine hat für das Trackdisk-Device folgendes Aussehen: fe9f92 iBOvem.l A3-A2,-(A7) fe9f96 move.l A1,A2 fe9f98 move.l 24(A2).A3 A2 und A3 retten Zeiger auf lORequest nach A2 Zeiger auf Laufwerks-Port Exec 395 nach A3 fe9f9c subq.w #1,36(A3) Anzahl der Zugriffe verringern fe9faO bne.s $fe9fa8 verzweige, wenn Laufwerk noch benötigt wird fe9fa2 bset #3,64(A3) Flag setzen fe9fa8 subq.w #1,32(A6) Anzahl der Zugriffe auf das Device verringern fe9f8c moveq iWff.DO Löschwert laden fe9fae move.l D0,2Ä(A2) Zeiger auf Port löschen fe9fb2 move.l D0,20(A2) Zeiger auf Device löschen fe9fb6 movem.l (A7)+,A3-A2 A2 und A3 wiederherstellen fe9fba moveq #$00,DO Rückmeldung Null nach DO fe9fbc rts Rücksprung 2.9.3 lO-Steuerung üb«r Exec-Funktionen Zu einem Device gehört immer auch ein Task, dem die Befehle über¬ geben werden. Um einem Device einen Befehl übergeben zu können, gibt es die folgenden Funktionen: Funktion: Fehler = DoIO(IORequest) 00 AI Offset: -456 Beschreibung Diese Funktion wird meistens für die Ein-/Ausgabesteuerung verwen¬ det. Sie wartet, bis der übergebene Befehl beendet wird, und kehrt erst dann wieder zum eigentlichen Programm zurück. Während des Wartens wird der Task auf "Wait" gesetzt. SendIO Funktion: SendlO(IORequeset) AI Offset: -462 Beschreibung Die Funktion dient zum Senden eines lOs an das entsprechende De¬ vice, wartet jedoch nicht auf dessen Beendigung. 396 Amiga intern CheckiO Funktion: fertig = ChecklO(IORequest) DO A1 Offset: -468 Beschreibung Die Funktion prüft, ob ein bestimmter lO-Prozeß bereits abgearbeitet wurde. Sollte dies der Fall sein, so wird in DO der Zeiger auf die entsprechende lORequest-Struktur zurückgegeben. Ist der lO-Prozeß nicht fertig, wird eine Null übergeben. WaitiO Funktion: UaitlOdORequest) A1 Offset: -474 Beschreibung Die Funktion wartet solange, bis der lO-Prozeß erledigt ist. Während dieser Zeit wird der laufende Task auf Wait gesetzt, um andere Tasks abarbeiten zu können. Die Funktionen SendIO und WaitIO ergeben zusammengesetzt den Befehl DoIO. AbortIO Funktion: AbortlO(IORequest) A1 Offset: -480 Beschreibung Die Funktion beendet einen lO-Prozeß, wie sich auch schon anhand des Namens ersehen läßt. Nach dieser Kurzbeschreibung der Funktionen wollen wir uns einmal ansehen, wie diese Funktionen im Betriebssystem aussehen. Die DoIO-Assembler-Routine hat folgendes Aussehen: In Al steht ein Zeiger auf die zuvor errichtete lORequest-Struktur. 397 Exec Al retten Quick-Bit setzen A6 retten Zeiger auf Device holen Zu lO-Ausf(ihren springen A6 holen AI holen Ab hier beginnt die Funktion WaitIO, die von der DoIO-Funktion be¬ nutzt wird. fcttSdc move.l ^,-(A7) fcd^ movea)/#$01,30(A1) fcOÖfethiieVeTl A6,-(A7) fc06e6 move.l 20(A1),A6 fc06ea jsr -30(A6) fc06ee move.l (A7H.A6 fc06f0 move.l (A7)+,A1 fc06f2 btst #0,30(41) fcOöfS bne.s Sfc0744 fc06fa move.l A2,-(A7) fcOöfc move.l A1,A2 fcOöfe move.l 14(A2),A0 fc0702 move.b 15(A0),D1 fcOTOö moveq #S00,D0 fcOTOS bset Dl,DO fcOTOa move.w #$4000,Sdff09a fc0712 addq.b «1,294(A6) fc0716 cfflpi.b #$07,8(A2) fc071c beq.s $fc0724 fc071e jsr -318(A6) fc0722 bra.s Sfe0716 fc0724 move.l A2,A1 fc0726 move.l (AD.AO fc0728 move.l 4(A1),A1 fc072c move.l A0,(A1) fc072e move.l A1,4(A0} fc0732 subq.b #1,294(46} fc0736 bge.a $fc0740 fc0738 move.w #$c000,$dff09a fc0740 move.l A2,A1 fc0742 move.l (A7)+,A2 fc0744 move.b 31 (AI),DO fc0748 ext.w DO fc074a ext.l DO fc074c rts Quick-Bit testen Fertig, wenn gesetzt A2 retten Zeiger auf lORequest nach A2 Zeiger auf Reply-Port Signal-Bit für Port holen DO löschen Bit für Signal setzen Disable- Hacro Typ der Hsg = Reply HSG? Verzweige, wenn Typ ok Sonst auf Hsg warten ( Uait() ) Unbestinmter Sprung lORequest nach AI Node aus Reply-Msg.-Liste entfernen Enable- Macro Zeiger auf lORequest nach AI A2 hersteilen Fehler-Flag nach DO Vorzeichen- erweitern Vorzeichen-erweitern Rücksprung j In der obigen Routine wird als erstes das Quick-Bit gesetzt. Dann i wird der Zeiger auf das Device nach A6 gebracht und in die BeginlO- j Funktion eingesprungen, die später noch anhand des Trackdisk-Devi- j ces beschrieben wird. In dieser Routine wird der auszuführende Be- I fehl auf seine Gültigkeit überprüft und gegebenenfalls an den Track- \ tok-Task übergeben. Kehrt das Programm aus dieser Routine zurück, i «t der Message-Typ in der lORequest-Struktur immer auf "Message" j gestellt. Dem Task wird in der Routine die lORequest-Struktur als ( Message übergeben. f ^ dieser Stelle ist die Befehlsübergabe schon abgeschlossen. Es wird jetzt lediglich auf deren Beendigung gewartet. Sollte das Quick-Bit 398 Amiga intern nicht gelöscht worden sein, ist die Routine jetzt zu Ende. Ansonsten muß geprüft werden, ob der lO-Prozeß beendet wurde. Für diese Überprüfung reicht es aus, den Typ der Message-Struktur auf Reply- Msg" zu testen. Ist das nicht der Fall, geht der Task in Wartestellung, bis eine entsprechende Message eingetroffen ist. Es ist wahrscheinlich nötig, genauer zu erklären, warum es ausreicht, die Unterstruktur "Message” in der lORequest-Struktur auf den Typ Reply-Msg. zu prüfen. Von der BeginlO-Routine (die Routine, die vom Device zur Verfü¬ gung gestellt wird (Offset -30)) wird eine Message zu dem entspre¬ chenden Task, der die Befehlsbearbeitung übernimmt, geschickt. Als Message wird hier unsere lORequest-Struktur gesandt, was mit der Funktion PutMsgO geschieht. In der Funktion wird der Typ der zu schickenden Message automatisch auf "Message" gestellt (Byte-Wert 05). Unsere lORequest-Struktur hat zwar immef noch die gleiche Po¬ sition im Speicher, ist jetzt jedoch mit ihrer Node-Struktur in die Message-Liste des Tasks eingehängt. Der Task arbeitet unseren Befehl ab und sendet als Rückmeldung, daß er mit seiner Arbeit fertig ist, eine Reply-Message. Als Message wird wieder die lORequest-Struktur gesandt. Von der ReplyMsgO-Funktion wird der Typ der zu senden¬ den Message wieder automatisch auf ReplyMsg (Byte-Wert 07) gestellt und in die Message-Liste des Reply-Ports eingehängt. Die WaitIO()- Funktion prüft den Typ der Message-Struktur in unserer lORequest- Struktur, stellt fest, daß es sich um eine Reply-Message handelt (sie wurde ja soeben als solche gesandt) und muß noch die Repy-Message, die in die ReplyMsg-Liste eingefügt wurde, wieder entfernen. Beschreibung der Funktion SendIO In Al steht der Zeiger auf die lORequest-Struktur. fc06ca clr.b fc06ce move.l fc06d0 move.l fc06cl4 jsr fc06d8 move.l fc06da rts 30(A1) A6,-(A7) 20(A1),A6 -30(A6) (A7)+,A6 Alle Flags löschen A6 retten Zeiger auf Device holen Zu BeginlO springen A6 zurückholen Rücksprung Sie sehen, daß die SendlO-Funktion nichts anderes ist als der erste Teil der DoIO-Funktion. 399 Exec Beschreibung der Funktion ChecklO In Al steht der Zeiger auf die lORequest-Struktur. fc074e btst #0,30(A1} Prüfe, ob Quick-Bit gesetzt fcOTSA beq.s SfcOTSa Verzweige, wenn nicht gesetzt fc0756 move.l AI,DO Ansonsten Ok-Heldung übernehmen fc0758 rts Rücksprung fcOTSa cmpi.b #$07,8(A1} Typ der Message-Struktur = Replymsg? fc0760 beq.s $fc0766 Ja, dann positive Rückmeldung fc0762 moveq #$00,D0 Ansonsten negative Rückmeldung fc0764 rts Rücksprung fc0766 move.l A1,D0 Ok-Heldung übergeben fc0768 rts Rücksprung j Die ChecklO-Funktion prüft lediglich, ob eine Reply-Message einge¬ gangen ist. Sollte das der Fall sein, wird der Zeiger auf die lORe- I quest-Struktur in DO übergeben, sonst wird einen Null in DO zurück¬ gesandt. Sie sehen, daß in dieser Routine zwar geprüft wird, ob eine Reply- Message angekomen ist, diese jedoch nicht aus der Liste der Reply- Messages entfernt wird. Das Entfernen der Reply-Message muß bei der Benutzung der ChecklO-Funktion, sofern das Quick-Bit nicht ge¬ setzt ist, von Hand, z.B. mit der GetMsg()-Funktion, erledigt werden. Bsschreibung der Funktion AbortIO In Al steht der Zeiger auf die lORequest-Struktur. fc076a move.l fc076c move.l fcOTTO jsr fc0774 move.l fc0776 rts A6,-(A7) 20(A1},A6 -36(A6) (A7)+,A6 A6 retten Zeiger auf Device nach A6 In AbortIO einspringen A6 zurückholen Rücksprung Wie schon gesagt verfügt jedes Device über eine kleine Sprungtabelle für die Verwaltung des Devices. Sehen wir uns anhand des Trackdisk- Devices die Routine, die sich hinter lO-Ausführen verbirgt, einmal nflher an. Sie wird mit Offset -30 aufgerufen und von den Funktionen DoIO und SendIO verwendet. In Al steht der Zeiger auf die lORequest-Struktur, in A6 der Zeiger auf das Device. fe9fbe clr.b 31(A1) fe9fc2 moveq #$00,DO Error-Flag löschen DO löschen 400 - Amiga intern fe9fc4 move.b fe9fc8 cmpi.b fe9fcc bcc.s fe9fce move.l fe9fd2 move.l fe9fd8 btst fe9fda bne.s fe9fdc andi.b fe9fe2 move.l fe9fe4 move.l fe9frt jsr fe9fec move.l fe9fee bra.s 29(A1),D0 #$16,D0 $fea016 24(A1},A0 lll$000c61c2.D1 D0,D1 $fe9ff0 «7e,30= DO = Zeiger auf die Device-Struktur >= AO = Zeiger auf die Segmentliste des geladenen Devices ;>= A6 = Zeiger auf ExecBase ;=> DO = Zeiger auf die Device-Struktur initRoutine: move.l a5,-(a7) ;A5 retten move.l dO,aS ,‘Zeiger auf Device nach AS move.l a6,md_SysLib(a5) ;Zeiger auf ExecBase in die ;Device-Struktur eintragen move.l aO,md_SegList(aS) ;Zeiger auf die Segment- ;Liste eintragen lea dosNaine(pc),a1 ;Zeiger auf den Namen der ;DOS-Library moveq «0,d0 .•Version für OpenLibO CALLSYS OpenLibrary ;DOS-Lib öffnen move.l dO,md DosLib(a5) .-Zeiger auf DOS-Lib eintragen bne.s Dos_0i( (■weiter, wenn DOS-Lib gefunden ;Fall8 die DOS-Library nicht geöffnet ueden kann, wird ein "Guru" ;au8gegeben. Oie Ausgabe geschieht mit Hilfe eines MACROs, das AG_OpenLib!AO_DOSLib (■in dem Include-File ALERT Do8_OK: (■ Hier, wem nötig. ; Hier, wem nötig. ; Hier, wem nötig. ; Hier, wem nötig. move.l move.l rts eigene Initialisierung einfügen a5,dO (a7)+,a5 ;Zeiger auf Device nach DO ;A5 zurückholen ;Rücksprung ;Nachdem die Device-Struktur nach dem Laden von Disk initialisiert jwurde, muß sie noch geöffnet werden. Hierbei wird der Task zur (■Verwaltung der Unit (Einheit) sowie die Unit-Struktur selbst ;crstcllt. Wenn mehrere Units erlaubt sind, muß die Zulässigkeit ;dar gewünschten Unit-Numer geprüft werden. ;>■ DO « Unit-Nummer ;>■ D1 B Flags ;>■ A1 = Zeiger auf lORequest ;>■ A6 = Zeiger auf das Device Opan: movem.l d2/a2-a4,-(a7) move.l a1,a2 moveq «MD_NUHUNITS,d2 cnp. l d2,d0 bcc.s Open_error ;Register retten ;lORequest retten ;Anzahl der Units nach D2 ;Unit-Nuniner erlaubt? ;Nein, Nummer zu hoch Exec 407 0pen_ok: 0pen_end: move.l d0,d2 ;Un^-NuTiner hach D2 Isl.l #2,d0 ;OMset für Zeiger auf ;UÖit lea md Units(a6,dO.L), a4 tZeiger auf Unit nach A4 move.l (aÄ),dO (•I^t Unit schon erstellt? bne.s Open_ok ;J^ ist bereits erstellt ;Uni\ erstellen ;Unit erfolgreich erstellt bsr InitÜnit move.l (a4),da beq.s Open error ;nein, Fehler move.l d0,a3 ;Zeiger auf Unit nach A3 move.l dO,IO UNIT(a2) ;in lORequest eintragen addq.w #1,LIB 0PENCNT(a6) ;Zähl er für Anzahl der ;Zugriffe auf Device +1 addq.w #1,UNIT_0PENCNT(a3);Zähler für Anzahl der (•Zugriffe auf Unit +1 bclr #LIBB_DELEXP,md_Flags(a6) ;Flag für ;Library entfernen, löschen movem.l = Al = Zeiger auf die lORequest-Struktur ;>= A6 = Zeiger auf die Device-Struktur ;=> DO = Zeiger auf die Segmentliste, wenn Segment (das ganze ; Device) entfernt werden soll, sonst Null Close: movem. l a2-a3,-(a7) ;Register retten move.l a1,a2 (■lORequest retten move.l 10 UNIT(a2),a3 (■Zeiger auf Unit holen moveq.l #-i,dO ;D0 = -1 move.l dO,IO UNIT(a2) (■Zeiger auf Unit löschen move.l dO,IO~DEVICE(a2) :Zeiger auf Device löschen subq.w #1,UNIT_0PENCNT(a3};Zähler für Anzahl der ;Zugriffe auf Unit -1 bne.s CloseOevice ;verzweige, wenn nicht Null ;Uenn keine Zugriffe mehr auf die Einheit (Unit) gewünscht sind. ;kann der von ihr belegte Speicher freigegeben werden. bsr ExpungeUnit ;Speicher von Unit freigeben CloseOevicerclr.l dO ;Rückgabeparameter löschen (■(wichtig) subq.w #1,LIB_OPENCNT(a6} ;Zähler für Anzahl der ;Zugriffe -1 bne.s Close_end ;verzweige, wenn nicht Null 408 Amiga intern ;Uenn kein Task mehr auf das Device zugreift, könnte es aus dem /System entfernt ueden. Da es jedoch oft der Fall ist, daB das /Device nach kurzer Zeit wieder verwendet wird, ist hier zusätzlich /eine Abfrage, ob das Entfernen des Devices erzwungen werden soll. /Wenn nicht, bleibt das Device solange im System, bis Speicherplatz- /Mangel besteht. In diesem Fall wird es automatisch von der RAM-Library /entfernt. /Erzwingen können Sie das Entfernen des Devices - wenn kein Task /mehr aud dasselbe zugreift - durch Setzen des 'LIBB_DELEXP'-Bit /im Flag-Eintrag des Devices, vor dem Aufruf "CloseDevice( btst «LIBB DELEXP,md Flags(a6) /Ist das Bit gesetzt ? beq.s Close_end Nein, Ende bsr Expunge Device entfernen Close_end: movem.l (a7)+,a2-a3 Register zurückholen rts Rücksprung /Dies ist die Routine, die das Device entfernt und den belegten /Speicherplatz wieder zurückgibt. />= A6 = Zeiger auf Device /=> DO = Zeiger aus Segmentliste des geladenen Devices Expunge: movem.l d2/a5-a6,- /Bit setzen, um clr. l dO anzuzeigen, daB Device entfernt werden soll Zeiger auf Segment löschen bra.s Expunge_end Rücksprung 1$ move.l md_SegL i st ( aS >, d2 Zeiger auf Segment liste holen move.l a5,a1 Zeiger auf Device nach A1 CALLSYS Remove Device aus Liste löschen move.l md_DosLib(a5),a1 Zeiger auf DOS_Lib holen CALLSYS CloseLibrary DOS-Lib löschen move.l a5,a1 Zeiger auf Device clr. l dO DO löschen move.w LIB HEGSIZE(a5),dO Negative Größe holen sub.w dO,a1 Anfang des Speichers für add.w LIB_POSSIZE(a5),dO Device bestimmen Länge von Device bestimmen CALLSYS FreeHem Speicher freigeben move.l d2,d0 Zeiger auf Segment liste Expunge_end:movem.l = D2 = Unit-Nuimer ;>= A6 = Zeiger auf Device MYPROCSTACK MYPROCPRI EQU S200 EOU 0 (-Länge des Stacks für den Unit-Task ;Prior{tät des Unit-Tasks InitUnit: movem.l move.l move.l d2-d4,- APTR moveq «MYPROCPRI,d2 ;Priorität des Prozesses move.l MiyNaine,d1 ;Zeiger auf Namen LINKSYS CreateProc,iiri DosLib(a6) ,-ProzeB erstellen tst.l dO (-Erfolgreich erstellt? beq InitUnit_FreeUnit ;Nein, Speicher für Unit ;freigeben move.l dO,mdu Process(a3) ,-Zeiger auf ProzeB eintagen move.l dO,aO ■ ;Zeiger nach AO lea -pr_MsgPort(aO),aO ;Zeiger auf Task-Struktur ;innerhalb vom ProzeB holen move.l aO,MP SIGTASK(a3) ,-Task als Signal-Task für Unit move.b «PA IGNORE,MP FLAGS(a3) ,-Message-Port-Bits setzen lea MP_MSGLIST{a3),a1 ,-Zeiger Message-Port-Liste ;NEWLIST ist ein MACRO aus dem Include-File "exec/lists.i" und erstellt ,-eine leere Liste an angegebener Stelle (aO). NEULIST lea move.l move.l move.l LINKSYS AI ;Leere Liste erzeugen mdu_Nsg(a3),a1 ;Zeiger auf Message-Struktur (-innerhalb der Unit-Struktur a3,mdn_Unit(a1} ;Zeiger auf Unit eintragen a6,mctai_Device(a1) ,-Zeiger auf Device eintragen dO,aO ;Message-Port für ProzeB PutMsg,md_SysLib(a6) ,-Message an ProzeB senden 410 Amiga intern clr.l move.b Isl.l move.l InitUnit_end: rnovein. l rts dO ;D0 löschen mdu_UnitNuni(a3),dO ;Unit-Nunrier nach dO #2,ä) ;Offset, im Zeiger auf Unit :iffl Device einzutragen a3,ind_Units(a6,dO.L) ,-Zeiger auf Unit eintragen (a7)+,d2-d4 .-Register zurückholen ;Rücksprung ;Die nächsten beiden Befehle werden nur durchlaufen, wenn ein Fehler ;bei der Erzeugung des Prozesses aufgetreten ist. InitUnit FreeUnit: bsr FreeUnit .-Speicher für Unit zurückgeben bra.s ImtUnit_end ; Rücksprung ;Speicher für Unit-Struktur an das System zurückgeben ;>= A3 = Zeiger auf Unit-Struktur FreeUnit: move.l move.l LINKSYS rts a3,a1 ,-Zeiger auf Unit nach A1 #MyOevUnit_SlZE,dO .-Länge der Struktur F reehem, ind_SysL i b( a6) ; F reeHemf) ;Rücksprung ,-Uenn eine bestinmte Einheit (Unit) von keinem Task mehr gebraucht .-wird, wird der von ihr belegte Speicher zurückgegeben und ,-der Prozeß aus dem System entfernt. Oie Routine wird von der ;CIose-Routine aufgerufen. ;>- A3 = Zeiger auf Unit ;>= a 6 = Zeiger auf Device ExpungeUnit move.l d2,-(a7) ;d2 retten move.l mdu_Proce8s(a3),8l ;Zeiger auf Prozeß lea -pr_M8gPort(a1),a1 ;Zeiger auf Anfang des iProzesses holen. LINKSYS RemTask.md SysLib(a6) ,-Task aus System entfernen clr.l d2 ;D2 löschen move.b mdu_UnitNum(a3),d2 .-Unit-Nummer nach 02 bsr.8 FreeUnit .-Speicher für Unit freigeben Isl.l «2,d2 ,-Offset für Unit-Eintrag ;in der Device-Stuktur clr.l md Units(s6,d2.L) .-Eintrag löschen move.l (a7)+.d2 ,-D2 zurückholen rts ;Rücksprung ,-Als nächstes schließt sich die Tabelle mit den Befehlen des Devices an. .-Die Reienfolge der in der Tabelle eingetragenen Funktionen ist ;festgelegt. Für jeden Eintrag in der Tabelle steht ein bestimmtes .-Bit. Für den 0. Eintrag Bit 0, für den 1. Bit 1, für den 2 . Bit 2 ;U8W. Mit Hilfe dieser Bits wird festgelegt, welcher Befehl direkt ;susgeführt oder an den Prozeß geschickt wird (siehe Erklärung vor ,-dem Programn). ,-Oie Befehle MyReset und MyStop wurden nicht Reset und Stop genannt, ,-da der Assembler dies für Assembler-Befehle halten würde. Exec 411 ;Die Befehle Invalid bis Flush müssen in der Tabelle in dieser ;Reihenfolge vertreten sein. Wenn nicht alle für Ihr Device ;sinnvoll sind, lassen Sie diese zur Funktion Invalid ;springen (siehe Update und Clear). cindtable: dc.l Invalid $001 dc.l Myfieset $002 dc.l Read $004 dc.l Urite $008 dc.l Update $010 de. l Clear $020 dc.l MyStop $040 dc.l Start $080 dc.l Flush $100 ;Ab hier können Sie eigene Funktionen eintragen. ;Uir haben hier zwei eigene Funktionen Status und Funk2. dc.l Status ;S200 dc.l Funk2 ;S400 cmdtable_end: ;Die mit 'ODER' verknüpften Bits repräsentieren die Befehle, ;die nicht an den ProzeS gesandt, sondern direkt ausgeführt werden. OirektBefehle EOU $1t$2!UOI$80IS100l200 ;Folgende Befehle werden direkt ausgeführt: ;Invalid, HyReset, MyStop, Start, Flush, Status FUNKANZ EOU 11 ;Anzahl der möglichen Befehle ;Hier beginnt die Routine, die jedesmal aufgerufen wird, wenn ;ein Befehl an das Device gesandt wird. Sie stellt fest, ob der ;Befehl zugelassen ist und ob er an den ProzeB gesandt oder ;direkt ausgeführt wird. ;>= AI = Zeiger auf die lORequest-Struktur ;>= A6 = Zeiger auf die Device-Struktur BeginlO: move.l a3,-(a7) ;A3 retten move.l IO UNIT(a1),a3 ;Zeiger auf Unit nach A3 move.w IO C0MHAN0(a1),d0 ;Befehl nach DO emp.w #FUNKANZ,dO ;Befehl erlaubt ? bcc BeginIO_NoCmd ;springe, wenn nicht erlaubt ;DISABLE ist ein HACRO, das in dem Include-File "exec/ables.i" zu ;finden ist. Es sperrt wie die Disable-Funktion alle Interrupts. ;Damit die Interrupts gesperrt werden können, muß das Hardware- ;Register $DFF09A beschrieben werden. Die Adresse des Registers ;ist innerhalb des NACROs nicht direkt angegeben, sondern über ;ein Label. Damit der Linker das Label erkennt, muß es mit XREF ;angegeben werden. Es existiert ein entsprechendes MACRO ;nainens INT_ABLES im Include-File "exec/ables.i", was von dem Programm ;nach den Definitionen der Library-Einsprünge aufgerufen wird. 412 Amiga intern DISABLE AO ;Interrupts sperren move.l #D{rektBefehle,d1 ;Languort für Direktbefehl btst dO,d1 ;SoU Befehl direkt ;ausgeführt werden ? bne.s BeginIO_Immediate ;Springe, wenn direkt .■Ansonsten wird die lORequest-Struktur (der Befehl) an den ;Unit-Task (richtiger ProzeB) weitergeleitet. Beginl0_QueueHsg: ;Das UINTB_INTASK-Bit gibt an, daB der Befehl innerhalb des Unit- ,-Tasks verarbeitet wird. bset #UN!TBJNTASK,UMIT_FLAGS(a3) ;Unit-Bit setzen ;Durch das Löschen des QUICK-Bits wird klargestellt, daB der Task, ;der den Befehl gesandt hat, bei der Benutzung der DoIO-Funktion ;in Wartestellung geht und das nach Beendigung des Befehls eine ;Reply-Message geschickt werden muB. bclr #IOB_ClUICK,IO_FLAGS(a1) ;IOB_QUICIC löschen ;Bei ENABLE handelt es sich wieder um ein HACRO aus dem ;"exec/ables.i"-File. ENABLE AO ;Freigeben der Interrupts move.l a3,a0 .-Zeiger auf Unit nach AO LINKSYS PutHsg,md_SysLib(a6) ;lORequest-Struktur ;zum ProzeB schieben bra.s BeginIO_end .-Ende ,-Einsprung für Direktbefehle BeginIO_Iinnediate: ENABLE AO ;Freigeben der Interrupts bsr PerformIO ;Befehl ausführen BeginIO_end:move.l (a7)+,a3 ,-A3 zurückholen rts ;Rücksprung ;Einsprung, wenn der gesandte Befehls-Code ungültig ist. BeginIO_NoCind: move.b #IOERR_NOCMD,IO_ERROR(a1) .-Fehlermeldung übergeben bra.s BeginIO_end ;Rücksprung ;Adresse der Funktion für den entsprechenden Befehl holen und .-einspringen ;>= AI - Zeiger auf die lOReqest-Struktor ;>= A3 = Zeiger auf die Unit-Struktur ;>= A6 = Zeiger auf die Device-Struktur PerformIO: move.l a2,-(a7) ;A2 retten Riove.l a1,a2 ;IORequest nach A2 Exec 413 move.w 10 C0MHAND(a2)(d0 ■rBefehl holen Toffset für Tabelle für ;Fuhktionsadressen Isl.w # 27 dO lea cmdtable(pc),aO ;Zeiger auf Tabelle move.l 00(a0,d0.U),a0 (■Zeiger auf Funktion jsr (aO) (■Funktion ausführen move.l (a7)+,a2 ;A2 zurückholen rts (■Rücksprung ;Oie folgende Routine nuB zur Beendigung sämtlicher Funktionen ;aufgerufen werden. Sie sendet, sofern der Befehl über den Unit-Task ;ausgeführt wurde, die Reply-Hessage an den wartenden Task. TermIO: move.w move.l btst bne.s btst bne.s bclr TermIO_Direkt: btst bne.s LINKSYS TermIO_end: rts IO_COMMAND(s1),dO ;Befehl holen #DirektBefehle,d1 ;Haskenwert für Direktbefehle d0,d1 ;Direktbefehl ? TermIO Direkt ;Springe, wenn Direktbefehl IIRJNITBjMTASK,UMlT_FLAGS(a3) ;Ging Befehl über ;den Task? TermIO_Direkt ;Nein, also Direktbefehl llAJNITB_ACTIVE,UNrT_FLAGS(a3) ;Task freigeben #IOB_OUICK,IO_FLAGS(a1) ;Ist Quick-Bit gesetzt? TermIO_end ;Ja, dann Direkbefehl ReplyHsg,md_SysLib(a6) ;Ansonsten ReplyMsg senden ;Rücksprung (■Routine zum Abbrechen eines laufenden Befehls (■Diese Routine ist Device-abhängig und deshalb hier nicht ausgeführt. (■Bei den meisten Devices ist es ohnehin kaun möglich, eine laufende (■Aktion zu unterbrechen. ;Die Abort IO-Funktion ist nicht eine der Funktionen, die aus der ;Befehlstabelle entnonmen werden, sondern ist von derselben Art wie ;BeginIO. Aus diesem Grund darf sie nicht mit TermIO abgeschlossen (■werden. Abort10: moveq #0,d0 rts ;Ab hier beginnen die Funktionen, die über die Befehle in der (■lORequest-Struktur aufgerufen werden. Sie müssen alle mit TermIO (■abgeschlossen werden. Jeder Funktion werden die gleichen Parameter (■übermittelt. ;Die Parameter sind: ;>= AI = Zeiger auf die lORequest-Struktur ;>= A3 = Zeiger auf die Unit-Struktur ;>= A6 = Zeiger auf die Device-Struktur ;Ungültiger Befehl Invalid: move.b lOERRNOCMD,IO_ERROR(a1) ;Fehlermeldung übergeben bsr TermIO ;Funktion beenden 414 Amiga intern rts ;Rücksprung ;Befehl Reset ;Uas resetet werden soll, hängt jeweils vom Device ab, weshalb ;hier kein sinnvoller Code eigefügt wurde. HyReset: ; Eigene Funktion einsetzen ; Eigene Funktion einsetzen ; Eigene Funktion einsetzen bsr TermlO ;Funktion beenden rts ;Rücksprung ;Befehl Read ;Uie schon gesagt, soll hier nur das Prinzip eines eigenen Devices ;erläutert werden. Deshalb ist unsere Read-Funktion von wenig praktischem (■Nutzen. Sie füllt den in IO_DATA angegebenen Speicherbereich mit ;Bytes, deren Wert die Nummer der Unit darstellt über eine Länge, die ;in 10_LENGTH angegeben wird. Die Länge wird zusätzlich in das ;IO_ACTUAL-Feld übertragen, was der Benutzer nach Beendigung des ;FüTlens als Rückgabewert aus seiner tORequest-Struktur auslesen kann. Read: move.l move.l move.l beq.s move.b Read_Loop: move.b subq.l bne.s Read_end: bsr rts ;Befehl Write IO DATA(a1),aO 10 LENGTH(a1>,dO d07lO_ACTUAL(a1) Read_end mdu_Uni tNunC a3), d1 d1,{a0)+ #1,d0 Read_Loop TermlO ;Speicheranfang holen (■Länge holen ;Länge als Rückgabe ;Ende, wenn Länge = 0 (■Unit-Nunner holen ;Bereich füllen ;Zähler verringern ;weiter kopieren (■Funktion beenden ;Rücksprung ;Die dritte-Funktion ist in unserem Beispiel noch spärlicher ;ausgefallen als die Read-Funktion. Hier ist eigene Kreativität ;gefragt. Unsere Funktion übergibt nur die Länge als Rückgabewert Urite: move.l I0_LENGTH(a1),I0_ACTUAL(a1) ,-Länge übergeben bsr TermlO ;Funktion beenden rts ;Rücksprung ;Die Funktionen Update und Clear sind für unser "Device" nicht ;sinnvoll, sie ergeben eine Fehlermeldung. Update: Clear: bra Invalid .-Fehler, Ende (■Befehl Stop ;Hit der Funktion ist es möglich, alle nach denj Stop-Befehl eingegangenen ;und an den Unit-Task gesandten Befehle zu stoptpen. Sie werden zwar in ;die Message-Liste eingefügt, jedoch nicht abge^beitet. ;Das Stoppen der Befehle bezieht sich nicht auf das ganze Device, ;sondern nur auf eine Einheit (Unit). HyStop: bset #M0UB_ST0PPED,UNlT_FLAGS(a3) ;Stop-Bit setzen bsr TermlO ;Funktion beenden rts ;Rücksprung ,-Befehl Start ;Dieser Befehl belebt den zuvor gestoppten Task wieder, so das er ;alle mittlerweile eingegangenen Befehle sogleich abarbeitet. ;Hierfür reicht es nicht, das Stop-Bit wieder zu löschen, der Task ;muB zusötzlich "gesagt bekommen", daB er wieder arbeiten darf. ;Um dies zu bewerkstelligen, wird dem Task vorgegaukelt, ein ;neuer Befehl (eine neue Message) sei angekoninen, die er sogleich ;auszuführen versucht. Tatsächlich wird jedoch nur das entsprechende ;Task-Bit gesetzt. Der Task beginnt die Message-List zu durchsuchen ;und die eingereihten Befehle abzuarbeiten. Start: bsr move.l bsr rts Internalstart: bclr move.l clr. l move.b bset LINKSYS rts ;Befehl Flush ;Mit diesem Befehl werden alle sich in der Message-Liste des ;Tasks befindlichen Messages (Befehle) mit einer Abbruchmeldung ;zurückgesandt. Flush: movem.l d2/a6,-(a7) ;Register retten move.l md_SysLib(a6),a6 ;ExecBase nach A6 ;FORBID ist ein MACRO aus dem File "exec/ables.i" und erfüllt ;die Funktion ForbidO. FORBID ; ForbidO Flush_loop: move.l a3,a0 ;Zeiger auf Unit CALLSYS GetMsg ;Message aus Liste löschen tst.l dO ;existiert noch eine Msg? Internalstart ;Task starten a2,a1 ;IORequest nach AI TermlO ;Funktion beenden ;Rücksprung in«UB_ST0PPED,UNIT_FLAGS(a3) ;Stop-Bit löschen MP_SIGTASK(a3),a1 ;Zeiger auf Task holen dO ;D0 löschen MP_SIGBIT(a3),d1 ;Signal-Bit holen d1,d0 ;Bit in Maske setzen Signal,md_SysLib(a6) ;Signal senden ;Rücksprung 416 Amiga intern Flush end: ;Befehl Status beq.s Flush end ;nein, Liste leer, Ende move.l dO.al“ ;Zeiger auf Hessage nach A1 move.b #IOERR_ABORTED, IO_ERROR,dO move.l dO,IÖ_ACTUAL(a1> bsr TertnlO rts .-Unsere letzte Funktion ist ebenfalls eine eigene, die jedoch .-nicht belegt ist. Funk2: . ■ i. j bsr TerralO ;Funktion beenden rts ;Rücksprung ;Hier beginnt das Segment, das der Funktion CreateProcO ,-übergeben wird, un den Unit-ProzeB zu starten. CNOP 0,4 iiiyproc_seglist: de. l 0 ;Auf Langwortadresse bringen ,-Kein weiteres Segment ;Der Prozeß beginnt bei Porc_Begin zu arbeiten. Proc_Begin: move.l sub. l CALLSYS move.l move.l lea _AbsExecBase,a6 a1,a1 FindTask d0,a0 d0,a4 pr_HsgPor t(aO), aO ;Execbase nach A6 ;Null nach A1 ;Zeiger auf eigenen Task holen ;Zeiger nach AO ;und A4 ;Zeiger aud Hessage-Port vom ;Prozeß ;Die Nachricht, auf die hier gewartet wird, wird von der lnit_Unit ;Funktion an den soeben errichteten Prozeß gesandt. Die Hessage- ;Struktur befindet sich innerhalb der Unit-Struktur. Exec 417 (»LLSYS UaitPort ;Uarten, bis Message erhalten ;Zeiger auf Mess^e nach AI move.l d0,a1 move.l d0,a2 ;und nach A2 CALLSYS Remove ;Message aus Liste löschen move.l mdm_DeviceCaZ),a5 ;Zeiger auf Device holen move.l mdm Unit(a2),a3 ;Zeiger auf Unit holen moveq #-1,d0 ;D0 = -1 CALLSYS AllocSignal ;Funktion AllocSignalO move.b dO,MP SlGBIT(a3) ;Signal-Bit eintragen move.b «PA_SIGNAL,MP_FLAGS(a3) ;Message soll beim ;Eintreffen eine Signal rauslösen clr.l d7 ;D7 löschen bset d0,d7 ;Masken-Bit für Signal setzen bra.s Proc_CheckStatus ;Port abfragen ;Die folgende Schleife ist die Hauptschleife des Unit-Tasks. ;Es wird gewartet, bis eine Message eingetroffen und verarbeitet ist. ;Nach der Bearbeitung wird in die Schleife zurückgesprungen. Proc_MainLoop: move.l d7,d0 ;Maske Signal-Bit holen CALLSYS Uait ;Auf Message warten Proc_CheckStatus: btst tlMDUB_STOPPED,UNIT. _FLAGS(a3);Uurde Task gestoppt? bne.s Proe_MainLoop ;Ja, dann Message nicht ;bearbeiten bset #UNITB_ACTIVE,UMn. ,FLAGS(a3) ;Ist Task bereits ;aktiv? bne.s Proc_MainLoop ;Ja, warten, bis Task frei Proc_NextMsg: move.l a3,a0 ;Zeiger auf Port nach AO CALLSYS GetMsg ;Message holen tst. l dO ;Message vorhanden? beq.s Proc Unlock ;Nein, Ende move.l d0,a1 ;Zeiger auf Message nach AI exg a5,s6 ;Zeiger auf Device nach A6 bsr PerformlO ;Befehl auswerten exg a5,86 ;Zeiger auf ExecBase nach A6 bra Proc_NextMsg ;Neue Message holen ;Es liegen keine Messages (Befehle) mehr an, der Task kann freigegeben ;Merden. Proc Unlock: bclr 1HJNITB_ACTIVE,UMIT_FLACS(83) ;Active-Bit löschen bclr «UNITB_INTASK,UNIT_FLAGS(a3) ;Intask-Bit löschen bra Proc_MainLoop ;Zurück in Hauptschleife END 418 Amiga intern 2.10 Interrupt-Handhabung auf dem Amiga In diesem Kapitel wollen wir uns einmal ansehen, wie der Amiga seine sieben vom Prozessor zur Verfügung gestellten Interrupt-Ebenen nutzt und wie man sie für sich nutzen kann. Zum besseren Verständnis des Kapitels sollten Sie den Interrupt-Teil aus Kapitel 1 dieses Buches schon gelesen haben. In diesem Kapitel wird der Interrupt erst betrachtet, nachdem der Prozessor ein entsprechendes Signal von der 4703-Interrupt-Logic be¬ kommen hat. Nachdem der Prozessor ein Interrupt-Signal erhalten hat und dieses nicht gesperrt war, lädt er die zu der entsprechenden Priorität gehö¬ rige Adresse in seinen Programmzeiger und führt den Interrupt an entsprechender Stelle weiter. Die Vektoren für die Fortsetzung des Interrupts stehen ab Adresse $0064 bis $007F, wobei die ersten vier Bytes den Vektor für Interrupt-Ebene 1 und die Bytes von $007C bis $007F den Vektor für Interrupt-Ebene 7 beinhalten. Da beim Amiga mehr Interrupts gebraucht werden, als der Prozessor zur Verfügung stellt, laufen alle Interrupts erst über ein Register, mit dem 15 verschiedene Interrupts verwaltet werden. Die eingegangenen Interrupts werden mit Hilfe eines Interrupt-Enable-Registers auf ihre Zulässigkeit überprüft, worauf dann, sofern der Interrupt erlaubt war, ein Interrupt am Prozessor durchgeführt wird. Da für 15 Interrupts le¬ diglich sieben Prioritäten zur Verfügung stehen, sind mehreren Inter¬ rupts gleiche Prozessor-Prioritäten zugeordnet. Aufgrund der gleichen Prioritäten haben somit verschiedene Interrupts einen gleichen Ein¬ sprung, wo sie softwaremäßig ihren Ursachen entsprechend "sortiert" werden. Die folgende Tabelle zeigt Ihnen, welche Interrupts welche Prioritäten haben. Mit Pseudo-Priorität ist die Priorität gemeint, die softwaremäßig abgefragt wird. Ihre Zahl entspricht der Bit-Nummer des im Interrupt-Request-Register vermerkten Interrupts. Ein Beispiel zur Verdeutlichung: Der Interrupt, der ausgelöst wird, wenn der Rasterstrahl des Bild¬ schirms Zeile 0 durchläuft, hat die gleiche Prozessorpriorität wie der Interrupt für die Beendigung der Blitter-Aktivität. Die beiden Inter¬ rupts werden an Bit 5 und 6 des Interrupt-Request-Registers ange¬ zeigt. Softwaremäßig wird der Interrupt mit der höheren Bit-Nummer Exec 419 (der höheren Pseudo-Priorität) vor dem mit der niedrigeren abgefragt^ In diesem Fall also der Blitter- vor dem Rasterstrahl-Interrupt. Ziie nächste Tabelle zeigt Ihnen die 1S Interrupts des Amiga mit ^ssen Prozessor- sowie Pseudoprioritäten. / Piaudo- Priorität Name ProseMor- Priorität Funktion 14 INTEN (6) Interrupta erlauben 13 EXTER 6 Interrupt von CIA-B oder Expanaion-Port 13 DSKSYN 5 Diak-Synchroniaationswert erkannt 11 RBF 5 Eingabepufler dea Seriellen Porta voll 10 AUD3 4 Audiodaten Kanal 3 auagegeben e AUD3 4 Audiodaten Kanal 3 auagegeben 8 AUDI 4 Audiodaten Kanal 1 auagegeben T AUDO 4 Audiodaten Kanal 0 auagegeben 6 BLIT 3 Blitter fertig 5 VERTB 3 Beginn der vertikalen AuataatlUcke erreicht 4 COPER 3 Reaerviert für Copper-Interrupta 3 PORTS 3 Interrupt von CIA-A oder Expanaion-Port 3 SOFT 1 Reaerviert fOr Software-Interrupta 1 DSKBLK 1 Diak-DMA-Tranafer beendet 0 TBE 1 Auagabepuffer dea aeriellen Porta leer 2.10.1 Aufbau der Interrupt-Strukturen Für die Interrupts des Amiga gibt es, was Sie nicht überraschen wird, auch wieder eine Struktur, mit der sie sich gut verwalten lassen. Die Struktur hat folgendes Aussehen; struct Interrupt { 0 struct Node is Node; U APTR is Data; 18 VOID (•Ts_Code)(); >; is_Node Node-Struktur, wie Sie sie schon des öfteren kennengelernt haben. is_Data Zeiger auf einen Datenspeicher, der vom Interrupt beliebig genutzt werden kann. Die Größe des Datenspeichers kann bei der Erstellung der Struktur beliebig groß gewählt werden. 420 Amiga intern is_(*Code)( ) Zeiger auf das Interrupt-Programm, das ausgeführt werden soll. Es gibt zwei verschiedene Möglichkeiten, einen Interrupt zu nutzen. Zum einen kann man mit einem Interrupt ein Interrupt-Programm ansprechen, was über einen Interrupt-Handler geschieht, oder, und das ist die andere Möglichkeit, mit einem Interrupt mehrere verschiedene Programme aufrufen lassen, was durch Interrupt-Server erledigt wird. Jeder zugelassene Interrupt ist in der ExecBase-Struktur (die Haupt¬ struktur von Exec, auf die wir noch zu sprechen kommen) vermerkt. In dieser Struktur befindet sich für jeden der 15 möglichen Interrupts noch eine kleine Unterstruktur, deren richtige Initialisierung für den Interrupt sehr wichtig ist. Die Unterstruktur heißt IntVector und hat folgendes Aussehen: struct IntVector { 0 APTR iv Data; A VOID <*Tv_Code)<); 8 struct Node *iv Node; >; Je nachdem, ob der entsprechende Interrupt von einem Interrupt- Handler oder Server verwaltet wird, ist die Initialisierung der Int- Vector-Struktur unterschiedlich. Für den Interrupt-Handler sieht diese Initialisierung wie folgt aus: iv_Data Zeiger auf den gleichen Datenspeicher, der auch schon in der Struktur "Interrupt" vorkam. (*iv_Code)( ) Zeiger auf das Interrupt-Programm, das ausgeführt werden soll. *iv_Node Zeiger auf die Interrupt-Struktur, die zuvor beschrieben wurde. Für den Interrupt-Server hat sie dieses Aussehen: 421 Exec iv_Data Zeiger auf eine ServerList-Struktur, die wir noch besprechen werden. (*iv_Code)( ) Zeiger auf eine Routine, die die Verwaltung von mehreren Interrupt- Programmen übernimmt. *iv_Node Hier nicht belegt und hat den Wert Null. Diese Interrupt-Vector-Strukturen brauchen, sofern man einen Inter¬ rupt-Handler verwenden will, nicht von Hand initialisiert zu werden. Die Initialisierung geschieht beim Aufruf der Exec-Funktion Setlnt- Vector. Zur Benutzung eines Interrupts mit einem Interrupt-Handler muß die Interrupt-Struktur initialisiert und daraufhin die Setlnt- Vector-Funktion aufgerufejLwerden. Sehen wir uns einmal an, wie öin Interrupt weiterverarbeitet wird, nachdem er vom Prozessor zur Verarbeitung freigegeben wurde. Als Beispiel für die Verwaltung eines Interrupts folgt jetzt der Assembler- Teil, der einen Level-3(Priorität 3)ylnterrupt verwaltet. Bei $FC0CD8 ist der Einsprung, wo der Prozessor seine Arbeit nach Erkennen eines Level-3-Interrupts fortsetzt. Dieser Adressenwert stimmt nur mit der Kickstart-Version des Amiga 500 oder Amiga 2000 überein. Beim Amiga 1000 ist die Routine zwar gleich, sie ist je¬ doch, abhängig von der Kickstart-Version, leicht verschoben. moveni. l A6-A5/A1-AO/01-DO,-(A7) Register in den Stapel retten lea SdffOOO.AO Anfang der Register nach AO fflove.l S0004,A6 SysBase nach A6 fflove.w 28(A0},D1 Interrupt-Enable-Register lesen btst #14,Dl Master-Bit testen beq.l L1 Kein Interrupt zugelassen and.w 30(A0},D1 Hit Interrupt-Request-Register die zulässigen Interrupts herausfiltern btst #6,Dl Bit für Blitter done testen beq.s L2 Nein, anderes Bit testen moveni. l 156(A6},A5/A1 Aus IntVector-Struktur Zeiger auf Daten und Programm holen pea -36(A6) Rücksprung durch RTS Rücksprungadresse auf Exitlnter stellen jmp (A5) In Interrupt einspringen 422 Amiga intern L2: btst #5,Dl Bit für Raster-Interrupt testen beq.s L3 Nein, weiter testen movan. l 144(A6},AS/AI Aus IntVector Struktur Zeiger auf Daten und Programm holen pea -36(A6) Rücksprung auf ExitInter jmp ; sh_List Eine gewöhnliche List-Struktur. sh_Pad Wort, das nicht verwendet wird und nur dazu gebraucht wird, die Struktur auf Langwortgröße zu halten. Es hat immer den Wert Null. Fünf dieser Strukturen befinden sich innerhalb der ExecBase-Struktur hintereinanderliegend beginnend bei Offset 434. Wie Exec diese Interrupts verwaltet, wird durch die folgenden As¬ sembler-Programme verdeutlicht. Als erstes folgt nun die Dokumentation der Routine Cause(): fc1320 move.w «$4000.$dff09a fc1328 addq.b #1,294(A6) fc132c cnpi.b «S0b,8(A1) fc1332 beq.s $fc1370 fc1334 move.b «$0b,8(A1) fc133a moveq «SOO.DO fc133c move.b 9(A1},D0 fc1340 andi.w «SOOfO.DO fc1344 ext.w DO fc1346 lea 466(A6},A0 fc134a adda.w DO.AO fc134c lea 4(A0),A0 fei350 move.l 4(A0),D0 Alle Interrupts sperren IdNestCount hochzählen Typ auf SoftInt testen Verzweige, wenn Typ Softint Ansonsten neu Einträgen Priorität noch DO Unerlaubte Bits löschen und vorzeichenrichtig erweitern Zeiger auf Int. mit Pri. 0 Position der SoftlntList anhand der Priorität bestitmen Zeiger auf lh_Tail Zeiger auf letztes Glied nach DO Exec 427 fc1354 move. l A1,4(A0> fc1358 move.l A0,(A1) fc13Sa move.l D0,4(A1) fc135e move.l D0,A0 fc1360 move. l A1,{A0) fc1362 bset «S,292(A6) fc1368 move.w l»8004,Sdff09c fc1370 subq.b #1,294(46) fc1374 bge.s $fc137e fc1376 move.w #$c000,Sdff9a fc137e rts Neuen Interrupt als letzten eintragen Int.-Nachfolger auf Null setzen Zeiger auf Vorgänger setzen Zeiger auf früheren Int. dessen Nachfolger auf jetzigen im SysFlag Softint erlauben Interrupt verursachen IdNestCount verringern verzweige, wenn Int. noch nicht erlaubt werden darf Interrupts zulassen Rücksprung Anhand des Assembler-Listings können Sie sehen, wie ein Soft-Inter- rupt erzeugt wird. Bevor der Interrupt ausgeführt werden kann, muß dieser in eine der fünf SoftlntList-Strukturen eingetragen werden. In welche dieser Stukturen er eingetragen wird, hängt von seiner Priorität ab. Aus der Ermittlung der Struktur, in die er eingetragen werden soll, läßt sich ersehen, warum nur die Prioritäten -32, -16, yO, +16, +32 zu¬ lässig sind. Jede SoftlntList-Struktur ist 16 Bytes lang. Es wird ein Zeiger auf die mittlere Struktur erzeugt. Zu diesem Zeiger wird die Priorität addiert, wodurch die Position der gewUfischten Struktur er¬ mittelt wird. Nachdem die zugehörige SoftlntList-Struktur festgestellt wurde, wird die Struktur des zu erzeugenden Interrupts als letztes Glied in die Li¬ ste eingehängt und in der ExecBase-Struktur im "SysFlags" vermerkt, das ein Soft-Interrupt anliegt. Daraufhin wird im Interrupt-Request- Register das Bit zur Ausführung eines Soft-Interrupts gesetzt und dieser auch in der ExecBase-Struktur als erlaubt gekennzeichnet. Nachdem alles erledigt ist, werden die Interrupts, die zu Beginn der Routine gesperrt wurden, wieder freigegeben. Der Soft-Interrupt wird jetzt ausgeführt und aus der InterruptVector- Struktur der Zeiger auf das auszuführende Programm geholt, welches die SoftlntList-Strukturen verwaltet. Das Programm liegt ab $FC1380. fc1380 move.w #$0004,Sdff09c fc1388 bclr #S,292(A6) fc138e bne.s $fc1392 fc1390 rts fc1392 move.w #$0004,Sdff09a fc139a bra.s $fc13c2 fc139c move #$2700,SR fc13a0 move.l {A0),A1 fc13a2 move.l (AI),00 fc13a4 beq.s $fc13ae Interrupt-Request-Bit löschen SysFlag-Bit löschen, testen Verzweige, wenn Interrupt erlaubt Rücksprung Interrupt-Enable-Bit löschen Unbedingter Sprung Alle Interrupts vom Prozessor erlauben Zeiger auf ersten Interrupt Ist Node noch gültig? Verzweige, wenn kein Int. mehr 428 Amiga intern fc13a6 move.l 00,(AO) fcISaS exg D0,A1 fc13aa aiove. l A0,A(A1) fc13ae move.l D0,A1 fc13b0 aiove.b lll$02,8(A1) fc13b6 move «$2000,SR fc13ba Riovem. l U(A1),A5/A1 fc13c0 jsr (A5) fc13c2 Rioveq «$04,00 fc13c4 lea 498(A6),A0 fc13c8 Riove.w «$0004,$dff09c fc13d0 cmpa.l 8(A0),A0 fc13d4 bne.s $fc139c fc13d6 lea -16(A0),A0 fc13da dbf 00,$fc13d0 fc13de move «$2100,SR fc13e2 move.w «$8004,$dff09a fc13ea rts Ersten Interrupt aus Liste löschen AI und D1 austauschen ln_Pred auf Liste stellen Zeiger auf ersten Interrupt ln_Type auf Int. setzen Alle Interrupts sperren AI = is_Data. A5 = (*is_Code)() Einspringen Anzahl der Int.-Listen -1 Zeiger auf Int.-Liste mit höchster Priorität Int.-Reqest-Bit löschen Liste leer? Verzweige, wenn nicht leer Sonst Zeiger auf Int.-Liste mit niedrigerer Priorität setzen Verzweige, wenn nicht alle Listen durchsucht Alle Interrupts bis auf Pri. 1 sperren Soft-Interrupt wieder erlauben Rücksprung Als erstes wird in der soeben gezeigten Routine geprüft, ob das Bit, das die Zulassung eines Soft-Interrupts angibt, gesetzt ist, wie es von der Cause()-Funktion erledigt wird. Sollte dies der Fall sein, wird die Routine ausgeführt. Als nächstes wird das Interrupt-Request-Bit ge¬ löscht und die Soft-Interrupt-Listen auf ihre Interrupt-Strukturen durchsucht. Ist eine Liste abgearbeitet oder leer, wird die nächste Li¬ ste durchsucht. Die Listen werden in fallender Priorität bearbeitet. 2.10.3 Die CIA-Interrupts Nachdem wir die Interrupt-Verwaltung des Amiga besprochen haben, soll auf die Interrupts, die von den CIAs ausgelöst werden, hier ge¬ sondert eingegangen werden. Die Prozessor-Prioritäten der beiden Bausteine sind 6 für CIAB und 2 für CIAA und werden beide von ei¬ nem Interrupt-Server verwaltet. Folglich zeigt das iv_Code-Element der InterruptVektor-Struktur auf eine ServerList-Struktur und "(*iv_Code)()" auf die Routine zur Verwaltung der Serverliste. Die InterruptVector-Strukturen der Interrupts liegen für CIAA ab Offset 120 und für CIAB ab Offset 240 in der ExecBase-Struktur. Exec 429 2.10.3.1 Die CIA-Resource-Struktur Normalerweise befindet sich in der Serverliste der Interrupts nur eine Interrupt-Struktur. Diese Interrupt-Struktur ist jedoch in diesem Fall ein Bestandteil der CIA-Resource-Struktur. Der is_Data-Zeiger zeigt wieder auf die Resource-Struktur und "(*is_Code)()" zeigt auf eine Routine, die die Resource-Struktur verwaltet. Von dieser Routine werden mit Hilfe der Struktur alle CIA-Interrupts verwaltet, als da sind: Timer-A-Int«rrupt \ Timer-B-Intemipt ' EchtEcituhr-Alarm-Int«rrupt Serieller-Port oder Taatatur-Interrupt / Flag-Leitung-Interrupt Die CIA-Resource-Struktur ist im Grunde eine Libräry-Struktur, die jedoch noch um eigene Einträge erweitert wurde. Die CIA-Resource Struktur sieht wie folgt aus: Aufschlüsselungen der Unterstrukturen sind eingerückt dargestellt. Offset Bedeutung 0 struct Node lib_Node 0 Zeiger auf nächste Resource 4 Zeiger auf vorhergehende Resource 8 Node-type 9 Node-pri 10 Zeiger auf Resourcename u UBYTE lib-Flags \* $00 *\ 15 UBYTE libjaab \* $00 *\ 16 UUORD lib_NegSize \* $0018 *\ 18 UUORD lib_PosSize \* $007C *\ 20 UUORD lib_Version \* $0000 *\ 22 UUORD lib_Revision \* $0000 *\ 24 APTR lib_IdString \* $00000000 *\ 28 ULONG lib_Sijn \* $00000000 *\ 32 UUORD lib_OpenCnt \* $0000 *\ 34 APTR CiaStartPtr 38 UORD IntRequestBit 40 BYTE IntEnableCia 41 BYTE IntReqestCia 42 Struct Interrupt cia Interrupt 42 Zeiger auf nächsten Interrupt 46 Zeiger auf vorherigen Interrupt 50 Node-type 51 Node-pri 430 Amiga intern 52 Zeiger auf Interrupt-Namen 56 Zeiger auf Datenpuffer (hier Resource) 60 Zeiger auf auszuführenden Code 64 struct IntVector Timer A CIAA und CIAB 64 Nicht initialisiert (00000000) 68 Nicht initialisiert (00000000) 72 Nicht initialisiert (00000000) 76 struct IntVector Timer B CIAA: 76 Zeiger auf Datenpuffer 80 Einsprung bei tFE9726 84 Zeiger auf Interrupt-Struktur CIAB: 76 Nicht initialisiert (00000000) 80 Nicht initialisiert (00000000) 84 Nicht initialisiert (00000000) 88 struct IntVector TOD Alarm CIAA: 88 Nicht initialisiert (00000000) 92 Nicht initialisiert (00000000) 96 Nicht initialisiert (00000000) CIAB: 88 Zeiger auf graphics.library 92 Einsprung bei $FC6D68 96 Zeiger auf Interrupt-Struktur 100 struct IntVector Seriell Data CIAA: 100 Zeiger auf Keyboard.device 104 Einsprung Tastenabfrage ($FE571C) 108 Zeiger auf Interrupt-Struktur CIAB: 100 Nicht initialisiert (00000000) 104 Nicht initialisiert (00000000) 108 Nicht initialisiert (00000000) 112 struct IntVector Flag Leitung CIAA: 112 Nicht initialisiert (00000000) 114 Nicht initialisiert (00000000) 118 Nicht initialisiert (00000000) 431 Exec CIAB: 112 Zeiger auf disk.resource 1H Einsprung bei SFCAABO 118 Zeiger auf Interrupt-Struktur Ein paar Erläuterungen dürften zu einigen Strukturunte: zu machen sein. rp^li nkten noch CiaStartPtr Zeiger auf den Beginn der CIA-Register. Bei CIAA ist di und bei CIAB SBFDOOO. SBFEOOl IntRequestBit Bit, das im Interrupt-Request-Register gesetzt wird, wenn der Inter¬ rupt ausgelöst werden soll. Für CIAA ist dies $0008 und für CIAB $ 2000 . IntEnableCia Byte, in dem vermerkt ist, welcher CIA-Interrupt erlaubt oder nicht erlaubt ist. IntRequestCia Byte, in dem vermerkt wird, welcher CIA-Interrupt anliegt. In den IntVektor-Strukturen sind die Vektoren für die einzelnen CIA- Interrupts vermerkt. Nicht initialisierte Strukturen werden vom Be¬ triebssystem nicht benutzt und können für eigene Zwecke verwendet werden. 2.10.3.2 Die Verwaltung der Resource-Struktur Nachdem die Struktur als solches bekannt ist, wird jetzt näher auf die Routine eingegangen, von der sie verwaltet wird. In Al ist der Beginn der Resource-Struktur gespeichert. Einsprung von CIAB: fcA610 moveni.l A2/D2,-(A7) fcA6H move.b $bfddOO,D2 fc461a bra.s $fc4626 D2 und A2 retten Int.-Cont-Reg. nach D2 (CIAB) Unbedingter Sprung 432 Amiga intern Einsprung von CIAA: fc461c movem.l A2/D2,-(A7) fc4620 move.b $bfed01,D2 fc4626 bclr #7,02 fc462a or.b 41(A1),D2 fc462e move.b D2.41(A1) fcA632 and.b 40(A1),D2 fc4636 beq.s $fc4658 fc4638 move.l A1.A2 fc463a eor.b 02,41(A2) fcA63e Isr.b «1,02 fc4640 bcs.s $fc4ö5e fc4642 Isr.b «1,02 fc4644 bcs.s $fc4668 fc4ö46 beq.s $fc46S8 fc4ö48 Isr.b «1,02 fc464a bcs.s $fc4672 fc464c beq.s $fc4658 fc464e Isr.b «1,02 fc4650 bcs.s Sfc4ö7c fc4652 beq.s $fc4658 fc4654 Isr.b «1,02 fc4656 bcs.s $fc4686 fc4«58 movem. l ; sr_Link MinNode-Struktur, mit deren Hilfe die SemaphoreRequest-Struktur in die ss_WaitQueue-Liste eingehängt wird. *sr_Waiter Zeiger, der auf den Task (die Task-Struktur) zeigt, der auf die Be¬ nutzung der Semaphore wartet. Als letzte im Bunde existiert noch eine Semaphore-Struktur, die von den Exec-Funktionen nicht verwendet wird. Sie besteht nur aus einem um ein WORT erweiterten Message-Port. Da sie nicht von den Funk¬ tionen der Exec-Library unterstützt wird und ohnehin im Grunde nichts anderes als ein Message-Port darstellt, gehen wir nicht weiter auf sie ein, sondern führen sie nur der Vollständigkeit halber mit auf. Wenn in den folgenden Passagen von Semaphore gesprochen wird, ist immer die SignalSemaphore gemeint. struct Semaphore { /* Offsets */ struct HsgPort sm MsgPort; /* 00 $00 */ WORD sm“Bicls; /* 34 $22 */ >; #define sm_LockMsg mp_SigTssk Als nächstes folgt die Beschreibung der von Exec zur Verfügung ge¬ stellten Funktionen zur Bearbeitung der Signal-Semaphoren, aus denen sich genauere Informationen über die Verarbeitung von Semaphoren entnehmen lassen. Exec 445 2.11.2 Die Semaphore-Funktionen InitSemaphor« Funktion: InitSemaphoreCSignalSemaphore) AO Offset: -558, -$22E, $FDD2 Beschreibung Um eine Signal-Semaphore z)KÄfstSleni^müssen ihre Einträge initiali¬ siert werden. Diese Aufgab^ wird von der Funktion übernommen. Sie erstellt die MinList-Struktur, löscht den ss_NestCount-, den SS _QueueCount-Eintrag, sowie den Zeiger auf den Task. Für Sie bleibt lediglich, Typ und Namen in der Node-Struktur zu setzen, was für die Funktion der Semaphore nicht wichtig ist, jedoch zum besse¬ ren Programmierstil gehört. Wenn es sich bei Ihrer Semaphore um eine globale Semaphore handelt, so muß ein Name eingetragen werden, da die zugehörigen Tasks die Semaphore ansonsten nicht finden können. Es muß darauf geachtet werden, daß nicht mehrere verschiedene globale Semaphoren den gleichen Namen haben, weil in diesem Fall immer nur eine Semaphore gefunden werden kann. Als global gelten Semaphoren, die in der Semaphore-Liste der ExecBase-Struktur eingetragen sind (Offset 532). Parameter Signal Semaphore Zeiger auf eine Signal-Semaphore-Struktur. Dokumentation AO = Zeiger auf Signal-Semaphore. fc2d94 lea fc2d98 move.l fc2d9a addq.l fc2d9c clr.l fc2da0 move.l fc2da4 clr.l fc2da8 clr.u fc2dac move.w fc2db2 rts 16(A0),A1 A1,{A1) #4,(AI) 4{A1) A1,8(A1) 40(A0) U(A0) #$ffff,44{A0) Zeiger auf ss_UaitQueue Leere Liste erstellen Eintrag ss_Owner löschen ss_HestCount löschen ss_QueueCount auf -1 setzen Rücksprung 446 Amiga intern ObtafnSamaphor» Funktion: ObtainSeinaphore(SignalSemaphore) AO Offset: -564, -$234, *FDCC Beschreibung Die Funktion überprüft den ss_QueueCounter daraufhin, ob die Semaphore von einem Task zur Zeit benutzt wird. Wenn nicht, wird die Funktion beendet, wobei vermerkt wird, daß die Semaphore jetzt benutzt wird. Ist die Semaphore schon belegt, wird nachgesehen, ob es sich beim jetzigen Benutzer um den gleichen Task handelt. Wenn dies der Fall ist, bedeutet das, daß der Task erneut auf die "Zugriffsgenehmigung” warten will, obwohl er diese bereits hat. Seine erneute Anfrage wird nicht in die Liste eingereiht, sondern sofort bearbeitet. Dies ist eine Vorsichtsmaßnahme, um einen Absturz zu verhindern, der entstehen würde, wenn ein Task darauf wartet, sich selbst freizugeben (Dead Lock). Als letztes bleibt festzustellen, ob die Semaphore im Moment von ei¬ nem anderen Task benutzt wird. In diesem Fall wird im Stack eine SemaphoreRequest-Struktur erstellt, die ans Ende der ss_WaitQueue gehängt wird. Als weiteres wird in der Task-Struktur in tc_SigRecvd das Bit, das den Empfang eines Semaphore-Signals repräsentiert, ge¬ löscht. Zum Schluß wird der Task in den Wait-Status versetzt und erst wieder "erweckt", wenn ein entsprechendes Signal an ihn gesandt wird (siehe ReleaseSemaphoreO). Parameter SignalSemaphore Zeiger auf eine initialisierte Signal-Semaphore-Struktur. Dokumentation AO ° Zeiger auf Signal-Semaphore A6 > Zeiger auf ExecBase fc2db4 addq.b #1,295(A6) TDNestCnt +1 (Forbid) fc2db8 addq.u #1,44(A0) ss_QueueCount +1 fc2dbc fane.s $fc2dc6 Semaphore benutzt, Task in Warteliste eintragen fc2dbe Bove.l 276(A6),40(A0) ThisTask aus OwnerTask eintragen Exec 447 fc2dc4 bra.s Sfc2dfa fc2dc6 movein.l A1-A0/D1-D0,-(A7) fc2dca move.l 276(A6),A1 fc2dce cnpa.l 40(A0),A1 fc2dd2 beq.s Sfc2df6 Semaphore frei, freier Zugriff Register retten ThisTask nach A1 Ist Task bereits Owner der Semaphore Ja, Zugriff erlaubt Task in ss_WaitQueue/«inrelheltx Zu diesem Zweck wird im Stapel eine Semaphore-Request-Struktur ei^tellt, die in die Liste eingetragen wird. \ fc2dd4 lea -12(A7),A7 Struktur in Speicher anlegen fc2dd8 move.l A1,8(A7) Eigenen Task als sr_Uaiter eintragen fc2ddc bclr tif4,29(A1) Signal-Bit in Task-Strukt löschen fc2de2 lea 16(A0),A0 Zeiger auf ss_UaitOueue fc2deö move.l A7,A1 Zeiger auf SemaphoreRequest fc2de8 bsr. l Sfc15e8 Funktion: AddHeadO fc2dec moveq #$10,DO Signal-Bit für Uait fc2de« jsr •318(A6) WaitO; fc2df2 lea 12 Semaphore belegt Reply = 1 => Semaphore frei zur Benutzung Dokumentation AO = Zeiger auf Signal-Semaphore-Struktur A6 = Zeiger auf ExecBase fc2e68 move.l 276= bedeutet, daß das nachstehende Register ein Eingabeparameter ist. => signalisiert einen Ausgabeparameter. Programmbeginn: include "exec/types.i" include “exec/tasks.i" include "exec/nieiixjry.i" include "exec/libraries.i" include "exec/initializers.i" include "exec/execbase.i" include "exec/seniaphores.i" XLIB HACRO XREF LV0\1 ENDH ~ CALLSYS HACRO jsr LV0\1(a6) ENDH~ LENCNT HACRO nnve.l #\1End-\1,dO ENDH FORBID HACRO addq.b #1,TDNestCnt(a6) ENDN TS Sizel EOU 200 TS~Size2 EOU 200 EntryNutil EQU 2 EntryNimZ EOU 4 STRUCTURE NYNL.NL SIZE STRUCT HEI,ME SIZE ;HE für Task-Struct STRUCT HE2.ME~SIZE ;ME für Task-Stack LABEL MYHL SIZE1 STRUCT ME37mE SIZE ;HE für Task-Code STRUCT ME4,ME~SIZE LABEL MYML_SIZE2 ;ME für Task-Natne STRUCTURE MYRES.LIB SIZE LONG Counter STRUCT HYR NAME,20 LABEL MYRSIZE STRUCTURE MYMP,MP SIZE STRUCT Message,MN SIZE LABEL MYMP_SIZE STRUCTURE LocalReg,0 APTR CorrentTask APTR TCode LABEL LR_SIZE STRUCTURE Global,0 APTR TI APTR T2 WORD Task Cnt APTR Res “ APTR Semaphore APTR SenMemoryList APTR Port APTR PortHemoryList LABEL gl_sizeof XREF _Ab6ExecBase XLIB ÄddTask XLIB AllocSignal XLIB AddTail XLIB AllocEntry XLIB FreeEntry XLIB AllocMem XLIB FreeMem XLIB MakeLibrary XLIB OpenResource XLIB AddResource XLIB RemTask XLIB InitSemaphore XLIB ObtainSemaphore 458 Amiga intern XLIB XLIB XLIB XLIB XLIB XLIB XLIB XLIB XLIB XLIB Start: 1 $: Ende: ReleaseSemaphore FindSemaphore Enqueue Remove Permit AddPort UaitPort PutMsg GetHsg FindPort move.l _AbsExecBase,a6 lea -gl_sizeof(a7),a7 move.l a7,aS lea SemaphoreHaine(pc),aO move.b #0,d0 ;Pri bsr MakeSemaphore move.l dO.SenMemoryListCaS) bmi Fehlerl move.l d1,Semaphore(a5) bsr MakeResource tst.l dO beq Fehler2 lea PortNaine(pc),aO bsr NakePort move.l dO,PortMemoryList(a5) bmi Fehlers move.l d1,Port(85) clr.H Task_Cnt(a5) lea TName1,aO lea TCode1,a1 LEHCHT TCodel move.l #TS_Size1,d1 bsr HakeTask addq.w #1,Task_Cnt(a5) lea TNaine2,aO lea TCode2,a1 LENCNT TCode2 move.l #TS_Size2,d1 bsr MakeTask addq.w #1,Task_Cnt(a5) move.l Port(a5),a0 CALLSYS UaitPort move.l Port{a5),a0 CALLSYS GetHsg subq.u #1,Task Cnt(aS) bne 1$ move.l Port(85),a1 CALLSYS Remove ;Zeiger auf Hamen ;Zeiger auf Code ;Länge des Task-Codes ;Stacksize ;Zeiger auf Namen ;Zeiger auf Code ;Länge des Task-Codes ;StackSize Exec 459 move.l PortHeinoryList(a5),aO CALLSYS FreeEntry Fehlers: move.l Re8(a5),a1 CALLSYS Remove move.l Res(aS),a1 move.l «HYR SIZE.dO CALLSYS FreeHem FehlerZ: move.l Sefflaphore(aS),a1 CALLSYS Remove move.l SeiiMemoryList(a5).aO CALLSYS FreeEntry Fehlerl: lea gl_sizeof(a7),a7 rts ;Task erstellen ;>= AO = Zeiger auf Task-Namen ;>= A1 = Zeiger auf Task-Code ;>= DO = Länge vom Task-Code ;>= Dl = Stack-GröBe ;=> AO = Zeiger auf den Task ;=> AI = Zeiger auf den Task-Code MakeTask: movem.l aZ-aS/dZ,-(a7) lea -LR_SIZE(a7),a7 move.l a7,aS lea -MYML_SIZEZ(a7),a7 move.l a7,a4 move.l aO,aZ move.l a1,a3 ;Zeiger auf /Zeiger auf /Zeiger auf Mem-List-Struct Task-Namen Task-Code move.w #EntryNumZ,ML_NUHENTRIES(a4) /Anzahl der Entries lea HL ME(a4),aO /Zeiger auf Entries move. rillHEMF_PUBLIC!HEMF_CLEAR,dZ move.l dZ,(aO)+ move.l #TC_SIZE,(aO)+ move.l dZ,7aO)+ move.l d1,(a0)+ move.l dZ,(aO)+ move.l dO,(aO)+ move.l aZ,a1 bsr StrLenCnt move.l dZ,(aO)+ move.l dO,(aO)+ move.l a4,a0 CALLSYS AllocEntry tst.l dO bmi MT_Fehler move.l d0,a4 lea HL_ME(a4),aO move.l (a0)+,a1 move.l a1,CorrentTask(a5) addq.l #4,a0 /Req übergeben /Task-Struct-Größe /Req übergeben /Stack-Größe /Req übergeben /Task-Code /Zeiger auf Task-Namen /Länge vom Task-Namen => DO /Req übergeben /Länge von Namen eintragen /Zeiger auf Mem-List /Zeiger auf Entries /Zeiger auf Task 460 Amiga intern move.l (aO)+.dO move.l dO.TC_SPLOWER(a1) .-Zeiger auf Stack add.l (aO)+.dO .-Länge von Stack move.l dO.TC SPREG(a1> ;Obere Grenze vom Stack move.l dO.TC_SPUPPER(a1) ;Obere Grenze vom Stack move.l (a0)+.a1 ;Zeiger auf Speicher für T-Code move.l al.TCode(aS) .-Zeiger retten move.l (aO)+.dO ;Anzahl der Zeichen Isr.l «1.d0 .-Byte zu Uort 3S: move.w (a3)+,{a1)+ subq.l #1.d0 bne 3S ;T-Code kopieren move.l (aO).al move.l 4= AI = Zeiger auf den String ;=> DO = Länge des Strings StrLenCnt move.l a1.dO beq IS move.l #-1.dO 2S: tst.b (a1)+ dbeq d0.2S not.l dO addq.l #1.dO 1$: rts .■Erstellt Semaphore und trägt sie in die Semaphore-Liste ein ;>= AO = Zeiger auf Semaphoren-Namen ;>= DO = Priorität 461 Exec ;=> Dl = Zeiger auf Semaphore ;=> 00 = Zeiger auf Mem-List-Struct ; Bit 31 gesetzt = Fehler MakeSemaphore: movem.l a2-a4/d2,-(a7) move.l a0,a3 move.l d0,d2 lea -MYML_SIZE1(a7).a7 move.l a7,s4 move.u #EntryNijn1,ML_NUMENTRIES(a4) lea ME1(s4),aO move.l #HEMF_PUBLIC!MEMF_CLEAR,d1 move.l d1,(a0)+ move.l #SS_SIZE,(aO)+ move.l a3,a1 bsr StrLenCnt lea ME2(a4).aO move.l d1,(a0)+ move.l d0,(a0) move.l a4,a0 CALLSYS AllocEntry movea.l d0,a4 tst.l dO bmi 3$ move.l HE1(a4),aO move.l a0,a2 move.b #NT SEMAPHORE,LN TYPE(aO) move.b d2,LN_PRI(aO) CALLSYS InitSemaphore move.l ME2(a4),a1 move.l ME2+ME LENGTH(a4),dO move.l a1,LN_NAHE(a2) move.l a3,a0 bra IS 2$: move.b (a0)+,(a1)+ 1$: dbf d0,2$ lea SemaphoreList(a6),aO move.l a2,a1 FORBIO CALLSYS Enqueue CALLSYS Permit move.l a2,d1 move.l a4,d0 3$: lea MYML_SIZE1(a7),a7 movem.l 7a7)+,a2-a4/d2 rts MakePort: ;>= AO = Zeiger auf einen Port-Namen ;=> 00 = Zeiger auf die Mem-List-Stuct ;=> 01 = Zeiger auf den Port movem.l a2-a4,-(a7) move.l a0,a3 lea -MYML_SIZE1(a7),a7 move.l a7,a4 462 Amiga intern move.w #EntryNum1,ML NUMENTRIES(a4) lea ME1(a4),aO move.l )IIHEHF_PUBLIC!MEMF_CLEAR,d1 move.l dlfCaO)-*- move.l )mYHP_SIZE,(aO)+ move.l a3,a1 bsr StrLenCnt lea ME2(a4),aO move.l dlfCaO)-*- move.l dO.(aO) move.l a4,a0 CALLSYS AllocEntry movea.l d0,a4 tst.l dO bmi 3$ move.l ME1(a4),eO move.l a0,a2 lea MP_HSGLlST(aO).aO NEULIST AO move.b #NT_MSGPORT,LN_TYPE(aO) move.l #-1,d0 CALLSYS AllocSignal tst.l dO bpi 4$ move.l a4,a0 CALLSYS FreeEntry move.l #-1,d0 bra 3$ 4$: move.b dO,HP_SIGBIT(a2} move.l ThisTask(a6),HP SIGTASK(a2} move.b #PA_SIGNAL,MP_FLAGS(b2) move.l HE2(a4),a1 move.l HE2+ME LENGTH(a4},dO move.l a1,LN_NAME(a2) move.l a3,a0 bra 1$ 2$: move.b (a0)+,(a1)+ 1$: dbf d0,2$ move.l a2,a1 CALLSYS AddPort move.l a4,d0 /Zeiger auf Mem-List move.l a2,d1 /Zeiger auf Port 3$: lea HYHL_SIZE1(a7),a7 movem.l Ta7)+,82-a4 rts HakeResource: move.l a2,-(a7) lea FunkTab(pc),aO lea ResStruct(pc),a1 lea Reslnit(pc),a2 move.l «HYR_SlZE,dO clr.l dl CALLSYS HakeLibrary move.l dO,Res(a5) beq 1$ Exec 463 move.l d0,a1 inove.l d0,a2 CALLSYS AddResource move.l a2,d0 1*: move.l (a7)+,a2 rts Reslnit: movem.l d0/d2/a2,-(a7) move.l d0,d2 lea ResName(pc),a1 move.l a1,a2 bsr StrLenCnt move.l d2,a0 lea HYR_NAHE(aO),a1 move.l a1,LN_NAHE(aO) 1*: move.b (a2)+7(a1)+ dbf dO,1S movem.l (a7)+,d0/d2/a2 rts TCodel: move.l _AbsExecBase,a6 lea ResName1(pc),a1 clr.l dO CALLSYS OpenResource tst.l dO beq 2$ move.l d0,a2 lea PortName1(pc),a1 CALLSYS FindPort tst.l dO beq 2S move.l d0,a3 lea SeiiMarae1(pc),a1 CALLSYS FindSemaphore tst.l dO beq 2$ move.l dO,aO move.l a0,a4 CALLSYS ObtainSemaphore clr.l Counter(a2} IS: addq.l #1,Counter(a2) move.w Counter+2(a2>,Sdff180 cmpi.l #$20000,Counter(a2) bis 1S move.l a4,a0 CALLSYS ReleaseSemaphore move.l a3,a0 lea Message(a3),a1 CALLSYS PutMsg 2$: move.l ThisTask(a6),a1 CALLSYS RemTask rts ResNamel: dc.b 'test.resource'.O SenMamel; dc.b 'test.Semaphore',0 PortNamel: dc.b 'test.port',0 CNOP 0,2 TCodelEnd: TCode2: move.l _AbsExecBase,a6 lea ResNaine2(pc),a1 clr.l dO CALLSYS OpenResource tst.l dO beq 3$ move.l d0,a2 lea PortName2(pc),a1 CALLSYS FindPort tst.l dO beq 2$ move.l d0,a4 lea SemName2(pc),a1 CALLSYS FindSemaphore tst.l dO beq 2$ move.l d0,a3 move.l a3,a0 CALLSYS ObtainSemaphore clr.l Counter(a2) 1$: move.l inl0000,d0 2$: sub.l #1,d0 bne 2$ bchg #1,$BFE001 add.l #1,Counter(82) cmpl.l #10,Counter(a2) bne 1$ move.l a3,a0 CALLSYS ReleaseSemaphore move.l aA,aO lea Message(aA),a1 CALLSYS PutMsg 3$: move.l ThisTask(a6),a1 CALLSYS RemTask rts ResNanie2; dc.b 'test.resource',0 SeniNaine2: dc.b 'test.Semaphore',0 PortName2: dc.b 'test.port',0 CNOP 0,2 TCode2EtyJ: ResStruct: INITBYTE LN_TYPE,NT_RESOURCE dc.l 0 Exec 465 FunkTab: dc.l -1 ResNatne: dc.b 'test.resource',0 SemaphoreNatne: dc.b 'lest.Semaphore',0 PortNatne: dc.b 'test.port',0 TNatnel: dc.b 'Taskl'.O TNatne2: dc.b 'Task2',0 END 2.12 Die RAM-Ubrary Mancher von Ihnen wird sich die Vektoren der Exec-Library einmal genauer angesehen haben, wobei er gemerkt hat, daß nicht alle Ein¬ sprünge ins ROM weisen, sondern zum Teil in den RAM-Bereich des Amiga. Da die Exec-Library vollständig im ROM abgelegt ist und deshalb nicht nachgeladen werden muß, ist der Einsprung ins RAM auf den ersten Blick recht erstaunlich. Wie man nach näherem Betrachten feststellt, ist der "Aufenthalt" im RAM-Bereich nicht von großer Dauer, denn von hier aus wird relativ schnell wieder auf das ROM zugegriffen. Wie auch aus der Kapitelüberschrift leicht zu erraten ist, haben diese RAM-Einsprünge etwas mit der RAM-Library zu tun. Exec stellt Funktionen zum öffnen und Schließen von Libraries und Devices zur Verfügung. Diese Exec-Funktionen beziehen sich jeoch nur auf bereits im RAM vorhandene Libraries und Devices. Wer je¬ doch die entsprechenden Funktionen wie beispielsweise OpenLibrary benutzt, weiß, daß sich mit dieser Funktion auch Libraries aus dem LIBS:-Ordner der Boot-Diskette öffnen lassen (z.B. Diskfont-Library). Beim Laden von Libraries handelt es sich um eine Erweiterung zu der elementaren Exec-Funktion. Solche Erweiterungen sind mit Hilfe der RAM-Library implementiert. Die RAM-Library stellt eine Schnitt¬ stelle zwischen den Exec-Basisroutinen und den Erweiterungen dar. Über Exec wird zuerst in die RAM-Library verzweigt, daraufhin der Zeiger auf die RAM-Library übergeben und in die Erweiterung ver¬ zweigt, von der aus über die Vektoren der RAM-Library in die Exec- Basisroutine verzweigt wird. Folgende Exec-Funktionen verwaltet die RAM-Library: 466 Amiga intern Offaet von RAM-Library Exec-Funktion -30,.-SIE, SFFE2 -36, -$24, SFFDC -42, -$2A, $FFD6 -48, -$30, SFFDO -54, -S36, SFFCA -60, -S3C, SFFC4 -66, -S42, SFFBE AllocMem OpanLibrary OpenDevice CloacLibrary CloaeDevica RemLibrary RemDavice Erweiterungen AllocMem Bei der AllocMem-Funktion wird, nachdem festgestellt wird, daß nicht genügend Speicher zur Verfügung steht, von der Erweiterung versucht, ungebrauchte Libraries zu entfernen, und erneut geprüft, ob genügend Speicher erreichbar ist. OpenLibrary Zusätzlich zur "normalen" OpenLibrary-Funktion wird im LIBS:-Ord- ner nachgesehen, ob die gewünschte Library vorhanden ist, und gege¬ benenfalls geladen und geöffnet. OpenDevice Die RAM-Library-Funktion arbeitet wie die OpenLibrary-Funktion, aber mit dem Unterschied, daß es sich um ein Device handelt und deshalb im DEVS:-Ordner gesucht werden muß. Für beide Funktionen wird die gleiche Routine mit anderen Übergabeparametern benutzt. CloseLibrary Falls eine Library beim Laden von Disk in mehrere Segmente zerlegt wurde, werden diese beim Entfernen der Library freigegeben. CloseDevice Die Funktion entspricht der Funktion CloseLibrary, bezieht sich je¬ doch auf von Disk geladene Devices. 467 Exec RemLibrary Die Funktion arbeitet wie die CloseLibrary-Funktion. Sollten mehrere Segmente bestehen, werden alle freigegeben. RemDevice Wie RemLibrary, nur für Devices. Es kann schon an dieser Stelle gesagt werden, daß die RAM-Library kaum für eigene Projekte genutzt werden kann. Wer jedoch von sich behaupten will, seinen Amiga wirklich zu kennen, sollte sich mit dem Aufbau dieser eigentümlichen Library vertraut machen. Sehen wir uns den Aufbau der RAM-Library einmal genau an. Sie beginnt wie jede normale Library, die von Disk geladen wird. Offset 00 $00 struct Library Strukur einer Norm-Library 34 $22 APTR ExecBase Zeiger auf ExecBase 38 $26 APTR DosBase Zeiger auf DosBase 42 $2A APTR Seglist Zeiger auf Segmentliste (00) Erstaunlicherweise folgen nun keine weiteren Dateneintragungen, son¬ dern Programmteile, die von einigen Funktionen der Exec-Library aufgerufen werden. Einsprung von AllocMem. subq.l #8,A7 pea $fe4dc2 bra.s Skipl Einsprung von Openlibrary. subq.l #8,A7 pea $fe4b62 bra.s Skipl Einsprung von OpenDevice. subq.l m,K7 pea $fe4b76 bra.s Skipl Einsprung von CloseLibrary. Platz im Stapel schaffen Einsprung für AllocMem Ins ROM einspringen Platz im Stapel schaffen Einsprung für Openlibrary Ins ROH einspringen Platz im Stapel schaffen Einsprung für OpenDevice Ins ROM einspringen subq.l #8,A7 Platz im Stapel schaffen 468 Amiga intern pea $fe4d9c bra.s Skip1 Einsprung von CloseDevice. subq.l #8,A7 pea $fe4da2 bra.s Skip1 Einsprung von RemLibrary. subq.l #8,A7 pea SfeAdbc bra.s Skip1 Einsprung von RemDevice. subq.l #8,A7 pea tfe4db6 bra.s Skip1 Einsprung für CloseLibrary Ins ROM einspringen Platz im Stapel schaffen Einsprung für CloseDevice Ins ROM einspringen Platz im Stapel schaffen Einsprung für RemLibrary Ins ROM einspringen Platz im Stapel schaffen Einsprung für RemDevice Ins ROM einspringen Als nächstes folgen wieder zwei Struktureinträge. Offset: 116 $74 BYTE Flag Flag für Expunge (s. Library) 117 $75 BYTE pad Adresse begradigen Hier geht das Programm weiter. Skipl: move.l a5,8(a7) A5 retten lea Skip2(PC),A5 Zeiger auf Rücksprung move.l A5,4(A7) Zeiger in Stack eintragen lea RamLib; #define SYSBASESIZE ((long)sizeof(struct ExecBase)) #define AFB_68010 OL #define AFB_68020 1L #define AFB_68881 4L #define AFF_6a010 (1L«0) üWefine AFF_6a020 (1L«1) #define AFF_68881 (1L«4) #define AFB~RESERVED8 8L #define AFb''reserveD9 9L LibNode Offset 0 Die Library-Struktur der Exec-Library, mit einer positiven Größe von $24C und einer negativen Größe von $276 Bytes. Anhand der positi¬ ven Größe kann man sehen, daß die ExecBase-Struktur in Wirklich¬ keit eine etwas groß geratene Library-Struktur ist. SoftVer Offset 34 LowMemChkSum Offset 36 Kann vom Programmierer verwendet werden, um die Prüfsumme, die über den Bereich von Offset 34 bis 78 berechnet wird, auszugleichen, falls eigene Vektoren eingefügt wurden. Wie die eigentliche Prüf¬ summe berechnet wird, kann man sich ChkSum (Offset 82) sehen. ChkBase Offset 38 Wird gebraucht, um die Position von EexecBase beim Reset zu über¬ prüfen. Die Position von Execbase wird zu ChkBase addiert, worauf das Ergebnis SFFFFFFFF sein muß. Ist dies nicht der Fall, muß es zu einem größeren Fehler gekommen sein, weshalb die ExecBase-Struktur 472 Amiga intern besser neu erstellt werden sollte. Ansonsten wird Zeit gespart und versucht, die Struktur nicht vollständig zu initialisieren. ColdCapture Offset 42 Vektor, der vom Programmierer benutzt werden kann, um während eines Resets in eine eigene Routine zu verzweigen. Ist der Vektor nicht benutzt, so steht er auf Null. Von der Resetroutine wird er¬ kannt, ob der Vektor gesetzt wurde, und in die angegebene Routine verzweigt. In A5 wird die Rücksprungadresse abgelegt. Vor dem Ein¬ sprung über den Vektor wird dieser automatisch wieder auf Null ge¬ setzt. Bis zu diesem Zeitpunkt ist bis auf das Sperren der Interrupts und des DMA-Zugriffs noch nichts Nennenswertes passiert. Man sollte bei der eigenen Routine keine Operationen durchführen, die mit dem Stapel arbeiten, da dieser noch nicht korrekt initialisiert ist. Das ist auch der Grund, warum der Rücksprung in A5 übergeben und die Routine nicht über einen JSR aufgerufen wird. CoolCapture Offset 46 Kann auch genutzt werden, um aus dem Reset in eine eigene Routine zu verzweigen. Der Unterschied zwischen ColdCapture und CoolCap¬ ture besteht darin, daß CoolCapture zu einem wesentlich späteren Zeitpunkt die eigene Routine auf ruft. CoolCapture wird nicht von der Reset-Routine zurückgesetzt. Da der Stapel, der Speicher, die Excep- tion-Tabelle und die Exec-Library hier schon initialisiert sind, ist der Vektor für die meisten Anwendungen besser geeignet als der Cold¬ Capture-Vektor. Aus der eigenen Routine kann mit RTS wieder in der Reset-Routine eingesprungen werden. WarmCapture Offset 50 Reset-Vektor, über den jedoch unseres Wissens nicht gesprungen wird. SysStkUpper Offset 54 Die oberste Grenze des Supervisor-Stacks. SysStkLower Die untere Grenze des Supervisor-Stacks. Offset 58 473 Exec MaxLocMem Offset 62 Maximal erreichbarer Chip-Memory-Bereich an, also 512 KB oder $80000 Bytes. DebugEntry Offset 66 Zeiger auf den Einsprung in den Debugger des Amiga. DebugData Offset 70 Zeiger auf den Datenpuffer des Debuggers (Null). AlertData Offset 74 MaxExtMem Offset 78 Oberste Grenze des zur Verfügung stehenden Speichers. Mit einer Speichererweiterung auf 1 MB ist dies $C80000. ChkSum Offset 82 Prüfsumme über den Bereich von Offset 34 bis 78 und wird vor dem Einsprung in ColdCapture geprüft. Wenn eigene Vektoren in diesem Bereich eingebunden werden, muß die Prüfsumme neu berechnet oder in LowChkSum ausgeglichen werden. Die Prüfsumme berechnet sich wie folgt: fc0440 lea fc0444 tnove.w fc0448 add.w fc044a dbf fc044e not.w fc0450 tnove.w 34(A6},A0 «$0016,00 (A0)+,D1 D0,$fc0448 01 01,82(A6} Zeiger auf Anfang setzen Anzahl der Worte -1 in Zähler Worte addieren Zähler verringern Negieren und in ChkSun speichern IntVectsfO] Offset 84 Interrupt bei serieller Ausgabe. Nach Reset nicht initialisiert. IntVectsfl] Offset 96 Interrupt bei Beendigung der Übertragung des Diskblocks. Nach Reset ist dies ein Interrupt-Handler. 474 Amiga intern IntVects[2] Offset 108 Soft-Interrupt. Für genauere Beschreibung siehe Kapitel 2.6 IntVectsfS] Offset 120 CIAA-Interrupt. Nach Reset dient der Interrupt hauptsächlich zur Tastaturabfrage (Interrupt-Server). IntVects[4 ] Offset 132 Copper-Interrupt. IntVectsfS] Offset 144 Interrupt, der ausgelöst wird, wenn der Rasterstrahl Rasterzeile Null passiert. Der Interrupt ist auch das Taktsignal für das Task-Switching (Interrupt-Server). IntVectsfö] Offset 156 Der Interrupt wird ausgelöst, wenn der Blitter mit seiner Arbeit fertig ist (Interrupt-Handler). IntVects[7] Offset 168 Audiokanal 0 (Interrupt-Handler). IntVects[8] Offset 180 Audiokanal 1 (Interrupt-Handler). IntVects[9] Offset 192 Audiokanal 2 (Interrupt-Handler). IntVectsf 10 ] Audiokanal 3 (Interrupt-Handler). Offset 204 Exec 475 IntVectsf 11 ] Offset 216 Der Interrupt wird ausgelöst, wenn eine serielle Eingabe erfolgt ist (nach Reset nicht initialisiert). IntVectsf 12] Offset 228 Wird bei Synchronisation der Diskette ausgelöst (Interrupt-Handler). IntVectsf 13] Offset 240 Wird bei Interrupt von CIAB ausgelöst (Interrupt-Server). IntVectsf 14] Offset 252 Kann nur softwaremäßig ausgelöst werden (n. Reset nicht initialisiert). IntVectsf 15] Offset 264 Nicht maskierbarer Interrupt. Der Interrupt wird zwar nicht verwen¬ det, ist jedoch als Interrupt-Server initialisiert. *ThisTask Offset 276 Zeiger auf die Task-Struktur, die gerade bearbeitet wird. Den Zeiger auf diese Task-Struktur kann man nicht mit einem Programm, das in einem Task läuft, auslesen und sinnvolle Werte erhalten, da man im¬ mer den Zeiger auf die eigene Task-Struktur erhält. Die einzige Mög¬ lichkeit, die man hat, sinnvolle Werte zu erhalten, besteht darin, den Wert aus einem Interrupt zu lesen. Hierfür bietet sich der Interrupt 5 (Rasterstrahl) an. idleCount DispCount Quantum Offset 280 Offset 284 Offset 288 Elapsed Offset 290 476 Amiga intern SysFlags Offset 292 In diesem Flag werden für das System wichtige Bits gesetzt. Bit 5: 0 = Soft-Interrupt nicht erlaubt, 1 = erlaubt. IDNestCnt Offset 294 Gibt an, ob Interrupts erlaubt sind. Steht IDNestCnt auf $FF (-1) sind Interrupts erlaubt, ansonsten wurden sie mit der Disable-Funktion gesperrt. Jedesmal, wenn die Disable-Funktion aufgerufen wird, wird IDNestCnt um eins erhöht. Bei der Enable-Funktion wird IDNestCnt wieder verringert. Erst wenn IDNestCnt auf -1 steht, werden die In¬ terrupts wieder ermöglicht (das Master-Bit wird wieder gesetzt). TDNestCnt Offset 295 Gibt an, ob die Forbid-Funktion aufgerufen wurde. Wurde sie aufge¬ rufen, so wird TDNestCnt um eins erhöht. Das Umschalten zu einem anderen Task ist erlaubt, wenn TDNestCnt nicht auf -1 steht. Durch die Permit-Funktion wird TDNestCnt wieder herabgezählt und auch das Umschalten auf einen anderen Task wieder ermöglicht, sofern TDNestCnt wieder auf -1 steht. AttnFlags Offset 296 Gibt an, welche Prozessoren angeschlossen sind: #define AFF 68010 (1L«0) #dcfine AFfI68020 <1L«1) #define AFF_68881 <1L«4) AttnResched Offset 298 Resmodules Offset 300 Zeiger auf resistente Module. Dies sind Strukturen, über die Routinen von einer Routine ab SfCOAFO aufgerufen werden. Die Module wer¬ den bei einem Reset aufgerufen. Es steht auch eine Funktion zur Verfügung, mit der sich diese Module nach ihren Namen suchen las¬ sen. Sie nennt sich FindResident. Exec 477 TaskTrapCode TaskExceptCode TaskExitCode TaskSigAlloc TaskTrapAlloc MemList Zeiger auf die Speicherliste, wo reich frei oder belegt ist. Offset 304 Offset 308 Offset 312 Offset 316 Offset 320 Offset 322 verzeichnet ist, welcher Speicherbe- ResourceList Offset 336 List-Struktur, in der die Resource-Strukturen verkettet sind. DeviceList Offset 350 List-Struktur, in der die Resource-Strukturen verkettet sind. IntList Offset 364 Wird nicht verwendet. LibList Offset 378 List-Struktur, in der die Library-Strukturen verkettet sind. Port List Offset 392 List-Struktur, in der die Port-Strukturen verkettet sind. TaskReady Offset 406 List-Struktur, in der die Task-Strukturen verkettet sind, die zur Zeit auf "Ready" stehen. 478 Amiga intern TaskWait Offset 420 List-Struktur, in der die Task-Strukturen verkettet sind, die zur Zeit auf "Wait" stehen. SoftlntsfO] Offset 434 List-Struktur, in der die Soft-Interrupts verkettet sind, die zur Zeit auf ihre Abarbeitung warten und die Priorität -32 haben. SoftlnsflJ Offset 450 List-Struktur, in der die Soft-Interrupts verkettet sind, die zur Zeit auf ihre Abarbeitung warten und die Priorität -16 haben. SoftIns[2] Offset 466 List-Struktur, in der die Soft-Interrupts verkettet sind, die zur Zeit auf ihre Abarbeitung warten und die Priorität 0 haben. SoftlnsfS] Offset 482 List-Struktur, in der die Soft-Interrupts verkettet sind, die zur Zeit auf ihre Abarbeitung warten und die Priorität +16 haben. SoftIns[4] Offset 498 List-Struktur, in der die Soft-Interrupts verkettet sind, die zur Zeit auf ihre Abarbeitung warten und die Priorität +32 haben. LastAlert[4] Offset 514 Hier werden die Daten für den Alert zwischengespeichert, nachdem sie von der Reset-Routine geholt wurden. VBlankFrequency Offset 530 Frequenz des Rasterstrahls, der ein Bild aufbaut. (50Hz). PowerSupplyFrequency Offset 531 Frequenz der Netzspannung, mit der der Amiga versorgt wird (50Hz). Exec 479 SemaphoreList Offset 532 List-Struktur, in der alle Semaphore-Strukturen, sofern sie benutzt werden, verkettet sind. KickMemPtr Offset 546 Zeiger auf eine MemList-Struktur, deren Speicher bei einem Reset wieder belegt wird. KickTagPtr Offset 550 Zeiger auf eine Resident-Tabelle, die bei der Erstellung der Haupt- Resident-Tabelle eingebunden wird. KickCheckSum Offset 554 Prüfsumme, die durch die Funktion SumKickData() berechnet wied. ExecBaseReservedf 10] Offset 558 10 Bytes, die für die ExecBase-Struktur reserviert sind, um Exec die Möglichkeit zu geben, beliebige Werte dort zu speichern (n. genutzt). ExecBaseNewReservedf20] Offset 568 20 Bytes, die für die ExecBase-Struktur reserviert sind, um Exec die Möglichkeit zu geben, beliebige Werte dort zu speichern (n. genutzt). 2.14 Reset-Routine und resetfeste Programme In diesem Kapitel wird der Frage nachgegangen, wie die Reset-Rou¬ tine genau abläuft, wie die Speichergröße bestimmt wird und ob es möglich ist, resetfeste Programme zur schreiben. 2. 1 4 .1 Dokumentation der Reset-Routine fc00d2 lea fcOOdS move.l fcOOde subq.l fcOOeO bgt.s fcOOeZ les S040000,A7 #$00020000,00 # 1,00 SfcOOde -228(PC)(=$fcOOOO),AO Stack-Pointer setzen Wert für Uarteschleife Wert verringern Verzweige, wenn nicht abgezählt Zeiger auf Kickstart- ROH setzen 480 Amiga intern fc00e6 lea $fOOOOO,A1 Vergleichsuert laden . fcOOec cmpa. l A1,A0 Liegt Reset ab SFOOOOO fcOOee beq.s SfcOOfe Verzweige, wenn ja fcOOfO lea 12(PC)(=$fc00fe),A5 Zeiger auf Programn- fortsetzung stellen fc00f4 cmpi.u #$1111,(AI) Liegt ab SFOOOOO Modul? fcOOfS bne.s $fcOOfe Verzweige, wenn nein fcOOfa jmp 2(A1) Sonst einspringen fcOOfe move.b #$03,$bfe201 Port auf Ausgang schalten fc0106 move.b #S02,$bfe001 LED ausschalten fcOIOe lea $dff000,A4 Zeiger auf Chip-Adressen fcOlU move.u #$7fff,D0 Uert laden fcOIIS move.w D0,154(A4) Alle Interrupts sperren fcOllc move.u D0,156(A4) Interrupt-Anfragen löschen fc0120 fc0124 fc012a move.u move.u move.u D0,150(A4) #$0200,256(A4) #$0000,272(A4) DMA-Zugriff sperren fc0130 move.u #$0444,3a4(A4) Farbe setzen fc0136 move.u #$0008,AO Zeiger auf Exceptions setzen fc013a move.u #$002d,D1 Zähler für Anzahl der Vektoren fc013e lea 1140(PC)(=$fc05b4),A1 Zeiger auf Harderror Routine setzen fc0142 move.l A1,(A0)+ Exceptions eintragen fc0144 dbf D1,$fc0142 Verzweige, wenn nicht fertig fc0148 bra. l $fc30c4 Guru prüfen Prüfen, ob Reset durch Guru entstanden, falls ja, Guru-Nummer D7 und Memory-Ptr. in D6 übernehmen. Ansonsten wird in D6 SFFFFFFFF geladen. Daraufhin wird der Reset weitergeführt. fc014c move.l $0004,00 ExecBase holen fcOISO btst #0,D0 ExecBase auf gerader Adresse? fc0154 bne.s SfcOlce Fehler, wenn ungerade fc0156 move.l 00, A6 Execbase nach 00 fc0158 add.l 38(A6),D0 ChkBase hinzuaddieren fcOISc not. l 00 Ergebnis invertieren fcOISe bne.s SfcOlce Verzweige, wenn Fehler Der Fehler falsch ist. tritt auf, wenn der Zeiger auf die ExecBase-Struktur fc0160 moveq #$00,01 01 für Prüfsumme löschen fc0162 lea 34(A6),A0 Zeiger auf ChkBase stellen fc0166 moveq #$18,00 Uert für Schleifen-Zähler fc0168 add.w (A0)+,D1 Prüfsumme bi Iden fc016a dbf D0,$fc0168 Verzweige, bis Summe gebildet fc016e not.w 01 Ergebnis invertieren fc0170 bne.s SfcOlce Fehler, wenn nicht Null fc0172 move.l 42{A6),D0 ColdCapture nach 00 fc0176 beq.s $fc0184 Verzweige, wenn nicht gesetzt fc0178 move.l 00,AO Pointer nach AO fc017a lea 8{PC)(=$fc0184),A5 1 Zeiger auf Fortsetzung fc017e clr.l 42(A6) ColdCapture löschen fc0182 jmp (AO) Einspringen fc0184 bchg #1,$bfe001 LED anschalten Exec 481 fcOISc move.l fc0190 cmp.l fcOIM bne.s fc0196 move.l fc019a cmpa.l fcOlaO bhi.s fc01a2 cmpa.l fcOlaS bcs.s fcOlaa move.l fcOlae move.l fcOlbO beq.l fc01b4 cmpa.l fcOlba bhi.s fcOlbc cmpa.l fc01c2 bcs.s fc01c4 move.l fc01c6 andi.l fcOlcc beq.s -382(PC)(=$fc0010),D0 20(A6),D0 SfcOlce 62(A6),A3 «$00080000,A3 SfcOlce lll$00040000,A3 SfcOlce 7B(A6},A4 A4,D0 $fc0240 IISOOdcOOOO.A4 $fc01ce IIS00c40000,A4 $fc01ce A4.DO «$0003ffff,D0 $fc0240 Kick.-Version mit der der Execlib. vergleichen Fehler, nenn Versionen ungleich Obere Grenze des Chip-Hemory 512 KB Chip-Hemory? Fehler, uenn größer 256 KB Chip-Hemory? Fehler, wenn kleiner HaxExtHem nach A4 Prüfe ob externer Speicher da Verzweige, wenn nicht vorhanden Obere Grenze bei SOCOOOO? Fehler, wenn höher Obere Grenze bei $C40000? Fehler, wenn niedriger Unsinnig, da A4 = DO Liegt Grenze auf glatter Adr.? Ja, sonst Fehler Hier beginnt der Teil der Routine, der nur angesprungen wird, wenn etwas mit der Initialisierung der Vorgefundenen ExecBase-Struktur nicht stimmt. Sie muß folglich neu initialisiert werden. fcOlce lea $0400,A6 fc01d2 suba.w «SfdSa.AÖ fcOldö lea fcOldc lea fc01e2 lea fcOleö bra.l $cOOOOO,AO $dc0000,A1 6 Bei diesem Reset-Einsprung blinkt die LED elfmal auf, worauf in das Boot-ROM eingesprungen und der Reset erneut gestartet wird. Zu dieser Routine wird auch verzweigt, wenn eine Exception-Bedingung während des ersten Teils des Resets auftritt. fc0240 lea $dff000,A0 Zeiger auf Chip-Adressen fc0246 move.w #$7fff,150(A0) DMA-Zugriffe sperren fc024c move.w «$0200,256(AO) fc0252 move.w #$0000,272(A0) fc0258 move.w «S0888,384(A0) Bildfarbe setzen fc025e lea 84(A6),A0 Zeiger auf ersten IntVector fc0262 movem.l 546; #define RTC_MATCHWORD 0x4AFCL #define RTF AUTOINIT (1L«7) Exec 489 #define RTF_COLDSTART (1L«0) mdefine RTH UHEN 3L #define RTU“mEVER OL mdefine RTu'cOLDSTART 1L #endif rt_MatchWord Wort, an dem die Struktur im Speicher erkannt wird. Nach diesem Er¬ kennungswort wird beim Reset gesucht, wenn die Resident-Strukturen gesucht werden. Das Wort muß den Wert $4AFC haben, damit die Struktur gefunden wird. rt_MatchTag Zeiger auf die Struktur selbst und wird ebenfalls zur Erkennung der Struktur im Speicher verwandt. Nachdem das MatchWord gefunden wurde, wird geprüft, ob das darauffolgende Langwort ein Zeiger auf die eigene Struktur ist. Ist das der Fall, wird eine Resident-Struktur erkannt. rt_ßndSkip Zeiger auf das Ende der Struktur. Mit diesem Zeiger ist es möglich, die Struktur beliebig lang zu machen und wichtige Daten in ihr zu vermerken. rt_Flags Gibt je nach Setzen der Bits an, ob die Resident-Struktur überhaubt bearbeitet werden soll, und ob, wenn sie bearbeitet wird, nur der an¬ gegebene Befehl oder der Einsprung in das angegebene Programm er¬ folgt. Ist das oberste Bit (Bit 7) gelöscht, so bedeutet dies, daß in das in der Struktur vermerkte Programm eingesprungen wird. Ist es ge¬ setzt, so zeigt rt_Init auf eine Tabelle für die Register, die für das Ausführen der Funktion MakeLib() nötig sind. rt_yersion Gibt an, um welche Version der Struktur es sich handelt. rt_Type Gibt an, welcher Befehl ausgeführt werden soll. 490 Amiga intern rt_Name Zeiger auf den Namen der Struktur. rt_idString Zeiger auf den String, der nähere Erläuterungen zu der Struktur gibt. rt_!nit Zeiger auf das auszuführende Programm oder ein Zeiger auf die Ta¬ belle für die zu ladenden Registerinhalte beim Aufruf der MakeLibO- Funktion. Soll die MakeLib()-Funktion aufgerufen werden, so müssen in der Tabelle folgende Register in angegebener Reihenfolge eingetra¬ gen sein: 00 = OataSize 01 = CodeSize AO = Funclnit AI = Structlnit A2 s Liblnit Wie eine Tabelle der Zeiger auf Resident-Strukturen aussieht, auf die ResModuls (in der ExecBase-Struktur) zeigt, läßt sich mit der folgen¬ den Tabelle verdeutlichen. Die Endmarkierung der Tabelle ist das letzte Langwort, das den Wert Null hat. Die Tabelle wird normalerweise von der Reset-Routine erstellt. Sollte das oberste Bit (Bit 31) eines Langworts in der Tabelle gesetzt sein, so bedeutet das, daß das Langwort ohne Berücksichtigung des obersten Bits ein Zeiger auf die Fortsetzung der Tabelle ist. OOfc 00b6 OOfc Aafc OOfe A880 OOfe 4fe4 OOfc A50c OOfc A79A OOfe 4774 OOfe 49cc OOfc S378 OOfe 502e OOfe 507a OOfe 90ec OOfc 34cc OOfe 50c6 OOfe 0d90 OOfe 510e OOfe 98e4 OOfd 3f5c OOfc 323a OOfe 424c OOfe b400 OOff 425a OOfe 8884 0000 0000 Sehen wir uns von der Tabelle ausgehend einmal die zweite Resident- Struktur an. Sie sieht wie folgt aus: fc4afc 4afc OOfc 4afc OOfc 516c 8121 096e OOfc fc4Mc 4b48 ÖÖfc 4b16 OOfc 4b38 6578 7061 6e73 .expans fc4b1c 696f 6e20 3333 2e31 3231 2028 3420 4d61 ion 33.121 (4 Ha fc4b2c 7920 3139 3836 290d OaOO 0000 0000 01c8 Exec 491 y 1986). fc4b3c OOfc 4b86 OOfc 4b5a OOfc 4bee 6578 7061 .expa fc4b4c 6e73 696f 6e2e 6c69 6272 6172 7900 eOOO nsion.library... Um die Initialisierung der Struktur besser nachvollziehen zu können, sind die entsprechenden Werte in der folgenden Struktur noch einmal aufgeführt. struct Resident C 0 UUORD rt HatchUord; $4AFC 2 struct Resident *rt MatchTag; $FC4AFC 6 APTR rt EndSkip; $FC516C 10 UBYTE rt_Flags; XI0000001 11 UBYTE rt Version; 33 12 UBYTE rt“Type; Library 13 BYTE rt_Pri; 110 14 eher *rt_Naiiie; expansion.li br a ry 18 char *rt_IdString $FC4B16 22 APTR rt_Tnit; $FC4B38 >: Das rt_Flag-Byte ist auf % 10000001 gesetzt. Das oberste Bit ist also gesetzt, weshalb das rt_Init auf Daten für die Register zeigt, die für den Aufruf der Funktion AddLibraryO gebraucht werden. Hier wird die AddLibraryO-Funktion aufgerufen, da der Typ der Resident- Struktur "Library" (NT_LIBRARY = 09) ist. Es stehen folgende Möglichkeiten zur Verfügung: Typ 03 = Device 08 = Resource 09 = Library Funktionsaufruf AddDevice() AddResourceO AddLibraryO Zum Untersuchen der Resident-Struktur ist die Funktion InitCode() zuständig. Beim Aufruf werden die zwei Parameter "StartClass" und "Version" übergeben. "Version" legt fest, daß nur Resident-Strukturen ausgeführt werden, deren Version gleich oder über der angegebenen ist. Der Wert, der in "StartClass" übergeben wird, wird mit rt_Flags UND-verknüpft. Sollte das Ergebnis ungleich null sein, wird die Struktur ausgeführt und somit die initResident-Funktion aufgerufen. Mit StartClass und rt_Flags wird also festgelegt, welche Resident- Strukturen beim Aufruf der InitCode-Funktion ausgeführt werden. 492 Amiga intern Bei einem Reset wird die InitClass-Funktion mit den Parametern StartClass = 01 und Version = 00 ausgeführt. Es werden somit nur die Resident-Strukturen ausgeführt, deren Bit 0 in rt_Flags gesetzt ist. Damit Sie sich noch ansehen können, wie die eben beschriebenen Routinen genau ablaufen, folgen hier die Assembler-Listings der Funktionen. iiiftCode Funktion: InitCodefStartClass, Version) 00 01 fcOafO movem.l A2/03-02,-(A7) Register retten fcOafA move.l 300(Aö),A2 Zeiger auf ResHoduls fcOafS move.b 00,02 StartClass nach 02 fcOafa move.b 01,03 versin nach 03 fcOafc move.l ;Port auf Ausgang ;LED ausschalten ;DMA-Zugriffe sperren lea.l $400,a6 suba.u «$fd8a,a6 ;Exec8ase-8asis ermitteln 499 Exec lea.l $cOOOOO,aO lea.l SdcOOOO.al fflove.l #$00,a4 nove.l a4,d0 move.l «$40000,a7 nove.l #$ffffffff,d6 jap $fc0208 ;Kein Fast-RAM vorhanden ;Hilfs-Stack-Pointer nach A7 ;Uert für keinen Alert gefunden ;E{nsprung in Reset 2.15 Booten ohne Disk (Die ROM-Boot-Library) Das folgende Kapitel bezieht sich nur auf die Kickstart-Version 1.3 und wahrscheinlich auch deren Nachfolger, Ist jedoch auch für Besit¬ zer der Kick 1.2 interresant. Durch die ROM-Boot-Library (nur in Kick 1.3) mit Hilfe der Expan¬ sion-Library und über die Strap-Routine (Boot-Routine) der Kick 1.3 ist es möglich, von einem anderem Device als DFO: zu booten. Ein solches Device ist beispielweise eine Festplatte. Auf der Workbench 1.3 ist ein neues Device enthalten, das sich ram- drive.device nennt. Bei diesem Device handelt es sich um eine reset¬ feste RAM-Disk, die bei Benutzung der Kick 1.3 auch gebootet wer¬ den kann. Das Booten aus dieser RAM-Disk funktioniert genauso wie das Booten von der Festplatte oder einem anderem Device. Da jeder, der über die Kick und Workbench 1.3 verfügt, aus seiner RAM-Disk booten kann, soll dies hier ausführlich besprochen werden. Bevor aus der RAM-Disk gebootet werden kann, muß sie resetfest sein, das heißt, es muß eine eigene Routine während eines Resets an¬ gesprungen werden, die dafür sorgt, daß die RAM-Disk nicht gelöscht wird. Um eine eigene Routine einzubinden, werden die Zeiger Kick- MemPtr, KickTagPtr und KickCheckSum innerhalb der ExecBase- Struktur verwendet (siehe Kapitel über resetfeste Programme). Über die soeben genannten Zeiger wird in die Routine verzweigt, die die RAM-Disk vor einem Reset schützt und die Möglichkeit gibt, aus ihr zu booten. Die Routine initialisiert eine DeviceNode- sowie eine MountNode- Struktur. Die MountNode-Struktur wird in die Mount-Liste der Ex¬ pansion-Library eingefügt. Sollte bei einem Reset keine Diskette in Laufwerk Null vorliegen, wird mit Hilfe der ROM-Boot-Library von einem anderem Device gebootet. 500 Amiga intern Um die Einzelheiten diese Routine zu verstehen, ist es nötig, die Ex- pansion-Library-Struktur und deren Unterstrukturen sowie einige ih¬ rer Funktionen zu kennen. Aus diesem Grund folgt erst eine Bespre¬ chung der für das Booten von der RAM-Disk wichtigen Teile. Die Teile der Expansion-Library, die sich auf die Hardware beziehen, werden an entsprechender Stelle separat behandelt. 2.15.1 Die Strukturen struct ExpansionBase { struct Library LibNode; UBYTE Flags; UBYTE pad; APTR ExecBase; APTR SegList; struct CurrentBinding CurrentBinding; struct List BoardList; struct List MountList; /* Offset 74 $4A */ UBYTE AllocTabletTOTALSLOTS]; struct SignalSemaphore BindSemaphore; struct Interrupt Int2List; struct Interrupt IntbList; struct Interrupt Int7List; >; #define TOTALSLOTS 256 #define EXPANSIONNAME "expansion.library" #define ADNB STARTPROC 0 fWefine ADNfIstartproC <1«0) Für uns ist lediglich der Eintrag "MountList" interessant. Alles andere ist nur für die Hardware wichtig. MountList List-Struktur, in der alle bootbaren Devices mit ihren wichtigsten In¬ formationen eingetragen sind, um gebootet zu werden. In dieser Liste sind Strukturen verkettet, die wir MountNode-Struk- turen nennen. Sie haben folgendes Aussehen: struct HountNode { /* Offsets */ struct HinNode iino_Node; UBYTE i«no_Type; UBYTE mno_Pri; struct ConfigOev ♦nnoJIountDev; UUORD lino_DoNotKnow; /* 00 $00 */ /* 08 $08 */ /* 09 $09 */ /* 10 $0A */ /* 14 $0E */ Exec 501 struct DeviceNode •iino_DevNode /• 16 $10 •/ >; mno_Node Gewöhnliche MinNode-Struktur, um die Verkettung innerhalb einer Liste zu ermöglichen. mno_Type Typ des Devices an, der durch diese Node-Struktur verwaltet wird. Der Typ DAC_CONFIGTIME (=16) gibt an, daß das Device gebootet werden kann. Die Typen entsprechen der der DiagArea-Struktur. mno_Pri Priorität des Devices beim Booten. Sollten mehrere Devices bootfähig sein, so bestimmt die Priorität, welches gebootet wird. Ein Device mit hoher Priorität wird vor dem mit niedrigerer gebootet. mno_MountDev Zeiger auf zugehörige ConfigDev-Struktur (Besprechung folgt). mno_DoNotKnow Dieser Eintrag ist nicht bekannt, jedoch nicht für das Booten eines Devices wichtig. mno_DevNode Zeiger auf zugehörige DeviceNode-Struktur (Besprechung folgt), die von der MakeDosNode-Funktion der Expansion-Library erstellt wird (Dokumentation folgt). In der MountNode-Struktur befindet sich ein Zeiger auf eine Config¬ Dev-Struktur. Die Struktur wird normalerweise beim Erstellen der Expansion-Library erstellt und enthält alle wichtigen Informationen über das Aussehen und die Lage einer in den Erweiterungs-Slot ein¬ gesteckten Karte (z.B. Festplatte). Da wir in diesem Kapitel jedoch nur die resetfeste/bootbare RAM-Disk besprechen, sind von dieser Struktur nur einige wenige Einträge für uns interessant, und somit werden auch nur diese erwähnt. Die vollständige Besprechung erfolgt 502 Amiga intern bei der Erläuterung der Expansion-Architektur. Die Struktur hat fol¬ gendes Aussehen. struct ConfigDev { /* Offsets •/ struct Node cd_Node; /* 00 $00 */ UBYTE cd_Flags; /* 14 $0E */ UBYTE cd_Pad; /* 15 $0F */ struct ExpansionRom cd_Rom; /* 16 $10 */ APTR cd_BoardAddr; /• 32 $20 */ APTR cd_BoardSize; /* 36 $24 */ UUORD cd_SlotAddr; /* 40 $28 •/ UUORD cd_SlotSize; /* 42 $2A •/ APTR cd_Driver; /* 44 $2C */ struct ConfigOev •cd_tlextCD; /* 48 $30 V ULONG cd_Unused[4] ; /* 52 $34 */ >; «define COB SHUTUP OL #defjne CDB~CONFIGHE 1L #define CDF~SHUTUP OxOIL «define CDF~CONFIGHE 0x02L cd_Node Normale Node-Struktur, deren ln_Type als NT_DEVICE (= 3) gesetzt ist. ln_Name ist ein Zeiger auf den Namen des Devices, beispielsweise "ramdrive.device". cd_Rom Struktur, deren Einträge normalerweise aus der Erweiterungskarte aus¬ gelesen werden. Sie enthalten Einträge, die die Art der Karte näher beschreiben. Für die RAM-Disk sind nur fünf Einträge wichtig. Die ExpansionRom-Struktur hat folgendes Aussehen, struct ExpansionRom { /* Offsets •/ UBYTE er_Type; /• 00 $00 */ UBYTE er_Product; /• 01 $01 */ UBYTE er~Flags; /* 02 $02 */ UBYTE er_Reserved03; /• 03 $03 */ UUORD er_Manufacturer; /* 04 $04 */ ULONG er_Ser i a l Nuniser; /* 06 $06 */ UUORD er_lnitDiagVec; /* 10 $0A */ UBYTE er_Reserved0c; /* 12 $0C */ UBYTE er_Reserved0d; /* 13 $0D */ UBYTE er_Reserved0e; /* 14 $0E */ Exec 503 UBYTE er_ReservedOf; /* 15 $0F */ #define ERTF_DIAGVALIO (1L«4) er_Type Hier wird der Typ des Boards (der Karte) angegeben. In unserem Fall - es handelt sich um kein Board, sondern um eine RAM-Disk - muß durch den Typ angegeben werden, daß ein Programm ausgeführt wer¬ den soll (das Programm zum Starten des Boot-Vorgangs aus der RAM- Disk). Es ist der gleiche Typ, der auch schon in unserer MountNode- Struktur angegeben werden mußte. Der Typ ist ERTF DIAGVALID oder DAC_CONFIGTIME (= 16). ^’'_ReservedOc/er_ReservedOd/er_ReservedOe/er_ReservedO f In diesen vier Byte-Einträgen ist der Zeiger auf den RAM-Bereich, in dem das auszuführende Programm steht. Es ist jedoch kein Zeiger, der direkt auf das Programm zeigt, sondern zeigt wiederum auf eine Struktur. Über diese Struktur kann jedoch endlich auf das gewünschte Programm zugegriffen werden. Die Struktur heißt DiagArea und wird jetzt besprochen. Die DiagArea-Struktur steht - wenn ein Board (eine Karte) verwaltet wird - am Anfang des Speicherbereichs, in den der ROM-Bereich der Karte kopiert wurde (sofern ein ROM-Bereich existiert). Die Struktur enthält einige Informationen über den ROM-Bereich (RAM-Kopie), die nur bei Karten interessant sind. Für die RAM-Disk werden nur zwei Einträge genutzt. struct DiagArea { /• Offsets •/ UBYTE da Config; UBYTE da~Flags; UUORD da_Size; UUORD da_DiagPoint; UUORD da BootPoint; UUORD da'Name; UUORD da ReservedOl; UUORD da~Reserved02; /• 00 $00 •/ /• 01 $01 •/ /• 02 $02 */ /* 04 $04 •/ /• 06 $06 */ /• 08 $08 */ /• 10 $0A */ /• 12 $0C */ #define DAC CONFIGTIHE OxIOL 504 Amiga intern da_Config Der Eintrag gibt an, welcher Art dieser RAM-Bereich ist. Für die RAM-Disk muß hier angegeben werden, daß der RAM-Bereich ein Programm enthält (das Programm zum Booten der RAM-Disk). da_ßootPoint Hier steht der Offset, der auf das auszuführende Programm zeigt. Der Offset wird zu der Basisadresse der DiagArea-Struktur addiert. Nachdem die ConfigDev-Struktur mit ihren Unterstrukturen beschrie¬ ben wurde, folgt nun die in der MountNode-Striiktur vermerkte De- viceNode-Struktur. Sie wird von DOS und vom FileSystem gebraucht, um das Device entsprechend anzusprechen. struct DeviceNode < /* Offsets */ BPTR dn_Next; /* 00 »00 */ ULONG dniType; /* 04 »04 */ struct MsgPort ‘dn Task; /* 08 »08 */ BPTR dn_Lock; /* 12 »OC */ BSTR dn_Haiidler; /* 16 »10 */ ULONG dn^StackSize; /* 20 »14 */ LONG dn_Priority; /* 24 »18 */ BPTR dn_Startup; /* 28 »IC */ BPTR dn_SegLlst; /• 32 »20 */ BPTR dn_GlobalVec; /* 36 »24 */ BSTR dn_Naine; /* 40 »28 */ >; dn_Next BPTR-Zeiger auf die nächste DeviceNode-Struktur. dn_Type Immer Null, da es sich um ein DOS-Device handelt (DLT_DEVICE). dn_Task Zeiger auf den (wie deim DOS üblich) zum entsprechenden Task ge¬ hörigen Message-Port. Wenn kein Zeiger eingetragen ist, heißt dies, daß ein Task beim Start erstellt wird. 505 Exec dn_Lock Für ein Device wird der Eintrag nicht verwendet und bekommt somit den Wert Null. dn_Handler Hier befindet sich der Zeiger auf den Namen des Händlers, der gela¬ den wird, sofern keine Segmentliste eingetragen ist. dn_StackSize Hier wird die Größe des Stacks eingetragen, der auf gebaut wird, wenn ein Task über die dn_SegList erstellt wird. dn_Priority Priorität des zu erstellenden Tasks. dn_Startup BPTR-Zeiger auf die erstellte FileSysStartupMsg-Struktur, die zum Starten des Devices benötigt wird. Sie wird von der MakeDosNode- Funktion erstellt (Besprechung folgt). dn_SegList BPTR-Zeiger auf die Segmentliste für das Task-Programm, welches abgesehen vom Device zusätzlich aufgebaut wird. Ist der Zeiger nicht gesetzt (Null), wird ein Händler aus dem L-Ordner geladen (siehe dn_Handler). Ist dieser Zeiger ebenfalls nicht gesetzt, wird kein wei¬ terer Task gestartet. Der dn_SegList-Zeiger wird beispielsweise ge¬ setzt, wenn ein FastFiling-Resource existiert, um den entsprechenden Task aufzubauen. Dieses Resource ist in der jetzigen Kickstart-Ver- sion nicht integriert. dn_GlobalVec Hier wird festgelegt, ob der erstellte Task ein oder kein BCPL-Pro- gramm ist. Sollte es ein BCPL-Programm sein (dn_GlobalVec = 0), wird es auch als solches beim Einbinden in das System behandelt. Ist es kein solches Programm (dn_GlobalVec = -1), wird dies vom DOS berücksichtigt. 506 Amiga intern dn_Name BSTR-Zeiger auf den Namen der DeviceNode-Struktur (z.B. CARD oder DFO).. Zum Erstellen der Struktur dient die Expansion-Funktion MakeDos- Node. In AO wird ein Zeiger auf eine Tabelle übergeben, mit deren Hilfe die DeviceNode-Struktur sowie eine weitere Struktur erstellt wird. Die Struktur heißt FileSysStartupMsg. Es existiert ein Zeiger auf die Struktur innerhalb der DeviceNode-Struktur. Diese neue Struktur wird gebraucht, wenn das Device geöffnet wird. Sie sieht wie folgt aus: Kt FileSysStartupMsg { /* Offsets */ ULONG fssin_Unit; /* 00 SOO */ BSTR fssra_Device; /* 04 S04 */ BPTR f ssin_Envi ron; /* 08 SOS */ ULONG fssiii_Flags; /* 12 SOC */ >; fssm_Unit Unit-Nummer, die bei der OpenDevice-Funktion der Exec-Library verwendet wird (z.B. Null für Öffnung von Laufwerk Null). fssm_Device BSTR-Zeiger auf den Namen des Devices. fssm_Environ BPTR-Zeiger auf eine Tabelle, deren Einträge das Aussehen des De¬ vices bestimmen (Environment-Table). Ein Beispiel hierfür ist die Anzahl der Tracks und Sektoren. fssm_Flags Flags, die in der OpenDevice-Funktion angegeben werden müssen. Wie soeben gesagt, ist in fssm_Environ der Zeiger auf eine Lang¬ worttabelle eingetragen. Diese Tabelle hat ein festgelegtes Aussehen. Die Position der entsprechenden Werte der Tabelle ist mit Hilfe von "defines" festgelegt. Sie stellen die Offsets innerhalb der Tabelle dar Exec 507 und beginnen folglich mit dem Wert Null. Die Tabelle hat den Namen Environment-Table. Der erste Eintrag der Tabelle gibt ihre Größe an, damit entsprechend viel Speicherplatz reserviert werden kann. #define DE TABLESIZE 0 Als nächstes ist die Größe eines vom Device verarbeiteten Blocks ver¬ merkt. Der Normwert ist 128 Langworte, was 512 Bytes entspricht. #define DEJSIZEBLOCK i Der dritte Eintrag wird nicht benutzt, muß den Wert Null haben. *define DE_SECORG 2 Der folgende Eintrag gibt die Anzahl der dem Device zur Verfügung stehenden Lese-/Schreibköpfe, das heißt die Anzahl der Oberflächen an. Dieser Wert ist bei einer 20-MB-Festplatte normalerweis 4 und bei Diskette 2. *defme DE NUMHEADS 3 Langwort Nummer 4 (es wird von Null an gezählt) bestimmt die An¬ zahl der Sektoren innerhalb eines Blocks. Beim Amiga enthält jeder Block nur einen Sektor, weshalb die Begriffe Block und Sektor das gleiche verkörpern. #define DE SECSPERBLK 4 Der sechste Eintrag (Langworts Nummer 5) gibt die Anzahl der Sek¬ toren (Blöcke) jedes Tracks an. Bei Disk ist diese 11. *defme DE BLKSPERTRACK J Der nächste Eintrag bestimmt die Anzahl der Blöcke, die für spezielle Zwecke reserviert sind. Im Normalfall sind es zwei (z.B. der Boot- Block). 508 Amiga intern #defme DE RESERVEDBLKS ö Der achte Eintrag (Langwort Nummer 7) wird nicht benutzt, sein Wert muß Null sein. #de/ine DE_PREFAC ' Der nächste Wert gibt an, wie viele physikalische Blöcke zwischen zwei logischen Blöcken stehen. Der Wert wird gebraucht, wennn die Datenübertragunug von dem Laufwerk zum Rechner langsamer als das Lesen der Daten von Disk geschieht. In einem solchen Fall wird ein Sektor gelesen und im nachhinein zum Rechner geschoben. In dieser Zeit dreht sich die Diskette jedoch weiter, so daß einige Sektoren überlesen werden. Wenn alle Sektoren hintereinander stehen, müßte längere Zeit gewartet werden, bis der nächste gewünschte Sektor er¬ reicht wird. Um dies zu verhindern, werden die logischen Sektoren nicht direkt hintereinander, sondern im Abstand einiger physikalischer Sektoren angelegt. Dieser Abstand wird "interleave" genannt. Beim Amiga ist er Null (die logische ist gleich der physikalischen Reihen¬ folge der Sektoren). *define DEJNTERLEAVE S Der nächste Wert gibt den Start-Zylinder an. Normalerweise ist dies Zylinder Null. i^de/ine DE LOWCYL ^ Der 11. Wert bestimmt die Nummer des letzten Zylinders. Der Wert ist Device-abhängig. Bei Disk ist dieser Wert 79. it^de/ine DEJJPPERCYL JO Der folgende Eintrag gibt die Anzahl der vom File-System verwende¬ ten Puffer an, um Daten im RAM zwischenzuspeichen. #define DE_NUMBUFFERS JJ Der letzte Eintrag bestimmt die Art des für die Puffer verwendeten Speichers. Exec 509 *define DE MEMBUFTYPE 12 Sie haben wahrscheinlich festgestellt, daß es sich bei diesen Eintra¬ gungen um die gleichen handelt, die in der MountList gemacht wer¬ den, um ein Device festzulegen. Wie schon erwähnt wird das Erstellen der DeciveNode-Struktur von einer Funktion der Expansion-Library erledigt. MakdOosNodd Funktion: DosNode - MakeOosNode (StartupTab) 00 AO Offset: -144, -$90. $FF70 Beschreibung Die Funktion erstellt eine DeviceNode-Struktur, indem sie sowohl Speicher für die Struktur selbst als auch für den Namen des Devices, die FileSysStartupMsg-Struktur und die Device-Aufbau-Tabelle (En- vironment-Table) reserviert und die entsprechenden Eintragungen vornimmt. Parameter DosNode Zeiger auf die entstandene DeviceNode-Struktur. StartupTab Zeiger auf die zuvor initialisierte Langworttabelle, die zur Erstellung der FileSysStartupMsg-Struktur dient. Der Aufbau der Tabelle ist wie folgt; 0. Zeiger auf DeviceNode-Namen (s.B. CARD oder DFO) 1. Zeiger auf den Device-Namen (s.B. ramdrive.device) 2. Unit-Nummer (siehe FileSysStartupMsg => fB8m_Unit) 3. Flags (siehe FileSysStartuoMsg => fssm_FIag8 4. -16. Environment-Table 510 Amiga intern Dokumentation Die hier abgedruckte Routine ist aus der Kickstart-Version 1.2, hat sich jedoch zu der uns vorliegenden Version 1.3 abgesehen von der Startadresse nicht geändert. AO = Zeiger auf die initialisierte FileSysStartupMsg-Struktur A6 = Zeiger auf Expansion-Base fc5170 movem.l A6/A4-A2,-(A7) fc5174 move.l 36(A6),A6 fc5178 move.l A0,A2 fc517a lea -56(A7).A7 fcS17e move.w «$0005, U(A7) fcS184 lea 16(A7),A1 fc5188 move.l «$00010001,01 fcS18e move.l D1,(A1)+ fcS190 move.l «S0000002c,(A1)+ fc5196 move.l 01, (AD* fc5198 move.l «S00000010, BPTR fcS20c move.l D0,8(A0) Zeiger auf Table eintragen fc5210 move.l AO.DO Zeiger auf FileSysStartup. fc5212 Isr.l «2,DO APTR => BPTR fc52H move.l D0,28(A3) Zeiger auf FileSysStartup. eintragen fc5218 lea 16(A2),A0 Zeiger auf Environment- Table fc521c fflove.l (A0),D0 Anzahl der Langworte fc521e move.l {A0)+, -1 fc5224 move.l «$00000258,20(A3) Stack-Size für Task eintragen fc522c move.l «»0000000a,2A(A3) Priorität für Task eintragen fc5234 moveq «$38,D0 56 Bytes für MemList fc5236 move.l A4,A1 Zeiger auf MemList [c5238 jsr -210(A6) FreeMemO (MemList-Strukt. aus Speicher entfernen fcS23c move.l A3,DO Zeiger auf DosNode-Strukt. fcS23e movem.l (A7)+,A6/A4-A2 Register zurückholen fe5242 rts Rücksprung Einsprung, wenn Fehler beim AllocMem. fc52AA moveq «$00,D0 Fehlermeldung nach DO fc52A6 bra.s tfc523e Rücksprung Länge eines mit Null abgeschlossenen Strings holen. Zu der Länge wird 1 addiert, da vor dem String auch die Länge abgelegt werden soll. >= AO = Zeiger auf String -> DO = Anzahl der Buchstaben fc52A8 move.l A0,D0 Zeiger auf String fc52Aa beq.s »fc5258 Ende, wenn kein String fc52Ac moveq «$ff,D0 Max. Länge nach DO fc52Ae tst.b (A0)+ Buchstabe = Null fc5250 dbeq D0,»fc52Ae Weiter, wenn nicht Null fc5254 not.l DO Anzahl der Buchstaben fc5256 addq.l «2, DO Anzahl der Zeichen mit Null und Byte für Länge fc5258 rts Rücksprung String kopieren und Länge vor dem String eintragen. 512 Amiga intern >= AO = Zeiger auf String >= AI = Zeiger auf Speicher für String fc525a move.l A0,D0 fc525c beq.s Sfc5276 fc525e addq.l #1.A1 fc5260 moveq mSff.DO fc5262 move.b (A0)+,(A1)+ fc5264 dbeq D0,Sfc5262 fc5268 move.l D0,D1 fc526a not. l DO fc526c lea -1(A1,D1.L),A0 fc5270 move.b D0,(A0) fc5272 move. l A0,D0 fc5274 fc5276 Isr. l rts «2, DO Zeiger auf String Ende, wenn kein String Zeiger auf Anfang der Buchstaben für Ziel Max. Anzahl der Buchstaben String kopieren Weiter, wenn nicht Ende Buchstaben Anz. negiert Buchstaben Anzahl Zeiger auf Byte vor String Anzahl eintragen Zeiger auf String mit Anz. APTR => BSTR Rücksprung Nachdem alle für die Erstellung der bootbaren RAM-Disk wichtigen Struktur und deren Initialisierung besprochen wurde, kann nun die ei¬ gentlichte Routine erleutert werden, die bootbare RAM-Disk zu einer solchen macht. 2.15.2 Die bootbare RAM-Disk Die Routine wird beim Reset über eine Resident-Struktur aufgerufen, deren Priorität 20 beträgt. Um so wenig störend wie möglich zu wir¬ ken, wird diese Routine in den obersten RAM-Bereich des Chip-Me- mories gelegt. Wenn Sie Ihren Amiga nicht mit einer RAM-Erweite- rung ausgestattet haben, verschiebt sich der Anfang der Routine auf $7E750, da ab $7E800 der System-Stack beginnt. Die Routine geht davon aus, daß eine initialisierte Tabelle zum Er¬ stellen der DeviceNode-Struktur existiert. Weiterhin muß eine Con- figDev-Struktur existieren, deren Node-Typ (ln_Type) ein Device an¬ gibt (NT_DEVICE). Der Typ der ExpansionRom-Struktur (er_Type) muß den Wert 16 (DAC_CONFIGTIME) besitzen. Die vier reservier¬ ten Bytes innerhalb der ExpansionRom-Struktur (er_ReservedOc bis er_ReserverOf) sind ein Zeiger auf initialisierte DiagArea-Struktur. struct DiagArea { UBYTE da Config; /* = 16 */ UBYTE da Flags; /* = 00 */ UUORD da~Size; /* = 00 */ UUORD da DiagPoint; /* = 00 */ UUORD da~BootPoint; /* = Offset für BootProg */ /* siehe dokumentation S7FFEA */ UUORD da_Name; /* = Offset für String */ Exec 513 UUORD da_Reserved01; /* = 00 */ UWORD da Reserved02; /* = 00 */ >; Um die Struktur erstellen zu können, ist eine Tabelle nötig. ErstellTab: (Tabelle für MakeDosNode) Jeder Eintrag hat Langwortgröße. 0. Zeiger auf DeviceNode Name 1. Zeiger auf den Device-Namen 2. Unit-Nimner 3. Flags Ab hier beginnt der Environment-Table. A. TabellengröBe = 12 5. BlockgröBe = 128 6. Sektororg. = 0 7. Anzahl der Köpfe = 2 8. Blöcke pro Sektor = 1 9. Blöcke je Track = 11 10. Reservierte Blöcke = 2 11. Vorfaktor » 0 12. Interleave-Faktor = 0 13. Erster Zylinder = 0 H. Letzter Zylinder = 7? (z.B. 10) IS. Anzahl der RAM-Puffer = 5 16. Speichertyp der Puffer = 0 beginnt die Routine. 07ff50 movem.l A6/A2,-(A7) Register retten 07ffS4 move.l $000004,A6 ExecBase holen 07ff5a move.l Adresse(pc),A1 Adresse der RAM-Disk 07ff5e move.l Size(pc),00 Größe der RAM-Disk 07ff62 jsr -204(A6) AllocAbsO 07ff66 tst.l DO Speicher belegt 07ff68 beq.s $07ffe4 Verzweige, wenn Fehler 07ff6a lea Device(pc),A1 Zeiger auf RAM-Device 07ff6e jsr -432(A6) AddDeviceO 07ff72 lea LibName(pc),A1 Zeiger auf ExpansionLib- Name 07ff76 moveq lllS22,D0 Version (34 = Kick 1.3) 07ff78 jsr -SS2(A6) OpenLibraryC) 07ff7c tst.l DO Zeiger auf Library 07ff7e beq.s $07ffe4 Verzweige, wenn Fehler 07ff80 move.l D0,A6 Zeiger auf Lib nach A6 07ff82 lea ErsteilTab(pc),A0 Zeiger auf Ersteil- Tabelle für DeviceNode 07ff86 jsr -144(A6) MakeDosNode (Expansion) 07ff8a lea 74(A6),A0 Zeiger auf MountList 07ff8e movem.l A6/A0,-(A7) Register retten = “CARD" = "ramdrive.device" = 2 = 0 514 Amiga intern 07ff92 move.l S000004,A6 Zeiger auf ExecBase 07ff98 lea nno_DevNode(pc),AI Zeiger auf nno_DevNode (Eintrag in HoüntNode- Struktur) 07ff9c move.l DO,(AI) Zeiger auf DeviceNode eintragen 07ff9e beq.s *07ffdc Ende, keine DeviceNode 07ffa0 move.l D0,A2 DeviceNode nach A2 07ffa2 lea FFSRes(pc),A1 Zeiger auf String “ffs.resource" 07ffs6 Jsr •498(AÖ) OpenResource(} 07ffaa tst.l DO Resource gefunden? 07ffac beq.s $07ffd2 Weiter, wenn nicht gefunden Der folgende Teil wird nur durchlaufen, wenn eine Fast-Filing-Sy- stem-Resource-Struktur existiert. Falls eine existiert, werden alle Zei¬ ger in der DeviceNode-Struktur gesetzt, um einen entsprechenden Task zu initialisieren. 07ffae move.l D0,A1 Zeiger auf Resource => AI 07ffb0 move.l 34(A1),D0 Zeiger auf Segment liste 07ffb4 tst.l DO Segmentliste vorhanden? 07ffb6 beq.s $07ffd2 Ende, wenn keine SegList 07ffb8 move.l D0,32(A2) SegListe in DeviceNode eintragen 07ffbc move.l GlobVec(pc),36(A2) GlobalVector eintragen (= Null) 07ffc2 move.l «$000000d2,20(A2) StackSize eintagen 07ffca move.l «$0000000f,24(A2) TaskPri. eintagen 07ffd2 move.l (A7),A0 Zeiger auf HountList 07ffd4 lea -372(PC)(=$7fe62),A1 Zeiger auf MountNode 07ffd8 jsr -270(A6) Enqueue() 07ffdc movem.l (A7)+,A1-A0 Register zurückholen 07ffe0 jsr -414(A6) CloseLibraryO 07ffe4 movem. l (A7)+,A6/A2 Register zurückholen 07ffe8 rts Rücksprung Ab hier beginnt das beim Booten auszuführende Programm, das auch im Boot-Block der Diskette zu finden ist. Es wird von der ROM- Boot-Library aufgerufen. BootProg: 07ffea lea 07ffee jsr 07fff2 tst.l 07fff4 beq.s 07fff6 move.l 07fff8 move.l 07fffc jsr LibName(pc),A1 -96(A6} DO $07fffe D0,A0 22(A0),A0 (A0> String “dos.library“ FindResidentO Gefunden? Nein, Ende Zeiger auf Resource Zeiger auf Einsprung DOS-Lib erstellen Falls kein Fehler auftritt, wird der rts-Befehl nicht mehr durchlaufen. 515 Exec 07fffe rts Nachdem die MountNode-Struktur initialisiert und in die Mount-Liste der Expansion-Library eingefügt wurde, wird die Reset-Routine wei¬ ter durchlaufen. Die Strap-Routine (die Routine, die von Laufwerk Null bootet), ist bei der Kickstart-Version 1.3 soweit geändert, daß nach dem 3. ungültigen Boot-Versuch von Disk die ROM-Boot-Li- brary geöffnet und mit Offset -30 eingesprungen wird. Die Routine, die dort ausgeführt wird, ermöglicht das Booten von ei¬ nem externen Device oder der bootbaren RAM-Disk. Die Funktion ist in C geschrieben. ROH-Boot-Library Offset -30: >= AO = Zeiger auf RonfiootBase fehl 78 movem.l A6,-(A7) fehl 7c move.l 3A(A0),A6 feblSO jsr $feb18c fehl86 movem. l (A7)+,A6 feb18a rts feb18c movem.l A3-A2/D2,-(A7) fehl90 pea $0021 feb194 pea $feb1e8 feb19a jsr $febAA8 feblaO move. l DO, A3 feblaZ move.l A3,D2 feblaA addq.l #8,A7 feb1a6 beq.s $feb1de feb1a8 lea 7A(A3),A0 feblac move. l (A0),A2 feblae bra.s SfebldO feblbO move.l 16(A2),D0 feblbA beq.s Sfebice feb1b6 cmpi.b #$10,8(A2) feblbc bne.s Sfebice feblbe move.l 10(A2),D0 feb1c2 beq.s Sfebice febIcA move.l D0,-(A7) feb1c6 jsr Sfebifc febicc addq.l #A,A7 febice move.l (A2),A2 febldO tst.l (A2) feb1d2 bne.s SfeblbO febldA move.l A3,-(A7) feb1d6 jsr SfebA3A febldc addq.l #A,A7 feblde moveq #S00,D0 febleO movem.l (A7)+,A3-A2/02 A6 retten Zeiger auf RomBootBase A6 zurückholen Rücksprung Register retten Versionsnunmer = 33 Zeiger auf String "expansion.library" OpenLibraryO Zeiger auf Expansion-Lib Zeiger nach D2 Stapel auf alten Wert Expansion-Lib nicht gefunden Zeiger auf MountList Zeiger auf ersten Eintrag (MountNode) Unbedingter Sprung Zeiger auf DeviceNode Fehler, wenn keine DeviceNode nino_Type = DAC_CONFIGTIHE ? Nein, Fehler Zeiger auf ConfigDev Fehler, wenn nicht vorhanden Zeiger auf ConfigDev in Stapel ConfigDev auswerten und gegebenenfalls booten Stapel auf alten Wert Neues Element aus Liste (MountNode) Element in Liste ? Ja, auswerten Zeiger auf Expansion-Lib CloseLibraryO Stapel auf alten Wert Rückmeldung (kein bootbares Device erreichbar) Register zurückholen 516 Amiga intern feb1e4 rts Rücksprung febleS dc.l "expansion.library“,0 febifc movem.l 0Z-D4,-(A7) Register retten feb200 move.l 16(A7),A1 Zeiger auf ConfigOev holen feb204 moveq #$00,D3 03 löschen feb206 lea 16(A1},A0 Zeiger auf ExpansionRom-Struk febZOa btst #A,(A0} er Type = OIAGVALID = OAC_CONFIGTIME febZOe beq.s SfebZSe Ja, dann Oevice nicht bootbar Ende febZIO moveq #$00,00 00 löschen Die folgende Teilroutine nimmt einzeln die Einträge er_ReservedOc bis er_ReserverdOf und schiebt diese zu einer Langwortadresse zu¬ sammen, die auf die DiagArea-Struktur zeigt. Einfacher ist dies mit "MOVE.L 12(A0),D0". febZTZ move.b 13echo "Hallo" >ser: Nach der Eingabe dieser Zeile im CLI liest das CLI diese Zeile erst einmal durch. Dabei wird zweierlei ausgelöst: Die Standard-Ausgabe wird auf SER: gestellt, und das ECHO-Programm aus dem C-Ordner wird gestartet. Dieses bekommt als Parameter den Rest der Zeile über¬ geben bzw. einen Zeiger darauf. Es gibt dann lediglich den angegebe¬ nen Text "Hallo" an die Standard-Ausgabe weiter. Das war eigentlich schon alles. Doch was geschieht im einzelnen bei der Umlenkung der Ausgabe auf SER:? Gehen wir einmal davon aus, daß zu diesem Zeitpunkt noch keine Ein- oder Ausgabe auf der seriellen Schnittstelle stattgefunden hat. Diese muß also erst einmal geöffnet werden. Und das geht folgender¬ maßen vor sich: Die Ein-/Ausgaben des AmigaDOS werden über sogenannte Händler geleitet. Diese Händler bearbeiten die Ein-/Ausgabekanäle, die dem Benutzer lediglich als Namen bekannt sind, wie z.B. SER:, PAR:, DFx: oder RAM:. Das DOS gibt an diese Händler, die als eigene Prozesse im Speicher liegen müssen, nur die nötigen Befehle weiter, den Rest be¬ sorgt der Händler. Die Befehle vom DOS an den Händler und die Rückmeldungen werden in Form von DOS-Packets übermittelt, die auf einer Message-Struktur aufliegen. Der genaue Aufbau der Packets wird in einem späteren Kapitel näher erläutert. In unserem Beispiel wird also vom £)OS ein Händler benötigt. In sei¬ ner internen Device-Liste sieht es somit nach, welcher Händler zu der Kennung SER: gehört. Sollte es hier nichts finden, so würden die Einträge in der MountList überprüft; ist dies auch erfolglos, kommt der beliebte Requester "Please insert volume xxx in any drive". Aber in unserem Beispiel hat das DOS ja schon bei der internen Liste Glück und findet heraus, daß der Port-Händler für das SER:,also die serielle Schnittstelle zuständig ist. Dieser Händler liegt auf der SYS:- 522 Amiga intern Diskette, von der der Rechner gebootet wurde, im L-Ordner. Von dort wird er geladen und gestartet. Nach dessen Initialisierung wird ihm dann der Befehl übermittelt, einen Ausgabekanal zur Verfügung zu stellen. Dies findet in Form ei¬ nes Packets des Typs ACTION_FIND_OUTPUT statt. Der Händler stellt nun fest, daß noch kein Ausgabe-Kanal zur Verfügung steht. Er lädt also das zur seriellen Schnittstelle gehörende Device (serial.device) nach und initialisiert dies. Ist dies ordnungsgemäß geschehen, so mel¬ det es das Device dem Händler, der daraufhin das Antwortfeld des DOS-Packets mit einer OK-Meldung ausfüllt und an das DOS zu¬ rückschickt. Das DOS gibt daraufhin dem aus dem zurückgekommenen Packet entnommenen Status der Operation, also das OK, an den auf¬ rufenden Prozeß weiter. Aus diesem kompliziert anmutenden Ablauf wird eine klare Hierarchie deutlich. Von einem Programm, das über das DOS auf die serielle Schnittstelle zugreift, sind es mehrere Schritte bis zur Hardware: Software ==> Hardware-Programm => DOS ==> Händler => Device => I/O-Chip (CIA) Der Nachteil dieser Methode gegenüber der "normalen", daß das DOS direkt auf die Hardware-Rescourcen des Rechners zugreift, ist klar ersichtlich: Das Ganze kostet seine Zeit. Dem stehen jedoch große Vorteile gegenüber, wie z.B. die mögliche Verteilung der verschie¬ denen Aufgaben auf mehrere Programmteile (Händler, Device und DOS) sowie die enorm offene Architektur des Amiga. Stellen Sie sich vor, es wird ein neuer Ein-/Ausgabekanal durch den Einbau einer neuen Hardware verfügbar: es muß dann lediglich ein passendes De¬ vice und/oder ein neuer Händler geschrieben werden, und schon ist die Sache voll kompatibel! Kommen wir nun zu den Arten von Händlern, die nicht nur einen bloßen Datenstrom verwalten können: den File-Systemen. 3.1.4 File-Systeme File-Systeme wie DFx: oder RAM: unterscheiden sich von normalen Händlern dadurch, daß sie nicht nur einen sequentiellen Datenstrom verwalten können, sondern auch einen direkten Datenzugriff bieten. Dazu kommt noch der Unterschied, daß zusätzlich zum Device-Namen wie DFO: noch ein Pfadname bzw. Filename angegeben werden kann bzw. muß. Händler sind also eine Untermenge der File-Systeme. Das AmigaDOS 523 Der Vorgang der Auswertung der DOS-Pakete ist zunächst einmal der gleiche wie bei normalen Händlern, da der Teil des Namens nach dem Doppelpunkt nicht beachtet wird. Ist jedoch das entsprechende Device geöffnet und initialisiert, wird der Filename ausgewertet. Die beiden standardmäßig vorhandenen File-Systeme DFx; und RAM: unterscheiden sich in einem wichtigen Punkt voneinander: der RAM:- Handler benötigt kein Device für seine Arbeit, da er direkt mit dem Speicher arbeitet. Für den DFx:-Handler ist dagegen das Trackdisk- Device nötig, um die Verbindung zur Hardware herzustellen. Somit ist der RAM:-Handler, welcher ja auch nicht im ROM, sondern im L- Ordner liegt, ein etwas ungewöhnliches Exemplar. 3.1.5 Devices Wenn nun der Händler sich entschieden hat, welche Art von Ein-/ Ausgabekanal er zu bearbeiten hat, ruft er das* dafür zuständige De¬ vice auf. Diese Devices sind also die Programme, die schließlich den direkten Zugriff auf die Hardware des Amiga haben. Der Händler (bzw. das File-System) sendet das vom Programm erhaltene Paket in geeigneter Form an das Device weiter. Hat das Device seine Aufgabe erledigt, z.B. einige Zeichen über die serielle Schnittstelle abgeschickt, so meldet dies der Händler durch Zurücksenden des Packets ans DOS. 3.2 Die DOS-Bibliothek Die vielfältigen Funktionen, die auf der oberen DOS-Ebene verfügbar sind, werden dem Benutzer in Form einer Bibliothek zugänglich ge¬ macht, die ähnlich der EXEC-Bibliothek aufgebaut ist. Ebenso wie die EXEC-Bibliothek befindet sich die DOS-Bibliothek nicht als Datei auf der Diskette wie z.B. die Intuition-Library. Den¬ noch muß sie vor der Benutzung erst einmal geöffnet werden, damit man den Zugriff zum DOS bekommt. Bei diesem Vorgang bekommt das öffnende Programm über eine Zeigertabelle Zugang zu den DOS- Funktionen. 524 Amiga intern 3.2.1 Laden der DOS.LIBRARY Will ein Programm eine Funktion der DOS-Bibliothek nutzen, so muß es diese erst einmal öffnen. Dafür wird eine Funktion des EXEC ver¬ wendet, die den Namen OldOpenLibrary trägt. Dieser Funktion wird ein Zeiger auf den Namen der Bibliothek übergeben, der in Klein¬ buchstaben vorliegen und mit einem Null-Byte abgeschlossen sein muß. Es kann auch die Funktion OpenLibrary verwendet werden, der ein weiterer Parameter übergeben werden muß: die gewünschte Ver¬ sion der Bibliothek. Ist die Versionsnummer der Bibliothek größer oder gleich dieser Nummer, so wird diese geöffnet. Aus diesem Grund wird üblicherweise hier eine Kuli eingetragen, damit die Version keine Rolle spielt. In C gestaltet sich dies recht einfach. Durch die Zeile DOSBase = OpenLibrary (''dos.Ubrary",0); erhält man von EXEC in DOSBase einen Zeiger auf die DOS-Biblio- thek übergeben, mit dem man dann die DOS-Funktionen aufrufen kann. Der Zeiger muß dabei nicht weiter verwendet werden, da der C-Compiler dies übernimmt. Lediglich die Kontrolle, ob das DOS ordnungsgemäß geöffnet wurde, kann durch Überprüfen des Rückga¬ bewertes geschehen: Er ist Null, wenn ein Fehler aufgetreten ist. Dies kann etwa so aussehen: if (DOSBase == 0) exit (DOS_OPEN_ERROR); In Maschinensprache ist dies nicht ganz so einfach, aber dennoch leicht zu überschauen. Das öffnen der DOS-Bibliothek wird etwa fol¬ gendermaßen programmiert: EXEC_Base = 4 OldOpenLibrary = -408 move.l lea jsr tnove. l beq EXEC_Base,a6 ;Zeiger auf EXEC-Base in A6 DOS_Na(ne,a1 ;Zeiger auf Bibliotheksnamen OldOpenLibrary(aö) ;Bibliothek öffnen dO,DOS_Base ;Zeiger auf DOS-Base retten error ;Fehler aufgetreten... error: ;Fehlerbehandlung... DOS_Base: DOS_Naine: dc.l 0 ;Platz für DOS-Basis dc.b "dos.library",0 Das AmigaDOS 525 Der in DO erhaltene Zeiger wird für jeden folgenden Aufruf einer DOS-Funktion benötigt. Hat das Öffnen der Bibliothek nicht funktio¬ niert, so wird in DO eine Null zurückgegeben und in diesem Pro¬ gramm in die Fehlerbehandlungsroutine "error" verzweigt. Die DOS-Bibliothek wird also von obigem Programm durch den Zei¬ ger verfügbar gemacht. Sie ist ähnlich wie die EXEC-Bibliothek auf¬ gebaut und wird daher auf die gleiche Art und Weise bearbeitet. Die Einsprung-Adressen der einzelnen Funktionen liegen unterhalb der in DOS_Base liegenden Basisadresse und werden daher auch mit negati¬ ven Offsets aufgerufen. 3.2.2 Funktionsaufruf und Parameterübergabe Für den Aufruf einer DOS-Funktion werden außer der Adresse der Funktion selbst meist noch einige Parameter benötigt, die übergeben werden müssen. Diese werden in den Prozessor-Datenregistern Dl bis D4 übergeben. Ein Beispiel: Um ein einfaches Fenster zu öffnen, wird die DOS- Funktion Open() verwendet. Es werden folgende Parameter benötigt: Ein Zeiger auf den Namen der zu öffnenden Datei, der mit ei¬ nem Null-Byte endet, im Register Dl. Für unser Beispiel wird als Name die Fensterdefinition CON: mit den entsprechenden Parametern eingesetzt. Die Angabe des Zugriffsmodus wird in D2 verlangt. Dieser Mo¬ dus gibt an, ob es sich um eine neu anzulegende oder eine be¬ reits existierende Datei handelt. Für das im Beispiel zu öffnende Fenster wird der Modus "alt" übergeben, damit aus dem Fenster auch gelesen werden kann. Das Maschinenprogramm für dieses Beispiel sieht dann etwa so aus: Open = -30 Mode_old = 1005 move.l #FileName.dl ;Zeiger auf Datei-Definition move #Mode_old,d2 ;Modus: alt move.l D0S_Base,a6 ;DOS-Basisadresse in A6 jsr 0pen(a6) ;Datei (Fenster) öffnen move.l dO,ConHandle ;Datei-Handle-Zeiger retten 526 Amiga intern beq error ;Fehler aufgetreteni ConHandle: dc.l 0 ;Platz für Datei-Handle FileName: dc.b ''CON:!0/10/620/200/** Test-Fenster **",0 Auf die genaue Verwendung des Standard-Kanals CON: wird in einem späteren Kapitel genauer eingegangen. 3.2.3 Die DOS-Funktionen In diesem Kapitel werden nun alle IX)S-Funktionen aufgeführt. Die Offsets werden ebenso angegeben wie die Register, in denen die ver¬ schiedenen Parameter übergeben werden müssen. 3.2.3.1 Allgemeine Ein-/Au8gabe-Funlctionen Op^n Bat«! Handle = Open (Name, Modus) 00 -30 01 02 öffnet die Datei, deren Definition als mit einem Null-Byte abge¬ schlossener Text vorliegt, auf den Dl zeigt. Der Modus in D2 kann Mode_readwrite (1004 bei DOS 1.2) für Ein- /Ausgaben, Mode_old (1005) für Eingaben aus oder Mode_new (1006) für Ausgaben in die Datei sein. In DO wird ein Zeiger auf die Filehandle-Struktur zurückgegeben oder eine Null, wenn die Funktion nicht ausgeführt werden konnte. Die Filehandle-Struktur hat folgenden Aufbau: OfTset Name Bedeutung 0 Link Unbenutst. 4 Interact Wenn <> 0, ist die Datei interaktiv. 8 ID Identifikationsnummer der Datei. 12 Buffer Zeiger auf intern benötigten Speicher. 16 CharPos Aktuelle Position im Puffer (intern benötigter Zeiger). 20 BufEnd Zeiger auf Ende des Puffers (intern benötigter Zeiger). 24 ReadFunc Zeiger auf Routine, die bei geleertem Puffer aufgerufen wird. Das AmigaDOS 527 28 WriteFunc Zeiger auf die Routine, die bei gefüll¬ tem Puffer aufgerufen wird. 32 CloiaFunc Zeiger auf die Routine, die beim SchlieSen der Datei aufgerufen wird. 36 Argumantl 40 Argumant2 Dateityp-abhängige Argumente. Die meisten Einträge sind für die interne Verwendung des AmigaDOS vorgesehen. Diese Werte sollten nicht manipuliert werden. CI0S8 Datei echliefien Close ( Handle ) -36 Dl Schließt die mit Open geöffnete Datei. Der in Dl übergebene Zeiger ist der von der Open-Funktion erhaltene Zeiger auf die Filehandle- Struktur. Read Daten laseii Anzahl = Read ( Handle, Buffer, Lange); DO -42 D1 D2 D3 Liest aus der mit "Handle" spezifizierten Datei bis zu "Länge" Bytes in den Speicher ab Adresse "Buffer". Der in DO zurückgegebene Wert gibt die Anzahl der wirklich gelese¬ nen Bytes an. Ist diese Zahl 0, so wurde das Ende der Datei erreicht. Tritt ein Fehler auf, so wird -1 zurückgegeben. Write Daten schreiben Anzahl = Write ( Handle, Buffer, Länge ) DO -48 D1 D2 D3 Schreibt in die mit "Handle" spezifizierte Datei "Länge" Bytes aus dem Speicher ab Adresse "Buffer". In DO wird die Anzahl der Bytes zurückgegeben, die wirklich ge¬ schrieben wurden. Ist dieser Wert -1, so ist ein Fehler aufgetreten. 528 Amiga intern SMk Dateizeiger versteüen Position = Seek ( Handle, Abstand, Modus ) DO -66 Dl D2 D3 Diese Funktion verstellt den internen Zeiger in der mit "Handle" spe¬ zifizierten Datei. Der "Modus" bestimmt, ob der in "Abstand" angege¬ bene Wert den Zeiger relativ zum Dateianfang, zur aktuellen Position oder zum Dateiende verstellen soll. Dieser Wert wird von dort aus vorzeichenrichtig gerechnet, so daß auch rückwärts verschoben werden kann. Die möglichen Modi sind; OFFSET_BEGINNING -1 OFFSET CURRENT 0 OFFSET^END 1 Der Rückgabewert gibt die nach der Ausführung der Funktion aktu¬ elle Position des Zeigers an. Um die momentane Position des Zeigers festzustellen, kann somit einfach der Modus "relativ zur aktuellen Po¬ sition" (OFFSET_CURRENT) eingestellt und um 0 Bytes verschoben werden; Die zurückgegebene Position ist gleich der alten. inpitl StMid«rd-ElitgAi)elt»n«l «rmllteln Handle = Input (} DO -54 Diese Funktion ermittelt das Handle des Kanals, aus dem die stan¬ dardmäßigen Eingaben gelesen werden können. Dies ist in dem Fall, wenn das Programm vom CLI aus aufgerufen wurde, das Handle des CLI-Fensters. Wird im CLI-Kommando, das das Programm aufgerufen hat, von der Möglichkeit der Datenumlenkung Gebrauch gemacht, so wird das Handle des gewählten Kanals ermittelt, z.B.; >Progrannnaine Prograiinname >PRT; die Standard-Ausgaben des aufgerufenen Programms zum Drucker schickt. Walt Auf Empfang eines ZckEens wsiteit Status = UaitForChar ( Handle, Timeout ) 00 -204 01 02 Diese Funktion wartet die in "Timeout" angegebene Anzahl von Mi¬ krosekunden auf den Empfang eines Zeichens aus dem mit "Handle" spezifizierten Kanal (z.B. RAW;-Fenster, warten auf Tastendruck). Wird in dieser Zeit kein Zeichen empfangen, so wird in "Status" eine 0 zurückgegeben, andernfalls der Wert -1. Das Zeichen kann dann mit der Read-Funktion ausgelesen werden. Die Funktion ist nur verfügbar, wenn es sich bei dem Kanal um einen interaktiven Kanal handelt (virtuelles Terminal), wie z.B. ein RAW:- Fenster, in dem Ein- und Ausgaben gleichermaßen stattfinden können und die Daten nicht unbedingt sofort auf Anforderung kommen. istnteractive K«iui>Typ «rmlfteln Status = Islnteractive ( Handle ) 00 -216 01 In "Status" wird TRUE (-1) zurückgegeben, wenn es sich bei dem mit "Handle" spezifizierten Kanal um ein virtuelles Terminal handelt, mit dem Ein- und Ausgaben stattfinden können. Andernfalls erhält man FALSE (0) zurück. 530 Amiga intern loErr EiB-/Ausga(iefe1>ier ermttteüi Error = loErr () DO -132 Wird nach dem Aufruf einer Funktion ein Fehler signalisiert, meist durch eine Null als Rückgabe wert in DO, so kann durch den Aufruf von Io£rr() die genaue Fehlermeldung ermittelt werden. DO enthält dann die Nummer des zuvor aufgetretenen Fehlers (vergl. WHY- Kommando des CLI). Eine Auflistung der Fehlerwerte finden Sie in einem späteren Kapitel. 3.2.3.2 Disketten-Operationen Die folgenden DOS-Funktionen beziehen sich auf die Verwaltung von Filing-Systemen. Die Überschrift ist daher auf die üblichste Art von Filing-Systemen bezogen; auf Disketten. CrSQtsDir Unter-Directory erstellen Lock = CreateOIr ( Name } DO -120 Dl Es wird das Unter-Directory "Name" im aktuellen Directory erstellt. Der Rückgabewert stellt einen Zeiger auf eine Datei-Struktur (Lock) dar, die folgenden Aufbau hat 0 NextBlock 4 DiakBlock 8 AccewTyp« 12 ProccHlD 16 VolNode Zeiger auf n&chsten, mit diesem ver¬ ketteten Lock oder Null. Block-Nummer dee Directories bsw. des File-Headers. Zugriffstyp: -1= exklusiver, -2= allgemeiner Zugriff Identifikationsnummer. Zeiger auf Disketten-Info. Diese Struktur stellt sozusagen den Schlüssel zu dieser Datei bzw. dem Unter-Directory dar, da mit ihr auf diese zugegriffen werden kann, (vergl. MAKEDIR-Kommando des CLI). Das AmigaDOS 531 Lock Datei$chla$sel ermitteln lock = Lock ( Name, Modus ) DO -84 D1 D2 Es wird eine Datei oder ein Unter-Directory mit Namen "Name" auf der Diskette gesucht und dafür eine Struktur erzeugt. Der Modus be¬ stimmt, welcher Art der Zugriff auf diese Datei sein soll. Ist er Lesen (-2), so kann aus dieser Datei von mehreren Tasks gelesen werden, ist er Schreiben (-1), kann nur dieses Programm in die Datei schreiben. CurrentDfr Uater-HlnsetoJiy zum aktuellen erheben oldLock = CurrentDir ( Lock ) DO -126 D1 Das durch "Lock" spezifizierte Unter-Directory wird zum aktuellen Directory erhoben (siehe CD-Befehl des CLI). Der zurückgegebene Wert stellt den Zeiger auf das vorher aktuelle Directory bzw. dessen Lock dar. ParontDir übergeordnetes Directory ermitteln Lock_neu = ParentDir ( Lock ) DO -210 Dl Das dem mit "Lock" angegebenen Directory übergeordnete Directory wird ermittelt und dessen Lock in DO zurückgegeben. Gehörte "Lock" bereits zum obersten Directory (Root-Directory), so wird in DO eine Null zurückgegeben. DoictcFila Datei löschen Status = DeleteFile ( Name ) DO -72 Dl Die Datei mit dem angegebenen Namen wird gelöscht. Der Name muß als mit einem Null-Byte abgeschlossener Text vorliegen. In DO wird eine Fehlermeldung zurückgegeben, wenn die Funktion nicht ausge- 532 Amiga intern führt werden konnte (Datei nicht vorhanden, Datei schreibgeschützt, Directory nicht leer etc.). Wird ein Unter-Directory zum Löschen angegeben, so dürfen in die¬ sem keine Einträge mehr vorhanden sein. Rename umbesanncB Status = Rename ( Naine_alt, Naffle_neu > DO -78 D1 D2 Die Datei oder das Directory mit dem in "Name_alt" angegebenen Namen wird umbenannt. Sollte eine Datei mit dem neuen Namen be¬ reits existieren, so wird abgebrochen und ein Fehler zurückgegeben. Die beiden Namensangaben können auch eine Pfadangabe enthalten. In diesem Fall wird die Datei vom alten in das neue Directory mit dem neuen Namen gebracht. Dies funktioniert allerdings nur auf ein und derselben Diskette. DupLöOk Lock kutpleten neuLock = DupLock ( Lock } DO -96 D1 Die alte Lock-Struktur wird in eine neue kopiert. DO zeigt dann auf die neue Struktur. Dies kann verwendet werden, wenn mehrere Pro¬ zesse auf diese Datei zugreifen sollen. Es kann jedoch kein Lock ko¬ piert werden, der nur zum Schreiben zugelassen ist, da dieser ohnehin nur für den exklusiven Zugriff zugelassen ist. UnLock Lock entfernen UnLock ( Lock ) -90 Dl Die Lock-Struktur, die mit Lock(), DupLock() oder CreateDir() er¬ stellt wurde, wird entfernt und belegter Speicher wieder freigegeben. Das AmigaDOS 533 ExStnlnO Datel-Iaformationen holen Status = Examine ( Lock, InfoBlock ) DO -102 01 02 Die Struktur, auf die D2 zeigt, wird mit Informationen über die durch Lock spezifizierte Datei gefüllt. Diese Struktur wird FilelnfoBlock ge¬ nannt und ist folgendermaßen aufgebaut; Offnt Name Bedeutung 0 DiskKey.L 4 D irEnt ry Ty pe .L 8 FileName 116 Protection.L 130 EntryType.L 134 Sise.L 138 NumBlocks.L 133 Days.L 136 Minute.L 140 Tick.L 144 Comment Diskettennummer. Eintragstyp (+=Directory, '=Datei) 108 Bytes mit dem Dateinamen. Datei geschUtet? Eintragstyp. Dateilänge in Bytes. Ansahl der damit belegten Blocks. Erstellungsdatum. Erstellungsseit. Erstellungsseit. 116 Bytes mit Kommentar. 0 enthält Null, wenn die Funktion nicht ausgeführt werden konnte. ExNexi Nächsten Directory-Eintrag ermitteln Status = ExNext ( Lock, InfoBlock ) 00 -108 01 02 Dieser Funktion wird der mit ExamineO gefüllte InfoBlock sowie der Lock des gewählten Directories übergeben. Es werden nun die Infor¬ mationen des ersten passenden Eintrages dieses Directories in den In¬ foBlock eingetragen. Bei einem weiteren Aufruf von ExNextO wird dann jeweils der nächste passende Eintrag dieses Directories gesucht, und dessen Informationen werden zurückgegeben. Ist kein weiterer Eintrag mehr zu finden oder ein sonstiger Fehler aufgetreten, wird in DO Null zurückgegeben. Mit den Befehlen Lock(), ExamineO und ExNextO kann das Inhalts¬ verzeichnis einer Diskette ausgelesen werden. Der Weg ist folgender: 1. Mit LockO wird der Schlüssel zum gewünschten Directory er¬ stellt. 534 Amiga intern 2. Mit ExamineO kann nun der Directory-Name bzw. der Name der Diskette ermittelt werden. Gleichzeitig wird der File- InfoBlock erstellt, der für die nächste Funktion benötigt wird. 3. Durch mehrmaligen Aufruf der ExNext()-Funktion werden nun die einzelnen Einträge des Directories ausgelesen und deren In¬ formationen im FilelnfoBlock eingetragen. Dies wird so oft wie¬ derholt, bis die ExNext()-Funktion eine Null zurückgibt: Keine weiteren Einträge mehr vorhanden! Hier ein kleines Maschinen-Programm, das diese Schritte ausführt. Die aufgerufene Print-Routine ist hier nicht aufgeführt, sie könnte z.B. den Namen und die Länge der momentan ausgelesenen Datei auf dem Bildschirm ausgeben. Vor dem Aufruf dieser Routine muß die DOS- Bibliothek geöffnet werden und die DOS-Basisadresse in "dosbase" ab- gelegt werden. Lock = -84 Examine = -102 ExNext = -108 loErr = -132 directory: ;• Inhaltsverzeichnis von DFO: inove.l dosbase,a6 ;DOS-Basisadresse in A6 move.l #name,di ;Zei 9 er auf Pfad-/Dateiname move.l #-2,d2 ;Modus 'Lesen' jsr Lock(a6} ;Datei suchen tst.l dO ;Gefunden? beq Error ;Nein! move.l dO,locksav ;Sonst Schlüssel retten move.l dosbase,a6 ;DOS-Basisadresse move.l locksav,dl ;Schlüssel in Dl move.l #fileinfo,d2 ;Zeiger auf FilelnfoBlock Jer Exaffline(a6} ;Disk-Namen holen tst.l dO ;0K? beq error ;Nein (komnt kaum vor) bra ausgeben ;Sonst Namen ausgeben loop: ;* Dateinamen auslesen move.l dosbase,s6 ;DOS-Basisadresse move.l locksav,dl ;Schlüssel in Dl move.l #fileinfo,d2 .-Zeiger auf FilelnfoBlock Jsr ExNexttaö) .-Nächste Datei suchen tst.l dO ;Gefunden? beq error ,-Nein: Ende ausgeben: ;* Namen ausgeben bsr Print ;Namen etc. ausgeben/ausuerten bra loop ;und ueitermachen... error: ,-* I/O-Status ermitteln move.l dosbase, a6 ;D0S-Basisadre8se in A6 Das AmigaDOS 535 ;Status holen ;Ende... name: dc.b 'DFOi'.O even locksav: blk.l 0 fileinfo: blk.l 260 jsr IoErr(a6) rts Nach der Beendigung dieser Routine wird in DO der Fehler-Code zu¬ rückgegeben, der durch die IoErr()-Funktion ermittelt wurde. Dieser Code sollte 232 (no_more_entries) sein, andernfalls ist etwas schief¬ gegangen... Dlskeftea'<'lafonuatinaett holaa Status = Info ( Lock, infoOata ) DO -104 01 02 Der Parameter-Block, auf den D2 zeigt, wird mit Informationen über die verwendete Diskette gefüllt. Dieser Block muß an einer Adresse beginnen, die durch 4 teilbar ist (Longword aligned). Lock muß zur Diskette oder einer Datei bzw. einem Unter-Directory dieser Diskette passen. Der Parameterblock InfoData hat folgenden Aufbau: Oftset Name Bedeutung 0 NumSoftErrors Ansahl der Diskettenfehler. 4 UnitNumber installierte Disketten-Einheit. 8 DiskState Disketten-Status (s.u.). 12 NumBlocks Ansahl der Blöcke auf der Diskette. 16 N umBlocksUsed Ansahl der verwendeten Blöcke. 20 BytesPerBlock Ansahl der Bytes pro Block. 24 DiskType Disketten-Typ (s.u.). 28 VolumeNode Zeiger auf Disketten-Namen. 32 InUse <>0, wenn Diskette aktiv. DiskState zeigt den Status der Diskette an. Dabei gibt es folgende Möglichkeiten: 00 Diskette ist schreibgeschOtst. 81 Diskette wird gerade repariert (validating). 82 Diskette OK und beschreibbar. DiskType enthüllt den Typ der Diskette als Text, wenn eine eingelegt ist. Die möglichen Werte sind: 536 Amiga intern -1 Keine Diskette eingelegt. BAD Diskette unlesbar (falsches Format). DOS DOS-Diskette. NDOS Format stimmt, keine DOS-Diskette. KICK Kickstart-Diskette. SstCOtnilHint IHtei-KoiBiBentar aetzen Status = SetCooment ( Name, Komientar ) DO -180 Dl D2 Die Datei oder das Unter-Directory "Name" wird mit einem Kom¬ mentar versehen. Der Kommentar darf bis zu 80 Zeichen lang sein und muß mit einem Null-Byte enden. SetProtection statu» »atzea Status - SetProtection ( Name, Maske > DO -186 01 D2 Der Schreib-/Lese-Status der Datei bzw. des Unter-Directories wird gesetzt. Die unteren 4 Bits der Maske haben folgende Bedeutungen: Bit Bedeutung, wenn geaetst _ 0 Datei nicht löschbar. 1 Datei nicht ausführbar. 2 Datei nicht Uberschreibbar. 3 Datei nicht lesbar. 3.2.3.3 Prozeß-VerwaKung CreateProc £iaen aeueu PiazeB enteilen Prozess = CreateProc ( Name, Pri, Segment, Stack ) DO -138 Dl D2 D3 D4 Es wird eine neue Prozeß-Struktur unter dem Namen erstellt, auf den Dl zeigt. Dieser Prozeß wird mit der in "Pri" angegebenen Prioritität laufen und einen Stack mit der Größe "Stack" erhalten. Das AmigaDOS 537 In "Segment" wird ein Zeiger auf eine Segment-Liste übergeben (siehe auch LoadSeg), in der der zu startende Programm-Code definiert wird. Im ersten Segment der Liste sollte dann das Programm beginnen. Das Ergebnis der Funktion ist die neue Prozeß-ID oder eine 0, wenn ein Fehler aufgetreten ist. Um die Funktionsweise von CreateProc, das ja eine recht interessante Funktion darstellt, zu demonstrieren, hier ein kleines Programm, das ein Programm namens Programm_Name lädt und es als Prozeß mit dem Namen Prozess_neu startet (als Hintergrund-Prozeß!): CreateProc-Demo S.D. ••••• OpenLib = -408 closellb = -414 ExecBase = 4 Open = -30 CI ose = -36 Read = -42 Urite » -48 iiiode_old * 1005 Loadseg » -150 UnLoadSeg = -156 CreateProc = -138 run: move.l execbase,a6 ;Zeiger auf EXEC-Bibliothek lea dosname(pc),a1 moveq #0,d0 jsr openlib(a6) ;Open DOS-Library move.l dO,dosbase beq error move.l #consolname,d1 ;Consol-Definition move.l )llli»de_old,d2 move.l dosbase, a6 jsr open(a6) ;Console öffnen beq error move. l d0,conhandle ;Consol'Handle retten move.l #name,d1 Jsr LoadSeg(a6) ;Prograiini laden tst. l dO beq error ;Schiefgegangen! move.l dO,Segment ;Zeiger auf Segmentliste retten move.l #pname,d1 ;Zeiger auf Namen in Dl move.l #0,d2 ;Prioritat in D2 move.l Segment,d3 ;Zeiger auf Segmentliste in D3 move.l #3000,d4 ;ProzeB-Stack-Länge in D4 jsr CreateProc(a6) ;Proze6 erstellen/Starten 538 Amiga intern beq error ;Schiefgegangen! move.l conhandle,dl move.l «inbuff,dZ ;Buffer-Adresse move.l «1,d3 ;1 Zeichen move.l dosbase,a6 jsr resd(a6) ;Von Tastatur einlesen move.l Segment,dl jsr LlnLoadSeg(a6) ;Neuen ProzeB weg error: move.l conhandle,d1 ;Fenster schlieBen move.l dosbase,a6 jsr close(a6) move.l dosbase,a1 ;DOS.Lib schlieBen move.l execbase,a6 jsr closelib(a6) rts ;Das war's dosbase: dc.l 0 conhandle: : dc.l 0 Segment: dc.l 0 inbuff: blk.b 8 consolname: dc.b 'RAW:100/50/300/100/** Haupt-ProzeB **’,0 dosname: dc.b 'dos.llbrary',0 nane: dc.b 'Programn-Nanie',0 pname: dc.b 'Prozess_neu',0 even Dieses Programm erzeugt also einen Prozeß. Auch in vorangegangenen Kapiteln war öfter die Rede von Prozessen. Was sind diese Prozesse eigentlich?. Prozesse sind mit Tasks vergleichbar, die über Exec erstellt werden können. Wie bei den Tasks handelt es sich bei einem Prozeß um eine Struktur die zur Verwaltung eines Programms dient, das innerhalb des Multitasking-Systems arbeitet. Diese Prozeß-Strukturen werden, wie auch die Task-Strukturen, in den Exec-Task-Listen (TaskReady-Liste und TaskWait-Liste) eingetragen. Der Unterschied zwischen Tasks und Prozessen ist der, daß Prozesse vom DOS erzeugt werden und dadurch auch, abgesehen von den In¬ formationen für Exec, Informationen für das DOS beherbergen. Es ist nicht verwunderlich, daß Prozeß-Strukturen innerhalb der Exec-Task-Listen zu finden sind, denn sie stellen lediglich eine Er¬ weiterung zu der bekannten Task-Struktur dar. Das AmigaDOS 539 Ein Prozeß wird immer dann kreiert, wenn ein Programm über das DOS geladen und gestartet wird (z.B. Starten eines CLI-Befehls). Sie können einen Prozeß auch "von Hand" erzeugen, indem Sie die DOS- Funktionen LoadSegO und CreateProc() benutzen. Die Prozeß-Struktur hat folgendes Aussehen: struct Process < /* Offsets */ struct Task pr_Task; /* 00 $00 */ struct MsgPort pr HsgPort; /* 92 S5C 00 $00 */ UORD pr_Pad; /* 126 J7E 34 $22 */ BPTR pr_SegList; /* 128 $80 36 $24 */ LONG pr_StackSize; /* 132 $84 40 $28 */ APTR pr_GlobVec; /* 136 $88 44 $2C */ LONG pr_TaskNun; /* 140 $8C 48 $30 */ BPTR pr_StackBase; /* 144 $90 52 $34 */ LONG pr_Result2; /* 148 $94 56 $38 •/ BPTR pr CurrentDir; /* 152 $98 60 $3C •/ BPTR pr_CIS; /• 156 $9C 64 .$40 •/ BPTR pr_COS; /* 160 $A0 68 $44 •/ APTR pr_ConsoleTask; /* 164 $A4 72 $48 */ APTR pr_FileSystemTask; /• 168 $A8 76 $5C */ BPTR pr'CLI; /• 172 $AC 80 $60 */ APTR pr_ReturnAddr; /• 176 $B0 84 $64 •/ APTR pr_PktWait; /• 180 $B4 88 $68 •/ APTR pr_UirKloHPtr; /• 184 $B8 92 $6C •/ >; Sie werden sich wahrscheinlich wundern, weshalb bei dieser Struktur pro Eintrag nicht ein, sondern zwei Offsets angegeben wurden. Hier¬ für müssen Sie wissen, daß der Zeiger, der von CreateProc() zurück¬ gegeben wird (der Zeiger auf den Prozeß), in Wirklichkeit ein Zeiger auf den Message-Port der oben gezeigten Prozeß-Struktur darstellt. DOS "hantiert" intern mit zwei Zeigern. Einerseits ist dies der Zeiger auf den Anfang der Struktur (also der Zeiger auf die Task-Struktur) und zum anderen ein Zeiger auf den Message-Port (Offset 92) der Prozeß-Struktur. Von beiden Zeigern ausgehend wird mit entspre¬ chenden Offsets auf die Struktur zugegriffen. Damit Sie die Zugriffe auf die hinter der Task-Struktur stehenden Eintragungen, auf die meistens über den Zeiger auf den Message-Port zugegriffen wird, besser nachvollziehen können, haben wir beide Offsets angegeben. Besprechung der Struktur: 540 Amiga intern pr_Task Task-Struktur, wie sie bereits im Exec-Kapitel beschrieben wurde. Sie wird für die Organisation des Multitasking benötigt. pr_MsgPort Message-Port-Struktur, die ebenfalls im Exec-Kapitel abgehandelt wird. Sie dient als Port für den Prozeß, so daß an diesen Messages gesandt werden können, ohne auf der Prozeßseite weitere Initialisie¬ rungen vornehmen zu müssen. Über diesen Port werden hauptsächlich Packets gesandt. Wir kommen noch darauf zurück. pr_Pad Dieses Wort ist eingefügt, um die nachfolgenden Einträge auf Lang¬ wortadressen zu bringen. pr_SegList BPTR-Zeiger auf das Feld der Segmentlisten, die für den Prozeß benötigt werden. pr_StackSize Das hier abgelegte Langwort gibt die Länge des für den Prozeß zur Verfügung stehenden Stapels an. pr_GlobVec Zeiger auf die globale Vektor-Tabelle, die für den Prozeß erstellt wurde, sofern es sich um ein BCPL-Programm handelt. pr_TaskNum Nummer des geöffneten CLL Es handelt sich dabei um die gleiche Nummer, die über den Prompt auf den Bildschirm ausgegeben wird. Sollte der Prozeß kein CLI sein, ist dieser Eintrag Null. pr_StackBase BPTR-Zeiger auf das obere Ende des für den Prozeß zur Verfügung stehenden Stapels. Das AmigaDOS 541 pr_Result2 Das zweite Ergebnis des letzten Aufrufs eines DOS-Befehls, eine Fehlermeldung. pr_CurrentDir BPTR-Zeiger auf FileLock-Struktur, über die das aktuelle Directory angesprochen werden kann. prCIS BPTR-Zeiger auf den jetzigen CLI-Eingabe-Stream (Input-Stream). pr_COS BPTR-Zeiger auf den derzeitigen CLI-Ausgabe-Stream (Output- Stream). pr_ConsoleTask Zeiger auf Handler-Task für das aktuelle Console-Ausgabe-Fenster. pr_FileSystemTask Zeiger auf Handler-Task für das aktuelle Laufwerk. prjOLI BPTR-Zeiger auf eine weitere Struktur, die nähere Angaben über das Aussehen des CLIs macht. Die Struktur wird noch besprochen. pr_ReturnAddr pr_PktWait Hier ist der Zeiger auf eine eigene Routine eingetragen, die auf ein Packet wartet. Die Routine wird von der Funktion GetPacket() auf¬ gerufen, sofern der Prozeß über eine eigene Wartefunktion verfügt. 542 Amiga intern pr_WindowPtr Zeiger auf das benutzte Fenster. Er wird benötigt, falls der sonst ver¬ wendete Zeiger aufgrund eines Fehlers verlorengehen sollte. Wie aus der Struktur erkennbar, steht bei Offset 172 der BPTR-Zeiger "pr_CLr. Er weist auf eine weitere Struktur. Die Struktur nennt sich CommandLinelnterface und hat folgendes Aussehen: Tuet CommandLinelnterface { /* Offsets */ LONG cli_Result2; /* 00 *00 */ BSTR cl i_SetNaine; /* 04 *04 */ BPTR cl i_Comman(t)i r; /* 08 *08 */ LONG cli_ReturnCode; /* 12 *0C */ BSTR c l i_CommandNaine; /* 16 *10 */ LONG cli_FaiILevel; /* 20 *14 */ BSTR cli_Prompt; /* 24 *18 */ BPTR c l {_S t an^ rd I nput; /* 28 *1C */ BPTR cli_CurrentInput; /* 32 *20 */ BSTR cli_CommandFile; /* 36 *24 */ LONG cli_Intcractivc; /* 40 *28 */ LONG cli_Background; /* 44 *2C */ BPTR cl1~Currcnt0utput; /* 48 *30 */ LONG cli_DcfaultStack; /* 52 *34 */ BPTR cli~StandardOutput; /* 56 *38 */ BPTR cli_Module; /* 60 *3C */ >; cU_Result2 Fehlermelung des letzten CLI-Aufrufs. cli_SetName BPTR-Zeiger auf den Namen des derzeitigen Directories. cli_CommandDir BPTR-Zeiger auf die FileLock-Struktur, über die auf das Verzeichniss der CLI-Befehle zugegriffen werden kann. cli_ReturnCode Rückgabewert des letzten CLI-Kommandos. cli_CommandoName Zeiger auf den Namen des gerade arbeitenden Befehls. Das AmIgaDOS 543 cli_FailLevel Wert, der mit FAILAT angegeben wird. cli_Prompt Zeiger auf den Prompt-String. cli_StandardInput Zeiger auf die standardmäßige Eingabe (Tastatur). cli_CurrentInput Zeiger auf derzeitige Eingabe. cli_CommandFile Zeiger auf den Namen des Kommando-Files, das abgearbeitet wird (z.B. Startup-Sequence). cli_Background Hat den Wert 1, wenn CLI-Befehl mit RUN aufgerufen. cli_CurrentOutput Zeiger auf derzeitige Ausgabe. cl i_DefaultStack Größe des zur Verfügung gestellten Stapels in Langworten. cli_StandardOutput Standardmäßige Ausgabe (Bildschirm). cli_Module Zeiger auf Segmentliste für derzeitig abgearbeiteten CLI-Befehl. 544 Amiga intern DdteStftirip Datum «ad Ulirzeit ermitteln DateStanp ( Vektor } -192 Dl In Dl wird ein Zeiger auf eine Tabelle aus drei Langworten zurückge¬ geben. Ist die Zeit im Amiga nicht gesetzt, so enthalten all diese Langworte eine 0. Andernfalls enthält das erste Langwort die Anzahl der vergangenen Tage seit dem 1. Januar 1978, das zweite die Anzahl der seit Mitternacht vergangenen Minuten und das dritte die in dieser Minute vergangenen 50stel Sekunden. Dieser Wert ist jedoch immer ein Vielfaches von 50, so daß nur die Sekundenzahl* 50 angegeben wird. Delay Laufenden Prozeß kurzzeitig 4t0ll|>en Delay ( Zeit ) •198 Dl Der laufende Prozeß wird um die in "Zeit" angegebene Anzahl von 50stel Sekunden angehalten. DevIcePfOc I/O verwendenden Prozeß ermitteln ProzeB = DeviceProc ( Name ) DO -17A Dl Die Identifikation des Prozesses, der momentan den mit "Name" ange¬ gebenen Ein-/Ausgabekanal verwendet, wird zurückgegeben oder eine 0, wenn kein Prozeß gefunden wurde. Bezieht sich der Name auf einen auf Diskette liegenden Kanal, so kann mit der IoErr()-Funktion ein Zeiger auf die Lock-Struktur des entsprechenden Directories erhalten werden. Das AmigaDOS 545 Exit Frognmun beendan Exit ( Parameter } -IM D1 Das laufende Programm wird beendet. War das Programm vom CLI aus aufgerufen worden, so wird diesem wieder die Kontrolle überge¬ ben und der in "Parameter" übergebene Integer-Wert als Rückgabewert interpretiert. Wurde das Programm als Prozeß gestartet, so wird durch Exit() dieser Prozeß gelöscht und der durch ihn verwendete Stack-, Segment- und Prozeß-Speicherbereich wieder freigegeben. EXTOUtO CLI->Kommaado «ifrufeit Status = Execute ( Kommando, Input, Output ) DO -222 D1 D2 D3 Das CLI-Kommando, das als Text vorliegt und auf das Dl zeigt, wird ausgeführt. Mit "Input" und "Output" können die Ein- und Ausgaben des CLI-Kommandos in irgendwelche Kanäle umgeleitet werden, de¬ ren Handle dann hier angegeben werden muß. Wird fürlnput oder Output eine 0 angegeben, so wird der Standard-Kanal verwendet. Mit diesem Kommando können Sie leicht ein eigenes CLI erstellen, welches z.B. ein eigenes Fenster öffnet und dann Execute() mit dem Fenster-Handle für Input und einem leeren Kommando-Text aufruft. Die Kommandos werden dann im Fenster eingegeben und die Ausga¬ ben ebenfalls in dieses Fenster gebracht. Dieses eigene CLI kann auch mit dem ENDCLI-Befehl beendet werden, wobei sich das Programm RUN im C:-Directory befinden muß. LoadSeg Frogradimdatel Udte Segment = LoadSeg ( Name ) DO -150 Dl Die Programmdatei "Name" wird in den Speicher geladen. Das Pro¬ gramm wird eventuell über mehrere Speichermodule verteilt, wenn nicht genug zusammenhängender Speicherplatz verfügbar ist. Die da¬ durch entstehenden Segmente werden untereinander verkettet, indem 546 Amiga intern der erste Eintrag jedes Segmentes ein Zeiger auf das nächste Segment der Liste ist. Ist dieser Zeiger 0, so ist dies das letzte Segment. Tritt bei diesem Vorgang ein Fehler auf, so werden alle bereits ge¬ ladenen Segmente wieder freigegeben, und eine 0 wird in DO zurück¬ gegeben. Andernfalls enthält DO einen Zeiger auf das erste Segment. Das geladene Programm kann nun mit CreateProc() gestartet werden oder mit UnLoadSegO wieder gelöscht werden. Geladene Fi‘0gr4mmd4tei I5$ch4n UnLoadSeg ( Segment ) -156 01 Die mit LoadSegO geladene Programmdatei wird gelöscht und der verwendete Speicher wieder freigegeben. Der Zeiger in Dl weist auf das erste Segment der Liste (siehe LoadSeg). GotPacket Paket abholen Status = GetPacket ( Uaitflag } 00 -162 01 Es wird ein Paket abgeholt, das von einem anderen Prozeß gesendet wurde. Ist das Waitflag TRUE (-1), so wird auf den Erhalt des Pake¬ tes gewartet, andernfalls wird nicht gewartet und eine Null zurQckge- geben, wenn kein Paket vorliegt. QueuePacket Paket abaeaden Status = QueuePacket ( Paket ) 00 -168 01 Das Paket, auf dessen Struktur Dl zeigt, wird abgeschickt. Ist dies einwandfrei geschehen, so wird in DO ein Wert <> 0 zurQckgegeben. Das AmigaDOS 547 3.2.4 DOS-Fehlermeldungen In der folgenden Liste werden die mit IoErr() bzw. dem WHY-Kom- mando des CLI ermittelten Fehler-Codes und deren Name bzw. Be¬ deutung auf geführt. Code Meldung Bedeutung 103 Insufficient free störe Nicht genügend Speicherplatc frei. 104 Task table full Bereits 20 Prosesse, mehr geht nicht. 120 Argument line invalid or too long Die Argumenteliste für diesen Befehl ist nicht korrekt oder enthält su viele Angaben. 121 File is not an object module Aufgerufene Datei ist nicht lauffähig. 122 Invalid resident library during load Aufgerufene residente Bibliothek ist ungültig. 202 Object in use Angegebene Datei bsw. Directory ist momentan bereits von einem anderen Programm in Benutsung und nicht für andere Anwendungen verwendbar. 203 Object already exists Der angegebene Dateiname existiert bereits. 204 Directory not found Das gewählte Directory existiert nicht. 20S Object not found Kanal mit dem Namen existiert nicht. 206 Invalid window Die Angabe der Parameter für das eu öffnende Fenster ist nicht korrekt. 209 Packet requested type unknown Die gewünschte Funktion ist auf dem angegebenen Gerät nicht möglich. 210 Invalid stream component name Dateiname ist ungültig (su lang oder mit unerlaubten Zeichen versehen). 211 Invalid object lock Angegebene Lock^Struktur ungültig. 212 Object not of required type Dateiname und Directory-Name sind verwechselt worden. 213 Disk not validated Die Diskette ist entweder noch nicht vom System anerkannt oder defekt. 214 Disk write-protected Die Diskette ist schreibgeschütst. 215 Rename across devices attempted RENAME-Funktion ist nur innerhalb einer Diskette möglich. 216 Directory not empty Ein nicht leeres Directory sollte ge¬ löscht werden. 218 Device not mounted Gewählte Diskette ist nicht eingelegt. 219 Seek error Seek()-Funktion ist mit unerlaubten Parametern versehen. 220 Comment too big Der Kommentar sur Datei ist su lang. 221 Disk full Die Diskette ist voll bsw. enthält nicht genug freien Plats für Anwen¬ dung. 222 File is protected from deletion Die Datei ist nicht löschbar bsw. ge¬ gen das Löschen geschütst worden. 223 File is protected from writing Die Datei ist gegen Überschreiben ge- BchUtst. 224 File is protected from reading Die Datei ist gegen Lesen geschütst. Bei den letsten drei Fehlermeldungen können Sie mit dem LIST-Befehl den Status der betroffenen Dateien über¬ prüfen. 548 Amiga intern 225 Not a DOS disk Diskette ist nicht im AmigaDOS- Format formatiert. 226 No disk in drive Im angegebenen Laufwerk ist keine Disketteeingelegt. 232 No more entriss in directory Die ExNext()-Funktion konnte keinen weiteren Eintrag im Directory finden. 3.3 Standard-l/0 Ein sehr wichtiger Bestandteil eines Programms ist der Datenaustausch mit der Umwelt, wie z.B. Bildschirm, Tastatur, Disketten oder Schnittstellen. Diese Ein-/Ausgabe, auch einfach I/O (Input/Output) genannt, befähigt ein Programm erst zu der vollen Ausnutzung des Computers, auf dem es läuft. Um dies zu bewerkstelligen, gibt es grundsätzlich drei Möglichkeiten. Die erste wäre die Ein-/Ausgabe mittels der entsprechenden DOS- Funktionen wie Open(), Close(), Read() und Write(). Diese Methode ist die deutlich einfachere, da Sie bei der Programmierung keinen großen Aufwand treiben müssen. Der Nachteil dessen ist es allerdings, daß die aufgerufene Funktion erst vollständig erledigt wird, bevor Ihr Pro¬ gramm Weiterarbeiten kann. Diesen Nachteil hat die zweite Methode nicht. Das Zauberwort heißt hier Devices. Mit den Devices können Sie die gesamte Ein-/Ausgabe vollständig selbständig ablaufen lassen, während Ihr Programm wei¬ terarbeitet. Sie läuft dabei im Hintergrund, also parallel zu Ihrem Pro¬ gramm, und kostet daher kaum wertvolle Rechenzeit. Der Nachteil dieser Technik ist allerdings der wesentlich höhere Programmierauf¬ wand, der dafür benötigt wird. Die dritte Methode der Ein-/Ausgabe ist schließlich die direkte Pro¬ grammierung der Hardware des Amiga. Dies setzt jedoch sehr genaue Kenntnisse des Systems voraus und hat zudem den Nachteil, daß diese Methode im Multitasking-Betrieb zu großen Komplikationen führen kann. Im Hardwareteil dieses Buches finden Sie dazu mehr Informa¬ tionen. Beginnen wir die Betrachtung der Ein-/Ausgabe-Programmierung bei der Standard-Methode: der Verwendung der DOS-Funktionen. Wie bereits erwähnt sind die vier DOS-Funktionen Open(), Close(), Read() und Write() für die Ein-/Ausgabe von Daten zuständig. Mit ihnen können die meisten Funktionen ausgeführt werden, die ein Pro¬ gramm benötigt. Das AmigaDOS 549 Es stehen eine Reihe von Ein-/Ausgabekanälen zur Verfügung, die das DOS bereits mit Namen kennt. Diese Namen können dann in ei¬ nem Open-Befehl verwendet werden. Die Standard-Kanäle heißen: DFn: Bezeichnet das Diskettenlaufwerk mit der Nummer n. Die Nummer n kann dabei 0, 1, 2 oder 3 sein. SYS: Bezeichnet das Diskettenlaufwerk, von dem das System geladen wurde. RAM: Steht für die RAM-Disk, die stets verfügbar ist und ihre Größe den enthaltenen Daten anpaßt. Sie kann wie ein Diskettenlaufwerk ver¬ wendet werden, nur daß die Daten nicht auf Diskette, sondern im RAM-Speicher des Amiga abgelegt werden. NIL: Dieser Kanal ist ein wahres Datengrab: die hierein geschriebenen Da¬ ten werden verworfen und bewirken nichts. Dies ist manchmal recht brauchbar, wenn z.B. ein Programm Ausgaben machen will, die Sie jedoch nicht haben wollen. SER: Steht für die serielle Schnittstelle (RS232) und bietet die Ein- und Ausgabe von Daten über diesen Port. PAR: Bezeichnet die parallele Schnittstelle (Drucker-Port), die 8 Ein-/Aus- gabeleitungen enthält. Sie können hier also parallel anliegende Daten direkt einiesen oder ausgeben. PRT: Steht ebenfalls für die parallele Schnittstelle, nur daß mit diesem Ka¬ nal ein Drucker angesprochen werden kann. Ist der Drucker jedoch für die serielle Schnittstelle definiert, so wird diese hierdurch ange- 550 Amiga intern sprochen. Die Definitionen für den Drucker können Sie mit dem Pre¬ ferences-Programm vorgeben. CON: Gibt ein Fenster für die Ein-/Ausgaben vor. Dieses Fenster wird beim öffnen des Kanals automatisch geöffnet. Die Fensterparameter werden folgendermaßen angegeben: C0N:x/y/b/h/Name X und y stellen die Koordinaten der linken oberen Ecke des Fensters auf dem Screen dar, b und h die Breite und Höhe des Fensters in Bildschirmpunkten und Name den Fenstertitel, der in der Titelzeile erscheinen wird. So definiert CON:20/10/200/1OO/Test-Fenster ein Fenster mit Namen "Test-Fenster", das an der Position x=20 und y=10 beginnt, 200 Punkte breit und 100 Punkte hoch ist. RAW: Stellt ebenfalls ein Fenster dar und gibt die Ein- und Ausgaben an dieses Fenster weiter. Im Gegensatz zu CON: werden hier jedoch keine Funktionen vorbearbeitet (z.B. Editieren einer Zeile), so daß mit die¬ sem Fenster nur auf ganz besondere Art und Weise gearbeitet werden kann. * Steht schließlich für das aktuelle Fenster, was z.B. das CLI-Fenster darstellt. Auf der etwa Ende 1988 ausgelieferten Workbench 1.3 sind zusätzliche Händler im DEVS-Ordner enthalten. Setzt man diese in die MountList ein (ist als Beispiel auf der WB-Disk vorgegeben) und läßt den Bind- Drivers-Befehl laufen, so werden folgende Standard-Devices einge¬ bunden: Das AmigaDOS 551 NEWCON: Dieses Device entspricht eigentlich dem alten CON:. Der große Un¬ terschied zwischen diesem und NEWCON liegt darin, daß NEWCON das Editieren einer Zeile zuläßt. Man kann also in der Zeile mit den Cursortasten hin- und herlaufen und Zeichen löschen oder einfügen. Sie können dies ja einmal mit dem Kommando >copy neucon:10/10/400/100/NeuCon • ausprobieren. In dem entstandenen Fenster kann nun editiert werden, der Text wird bei Return in dem aktuellen CLI-Fenster ausgegeben. Der Clou ist allerdings erst die History-Funktion, d.h. mit den Cursor- Tasten hoch und runter können die zuvor eingegebenen Zeilen wie¬ derholt werden! Auf diese Weise arbeitet ja auch die AmigaShell der 1.3-Workbench. AUX: Stellt einen ungepufferten Seriell-Handler zur Verfügung. Der Vorteil liegt auf der Hand: Mit >neucli aux: kann ein an der seriellen Schnittstelle angeschlossenes Terminal zum zweiten Arbeitsplatz gemacht werden, da alle Textein- und Ausgaben dieser neuen CLI auf dem Terminal abgewickelt werden. Aus der Multitasking-Maschine Amiga wird somit auch eine Multiuser-Anlage! PIPE: Bietet eine sehr einfache Kommunikation zwischen zwei Prozessen. Ähnlich wie die RAM-Disk kann dieses Device zur temporären Spei¬ cherung von Daten verwendet werden. So kann man auf PIPE: etwas schreiben, wobei auch ein "Filename" mit angegeben werden kann. Ein anderer Prozeß kann dann einfach aus PIPE: lesen und hat die Daten des anderen Prozesses erhalten. Im Gegensatz zur RAM-Disk ist eine belegte Pipe nicht überschreib- bar. Wenn Sie versuchen, in eine Pipe ein zweites Mal zu schreiben, so funktioniert dies nur, wenn sie bereits wieder ausgelesen worden ist. Das Auslesen einer Pipe löscht nämlich ihren Inhalt. Liest man aus ei¬ ner noch unbelegten Pipe, so wartet der lesende Prozeß solange, bis die Pipe beschrieben wird. Dies ist leicht mit 2 CLI-Fenstern auszu¬ probieren. 552 Amiga intern SPEAK: Dieses Device ist eine alte Sache in neuer Verpackung. Es entspricht in etwa dem Say-Befehl, nur daß die Sprachausgabe hier als einfaches Device funktioniert. So können Sie sich z.B. die MountList durch den Befehl >copy devsiMountList speak: vorlesen lassen. Obwohl dies nicht gerade praktikabel ist... RAD: Dies ist eine neue RAM-Disk mit zwei gravierenden Unterschieden zur alten RAM:-Disk: Sie hat eine feste Länge und ist Reset-fest! Die Größe der RAD wird in der MountList festgelegt. Die vorgege¬ bene Größe ergibt sich durch die Disk-entlehnten Einstellungen; Surfaces = 2 BlocksPerTrack = 11 LouCyl = 0; HIghCyl = 21 Wollen Sie die Größe ändern, so brauchen Sie lediglich den HighCyl- Wert zu ändern. Dieser gibt die Anzahl der Tracks -1 (Beginn bei 0) an, die je ca. 11 KByte enthalten. FAST: Dieses Device bezieht sich auf eine Festplatte, welche mit dem Fast- Filing-System formatiert ist. Beginnen wir mit der wohl wichtigsten Anwendung; der Tastatur-Ein¬ gabe und Bildschirm-Ausgabe. 3.3.1 Tastatur und Bildschirm Wie Sie aus obiger Tabelle entnehmen können, stellt das AmigaDOS drei Möglichkeiten für die Bildschirmein-/-ausgabe zur Verfügung; CON;, RAW; und *, ab Workbench 1.3 kommt noch NEWCON; hinzu. In den folgenden Beispielen können Sie dann anstelle von CON; auch mal NEWCON; einsetzen. Das AmigaDOS 553 CON:-/NEWCON .--Fenster Um ein CON:-Fenster zu öffnen, wird die Open()-Funktion des DOS verwendet. Die Funktion erwartet als Parameter einen Zeiger auf den Namen des zu öffnenden Kanals und den Modus, unter dem dieser geöffnet werden soll. Als Modus kommen in Frage: Mode__new Für einen Kanal, ln den nur geschrie¬ ben werden kann. Mode_old Für einen Kanal, aus dem auch gele¬ sen werden kann. Mode__readwrite Ab der DOS-Version 1.2, bei der der Kanal sowohl beschrieben als auch ausgelesen werden kann. Zum öffnen eines CON;- oder auch RAW:-Fensters wird der Modus Mode_old verwendet, da der Kanal ja eigentlich bekannt ist und aus ihm auch gelesen werden kann. Um dies zu demonstrieren, hier ein kleines Maschinenprogramm, das ein CON:-Fenster öffnet, einen Text darin ausgibt, auf eine Eingabe wartet und dann das Fenster wieder schließt: .***** Einfache COtl:-Ein-/Ausgabe ***** OpenLib = -408 closelib = -414 ExecBase = 4 ; Ainiga-DOS-Offsets Open = -30 Close = -36 Read = -42 Urite =-48 Exit =-144 Hode_old = 1005 run: move.l execbase,a6 ;Zeiger auf EXEC-Bibliothek lea dosname,a1 moveq «0,d0 jsr openlib(a6} ;Open DOS-Library move.l dO,dosbase beq error ;Nicht geklappt move.l dosbase,a6 ;DOS-Basisadresse in A6 move.l tVname.dl ;Zeiger auf Namen move.l #tnode_old,d2 ;Modus jsr 0pen(a6) ;Fenster öffnen move.l dO.conhandle ;Handle retten 554 Amiga intern beq error nnve. l conhandle,dl ;Fenster-Handle in I nnve. l #text,d2 ;Text-Adresse in 02 move.l #tende-text,d3 ;Länge in 03 jsr Urite(a6} ;Text ausgeben nnve. l conhandle,dl .-Fenster-Handle move.l #buffer,d2 ;Puffer-Adresse nnve. l «80,d3 ;Hax. Länge jsr Read(a6) .-Eingabe erwarten move.l conhandle,d1 jsr Close(a6) ;Fenster schließen bra ende ;Fertig error: move.l #-1,d0 ;Error-Status ende: move.l d0,d1 move.l dosbase, a6 jsr Exit(a6) ,-Ende des Progranns rts ;Konint nicht vor dosname: dc.b 'dos.library',0 name: dc.b 'CON:20/10/200/100/** Test-Fenster',0 text: dc.b 'Bitte etwas eingebent'.O tende: buffer: blk.b 80 even dosbase: dc.l 0 conhandle: : dc.l 0 RAW:-Fenster Das oben aufgeführte Programm kann auch mit RAW: anstelle von CON: gestartet werden. Wenn Sie dies ausprobieren, so werden Sie den Unterschied sofort bemerken. Während die CON:-Version nämlich auf die Betätigung der Return-Taste nach der Eingabe wartet, kehrt das Programm mit dem RAW:-Fenster sofort nach der Betätigung irgend¬ einer Taste zurück. Dies gilt auch für die Funktions- oder Cursor-Ta¬ sten, die ja vom CON:-Fenster nicht erkannt werden. Ein CON:-Fenster bietet somit wesentlich mehr Komfort bei der Ein¬ gabe ganzer Texte, ein RAW;-Fenster dagegen macht die gesamte Ta¬ statur verfügbar. Aber nicht nur die normale Zeichendarstellung ist bei beiden Fen¬ sterarten möglich. Es gibt auch noch die Möglichkeit, andere Schriftarten darzustellen, wie z.B. unterstrichen oder fettgedruckt. Des Das AmigaDOS 555 weiteren lassen sich noch andere Funktionen auslösen, mit denen man das Fenster manipulieren kann. So läßt sich das Bild löschen, hoch- oder runterschieben usw. All diese Funktionen werden durch Steuerse¬ quenzen ausgelöst, die teilweise mit Parametern versehen in dem Fen¬ ster ausgegeben werden müssen. Hier nun eine Liste der Steuerzeichen, die eine Funktion auslösen. Diese Zeichen sind als Sedezimalzahlen auf geführt. Sequens Funktion 08 Backspac«. OA Linefeed, Cursor runter. OB Cursor eine Zeile hoch. OC Fenster löschen. OD Carriage Return, Cursor in die erste Spalte. OE Auf Normaldarstellung schalten (OF surücknehmen). OF Auf Sonderseichen schalten. IB Escape. Die folgenden Sequenzen beginnen mit dem Zeichen $9B, dem CSI (Control Sequence Introducer). Die auf dieses Zeichen folgenden Zei¬ chen bewirken dann eine Funktion. Die in eckigen Klammern angege¬ benen Werte können entfallen. Die Angabe für "n" ist in ASCII-Zei- chen als ein- oder mehrstellige dezimale Zahl anzugeben. Der Wert, der bei weggelassener Angabe für n angenommen wird, ist hier in Klammern hinter n angegeben. Sequenz Funktion 9B [n] 40 n Leerseichen einschieben. 9B [nj 41 Cursor um n (1) Zeilen hoch. 9B [nj 42 Cursor um n fl) Zeilen runter. 9B [nj 43 Cursor um n (1) Zeilen rechts. 9B jnj 44 Cursor um n (1) Zeilen links. 9B [nj 46 Cursor n (1) Zeilen runter in Spalte 1. 9B [nj 46 Cursor n (1) Zeilen hoch in Spalte 1. 9B [nj [SB n] 48 Cursor in Zeile; Spalte setsen. 9B 4A Fenster ab Cursor löschen. 9B 4B Zeile ab Cursor löschen. 9B 4C Zeile einfügen. 9B 4D Zeile löschen. 9B [nj 60 n Zeichen ab Cursor löschen. OB [nj 63 n Zeilen hochschieben. OB [nj 64 n Zeilen runterschieben. OB 32 SO 68 Linefeed => Linefeed+Retum. OB 32 SO 6C Ab sofort: Linefeed => nur Linefeed. OB 6E Sende die Cursor-PositionI Eine Zei¬ chenkette folgender Form wird au- rückgegeben: 9B (Zeile) SB (Spalte) 62 9B (Stil);(Vordergrundfarbe); (Hintergrundfarbe) 6D. 556 Amiga intern 9B (Länge) 74 9B (Breite) 75 9B (Abstand) 78 9B (Abstand) 79 9B 30 20 70 9B 20 70 9B 71 Die drei Parameter sind DesimalEah- len im ASCII-Format. Sie bedeuten: Stil: 0 = Normal 1 = Fett 3 = Italic (schräg) 4 = Unterstrichen 7 = Invers-Schrift Vordergrundfarbe: 30-37: Farben 0-7 für Text Hintergrundfarbe: 40-47: Farben 0-7 für Hintergrund Setst die maximale Anzahl der dar- Eustellenden Zeilen fest. Setst die maximale Zeilenlänge fest. Definiert den Abstand in Pixeln vom Unken Rand des Fensters, ab dem die Ausgabe beginnen soll. Definiert den Abstand in Pixeln vom oberen Rand des Fensters, ab dem ausgegeben werden soll Die leisten 4 Funktionen können durch Weglassen des Parameters auf die Normalstellung gebracht werden. Cursor unsichtbar machen. Cursor sichtbar machen. Sende Fenster-Ausmaße! Es wird eine Zeichenkette folgender Form surUck- gegeben; 9B 31 3B 31 3B (Zeilen) 3B (Spalten) 73 Um die Anwendung dieser Steuerzeichen zu demonstrieren, lassen Sie doch einmal folgenden Text in Ihrem Fenster ausgeben: text: dc.b $9b,''4;31;40m'‘ dc.b "Überschrift" dc.b $9b,"3;33;40m",$9b,»5;20H" dc.b "** Hallo, Welt! **",0 Die Parameter für die Steuersequenzen sind hier einfach, durch die Anführungszeichen markiert, als ASCII-Zeichenketten angegeben. Wie Sie sehen, können Sie so einfach recht wirkungsvolle Textausgaben realisieren! Ebenso, wie diese Sequenzen gesendet werden können, werden diese auch empfangen, wenn eine Funktionstaste der Tastatur bzw. eine Cursor-Taste gedrückt wurde. Die Zeichen, die man in diesem Fall empfängt, sind folgende ( steht für $9B): Das AmigaDOS 557 Taste Ohne Shift Mit Shift Fl 0- 10- F2 1- 11- F9 8- 18- FlO 9- 19- HELP ?- ?- hoch A T- runter B S- links C A- rechts D Auf diese Weise kann man also fast alles erfahren, was der Benutzer mit der Tastatur anfängt. Wenn das immer noch nicht ausreicht, so gibt es eine weitere Informationsquelle: die RAW-Input-Events. Dies sind Ereignisse, die auf Wunsch durch eine Sequenz gemeldet werden. Der Wunsch nach der Meldung dieser Ereignisse wird dem DOS wie¬ derum durch eine Sequenz mitgeteilt, die so aussieht: n{ Das "n" steht dabei für eine Zahl zwischen 1 und 16, die dem Ereignis entspricht, das gemeldet werden soll. Diese Ereignisse sind folgende: 1 Taste gedrückt. 2 Maustaste gedrückt. S Fenster wurde aktiviert. 4 Maus verschoben. 5 Unbenutet. 6 Timer. 7 Gadget angewählt. 8 Gadget losgelassen. 9 Requester eingeblendet. 10 Menü angewählt. 11 Fenster geschlossen (s. Console-Device). 12 Fenstergröße verändert. 13 Fenster erneuert. 14 Einstellungen verändert. 15 Diskette aus dem Laufwerk genommen. 16 Diskette eingelegt. Einige dieser Ereignisse (10, 11) sind in unserem Fall nicht verfügbar, da ein mit DOS geöffnetes Fenster ja weder über Menüs noch über das Schließsymbol verfügt. Diese Dinge werden aber dann wieder in¬ teressant, wenn man sein Consolen-Fenster selbst gestaltet hat. Dies wird allerdings nur über die Kombination Intuition und Devices mög¬ lich und wird daher später im Consol-Device-Kapitel extra behandelt. Wenn nun ein solchermaßen gewünschtes Ereignis eintritt (Diskette eingelegt o. ä.), so wird eine Sequenz folgenden Formats gesendet: 558 Amiga intern :;;;:: j Dabei bedeutet csi Klasse Unterklasse Taste Status Bit Maske 0 0001 1 0002 2 0004 3 0008 4 0010 5 0020 6 0040 7 0080 8 0100 9 0200 10 0400 11 0800 12 1000 13 2000 14 4000 15 8000 X und Y Sekunden Mikroeekunden Control-Sequence-Introducer $9B. Ereignis-Nummer (s.o.). Immer 0. Code letster Taste oder Maustaste. Tastaturstatus: Siehe Tabelle. Bedeutung ___ Linke Shift-Taste. Rechte Shift-Taste. C apsLock - Taste. Control. Linke Altemate-Taste. Rechte Altemate-Taste. Linke Amiga-Taste. Rechte Amiga-Taste. Ziffemblock. Tasten Wiederholung. Interrupt (unbenutst). Aktives Fenster. Linker Mausknopf. Rechter Mausknopf. Mittlerer Mausknopf (unbenutst). Relative Mauskoordinaten. Koordinaten des Mausseigers bei dem Maus-Ereignis. Systemseit bei Ereignis. Die Werte, die auf diese Weise erhalten werden, sind Dezimalzahlen im ASCII-Code. Wenn Sie diese Werte also im Programm auswerten wollen, so müssen Sie diese erst umwandeln. *-Fenster Die meisten CLI-Kommandos verwenden *, da dies die einfachste Art ist. Da dieses das aktuelle Fenster angibt, das natürlich schon geöffnet ist, kann dabei sogar das Öffnen und Schließen des Kanals entfallen. Da die Funktionen Read() und Write() dennoch ein Handle des Kanals benötigen, in den oder aus dem sie Daten lesen sollen, muß dies zuerst einmal ermittelt werden. Für diesen Zweck sind die DOS-Funktionen Input() und Output() vor¬ gesehen. Diese Funktionen benötigen keine Parameter und geben das Das AmigaDOS 559 Handle des entsprechenden Standard-Kanals zurück. Dies wird das CLI-Fenster sein, wenn das Programm einfach von dort aus aufgeru¬ fen wurde. Wurde beim Aufruf jedoch die Eingabe oder Ausgabe mit der <- bzw. >-Funktion des CLI umgeleitet, so wird das dadurch ak¬ tuelle Handle von der Input()- bzw Output()-Funktion ermittelt. 3.3.2 Disketten-Dateien Auf die gleiche Art wie CON:- oder RAW:-Fenster können auch Dis¬ ketten-Dateien geöffnet und bearbeitet werden, wobei nur einige Dinge dazukommen. So spielt der beim Öffnen übergebene Modus eine große Rolle: Wird "Modus alt" übergeben, so wird eine bestehende Datei auf der Diskette gesucht, aus der auch nur gelesen werden kann. Bei "Modus neu" wird die Datei neu angelegt, eine bereits unter die¬ sem Namen existierende Datei wird gelöscht. In die so geöffnete Datei kann nur geschrieben werden. Beim "Modus readwrite" kann eine be¬ reits bestehende Datei sowohl gelesen als auch beschrieben, also ver¬ ändert werden. Für die DOS-Funktionen Read(), Write() und Close() ändert sich ge¬ genüber der Bildschirmein/-ausgabe nichts. Es kommem aber einige Funktionen dazu, die bei der Bedienung von Diskettendateien sehr nützlich sind. Da man aus einer Datei Daten nach und nach auslesen kann, muß sich das System merken, an welcher Stelle innerhalb der Datei zuletzt zuge¬ griffen wurde. Dies wird durch einen Zeiger bewirkt, den man auch direkt verstellen kann. Dazu dient die Seek()-Funktion, mit der man den Zeiger beliebig innerhalb der Datei vorwärts und rückwärts ver¬ schieben kann. Dabei wird eine absolute Position angegeben, die ent¬ weder relativ zur aktuellen Position, zum Dateianfang oder zum Da¬ teiende zählt. Eine weitere Funktion des DOS erlaubt es, eine Datei von der Diskette zu löschen: die Delete()-Funktion. Mit ihr können auch Unter-Direc- tories gelöscht werden, allerdings nur, wenn diese leer sind. Die Namen der Dateien sind ebenfalls veränderbar, und zwar mit der RenameO-Funktion. Hier wird einfach der alte und der neue Da¬ teiname übergeben. Interessant bei dieser Funktion ist es, daß man nicht nur einfach den Namen einer Funktion, sondern auch ihre Posi¬ tion innerhalb der Diskette verändern kann. Wird nämlich im neuen 560 Amiga intern Namen ein anderer Suchpfad angegeben, so wird die Datei in dieses andere Unter-Directory "verschoben" (nicht kopiert). Dies funktioniert allerdings nur auf einer Diskette, die Angabe eines anderen Disketten¬ namens in neuen Namen führt zu einer Fehlermeldung. Eine Diskettendatei kann zusätzlich gegen verschiedene Funktionen geschützt werden. Dies wird durch die Maske bestimmt, die der Set- ProtectionO-Funktion übergeben wird. Die ersten 4 Bits dieser Maske (Bit 0-3) geben jeweils an, ob die Datei gegen folgende Aktionen geschützt ist: Bit _ Bedeutung, wenn gesetet _ 0 Datei nicht löschbar. 1 Nicht ausführbar. 2 Nicht überschreibbar. S Nicht lesbar. 3.3.3 Serielle Schnittstelle Die serielle Schnittstelle kann ebenso wie etwa Bildschirmein-/-ausga- ben behandelt werden. Es wird ein Kanal mit dem Namen SER: ge¬ öffnet und in diesen Kanal geschrieben bzw. aus ihm gelesen. Dabei können jedoch drei Probleme auftauchen: 1. Beim Aufruf der Read()-Funktion wartet der Amiga auf den Empfang von einem oder mehreren Zeichen von der seriellen Schnittstelle. Kommen jedoch dort keine an, so wartet der Amiga, bis er schwarz wird. Aus diesem Grund sollte man in ei¬ nem Programm, das Daten von dieser Schnittstelle holen will und nicht absolut sicher ist, daß von dort auch welche kommen, besser vor der Read()- die WaitForChar()-Funktion aufrufen. Mit dieser Funktion kann eine beliebige Zeit (anzugeben in Mi¬ krosekunden) auf den Empfang gewartet werden. Kommt in dieser Zeit nichts an, so wird eine Null zurückgegeben, und das Programm kann ggf. eine Fehlermeldung ausgeben und aufge- ben. Wenn doch etwas angekommen ist, so erhält man den Wert -1 und kann nun mit dem Lesen beginnen. 2. Es werden Daten empfangen, ohne daß bekannt ist, wie viele Daten kommen werden. Dabei kann das unter 1. beschriebene Problem auftreten. Hier liegt auch der Grund dafür, weshalb man nicht vom CLI aus mit z.B. COPY SER: TO * von der seri¬ ellen Schnittstelle ankommende Daten ansehen kann. Das CLI weiß ja nicht, wann die Daten anfangen oder aufhören, und Das AmigaDOS 561 streikt. Einen solchen Befehl kann man dann leider nur noch mit Reset beenden. 3. Ein Programm will über die Schnittstelle Daten senden oder empfangen, deren Einstellungen jedoch nicht stimmen. Man kann zwar dann mit dem Preferences-Programm diese Einstel¬ lungen vornehmen und neu starten, was jedoch sehr lästig ist. Ebenso wie das Preferences-Programm kann natürlich auch Ihr Programm diese Einstellungen selbst vornehmen. Dies ist jedoch nicht mit einem einfachen DOS-Kommando möglich, sondern muß über die I/O-Funktionen mit dem Serial-Device erledigt werden, deren Behandlung Sie im entspr. Kapitel finden. 3.3.4 Parallele Schnittstelle Die Programmierung der parallelen Schnittstelle (PAR:) ist normaler¬ weise nicht notwendig, da dort meist der Drucker angeschlossen ist. Dennoch ist diese Schnittstelle recht interessant, da man dort nicht nur Daten ausgeben, sondern auch einiesen kann. Die wohl einfachste Art der Programmierung gerade dieser Schnitt¬ stelle ist der direkte Weg über die Hardware-Register. Dies hat aller¬ dings den Nachteil, daß so eventuell Probleme beim Multitasking auf- treten können, wenn ein anderes Programm auch auf diese Schnitt¬ stelle zugreifen will. Aus diesem Grund ist es sicherer, über das DOS zuzugreifen. Dabei ist allerdings auch das Datenformat vorgeschrieben, und die Möglichkeit entfällt, einzelne Bits als Eingang und andere als Ausgang zu programmieren. 3.4 Programme Ein Programm, das von einem Linker oder direkt von einem As¬ sembler erstellt wurde, kann einfach durch die Eingabe seines Namens im CLI gestartet werden. Will man es von der Workbench aus starten, so muß man zusätzlich noch eine .INFO-Datei erstellen, die das Icon des Programms im Workbench-Fenster enthält. Dieses Icon kann dann angeklickt werden, und das Programm wird gestartet. 562 Amiga intern 3.4.1 Programmstart und Parameter Wie man bereits von den CLI-Kommandos her weiß, gibt es beim Amiga die Möglichkeit, in der das Programm aufrufenden Zeile einige Parameter mit anzugeben, die das Programm dann übernehmen und auswerten kann. Eine solche Zeile kann beim Start aus der Workbench natürlich nicht so übergeben werden. Aus diesem Grund gibt es einen deutlichen Unterschied in der Parameterübergabe an das Programm zwischen dem CLI und der Workbench. Das Programm, das aufgerufen wird, muß deshalb feststellen, von welcher Oberfläche aus es gestartet wurde, und dann eventuell seine Parameter auf die entsprechende Art und Weise abholen. Betrachten wir dafür zuerst einmal den einfacheren Fall, und zwar den Start eines Programms mittels des CLI. 3.4.1.1 Aufruf mK CLI Das Programm, das vom CLI aus gestartet wurde, bekommt in zwei Registern die nötigen Informationen über die Parameter, die ggf. dem Namen folgend eingegeben wurden. Im Adreßregister AO wird die Adresse der Zeile im Speicher übergeben, wo der dem Programmna¬ men folgende Text der im CLI eingegebenen Zeile liegt. Zusätzlich wird im Datenregister DO die Anzahl der Zeichen mitgeteilt, die hin¬ ter dem eigentlichen Programmnamen liegen. Mit diesen beiden Informationen kann nun das Programm leicht die Parameter auslesen und auswerten. Um dies einmal zu demonstrieren, folgt nun ein kleines Maschinenprogramm, das mit und ohne Parame¬ ter aufrufbar ist. Es handelt sich bei diesem Programm um ein CLI-Kommando, das Sie auch in den C-Ordner kopieren können. Es hat die Aufgabe, die Dar¬ stellungsart der auf den Aufruf folgenden Texte zu bestimmen. Sie können dies z.B. in der Startup.Sequence verwenden, wenn Sie eine Meldung etwa unterstrichen oder kursiv ausgeben wollen. Wenn Sie das Programm im C-Ordner unter dem Namen "Font" liegen haben, können Sie es mit dem Kommando >Font n Das AmigaDOS 563 aufrufen. Der Parameter n kann dabei auch weggelassen werden, das Programm schaltet dann wieder die normale Textdarstellung ein. Wenn Sie ihn dennoch angeben, so muß er eine Ziffer zwischen 0 und 7 sein. Die Wirkungen dieser Ziffern sind: 0 Normal-Darstellung. 1 Fettschrift. S Kursive (schräge) Schriftart. 4 Unterstrichen. 7 Inverse Schrift. Sie können auch fett und unterstrichen einstellen, wenn Sie Font 1 und Font 4 hintereinander aufrufen. Hier nun das Programm: .***** FONT-Befehl ***** ; EXEC-Offsets OpenLib = -30-378 ExecBase ° 4 ; AinigaOOS-Offsets Urite = -30-18 Output = -30-30 Exit = -30-114 run: subq #1,d0 ;Byte-Anzahl-1 beq normal ;Keine Parameter? search: cmp.b #$20,{a0)+ ;Argunient suchen bne found .■Gefunden dbra dO,search bra normal ;Normalen Font setzen found: move.b -{a0),text+1 ;5til setzen normal: move.l execbase,a6 ;Zeiger auf EXEC-Bibliothek lea dosname.al moveq #0,d0 jsr OpenLib(a6} ;Open DOS-Llbrary move.l dO,dos base beq error ;Nicht geklappt move.l dos base, a6 jsr 0utput(a6} ;Standard-Outputhandle holen 564 Amiga intern tnove. l d0,d1 ;Output-Handle in Dl tnove. l #text,d2 ;Text-Adresse in D2 tnove. l #tende-text,d3 ;Länge in D3 tnove. l dosbase,a6 jsr Urite(a6) ;Text ausgeben bra ende ;0K: Ende error: ende: nK>ve.l #1,d0 ;Error-Status tnove. l d0,d1 ;Rückgabe-Paraineter move. l dosbase,a6 jsr exit(a6) ;Ende des Prograitms rts ;Koinnt eigentlich nicht wieder dosbase: dc.l 0 dosname: dc.b 'dos.library',0 text: dc.b $9b,'O.-SI.-AOm' tende: even Wenn Sie nun ein C-Programm schreiben, das diese Parameterzeile benötigt, so können Sie dies auch leicht erreichen. Sie müssen nur die Datei "startup.o" als erstes Element in der Linker-Anweisung einsetzen (beim Lattice-Compiler, bei Verwendung des Aztec-Compilers ge¬ schieht dies automatisch durch Einbinden des c.lib-Files), was auch normalerweise immer getan wird. Die Parameterzeile finden Sie dann in der Variablen "argv", die Anzahl der Parameter in "arge". Das Startup-Programmteil erledigt noch mehr. Es öffnet auch schon die DOS-Bibliothek und stellt mit den DOS-Funktionen Input() und OutputO den Standard-Ein-und -Ausgabekanal fest. Die Handles die¬ ser Kanäle finden Sie dann in "stdin" und "stdout". Die Routine startet danach die main-Routine Ihres C-Programms. Eine weitere Information, die vom CLI an das Programm übergeben wird, ist die Größe des reservierten Stack-Bereiches. Dieser liegt hin¬ ter der Rücksprungadresse zum CLI auf dem Stack und kann z.B. mit dem Befehl HOVE.L 4(SP),D0 eingelesen werden. Auf diese Weise kann das Programm testen, ob es für seine speziellen Anforderungen genügend Platz auf dem Stack hat. Zusätzlich zu diesen Parametern werden noch einige andere vom CLI aus übergeben. Diese Parameter bieten eine große Vielfalt an Mög- Das AmigaDOS 565 lichkeiten, ein CLI-Programm zu vereinfachen. Näheres dazu finden Sie in dem Kapitel über den internen Aufbau des AmigaDOS. Dies ist also der Vorgang, ein vom CLI aus gestartetes Programm zu initialisieren. Betrachten wir nun den anderen Fall: den Start von der Workbench aus. 3.4.1.2 Start von der Workbench aus Wenn Sie das Icon eines Programms, das als Bild in einem Workbench- Fenster vorliegt, mit einem doppelten Klick starten, so wird das Pro¬ gramm unter dem angezeigten Namen gestartet. Diesem Programm werden dann ebenfalls Parameter übergeben, allerdings nicht in Form einer Textzeile, sondern als Message. Haben Sie dieses Programm in C geschrieben und ihm mit dem Linker das Startup-Programm vorangesetzt, so brauchen Sie sich um diese Message, die sogenannte Startup-Message, nicht zu kümmern. Das Startup-Programm erledigt selbständig folgende Arbeiten, wenn es festgestellt hat, daß es von der Workbench aus gestartet wurde: 1. Es öffnet zuerst einmal die DOS-Bibliothek. 2. Nun wird auf die Startup-Message gewartet (WaitPort). 3. Die Message wird abgeholt (GetMsg). 4. Die Anzahl der Argumente innerhalb der Message wird getestet. Ist sie 0, so wird der nächste Schritt(5) übersprungen. 5. Die Argumente, die übergeben wurden, werden als Lock-Struk- tur interpretiert, und mit ihr wird das dazugehörende Directory zum aktuellen Directory erklärt (CurrentDir). 6. Das Argument sm_ToolWindow wird überprüft. Ist es nicht 0, so wird das angegebene Fenster geöffnet und dessen Handle, wenn es sich öffnen ließ, zur Standard-Eingabe erklärt. Wie muß ein Programm aussehen, das nicht über dieses komfortable Startup-Programm verfügt, etwa ein Maschinenprogramm? Wenn Sie die Message, die die Workbench Ihrem Programm sendet, nicht benötigen, so müssen Sie dennoch diese Message abholen. An¬ dernfalls wird der Guru wieder einmal meditieren, da bei der nächsten I/O-Funktion, etwa öffnen eines Fensters, eine Meldung im Message- Port ankommen wird, die nicht zu dieser Funktion paßt. 566 Amiga intern Sie müssen also in Ihrem Programm die gleichen Funktionen ausführen wie das Startup-Programm. Zuerst rufen Sie die FindTask()-Funktion des EXEC auf, um einen Zeiger auf die Struktur des Prozesses, also Ihres Programms, zu bekommen. Als Argument übergeben Sie dafür eine Null in Al: execbase = 4 FitxtTask = -294 WaitPort = -384 GetMsg = -372 move.l execbase,a6 ;EXEC-Basisadresse in A6 suba.l a1,a1 ;Argijnent AI löschen jsr FindTask(s6) ;Zeiger holen In DO erhalten Sie den Zeiger auf Ihre Prozeß-Struktur. In dieser Struktur finden Sie nun die Information, ob dieser Prozeß aus dem CLI oder von der Workbench aus gestartet wurde: tnove.l d0,a4 ;Zeiger auf ProzeB in A4 tst.l $ac(a4) ;pr_CLl: CLI oder Workbench? bne fromCLI ;Es”war CLII Ist das getestete Argument Null, so wurde das Programm von der Workbench aus gestartet. Ist dies der Fall, muß als nächstes auf den Empfang der Startup-Mes- sage gewartet werden. Dies wird mit der WaitPort()-Funktion reali¬ siert: lea $Sc(a4),aO ;pr_MsgPort: HessagePort in AO Jsr WaitPort(a6} ;Auf Message warten Diese Funktion wartet auf den Empfang einer Message im Message- Port. In unserem Fall wird dies die Startup-Message der Workbench sein. Diese Message muß nun abgeholt werden, damit sie aus der Warteschlange der Messages entfernt wird. Dazu dient die GetMsgO- Funktion: lea $5c(a4),a0 ;RastPort-Adresse in AO Jsr GetHsg(a6) ;Hessage abholen Sie können diese Message nun bei Bedarf auswerten. In DO erhalten Sie aus der GetMsgO-Funktion einen Zeiger auf die Message-Struktur, die den Namen WBStartup trägt. Das AmigaDOS 567 Diese Message enthält folgende Elemente: Am Anfang liegt eine normale Message-Struktur. Darauf folgen die Elemente der eigentlichen Startup-Struktur: Offset Name )14 sm Process *18 8nri_ _Segment *1C 8nri_ _NumArg8 *20 8nri_ ToolWindow *24 sm _ArgLi8t Bedeutung Proseß-Descriptor. Programmsegment'Descriptor. Ansahl der Übergebenen Argumente. Beschreibung des eu Öffnenden Fen¬ sters. Zeiger auf die Argumente selbst. sm_Arglist Zeigt auf die Elemente der übergebenen Argumente. Diese Argumente beinhalten die Informationen über die zum Zeitpunkt des Programm¬ startes aktivierten Icons der Workbench. Einige Programme nutzen dies so, daß z.B. eine beim Aufruf des Programms durch Shift-Klick zu¬ sätzlich aktivierte Text-Datei vom Programm geladen und ausgegeben wird. Die Argumente der Liste, auf die sm_ArgList zeigt, bestehen aus den Zeigern; wa_Lock Datei-Lock (Directory-Beschreibung) wa_NaiTie Zeiger auf Dateinamen Um die Anwendung und Programmierung dieser Message-Auswertung zu demonstrieren, wollen wir nun ein Programm schreiben, das die Tool-Types ermittelt und ausgibt. Diese Tool-Types sind die Eintra¬ gungen, die im Workbench-Programm INFO in die jeweilige Datei geschrieben werden können. Dafür selektieren Sie eine Datei (einmal Klick) und wählen dann aus dem Workbench-Menü den Punkt Info an. Es öffnet sich dann ein Dialogfenster, in dem Sie in der Eingabe¬ maske TOOL TYPES durch Anklicken von Add Eintragungen machen können. Diese Eintragungen werden von einigen Programmen für Voreinstellungen verwendet (z.B. Notepad). Diese Daten werden in dem zum Programm gehörenden .info-File ab¬ gespeichert. In dieser Datei stehen außerdem noch die Daten für das Icon, seine Position im Fenster und vieles mehr. Um in einem Pro¬ gramm diese Daten zu bekommen, ist eine weitere Library auf der Workbench-Diskette im LIBS-Ordner enthalten: die Icon-Library. Diese Library enthält nun Funktionen zur Bearbeitung der .info-Files. Eine davon ist die Funktion GetDiskObject(), die die .info-Datei lädt 568 Amiga intern und einen Zeiger auf deren Struktur zurückgibt. Diese Funktion ver¬ wendet auch unser Programm. Bevor wir auf die Einzelheiten der Icon-Library und der DiskObject-Struktur eingehen, möchte ich Ihnen dieses Programmm vorstellen, da dies einiges anschaulicher machen kann: Uorkbench-Message- und .info-Auswertungsdemo S.D. ** Execbase = A ;EXEC-Basisadresse FindTask = -294 ;Task suchen UaitPort = -384 ;Auf Message warten GetMsg = -372 .-Message abholen OpenLib = -408 ;Library öffnen CloseLib = -414 ;Library schließen Open = -30 ;ICanal öffnen CI ose = -36 ;ICanal schließen Read = -42 ;Daten einiesen Uri te = -48 ;Daten ausgeben CurrentDir = -126 ;Aktuelles Directory setzen inode_old = 1005 ;Modus für’s Öffnen GetDiskObject = -78 ;DiskObject laden run: move.l execbase,a6 ;EXEC-Basisadresse suba.l a1 ,a1 jsr FindTask(a6) ;Eigenen Task suchen move.l d0,a4 ;Zeiger in A4 tst.l $ac(a4) ;pr CLI: CLI oder Uorkbench? bne fromCLl ;CLn Ende... lea $Sc(a4),a0 ;UBench-Message jsr UaitPort(a6) ;Abuarten jsr GetMsg(a6) .-Message abholen move.l dO,message ;Zeiger retten . **** Libraries und Fenster öffnen lea iconname.al ;"icon.library" clr. l dO jsr OpenLib(a6) ;ICON.library öffnen move.l dO,iconbase ;Basis retten beq ende3 ;Fehler aufgetreten! lea dosname,a1 ;"dos.library" clr. l dO jsr OpenLib(a6) ,-DOS öffnen move.l dO,dosbase beq ende2 ;Fehler aufgetreteni move. l d0,a6 move. l lliconname,d1 Das AmigaDOS 569 move.l ilfniode_old,d2 jsr 0pen(a6) ;COM-Fenster öffnen move.l dO,conbase beq en^l .-fehler sufgetreten! **** Das aktuelle Directory setzen, wenn nötig **** move.l message.aO move.l $24(a0).a0 beq ende ;Zeiger auf UBHessage ;sffl_ArgList: Zeiger auf Argumente ;Keine Argumente! move.l (aO).dl ;D1 => Lock move.l dosbase.a6 jsr CurrentDir(a6) ;Aktuelles Directory setzen **** Disk-Objekt (.info-Datei) laden **** move.l message.aO move.l $24(a0).a0 ;sm_ArgList-Zeiger move.l 4(a0).a0 ;ua_Name: Zeiger auf Mamen move.l iconbase.a6 jsr GetDisk0bject(a6) ;Disk-Objekt laden **** Tool-Type-Einträgc im Fenster ausgeben **** move.l d0.a1 move.l $36(a1).a1 move.l a1. typetext typesloop; move.l typetext.a1 move.l (a1)+.a0 cmp.l #0,a0 beq nomore move.l a1.typetext move.l a0.d2 move.l aO.dS lenlop: tst.b (a0)+ bne lenlop sub.l a0.d3 not.l d3 move.l dosbase.a6 move.l conbase.dl jsr Urite(a6) move.l conbase.dl move.l «lf.d2 move.l #1.d3 jsr Urite(a6) bra typesloop ;Zeiger auf DiskObject-Struktur ;do_ToolTypes: Zeiger a. ToolType-Array .•Textzeiger retten ;Textzeiger laden ;Zeiger auf Text in AD ;Text vorhanden? ;Nein: Ende der Ausgaben .-Sonst Zeiger retten ;= Textadresse für Ausgabe ;Nun noch die Länge ermitteln ;Ende suchen .-Länge des Textes errechnen ;und korrigieren ;Text im Fenster ausgeben ;Linefeed: ;Nächste Zeile ;Auf zun nächsten Eintrag! **** Das waren alle, nun auf Taste warten **** 570 Amiga intern noinore: move.l conbase,d1 move.l #1,d3 move.l #txjffer,d2 jsr Read(s6> ;Ein Zeichen ;in Buffer ;einlesen (auf Return warten) **** Programn-Ende: Alles schlieBen und zurück **** ende: move.l conbase.dl move.l dosbase,a6 jsr Close(a6) move.l execbase,a6 move.l dosbase,a1 Jsr Closel{b(a6) endeZ: move.l iconbase,a1 jsr Closelib(a6) fromCLI: ende3: rts . **** Datenfelder **** dosbase: blk.l 1 conbase: blk.l 1 iconbase: blk.l 1 message: blk.l 1 typetext: blk.l 1 ;Fenster schlieBen ende1: ;DOS schlieBen ;ICON.library schlieBen ;Programm-Ende ;DOS'Basisadresse ;Fenster-Basis ;icon.library'Basis ;Zeiger auf UBHessage ;Text-Zeiger dosname: dc.b 'dos.library',0 icomame: dc.b 'icon.library',0 conname: dc.b 'CON;10/20/300/100/** Hessage-Ausgabe',0 If: dc.b $a buffer: blk.b 2 Dieses Programm funktioniert nur, wenn es von der Workbench aus gestartet wurde. Andernfalls wird einfach abgebrochen (fromCLI). Um es starten zu können, muß noch ein Icon für dieses Programm erstellt werden. Dies können Sie mit dem Icon-Editor auf einfache Weise er¬ ledigen. Danach muß es unter dem gleichen Namen wie obiges Pro¬ gramm abgespeichert werden, der Anhang .info wird automatisch er¬ stellt. Ist dies geschehen, so können Sie nun das Icon anklicken und im Workbench-Menü den Punkt Info wählen. In dem so erscheinenden Fenster können Sie nun einen oder mehrere Einträge in TCX)L TYPES eingeben und mit SAVE abspeichern. Wenn Sie dann Ihr Icon mit einem Doppelklick aktivieren, so wird das dazugehörige Programm geladen und gestartet. Das Programm führt Das AmigaDOS 571 dann die nötigen Schritte aus, um sowohl die Workbench-Startup- Message (WBStartup) als auch die DiskObject-Struktur zu erhalten und auszuwerten. Die Struktur des Disk-Objektes bzw. der .info-Datei ist folgender¬ maßen aufgebaut: Offset Name Inhalt 0 do__Magic Eine "magische" Zahl, die diese Datei $2 do_Ver«ion als gültig erklärt f$E310). Versionsnummer (1). $4 do_Gadget Hier beginnt eine Gadget-Struktur, welche das Aussehen und die Position des Icons bestimmt. $30 do_Type Objekt-Typ (Tool, Projekt etc.). $32 $36 do_D efau 11 Tool Standard-Programm der Diskette/des Programms. do_TooITyp«fl Zeiger auf Text-Feld der Typen. $3A do_CuiT«ntX $3E do_CurrentY Icon-Position im Fenster. $42 do^D r a w erD at a Zeiger auf die Unterdirectory-Fen¬ ster-Struktur. $46 do_TooIWmdow Standard-Fenster für Tools. $4A do StackSice Stack-Größe für Tools. Der Zeiger do_ToolTypes zeigt auf eine Zeigerliste, deren Einträge wiederum auf die Texte der im Info-Fenster eingetragenen Tool-Ty- pes zeigen und mit einer Null enden. Diese Zeiger werden im Pro¬ gramm verwendet, um die Texte ausgeben zu können. Aus dem Beispielprogramm können Sie also leicht entnehmen, wie Sie an die Tool-Types Ihres Programms herankommen. Dort können grundsätzliche Eintragungen vorgenommen werden, die die Funktion des Programms steuern sollen. Dies ist auch bei dem Notepad-Pro¬ gramm der Workbench realisiert, bei dem über die Tool-Types Para¬ meter wie die Größe des Eingabefensters oder die zu verwendende Schrift bestimmt werden. Die Eintragungen dort sind üblicherweise immer in der Form NAME= [ [ ] eingetragen. Dies wird auch bei Notepad verlangt. Der Vorteil dieser Eintragsform ist einfach der, daß in der Icon-Library zwei Funktionen vorgesehen sind, die diese Zeilen überprüfen können. Die erste Funktion, FindToolType() mit dem Offset -96, sucht die Einträge der Tool-Types nach einem bestimmten Namen durch. Im 572 Amiga intern Beispiel Notepad wird nach einer Zeile mit dem Namen WINDOW gesucht. Es wird dann ein Zeiger auf die dem Gleichheitszeichen fol¬ genden Parameter zurückgegeben oder eine Null, wenn keine Zeile mit diesem Namen vorkommt. Dieser Zeiger wird dann der anderen Funktion, MatchToolType() mit dem Offset -102, zusammen mit einem weiteren Zeiger auf einen Vergleichsparameter übergeben. Der daraus resultierende Wert zeigt dann an, ob der Vergleichsparameter in der Zeile vorkommt oder nicht. Dies wird z.B. dann benutzt, wenn ein Programm Dateien lesen kann, aber nur bestimmte Arten von Dateien lesen soll. Werden diese Arten in Tool-Types eingetragen, können sie nachher vom Programm mit dem zu ladenden Dateityp verglichen werden, um festzustellen, ob es diese Datei laden darf. 3.4.2 Programm-Datei-Strukturen Betrachten wir den internen Aufbau von Amiga-Programmdateien: 3.4.2.1 Programmsegmente Ein Programm ist beim Amiga in drei logische Segmente unterteilt, Code, Data und Bss. Das Code-Segment enthält die Programmbefehle, also das eigentliche Programm. Im Data-Segment sind dann die Daten enthalten, die das Programm braucht und die einen definierten Inhalt haben müssen. Diese Daten sind oft im Code-Segment direkt enthal¬ ten, so daß das Data-Segment entfällt. Dies ist eigentlich nicht weiter problematisch. Die Unterteilung in Code und Data hat allerdings den Vorteil, daß der Loader diese beiden Segmente in verschiedene Spei¬ cherplätze packen kann, wo immer Platz ist. Dies ist bei einem zu¬ sammenhängenden Code-Segment nicht ganz so einfach, wenn kein genügend großer zusammenhängender Speicherbereich frei ist. Das dritte Segment ist das Bss-Segment. In ihm werden lediglich Da¬ tenbereiche definiert, deren Anfangszustand bzw. -inhalt unwichtig ist. Der Loader reserviert dann für das Programm irgendwo einen Speicherbereich, dessen erforderliche Länge als Bss-Bereich im Pro¬ gramm vermerkt ist (hunk_bss, siehe unten). Das AmigaDOS 573 3.4.2.2 Programmaufbau (Hunks) Eigentlich ist ein Programm nichts anderes als eine Reihe von binären Datenworten, die ein Maschinenprogramm bilden. Bei den "guten, al¬ ten 8-Bittern" war daher die Abspeicherung eines solchen Programms kein Problem: Man mußte nur das Programm aus dem Speicher auf die Diskette schreiben und fertig. Eine Maschine wie der Amiga wirft dabei allerdings schon eine Reihe von Problemen auf, die diese Methode unbrauchbar machen. Das erste Problem wäre die Aufteilung des Speichers. Ist der Speicher, in dem das Programm zum ersten Mal gelaufen ist, beim nächsten Aufruf bereits belegt, so wäre das einfache "Reinladen" des Programms nicht mehr so ohne weiteres möglich. Das Programm muß an eine andere Stelle des Speichers geladen werden, was für ein normales Maschinen¬ programm, das absolute Adressen verwendet, bedeutet, daß es nicht mehr läuft. Dieses Problem wird beim Amiga dadurch gelöst, daß ein Programm auf der Diskette so abgespeichert ist, daß alle absoluten Adressen im Programm für die Startadresse $00000 ausgelegt sind. Wird das Pro¬ gramm nun z.B. ab $20000 geladen, so müssen vor dem Start alle diese falschen Adressen korrigiert, d.h. um $20000 erhöht werden. Damit das DOS, das dies erledigt, die zu ändernden Adressen im Programm finden kann, ist zusätzlich zum eigentlichen Programm eine Tabelle mit abgespeichert. In dieser Tabelle stehen die Offsets, die jeweils auf ein zu änderndes Langwort zeigen. Dies macht deutlich, daß ein einziger Abschnitt nicht ausreicht, ein Programm auf der Diskette abzuspeichern. Bisher sind es schon zwei Abschnitte, die wir kennen: das Programm selbst und die Tabelle, die Relocation-Tabelle genannt wird. Es gibt aber noch eine Reihe weite¬ rer solcher Abschnitte, die der Amiga verwendet. Ein aus solchen Teilen zusammengesetztes Programmteil wird Hunk genannt. Ein oder mehrere Hunks ergeben zusammen eine Programm-Einheit (program unit), wovon eine oder mehrere ein Object-File bilden. Aus einem oder mehreren solcher Object-Files setzt sich schließlich ein Load-File zusammen, was ein lauffähiges Programm darstellt. Der Unterschied zwischen diesen beiden File-Typen ist der, daß ein Object-File ein noch nicht lauffähiges Programm beinhaltet, das z.B. von einem Compiler oder Assembler erstellt wurde. Will man ein oder mehrere dieser Files zu einem lauffähigen Programm machen, so muß 574 Amiga intern man dafür den Linker aufrufen. Dies ist ein Programm, das Object- Files zu einem einzigen Programm zusammenbindet (engl, to link: ver¬ binden), das dann als Load-File abgespeichert wird. Das Ergebnis kann dann einfach durch die Eingabe seines Namens im CLI gestartet werden. Der Vorteil dieses "Umweges“ ist, daß so mehrere Teilprogramme, welche sich ggf. gegenseitig aufrufen, einzeln erstellt und übersetzt werden können. Das Hauptprogramm, das z.B. in C geschrieben ist und die main-Routine enthält, kann dann die in den anderen Dateien verteilten Funktionen oder Unterprogramme einfach aufrufen. Die Auslagerung der Funktionen aus dem Hauptprogramm verbessert somit die Übersichtlichkeit des Programms, da es wesentlich kürzer als das gesamte Programm sein kann. Der Grund dafür, daß die einzelnen Programme nicht alleine laufen können, wird somit klar. Es werden ja Programmteile aufgerufen, die überhaupt nicht in diesem Programm enthalten sind! Erst nach dem Durchlauf des Linkers sind alle Funktionen und Unterprogramme in einem einzigen Programm-File enthalten. Doch beginnen wir mit den kleinsten Abschnitten, aus denen sich die Hunks zusammensetzen. Einige dieser Programm-Datei-Teile kommen nur in Object-Files vor, einige nur in Load-Files. Sie beginnen jeweils mit einem bestimmten Langwort, das in der folgenden Tabelle in Klammern sedezimal mit angegeben wird. Hier eine Übersicht über alle möglichen Hunk-Teile: hunk_unit (S3E7) Mit diesem Teil beginnt eine Programm-Einheit in Object-Files. Nach der Kennung $3E7 folgt die Länge des Namens dieser Einheit und da¬ nach der Name selbst, der an einer Langwort-Grenze enden muß. hunk_name ($3E8) Hier liegt der Hunk-Name: Nach der Kennung $3E8 folgt die Na¬ menslänge und der Name selbst, der an einer Langwortgrenze enden muß. Das AmigaDOS 575 hunk_code (S3E9) Dieser Teil enthält einen Programmteil, der nach der Korrektur der absoluten Adressen laufen kann. Auch hier kommt nach der Kennung $3E9 die Anzahl der Langworte des Programms, und dann folgen die Langworte selbst. hunk_data ($3EA) Auch hier beginnt ein Programmteil, allerdings nur Daten des Pro¬ gramms, die einen definierten Inhalt haben müssen (data). Einige die¬ ser Daten können auch eine Adreß-Korrektur erfordern. Der Kennung folgt die Anzahl der Daten und die Daten selbst. hunkjbss (S3EB) Die Daten dieses Teiles gehören zwar auch zum Programm selbst, ha¬ ben aber keinen definierten Inhalt. Aus diesem Grund ist hier auch nach der Kennung nur die Anzahl der benötigten Langworte, nicht jedoch die Daten selbst aufgeführt (bss = block storage segment). hunk_reloc32 (S3EC) Dieser Block enthält die Offsets, die auf die zu korrigierenden Adreß- Langworte innerhalb des Programms zeigen. Diese Offsets sind für das gesamte Programm gültig. Die Aufteilung dieses Blocks ist folgende: Nach der Kennung $3EC folgt die Anzahl der Offsets, die in der er¬ sten Tabelle enthalten sind. Das nächste Langwort bezeichnet die Nummer des Hunks, auf den sich diese Offsets beziehen, danach fol¬ gen die Offsets selbst. Das darauffolgende Langwort ist wieder eine Anzahl, danach folgt die Hunk-Nummer dieser Tabelle usw., bis als Anzahl eine Null auftritt und somit diesen Hunk-Teil beendet. Das Ganze noch einmal in übersichtlicher Form: $3EC (hunk_reloc32) Anzahl der Offsets Hunk-Nunner Offsets... Anzahl der Offsets (oder 0: Ende) Hunk-Nunner Offsets... 576 Amiga intern 0: Ende von hunk_reloc32 Auf diese Weise kann diese Tabelle alle Hunks abdecken, aus denen das Programm, das schließlich im Speicher liegt und ablaufen soll, zusammengebaut ist. hunk_relocl6 ($3ED) Diese Tabelle ist ebenso wie hunk_reloc32 aufgebaut, nur daß diese Offsets sich auf 16-Bit-Adressen beziehen. Solche Adressen tauchen bei PC-relativen Adressierungen auf. hunk_reloc8 (S3EE) Auch diese Tabelle hat das gleiche Format wie hunk_reloc32. Die hier enthaltenen Offsets werden bei 8-Bit-Adressen verwendet, die eben¬ falls bei PC-relativen Aressierungen auf treten. hunk_ext ($3EF) In diesem Block sind die Namen von externen Referenzen eingetragen. Solche Referenzen treten nur in Object-Files auf. Es handelt sich da¬ bei um Adressen von Funktionen oder Unterroutinen, die dem Pro¬ grammteil nicht bekannt sind und vom Linker eingesetzt werden müssen. Der Kennung folgen mehrere sogenannte "symbol data units", die durch ein Null-Wort abgeschlossen werden. Diese Symbol-Definitionen haben folgenden Aufbau: 1 Byte: Symbol-Typ. Hierfür gibt es folgende Möglichkeiten: Wert _ Name _ 0 ext_»ymb 1 ext_def 2 ext_abs 3 ext_re8 129 ext_ref32 130 ext_common 131 ext_refl6 132 ext ref8 Symbol-Typ _ Symbol-Tabelle für Fehlersuche. Zu korrigierende Definition. Absolute Definition. Besug auf residente Bibliothek. 32-Bit-Korrektur. Allgemeine 32-Bit-Korrektur. 16-Bit-Korrektur. 8-Bit-Korrektur. Das AmigaDOS 577 3-Byte-Wert für die Namenslänge (in Langworten). Symbolname. Symbolwert und ggf. weitere Daten. Die nach dem Namen folgenden Daten haben in diesen Hunks dreier¬ lei Aufbau, abhängig vom Symbol-Typ. Bei den Typen _def, _abs und_res folgt dem letzten Langwort des Namens lediglich der abso¬ lute Wert des Symbols als Langwort. Dem Namen der drei _ref-Typen folgt hingegen ein Langwort, das die Anzahl der darauf folgenden Referenzwerte enthält. Das gleiche gilt auch für ext_common, nur daß hier zwischen dem Namen und diesem Zähler noch die Größe des Common-Blocks liegt. hunk_symbol (S3F0) In diesem Block liegen ebenfalls Symbole mit ihrem Namen und Wert. Diese Symbole sind jedoch nicht für den Linker interessant, sondern für einen Debugger, ein Programm zur Fehlersuche in Programmen. In diesen Programmen kann dann eine Routine getestet werden, deren Adresse nicht als Zahl, sondern als Name verfügbar wird, ebenso wie die Speicherplätze von Variablen. Der Kennung $3F0 folgen wieder symbol-data-units, abgeschlossen von einer Null. hunk_debug (S3FI) Der Aufbau dieses Blocks ist nicht vollständig vorgeschrieben. Es kön¬ nen hier Informationen über das Programm eingetragen werden, über die das Programm zur Fehlersuche verfügen soll. Der Block muß nur mit der Kennung $3F1 beginnen, und die Anzahl der anschließenden Langworte muß folgen. hunk_end (S3F2) Dies ist der einzige unbedingt nötige Block innerhalb des Hunks. Er besteht nur aus der Kennung, die somit auch das letzte Langwort eines Programms bzw. einer Objekt-Datei auf der Diskette ist. hunk_header ($3F3) Mit diesem Block beginnt ein Load-File. Hier wird angegeben, aus wie vielen Hunks das zu ladende Programm besteht und wie groß 578 Amiga intern diese jeweils sind. Außerdem enthält dieser Block die Namen der resi¬ denten Bibliotheken, die beim Laden dieses Programms mitgeladen werden müssen. Der Aufbau ist folgender: hunk_header (S3F3). Länge des Namens des ersten Hunks (in Languorten). Hunk-Hame. Länge des Namens des zweiten Hunks (oder 0: Ende). Hunk-Name. 0: Ende der Namensliste. Höchste Hunk-Nunmer+I. Nunner vom zuerst zu ladenden Hunk. Nunmer vom zuletzt zu ladenden Hunk: Tabellenlänge. Längen der Hunks. Hier beginnen die Hunks dieses Prograanms. hmk_overlay (S3F5) Dieser Block wird benötigt, wenn mit Overlay gearbeitet werden soll. Dies bedeutet, daß in einen bereits vom Programm belegten Speicher¬ bereich ein anderes Programm- bzw. Datensegment geladen werden soll. Die Tabelle nach der Kennung $3F5 enthält die Angaben über die Tabellengröße, die höchste Ebene der Überlagerungen (die Anzahl der Überschreibvorgänge) und die nachzuladenden Daten selbst. htmk_break ($3F6) Mit dieser alleinstehenden Kennung wird das Ende eines Overlay-Pro¬ grammteils gekennzeichnet. Um diese Aufteilung von Programmen etwas zu entwirren und zu de¬ monstrieren, hier ein kleines Beispiel. Im CLI-Kapitel habe ich Ihnen ein kleines Maschinen-Programm vorgestellt, das den CLI-Befehl FONT darstellt. Wie dieses Programm (vom K-SEKA-Assembler) auf die Diskette gebracht wird, können Sie sich leicht ansehen, indem Sie mit dem TYPE-Kommando des CLI den Inhalt der Programmdatei "Font" ausgeben lassen. Das können Sie mit dem Kommando >type Font opt h Das AmigaDOS 579 auf dem Bildschirm oder mit >type Font to PRT: opt h auf dem Drucker bewirken. Sie erhalten dann folgenden Ausdruck: 0000 000003F3 00000000 00000002 00000000 0010 00000001 00000024 00000001 / 000003E9 0020 00000024 / 53406700 00180C18 00206600 0030 000A51C8 FFF66000 000813E0 0000007F 0040 2C790000 000443F9 00000072 70004EAE 0050 FE6823C0 00000088 67000028 2C790000 0060 00884EAE FFC42200 243COOOO 007E263C 0070 00000009 2C790000 00884EAE FFD06000 0080 0008203C FFFFFFFF 22002C79 00000088 0090 4EAEFF70 4E75646F 732E6C69 62726172 OOAO 79009B30 3B33313B 34306000 00000000 OOBO 3B4F706S / 000003EC 00000007 00000000 OOCO 00000018 00000024 00000030 0000003A 0000 00000046 00000052 00000068 00000000 / OOEO 000003F2 / 000003EB 00000001 / 000003F2 Die Schrägstriche erhalten Sie nicht bei der Ausgabe, diese sollen nur jetzt die einzelnen Abschnitte bzw. Hunk-Teile trennen. Sehen wir uns diese Teile einmal an: Zu Beginn steht die Kennung $3F3, hunk_header. Die darauffolgende $0 bedeutet, daß keine Hunk-Namen vorliegen. Danach gibt die $2 an, daß dieses Programmfile aus nur zwei Hunks besteht (wie gesagt, nur ein einfaches kleines Beispiel). Das erste zu ladende Hunk ist die Nummer $0, das letzte die Nummer $1. Die Größen dieser beiden Hunks sind $24 und $1. Mit der Kennung $3E9 (hunk_code) beginnt nun der Teil, in dem die Programmdaten selbst stehen. Die Länge wird mit $24 angegeben. Da¬ nach folgen $24 (36) Langworte des Programmkodes. Danach beginnt mit der Kennung $3EC (hunk_reloc32) der Bereich mit den Offsets für die Korrektur der Adressen. Die Anzahl wird mit $7 angegeben, und diese Offsets beziehen sich auf Hunk-Nummer $0. Nun folgen die 7 Offsets selbst. Der erste dieser Offsets, $18, deutet auf den Wert $0000007F, das $18. Wort des Codes. Zu diesem Lang¬ wort wird also nach dem Laden des Programms die Anfangsadresse addiert, so daß dort die effektive Speicheradresse des addressierten Bytes hinkommt. Ebenso wird mit den anderen Offsets der reloc32- Liste verfahren. 580 Amiga intern Der Liste folgt nun eine $0, was das Ende der hunk_reloc32-Liste bedeutet. Die darauffolgende Kennung $3F2 (hunk_end) gibt nun das Ende des ersten Hunks an. Es folgt nun also das zweite, dessen Länge ja eins war. Nun folgt die Kennung $3F2 (hunk_bss), der die Zahl $1 folgt. Dies bedeutet, daß ein Langwort zusätzlich reserviert werden muß, in das das Programm ja die DOS-Basisadresse legen will. Den Abschluß bil¬ det nun noch die Kennung $3F2 (hunk_end), was das Ende des zwei¬ ten Hunks und auch des gesamten Programms darstellt. Um diese Aufteilung eines Programms oder einer Objekt-Datei analy¬ sieren zu können, müßte man also immer den oben vorgeschlagenen Hex-Ausdruck anfertigen und die einzelnen Hunks bzw. deren Header suchen. Um einerseits dies zu automatisieren und andrerseits die Aus¬ wertung der verschiedenen Hunks zu demonstrieren, folgt nun ein kleines Programm, das eine Datei durchsucht und die darin enthalte¬ nen Hunks in einem Fenster darstellt. Aufgerufen wird dieses Pro¬ gramm vom CLI aus mit Angabe eines Dateinamens. Wenn Sie also das Programm HUNKS nennen, so können Sie die oben über das Font- Programm erhaltenen Kenntnisse verifizieren, indem Sie eingeben: >Hunks c:Font Das Hunks-Programm wird daraufhin ein Fenster öffnen, den überge¬ benen Dateinamen ausgeben und die angegebene Datei öffnen. Danach werden die Namen der gefundenen Hunks untereinander ausgegeben und mit der Aufforderung "Bitte Return drücken" abgeschlossen. Na¬ türlich können Sie auch mit Hilfe der DOS-Funktion OUTPUT die Ausgabe auf das Standard-Ausgabegerät leiten, dessen Handle diese Funktion ja ergibt. In dem Fall können Sie dann auch die Ausgaben dieses Programms z.B. auf den Drucker leiten. Sollten in der Datei hunk_ext-Hunks auftreten, so sind dies meist eine ganze Reihe. Um zu verhindern, daß dabei durch die Ausgabe von -zig "hunk_ext" der Rest untergeht bzw. aus dem Bild scrollt, werden bei Wiederholungen nur Punkte neben der Angabe des _ext- Typen ausgegeben. Hier nun das Programm, in dem Sie auch die Aufteilung der einzelnen Hunks deutlich erkennen können: Das AmigaDOS 581 f Datei-Hunks-Analysator 5/88 S.D. ***** OpenLib =-408 closelib 1 =-414 ExecBase =4 Open =-30 CI ose =-36 Read =-42 Urite =-48 Seek =-66 mode_old =1005 <0 II run; clr.b -KaO.dO) subq #1,dO ;Anzahl Bytes-1 beq ret ;Kein Argument da! nnve dO,length search: cmp.b «S20,(a0)+ ;Argument (FM) suchen bne found » dbra dO,search bra ret ;Doch kein Argument 1 found: subq.l #1,a0 move.l aO,fname ;FM-Adresse retten move.l sp,spsave ;SP retten clr ext_nr move.l execbase,a6 ;Zeiger auf EXEC-Bibliothek lea dosname(pc),a1 moveq «0,d0 jsr openlib(a6) ;Open DOS-Library move.l dO,dosbase beq error move.l #consolname,d1 ;Console-Definition move. l #mode_old,d2 move. l dosbase,a6 jsr open(a6) ;Console öffnen beq error move.l dO.conhandle move. l fname,dl ;File-Name move.l #mode_old,d2 jsr open(a6) ;File öffnen beq error move.l dO,fhandle move. l fname,d2 move length,d3 move.l d2,a0 move.b #lf,0(a0,d3) move.b #lfj(a0,d3) 582 Amiga intern addq #2,d3 move. l conhandle.dl jsr Urite(a6) ;File-Namen ausgeben loop: bsr read Iw ;Langwort einiesen move.l (aD.dO ;Languort in DO: Hunk-Kennung sub.l «%Ze7,-Signale einem Task senden. 25 0004 FF4A42 -Signale auswerten, löschen. 26 0098 FF4BB4 Alert dantellen. 27 OOOC FF4B60 BPTR auf Root-Node holen. 28 OOAO FF82CC Vom CIS lesen. 29 00A4 FF49C8 Warte auf Message für diesen Task. 2A 00A8 FF4080 Packet an ein Device senden. 2B OOAC FF8314 In COS schreiben. 2C OOBO FF52C4 Langwort-Feld in BSTR umwandeln. 2D 00B4 FF62AC BSTR in Langwort-Feld umwandeln. 2E 00B8 FF67FC Programm ausladen + Task beenden. 2F OOBC FF6888 AmigaDOS Delay(] 30 OOCO FF58D8 Packet senden -t- auf Antwort warten. 31 00C4 FF508C Auf Packet antworten. 32 00C8 FF51BE Intuition Window öffnen. 33 OOCC FF4B04 Process.CurrentDir-Feld holen/Update. 34 OODO FF6068 Requester darstellen. 36 00D4 FF6804 Padded BSTR auf COS ausgeben. 36 00D8 FF59B8 Zeichen vom CIS holen. 37 OODC FF6A14 Zeichen vom CIS entfernen. 38 OOEO FF5A84 Zeichen auf COS ausgeben. 30 00E4 FF82E4 Unbekannte Funktion 3A 00E8 FF832C Unbekannte Funktion 3B OOEC FF5C30 Input-File öffnen. 3C OOFO FF6C40 Output-File öffnen. 3D 00F4 FF4B18 Input-Handle setsen. 3E 00F8 FF4B20 Output-Handle setsen. 3F OOFC FF660C CIS schlieBen. 40 0100 FF6620 COS schlieBen. 41 0104 FF4B28 AmigaDOS-Input(] 42 0108 FF4B30 AmigaDOS-Output() 43 OlOC FF6580 Desimalsahl vom CIS einiesen. 44 0110 FF6664 Linefeed auf COS ausgeben. 46 0114 FF6660 Desimalsahl auf COS ausgeben (DO unklar). 46 0118 FF6720 Desimalsahl auf COS ausgeben. 47 OllC FF672C Hexsahl auf COS ausgeben. 48 0120 FF67A0 Octalsahl auf COS ausgeben. 49 0124 FF67C8 BSTR auf COS ausgeben. 4A 0128 FF6890 Format-BSTR mit Param. auf COS. 4B 012C FF6A30 Klein- in GroBbuchst. umwandeln. 4C 0130 FF6A60 Zwei Zeichen vergleichen. 4D 0134 FF6A74 Zwei BSTRs vergleichen. 4E 0138 FF6B1C Programm-Arg. mit Template lesen. 4F 013C FF6E68 Ein Wort vom CIS einiesen. 50 0140 FF6FC8 Schlüsselwort testen. 61 0144 FF717C Programm laden. 62 0148 FF7AF0 AmigaDOS UnLoadSeg() 53 014C 000000 Keine Funktion 54 0160 000000 Keine Funktion 66 0154 FF6114 Neues Dev. erstellen + in Device-List. 56 0158 FF7DA0 Unbekannte Funktion 67 015C FF5A50 AmigaDOS WaitForChar() Das AmigaDOS 601 68 0160 FF4A68 Exec-Library-Funktion aufrufen. 69 0164 FF4AE4 BPTR auf akt. SegList-Feld holen. 6A 0168 FF7F00 File loschen. 6B 016C FF81S8 File umbenennen. 6C 0170 xxxxxx Zeiger auf Intuition-Base. 6D 0174 FFee34 AmigaDOS Close() 6E 0178 FF4E62 Wort aus (Dl * 4 + D2 * 2) holen. 6F 017C FF4E70 Wort DS i. (Dl * 4 + D2 * 2) setsen. 60 0180 000000 Keine Funktion 61 0184 000000 Keine Funktion 62 0188 000000 Keine Funktion es 018C 000000 Keine Funktion 64 0190 FFeeoo Wait-for-Message-Funktion aufrufen. 66 0194 FF7BC8 Kommando-BSTR ausführen. 66 0198 FF6C60 Zeiger auf Device-MsgPort holen. 67 0190 FF4A8C Library-Funktion aufrufen. 68 OIAO FFe328 AmigaDOS Fehlermeldung ausgeben. 69 01A4 FF4AEC Z. auf Console-Handler-Task holen. 6A 01A8 FF4AF4 Zeiger auf Filesystem-Taak holen. 6B OIAC FF6E4C BSTR (bis SU SO Zeichen) kopieren. 6C OlBO FF8018 Unbekannte Funktion 60 01B4 FF8200 AmigaDOS UnLock() eE 01B8 FF4AAC Langwort aus (Dl * 4 -I- D2). 6F OlBC FF4AB6 Langwort aus DS nach (Dl * 4 -l- D2). 70 0100 FF61E0 Device-Handler-Task starten. 71 0104 FF8S4C AmigaDOS DupLock() 72 0108 FF6408 Message erstellen, Req. ausgeben. 73 OlOO FFe284 BSTR verschieben. 74 OIDO 000000 Keine Funktion 76 01D4 000000 Keine Funktion 76 01D8 000000 Keine Funktion 77 OIDO 000000 Keine Funktion 78 OlEO 000000 Keine Funktion 79 01E4 FF4C66 Programm ausfUhren. 7A 01E8 000000 Keine Funktion TB OlEO FF800C Unbekannte Funktion 7C OIFO FF6080 Device in Device-List suchen. 70 01F4 FF7FE» Directory erstellen. 7E 01F8 FF4FA4 Unbekannte Funktion 7F OIFO FFe824 Kommando an Timer senden. 80 0200 FF7DB0 Unbekannte Funktion 81 0204 000000 Keine Funktion 82 0208 000000 Keine Funktion 83 0200 000000 Keine Funktion 84 0210 000000 Keine Funktion 86 0214 FF860C CLI-Initialisation. 86 0218 FF4B38 BPTR auf akt. CLI-Struktur holen. 87 bis 96 000000 Keine Funktion Die Beschreibungen der einzelnen Funktionen ist zugegebenermaßen nicht besonders ausführlich. Dies würde nicht nur den Rahmen spren¬ gen, sondern könnte verführen, die Funktionen umfassend zu nutzen. Dies ist allerdings nicht grundsätzlich empfehlenswert, da die Funk- 602 Amiga intern tionen nicht dokumentiert und somit nicht vor Änderungen sicher sind. Und schließlich soll Ihr Programm ja auch unter Kickstart 1.2 und 1.3 (und 1.4) laufen, oder? Der Vollständigkeit halber seien hier daher nur einige Beispiele auf¬ gezeigt, wie die nicht dokumentierten Funktionen der GVT zu ver¬ wenden sind. Nehmen wir uns dafür einige interessante Hilfsfunktio¬ nen heraus, deren Verwendung durchaus sinnvoll sein kann. Betrachten wir hierfür einmal die Funktionsgruppe mit den Vektor¬ nummern $44-$49. Mit diesen Funktionen kann man leicht Wert- und Textausgaben auf das Standard-Ausgabe-Device (COS) erreichen. Dies entspricht, sofern keine Umlenkung im CLI-Befehl erfolgt ist (mit > oder <), dem CLI-Fenster. Ein kleines Beispiel: Wenn Sie im CLI-Fenster einen Wert dezimal ausgeben wollen, so erreichen Sie dies z.B. mit folgenden Zeilen (das Macro wird vorausgesetzt): ;*** Dezimale Uerteausgabe im COS *** Pdez =$46 Neuline =$44 run: doscall Newline move.l #12345,dl doscall Pdez doscall Newline clr.l dl jsr (a6) ;Ende Übrigens: Wenn Sie anstelle der $46 die Nummer $68 angeben, so wird zusätzlich zum Dezimalwert noch der Text "Error code" ausgegeben. Um diesen Wert sedezimal auszugeben (hex), müssen Sie die Vektor¬ nummer $47 verwenden und der Funktion zusätzlich die Anzahl der gewünschten Stellen in D2 mitgeben. Etwas anders sieht es bei Textausaben aus. Mit der Funktion $49 kön¬ nen Sie einen BSTR ausgeben lassen. Diese Stringform enthält im er¬ sten Byte die Anzahl der auszugebenden Zeichen. Die Adresse dieses Strings, der an einer durch 4 teilbaren Adresse liegen muß (!), wird in Dl fibergeben und durch 4 geteilt. Dies sieht etwa folgendermaßen aus: ;Leerzeile ;Dlesen Wert ;dezimal ausgeben ;und LF dazu Das AmigaDOS 603 PrBSTR =$49 Neuline =$44 run: doscall Neuline move.l #BSTR,d1 .-Adresse des Strings Isr. l #2,d1 .-durch 4 doscall PrBSTR ;Text ausgeben clr. l d1 jsr {a6) ;Ende BSTR; dc.b 7.“Hallo!" 3.6 DOS-Handler Wie bereits erwähnt, finden die meisten aller Ein-/Ausgabe-Operatio- nen im Amiga über einen Händler statt, wie z.B. den Port-Händler beim Zugriff auf die serielle oder parallele Schnittstelle. Neben diesem Port-Händler gibt es allerdings noch weitere Händler, wie z.B. den RAM-Handler. Auf der Workbench 1.3 gibt es aber außer diesen beiden Händlern noch weitere: Aux-, Speak-, Newcon- und Pipe-Handler. Alle diese Händler befinden sich auf der Work- bench-Diskette im L-Verzeichnis. 3.6.1 Funktion der DOS-Handler Die Händler haben folgende Aufgabenbereiche: Port-Händler PAR:, SER:, PRT: RAM-Handler RAM: Aux-Handler AUX: RAM-Disk Speak-Handler SPEAK: Ungepufferte serielle Schnittstelle Newcon-Handler NEWCON: Sprachausgabe Pipe-Handler: PIPE: Editierbares CLI-Fenster Datentransfer Sie fragen sich jetzt sicher, welche Händler z.B. für das Device DFO: oder CON:w zuständig sind, da diese Devices in der Liste gar nicht aufgeführt sind. Ganz einfach: Neben den Händlern auf Diskette ver¬ waltet der Amiga auch Händler im ROM. Dies ist ja auch ganz lo¬ gisch, denn wie sollte z.B. der Boot-Vorgang vor sich gehen, wenn für 604 Amiga intern den Diskettenzugriff erst einmal ein Händler von Diskette geladen werden müßte? Wie tritt man aber nun mit diesen Händlern in Kontakt? Nun, in un¬ serem obigen Beispiel war das sehr einfach. Hier brauchte man ja nur anzugeben, wohin die Daten geschickt werden sollen. Versucht man dies aber z.B. mit dem Speak-Handler - z.B. mit "type text to SPEAK:" - so erscheint ein System-Requester, der von Ihnen verlangt, die Diskette mit dem Namen SPEAK: einzulegen. Natürlich wollen wir nicht, daß unser Text auf eine Diskette names SPEAK: kopiert wird, sondern daß er über das Narrator-Device ausgegeben wird. Was Ist also zu tun? Zunächst einmal muß man wissen, daß außer den "logischen" Devices DFx:, SER:, PAR:, PRT:, RAW:, CON: und RAM: alle weiteren Devi¬ ces in das System eingebunden werden müssen. Da dies mit dem Mount-Befehl durchgeführt wird, hat sich der Begriff "mounten" ein¬ gebürgert. Also werden wir im folgenden diesen neudeutschen Begriff verwenden. Der Vorgang ist eigentlich recht einfach. Sie müssen nur "Mount SPEAK:" einzugeben, um das logische Device SPEAK: zu mounten. Bevor dies jedoch möglich ist, müssen noch einige andere Vorberei¬ tungen getroffen werden. Sie müssen nämlich die MountList entspre¬ chend vorbereiten, in der die für die einzelnen Devices nötigen Para¬ meter eingestellt werden. Für den Speak-Handler sieht der MountList-Eintrag so aus: SPEAK: Händler = L:Speak-Handler StackSize = 6000 Priority = 5 GlobVec = -1 # Nach "mount SPEAK:" haben Sie also Zugriff auf den Speak-Handler. Für die anderen Devices gelten die nachstehenden MountList-Einträge: NEUCON: Händler = LiNewcon-Händler Priority = 5 StackSize = 1000 # Das AmigaDOS 605 PIPE: Händler = L:Pipe-Handler Priority = 5 Stacks!ze = 6000 GlobVec = -1 « AUX; Händler = L:Aux-Handler Priority = 5 Stacks!ze = 6000 GlobVec = -1 # Jeder MountList-Eintrag muß mit einem Fis (#) abgeschlossen wer¬ den. Der Mount-Befehl, der die MountList nach dem zu mountenden Device durchsucht, erkennt an diesem Fis jeweils das Ende eines neuen Eintrages bzw. der Mountlist. Konnte das zu mountende Device nicht in der MountList gefunden werden, so gibt Mount den Fehler Device xxx not recognized" aus. Wollen Sie ein logisches Device mehrmals mounten, so wird die Fehlermeldung "Device xxx already mounted" ausgegeben. Doch was geschieht eigentlich beim Mounten? Um diese Frage zu klären, müssen wir uns ein wenig intensiver mit dem Betriebssystem des Amiga befassen. Der Dreh- und Angelpunkt der Device-Handler ist die DOS-Library. Die dazugehörige C-Struktur hat folgenden Aufbau (mit Angabe der für Maschinenprogrammierung notwendigen Offsets): Offset: Struktur: struct DosLibrary 0 $00 struct Library dl_lib; 34 $22 APTR dl_Root; 38 $26 APTR dl GV; 42 $2a LONG drA2; 46 $2e LONG dl AS; 50 $32 LONG drA6; 54 $36 > /* |-> */ /* DOS'Global-Vector */ Von dieser Struktur gehen zwei Zeiger aus. Erstens der Zeiger "dl_Root", mit dem wir uns gleich weiter befassen werden. Der Zeiger "dl_GV" zeigt auf die Global-Vector-Table des DOS. Dort ist die in¬ terne DOS-Library abgespeichert, die den schnellen Zugriff auf die vom DOS bzw. den CLI-Befehlen benötigten Befehle gibt, ohne daß diese die DOS-Library eröffnen müßten. 606 Amiga intern Doch zurück zum Zeiger "dl_Root". Dieser Zeiger zeigt auf eine RootNode-Struktur: Offset: Struktur: struct RootNode { 0 $00 BPTR rn_TaskArray; /* Maximal 20 Prozesse */ 4 $04 BPTR rn_ConsoleSegment; 8 $08 struct DateStamp rn_Time; 20 $14 LONG rn_RestartSeg; 24 $18 BPTR rn_Info; /* j--> */ 28 $1c BPTR rn_Fi leHandlerSegtnent; 32 $20 } Von hier aus zeigt der Zeiger "rn_Info" auf eine für uns interessante Doslnfo-Struktur. Die anderen Elemente der RootNode-Struktur sind dagegen für unsere Zwecke nicht so interessant. Hier die Doslnfo-Struktur, die einen Zeiger auf eine DeviceNode- Struktur enthält: Offset: Struktur: struct Doslnfo f 0 $00 BPTR di_McName; 4 $04 BPTR di_DevInfo; /* Zeigt auf DeviceNode-Structur */ 8 $08 BPTR di_Devices; 12 $0c BPTR di_Handlers; 16 $10 APTR di NetHand; 20 $14 J Offset: Struktur: struct DeviceNode { 0 $00 BPTR dn_Next; 4 $04 ULONG cln_Type; 8 $08 struct MsgPort *dn_Task; 22 $16 BPTR dn_Lock; /* Directory */ 26 $1a BSTR dn_Handler; 30 $1e ULONG dn_StackSize; 34 $22 LONG dn_Priority; 38 $26 BPTR dn_Startup; 42 $2a BPTR dn_SegList; 46 $2e BPTR dn_GlobalVec; 50 $32 BPTR dn_Name; 54 $36 } Diese Struktur enthält endlich die für die Händler benötigten Infor¬ mationen. Allerdings werden mit Hilfe der DeviceNode-Struktur nicht nur Devices aufgelistet. Auch Directories und Volumes finden hier Das AmigaOOS 607 Platz, wobei zu erwähnen ist, daß Volumes über eine abgewandelte Struktur der DeviceNode-Struktur verfügen (DeviceList-Struktur). Doch reicht für die Verwaltung der Händler obige Struktur vollkom¬ men aus. Folgendes Programm macht sich die oben aufgelisteten Strukturen zunutze, um alle dem System bekannten Devices inklusive deren Händler und inklusive der Environment-Variablen auszugeben: #include "exec/types.h" #include "libraries/dos.h" #include "llbraries/dosextens.h" #i nc l ude '• l i brar i es/f i l ehand l er. h" #define CR printf ("\n“) /* Carriage Return*/ #define EnvecSIze (DosEnvec->de_TableSize) extern struct DosLibrary »DOSBase; struct DeviceList *DeviceList; struct DeviceNode *DevtceNode; struct RootNode *RootNode; struct DosInfo »DosInfo; struct FfleSysStartupMsg *FSSM; struct OosEnvec { ULONG de_TableSize; ULONG de SizeBlock; ULONG de“SecOrg; ULONG de_Surfaces; ULONG d€_SectorPerBlock; ULONG de_BlocksPerTrack; ULONG de_Reserved; ULONG de_PreAlloc; ULONG de Interleave; ULONG de~LowCyl; ULONG de HighCyl; ULONG de_NiinBuffers; ULONG de_BufMeinType; ULONG de_MaxTransfer; ULONG de_Mask; LONG de_BootPri; ULONG de_DosType; >; /• Größe des Environment-Vektors •/ /• BlockgröBe (in Languorten) (128) •/ /• Unbenutzt; == 0 •/ /* Anzahl der Schreib/Leseköpfe (2) •/ /* Unbenutzt; == 1 •/ /• Blocks pro Track •/ /• Reservierte Blocks am Anfang •/ /• einer Partition (BootBlocks) •/ /• Reservierte Blocks am Ende •/ /• einer Partition •/ /• Gewöhnlieh 0 •/ /• Start-Cylinder (0) •/ /• End-Cylinder (79) •/ /• Anzahl Buffer (SizeBlock*4 Bytes)*/ /* Speichertyp für Buffers */ /* Haximale Anz. Blöcke, die pro */ /* Zeiteinheit gelesen werden können */ /* Adreß-Maske un bestimmten */ /* Speicher auszugrenzen */ /* Boot-Priorität für Auto-Boot */ /* ASCII(HEX)String f. */ /* File-System-Typ */ /* 0XA44F5300 == Altes File-System */ /* 0XAA4F5301 == Fast-Filing-System */ 608 Amiga intern struct DosEnvec ‘DosEnvec; /* printbstrO */ /* */ /* Funktion: BCPL-String ausgeben */ /*.*/ /* Eingabe-Parameter: •/ /* */ /• String: Auszugebender BCPL-String */ printbstr (String) BSTR String; < UUORD i; BYTE BufferWO]; UBYTE ‘Help; Help = (UBYTE •) BADDR (String); for (i=0;i<(*Help);i++) BufferCi] = *(Helpti+1); Buffer[i] = 0; printf (Buffer); ) tnainO < RootNode = (struct RootNode •) DOSBase->dl_Root; Doslnfo = (struct Doslnfo •) BADDR(RootNode->rn_Info); DeviceNode = (struct DeviceNode •) BADDR(DosInfo->di_DevInfo); do ( if (DeviceNode->dn Type == (ULONG)DLT_DEVICE) < printf ("Device: "); printbstr (DeviceNode->dn_Task, DeviceNode->dn_StackSize); CR- printf ("Pri: OxXOSlx GlobVec: 0x%08lx", DeviceNode->dn_Priority, II Das AmigaDOS 609 BADDR(Dev{ceNode->dn_ClobalVec}}; printf ("Händler: "); printbstr (DeviceNode->dn Händler); CR; if (DeviceNode->dn_Startup > 21 ) FSSM = (struct FileSysStartupHsg *) BADDR (DeviceNode->:^_Startup); printf ("Unit: 0x%08lx ",FSSM->fssm Unit); printf ("Device: "); printbstr (FSSM->fssm Device); CR; printf ("Flags: 0x%08lx“,FSSH->fssm_Flags); if (FSSM->fssm_Environ != Ol) { CR; DosEnvec = (struct DosEnvec *) BADDR (FSSM->fssm Environ); printf ("EnvecSize: %08ld\n",EnvecSize); if (EnvecSize > 0) printf ("SizeBlock: *08ld ",DosEnvec->de SizeBlock); if (EnvecSize >1) printf ("SecOrg: X08ld\n",DosEnvec->de SecOrg); if (EnvecSize >2) printf ("Surfaces: *08ld ",DosEnvec->de Surfaces)- if (EnvecSize >3) “ printf ("SectorPerBlock: J!08ld\n",DosEnvec ->de_SectorPerBlock); if (EnvecSize > 4) printf ("BlocksPerTrack: %08ld ", DosEnveode BlocksPerTrack); if (EnvecSize >5) printf ("Reserved: X08ld\n",DosEnveode Reserved)- if (EnvecSize >6) ~ ' printf ("PreAlloc: *08ld ",DosEnveode PreAlloc); if (EnvecSize >7) ~ printf ("Interleave: %08ld\n",DosEnvec->de Interleave)- if (EnvecSize >8) ~ printf ("LowCyl: *08ld ",DosEnveode LowCyl); if (EnvecSize >9) printf ("HighCyl: *08ld\n",DosEnveode_HighCyl ); if (EnvecSize > 10) printf ("NumBuffers: %08ld ",DosEnveode NuiBuffers)- if (EnvecSize >11) ~ ' printf ("BufMemType: %08ld\n",DosEnvec->de BufMemType); if (EnvecSize > 12) printf ("HaxTransfer: 0xX08lx ", DosEnvec->de_MaxTransfer); if (EnvecSize > 13) printf ("Mask: 0xX08lx\n",DosEnvec->de_Mask); if (EnvecSize > 14) printf ("BootPri: %08ld ",DosEnvec->de BootPri); if (EnvecSize > 15) 610 Amiga intern printf ("DosTypc: 0xX08lx\n",DosEnvec->de DosType); > eise printf ("No Environment VectorXn“); > CR; > DeviceNode = (struct OeviceNode *) BADDR (DeviceNode->dn Next); > while (DeviceNode != 01); Als Ergebnis liefert dieses Programm eine Ausgabe etwa folgender Form: Device: DPI: Task: OxOOOObccc Stack: 00000000 Pri: 0x00000000 ClobVec: 0x00000000 Händler: Unit: 0x00000001 Device: trackdisk.device Flags: 0x00000000 EnvecSize: 00000012 SizeSlock: 00000128 SecOrg: 00000000 Surfaces: 00000002 SectorPerBlock: 00000001 BlocksPerTrack: 00000011 Reserved: 00000002 PreAlloc: 00000011 Interleave: 00000000 LOHCyl: 00000000 HighCyl: 00000079 NunBuffers: 00000005 BufMeniType: 00000003 Device: PRT: Task: Pri: Händler: 0x00000000 0x00000000 ; L:PORT-HANDLER Stack: 00001000 GlobVec: 0x0000a620 Device: PAR: Task: Pri: Händler: 0x00000000 0x00000000 : L:PORT-HANDLER Stack: 00000800 GlobVec: 0x0000a620 Device: SER: Task: Pri: Händler: 0x00000000 0x00000000 : L:PORT-HANDLER Stack: 00000800 GlobVec: 0x0000a620 Device: RAU: Task: Pri: 0x00000000 0x00000005 Stack: 00000700 GlobVec: 0x0000a620 Händler: Das AmigaDOS 611 Device: CON: Task: Pri: Händler: 0x00000000 0x00000005 stack: 00000700 GlobVec: 0x0000a620 Device: RAH: Task: Pri: Händler: OxOOOldcbA 0x00000000 stack: 00000000 GlobVec: 0x00000000 Device: DFO: Task: Pri: OxOOOOOedc OxOOOOOOOa Stack: 00000600 GlobVec: 0x00000000 Händler: Unit: 0x00000000 Device: trackdisk.device Flags: 0x00000000 EnvecSize: 00000012 SizeBlock: 00000128 SecOrg: 00000000 Surfaces: 00000002 SectorPerBlock: 00000001 BlocksPerTrack: 00000011 Reserved: 00000002 PreAlloc: 00000000 Interleave: 00000000 LouCyl: 00000000 HighCyl: 00000079 NunAuffers: 00000005 BufMemType: 00000002 sehen, daß bei manchen Devices die Angabe des H Dies ist darauf zurückzuführen, daß die Händler speicherresident sind, also fest im Betriebssystem verankert sind. Doch neben den Handler-Namen finden Sie noch eine Menge weiterer Informationen. So z.B. die Environment-Vektoren (struct DosEnvec). Diese Vektoren enthalten Informationen über "File-gestützte" Devices. Diese Struktur ist in den Include-Files nirgends zu finden. Im Include-File "libraries/filehandlers.h" wird auf diesen Environment- Vektor zwar hingewiesen, aber man hat hierfür keine eigene Struktur angegeben. Es existieren nur einige Defines, die angeben, in welchem Langwort des Environment-Vektors welche Information zu finden ist. Synonym zum Environment-Vektor ist also; ULONG DosEnvec[]; ... EnvecSize = DosEnvec[DE_TABLESIZE]; Diese Defines reichen aber nur von 0 bis 12; #define DE_TABLESIZE #define DE_SIZEBLOCK «define DE SECORG 0 1 2 Amiga intern 612 mdefine DE NUMHEADS 3 #define DeIsECSPERBLK 4 #define DE_BLKSPERTRACK 5 #define DE RESERVEDBLKS 6 #define DE PREFAC 7 «define DEJNTERLEAVE 8 #define DE LOUCYL 9 #define De'uppercyl 10 «define DE~NUMBUFFERS 11 #define DE~HEHBUFTYPE 12 Angaben wie z.B. der DOS-Typ der Diskette ("DOS\0" für normales Filing-System, "DOS\r für Fast-Filing-System) ist hier (auch in den Includes für Kick VI.3) noch nicht enthalten! Deshalb sollten Sie bei Verwendung des Environment-Vektors unsere Struktur-Definition benutzen. Doch welche Verbindung besteht zwischen Environment-Vektor und DOS-Library? Damit der Environment-Vektor nicht "in der Luft schwebt", zeigt "DeviceNode->dn_Startup" auf eine FileSysStartupMsg, die mit ihrem fssm_Environ-Zeiger auf den Environment-Vektor zeigt: Das AmigaDOS 613 9 $00 U $22 SO $26 12 $2a 16 $2e )B $32 i4 $36 $00 $04 $08 0 $14 4 $18 8 $lc $20 $00 $04 $08 Z $Bc B $10 9 $14 I $18 3 $2c l $20 3 $24 3 $28 struct DosLibrary struct Library dl.Lib; flPTR dl-Root; - flPTR dl_GV; /Jf Global Uector LOHG dl_fl2j LONG dLflS; LOHG dl-OG; /* Dos Copy of fl2 */ Copy of RS struct RootHode BPTR BPTR ro-TaskOrray; nax, 20 Tasks */ rn-ConsoleSegnent; struct DateStanp rn_Tinej rn_RestartSegj rn_Infoj LOHG BPTR BPTR rn_FileHandlerSegnent; $00 $04 $08 $0c $10 struct DosInfo+- 2 6 0 $14 } BPTR BPTR BPTR BPTR flPTR di-HcHane; di-DevInfo; di-Oevices; di_Handlers di-NetHand; struct BSTR LONG fiPTR BPTR BSTR LOHG LONG LOHG BPTR BPTR BSTR OeviceNode - 4 - dn_Nextj - dn_Type; do-Task; dn_Lock; dn_Handler; StackSize; dn_Priority; dn-Startup; dn-SegList; dn_GlobUec; dn_Nane; $00 $04 $08 12 $0c 16 $10 28 $lc 32 $20 36 $34 40 $28 44 $2c > struct DeviceList BPTR LONG Struct MsgPort BPTR dl. dl. »dl. dl. struct DateStanp dl. BPTR LONG LONG BSTR dl. dl. dl. »dl. Hext; ——♦ Type; .Task; .Lock: .VoluneData; .LockList; DiskType; unused; Nane; Abb. 3.6.1 Die FileSysStartup-Struktur enthält auch noch einige Informationen für den Händler. Diese beziehen sich auf das Device, das der Händler verwenden soll. Die Händler für die Diskettenlaufwerke greifen z.B. intensiv auf das Trackdisk-Device zurück. Dies kommt in "FileSys- Startup" durch den BCPL-String "fssm_Device" zum Ausdruck. Die beiden Parameter "Unit" und "Flag", die vom OpenDevice()-Befehl verlangt werden, sind in "fssm_Unit" und "fssm_Flags" enthalten. 614 Amiga intern Nachdem Sie nun ein genaues Bild davon haben, wie das EX)S heraus¬ findet, welches logische Device mit welchem Händler betrieben wird, wollen wir uns noch einmal dem Mount-Befehl widmen. Der Befehl verwendet - wie Sie wissen - die MountList im devs-Verzeichnis. Hier finden Sie z.B. Zeilen wie "GlobVec = -1" oder "StackSize = 2000". Kommen Ihnen diese Ausdrücke nicht irgendwie bekannt vor? Na klar, werden Sie jetzt sicher sagen. GlobVec, StackSize, Device, Händler etc. kommen doch alle als Variablen in der einen oder ande¬ ren der oben erklärten Strukturen vor. Welches Mount-Schlüsselwort in welcher Struktur wiederzufinden ist, können Sie folgenden Tabellen entnehmen: DeviceNode Händler FileSyatem Priority Startup GlobVec BootPri FileSysStartup Device Unit Flaga PreAlloc HighCyl BufTert BufMemType: (0,1 = FAST I CHIP) (2.3 = CHIP) (4,6 = FAST) MaxTransfer Maak DoaType DoaEnvec Surfacea BlockaPerTrack Reaerved Stack Interleave LowCyl Das einzige Schlüsselwort, das sich nicht in einer der oberen Sparten einreihen läßt, ist das Schlüsselwort "Mount". Dieses Schlüsselwort hat auch weniger mit den Devices zu tun als vielmehr mit der Ausführung des Mount-Befehls. Ist nämlich "Mount = 1", so steht das logische De¬ vice direkt nach dem Mount-Befehl zur Verfügung. Ohne "Mount = 1" muß man es erst einmal ansprechen, z.B. durch "dir logi- sches_Device:", damit es wirklich eingebunden wird. Doch wollen wir neben dem Mount-Schlüsselwort noch einige andere Schlüsselwörter betrachten: Beginnen wir mit dem Schlüsselwort "GlobVec". Wie Sie wissen, ver¬ waltet das AmigaDOS eine interne Library, die z.B. den CLI-Kom- mandos ohne große Umschweife Zugriff zu den wichtigsten DOS- und EXECBase-Befehlen verschafft. Geben Sie für GlobVec den Wert -1 an, so verzichtet Ihr Händler auf diese interne Library. Dies ist beson¬ ders dann angebracht, wenn Sie Ihre Händler in C schreiben. Bei Das AmigaDOS 615 GlobVec = 0 wird der DosBase->dl_GV-Zeiger auf den DOS-Base- GlobalVector verwendet. Interessant ist auch das Schlüsselwort "BootPri". Mit diesem Schlüssel¬ wort legen Sie die Priorität eines FileSystems für den Boot-Vorgang fest. Schließlich ist der Amiga ab Kickstart VI.3 in der Lage, von je¬ dem beliebigen FileSystem (DFO:, DFl;, HD:, RAM:) zu booten. Mit BootPri legen Sie die Reihenfolge der zu untersuchenden FileSysteme fest (-127 bis 128). ^ In Verbindung mit FileSystemen muß man auch das Fast-Filing-Sy- stem nennen. Dieses Fast-Filing-System beschleunigt die Zugriffzeiten auf Files und Directories erheblich. Doch was hat das Fast-Filing-Sy- stem (FFS) mit dem Environment-Vektor zu tun? Um fremde Disketten von Amiga-Disketten zu unterscheiden, wird in den Boot-Block die Kennung "DOS\0" hineingeschrieben. Beim Fast- File-System steht hier aber "DOS\r. Dieser Unterschied wird durch "DosType" festgelegt. Wenn Sie sich den Environment-Vektor noch einmal ansehen, werden Sie feststellen, daß die Kennungen "0x444F5300" und "0x444f5301" die ASCII-Codes von "DOS\0” bzw "DOS\l" sind. Die anderen Schlüsselwörter ergeben sich aus der Übersetzung ins deutsche und den Kommentaren beim Environment-Vektor. 3.6.2 Aufbau und Programmierung eines Händlers Nachdem Sie nun wissen, wie ein Device gemountet wird und wie man ihm über die MountList Informationen zukommen lassen kann, wollen wir uns mit der Programmierung eines Händlers beschäfigen. Für die Programmierung eines Händlers muß man wissen, daß die DosPackets eine wichtige Rolle im Datentransfer AmigaDOS <-> Händler spielen. Das DOS sendet, nachdem der Händler geladen und gestartet wurde, ein Packet an den Händler. Dieses Startup-Packet muß vom Händler bearbeitet und ans DOS zurückgeschickt werden. Dieser Mechanismus von Hin und Zurück dient DOS und Händler gleichermaßen zur wechselseitigen Synchronisation. Erst wenn das DOS sein vorher ab¬ geschicktes Packet zurückerhält, wird ein neues gesendet. Genauso 616 Amiga intern verhält es sich auf Händler-Seite. Erst wenn das empfangene Packet zurückgesendet wurde, ist der Händler bereit, ein neues Packet zu empfangen. Für das Empfangen und Senden wird sehr stark auf den Exec-Mes- sage-Mechanismus zurückgegriffen. Bevor wir diesen Mechanismus aber beschreiben, wollen wir einen Händler abdrucken, damit Sie sich ein Bild von diesem Mechanismus machen können. Dieses Programm ist in Assembler geschrieben, da sich so die Verwendung der diversen Tabellen leicht zeigen läßt. Für den C-Programmierer haben wir je¬ weils die entsprechende C-Anweisung danebengesetzt. ********************** * DOS-Handler DEMO * * * * Bruno Jennrich * ********************** ;EXEC ExecBase: S 4 FindTask: S -294 AO Uait: s -318 DO Setsignal: s -306 DO,Dl GetMsg: 8 -37? AO PutMsg: S -366 AO.AI OpenLibrary: = -552 AI,DO Clösetibrary: S -414 AI Forbid: = -132 -- .-INTUITION OpenScreen: = -198 AO CloseScreen: - -66 AO .-GAPHICS Move: S -240 AI,DO,Dl Drau: 8 -246 AI,DO,Dl Text: = -60 A1,A0,D0 SetRGBA: = -288 A0,D0,D1,D2,D3 SetAPen: = -342 AI,DO RectFill: = -306 A1,D0,D1,D2,D3 ;DOS DosSignal: = 256 UnloadSeg: = -156 ; Dl ********************* - oos-Handler - ************************ DOSHandler: jsr TaskUait ;Uarte auf Packet move.l dO,OurPacket .-Packet vom DOS move.l OurPacket.aO move.l 20(a0),d0 ;OpenString = asl.l #2,d0 Das AmigaDOS 617 move.l dO,OpenString ;Packet->dp_Arg1 « 2 move.l OurPacket,aO move.l 28(a0),d0 ;Node = asl.l #2,d0 move.l dO.node ;Packet->dp_Arg3 « 2 move.l ExecBase,a6 lea DosName,a1 ;DosBase = move.l mo.do jsr OpenLibrary(a6} ;OpenLibrary (DosName,0) move.l dO,DosBase Jsr Init tst.l dO bne ReturnPacketError Jsr HandleStartif^Packet Jsr ReturnPacketOK NainLoop : Jsr TaskUait ;Uarte auf Packet move.l dO,OurPacket ;OurPacket = TaskwaitO Jsr HandlePacket ;PacketTyp ermitteln ;und ausgeben move.l OurPacket,aO move.l 8(a0),d2 ; Packet - Typ-Rout i ne jmp ActType ;ausfüh ren Back: Jsr blip Jsr HouseKlick ;Mausklick abuarten Jmp MainLoop node: ds.l 1 OurPacket: ds.l 1 ********4 TaskUait: move.l ExecBase,a6 move.l Ii>0,a1 Jsr FindTask(a6) ;TWProcess = FindTask (NULL) move.l dO,TUProcess add.l #92,d0 move.l dO,TUPort ;TWPort = TWProcess->pr MsgPort move.l ExecBsse,a6 move.l Ii!0,d0 move.l M>osSignal,D1 ;SetSignal (0,256) jsr SetSignal(a6) move.l ExecBase,a6 inov«.l M>osSignal,dO jsr Uait(a6} move.l ExecBase,a6 ;Wait(256) 618 Amiga intern move.l TUPort,aO jsr 6etMsg(a6) move.l dO,aO move.l 10(a0),d0 rts TUProcess: ds.l 1 TUPort: ds.l 1 ;Message = 6etMsg(TUPort) ;return ; (Message->inn_Node. ln_Name) - ReturnPacketOK - ReturnPacketOK: move.l OurPacket,aO move.l aO.dO ;Packet->Res1 = -1 move.l move.l 16(a0),d2 ;Packet->Res2 = jmp ReturnPacket ;Packet->Res2 ********************* . ReturnPacketError - •**•*****•*•***** ReturnPacketError: move.l OurPacket.aO move.l aO.dO move.l #0,d1 ;Packet->Res1 = 0 move.l #202,d2 ;Packet->Res2 = ;ERROR_OBJECT_IM_USE *••••••*•***••**•**** - ReturnPacket (Universal) - ********** ReturnPacket: move.l dO,RTPacket move.l d0,a0 move.l d1,12(a0} move.l d2,16(a0) move.l (aO),RTMessage move.l 4(aO},RTPort move.l ExecBase,a6 move.l j|IO,aO jsr F{ndTaskRes1 = dl ;Packet*>Res2 * d2 ;Message = Packet->dp_Link ;Port = Packet->dpj5ort ;RTProcess = FindTask (0) ;Packet->dp_port = ftProcess ; pr_HsgPort ;Message->mn_Node.ln_Naffle = ; Packet” ;Message->inn_Node.ln_Succ = ;Message->nn Node.ln~Pred = ;MULL ;PutMsg (RTPort,RTMessage) Das AmigaDOS 619 RTHessage: ds.l 1 RTProcess: ds.l 1 RTPacket: ds.l 1 - HandleStartupPacket - HandleStartupPacket: iiiove.l GfxBase,a6 move.l RastPort.al move.l #3,d0 jsr SetAPen(a6) move.l GfxBase,a6 move.l #S5,dO move.l #40,dl move.l RastPort,a1 jsr Hove(a6) move.l GfxBase,a6 move.l RastPort.al lea HarxtyText.aO move.l HarxlyLen.dO jsr Text(a6) move.l GfxBase,a6 move.l RastPort.al move.l #2,d0 jsr SetAPen(a6) move.l GfxBase,a6 move.l OpenStrIng.aO move.l RastPort.al move.b (a0),d0 add.w #1,a0 jnp Text(a6) .-SetAPen (RastPort,3) ;Hove (RastPort,10,20) ;Text (RastPort,HandyText, ; HandyLen) .-SetAPen (RastPort,2) .-Text (RastPort,OpenString+1, ; ‘OpenString) HandyText: dc.b "Name of our DEVICE: " HandyLen: dc.l 20 ••••••••••••••••••••• . HandlePacket HandlePacket: move.l OurPacket.dO move.l GfxBase,a6 move.l RastPort.al move.l #2,d0 jsr SetAPen(a6) move.l OurPacket.aO move.l 8(a0),d2 move.l #25,dO move.l #100,dl jmp PrintType ;SetAPen(RastPort,2) ;Packet->dp_Type ;Packet-Typ Ausgeben 620 Amiga intern ********************* - ActType - **************i ActType: lea ACTI0N_TYPES,a1 move.l #34,dO ;32 Einträge lea ACTION_ACTS,aO loopac: add.l #4,a0 move.l (a1)+,d1 cmp.l d2,d1 beq DO_ACT dbra d 07 loopac rts DO_ACT: move.l (aO),aO jmp (aO) ********************* . Quit - •••••»************************ QuitReturn: jsr ReturnPacketOK Quit: move.l IntuitionBase,a6 move.l Screen,aO jsr CloseScreen(a6) move.l ExecBase,a6 move.l DosBase,a1 jsr CloseLibrary(a6) move.l ExecBase,a6 move.l GfxBase,a1 jsr CloseLibrary(a6) move.l ExecBase,a6 move.l IntuitionBase,a1 ;CloseLibrsry (IntuitionBase) jsr CloseLibrary(a6) jsr ReturnPacketOK move.l ExecBase,a6 jsr Forbid(a6) ;ForbidO move.l DosBase,a6 move.l node,aO move.l 32(a0},d1 jsr UnloadSeg(a6) ;UnloadSeg (node->SegList) move.l node.aO ;node->SegList = 0 clr.l 32(a0) rts ;Back to DOS ********************* . Cl69r * ***************************** Clear: movem.l d3/a0,-(sp) move.l GfxBase,a6 move.l RastPort,a1 ;CloseScreen(Screen) ;CloseLibrary (DosBase) ;CLoseLibrary (GfxBase) .-Adresse der jeweiligen .-Routine ermitteln Das AmigaDOS 621 move.l #0,d0 jsr SetAPen(a6) move.l GfxBase,a6 move.l RastPort,a1 move.l #0,d0 move.l #106,dl move.l #319,dZ move.l #255,d3 jsr RectFUl(a6) move.l GfxBase,a6 move.l RastPort,a1 move.l #3,d0 jsr SetAPen(a6) movem.l (sp)+,d3/a0 rts ;SetAPen (RastPort,0) ;RectFill (RastPort,0,106, ; (319,255) ;SetAPen (RastPort,3) - Urite - ***************************** Write: jsr Clear ;Ausgabebereich löschen move.l OurPacket,a1 move.l 2A(a1),aO move.l 28(a1),d3 sub.l #1,d3 move.l #1,x move.l #115,y WriteLoop: movem.l d3/a0,-(sp) move.l GfxBase,a6 move.l RastPort,a1 move.l x,d0 move.l y,d1 jsr Hove(a6) ;a0 =» Auszugebender String ;d3 = Strien ((aO)) ;Move(RastPort,x,y) movem.l (sp)+,d3/a0 movem.l d3/a0,-(sp) move.b (a0),d0 cmp.b #10,dO boe UriteMore ;LineFeed? movem.l (sp)+,d3/a0 add.l #1,a0 jmp AddY ;Nächste Zeile WriteMore: move.l GfxBase,a6 move.l RastPort,a1 move.l #1,d0 jsr Text(a6) ;Text(RastPort,(aO),1) 622 Amiga intern movem.l (sp)+,d3/a0 add.l #1,a0 add.l #9,x move.l x,dO ;x += 9 cmp #320-10,dO ble OK AddY: move. l «1,x add.l #10.y ;y += 10 move.l y,d0 cmp. l #256-10,dO ble OK movem.l d3/a0,-(sp) ;Ende des Ausgabebereichs ;erreicht move.l GfxBase,a6 ;SetAPen(RastPort,1) move.l RastPort,a1 move.l #1,d0 jsr SetAPen(a6) move.l GfxBase,a6 move.l RastPort,a1 move.l #160-32,dO ;Move (RastPort,160-32,80) move.l #80,dl jsr Move(a6} move.l GfxBase,s6 move.l RastPort,a1 lea MoreText,aO ;Text (RastPort,HoreText,8) move.l #8,d0 jsr Text(s6} jsr HouseKlick move.l GfxBase,s6 move.l RastPort,a1 move.l #160-32,dO ;Move(RastPort,160-32,80) move.l #80,dl jsr Hove(s6) move.l GfxBase,s6 move.l RastPort,a1 ;Text(RastPort,MoveClear,8) lea MoreClear,a0 move.l #8,d0 jsr Text(a6} movem.l (sp)+,d3/a0 jsr Clear ;Ausgabebereich löschen move.l #1,x move.l #115,y ;L{nks, oben weitermachen OK: dbra d3,UriteLoop ;Alle Zeichen ausgegeben? Das AmigaDOS 623 jnp ReturnPacketOK HoreText: dc.b '•< MORE >" HoreClear: dc.b " " even ;Packet-Types-Definitionen ACTION_TYPES: dc.l $0 ;NIL dc.l »2 GET BLOCK dc.l $4 SET MAP dc.l $5 DIE dc.l $6 EVENT dc.l *7 CURRENT VOLUME dc.l SS LOCATE OBJECT dc.l $9 RENAME~DISK dc.l S57 URITE dc.l SS2 READ dc.l 1002 RETURN URITE dc.l 1001 RETURN~READ dc.l 15 FREE LOCK dc.l 16 DELETE OBJECT dc.l 17 rename'object dc.l 19 coPY dTr dc.l 20 UAIT CHAR dc.l 21 SET PROTECT dc.l 22 CREÄTE DIR dc.l 23 EXAMINE OBJECT dc.l 24 £XAMINE~NEXT dc.l 25 DISK INFO dc.l 26 INFO” dc.l 28 SET COMMENT dc.l 29 PARENT dc.l 30 TIMER dc.l 31 INHIBIT dc.l 32 DISK TYPE dc.l 33 DISK CHANGE dc.l 1005 FIND~INPUT dc.l 1006 FIND OUTPUT dc.l 1008 seek” dc.l 1007 END ; Adressen der Packet-Type Routinen; ACT ION ACTS: dc.l DO_NOT_KNOWN dc.rDO_NIL dc.l DO_GET_BLOCK dc.l DO_SET_HAP dc.l DO_DIE dc.l DO EVENT dc.l DO~CURRENT VOLUME dc.l D0 “l0CATE OBJECT dc.l do“rename“disk dc.l DO'URITE dc.l do“read dc.l DO~RETURN URITE 624 Amiga intern dc.l DO RETURN READ dc.l DO FREE_LÖCK dc.l DO DELETE OBJECT dc.l DO RENAME OBJECT dc.l DO COPY dTr dc.l DO~UAIT~CHAR dc.l DOlSET PROTECT dc.l DO CREÄTE DIR dc.l DO~EXAMINE OBJECT dc.l DO EXAMINE~NEXT dc.l DO_DISK INFO dc.l D0_INF0“ dc.l DO SET_COMHENT dc.l DO PARENT dc.l DoItIMER dc.l DO INHIBIT dc.l DO_DISK TYPE dc.l DO_DISK~CHANGE dc.l DO FIND~INPUT dc.l DO_FIND~OUTPUT dc.l DO_SEEK dc.l DO_END ; Packet-Type Routinen: DO_NOT_KNOUN: jsr ReturnPacketError jnf) Ouit DO_NIL: jsr Blip Tsr ReturnPacketOK jmp Back DO_QET_BLOCK: jsr ReturnPacketOK jmp Back DO_SET_MAP: jsr ReturnPacketOK Jinp Back DO_DIE: Jmp QuitReturn DO~EVENT: jsr ReturnPacketOK jnp Back DO_CURRENT_VOLUHE: jsr ReturnPacketOK jnp Back~ DO_LOCATE_OBJECT: jsr ReturnPacketOK jnp Back DO_RENAHE_DISK: jsr ReturnPacketOK jnp Back DO_WRITE: jsr Urite jnp Back DO_READ: jsr ReturnPacketOK jnp Back DO_RETURN_WRITE: jsr ReturnPacketOK jnp Back DO_RETURN_READ: jsr ReturnPacketOK jnp Back DO_FREE_LOCK: jsr ReturnPacketOK jnp Back DO_DELETE_OBJECT: jsr ReturnPacketOK jnp Back DO_RENAME_OBJECT: jsr ReturnPacketOK jnp Back DO_COPY_DIR: jsr ReturnPacketOK Das AmigaDOS 625 jmp Back DO_UAIT_CHAR: jsr ReturnPacketOK jmp Back DO_SET_PROTECT: jsr ReturnPacketOK jmp Back DO_CREATE_DIR: jsr ReturnPacketOK jmp Back DO_EXAMIME_OBJECT: jsr ReturnPacketOK jmp Back DO_EXAMINE_NEXT: jsr ReturnPacketOK jmp Back DO_DISK_IMFO: jsr ReturnPacketOK jmp Back DO_INFO: jsr ReturnPacketOK jmp Back DO_SET_COMMENT: jsr ReturnPacketOK jmp Back DO_PAREMT: jsr ReturnPacketOK jmp Back DO_TIMER: jsr ReturnPacketOK jmp Back D0_INHIB1T: jsr ReturnPacketOK jmp Back DO_DISK_TYPE: jsr ReturnPacketOK jmp Back D0_D1SK_CHANGE: jsr ReturnPacketOK jmp Back DO_FIHD_IMPUT: jsr ReturnPacketOK jmp Back DO_FINO_OUTPUT: jsr ReturnPacketOK jmp Back DO_SEEK: jsr ReturnPacketOK jmp Back DO_END: jmp QuitReturn ; Adressen der Packet-Type Strings: ACT ION TABLE: dc.l ACT ION NOT_KNOWH dc.l ACTION NIL dc.l ACTION~GET BLOCK dc.l ACTION SEt"mAP dc.l ACTION~DIE dc.l action“evemt dc.l actionIcurrent_volume dc.l ACTION_LOCATE_OBJECT dc.l ACTION RENAME DISK dc.l ACTION_URITE ” dc.l ACTION_REAO dc.l ACTION_RETURM_URITE dc.l ACTION RETURN READ dc.l actionIfree_löck dc.l ACTION DELETE OBJECT dc.l action“rena«e”object dc.l ACTION_COPY d7r dc.l action_wait“char dc.l ACTION SET PROTECT dc.l ACTION CREÄTE DIR dc.l ACTION~EXAMINE OBJECT 626 Amiga intern dc.l ACTION_EXAMINE NEXT dc.l ACTI0N_D1SK INFO dc.l ACTION INFO~ dc.l ACTION~SET COHHENT dc.l ACTION PARENT dc.l actionItiher dc.l ACTION INHIBIT dc.l ACTION"dISK type dc.l ACTION DISK'CHANGE dc.l ACTION FIND~INPUT dc.l ACTION_FIND~OUTPUT dc.l ACTION SEEX~ dc.l ACTION~END ; Packet-Type Strings ACTION NIL: dc.b ■■ACTION NIL (SOOOOOOOO)^^ ACTION GET BLOCK: dc.b ■■ACTION GET BLOCK (SOOOOOOOZ)^^ ACTION SET MAP: dc.b •■ACTION SET MAP (SOOOOOOOA)^^ ACTION DIE: dc.b ■■ACTION DIE (SOOOOOOOS)^^ ACTION EVENT: dc.b ■■ACT ION EVENT (S00000006)^^ ACTION CURRENT VOLUME: dc.b ■■ACTION CURRENT VOLUME (S00000007)^^ ACTION LOCATE OBJECT; dc.b ■■ACTION LOCATE OBJECT (SOOOOOOOS)^^ ACTION RENAME DISK: dc.b ■•ACTION RENAME DISK (S00000009>^^ ACTION URITE: dc.b ■■ACTION URITE (S000000S7)^^ ACTION READ; dc.b ■■ACT ION READ (SOOOOOOSZ)^^ ACT ION RETURN WRITE: dc.b ■■ACTION RETURN WRITE (SOOOOOSEA)^^ ACTION RETURN READ; dc.b •■ACTION RETURN READ {S000003E9)^^ ACTION FREE LOCK: dc.b ■■ACTION FREE LOCK (SOOOOOOOF)^^ ACTION DELETE OBJECT: dc.b ■■ACT ION DELETE OBJECT (SOOOOOOIO)^^ ACTION RENAME OBJECT: dc.b •■ACTION RENAME OBJECT (SODODOOID^^ ACTION COPY DIR: dc.b ■■ACTION COPY dTr (S00000013}^^ ACTION UAIT CHAR: dc.b ■■ACTION WAIT CHAR (SOOOOOOU}^^ ACTION SET PROTECT: dc.b ■■ACTION SET PROTECT (SOOOOOOIS}^^ ACTION CREÄTE DIR: dc.b ■■ACTION CREATE DIR ($00000016}^^ ACTION EXAMINE OBJECT: dc.b •■ACTION EXAMINE OBJECT ($00000017)^^ ACTION EXAMINE NEXT: dc.b ■■ACTION EXAMINE NEXT ($00000018)^^ ACTION DISK INFO: dc.b ■■ACTION DISK INFO ($00000019}^^ ACTION INFO: dc.b ■■ACTION INFO (SOOOOOOIA}^^ ACTION SET COMMENT: dc.b ■■ACTION SET COHMENT (SOOOOOOIC}^^ ACTION PARENT: dc.b •■ACTION PARENT (SOOOOOOID}^^ ACTION TINER: dc.b ■•ACTION TIMER (SOOOOOOIE}^^ ACTION INHIBIT: dc.b ■•ACTION INHIBIT ($0000001F)^^ ACTION DISK TYPE: dc.b ■■ACTION DISK TYPE ($00000020)^^ ACTION DISK CHANGE: dc.b •■ACTION DISK CHANGE ($00000021 }■■ ACTION FIND INPUT: dc.b ■•ACTION FIND INPUT ($000003ED}^^ ACTION FIND OUTPUT: dc.b ■•ACTION FIND OUTPUT ($000003EE}^^ ACTION SEEK: dc.b ■■ACTION SEEK ($000003FO}^^ ACTION END: dc.b ■■ACTION END ($000003EF}^^ ACTION NOT KNOUN: dc.b ■■ACTION NOT KNOUN ($7?7777?7)^^ Init Init: move.l ExecBase,a6 lea GfxNante.al ;GfxBase = move.l #0,d0 Das AmigaDÖS 627 jsr 0penLibrary(a6) move.l dO,GfxBase tst.l GfxBase beq EndlnitError move.l ExecBase,a6 lea IntuiName,a1 move.l #0,d0 jsr 0penLibrary(a6) move.l dO,IntuitionBase tst.l tntuitionßase beq EndlnitError move.l IntuitionBase,a6 lea NeuScreen,aO jsr OpenScreen(a6) move. l dO,Screen move.l Screen,d0 tst.l Screen beq EndlnitError move. l Screen,d0 add.l #44,dO move.l dO,ViewPort move.l GfxBase,s6 move. l VieuPort,aO move. l #0,d0 move.l #6,d1 move. l #6,d2 move. l #6,d3 jsr SetRGB4(s6) move.l GfxBase,a6 move.l VieuPort,aO move.l #1,d0 move.l #15,dl move.l #7,d2 move.l #7,d3 jsr SetRGB4(a6) move.l GfxBase, a6 move.l VieHPort,aO move.l #2,d0 move.l #10,d1 move.l #10,d2 move.l #15,d3 jsr SetRGB4(a6) move. l GfxBase,a6 move.l VieuPort,aO move.l #3,d0 move.l #7, dl move.l #7,d2 move.l #15,d3 jsr SetRGB4(a6) ;OpenLibrary (GfxName.O) ;IntuitionBase = ;OpenLibrary (IntuiName.O) ;Screen = ;OpenSc reen(NewSc reen) ;ViewPort = &Screen*>V)ewPort ;SctRGß4(VienPort,0,6,6,6) ;SetRGB4(ViewPort, 1,15,7,7) ;SetRGß4(VieHPort,2,10,10,15) ;SetRGB4(ViewPort,3,7,7,15) 628 Amiga intern move.l Screen,dO ;RastPort = add.l #84,dO ;&Screen->RastPort move.l dO,RastPort move.l GfxBase,a6 move. l RastPort,a1 move.l #1,d0 ;SetAPen(RastPort,1) jsr SetAPen(a6) move.l GfxBase,a6 move.l RastPort,a1 move.l «O.dO move.l #105,dl ;Move(RastPort,0,105) jsr Hove(a6) move. l GfxBase,a6 move.l RastPort,a1 move. l #319,d0 ;D raw(RastPort,319,105) move.l #105,dl Jsr Draw(a6) Endlnit: move.l #0,d0 rts EndlnitError: move.l #-1,d0 rts NeuScreen: dc.w 0 de. .H 0 de. .H 320 de. .W 256 de. .H 2 de. .b 1 de. .b 0 de. .w 0 de. .w $f de. .1 0 de. .1 Title de, .1 0 de. .1 0 Title: dc.b " even Screen: ds. ,i 1 RastPort: ds. ,i 1 ViewPort: ds. ,i 1 DosBase: ds. .1 1 GfxBase: ds. ,i 1 IntuitionBase: ds. ,i 1 ;LeftEdge ;TopEdge ;Uidth ;Height ;Depth ;DetaiIPen ;BlockPen ;ViewModes ;CUSTOMSCREEM ;Font ;Screen-Titel ;Gadgets ;BitHap Handler-Trace",0 OpenString: ds.l 1 Das AmigaDOS 629 DosName: dc.b "dos.library“,0 GfxName: dc.b "graphics. l ibrary'',0 IntuiName: dc.b ''intuition.library“,0 even ********************* . Blip - ****************************** Blip: move.w iVSOfff.dO Bl ipLoop: move.w lll$0F77,$dff180 move.w «$0AAF,$dff182 move.w #$077F,Sdff184 ;BUdschirii<>linken move.w lll$0666.Sdff186 dbra dO.BlipLoop rts ***************.***.* . MouseKlick - ************************ HouseKlick: btst #6,$bfe001 ;Linke Maustaste abwarten bne MouseKlick rts ********************* . H6xDunf> ' *************************** HexDump: lea HexTable,a2 lea Buff'«'8,aÖ move.l #7,d0 HexLoop: move.l Niinber,d2 and.l #$0000000f,d2 move.b 0(a2,d2),-(a0) move.l Number,d2 ;NiJiber nach Hex wandeln asr.l #4,d2 move.l d2,Nunber dbra dO,HexLoop move.l GfxBase,a6 lea Buff.aO ;Text(RastPort,Buff,8) move.l RastPort,a1 move.l #8,d0 jsr Text(a6) rts HexTable: dc.b ■■0123456789ABCDEF'' Nunber: ds.l 1 Buff: ds.l 2 even ********************* . ppintType - PrintType: move.l GfxBase,a6 move.l RastPort,a1 ;Move(RastPort,dO,d1) 630 Amiga intern jsr Hove(a6} lea ACTION TYPES,a1 move.l «34,dO lea ACTION_TABLE,aO move.l d2,NLinber loopab: add.l «4,a0 move. l (a1)+,d1 cmp. l d2,d1 beq Print dbra dO,loopab lea ACT ION TABLE,aO Print: move. l (aO),aO move.l GfxBase,a6 move.l RastPort,a1 move.l «24,dO jsr Text(a6) jsr HexOunp move.l GfxBase,a6 lea KlaninerZu,aO move.l RastPort,a1 move. l «1,d0 jsr Text(a6) rts KlaainerZu: : dc.b ")•' even ;34 types ;Action_Typ_String ermitteln ;Text(RastPort, ; Action_String,24) ;PacketType nach Hex ;Text (RastPort,")",!) x: ds.l 1 y: ds.l 1 end Insbesondere die TaskWait- und die ReturnPacket-Funktionen spielen beim Packet Hin- und Hergeschiebe eine große Rolle. Zu Beginn des Händlers, der mittels LoadSegO und CreateProc() gela¬ den und gestartet wurde, wird auf das Startup-Packet vom DOS ge¬ wartet. Wurde dies empfangen (für DosPackets wird Signal-Bit 8 ver¬ wendet), enthält "OurPacket->dp_Argl" den OpenString. Dieser ent¬ hält den Namen des logischen Devices (z.B: DFO:, PAR: etc). Wenn Sie dieses Startup-Packet verarbeitet haben (z.B. die nötigen De¬ vices wie Trackdisk-Device o.ä eröffnet), können Sie das Packet an den Sender, also ans DOS, zurücksenden. Wir haben im obigen Pro¬ grammbeispiel einen Intuition-Screen eröffnet, in dem alle Packet-Ty- pen der vom DOS gesendeten Packets angezeigt werden. - Das AmigaDOS 631 Nach dieser Initialisierung wird mit "MainLoop" fortgefahren. Hier spielt sich alles ab. Es wird auf ein DosPacket gewartet und dann je nach Typ eine Routine angesprungen. Wir haben die Realisation über Sprungtabellen gewählt. Je nach PacketTyp wird die anzuspringende Routine ermittelt und ausgeführt (ActType). Wurde die entsprechende Routine ausgeführt, wird das Packet retur- niert, und es wird mit der MainLoop weitergemacht. Hierbei gibt es allerdings zwei Ausnahmen: Wurde ein Packet des Typs ACTION DIE oder ACTION_END gesendet, werden alle Maßnahmen zum Verlässen des Händlers getroffen. Zunächst wird auch hier das Packet returniert. Dann aber wird der Intuition_Screen geschlossen und das Programm "entladen" (Unload- Seg). Damit bei einem späteren Zugriff dem AmigaDOS auch klar ist, daß der Händler nicht mehr im Speicher vorhanden ist, wird in der DeviceNode der Zeiger "SegList" gelöscht. Dies signalisiert dem DOS, daß der Händler bei erneutem Zugriff wieder geladen werden muß. Nach diesem globalen Ablaufschema eines Händlers wollen wir nun ein wenig tiefer in diese Materie eindringen. Zunächst wollen wir die Routinen TaskWait und ReturnPacket betrachten: ********************* - Taskwait - TaskUait: move.l ExecBa8e,a6 move.l «0,a1 jsr FindTask(a6) move.l dO,TWProcess add.l #92,d0 move.l dO,TUPort move. l ExecBase,a6 move.l XlO.dO move. l MosSignal.OI jsr SetSignal(a6) move. l ExecBase,a6 move. l MosSignal.dO jsr Uait(a6) move.l ExecBase,a6 move.l TUPort,aO jsr GetHsg(a6) move.l d0,a0 move.l 10(a0),d0 rts TWProcess: ds.l 1 TUPort: ds.l 1 ;TUProcess = FindTask (NULL) ;TWPort = TWProcess->pr_MsgPort ;SetSignal (0,256) ;Wait(256) .•Message = GetMsg( TUPort) ;return ; (Message->mn_Node.ln_Naine) 632 Amiga intern TaskWait wartet zunächst darauf, daß das Signal-Bit 8 gesetzt wird. Dieses Signal ist für die Inter-Prozeß-Kommunikation reserviert und dient dem DOS zur Signalisierung von DosPacket-Messages. Wenn TaskWait feststellt, daß das Signal-Bit gesetzt worden ist, holt sich diese Routine über den prozeßeigenen Message-Port die vom DOS gesandte Message (natürlich ein Packet) mittels GetMsg(). In "Mes- sage->mn_Node.ln_Name" ist - etwas unkonventionell - der Zeiger auf das gesandte Packet enthalten und wird an das TaskWait aufru¬ fende Programm in DO übergeben. Nun haben wir also das Packet vom DOS erhalten und können uns dem Verarbeiten dieses Packets widmen. Dabei gilt natürlich zu un¬ terscheiden, ob das Packet ein Startup-Packet (das allererste) oder ein "gewöhnliches" Packet ist. Beim gewöhnlichen Packet wird in ActType die jeweilige Routine an¬ gesprungen, die für die Bearbeitung des gesandten Packets notwendig ist. Damit wir das Programm gegebenenfalls auch aus einer dieser, Routinen verlassen können, werden diese Routinen mittels "jmp" an¬ gesprungen. Bei "jsr" würde ein "rts" ja dafür sorgen, daß das Pro¬ gramm wieder in die MainLoop zurückspringt. Das Programm soll aber verlassen werden. Bei Packets, nach deren Abarbeitung noch weitere Packets erwartet werden (alle außer ACTION_DIE und ACTION_END), wird mittels "jmp Back" in die MainLoop zurückgesprungen. Im oben abgedruckten Händler werden die meisten Packets durch "jsr ReturnPacketOK" und "jmp Back" bearbeitet. Dies deshalb, weil ja vielleicht einmal ein unerwartetes Packet gesendet werden könnte, was bei Nichtbearbeitung dazu führen würde, daß das DOS bis in alle Ewigkeit auf die Antwort auf sein vormals gesendetes Packet wartet. Doch wie geht das Returnieren eines Packets vor sich? Sehen wir uns dazu folgende Routinen an: ReturnPacketOK - ******************** ReturnPacketOK: move.l OurPacket,aO move.l aO,dO ;Packet->Res1 = -1 move.l #-i,d1 move.l 16(a0),d2 ;Packet->Res2 = jmp ReturnPacket ;Packet->Res2 Das AmigaDOS 633 **********••••••••••• - ReturnPacketError - ••••••••••••••••• ReturnPacketError: move.l OurPacket,aO move.l aO,dO move.l #0,d1 ;Packet->Res1 = 0 move.l #202,cl2 ;Packet->Res2 = ;ERROR_OBJECT_IN_USE *••••*•••••••••••••*• . ReturnPacket (Universal) - •••••••••• ReturnPacket: 'move.l dO,RTPacket ;Packet sichern move.l d0,a0 move.l dl,12(80) ;Packet->Res1 = dl move.l d2,16(a0) ;Packet->Res2 = d2 move.l (aO),RTHessage ;Message = Packet->dp Link move.l 4(aO),RTPort ;Port = P8cket->dpjx>rt move.l ExecBase,a6 move.l #0,a0 ;RTProcess = FindTask (0) jsr FindTask(a6) move.l dO,RTProcess add.l «92,dO move.l RTPacket,80 ;P8eket->dp_port = &Process-> move.l d0,4(a0) ; pr_HsgPort move.l RTHess8ge,80 move.l RTPecket,10(80) ; Message->im_Node.ln_Naine » ; Packet clr.l (aO) ;Message->nn_Node.ln_Succ = clr.l 4(80) ;Hessage->iin Node.ln Pred = ; NULL move.l ExecB8se,86 move.l RTPort,80 move.l RTHe8S8ge,8l ;PutMsg (RTPort,RTHess8ge) jmp PutMsg(a6) RTPort: ds.l 1 RTHessage: ds.l 1 RTProcess: ds.l 1 RTPecket: ds.l 1 In ReturnPacketOK wird zunächst Packet->dp_Resl auf -1 gesetzt. Packet->dp_Res2 bleibt erhalten. Diese Variable wird vom DOS beim Senden immer auf 0 gesetzt, so daß wir auch hier 0 in Packet- >dp_Res2 zurückgeben. "-1" in Resl und "0" in Res2 signalisiert dem DOS: Kein Fehler! Sollte einmal ein Fehler bei der Abarbeitung eines Packets auftauchen, sollten Sie dies auch dem DOS mitteilen. Dies geschieht durch Über¬ gabe des Wertes 0 in "Resl" und des DOS-Fehler-Codes in "Res2". 634 Amiga intern (Mit Hilfe der DOS-Routine IOError() ist es ja möglich, den Fehler- Code bei einer fehlgeschlagenen DOS-Funktion zu ermitteln. Dieser Fehler-Code hat den gleichen Wert wie "dp_Res2''). In unserem Programmbeispiel geben wir bei einem Fehler den Wert 202 in Res2 zurück. Dieser Fehler-Code steht für "OBJECT IN USE". Doch kommen wir nun dazu, was beim Returnieren eines Packets ge¬ schehen muß: Unsere ReturnPacketUniversal-Routine erwartet von Ihnen den Wert für "Packet->Resr in DO und den Wert für "Packet->Res2'' in Dl. Dann wird mittels FindTask() der Prozeß-Message-Port ermittelt, über den das Packet zurückgeschickt werden soll. Danach wird die Adresse des zurückzusendenden Packets in "Message->mn_Node.lnName" an¬ gegeben und das Packet über den MessagePort durch PutMsgO zu¬ rückgeschickt. Weil bekanntlich Messages verkettet werden können, werden bei dieser Message Vorgänger und Nachfolger gelöscht. So ist sichergestellt, daß nur eine eine Message, sprich Packet, gesandt wird. Nach diesen recht komplexen Mechanismen für die Verwendung von DosPackets wollen wir uns den einzelnen Packet-Typen widmen: (1005) ACTION_FIND_INUPUT Versuche File für lesenden Zugriff bu öffnen. Gib FileHandle in DosPacket- >Argl Burück. (1006) ACTION_FIND_OUTPUT Versuche File für schreibenden Zugriff EU öffnen. Gib FileHandle Burück in Do8Packet->Argl. (1007) ACTION_END Schließe offenes File. FileHandle des Files in DosPacket->Argl. Dem erfahrenen Programmierer wird auf fallen, daß die Werte 1005 und 1006 auch in Verbindung mit dem DOS-Open() Befehl verwendet werden. Sie stehen für MODE_OLDFILE (1005) und MODE_NEWFILE (1006). Wenn Sie einen eigenen Händler schreiben, sollten Sie darauf achten, daß die von Ihnen erzeugten FileHandles sinnvoll sind, da Sie anhand dieser das geöffnete File wieder schließen müssen. Wählen Sie jeweils verschiedene FileHandles. (82) ACTION_READ (87) Lies Daten aus File (Hier kann ein be¬ liebiges Device angesprochen werden). Schreibe Daten in File (Hier kann ein beliebiges Device angesprochen wer¬ den). ACTION WEITE Das AmigaDOS 635 (1001) ACTION_RETURN_READ Dieses Packet wird vom Device und (1002) ACTION_RETURN_WRITE nicht vom DOS gesendet. Es enthält die von ACTION_R angeforderten Daten (Argl). Dieses Packet wird vom Device und (20) ACTION_WAITCHAR nicht vom DOS gesendet. Es enthält die Fehlermeldung des Device- Schreibsugriff (Argl). Warte auf Zeichen, dieses wird su- (26) ACTION_DISKINFO rückgegeben Uber ACTION READ und ACTION_RETURN_READ . Infos Uber Disk holen (Rückgabe Uber (994) ACTIONSETRAWMODE Argl). Set Raw-Modus (s.B. Console-De- Ä ACTION DIE ACTION_TIMER vice) SchlieSe Device, verlasse Händler. Hole Zeit. Wenn ein Packet ein Ergebnis (z.B. aus einem lesenden Zugriff) ans E^S zurückgeben will, geschieht das über DosPacket->Argl. Dies ist ein BCPL-Zeiger auf die Daten. Als aufmerksamer Leser werden Sie sicher festgestellt haben, daß wir oben weniger Packet-Typen vorgestellt haben als in unserem Pro¬ grammbeispiel Vorkommen. Dies liegt daran, daß die übrigen Typen für FileSysteme verwendet werden. Beim FileSystem sieht auch schon das Startup-Packet anders aus als das für den Händler. In DosPacket->Argl wird Ihnen der Name des zu bearbeitenden FilesSystems (z.B. FAST, siehe FastFileSystem) über¬ geben. In DosPacket->Arg2 erhalten Sie die Adresse der FileSysStart- upMsg-Struktur (BPTR), auf die der Zeiger "DeviceNode- >dn_Startup" bekanntlich zeigt. In DosPacket->Arg3 erhalten Sie dann noch die Adresse der zugehörigen DeviceNode-Struktur. Hier die Packet-Types für FileSysteme: (1006) ACTION_FIND_INPUT OHne File (Lesen) Argl => enthält FileHandle (vom FileSystem erseugt) Arg2 => Directory Lock ArgS => Name des Files. (1006) ACTION FIND OUTPUT Offne File (Schreiben), Parameter s.o. ACTION FIND UPDATE Offne File, Parameter s.o. ACTIONREAD Lies Daten aus File (Read()-Funktion). Argl; Vorher er- seugtes FileHandle. ACTION_WRTIE Schreibe Daten in File (Write()-Funktion). Argl: Vorher erseugtes FileHandle. ACTIONSEEK Lese-/Schreibposition setsen (Seek()-Funktion). Argl; Vorher er¬ seugtes FileHandle. Hier- seigt sich. 636 Amiga Intern daB FileHandle linnvolle Daten ent¬ halten lollte (siehe ’libraries/dos.h’). ACTION_END Schließe File (Close()-Funktion) (Alle begonnenen Writes vollendeni) Argl; FileHandle. ACTION CREATE DIR ACTION_LOCATE_OBJECT ACTION_FREE_LOCK ACTION_COPY_DIR ACTION_EXAMNE_OBJECT ACTION_EXAMINE_NEXT ACTION_DISK_INFO ACTION INFO Erseuge neues Directory (CreateDir-FunktionO). Parameter: siehe ACTION_FIND OUTPUT. Erseuge Lock für File ^Lock()-F.). Gib Lock() wieder frei (Unlock()). Kopiere lÄck (DupLock()-Funktion). ExamineO-Funktion. ExNext()-Funktion. Info()-Funktion. Packet ist ACTION DISK INFO ACTION CURRENT VOLUME ähnlich. Es wird überprüft, ob Lock auf eine Diskette sich auf eine einge¬ legte (gemountete) Diskette besieht. ACTIONPARENT ACTIONRENAMEOBJECT ACTION_DELETE_OBJECT ACTION_SET_DATE ACTION_SET_COMMENT ACTION_SET_PROTECT ACTION_FLUSH ACTION_MORE_CACHE ACTION_RENAME_DISK ACTION INHIBIT Gibt Zeiger auf DosNode der Diskette (DLT_VOLUME) surück. ParentD ir() -Funktion. Rename()-Funktion. Delete()-Funktion. DateStamp()-Funktion- SetConunent()-Funktion. SetProtection()-Funktion. CMD_FLUSH-Kommando für Device. CLI-AddBuffers-Kommando. Rename()-Funktion. Volume BUS DeviceList entfernen (s.B. wenn aus Laufwerk entfernt). Es ist doch beachtlich, wie viele DOS-Funktionen über Händler abge¬ wickelt werden. Gerade das ist es, was das DOS so vielseitig macht. Sie können sowohl Open ("df0:text",1005) als auch Open ("CON;0/0/640/200/Test Window", 1005) programmieren. Obwohl das Ergebnis dieser beiden Funktionsaufrufe vollkommen verschieden ist - abgewickelt werden sie beide über den DOS-Open()-Befehl! 3.7 Devices In den Devices liegt eine der großen Stärken des Amiga-Betriebssy- stems. Es handelt sich dabei um Programmpakete, die einige Aufgaben übernehmen. Diese Aufgaben werden den Devices von einem laufen¬ den Programm erteilt, das dann entweder auf ein Ergebnis wartet oder Weiterarbeiten kann. Auf diese Weise kann ein Programm relativ ein¬ fach das Multitasking verwenden. Der grundsätzliche Aufbau solcher Devices wurde bereits im EXEC- Kapitel erklärt. Wir wollen uns nun mit der praktischen Anwendung Das AmigaOOS 637 befassen. Zunächst ein Blick auf die Vorgehensweise bei der Pro¬ grammierung, die in folgenden Schritten abläuft: 1. Da die Erledigung einer Aufgabe von der Device mittels einer Message gemeldet wird, muß der Empfänger dieser Message, eben das eigene Programm, zunächst einmal ermittelt werden. Dies wird mit der FindTask()-Funktion des EXEC erledigt, der man als Parameter eine Null übergibt. Der so erhaltene Wert wird dann im nächsten Schritt verwertet. 2. Für die Message der Device wird ein Port mittels der AddPort()- Funktion angelegt. Es handelt sich dabei um einen Reply-Port, was soviel wie Antwort-Port heißt. In dieser MessagePort- Struktur wird im Eintrag SigTask (Portadresse +$10) der eben erhaltene Zeiger auf die eigene Task-Struktur abgelegt. 3. Die Device wird mittels der OpenDevice()-Funktion geöffnet. Dabei muß ein Zeiger auf den Device-Namen und einer auf die I/O-Struktur übergeben werden, die dann zur Kommunikation mit dem Device-Programm benötigt wird. 4. In der I/O-Struktur werden die Parameter eingetragen, welche für die gewünschte Funktion nötig sind. Die Anzahl und Art der einzutragenden Parameter sind je nach Funktion sehr unter¬ schiedlich. 5. Die Arbeit der Device wird mit DoIo() oder SendIo() gestartet. Bei DoIo() wartet das aufrufende Programm auf das OK-Signal im Reply-Port, mit SendIo() wird die Arbeit der Device nur ge¬ startet, und das Programm kann Weiterarbeiten. Hier sind zwei Strukturen aufgetaucht, die die Kommunikation zwi¬ schen Anwender-Programm und Devices steuern. Dies sind die Port- und die I/O-Struktur, die an anderer Stelle bereits beschrieben wur¬ den. Hier noch einmal die Standard-Struktur der I/O-Operationen: Offset Name STRUCT MsgNode 0 Succ 4 Fred 8 Type 9 Pri 10 Name 14 ReplyPort 18 MNLength STRUCT lOExt 20 lODEVICE 24 IO_UNIT 28 IO_COMMAND 30 lOFLAGS 31 IO ERROR Bedeutung _ Zeiger auf nachfolgenden Eintrag. Zeiger auf vorhergehenden Eintrag. Eintragstyp Priorität Zeiger auf Namen. Zeiger auf ReplyPort. Node-Länge Zeiger auf Device-Node. Interne Einheitsnummer. Kommando Flags Fehlerstatus 638 Amiga intern STRUCT lOStdExt 32 IO_ACTUAL 36 lOLENGTH 40 IO_DATA 44 IO OFFSET Anzahl der übertragenen Bytee. Anzahl der zu übertragenden Bytes, Zeiger auf Datenpuffer. Offset (z.B. für Trackdisk-Device). 48 Hier beginnt die jeweilige erweiterte Struktur Die normalen I/O-Funktionen werden mit den Standard-Kommandos ausgelöst, die zu den I/O-Definitionen gehören. Diese Kommandos sind: (0) CMD_INVALID (1) CMDRESET 6 ) (7) ( 8 ) CMD_READ CMD_WRITE CMDUPDATE CMD_CLEAR CMDSTOP CMD_START CMD FLUSH Ungültiges Kommando. Zurücksetsen der Device auf den An- fangszustand. Lesen aus der Device. Schreiben in die Device. Aufarbeiten der Puffer. LSschen aller Puffer. Pause einlegen. Nach der Pause Weiterarbeiten. Abbruch der momentanen Arbeit. Zusätzlich ZU diesen Kommandos gibt es für jede Device einige wei¬ tere, die in den folgenden Beispielen erklärt werden. Auf einer normalen Workbench-Diskette befinden sich etliche Devices im DEVS-Unter-Directory. Einige weitere Devices sind nicht auf der Diskette zu finden und trotzdem verfügbar, da sie resident im Amiga liegen. Wir wollen uns nun mit der Programmierung der wichtigsten Devices anhand von Beispielen beschäftigen, die Sie in eigenen Programmen vielleicht gut gebrauchen können. 3.7.1 Trackdisk-Device: Zugriff auf Disketten Das Trackdisk-Device ist die vom Betriebssystem vorgesehene Verbin¬ dung zu den Disketten. Dies wird auch vom DOS verwendet. Es bietet die Möglichkeit des direkten Disketten-Zugriffes, ohne auf die Hard¬ ware-Register zugreifen zu müssen. Die erweiterte I/O-Struktur enthält folgende zwei Einträge (Lang¬ worte), die allerdings nur für erweiterte Kommandos nötig sind: Das AmigaDOS 639 lOTD COUNT Aneahl erlaubter Diskettenwechsel. IOTD_SECLABEL Zeiger auf Sektor-Header-Feld, wel¬ ches pro SU lesenden Sektor 16 Bytes groß sein tnu6. Diese Device besitzt eine große Anzahl von zusätzlichen Kommandos. Etebei wird noch zwischen den normalen und den erweiterten Track¬ disk-Kommandos unterschieden. Hier eine Liste aller gültigen Track- disk-Kommandos: Standard-Kommandos (*) CMD_READ (3) CMD_WRITE (4) CMD_UPDATE (5) CMD_CLEAR Trackdisk-Kommandos: (Sl TD MOTOR TD_SEEK (11) TD_FORMAT (12) TD_REMOVE (13) TD_CHANGENUM (14) TD_CHANGESTATE (15) TD_PROTSTATUS (16) TD_RAWREAD (17) TDRAWWRITE (18) TDGETDRIVETYPE 19) TD_GETNUMTRACKS 20) TD_ADDCHANGEINT (21) TD_REMCHANGEINT (22) TD_LASTCOMM Lesen eines oder mehrerer Sektoren von Diskette. Schreiben eines oder mehrerer Sekto¬ ren auf Diskette. Trackpuffer auf Diskette surUck- schreiben. Trackpuffer als ungültig erkl&ren. Ein-/Ausschalten des Motors. Schreib-/Lesekopf des Laufwerks auf einen bestimmten Track positionieren. Initialisierung eines oder mehrerer Tracks. Interrupt-Routine installieren, welche bei einem Diskettenwechsel aufgerufen wird. Ansahl der Diskettenwechsel ermit¬ teln. Feststellen, ob eine Diskette eingelegt ist. Feststellen, ob die Diskette schreib¬ geschützt ist. Lesen des unbearbeiteten Diskettenin¬ halts. Unbearbeitete Daten auf Diskette schreiben. Laufwerkstyp ermitteln (1 == 3)-, 2 = 5} Zoll). Gesamtzahl der Tracks ermitteln. Interrupt-Routine installieren, die bei Diskettenwechsel aufgerufen wird. Obige Routine abschalten. Letztes Kommando ermitteln. Erweiterte Kommandos (Nummern alle +32768 bzw. +$8000): Gleiche Funktion wie oben, jedoch nur, wenn kein Disketten-Wechsel vorge¬ nommen wurde: 640 Amiga intern (2) ETDREAD S ETD_WRITE ETD_UPDATE S ETD_CLEAR ETD_MOTOR (10) ETD_SEEK (11) ETDFORMAT (16) ETDRAWREAD (17) ETD_RAWWRITE Wir wollen uns nun ein kleines Maschinenprogramm ansehen, das die Trackdisk-Device anwendet. Ein einfaches Beispiel ist es hierfür, ein paar Sektoren von der Diskette in den Speicher zu laden. Wenn Sie im Besitz eines Assembler-/Debugger-Paketes wie z.B. dem Profimat oder dem K-SEKA sind, so können Sie sich das Ergebnis direkt ansehen. Andernfalls können Sie die erhaltenen Daten ja auch mittels des Open()- und Write()-Kommandos des AmigaDOS als Datei auf die Diskette schreiben und nachher mit TYPE und der Option H auf dem Bildschirm bzw. Drucker ausgeben lassen. Beachten Sie bitte vor dem Ausprobieren dieses Programms, daß das Laden von Diskettensektoren in den Speicher über DMA (Direct Me¬ mory Access) läuft. Aufgrund der Architektur der alten Amiga-Typen ist dies nur in den unteren 512 KByte, dem Chip-RAM, möglich. Sollten Sie also einen Rechner mit mehr als 512 KByte haben, so star¬ ten Sie bitte vor diesem Beispielprogramm das NoFastMem-Utility, das sich auf der Workbench befindet! ;*** Trackdisk-Device-Detno; Sektoren lesen 6/87 S.D. *** ExecBase = 4 ;EXEC-Basisadresse FindTask = -394 ;Task-Struktur suchen AddPort = -354 ;Port erstellen ReinPort = -360 ;Port entfernen OpenLib = -408 ;LibrBry öffnen CloseLib = -414 ;Library schließen OpenDev = -444 ;0evice öffnen CloseOev = -450 ;Device schließen Dolo = -456 ;I/0 starten und warten run: move.l execbase,a6 ;Zeiger auf EXEC-Bibliothek sub. l a1,a1 ;Eigener Task jsr FindTask(a6) ;Task suchen move.l dO,readreply«'S10 ;SigTask: eigener Task lea jsr readreply,a1 AddPort(a6} ;Add Reply-Port lea diskio.al ;I/O-Struktur move.l «0,d0 ;Laufwerk DFO: clr. l dl ;Keine Flags Das AmigaDOS 641 lea trddevice,aO ;Device-Name jar 0perDev(a6) ;Open trackdisk.device tst.l 40 ;0K7 bne error ;Nein: Fehler aufgetreteni lea diskio,a1 move.l #readreply,U(a1) ;set Reply-Port move #2,28(a1) ;Coninand: READ move.l #diskbuff,40(a1) ;Puffer move.l #2*512,36(a1) ;Länge: 2 Sektoren move.l #880*512,44(01) ;0ffset: 880 Sektoren (Root) move.l execbase,a6 ;EXEC-Basisadresse jsr DoIo(a6) ;Sektoren lesen! move.l diskio*32,d6 ;I0_ACTÜAL in D6 lea diskio,a1 move «9,28(a1) ;Coramand: TD_HOTOR move.l «0,36(a1) ;Notor aus- jsr DoIo(a6) ;schalten! lea readreply,a1 Jsr RenPort(a6) ;Port entfernen lea diskio,a1 closedev(a6) Jsr ;Trackdisk-Device schlieBen error: rts ;Ende trddevice: dc.b 'trackdisk.device',0 even diskio: blk.l 20,0 readreply; blk.l 8,0 diskbuff; blk.b 512*2,0 In diesem Beispiel werden die Sektoren 880 und 881 von Laufwerk 0 in den Speicher ab "diskbufP geladen. Bei dem Sektor 880 handelt es sich um den Root-Block, der ja den Diskettennamen und einiges mehr enthält. Danach wird noch einmal DoIo() aufgerufen, um den Laufwerksmotor wieder auszuschalten (für’s Einschalten hätte eine 1 in 36(A1) ge¬ schrieben werden müssen). Zum Ablauf des Programms: Mit dem Aufruf der FindTask()-Funktion, der als Argument in Al eine Null übergeben wird, wird der Zeiger auf die eigene Task- Struktur ermittelt. Dieser Zeiger wird dann in die Port-Struktur ein¬ getragen, damit das System weiß, welcher Task nach Beendigung der 642 Amiga intern I/O wieder "aufgeweckt" werden soll. Als nächstes wird der so vorbe¬ reitete Port im System installiert. Nun wird das Trackdisk-Device geöffnet. In DO können Sie hierbei wählen, auf welches Laufwerk sich diese Funktion beziehen soll. Wol¬ len Sie mehrere Laufwerke gleichzeitig bearbeiten, müssen Sie mehrere I/O-Strukturen vorbereiten und OpenDevice()-Aufrufe machen. Tritt beim öffnen der Device ein Fehler auf, so wird zum Label "error" verzweigt, wo das Programm beendet wird. Andernfalls wird nun die I/O-Struktur mit den notwendigen Daten versehen: Dem Zeiger auf die Port-Struktur, über die die OK-Meldung der Device erfolgen soll. Das Kommando, welches bei der nächsten I/O-Operation aus¬ geführt werden soll (hier: 2=CMD_READ). Einem Zeiger auf den zu füllenden Pufferspeicher. Der Länge dieses Speichers. Für das Lesen von Sektoren die Angabe des Offsets vom Anfang der Diskette, was der Sektor- bzw. Blocknummer*512 entspricht. Die so vorbereitete I/O-Struktur wird nun mit DoIo() dem System übergeben und die gewählte Funktion ausgelöst. Das Programm wartet dann solange, bis die I/O-Operation beendet ist. Ein eventueller Rückgabeparameter, wie etwa beim TD_PROTSTATUS-Kommando, wird dann in IO_ACTUAL übergeben und wird mit dem folgenden MOVE.L-Befehl ins Datenregister D6 geladen. In obigem Beispiel ist dies der Wert $400, was der Anzahl der gelesenen Bytes entspricht. Das Datenregister wird zwar nicht weiter verwendet, kann aber bei Verwendung eines Debuggers ausgegeben und abgelesen werden. Anschließend folgt ein weiterer Aufruf der DoIo()-Funktion, diesmal mit dem Kommando TD_MOTOR (9). Der Parameter in IO_LENGTH (diskio-(-36) gibt dabei an, ob der Motor ein- (1) oder ausgeschaltet (0) werden soll. Ist dies erledigt, so wird der Port abgemeldet und das Device ge¬ schlossen. Das war’s. Sollten Sie dieses Programm im Profimat oder K-SEKA gestartet ha¬ ben, so werden nach dessen Beendigung die Prozessor-Register ausge¬ geben. In D6 finden Sie dann den Rückgabeparameter $400. Sie kön¬ nen nun außerdem durch die Eingabe von "q diskio+432" (SEKA) den Das AmigaDOS 643 Diskettennamen ausgeben lassen, wobei das erste Byte die Länge des Namens angibt. Sollte eine Funktion nicht so recht klappen, so wird ein Statuswert im IO_ERROR übergeben. Die möglichen Werte sind hierbei: 20 NotSp«cified Unbekannter Fehler. 21 NoSecHdr Kein Sektor-Header vorhanden. 22 BadSecPreamble Ungültiger Sektor-Vorspann. 23 BadSecID Ungültige Sektor-ID. 24 BadHdrSum Falsche Header-Checksumme. 26 BadSecSum Falsche Sektor-Checksuznme. 26 TooFewScci Nicht genug Sektoren verfügbar. 27 BadSecHdr Ungültiger Sektor-Header. 28 WriteProt Diskette schreibgeschütst. 20 DiskChanged Diskette ist gewechselt worden. SO SeekError Track nicht gefunden. 31 NoMem Nicht genug Speicher. 32 BadUnitNum Ungültige Sektornummer. SS BadDriveType Ungültiger Laufwerkstyp. 34 DrivelnUsc Laufwerk bereits aktiv. 36 PoatBeset Reset-Phase. Dies war die eine Richtung. Die andere, also das Schreiben von Daten auf die Diskette, läuft genauso ab, nur daß das Kommando auf 3 (CMD_WRITE) geändert werden muß. Probieren Sie dies aber bitte nur auf weniger wichtigen Disketten aus, da bei einem Fehler eventu¬ ell Daten verlorengehen können... Ein anderes Kommando ist recht interessant: TD_FORMAT. Mit die¬ sem Kommando können ein oder mehrere Tracks auf der Diskette beschrieben werden. Die Daten, die im Speicher bereitliegen müssen und auf die der IO_DATA-Zeiger zeigt, werden dann in jeden der angegebenen Tracks geschrieben, ohne daß irgendein Test auf Disket¬ tenwechsel o.ä. stattfindet. Man kann also mit diesem Kommando nicht nur Disketten formatieren, sondern auch kopieren, indem man aus der einen Diskette die Sektoren liest und mit TD_FORMAT diese Daten auf die Ziel-Diskette zurückschreibt. Der Vorteil dieser Me¬ thode gegenüber dem Zurückschreiben mit CMD_WRITE ist einfach der, daß die Zieldiskette nicht vorformatiert werden muß! Wenn Sie allerdings eine ganze Diskette lediglich neu formatieren wollen und nicht das FORMAT-Kommando des CLI verwenden wol¬ len, sollten Sie eines bedenken: Die Daten für die Tracks müssen in einem bestimmten Format vorbereitet sein (s. Disketten-Kapitel), was recht viel Arbeit bedeutet. Der Weg über das TD_FORMAT-Kom- mando der Device ist daher so mühsam, daß dies nicht sinnvoll er¬ scheint. 644 Amiga intern Wir wollen uns nun eine Anwendung des Trackdisk-Device ansehen, bei der auch die Kenntnisse der Disketten-Aufteilung angewandt wer¬ den können. Das gleich vorgestellte Maschinenprogramm kann als Dia¬ gnose-Programm verwendet werden, entweder aus Neugierde oder auch zum Feststellen, welche Dateien bei einem Diskettenfehler ver¬ lorengehen oder -gingen. Das Programm wird vom CLI aus aufgerufen, wobei als Parameter ein Dateiname mit angegeben werden muß. Es errechnet aus diesem Na¬ men die Hash-Nummer (wie versprochen) und gibt diese aus. Danach lädt es den Root-Sektor der Diskette und gibt den Diskettennamen aus. Mit Hilfe der errechneten Hash-Nummer wird nun die Hash- Kette nach dem angegebenen Namen durchsucht. Wird er nicht gefun¬ den bzw. ist der Eintrag in der Hash-Tabelle unbelegt, so wird "- unknown-" ausgegeben und abgebrochen. Wird der passende File- bzw. Directory-Header gefunden, so wird dessen Block-Nummer sowie die Anzahl der durch diese Datei beleg¬ ten Datenblocks ausgegeben. Es werden dann alle diese Datenblocks der Reihe nach geladen und ihre Nummern ausgegeben. Es wäre zwar auch möglich, diese Block¬ nummern dem File-Header-Block und evtl, dessen Extension-Block zu entnehmen, jedoch wäre dadurch nicht die Sicherheit gegeben, daß diese Blocks auch in Ordnung sind. Sollte der Requester mit der Mit¬ teilung "Disk-Structure Corrupt" auf tauchen, können Sie mit diesem Programm Ihre wichtigen Dateien auf Vollständigkeit testen. Um die Übersichtlichkeit des Programms zu erhalten, ist es allerdings nicht möglich, Dateien aus Unter-Directories zu testen. Dies ist mög¬ lich, wenn man das Programm so ändert, daß es die Hash-Tabelle ei¬ nes Directory-Header-Blocks anstelle des Root-Blocks verwendet. Hier nun das Programm, in dem einige interessante Funktionen auf¬ tauchen, die Sie auch leicht in Ihren eigenen Programmen verwenden können. Das Programm wurde auf dem K-SEKA-Assembler erstellt, eine Anpassung auf einen anderen Assembler ist aber recht einfach. Bitte beachten Sie bei der Verwendung dieses Programms, daß es nicht im Fast-RAM des Amiga läuft, da es in seinen Datenbereich über das Trackdisk-DMA lädt. Haben Sie also einen Amiga mit Fast- RAM, so starten Sie vorher das NoFastMem-Programm! Das AmigaDOS 645 File-Tracer 6/87 S.D. ***** ExecBase = 4 ;EXEC-Basisadresse FindTask = -294 ;Task-Struktur suchen AddPort = -354 ;Port erstellen RenPort = -360 ;Port entfernen OpenLib = -408 ;Bibliothek öffnen CloseLib = -414 ;Bibliothek schließen OperDev = -444 ;Device öffnen CloseDev = -450 ;Device schließen Dolo = -456 ;I/0 starten und warten output = -60 :Standard-Ausgabe ermitteln urite = -48 ;Daten ausgeben run: move.l aOjCommpnt n»ve. l dO,coninlen n»ve. l execbase,a6 lea dosname.al ;Nanie: dos.library clr. l dO jsr openlib'z'? bhj upperx ;Ja: so lassen sub upperx: #$20,d2 ;Sonst korrigieren rts ;Fertig trddevice: dc.b 'trackdisk.device '.0 dosname: dc.b 'dos.library'.O hashtxt: dc.b $a, 'Hashniin: ■ voltxt: c.b Sa,'Volume: ' unknoun: c.b Sa,'-unknoun- ' header: c.b Sa,'Header: ' extxt: c.b Sa,'Extension: ' dirtxt: c.b Sa,'Directory ',Sa sectxt: c.b 'Sektoren: ',$a crtxt: c.b ' ■ ',Sa data even outpuff: blk.b 6 sector: blk.l 1 Counter: blk.w 1 dosbase: blk.l 1 outbase: blk.l 1 hash: blk.u 1 commpnt: blk.l 1 conmlen: blk.l 1 diskio: message; blk.b 20,0 io: blk.b 12,0 ioreq; blk.b 16,0 readreply: blk.l 8,0 diskbuff: blk.b 512,0 ;Puffer für Hexzahlen-Ausgabe ;Sektor-Zwischenspeieher .-Zähler für Ausgabeformatierung ;DOS-Basisadresse ;Standard-Ausgabe-Handle ;Nash-Nummer ;Zeiger auf Eingabezeile ;Länge der Eingabezeile ;Disk-I/O-Struktur Auf diese Weise läßt sich auch ein Programm schreiben, das eine Da¬ tei von der Diskette lädt, ohne dabei das DOS zu bemühen. Es müssen dann nur die eigentlichen Daten aus den Datenblocks in den Speicher kopiert werden. 3.7.2 Consol-Device: Editor-Fenster Dieses Device, mit dem Fenster für Tastaturein- und -ausgaben vor¬ bereitet und bearbeitet werden können, fällt ein wenig aus dem Rah¬ men der Standard-Devices. Es kann nämlich nicht so einfach für sich geöffnet und verwendet werden, sondern muß in Verbindung mit ei- 650 Amiga intern nein Fenster stehen. Dieses Fenster dient dann für die Ein- und Aus¬ gaben der Console-Device. Bevor also das Device selbst geöffnet werden kann, müssen wir erst einmal ein Fenster öffnen. Dafür müssen wir die Intuition-Bibliothek öffnen, einen Screen und dann das Fenster öffnen. Den dadurch er¬ haltenen Zeiger auf die Fenster-Struktur übergeben wir dann beim öffnen der Console-Device. Wir erhalten somit also ein Fenster auf einem eigenen Screen, in dem ein Cursor in der linken oberen Ecke zu sehen ist. Dieser Cursor hat allerdings noch keine Funktion, wir müssen die Tastatur-Eingabe und die Ausgabe der Zeichen in das Fenster erst einmal programmieren. Wir benötigen also zwei I/O-Strukturen, eine für die Eingaben und eine für die Ausgaben. Dazu gehören natürlich auch zwei Message- Ports, damit das Device auch erfährt, wohin bzw. woher die Daten kommen sollen. Bevor wir nun mit der doch recht trockenen Theorie fortfahren, soll¬ ten Sie sich erst einmal das folgende Programm ansehen, das die oben beschriebenen Schritte vornimmt. Es öffnet einen Screen und ein Fenster, in dem dann über die Consol-Device Ein- und Ausgaben laufen. Dabei werden lediglich die über die Tastatur eingegebenen Zeichen im Fenster wieder ausgegeben, wobei Return und Backspace speziell behandelt werden. Wird die Close-Box des Fensters mit der Maus angeklickt, so wird das Programm beendet. Weitere Aktionen mit der Maus können zusätzlich ausgewertet werden, die entspre¬ chende Erkennung eines Maus-Klicks ist vorbereitet. Hier das Programm: ** Demo-Programn zur Console-Device 6/87 S.D. ** openlib = -408 ;Library öffnen closelib = -414 ;Library schlieBen AddPort = -354 ;Port erstellen RemPort = -360 ;Port entfernen OperDev = -444 ;Device öffnen CloseDev = -450 ;Device schließen execbase = 4 ;EXEC-Basisadresse GetMsg = -372 ;Message holen FindTask = -294 ;Task ermitteln Dolo = -456 ;I/0 ausführen Sendio = -462 ;I/0 starten ** Intuition-Funktionen ** Das AmigaDOS openscreen = -198 ;Screen öffnen closescreen = -66 ;Screen schließen openwindow = -204 /Fenster öffnen closewindow = -72 /Fenster schließen run: bsr openint /Intuition öffnen bsr scropen /Screen öffnen bsr windopen /Fenster öffnen move. l execbase,a6 /Zeiger auf EXEC-Bibliothek sub. l a1 ,a1 /Eigener Task jsr FindTask(a6) /Task suchen move. l dO.readreply+SIO /SigTask setzen lea readreply,a1 jsr AddPort(a6) /Add Read-Reply-Port lea Mriterep.al jsr AddPort(a6) /Add Urite-Reply-Port lea readio,a1 move.l uindowhd,readjo+$28 /Unser Uindow move.l #48,readio+$24 /Länge der Struktur clr.l dO clr.l d1 lea devicename,aO jsr 0pen0ev(a6) /Open Consol-Device tst. l dO bne error move.l readio+$14,Hriteio+$14 . /DEVICE und move.l readio+$18,Hriteio+$1£ 1 /UNIT kopieren go: bsr queueread /Eingabe anwerfen loop: /* Ereignisse auswerten * move. l execbase,a6 move.l windowhd.aO move.l 86(aO),aO /Fenster-User-Port jsr GetMsg(a6) tst. l dO bne wevent /Fenster-Ereignis lea readreply,aO jsr GetMsg(a6) /Console-Ereignis (Taste)? tst. l dO beq loop /Kein Ereignis cevent: /* Taste verarbeiten * bsr conout /Zeichen ausgeben cmp.b )ll$d,buffer /Return? bne nol /Nein move.b #$a,buffer /Sonst LF ausgeben 651 652 Amiga intern bsr conout nol: cnp.b «$8,buffer ;Backspace? bne no2 move.b #' '.buffer ;Sonst Zeichen löschen bsr conout move.b #8.buffer bsr conout ;und wieder zurück no2: bra go ;und so weiter wevent: ;• Fenster-Ereignis auswerten * move.l dO,aO move.l $16(a0),d6 ;Message in D6 cmp. l #$2000000,d6 ;U{ndow-Close? beq ende ;Ja: Ende ;* Hier kann eine weitere Auswertung stattfinden: * move.l windowhd,aO move.l 12(aO),dS ;Mausposition in D5 ;* z.B. Cursor setzen auf Maus- ■Position... • ende: ;• Progranmende: alles schließen lea readreply,a1 jsr RemPort(a6) ;Remove Port lea readio,a1 jsr closedev(a6} ;Close Device lea writerep.al jsr RemPort(a6] ;Renx)ve Port error: bsr windelose ;Fenster schließen bsr scrclose ;Screen schließen bsr closeint ;Intuition schließen rts ;* ENDE • ;** Unter ■Routinen ** queueread: ;• Consol-Eingabe starten * move.l execbase,a6 lea readio.ai move #2,28(a1) ;Command: READ move.l #buffer,Ä0(a1) .-Puffer move.l #1,36{a1) ;Länge: move.l #readreply,14(a1) ;set Reply-Port jsr sendIo(a6) ;Funktion auslösen « rts conout: ;* 1 Zeichen ausgeben * move.l execbase,a6 lea writeio,a1 Das AmigaDOS 653 tnove #3,28(a1} /Comand: URITE move. l #buffer,40(a1} /Puffer move. l #1,36(a1) /Länge: tnove. l #writerep,14(a1) /set Reply-Port jsr rts DoIo(a6) /Funktion ausführen openint: /* Intuition öffnen * move. l execbase,a6 lea intnaine,a1 /Library-Name jsr openlib(a6} tnove. l rts dO,intbase closeint: /• Intuition schließen tnove. l execbase,a6 tnove. l intbase,a1 jsr rts closelib(a6) scropen: /* Screen öffnen * move.l intbase,a6 lea screen_defs,a0 jsr openscreen(a6) move.l rts d0,screenhd scrclose: :* Screen schließen * move.l intbase, s6 move.l screenhcl,a0 jsr rts closescreen(s6} windopen: /• Fenster öffnen • tnove. l intbase,s6 lea windowdef,a0 jsr openwindow(s6} move.l rts d0,windowhd windclose: /• Fenster schließen • move.l intbase,a6 move.l windowhd,a0 jsr closewin^w(a6} rts screen_defs: dc.w 0,0 dc.w 640,200 dc.w 4 dc.b 0,1 dc.w $800 dc.w 15 dc.l 0 dc.l titel dc.l 0 dc.l 0 ;* Screen-Struktur * ;Position ;Größe ;Bit-Haps /Farben /Modus .‘Typ /Standard-Zeichensatz /Screen-Titel /Standard-Titel /Keine Gadgets 654 Amiga intern uindowdef: dc.w 10,20 dc.u 300,150 dc.b 0,1 dc.l $208 dc.l $100f dc.l 0 dc.l 0 dc.l windname screenhd: dc.l 0 dc.l 0 dc.u 100,50 dc.u 300,200 dc.u Sf ;• Fenster-Struktur * ;Position ;GrölSe ;Farben ;IDCMP-Flags ;Ujndou-Flags ;Keine Gadgets ;Keine Henu-Checks ;Fenstername ;Screen-Struktur-Zeiger ;Keine Bit-Map ;Mindestgrc>Be ;HöchstgröBe ;Screen-Typ titel: dc.b "Editor-Screen“,0 uindname: dc.b “Console-Fenster'^O intname: dc.b "intuition.library“,0 devicename: dc.b 'console.device',0 even uindouhd: blk.l 1 intbase: blk.l 1 conbase; blk.l 1 readio: message; blk.b 20,0 io: blk.b 12,0 ioreq: blk.b 16,0 uriteio: blk.b 20,0 blk.b 12,0 blk.b 16,0 readreply: blk.l 8,0 uriterep: blk.l 8,0 buffer: blk.b 80,0 Die Sequenzen, die diverse Funktionen im Fenster auslösen können, sind dieselben wie bei den mittels des DOS geöffneten RAW:- bzw. CON:-Fenstern. 3.7.3 Narrator-Device: Sprachausgabe Der Narrator, was auf englisch soviel wie Erzähler heißt, ermöglicht dem Amiga, sich verbal auszudrücken, sprich zu reden. Dies ist sicher ein sehr interessanter Punkt, mit dem man seine Programme buchstäb¬ lich ansprechender gestalten kann. Der Narrator ist ein Programmpaket, das als Device aufgebaut ist. Man kann so einen Text sprachlich ausgeben lassen und währenddes- Das AmigaDOS - 655 sen Weiterarbeiten. Die Extended-I/O-Struktur der Narrator-Device ist folgendermaßen aufgebaut: Wort _ Name _ 0 RATE 1 PITCH 2 MODE 3 SEX 4 CHMASKS 6 NUMMASKS 7 VOLUME 8 SAMPFREQ 9 MOUTHS CHANMASK Bedeutung Sprechgeichwindigkeit Worte/Minute. Sprachgrundfrequeni in Herta. Modua (0 = mit, 1 = ohne Betonung). Geechlecht (0 = minnl., 1 = weibl.). Zeiger auf Kanal-Maekenfeld. Anaahl der Kanal-Masken. Lautstärke Abtastrate Munderaeugungs-Flag (Byte) Akt. Kanal (nur interne Bedeutung). Die Programmierung der Narrator-Device ist so ähnlich wie die der anderen Devices. Was jedoch dazukommt, ist die Verwendung des Translators, der einen normalen Text in die Lautschrift für den Nar¬ rator übersetzt. Dieser Translator ist kein Device, sondern eine Bi¬ bliothek, die nur eine einzige Funktion enthält. Hier ein Maschinen-Programm, das einen Beispieltext ausgibt. Mit diesem Programm können Sie schön experimentieren, da Sie die Para¬ meter beliebig ändern und das Ergebnis testen können. Vorteilhaft ist hier ebenfalls die Verwendung eines Assemblers mit eingebautem De¬ bugger wie dem Profimat oder dem K-SEKA, auf dem dieses Pro¬ gramm geschrieben wurde. Narrator-Demo 6/87 S.D. ••••• ExecBase =4 ;EXEC-Basisadresse FindTask =-294 ;Find Task AddPort =-354 ;Add Port RemPort =-360 ;Remove Port OpenLib =-408 ;Open Library closelib =-414 ;Ctose Library OpenDev =-444 ;Open Device CloseDev =-450 ;Close Device Dolo =-456 ;Do I/O SendIo = -462 ;Send I/O Translate =-30 ;Translate Text run: System initialisieren und öffnen ** move.l execbase,a6 lea transname.al clr. l dO jsr openlib(a6) ;0pen Translator-Library move.l d0,tranbase beq error sub. l a1 ,a1 ;Task-Nunner = 0: eigener Task 656 Amiga intern jsr FindTask(a6) ;Find Task move.l d0,writerep<’$10 ;set SigTask lea writerep,a1 jsr sdc^rt(a6) ;Add Reply-Port lea talkio,a1 clr.l dO clr. l dl lea nardevice,aO jsr opendev(a6) ;Open Narrator.device tst.l dO bne . error lea talkio,a1 move.l lllwriterep,14(a1) ;set Reply-Port (*) fflove #1S0,48(a1) ;Rate (40-400) fflove #110,50(a1) ;Pitch (65-320) move «0,52(a1) ;Mode: mit Betonung (0/1) move «0,S4(a1) ;Geschlecht: männlich (0/1) move. l #afflaps,S6(a1) ;Masks (*) move #4,60(a1) ;4 Masken (*) move #64,62(a1) ;Lautstärke (0-64) move «22200,64(a1) ;Abtastrste (5000-28000) sayit: Text übersetzen und sagen lea intext,a0 ;Ori9inal-Text move.l «out text • i ntex t, dO ;0essen Länge lea outtext,a1 ;Puffer für Übersetzung move.l «512,dl ;Dessen Länge move.l trsnbase,a6 jsr Translste(s6} ;Text übersetzen lea talkio,s1 move «3,28ort(a6) ;Ren)ove Port lea talkio.al jsr closedev(a6) ;Close Narrator move.l tranbase,a1 jsr closelib(a6} ;Close Translator-Lib clr.l dO error: rts transname: dc.b 'translator.library',0 * nardevice: dc.b 'narrator.device',0 amaps: dc.b 3,5,10,12 Das AmigaDOS 657 intext: dc.b 'hello, i am the amiga conputer.'.O even outtext: blk.l 128,0 tranbase: blk.l 1,0 narread: blk.l 20,0 talkio: blk.l 20,0 uriterep: blk.l 8,0 In der Vorbereitung des I/O-Bereiches für die verschiedenen Modi bzw. Raten sind nur die Werte, die im obigen Listing mit einem Sternchen versehen sind, unbedingt einzutragen. Alle anderen werden automatisch auf die Standardwerte gesetzt (Default). Diese Werte sind auch im Programm vorgegeben, sie können in den in Klammern ange¬ gebenen Grenzen variiert werden. Das Narrator-Device bietet zusätzlich die Möglichkeit, während der Sprachausgabe Daten an das aufrufende Programm zu übertragen. Dies ist natürlich nur dann möglich, wenn die Sprachausgabe nicht mit DoIoO, sondern mit SendIo() gestartet wird, damit auch während der laufenden Sprachausgabe die Daten empfangen werden können. Die so empfangenen Daten stellen ein Bit-Muster dar, das auf dem Bildschirm ausgegeben einen Mund darstellt, der den gerade gespro¬ chenen Lauten entspricht. Diese Möglichkeit soll hier aber nicht näher erläutert werden, da dies mit Grafikausgaben verbunden ist, die ja in ein Grafikbuch gehören. Es sei nur erwähnt, daß diese Grafik recht primitiv ist, da sie den sprechenden Mund nur als entsprechend hohes Parallelogramm darstellt. Die Breite und Höhe dieser Form bekommt man vom Device in der Erweiterung der Read-Request-I/O-Struktur. Diese Erweiterung hat folgenden Aufbau: _ Name _ Inhalt _ MRB_WIDTH Breite der "Mundform". 49 MRBHEIGHT Höhe. MRB_SHAPE internes Daten-Byte. MRB_PAD Füll-Byte fUr gerade Adresse. Die I/O-Funktion kann auch schiefgehen. Die sich ergebenden Feh¬ lermeldungen können folgende Werte annehmen: Narrator-Fehlermeldungen Nummer _ Bedeutung _ -2 Nicht genug Speicher. ■3 Audio-Device nicht vorhanden. -4 Library nicht erstellbar. 658 Amiga intern -5 Falsche Unit-Nummer in der I/O-Struktur (nur 0). -6 Keine Audio-Kanäle verfügbar. -7 Unbekanntes Kommando. -8 Mund-Daten gelesen, aber nicht geschrieben. -9 Kein öffnen möglich. -20 Lautschrift unaussprechlich. -21 Ungültige Rate. -22 Ungültiges Pitch. -23 Ungültiges Geschlecht. -24 Ungültiger Modus. -26 Ungültige Abtastrate. -26 Ungültige Lautstärke. 3.7.4 Serial-Device: Die RS232-Schnittstelle Dieses Device ist für die serielle Kommunikation mit der Außenwelt zuständig. Auch diese Ein- und Ausgaben über den seriellen Port des Amiga lassen sich mit normalen DOS-Funktionen realisieren, wenn man als Dateinamen SER: angibt. Diese Methode hat jedoch einige große Nachteile. Der übliche Nachteil der DOS-Verwendung ist bekanntlich der, daß die Ein-/Ausgaben nicht im Hintergrund laufen und man somit auf deren Beendigung warten muß. Dies läßt sich bei Device-Program¬ mierung mittels der SendIo()-Funktion vermeiden. Ein weiterer Nachteil, der gerade in diesem Fall auftritt, besteht darin, daß man die seriellen Parameter wie z.B. die Übertragungsrate vorher mit dem Preferences-Programm eingestellt haben muß. Das Serial-Device bietet hierfür eine eigene Funktion, mit der alle Parameter eingestellt werden können. Für diese und die anderen Funktionen steht wieder eine erweiterte I/O-Struktur zur Verfügung, die folgende Einträge besitzt (Standard-Werte in Klammern): Offset_lOName_ Inhalt 0 CTLCHAR KontrollMichen: xON, xOFF, frei, frei ($11130000). 4 RBUFLEN BingabepufTer-LSnge ($200). 8 WBUFLEN Ausgabepuffer-LSnge ($200). 12 BAUD Baudrate (9600). 16 BRKTIME Break-Länge in Mikrosekunden (250000). 20 TERMARRAY Abbruchseichenfeld (8 Bytes). 28 READLEN Bits pro Zeichen beim Lesen (8). 20 WRITELEN Bits pro Zeichen beim Senden (8). 30 STOPBITS Ansahl der Stopp-Bits (1). 31 SERFLAGS Seriell-Flags (s.u.) ($20). 32 STATUS Statuswort Das AmigaDOS 659 Die Bits des zurückgegebenen Statuswortes sind folgende: Bit Wenn Dann 0 0 Busy, Übertragung ISuft. 1 0 Paper out, Empfünger nicht bereit 3 0 Select, angew&hlt. 3 0 Data Set Ready (DSR). 4 0 Clear To Send (CTS). 5 0 Carrier Detect (CD). 6 0 Ready To Send (RTS). 7 0 Data Terminal Ready (DTR). 8 1 Read overrun, Puffer-Überlauf. 9 1 Break sent, Break gesendet. 10 1 Break received, Break empfangen. 11 1 Transmit x-OFFed, xOFF gesendet. 13 1 Receive x-OFFed, xOFF empfangen. 13-15 Reserviert. Die Bits des Flags in IO_SERFLAGS haben folgende Bedeutungen; Bit Name Bedeutung, wenn gesetst 0 PARITY ON Panty-Bit gewünscht. 1 PARITY_ODD Ungerade Parity. 3 Unbenutst. 3 QUEUEDBRK Break im Hintergrund. 4 RAD BOOGIE Hochgeschwindigkeitsmodus ein. 5 SHARED Allgemeiner Zugriff ermöglicht. 6 EOFMODE EOF-Erkennung eingeschaltet. 7 XDISABLED xON/xOFF ausgeschaltet. Serial-Device sätzliche: besitzt außer den Standard-Kommandos noch drei zu- Zahl SDCMD Name Funktion 9 QUERY 10 BREAK Break senden. 11 SEPARAMS Parameter einstellen. Um vor allem das letzte dieser Kommandos, SDCMD_SETPARAMS, zu demonstrieren, folgt nun wieder ein Beispielprogramm. In diesem Programm wird die Baudrate auf 1200 eingestellt und dann der be¬ rühmte Text "Hello, world!" gesendet. ;***** Serial-Device-Demonstration 6/87 S.D. ***** ExecBase FindTask = 4 = -294 ;EXEC-Basisadresse ;Task-Struktur suchen 660 Amiga intern AddPort = -354 ;Port erstellen Reimport = -360 ;Port entfernen OpenLib = -408 .-Bibliothek öffnen CloseLib = -414 ;Bibliothek schließen OpenDev = -444 ;Deyice öffnen CloseDev = -450 ;Device schließen Dolo =-456 ;l/0 starten und warten output = -60 ;Standard-Ausgabe ermitteln write = -48 .-Daten ausgeben nun: move.l execbase,a6 ,-Zeiger auf EXEC-Bibl iothek sub. l a1 ,a1 ;Eigener Task jsr FindTask(a6) ;Task suchen move.l d0,reply+$10 ,-SigTask setzen lea reply.al jsr AddPort(a6) ;Reply-Port erstellen lea devio,a1 ,-Zeiger auf I/O-Struktur clr. l dO clr.l dl lea devicename,aO jsr 0penDev(a6} ;Serial-Device öffnen tst.l dO ,-OK? bne error ,-Nein: Ende lea devio,a1 ;Zeiger auf I/O-Struktur move.l #reply,14(a1} ;set Reply-Port move #11,28(a1) ;Command: SETPARAMS move.l #1200,ioextd+12 ;1200 Baud jsr 0olo(a6} ;Parameter setzen move #3,28(a1} ;Command: URITE move.l #text,40(a1} ;Puffer move.l #textl,36(a1) ;Länge: jsr 0olo(a6) ;Text senden lea reply.al jsr RemPort(a6) ;Remove Port lea devio,a1 jsr CloseDev(a6) ;Close Device error: rts ;Ende devicename: dc.b 'serial.device',0 text; dc.b 'hello, world!' textl = *-text even devio: message: blk.w 10,0 io: blk.w 6,0 Das AmigaDOS 661 ioreq: blk.w 8,0 ioextd: blk.w 17,0 reply: blk.w 8,0 Auf diese Weise können natürlich auch mehr Parameter der RS232- Schnittstelle eingestellt werden. 3.7.5 Printer-Device: Drucker-Programmierung Auch der Drucker läßt sich noch auf andere Weise als über den Kanal PRT: ansprechen. Dafür dient das Printer-Device, das außer dem normalen Druckbetrieb noch eine weitere interessante Fähigkeit be¬ sitzt, nämlich die Ausgabe eines Fenster- oder Screen-Inhaltes auf dem Drucker. Für den normalen Drucker-Betrieb wird folgende I/O-Struktur-Er- weiterung mit dem Namen lOPrtCmdReq benötigt: Offset Name Inhalt 33 io^PrtCommand Druckerbefehl 34 io_ParamO Kommando-Parameter 35 io_Paraml Kommando - P arame ter 36 io__Param2 Kommando-Parameter 37 io__ParamS Kommando-Parameter Hier werden die Steuer-Kommandos an den Drucker übergeben, die Schriftarten etc. einstellen. Dies wird über den PRTCOMMAND-Be- fehl ausgegeben. Die möglichen Kommandos, die bei diesem Device zu den Standard- Kommandos beigefügt sind, sind folgende: Wert _ Name _ Funktion _ 9 PRD_RAWWRITE Ausdrucken ohne Konvertierung von Steuerseichen. 10 PRD_PRTCOMMAND Druckerkommsndo senden. 11 PRD_DUMPRPORT Ausdruck eines Screen- oder Fenster- Inhalts. Wird nicht RAWWRITE verwendet, sondern werden die Daten über die normale WRITE-Funktion ausgegeben, so werden die Amiga-Stan- dard-Steuerzeichen je nach installiertem Drucker in die druckerspezi¬ fischen Steuerzeichen umgewandelt. Dadurch kann ein und dasselbe Programm seine Ausgaben egal welcher Form auf jeden beliebigen Drucker senden, ohne daß es über die Eigenarten dieses Druckers Be¬ scheid wissen muß. 662 Amiga intern Das Kommando DUMPRPORT bietet, wie bereits erwähnt, die Mög¬ lichkeit, den Inhalt eines Fensters oder Screens auf dem Drucker aus¬ zugeben. Die hierfür notwendige Zusatz-I/O-Struktur mit dem Namen lODRPReq ist folgendermaßen auf gebaut: Offset Name Inhalt 32 RaatPort Zeiger auf aussugebenden RaatPort. 36 ColorMap Zeiger auf die Farbtabelle. 40 Modes Graphik-Modua dea View-Port. 44 SrcX X-Poaition dea Fenatera/Screens. 46 SrcY Y-Poaition 48 SrcWidth Fenster-/Screen-Breite. 50 SrcHeight -Höhe 52 DestColfl Zielbreite 56 DeatRows Zielhöhe 60 Special Flags für Sonderfunktionen. 3.7.6 Parallel-Device: Digital-Ein-/-Ausgaben Am Anschluß, an dem normalerweise der Drucker hängt, können auch andere Geräte angeschlossen werden, wenn sie über die entsprechen¬ den elektrischen Eigenschaften verfügen. Es lassen sich dann digitale Daten sowohl ein- als auch ausgeben. Dabei hat man zusätzlich die Möglichkeit, einzelne Bits der 8 Datenleitungen als Eingang und die anderen als Ausgang zu programmieren. Dies ist nur durch die direkte Programmierung des Hardware-Registers $BFE301 möglich. Bleiben wir daher bei der Programmierung des gesamten Ports als Ein- oder Ausgang. Auch hierfür gibt es ein Device: das Parallel-De¬ vice. Für die Verwendung dieses Device muß wieder zusätzlich zu der normalen I/O-Struktur eine Erweiterung vorgenommen werden. Diese Erweiterung hat folgenden Aufbau: Offiet Name Inhalt 48 PWBufLen Länge dea Ausgabepuffera 62 ParStatuB Status der Device 83 ParFlags Parallel-Flags 84 PTermAiray Abbruch-Maake -61 Das Status-Byte enthält folgende Status-Bits: Bit Name Bedeutung, wenn geaetst: 0 PSEL Drucker angewählt. 1 PAPEROUT Papier alle. 2 PBUSY Drucker beschäftigt. Das AmigaDOS 663 3 RWDIR Datenrichtung (0 = lesen, 1 = schreiben). Die Parallel-Flags stellen folgende Bits dar: Name Bedeutung, wenn gesetst: _ 1 EOFMODE EOF-Modus eingeschaltet. 5 SHARED Zugriff auch für andere Tasks möglich. Wenn aus dem Parallel-Port gelesen wird, stellt sich wieder einmal die Frage, woran der Empfänger das Ende der Übertragung erkennen soll. Dafür gibt es hier die Möglichkeit, bei einer bestimmte Byte-Sequenz den Empfang abbrechen zu lassen. Diese Sequenz wird in den zwei Langworten des TermArray eingetragen. Aktiviert wird diese Ab¬ bruch-Sequenz, wenn Bit 1 des Flags-Bytes gesetzt wird (EOFMODE) und dann das SETPARAMS-Kommando (10) aufgerufen wird. 3.7.7 Game-Port-Oevice: Maus und Joystick Mit diesem Device werden alle Eingaben aus den beiden Ports bear¬ beitet, die von einer Maus oder von einem Joystick kommen. Die I/O- Struktur dieses Device benötigt keine Erweiterung, jedoch werden zwei weitere Strukturen verwendet. Eine dieser Strukturen ist die Event-Struktur, die bereits im Kapitel über die RAW:-Fenster vorgestellt wurde. Diese Struktur namens In- putEvent hat folgenden Aufbau: Offset Name Inhalt 0 NextEvent Evtl. Zeiger auf die folgende Struktur. 4 Class Ereignis-Klasse 5 SubClass Evtl. Unterklasse des Ereignisses. 6 Code Ereignis-Code 8 Qualifier Ereignis-Typ 10 X X-Position 12 Y Y-Position, meist relativ. 14 TimeStamp: Sekunden Mikrosekunden Die andere Struktur ist für die Einstellung des Ereignisses nötig, das die Übergabe der Parameter in der Event-Struktur auslöst. Dies kann das Drücken oder Loslassen eines Knopfes oder die horizontale bzw. vertikale Bewegung der Maus oder des Joysticks sein. Der gewünschte Wert ist dafür in das entsprechende Wort der folgenden Struktur na¬ mens GamePortTrigger einzutragen: 664 Amiga intern Offaet Name Inhalt 0 Keys Tastenändening; Bit 0: Taste gedrückt Bit 1: Taste loegelaasen 2 Timeout Abbruch bei Ablauf dieser Ansahl von 1/SO Sekunden 4 XDelta Horisontale Bewegung. 6 YDelta Vertikale Bewegung. Will man also 10 Sekunden darauf warten, daß ein so überwachter Joystick horizontal bewegt wird oder seine Taste gedrückt wird, so schreibt man in Keys eine 1 (Taste drücken), in Timeout eine 500 (500/50=10 Sekunden) und in XDelta eine 1. Bevor man nun diese Überwachung starten kann, muß man natürlich einige Vorbereitungen treffen. Zuerst muß das Game-Port-Device ge¬ öffnet werden, dann muß der Port, in dem der Joystick steckt, als Joystick-Port angemeldet und schließlich auf das gewählte Ereignis gewartet werden. Die Kommandos dieses Device sind die folgenden: Kommando Name Bedeutung 9 READEVENT Überwachung starten. 10 ASKCTYPE Port-Typ abfragen. 11 SETCTYPE Port-Typ setsen. 12 ASKTRIGGER Auslösungsereignisse ermitteln. 13 SETTRIGGER Auslösungsereignisse setaen. Die möglichen Port-Typen, welche mit SETCTYPE eingestellt werden können, sind: Nummer Name Bedeutung 0 NOCONTROLLER Abmelden des Ports. 1 MOUSE Maus-Port. 2 RELJOYSTICK Port für relative Joysticks. 3 ABSJOYSTICK Port fUr absolute Joysticks. zusätzlich gibt es noch den Typ -1 ALLOCATED der bei ASKCTYPE erscheinen kann, der Port ist dann bereits von ei¬ ner anderen Task belegt. Der Unterschied zwischen einem relativen und absoluten Joystick ist der, daß der X- bzw. Y-Wert eines relativen Joysticks beim Festhalten in einer Richtung ständig hoch- bzw. runtergezählt wird, beim abso¬ luten Joystick gibt es nur eine Positionswertänderung pro Bewegung. Das AmigaDOS 665 Um die Programmierung des Game-Port-Device zu demonstrieren, folgt nun ein Maschinenprogramm, das einen im rechten Port einge¬ setzten Joystick überwacht: Game-Port-Device-Demo: Joystick 6/87 S.D. ExecBase = 4 FindTask = -294 AddPort = -354 RemPort = -360 OpenLib = -408 CloseLib = -414 OpenDev = -444 CloseOev = -450 Dolo = -456 Sendio = -462 run: move.l execbase,a6 sub. l a1,a1 jsr FindTask(a6) move.l dO,readreply+SIO lea readreply.al jsr AcldPort(a6) lea devio,a1 move.l #1,d0 clr.l dl lea devicename.aO jsr 0penDev(a6} tst.l dO bne error :••• Port-Typ setzen ••• move #11,28(a1} move.l #Event,40(a1} move.l #1,36(a1) move.b #3,NextEvent lea devio,a1 move.l #readreply,U(a1} move.l execbase,a6 jsr DoIo(a6) ••• Auslösung definieren *** move #13,28(a1} move.l #trigger,40(a1} move.l #8,36(a1} move #3, Keys move #0, Timeout move #1,XDelta move #1,YDelta ;Zeiger auf EXEC-Bibliothek ;Eigener Task ;Task suchen :set SigTask ;Add Reply-Port ;Unit 1: rechter Port ;Gaine-Port-Device öffnen ;Comnand: SETCTYPE ;Puffer ;Länge; ;A8SJ0YSTICK ;set Reply-Port ;Joystick anmelden .-Conmand: SETTRIGGER ;Puffer ;Länge ;D0WN & UP ;Timeout ;XDelta ;YDelt6 ooo Amiga intern lea devio,a1 move.l #readreply,14(a1) ;set Reply-Port move.l execbase,a6 jsr DoIo(a6) ;Auslösung setzen *** Überwachung starten **• move «9,28(a1) .-Coamand: READEVENT move. . l #Event,40(a1) ;Puffer move. l #22,36(a1) ;Länge: ein Event clr.b 30(a1) ;Flags lea devio.al move.l #readreply,14(a1) ;set Reply-Port move. l execbase,a6 Jsr 0olo(a6) :Auf Event warten ;*** Port wieder abmelden ••• move #11,28(a1) .-Conmand: SETCTYPE move. l #Event,40(a1) .-Puffer move. l #1.36(a1) ;Länge: move. b #0,NextEvent ;NOCONTROLLER lea devio.al move. l #readreply,14(a1) ;set Reply-Port move. l execbase,a6 Jsr Dolo(aö) .-Joystick abmelden ende: lea readreply,a1 jsr RemPort(a6) ;Port entfernen lea devio,a1 isr closedev(a6) ;Device schließen error: rts ;* Ende • devicename: even devio: dc.b 'gameport.device'.O message: blk.b 20.0 fo; blk.b 12.0 ioreq: blk.b 16.0 readreply: blk.l 8.0 Event: NextEvent: dc.l 0 dass: dc.b 0 SubClass: dc.b 0 Code: dc.w 0 Qualifier: dc.w 0 ie_X: dc.w 0 ie_Y: dc.w 0 TimeStamp: dc.l 0.0 Das AmigaDOS 667 Trigger: Keys: dc.w 0 Timeout: dc.w 0 XDelta: dc.w 0 YDelta: dc.w 0 Normalerweise ist es wohl sinnvoller, die Überwachung mit SendIo() zu starten, da das Programm dann nicht auf die Betätigung des Joy¬ sticks warten muß, sondern Weiterarbeiten kann. Für dieses Beispiel ist es allerdings empfehlenswert zu warten, da sonst das Device vor dem Ereignis abgemeldet wird, was zu Problemen führen kann. 3.8 Disketten Der Amiga arbeitet sehr stark diskettenorientiert, d.h. er lädt oft ir¬ gend etwas nach. Aus diesem Grunde ist es wichtig, daß die Informa¬ tionen auf einer Diskette sicher untergebracht und einigermaßen schnell gefunden werden können. Wir wollen uns nun mit der Auftei¬ lung der Disketten und der Interpretation der darauf liegenden Daten beschäftigen. Die grundlegende Unterteilung einer Diskette sieht so aus: Seite bzw. Kopf-Nummer (0 oder 1) Spur oder Track oder Zylinder (0 bis 79) Sektor (0 - 10) Jede Amiga-Diskette wird beidseitig bespielt, wodurch zwei Seiten verfügbar sind. Jede Seite der Diskette ist ihrerseits in 80 Spuren unterteilt, die als konzentrische Ringe um ihren Mittelpunkt angeordnet sind. Die äußerste Spur trägt die Nummer Null, die innerste die Nummer 79. Diese Spuren werden auch als Tracks bezeichnet. Hat man wie bei ei¬ ner Diskette zwei Seiten im Zugriff, so werden die zwei direkt gegen¬ überliegenden und somit von den zwei Schreib-/Leseköpfen des Laufwerks gleichzeitig zugegriffenen Tracks als ein Zylinder bezeich¬ net. Jede dieser Spuren wird wiederum in 11 Sektoren unterteilt, die auch von 0 an gezählt werden. Diese Sektoren werden auch als Blocks be¬ zeichnet, wobei die Sektoren nur von 0 bis 10, die Blocks jedoch von 0 bis 1759 gezählt werden, da diese die logische Sektornummer der Diskette bezeichnen. 668 Amiga intern Jeder dieser Sektoren enthält 512 Bytes an verfügbaren Informationen, wodurch jede Diskette 512*11*80*2=901120 Bytes tragen kann. Diese Anzahl ist allerdings nicht vollständig für Ihre Daten verfügbar, da die Verwaltung der Daten ebenfalls einigen Platz benötigt. Beim Fast-Fi- ling-System, das später behandelt wird, werden allerdings alle 512 Bytes für Daten verwendet. Der erste logische Sektor, also der erste Block auf einer Diskette liegt auf Seite 0, Spur 0, Sektor 0. Der darauffolgende Block ist der nächste Sektor dieser Spur und so weiter. Block 11 ist nun nicht der erste Sektor der zweiten Spur, sondern derjenige der ersten Spur auf der Rückseite (Seite 1) der Diskette. Auf diese Weise wird die Diskette immer abwechselnd oben und unten beschrieben bzw. gelesen. 3.8.1 Der Boot-Vorgang Der erste Kontakt zur Diskette findet bereits statt, wenn man den Amiga einschaltet. Nachdem einige hardwaremäßige Initialisierungen des Rechners durch das Exec erledigt sind, läuft das Laufwerk 0 an. Was geschieht da? Egal, ob in diesem Amiga das Kickstart bereits eingebaut ist oder nicht, es wird von der Diskette in diesem Laufwerk etwas geladen. Ist das Kickstart nicht eingebaut, so will der Rechner dies von der Dis¬ kette laden, andernfalls sucht er nach einer Workbench-Diskette. Die¬ ser Ladevorgang beim Einschalten des Rechners wird "booten" (sprich: buuten) genannt. Das erste, was von der eingelegten Diskette geladen wird, sind die Boot-Blöcke, die die ersten beiden Sektoren (0 und 1) der Diskette belegen. Dort findet sich nun die Information darüber, um welchen Disketten-Typ es sich hier handelt. Die möglichen Typen sind: Kickstart-Diskette. DOS, eventuell ladbare DOS-Diskette (Worbench-Disk). Unformatierte bzw. in fremdem Format initialisierte Diskette. Die ersten vier Bytes des ersten Blocks der Diskette zeigen an, um welchen Typ es sich hier handelt. Dort stehen nämlich entweder die Buchstaben DOS mit abschließender binärer Null oder Eins für eine DOS-Diskette oder KICK für eine Kickstart-Diskette. Steht hier bei¬ des nicht, so handelt es sich um eine fremde Diskette (BAD). Das AmigaDOS 669 Die nun folgenden 4 Bytes stellen als Langwort die Checksumme des Boot-Blocks dar. Ist diese Summe korrekt, so nimmt der Amiga an, daß es sich hier um eine Workbench-Diskette handelt. Das nächste Langwort enthält die Nummer des Diskettenblocks, der Root-Block genannt wird und normalerweise in Block $370 (880) liegt. Die Bedeutung dieses Blocks folgt gleich, bleiben wir zunächst beim Boot-Block. Beginnend im 7. Wort liegt hier nun ein Programm. Dieses Programm wird, wenn die Checksumme stimmt, als erstes gestartet. Es bekommt in A6 einen Zeiger auf die EXEC-Basisadresse übergeben und kann so direkt ein EXEC-Kommando ausführen. Boot-Block (Sektor 0) L-Wort Name Inhalt Bedeutung 0 Disk-Type DOS, KICK Diskettentyp in 4 Buchstaben. 1 Checksumme ??? Checksumme des Blocks. 2 3- Rootblock $370 Blocknummer des Root-Blocks. 127 Daten Boot-Programm. Das Programm, das üblicherweise dort liegt, testet mittels des Find- ResidentO-Kommandos des EXEC, ob die DOS-Bibliothek resident vorliegt. Ist dies nicht der Fall, so wird im Register DO der Wert -1 übergeben. Wenn doch, so wird in DO eine Null und in AO ein Zeiger auf die Initialisierungsroutine des DOS zurückgegeben. Mit einem geeigneten Programm kann an dieser Stelle der gesamte Boot-Vorgang und somit die Initialisierung des Amiga selbst gestaltet werden. Sie haben hier also die Möglichkeit, eine eigene Workbench- Diskette zu gestalten, da einige Disketten-Monitor-Programme die Erstellung der Checksumme übernehmen können. Diese Summe aller Worte des Blocks ist unbedingt nötig, damit der Amiga diesen Block als Boot-Block anerkennt. 3.8.2 Daten-Verteilung auf Diskette Die Verteilung der einzelnen Datenblöcke auf der Diskette hängt na¬ türlich davon ab, was und in welcher Reihenfolge auf dieser Diskette gespeichert wurde. Dennoch ist die Aufteilung der einzelnen Sektoren in sich vorgeschrieben. 670 Amiga intern Da ab Workbench 1.3 zusätzlich zum normalen Filing, das im ROM liegt, das Fast-Filing-System auf der Diskette mitgeliefert wird, folgt die Betrachtung der einzelnen Datenformate nun in zwei Teilen. 3.8.2.1 Normales Filing-System Um auf einer Diskette mit einer Kapazität von ca. 800 KByte seine Daten so unterzubringen, daß sie auch später wieder auffindbar sind, gibt es bei der Verteilung von Daten einige Regeln. Diese Regeln sind dem AmigaDOS natürlich bekannt, so daß man sie eigentlich nicht unbedingt wissen müßte. Tritt jedoch einmal ein Fehler auf einer Dis¬ kette auf, so muß man die verbliebenen Daten retten können. Hierfür ist das DISKDOCTOR-Programm vorgesehen, das der Amiga bei einem Diskettenfehler sogar in einem Requester empfiehlt. Um die Arbeitsweise dieses Retters in der Not zu verstehen, sind eingehende Betrachtungen der Diskettenformate nötig. Ein wichtiger Punkt hierbei ist die Verteilung der Dateien auf der Diskette und die Technik des Inhaltsverzeichnisses. Im Gegensatz zu den meisten Diskettenformaten ist das Inhaltsverzeichnis von Amiga- Disketten nicht in einigen zusammenhängenden Sektoren zu finden. Dies ist der Grund, weshalb die Ausgabe des Directory (z.B. mit dem CLI-Kommando DIR) so lange dauert. Diese Methode hat Vor- und Nachteile. Der Nachteil ist die lange Zu¬ griffszeit auf das Inhaltsverzeichnis, was teilweise ganz schön Nerven kostet. Dieser Nachteil wird jedoch durch einen großen Vorteil wett¬ gemacht; die Möglichkeit der "Reparatur" einer beschädigten Diskette. Tritt bei einem der üblichen Disketten eines anderen Systems, z.B. bei MS-DOS oder dem Atari ST, ein Fehler ausgerechnet in Track 0 auf, wo das gesamte Inhaltsverzeichnis der Diskette liegt, so treten sehr große Probleme auf. Die Position der Daten und die Zusammenhänge der Sektoren sind nämlich dann nicht mehr bekannt und die Dateien somit nur mit einem Riesenaufwand wieder zu retten. Dies ist beim Amiga nicht der Fall. Wie schon durch die Existenz des DISCDOCTOR-Programms deutlich, sind die einzelnen Dateien relativ leicht wiederzufinden, ohne daß eine zentrale Verteilungsstelle not¬ wendig ist. Dies wird durch große Redundanzen erreicht, die zwar Das AmigaDOS 671 Speicherplatz auf den Disketten verbraucht, aber dadurch große Da¬ tensicherheit gewährleistet. Wie funktioniert dies? Um die Struktur der Datenverteilung zu durchschauen, müssen wir nun den Aufbau der verschiedenen Disket¬ tensektoren betrachten. Root-Block Abgesehen von den Boot-Sektoren befindet sich der Root-Block an einer festgelegten Stelle auf der Diskette. Dies ist üblicherweise auf Seite 0, Track 40, Sektor 0 und hat somit die Nummer 880 ($370). Das dritte Langwort des Boot-Sektors enthält auch diese Nummer. In diesem Block befindet sich die Wurzel der gesamten Diskette. Hier liegt das oberste Directory sowie der Diskettenname und dessen Er¬ stellungsdatum. Die Aufteilung dieses Blocks ist folgende (alle Werte sind Langworte, also 4 Bytes); Root-Block Wort Name Inhalt Bedeutung 0 Type 2 Typ 2 (T.SHORT) bedeutet, daS es sich bei diesem Block um einen An¬ fangsblock einer Struktur handelt 1 Header Key 0 Hat hier keine Bedeutung 2 High Seq 0 Hat hier keine Bedeutung 3 HT-Siie *48 Dies ist die GrbBe der Tabelle, in der die Anfangsblocks der Files oder Un- terdirectories aufgefOhrt sind (Hashtable), die susammen in einer Kette liegen 4 Reserviert 0 Hat hier keine Bedeutung 5 Checksumme ??? Enthält einen Wert, welcher die Summe aller Worte dieses Blocks su Null macht 6 Hashtable Hier beginnt die Tabelle, in der die Anfangsblocks der Dateien bsw. Un- ter-Directories stehen 78 BM-Flag -1 Dieses Flag enthält -1 (TRUE), wenn die Bit-Map der Diskette gültig ist 79 BM-Pages Die folgende Tabelle enthält Zeiger auf die Blocks, welche die Bit-Map enthalten. Meistens ist dies nur ein einseiner Block, so daS die übrigen Zeiger der Tabelle Null sind 105 Days Enthält das Datum, an dem die Dis¬ kette suletst verändert wurde 106 Mins Uhreeit der Änderung 672 Amiga intern 107 Tickt Sekunde der Änderung 108 Ditk Name Hier liegt nun der Name der Diskette als BCPL-String, d.h. das erste Byte stellt die Ansahl der Zeichen des Na¬ mens (max. 30) dar 121 Create Days Erstellungsdatum der Diskette 122 Create Mine Erstellungsseit 123 Create Ticks Erstellungssekunde 124 Next Hash 0 Immer Null 125 Patent Dir 0 Zeiger auf übergeordnetes Directory: immer Null 126 Extension 0 Immer Null 127 Sec. Type 1 Dieses Wort stellt den Sekund&r-Typ des Blocks dar. Für den Root-Block ist dies eine 1. Die Werte in der Hashtable geben die Blocks an, in denen die Datei- bzw. Unter-Directory-Ketten innerhalb des Root-Directories begin¬ nen. Da in diese Tabelle nicht genug Werte passen, werden aus den Dateien bzw. Unter-Directories Ketten gebildet, deren Namen einen bestimmten Zusammenhang haben. Es wird aus dem Datei- bzw. Directory-Namen ein Wert errechnet, der zwischen 6 und 77 liegt. Mit diesem Wert kann dann auf das Langwort der Hash-Tabelle zugegriffen werden, wo die gewählte Kette beginnt. Die Funktion, nach der dieser Wert errechnet wird, ist folgende: Hash=Länge des Namens pro Buchstabe des Namens: Hash=Hash *13 Hash=Hash +ASCII-Uert des Zeichens (inmer Großbuchstaben) Hash=Hash & $7FF (logisch UND) Hash=Hash modulo 72 Hash=Hash +6 Wie dies zu programmieren ist, finden Sie im Kapitel über das Track¬ disk-Device, in dem ein Maschinenprogramm u.a. zur Auswertung der Hash-Tabelle vorgestellt wird. Der Anfang der Kette liegt also dort, wohin der so berechnete Zeiger der Hash-Table zeigt. In dem so gefundenen Block steht dann im 124. Langwort die Nummer des nächsten Eintrages der Kette und so wei¬ ter, bis schließlich durch eine Null in diesem Zeiger das Ende der Kette angezeigt wird. Diese Blocks, die den Anfang einer Datei- oder Directory-Struktur bilden, sind ebenfalls besonders aufgebaut. Beginnen wir mit dem er¬ sten Block einer Datei, dem File-Header-Block. Das AmigaDOS 673 File-Header-Block Dieser Block enthält die Informationen über die entsprechende Datei. Darunter sind deren Name, Erstellungszeit und Kommentar sowie die Größe und Verteilung der Datei auf der Diskette. Die Aufteilung dieses Blocks ist folgende: File-Header-Block Wort Name Inhalt Bedeutung 0 Typ« 2 Typ 2 (T.SHORT) bedeutet, daß es sich bei diesem Block um einen An- fangsblock einer Struktur handelt 1 Header Key Hier steht die eigene Block-Nummer. 2 High Seq Enthält die gesamte Ansahl der Blocks dieser Datei. 3 Data-Siae 0 4 first Data 0 Hier steht die Nummer des ersten Datenblocks der Datei. Dieser Wert findet sich auch in Wort Nr.77 wieder. 5 Checksumme ??? Enthält einen Wert, welcher die Summe aller Worte dieses Blocks su Null macht. 6 Data-Blocks Hier beginnt die Tabelle, in der die Datenblocks der Datei aufgeführt sind. Die Tabelle beginnt bei Wort 77 und sählt dann rückwärts. 78 Reserviert 0 79 Reserviert 0 80 Protect Dieses Wort enthält in den unteren 4 Bits die Status-Information der Datei: Bit Geschütst gegen, wenn gesetst: 0 Löschen 1 Verändern 2 Überschreiben 3 Lesen 81 Byte Sise Länge der Datei in Bytes. 82 Comment Hier beginnt der Kommentar sur Da¬ tei als BCPL-String (max. 22 Zei¬ chen). lOG Days Enthält das Datum, an dem die Datei erstellt wurde. 106 Mins Uhrseit der Erstellung. 107 Ticks Sekunde der Erstellung. 108 Datei'Name Hier liegt nun der Name der Datei als BCPL-String, d.h. das erste Byte stellt die Ansahl der Zeichen des Na¬ mens (max. 30) dar. 124 Hash Chain Blocknummer der nächsten Datei aus dieser Kette oder Null. Amiga intern 674 125 Parent 126 Extension 126 127 Sec. Type -3 Zeiger auf das Directory, in dem diese Datei aufgetaucht ist. Zeiger auf Erweiterungsblock oder Null. Immer dann ungleich null, wenn die Data-Block-Tabelle nicht lang genug ist, um alle Blocks dieser Datei auf- euaeigen. Ist dies der Fall, so wird dort auf einen Block geaeigt, der diese Liste fortführt. Dieses Wort stellt den Sekundär-Typ des Blocks dar. Für den File-Header- Block ist dies -3 ($FFFD). File-List-Block Dieser Block, der die Block-Liste fortführt, wird Erweiterungsblock (File-List-Block) genannt und ist folgendermaßen aufgebaut: File-List-Block Wort Name Inhalt Bedeutung 0 Type $10 Typ $10 (T.LIST) bedeutet, daB es sich bei diesem Block um einen Er¬ weiterungs-Block einer Datei-Struk¬ tur handelt. 1 Header Key Hier steht die eigene Block-Nummer. 2 High Seq Enthält die gesamte Ansahl der Ein¬ träge in der Data-Block-Tabelle. 3 Data-Siie 0 4 first Data 0 Hier steht die Nummer des ersten Datenblocks der Datei. Dieser Wert findet sich auch in Wort Nr.77 des File-Header-Blocks wieder. 5 Checksumme 777 Enthält einen Wert, welcher die Summe aller Worte dieses Blocks su Null macht. 6 Data-Blocks Hier beginnt die Tabelle, in der die susätslichen Datenblocks der Datei aufgefUhrt sind. Die Tabelle beginnt bei Wort 77 und sählt dann rück¬ wärts. 78 info 0 Reserviert 124 Hash Chain 0 Blocknummer der nächsten Datei aus dieser Kette (immer Null). 125 Parent Zeiger auf denFile-Header-Block. 126 Extension 0 Zeiger auf Erweiterungsblock oder Null. Das AmigaDOS 675 127 Sec. Type -3 Dieses Wort stellt den Sekundär-Typ des Blocks dar. Die andere Möglichkeit eines Start-Blocks ist der User-Directory- Block, der am Anfang einer Directory-Struktur steht. User-Directory-Block Jedes Unter-Directory wird durch einen solchen Block begonnen, der ähnlich wie der Root-Block aufgebaut ist: Wort Name Inhalt Bedeutung 0 Type 2 Typ 2 (T.SHORT) bedeutet, dafi es sich bei diesem Block um einen An- fangsblock einer Struktur handelt. 1 Header Key Hier steht die eigene Blocknummer. 2 High Seq 0 Hat hier keine Bedeutung. 3 HT-Sise 0 Hat hier keine Bedeutung. 4 Reserviert 0 Hat hier keine Bedeutung. S Checksumme ??? Enthält einen Wert, welcher die Summe aller Worte dieses Blocks eu Null macht. 6 Hashtable Hier beginnt die Tabelle, in der die Anfangsblocks der Dateien bew. Un- ter-Directories stehen. 78 Reserviert 0 Hat hier keine Bedeutung. 80 Protect Dieses Wort enthält in den unteren 4 Bits die Status-Information der Datei: Bit Geschütst gegen, wenn gesetst: 0 Löschen 1 Verändern 2 Überschreiben 3 Lesen 81 Reserviert 0 Ohne Bedeutung. 82 Comment Hier beginnt der Kommentar zu die¬ sem Unter-Directory als BCPL-String (max. 22 Zeichen). 105 Days Enthält das Datum, an dem dieses Unterdirectory erstellt wurde. 106 Mins Uhrseit der Erstellung. 107 Ticks Sekunde der Erstellung. 108 Dir. Name Hier liegt nun der Name des Directo- ries als BCPL-String (max. 30). 124 Next Hash Nächster Eintrag derselben Kette. 125 Parent Dir Zeiger auf das übergeordnete Direc¬ tory. 126 Extension 0 Immer Null. Amiga intern 676 127 Sec. Type 2 Dieses Wort stellt den Sekundär-Typ des Blocks dar. Für den User-Direc- tory-Block ist dies eine 2. Außer diesen Struktur-Blocks sind natürlich auch Datenblocks auf der Diskette vorhanden. Diese haben den einfachsten Aufbau: Data-Block Wort Name Inhalt Bedeutung 0 Type 8 Typ 8 (T.DATA) bedeutet, daß es sich hier um einen Datenblock han¬ delt. 1 Header Key Hier steht die Blocknummer des File- Headers. 2 Seq Num Lfd. Nummer des Datenblocks in die¬ ser Datei. 3 Data-Sice $1E8 Gültige Datenworte dieses Daten¬ blocks ($1E8 oder weniger). 4 Next Data Nummer des nächsten Datenblocks dieser Datei. 6 Checkaumme ??? Enthält einen Wert, welcher die Summe aller Worte dieses Blocks eu Null macht. 6 Data Hier beginnen die Daten selbst. Nun fehlt nur noch der Block, der die beim Root-Block erwähnte Bit- Map enthält. In diesem Block ist für jeden Block der Diskette ein Bit vorhanden, das anzeigt, ob jener Block belegt oder frei ist. Der Auf¬ bau des Bit-Map-Blocks ist sehr einfach: Bit-Map-Block Wort Name Inhalt Bedeutung 0 Checksumme ??? Checksumme des Blocks Bit-Muster Belegungs-Bits für alle Blöcke, Bit 0 des ersten Langworts steht für Block 2 usw. Ein gesetstes Bit bedeutet einen freien Block. 3.8.2.2 Fast-Filing-System Der Unterschied zwischen den beiden Filing-Systemen ist wesentlich geringer, als der dadurch erreichte Geschwindigkeitszuwachs vermuten läßt. Der eine Grund für die Beschleunigung ist der, daß der neue FFS-Handler optimiert programmiert wurde (in Maschinensprache!) und somit schneller ist. Der zweite Grund ist jedoch entscheidend: Die Datenblöcke enthalten nur noch die Daten! Das AmigaDOS 677 Um eine FFS-Diskette bzw. Festplatten-Partition zu markieren, wird hinter dem Diskettentyp im Boot-Block, "DOS", eine binäre 1 gesetzt. Nach der Formatierung mit dem neuen FORMAT-Befehl und der Option FFS wird also lediglich dieses eine Bit anders gesetzt, die rest¬ liche Formatierung ist identisch. Lediglich der initiale Inhalt der Sek¬ toren, in die ja DOS1DOS2DOS3 usw. geschrieben wird, verschiebt sich um 1, da ja nicht mit 0, sondern mit 1 begonnen wird. Die so erreichten Vorteile liegen auf der Hand: Pro geladenen Daten¬ block sind 512 Bytes geladen, also immerhin 24 Bytes mehr als vorher. Außerdem spart man sich die Zeit, die der Händler dafür braucht, die Checksumme zu bilden, sie zu vergleichen und schließlich die reinen Daten-Bytes an die gewünschte Stelle im Speicher zu kopieren. Beim FFS kann der gesamte Sektorinhalt in den angegebenen Speicher ge¬ setzt werden. Da außerdem bei großen Dateien die Datensektoren hintereinander angeordnet sind, kann durch das direkte Laden eines großen Schwungs von Sektoren in den Speicher die Hardware voll ge¬ nutzt werden, während das alte System Sektor für Sektor laden muß. Um nun eine Festplatte mit dem FFS betreiben zu wollen, können ei¬ nige Vorbereitungen getroffen werden. Die erste Partition der Platte muß nach wie vor für das alte System vorbereitet werden bzw. sein, da das FFS ja erst einmal geladen werden muß. Es empfiehlt sich, diese erste Partition recht klein zu wählen. Alle weiteren Partitionen müssen für die Verwendung des FFS neu formatiert werden. Sollten Sie also bereits die Festplatte mit Daten beschrieben haben, so retten Sie diese erst einmal auf Disketten. Kopieren Sie nun die Programme l:FastFileSystem, crMount und c:Format von der Workbench 1.3-Diskette auf Ihre Boot-Disk. Danach müssen Sie für jede Partition, die unter FFS laufen soll, in der MountList folgende Zeilen zusätzlich eintragen: GlobVec = -1 FileSystetn = UFastFileSystem DosType = 0x4444F5301 Der Eintrag "GlobVec" wird auf -1 gesetzt, da keine Global-Vectors- Tabelle verwendet wird. Die Angabe des FileSystems ist natürlich klar, wobei auch zu beachten ist, daß Sie das FastFileSystem auch in den 1- Ordner kopieren müssen. Mit dem Eintrag "DosType" wird dann schließlich festgelegt, daß die Kennung der Partition auch "DOS\r ist. 678 Amiga intern Die erste Ziffer 4 von 0x4444F5301 wird benötigt, da es sich um einen BCPL-String handelt. Wenn Sie nun einen Reset ausführen, erscheint für jede der FFS-Par- titions ein Requester "Not a DOS-Disk". Klicken Sie ruhig Cancel an, er hat ja recht. Nach dem fertigen Hochfahren des Rechners müssen die FFS-Partitionen nämlich erst einmal entsprechend formatiert wer¬ den. Dies wird mit dem neuen Format-Befehl durchgeführt, bei dem an das Ende der aufrufenden Befehlszeile noch FFS angehängt wird. Danach können Sie die Partitions mit FFS verwenden. Der Geschwin¬ digkeitszuwachs ist enorm! Wenn Sie sich nun auch Disketten im FFS anlegen wollen, ist der Vor¬ gang etwas anders. Zunächst einmal ist zu bemerken, daß eine Dis¬ kette im Gegensatz zur Festplatte wechselbar ist. Da dies aber vom FFS nicht beachtet wird, muß nach jedem Wechsel einer Diskette der Befehl DiskChange eingegeben werden (im C-Directory der WB1.3- Disk)! In die Mountlist für die Diskette muß nun ein Extraeintrag getippt werden, der für FFS-Disketten gilt. Dieser sieht dann etwa folgender¬ maßen aus: FAST: Device 5 trackdisk.device FileSystem = L:FastFileSystem GlobVec S -1 DosType = 0X4444FS301 StackSize S 5000 Unit S 1 Flags = 0 Surfaces S 2 BlocksPerTrack S 11 Reserved = 2 Interleave S 1 LowCyl S 0 HighCyl = 79 Buffers = 5 BufHefliType = 1 Mount = 1 # Dieser Eintrag bietet nach dem Befehl Mount FAST: einen Zugriff auf eine Diskette mit FastFileSystem, welche natürlich jeweils mit der FFS-Option formatiert werden muß. Die einzelnen Bedeutungen der MountList-Einträge entnehmen Sie bitte den früheren Kapiteln dieses Buches bzw. Ihrem DOS-Handbuch. Die Expansionsarchttektur 679 4. Die Expansionsarchitektur 4.1 Die Hardware Wer schon einmal eine Erweiterungskarte im Amiga verwendet hat, dem wird aufgefallen sein, daß man sich bei diesen Karten um nichts mehr kümmern muß. Einfach nur anstecken und einschalten. Eine RAM-Erweiterung ist so nach dem Einschalten und Booten automa¬ tisch zum freien Gesamtspeicher hinzugefügt. Steckt man noch eine weitere ein, gibt es, auch wenn die zweite Karte von einem anderen Hersteller stammt, keine Probleme. Man muß nicht, wie bei anderen Computern, erst eine Vielzahl von DIP-Schaltern oder Jumpern ent¬ sprechend einstellen, damit zwei Karten nicht an denselben Adressen liegen. Gleiches gilt auch für die Software. Es werden so automatisch nur jene Treiber geladen, deren zugehörige Hardware auch tatsächlich ein¬ gesteckt ist. Um dies alles zu ermöglichen, wurde von Commodore ein Hard-und Softwarekonzept entwickelt, das seit Kickstart 1.2 diese sog. Autokonfiguration von Erweiterungskarten unterstützt. Das Hauptproblem jeder Erweiterungskarte ist der von ihr benötigte Adreßbereich. Damit sind diejenigen Adressen gemeint, an denen die Karte von der Software angesprochen werden kann. Beim Amiga ist der Bereich von $200000 bis $9FFFFF für Erweiterungskarten vorge¬ sehen. Da aber eine Erweiterungskarte nicht weiß, welche anderen Erweiterungen sich schon in diesem Adreßbereich befinden, kann es zu Kollisionen kommen. Nehmen wir an, eine 2-MByte-RAM-Erwei- terung liege an der Adresse $200000. Jetzt will man durch Einstecken einer zweiten Karte den Speicher auf 4 MBytes erweitern. Steckt man dazu einfach noch einmal dieselbe Karte ein, führt dies natürlich zu nichts, da diese dann auch ab $200000 im Speicher liegt. Man muß also die Adresse einer der Karten umstellen, z.B. auf $400000. Dann würde es funktionieren. So geht dies auch bei den meisten anderen Computern. Die Erweiterungskarten haben kleine Schalter auf der Platine, mit denen man ihre Basisadressen einstellen kann. Wehe dem, der einmal die Dokumentation zu so einer Karte verlegt hat und jetzt verzweifelt vor ihr steht, während er überlegt, welche Funktion die einzelnen Schalter denn hatten... 680 Amiga intern Beim Amiga dagegen besitzt jede Karte eine spezielle Hardware, die es dem Amiga ermöglicht zu ermitteln, wieviel Speicherplatz die Karte benötigt. Ist dieser noch verfügbar, weist sie der Karte die entspre¬ chende Adresse zu. Ist die Karte eine RAM-Erweiterung, wird der Speicher gleich noch der Liste des freien Speichers hinzugefügt. Außerdem kann auch noch eine Treibersoftware, z.B. ein Harddisk- Treiber, automatisch von einem auf der Erweiterungskarte befindli¬ chen ROM gelesen werden. Die Informationen über die Erweiterungskarten liest das Amiga-Be- triebssystem immer ab der Adresse $E80000. Wie klappt das, wenn mehrere Karten eingesteckt sind? Jede Erweiterungskarte besitzt einen CFGIN und einen CFGOUTPin (Configuration In und Configuration Out, siehe Beschreibung der A2000-Slots). Nach einem Reset legen alle Karten ihren CFGOUT- Ausgang auf high. Da der CFGOUT-Ausgang jeder Karte mit dem CFGIN-Eingang der nächsten verbunden ist, sehen alle Karten CFGIN = high (Leere Slots werden beim A2000 automatisch übergangen, man muß die Karten nicht der Reihe nach einstecken, wenn die Autokon¬ figuration funktionieren soll.) Einzige Ausnahme ist die erste Karte, ihr CFGIN ist immer low, ebenso wie das CFGIN einer Karte, die am Al000 oder A500 angesteckt ist. Nun gilt für alle Karten die Regel, daß sie nur dann auf einen Zugriff bei $E80000 antworten dürfen, wenn ihr CFGIN-Eingang low und ihr CFGOUT-Ausgang high ist. Daher antwortet nach einem Reset zuerst die Karte im ersten Slot (bzw. diejenige im Coprozessor-Slot beim A2000-B oder beimA500/1000 die Karte direkt am Expansion-Port.) Hat der Amiga alle notwendigen Daten dieser Karte gelesen, prüft er, ob der von ihr angeforderte Speicherplatz vorhanden ist. Wenn ja, schreibt er dessen Anfangsadresse in ein spezielles Register auf der Erweiterungskarte. Damit ist der Konfigurationsprozeß beendet, sie legt ihren CFGOUT- Ausgang auf low und erlaubt damit der nächsten Karte, mit ihrem Autokonfigurationsprozeß zu beginnen. Die Adresse, die der Amiga einer Karte zuweist, liegt immer auf einer Speichergrenze, die der Größe des angeforderten Speichers entspricht. Eine 2-MByte-RAM-Erweiterung bekommt also eine Adresse, die auf einer 2-MByte-Grenze liegt: $200000, $400000, $600000 oder $800000. Außnahmen sind lediglich 4- und 8-MByte-Karten, die aufgrund ih¬ rer Größe anders nicht unterzubringen wären. Die ExpansionsarchHektur 681 Die so konfigurierte Karte ist jetzt unter der ihr zugewiesenen Adresse zu erreichen. War der von ihr angeforderte Speicherplatz nicht mehr frei, was bei über 8 MByte freiem Adreßraum wohl selten vor¬ kommt, wird sie vom Amiga über eine andere Adresse, Shut_up_forever genannt, dazu aufgefordert, für immer zu schweigen und ihren CFGOUT-Ausgangauf low zu legen, damit die Autokonfi¬ guration weitergehen kann. Erst wenn keine Karte bei $E80000 mehr antwortet, weiß der Amiga, daß er alle Erweiterungskarten erfaßt hat und beendet den Autokonfi¬ gurationsprozeß. Der Aufbau der Autokonfiguration Wie gesagt, beginnt der Autokonfigurationsbereich bei SESOOOO. Um die Hardware auf der Erweiterungskarte billig zu halten, werden nur vier Daten-Bits benutzt D12-D15. Jedes Daten-Byte ist also auf die beiden oberen Nibbles zweier aufeinanderfolgender Worte verteilt AdroM D16-Dn Dll-DO tESOOOO Oberes Nibble des Bytes Unbelegt tE!80002 Unteres Nibble des Bytes Unbelegt Die Adressen sind folgendermaßen verteilt (Basisadresse ist während des Autokonfigurationsprozesses $E80000, danach hängt es von der Karte ab, ob diese Daten an der neuen Adresse noch erreichbar sind oder nicht.) Adrssssn _ Funktion _ 00/02 Bits 0-2: GröBe des gewünschten Speicherbereichs: 000 = 8 MByte 001 = 64 KByte 010 - 128 KByte 011 ° 2S6 KByte 100 = S12 KByte 101 = 1 MByte 110 = 2 MByte 111 - 4 MByte Bit 3: Eine 1 in diesem Bit bedeutet, daB die nächste Erweiterungskarte auf derselben Platine, also im selben Slot, sitst. Bit 4: Gültiges ROM auf der Platine Bit 6; Speicherbereich sur Liste des freien Speichers hinsufügen (bei RAM-Erweiterungen) Amiga intern 682 04/06 08/0A OC/OE 10/12 14/16 18/lA IC/IE 20/22 24/26 28/2A 2C/2E 30/32 34/36 38/3A 3C/3E 40/42 Bit 6 und 7: Art der Erweiteninge- karte: 00, 01 und 10; für künftige Karten 11: normale Karte Produktnummer: Mit diesem Byte kann ein Hersteller seine unter¬ schiedlichen Erweiterungskarten durchnumerieren, damit sie von der Software erkannt werden können. Bits 0-5: Immer Null Bit 6: Eine 0 bedeutet, dafi diese Karte abgeschaltet werden kann (Shut_up__forever) Bit 7: Wenn Bit 7 = 0 ist der Karte ihre Adresse egal, wenn es gleich 1 ist, will sie in den Bereich von $200000 bis $9FFFFF. Immer Null Herstellemummer, oberes Byte (High- Byte) Hentellemummer, unteres Byte (Low-Byte) Die Herstellemuimner ist eine 16-Bit-Zshl, die sich jeder Her¬ steller von Amiga-Erweiterungsksrten von Commodore suweiscn lassen kann, damit die Software die verschiedenen Karten auseinanderhalten kann (mit der Produktnummer) Seriennummer der Karte, Byte 0 (LSB) Seriennummer der Karte, Byte 1 Seriennummer der Karte, Byte 3 Seriennummer der Karte, Byte 4 (MSB). Die Seriennummer muS nicht verwendet werden, sie ist allein Sache des Herstellers. ROM-Adresse, oberes Byte ROM-Adresse, unteres Byte. Die ROM-Adresse ist die Adresse des ROM in Besug auf die Anfangsadresse der Platine. Zusammen mit dieser Adresse setst man Bit 4 im ersten Byte (00/02) auf 1, um dem Amiga su sagen, daB die ROM-Adresse gültig ist. Ist kein ROM vorhanden, sind diese beiden Bytes bedeutungslos. Lesesugriff immer Null, mit einem Schreibsugriff kann man die auf der Karte gespeicherte Basisadresse lö¬ schen. Immer Null. Immer Null. Immer Null. Kontroll/Statusregister (wahlweise) Bit 0: Interrupt erlauben (interrupt enable). Bit 1: Nicht festgelegt, Funktion je nach Karte. Die Expansionsarchitektur 683 Bit 2: Reset der Erweiteningskarte. Bit 3: Nicht festgelegt. Bit 4-7 sind beim Schreibsugriff nicht festgelegt, beim Lesen haben sie fol¬ gende Funktion: Bit 4: INT2 liegt an. Bit B: INTe liegt an. Bit 6: INT7 liegt an. Bit 7: Karte ISst gerade einen Inter¬ rupt aus. 44/46 Immer Null. 48/4A Basisadresse (AdreB-Bits A23 bis A16) An diese Adresse schreibt der Amiga die Basisadresse der Erweite¬ rungskarte. 4C/4E Bhut_op_forever (nur 4C), schaltet Karte ab. 50/52 bis 7C/7E Immer Null. Aufgrund der verwendeten Hardware müssen alle gelesenen Werte, außer denen an Adresse 00/02 und 40/42, noch invertiert werden. Die mit "immer Null" bezeichneten Bytes werden als $FF statt 0 gelesen. Wie wird das Konzept nun auf der Erweiterungskarte hard-waremäßig realisiert? Basis des Ganzen ist nicht ein ROM, wie man vielleicht vermuten könnte, sondern ein PAL-Baustein. Er übernimmt gleichzei¬ tig zwei Funktionen: Estens steuert er die Konfiguration der Platine, verwaltet unter anderem die CFGIN- und CFGOUT-Leitungen, zweitens übernimmt er die Funktion eines ROMs, in dem er die Kon¬ figurationsdaten, wie z.B. die gewünschte Speichergröße, speichert. Daher werden die Daten auch nur über vier Datenbusleitungen zu¬ rückgelesen, damit die anderen Ausgänge des PALs frei bleiben. Die Verwendung eines PAL ist auch daran schuld, daß die meisten Daten invertiert werden müssen, d.h. als 1 statt als 0 gelesen werden. Denn ein PAL wird genau umgekehrt wie ein ROM programmiert. Statt daß zu jeder Adresse ein Bit-Muster angegeben werden kann, legt man bei einem PAL fest, für welche Kombinationen von Eingangsdaten (also Adressen, R/W, CFGIN usw.) eine bestimmte Ausgangsleitung auf Null gelegt werden soll. Im Normalfall sind die Ausgangsleitungen da¬ her high. Betrachtet man nun obige Adreßtabelle, stellt man fest, daß es wesentlich mehr Daten-Bits gibt, die Hi (== 1) sind (invertieren nicht vergessen!),als solche, die auf 0 liegen. Dies vereinfacht die Pro¬ grammierung des PAL. Die Verwendung eines PAL schränkt leider die Selbstbaumöglichkeiten ein, da nicht jeder Hobbybastler ein PAL-Programmiergerät besitzt. 684 Amiga intern Dabei sind die Preise für PAL-Bausteine inzwischen deutlich unter die 10-Mark-Grenze gesunken! Wie funktioniert jetzt die Adreßdekodierung? Jede Erweiterungskarte besitzt einen 8-Bit-Speicherbaustein mit abschaltbaren (Tri-State) Ausgängen, z.B. vom Typ 74LS374. In ihm wird die Adresse abge- siMichert, die der Amiga der Karte zuweist (48/4A in obiger Tabelle). Diese Adresse wird an den Eingang eines sog. Adreßkomparators ge¬ legt. Dieser Chip, z.B. vom Typ 74F688, vergleicht die Adresse auf dem Adreßbus (A23 bis Al6, wenn die Karte 64 KByte Speicher be¬ ansprucht) mit der Adresse in dem 74LS374. Sind beide identisch, er¬ kennt dies der Komparator und signalisiert der Karte, daß sie vom Prozessor angesprochen wird. Die Ausgänge des 74LS374 werden aller¬ dings erst eingeschaltet, wenn die Karte konfiguriert ist. Damit die Karte vorher auch an einer definierten Adresse liegt, befinden sich parallel zu den Ausgängen noch 8 Widerstände, die zum Teil gegen +5 Volt oder Masse geschaltet sind, und zwar genau so, daß sich die Adresse $E80000 ergibt - die Karte liegt daher vor der Konfiguration bei $E80000 und danach an der Adresse, die vom Amiga in den 74LS374 geschrieben wird - sie ist autokonfiguriert! Der Aufbau des ROM Wie man aus der vorangegangenen Tabelle entnehmen konnte, besteht die Möglichkeit, eine beliebige Software in einem ROM auf der Er¬ weiterungskarte unterzubringen. Der Inhalt dieses ROM wird automa¬ tisch ins RAM kopiert, wenn das vierte Bit in 00/02 gesetzt ist. Dann beginnt der Amiga, an der Adresse, die er in 28/2A und 2C/2E fin¬ det, nach einem ROM Ausschau zu halten. Dabei hat der Erbauer der Karte die Freiheit, dieses ROM vier, acht oder sechzehn Bits breit zu machen, sein Inhalt wird auf jeden Fall ordentlich im Speicher zu¬ sammengebaut. Verwendet werden dabei entweder die Daten-Bits D12-D15, D8-D15 oder alle sechzehn. Das ROM muß folgenden Inhalt haben (in Worten): Adrew _ Funktion _ I) Biti 15 und 14 legen die Breite dei ROM feit: 00 = Vier Bit breit 01 = Acht Bit breit 11 = Sechtehn Bit breit Die Expansionsarchitektur 685 Bits 13 und 12 bestimmen, wann der (eventuell] im ROM vorhandene Boot-Code aufgerufen wird: 00 = Nie 01 = Beim Installieren der Karte 10 = Beim Ausführen von BindDrivers Achtung: Wenn der Boot-Code aufgerufen werden soll, muß der Kon¬ figurationsbereich der Karte auch nach der Konfiguration noch er¬ reichbar sein! Die restlichen Bits sind vorerst noch alle Null. 2 GröBe des ROM in Byte 4 Adresse des Programms, das beim Konfigurieren aufgerufen werden soll (als Offset vom ROM-Anfang). 6 Adresse des Programms, das sum Booten aufgerufen werden soll (s. o.) 8 Offset auf den Namen der Karte, des Herstellers oder sonstwas, sumindest eine 0. A Immer Null C Immer Null Ab hier kann der ROM-Inhalt beginnen, z.B. ein Harddisk-Treiber. Beim Aufruf einer der beiden Adressen werden vom Amiga folgende Registerinhalte übergeben: A7 Zeiger auf einen Stack von mindestens 2 KByte GröSe A6 ExecBase AB ExpansionBase A3 ConfigDev-Struktur A2 Adresse des ROM-lnhalts im RAM (wohin es kopiert wurde) AO Basisadresse der Karte nach der Konfiguration Rückgabe wert: DO Wenn man in DO den Wert 0 surückgibt, wird der Speicher, in den der Amiga das ROM kopiert hatte, wieder sum freien Systemspeicher hinsugefügt. 4.2 Die Software Verantwortlich für alles, was mit den Expansionkarten zu tun hat, ist beim Amiga, wie könnte es anders sein, eine Library: Die Expan- sion.library. Sie wird von Exec unmittelbar nach einem Reset aufge¬ rufen und konfiguriert danach alle Erweiterungsboards, die sie findet, weist ihnen Basisadressen zu und kopiert den Inhalt eventueller ROMs ins RAM. Normalerweise hat der Programmierer mit der Arbeit dieser Library nichts zu tun. Auch der Erbauer einer Erweiterungskarte muß 686 Amiga intern sich nicht darum kümmern, wie seine Karte nun erkannt und konfi¬ guriert wird, Hauptsache es geschieht. Bis auf eine Ausnahme: Die Expansion.library erzeugt ja einige Daten, die eng mit der Karte verbunden sind: ihre Basisadresse, ob die Kon¬ figuration erfolgreich war usw. Diese Daten werden jetzt von der Software benötigt, die diese Karte betreiben will. Denn wie soll sie ohne die Basisadresse der Karte auf diese zugreifen? Die normale Methode, einen Treiber für eine Erweiterungskarte zu installieren, arbeitet mittels des CLI-Kommandos "binddrivers". Dieser Befehl, der normalerweise in der Startup-Sequence aufgerufen wird, durchsucht das Expansion-Directory nach Treibern, die zu einem Board, daß bei der Autokonfiguration erkannt wurde, passen. So wer¬ den automatisch nur diejenigen Treiber installiert, deren zugehörige Hardware auch tatsächlich vorhanden ist. Um einen Treiber für bind¬ drivers erkennbar zu machen, muß man ein .info-File für ihn erzeu¬ gen. In diesem trägt man im Tooltypes-Feld "PRODUCT" ein. Findet binddrivers dieses Wort, liest es aus dem Rest der Zeile die Hersteller¬ und Produktnummer, um zu erkennen, für welche Erweiterungskarte der Treiber gedacht ist. Die Syntax ist dabei folgende. Erst kommt, wie gesagt, "PRODUCT" gefolgt von einem "=". Jetzt kommt die Herstellernummer, dann ein "/", dahinter die Produktnummer. Soll der Treiber mit allen Produkten eines Herstellers arbeiten, läßt man den "/" samt Produktnummer ein¬ fach weg.Soll er nur mit bestimmten Treibern arbeiten, folgt nach der ersten Angabe einfach ein "|", danach eine zweite. Dies kann folgen¬ dermaßen aussehen: PRODUCT=100/01 :HersteUer 100, Produktnunner 01 PRODUCT^IOO :HersteUer 100, alle Karten PR00UCT=100/01I100/02 :Hersteller 100, P. 01 und 02 Alle Angaben dürfen keine Leerzeichen enthalten! Binddrivers lädt den Driver (mittels LoadSeg), wenn es mindestens ein passendes Board gefunden hat, und ruft ihn über "InitResident" auf. Kehrt dieses mit NULL zurück, wird der geladene Treiber wieder aus dem Speicher entfernt (UnLoadSeg). Der geladene Treiber muß zuerst die Expansion.library öffnen. Man bekommt die Adresse der ExpansionBase-Struktur zurück. Diese hat folgenden Aufbau: \ Die Expansionsarchitektur 687 struct ExpansionBase { struct Library LibNode; UBYTE Flags; UBYTE pad; APTR ExecBase; APTR SegList; struct CurrentBinding CurrentBinding; struct List BoardList; struct List MountList; UBYTE AllocTable[TOTALSLOTS]; struct SignalSemaphore BindSemaphore; struct Interrupt Int2List; struct Interrupt IntdList; struct Interrupt Int7List; >; (zu finden in “Expansion.h“) Benötigt wird davon nur die CurrentBinding-Struktur: struct CurrentBinding ( struct ConfigOev *cb_ConfigDev; UBYTE *cb_FileNa(ne; UBYTE *cb_ProductString; UBYTE **cb_ToolTypes; >;(zu finden in "ConfigvarsTh") In ihr findet man den Filenamen des geladenen Treibers (cb_FileName), den "PRODUCT usw."-Text aus dem .info-File (cb_ProductString) und einen Zeiger auf das Tooltypes-Feld (cb_ToolTypes). Das Wichtigste für den Treiber sind aber die Con- figDev-Strukturen. Die CurrentBinding-Struktur enthält einen Zeiger auf die erste dieser Strukturen. Jede ConfigDev-Struktur steht für eine Erweiterungskarte, die den Angaben hinter PRODUCT entspricht. Sie hat folgenden Aufbau (ebenfalls in "Configvars.h" zu finden): struct ConfigOev ( struct Node UBYTE UBYTE Struct ExpansionRom APTR APTR UUORD UWORD APTR Struct ConfigOev ULONG >; cd_Node; cd_Flags; cd_Pad; cd_Rom; cd_BoardAddr; cd_BoardSize; cd_SlotAddr; cd_SLotSize; cd Oriver; •cd“NextCO; cd_Unused[4]; Hier findet der Treiber nun endlich alles, was er benötigt. Die tatsächliche Adresse der Karte steht in cd BoardAddr. Ob der Auto- 688 Amiga intern konfigurationsprozeß erfolgreich war, steht in cd_Flags:. Bit 0 heißt CDF_^SHUTUP. Ist es 1, wurde die Karte mit Shut_up_forever zum Schweigen gebracht. Gleiches sollte auch der Treiber tun, wenn er CDF_SHUTUP gesetzt vorfindet. Bit 1, CDF_CONFIGME, sagt, daß diese Karte noch keinen Treiber besitzt. Dieses Bit sollte vom Treiber, nachdem er von "binddrivers" auf gerufen wurde, gelöscht werden. Außerdem muß man die Adresse der "node" des Treibers incd_Driver eintragen. Die "node" kann z.B. eine Device-Node, Library-Node oder Resource-Node sein, je nachdem, was für einen Treiber man hat. Die cd_Rom-Struktur entspricht übrigens der Adreßtabelle für Er¬ weiterungskarten am Anfang dieses Kapitels, allerdings nur der Be¬ reich von 00/02 bis 3C/3E. Statt in Nibbles wie auf der Karte ist sie hier ordentlich im Byte-Format abgespeichert. Cd_NextCD zweigt auf die nächste ConfigDev-Struktur, die dem im Tooltype angegebenen PRODUCT entspricht. Amiga und PC 689 5. Das Januskonzept - Amiga und PC "Januskonzept", so nennt Commodore die Kombination eines IBM-PC ode AT-kompatiblen Computers mit dem Amiga 2000. Natürlich besteht diese Kombination nicht darin, einen billigen IBM- Nachbau auf seinen Amiga zu stellen, sondern in einer speziellen Hardware-Kopplung zwischen beiden Computern. Der IBM-Rechner ist dabei auf einer Steckkarte untergebracht, dem sogenannten Bridge¬ board. Diese Karte schlägt auch im wahrsten Sinne des Wortes eine Brücke vom Amiga zum PC, da sie die einzige Erweiterungsplatine im A2000 ist, die sowohl in einem Amiga als auch in einem IBM-Steck- platz eingesteckt wird. Diese Karte verbindet beide Computerwelten. Die Vorteile dieser Kombination liegen auf der Hand; Man hat sowohl einen vollwertigen Amiga als auch PC, die beide mit ein und dersel¬ ben Peripherie auskommen. Dies spart sowohl Platz als auch Geld. Eine 60-MByte-Festplatte kostet eben weniger als zwei 30-MByte- Platten. Der PC benutzt normalerweise die Tastatur und den Monitor des Amiga, seine serielle und parallele Kommunikation erledigt er über die entsprechenden Buchsen des Amiga. Der Amiga hingegen profitiert von der billigen IBM-Festplatte, die gerade mal die Hälfte einer Commodore-Festplatte kostet und pro¬ blemlos vom AmigaDOS mitbenutzt werden kann. Damit ist ein voll¬ wertiger Grundbetrieb des PC einschließlich Farbgrafik möglich. Wer mehr will, kann entsprechende PC-Erweiterungskarten in die PC- Slots im Amiga stecken. Wie ist die PC-Karte nun aufgebaut? Amiga und PC 691 Abbildungen 5.1 und 5.2 zeigen den Grundaufbau der gesamten Karte. 5.1 zeigt den PC. Da dieser ja fast die gesamte Amiga-Peripherie be¬ nutzt, findet man hier nur ein absolutes Minimalsystem vor. Zentrum ist der Prozessor eines IBM-PC: der 8088-Prozessor von Intel. Wer hier jetzt einen schnellen, modernen 16- oder gar 32-Bit-Prozessor erwar¬ tet, wie es an der Verbreitung des PCs gemessen, zu erwarten wäre, wird enttäuscht. Der 8088 ist lediglich ein 8-Bit-Prozessor, noch dazu ein recht betagter, der bei der niedrigen Taktfrequenz des Bridge¬ boards von 4.77 MHz kaum schneller als ein C64 ist. Eine Besonderheit des 8088 sollte noch erwähnt werden. Seine Adres¬ sen- und Datenleitungen sind nicht getrennt aus dem Gehäuse heraus¬ geführt, sondern werden gemultiplext. D.h. am Anfang eines Speicherzugriffes legt er die Adressen auf den Bus, hat der angespro¬ chene Baustein diese übernommen, wird auf die unteren 8 Adreßlei¬ tungen des Datenbusses geschaltet. Daher auch die mit "A/D" bezeich- nete Leitung in Abbildung 5.1. Unter dem Prozessor ist ein Chip eingezeichnet, den es eigentlich gar nicht gibt, besser gesagt, er wird nicht mitgeliefert, sondern muß gekauft werden. Nur ein leerer Sockel auf dem Bridgeboard zeugt von seiner (Nicht-)Existenz. Bei diesem geheimnisvollen Chip handelt es sich um einen Mathematik-Coprozessor vom Typ 8087. Da man auch heute noch einige Märker für ihn auf den Tisch blättern muß und nicht jeder diesen Rechenknecht benötigt, verzichten so gut wie alle PC-Hersteller auf diesen Chip und überlassen dem Käufer eine even¬ tuelle Nachrüstung (Ausnahmen machen nur einige Nobelmarken, bei denen der Preis für einen 8087 im Vergleich zum Gesamtpreis nicht mehr ins Gewicht fällt.) Fast die gesamte Steuerlogik eines IBM-PC sowie die im ursprüngli¬ chen PC vorhandenen Timer und Interrupt-Chips sind im Laufe der Jahre (und mit der explosionsartigen Zunahme von PC-Clones, wie man die unzähligen Nachbauten des IBM-Originals nennt) von ver¬ schiedenen Halbleiterfirmen in hochintegrierten Custom-Chips verei¬ nigt worden. Beim Bridgeboard wird sogar nur noch ein einziges die¬ ser Chips verwendet, in Abbildung 5.1 unter dem 8087 mit dem Na¬ men PC-Custom-Chip zu finden. Als Haupspeicher verfügt der IBM über 512 KB dynamisches RAM. Die Ansteuerung dieser RAM-Chips übernimmt ein von Commodore entwickeltes PAL vom Typ 16L8. Der Datenbus ist über einen 74LS245 Datenbuspuffer mit den RAM verbunden, die Adressen über 692 Amiga intern zwei 74HCT257-Multiplexer. Dabei handelt es sich allerdings nicht um die kombinierten Daten-/Adreßleitungen vom 8088, sondern um ganz normale, getrennte Adreß- und Datenbusleitungen. Dies wird durch drei 74LS373-Bausteine erledigt. Sobald der 8088 die Adressen aus¬ gibt, werden sie von diesen drei Chips gespeichert und während der Dauer des gesamten Speicherzugriffs auf den PA (PC Adresse) Leitun¬ gen bereitgestellt. Sobald der Prozessor die unteren Adreßleitungen auf Datenbusleitungen umschaltet, werden sie über einen 74LS245-Puffer mit dem PC-Datenbus (PD) verbunden. An diesem liegen, wie man in 5.1 sehen kann, der Floppy-Controller, das RAM und das gesamte Amiga-Interface. Das mit BIOS-ROM bezeichnete Chip ist, wie der Name schon sagt, ein ROM. Es ist 16 KByte groß und enthält das sogenannte "Basic In¬ put Output System" des PCs. Dieses BIOS ist die unterste Ebene des IBM-Betriebssystems, man kann es etwa (aber auch nur etwa) mit dem EXEC des Amiga vergleichen. Das restliche Betriebssystem, das MS- DOS, wird nach einem Reset von Diskette in den Hauptspeicher gela¬ den. Zu diesem Zweck dient der Floppy-Controller links oben. Er ist vom Typ FDC 9268. An ihn wird auch das mitgelieferte 5i-Zoll- Laufwerk angeschlossen. Der Floppy-Anschluß auf der PC-Karte ist, genauso wie der vom Amiga, zum Shugart-Bus kompatibel. Man kann die Laufwerke also jederzeit austauschen. (Näheres zur Belegung des Shugart-Busses findet man im Kapitel 1.3.5.) Hauptgrund dafür, daß der PC nicht auch noch den Floppy-Controller des Amiga mitbenutzt ist allerdings, daß eine beträchtliche Anzahl von Programmen für den PC direkt auf den Floppy-Controller zugreifen und dabei natürlich die Original-Register erwarten. Das Interface zum Amiga Das Blockschaltbild dieses^Interfaces findet man in der Abbildung 5.2. Es besteht im wesentlichen aus einem 128 KByte großen Dual-Port- RAM und zwei hochintegrierten Custom-Chips aus der Commodore- Chip-Küche, dem Data Bus Translator und dem Adreß Bus Translator. Erinnern wir uns noch einmal an die Aufgaben, die dieses Interface zu erledigen hat: Übermittlung der Tastaturdaten vom Amiga zum PC. Umwandlung der IBM-Grafikdaten in ein Amiga-kompatibles Format und Darstellung auf dem Amiga-Bildschirm. Amiga und PC 693 Datenübertragung zwischen serieller und paralleler Schnittstelle des Amiga und dem PC. Übertragung von Files in beide Richtungen. Das Hauptproblem liegt aufgrund der großen Datenmengen und der notwendigen schnellen Bearbeitung bei der Grafik. Der IBM-PC kennt zwei grundsätzliche Darstellungsmodi: monochrome oder farbige Text¬ darstellung und hochauflösende Grafikdarstellung mit entweder 320 oder 640 Punkten pro Zeile. Das Interface stellt nun mit dem Dual-Port-RAM den dazu notwendi¬ gen Bildschirmspeicher zur Verfügung. Dual-Port-RAM bedeutet, daß dieses RAM von zwei Seiten, sprich Amiga und PC, unabhängig von¬ einander verwendet werden kann. Das RAM auf der PC-Karte ist kein "echtes" Dual-Port-RAM, da nicht beide Prozessoren tatsächlich gleichzeitig darauf zugreifen können. In diesem Fall muß nämlich derjenige, der später kommt, warten, bis der andere fertig ist. Diese Einschränkung ist aber für die Aufgabe, die das RAM zu erfüllen hat, nicht von Belang. Es erfüllt völlig seinen Zweck. Befinden sich die Grafikdaten erst einmal im Dual-Port-RAM, kann sie der Amiga auslesen und in einem eigenen Screen innerhalb des Amiga-Betriebsystems darstellen. D.h. er könnte, wenn es da nicht noch ein kleines Problem gäbe: Das Datenformat ist nämlich unter¬ schiedlich. Auf Amiga-Seite sind die Daten für einen Punkt eines Farb-Grafikbildschirm auf unterschiedliche Bit-Planes verteilt, beim IBM dagegen enthalten je zwei Bits eines Grafik-Bytes gemeinsam die Daten für einen Punkt. Da es sehr lange dauern würde, die IBM-Gra- fikdaten mit dem 68000 in das Amiga-Format umzurechnen, erledigt dies einer der beiden Custom-Chips, der Data Bus Translator, auto¬ matisch, in dem er aus einem Grafikdatenwort des PC, das acht Punkte zu je zwei Bit enthält, zwei Bytes macht, die zu den Amiga- Bit-Planes kompatibel sind. Außerdem ist die Reihenfolge, in denen 68000 und 8088 Worte und Langworte im Speicher ablegen, unter¬ schiedlich. Beim 68000 stehen die oberen acht Bits eines Wortes vor den unteren im Speicher. Bei einem Byte-Zugriff liegen sie eine Adresse niedriger als die unteren acht. Gleiches gilt für Langworte. Das Wort mit den höherwertigen 16 Bits liegt eine Wortadresse vor demjenigen mit den niederwertigen. Beim 8088 ist es dagegen genau umgekehrt, das Low-Byte wird vor dem Hi-Byte im Speicher abgelegt. Auch dieses Problem löst der Data Bus Translator. 694 Amiga intern Bei der Übertragung der Schnittstellendaten wurde ein anderer Weg gewählt. Sämtliche Register der seriellen und der parallelen Schnitt¬ stelle (und auch die Steuerregister für die Grafik) wurden im zweiten Custom-Chip, dem Adreß Bus Translator, untergebracht. Dieser macht zwar nicht viel mit diesen Daten, aber er ermöglicht dem Amiga das Lesen und Verarbeiten dieser Werte. Außerdem wird bei einem Zu¬ griff auf bestimmte I/O-Register ein Interrupt im Amiga ausgelöst, so daß dieser die Daten in den Registern bearbeiten kann. Auch für die Tastatur gibt es eine ganz spezielle Emulation. Der PC-Custom-Chip in Abbildung 5.1 enthält unter anderem einen PC-Tastaturanschluß. Eine solche Tastatur überträgt ihre Daten ähnlich wie die Amiga-Ta- statur seriell mittels einer Daten- und einer Taktleitung. Statt diese beiden Leitungen des PC-Custom-Chips jetzt aber mit einer Tastatur zu verbinden, führen sie zum Adreß-Bus-Translator-Chip. Will der Amiga einen Tasten-Code an den PC übertragen, schreibt er ihn ein¬ fach in ein spezielles Register dieses Chips, welcher ihn dann, genau wie es eine reale IBM-Tastatur tun würde, seriell an den PC-Custom- Chip sendet. Der wechselseitige Zugriff auf Massenspeicher wie Harddisks und Diskettenlaufwerke läuft lediglich über das Dual-Port-RAM ab. Die entsprechenden Treiber holen dann die Daten statt von einem realen Speichermedium aus diesem RAM bzw. schreiben sie zurück. Die Aufgaben der beiden Interface-Custom-Chips sind also folgen¬ dermaßen verteilt; Data Bus Translator Autokonfiguration (siehe Kapitel 4) Umwandlung Datenformat PC -> Amiga Adreß Bus Translator Zugriffssteuerung auf das Dual-Port-RAM und die gemeinsa¬ men I/O-Register. Interrupt-Logik zwischen Amiga und PC - I/O- und Grafik- Controller-Register des PC Steuerregister, um das Interface vom Amiga aus zu kontrollieren Tastaturemulation Amiga und PC 695 Speicherbelegung der PC-Karte von seiten des Amiga Die Basisadresse der Karte wird durch die Expansion.library festge¬ legt. Insgesamt belegt sie 512 KByte Adreßraum im Amiga. Will man die Basisadresse der Karte wissen, muß man sich durch die Daten¬ strukturen der Expansion.library hangeln (Kapitel 4). Die 512 KByte sind in vier 128-KByte-Blöcke aufgeteilt: Basisadresse+CSOOOOO - SIFFFF): Byte-Zugriff Basisadresse+($20000 - $3FFFF): Uort-Zugriff Basisadresse+(S40000 - S5FFFF): Grafik-Zugriff Basisadresse+($60000 - S7FFFF): I/O-Register-Zugriff Bei den ersten drei Bereichen handelt es sich jedesmal um das Dual- Port-RAM, die Daten sind allerdings unterschiedlich gruppiert. Aufgeteilt ist das Dual-Port-RAM wie folgt: $00000 - $0FFFF 64 KB Zwischenspeicher für Disk/Harddisk $10000 - $17FFF 32 KB Farbgrafikspeicher $18000 - $1BFFF 16 KB Parameter RAM (für Textmodus) $1C000 - $1CFFF 8 KB Monochrom Grafikspeicher $1E0O0 - $1FFFF 8 KB I/O Bereich Anhang 697 Anhang: Bibiiotheksfunktionen im Überblick Die nun folgende Tabelle gibt Ihnen einen Überblick über alle ver¬ fügbaren Bibliotheken mit ihren Funktionen. Die Tabelle beginnt je¬ weils als Überschrift mit dem Namen der Bibliothek, in der die fol¬ genden Funktionen liegen. Diese Funktionen werden jeweils mit ihrem negativen Offset hex und dezimal, ihrem Namen und schließlich ihren Übergabeparameter dar¬ gestellt. Die Parameternamen stehen in Klammern hinter dem Funkti¬ onsnamen, die zweite Klammer enthält in gleicher Reihenfolge die Register, in denen diese Parameter übergeben werden müssen. Werden keine Parameter benötigt, wird dies durch () angedeutet. clist.library -$001E -30 InitCLPool (cLPool, size)(A0,D0) -$0024 -36 AllocCList (cLPoolUAI) -$002A -42 FreaCList (cLIstXAO) $0030 •48 FlushCList (cListXAO) ■$0036 -54 SizeCList (cListXAO) -$003C -60 PutCLChar (cList,byteXA0,D0) $0042 -66 GetCLChar (cListXAO) -$0048 -72 UnGetCLChar (cList,byte)(A0,D0) -$004E -78 UnPutCLChar (cListXAO) -$00S4 -84 PutCLUord (cList,word)(A0,D0) -$00SA -90 GetCLUord (cList)(A0) -$0060 -96 UnGetCLUord (cList,word)(A0,D0) -$0066 -102 UnPutCLUord (cList)(A0) -$006C -108 PutCLBuf (cList.buffer,length)(A0,A1,D1) -$0072 -114 GetCLBuf (cList,buffer,maxLength)(AO,AI,D1) -$0078 -120 HarkCList (cList,offset)(AO.DO) •$007E -126 IncrCLHark (cList)(A0) -$0084 -132 PeekCLHark (cList)(A0) -$008A -138 SplitCList (cList)(A0) -$0090 -144 CopyCList (cList)(A0) -$0096 -150 SubCList (cList,index,length)(A0,D0,D1) -$009C ■156 ConcatCList (sourceCList,destCList)(A0,A1) console.library -$002A -42 CDInputHandler (events,device)(A0,A1) -$0030 -48 RawKeyConvert (events.buffer,length.keyMap) (A0,A1,D1,A2) diskfont.library -$001E -30 OpenDiskFont (textAttrXAO) -$0024 -36 AvailFonts (buffer,bufBytes,flags)(A0,D0,D1) 698 Amiga intern dos.library -$001E -30 Open (naine,accessMode)(D1,D2) -$0024 -36 Close (fUe)(D1) -$002A -42 Read (file,buffer,length)(D1,D2,D3} -$0030 -48 Urite (fUe,buffer,length)(D1.D2,D3) -$0036 -54 Input () -$003C -60 Output () -$0042 -66 Seek (fUe,position,offset)(D1,D2,D3) -$0048 -72 DeleteFUe (nameXDI) -$004E -78 Rename (oldNaine,neuNaflie)(D1,D2) -$0054 -84 Lock (naine,type)(D1,D2) -$005A -90 UnLock (lock)(D1) -$0060 -96 DupLock (lockXDI) -$0066 -102 Examine (lock,fileInfoBlock)(D1,D2) -$006C -108 ExNext (lock,fileInfoBlock)(D1,D2) -SO072 -114 Info (lock,paraineterBlock)(D1,D2) -$0078 -120 CreateOir (nameXDI) -$007E -126 CurrentDir (lockXDI) -$0084 -132 loErr () -$008A -138 CreateProc (name.pri,segList,stackSizeXD1,D2,D3,D4} -$0090 -144 Exit (returnCodeXDI) -$0096 -150 LoadSeg (fileNameXDD -$009C -156 UnLoadSeg (segmentXDI) -$00A2 -162 GetPacket (waitXDI) -$00A8 -168 QueuePacket (packetX01) -$00AE -174 OeviceProc (nameXOl) -$OOB4 -180 SetConment (name,connientXD1 ,D2) -$OOBA -186 SetProtection (naiiie,maskXD1,D2) -$OOCO -192 DateStamp (dateXDI) -$00C6 -198 Delay (timeoutXDI) -$00CC -204 WaitForChar (fIle,timeoutXDI,D2) -$0002 -210 ParentDir (lockXDI) -$0008 -216 Islnteractive (file)(D1) -$OOOE -222 Execute (string.file,file)(D1,D2,D3) exec.library -$001E -30 Supervisor () -$0024 -36 Exitlntr () -$002A -42 Schedule () -$0030 -48 Reschedule () -$0036 -54 Switch () -$003C -60 Dispatch () -$0042 -66 Exception () -$0048 -72 InitCode (startClass,version)(D0,D1) -$004E -78 InitStruct (initTable,memory,size)(A1,A2,D0) -$0054 -84 MakeLibrary (funclnit.structinit,liblnit.dataSize, codeSize) (A0,A1,A2,D0,D1) -$005A -90 MakeFunctions (target,functionArray,funcDispBase) (A0,A1,A2) -$0060 -96 FindResident (name)(A1) -$0066 -102 InitResident (resident,segList)(A1,D1) -$006C -108 Alert (alertNijii,parameters)(D7,A5) -$0072 -114 Debug () -$0078 -120 Disable () -$007E -126 Enable () -$0084 -132 Forbid () Anhang 699 ■$008A -138 Permit () -$0090 -144 SetSR (newSR,mask}(D0,D1} -$0096 -150 SuperState (} -$009C -156 UserState (sysStackXDO) -$00A2 -162 SetlntVcctor (intNumber,Interrupt)(D0,AI) -$00A8 -168 AddlntServer (IntNumber,Interrupt)(D0,AI) -$00AE -174 RemlntServer (intNumber,Interrupt>(D0,AI) -$00BA -180 Cause (interruptXAl) -$00BA -186 Allocate (freeList,byteSize)(A0,D0) -$00C0 -192 Deallocate (freeList,me(noryBlock,byteSizeXA0,A1,D0} -$00C6 -198 AUocHem (byteSize,requireinents>(D0,D1 > -$00CC -204 AUocAbs (byteSize, locationXD0,A1) -$0002 -210 FreeMem (nieinoryBlock,byteSizeXA1,D0) -$0008 -216 AvailHem (requirementsXDI) -$OOOE -222 AUocEntry (entry)(A0) -$00E4 -228 FreeEntry (entryXAO) -$00EA -234 Insert (list,node,predXA0,A1,A2) -$00F0 -240 AddHead (list,nodeXA0,A1) -$00F6 -246 AddTail (list,nodeXA0,A1) -$00FC -252 Remove (node)(A1) -$0102 -258 RemHead (listXAO) -$0108 -264 RemTail (listXAO) -$010E -270 Enqueue (list,node)(A0,A1) -$01U -276 FindName (list,name)(A0,A1) -$011A •282 AddTask (task,initPC,finalPC)(A1,A2,A3) -$0120 -288 RemTask (taskXAl) -$0126 •294 FindTask (name)(Ai) -$012C -300 SetTaskPri (task,priority)(A1,DO) -$0132 -306 SetSignal (newSignals,signalSet)(D0,D1) -$0138 -312 SetExcept (newSignals,signalSet)(D0,D1) -$013E -318 Uait (signalSet)(DO) -$0144 •324 Signal (task,signalSet)(A1,D0) -$014A •330 AllocSignal (signalNun)(DO) -$0150 -336 FreeSignal (signalNum)(DO) -$0156 -342 AllocTrap (trapNum)(DO) -$015C -348 FreeTrap (trapNun)(DO) $0162 -354 AddPort (port)(A1) -$0168 -360 RemPort (port)(A1) -$0168- -366 PutHsg (port,message)(A0,A1) -$0174 -372 GetHsg (port)(A0) -$017A -378 ReplyHsg (message)(A1) $0180 -384 UaitPort (port)(A0) -$0186 -390 FindPort (naine)(A1) -$018C -396 AddLibrary (library)(A1) $0192 -402 RemLibrary (library)(A1) $0198 -408 OldOpenLibrary (libNanie)(A1) -$019E -414 CloseLibrary (library)(A1) -$01A4 -420 SetFunction (library,funcOffset,funcEntry)(A1,A0,D0) -$01AA -426 SumLibrary (library)(A1) $01 BO -432 AddDevice (device)(A1) -$01B6 -438 Renfievice (device)(A1) -$01BC -444 OpenOevice (devNaine,unit,ioRequest,flags)(A0,D0,A1,D1) -$01C2 -450 CloseDevice (ioRequest)(A1) -$01C8 -456 DoIO (ioRequest)(A1) -$01CE -462 SendIO (ioRequest)(A1) $0104 -468 ChecklO (ioRequest)(Al) -$01DA -474 UaitIO (ioRequest)(A1) -$01E0 -480 AbortIO (ioRequest)(A1) 700 Amiga intern -*01E6 -486 AddRescource (rescource)(A1) -$01EC -492 Renaescource (rescourceXAl) -$01F2 -498 OpenRescource (resNatne,version)(Al ,D0) -$01F8 -504 RawIOInit () -*01FE -510 RawMayGetChar () -$0204 -516 RawPutChar (char}(D0) -$020A -522 RawOoFmt <) -$024C -588 ReleaseSemaphoreLtst (List)(A0) -$0252 -594 FindSemaphore (SignalSemaphoreXAO) -$0258 -600 AddSemaphore (SignalSemaphore) (AO) FEHLERHAFT !!! -$025E -606 RetnSemaphore (SignalSemaphoreXAO) -$0264 -612 SunKickOata (SignalSemaphore)(A0) -$026A -618 AddMemList (Size,Attr,Pri,BasePtr,Name)(D0,D1,D2,A0,A1) -$0270 -624 CopyMem (SourcePtr,DestPtr,Size)(A0,A1,D0) -$276 - 630 Cop^emOuick (SourcePtr,DestPtr,Size)(A0,A1,D0) graphics.library -$001E -30 BltBitMap (srcBitMap,srcX,srcY,destBitMap,destX,destY, sizeX,sizeY,minterm,mask,tempA) (A0,D0,D1,A1,D2,D3,D4, 05,06,07,A2) -$0024 -36 BltTemplate (source,srcX,srcMod,destRastPort,destX,destY, sizeX,sizeY)(A0,00,01,A1,02,03,04,05) -$002A -42 ClearEOL (rastPort)(A1) -$0030 -48 ClearScreen (rastPort)(A1) -$0036 -54 TextLength (RastPort,String,count)(A1,A0,00) -$003C -60 Text (RastPort,String,count)(A1,A0,00) -$0042 -66 SetFont (RAstPortI0,textFont)(A1,A0) -$0048 - 72 OpenFont (textAttrXAO) -$004E -78 CloseFont (textFont)(A1) -$0054 -84 AskSoftStyle (rastPort)(A1) -$005A -90 SetSoftStyle (rastPort,style,enable)(A1,00,01) -$0060 -96 AddBob (bob,rastPort)(A0,A1) -$0066 -102 AddVSprite (vSprite,rastPort)(A0,A1) -$006C -108 OoCollision (rastPort)(A1) -$0072 -114 OrawGList (rastPort,viewPort)(A1,A0) -$0078 -120 InitGels (duniTiyHead,dummyTai l,GelsInfo)(A0,A1 ,A2) -$007E -126 InitMasks (vSprite)(A0) -$0084 -132 RemIBob (bob,rastPort,viewPort)(A0,A1,A2) -$008A -138 RemVSprite (vSprite)(A0) -$0090 -144 SetCollision (type,routine,gelsInfo)(00,A0,A1) -$0096 -150 SortGList (rastPort)(A1) -$009C -156 AddAnimObj (obj,animationKey,rastPort)(A0,A1,A2) -$00A2 -162 Animate (aniniationKey,rastPort)(A0,A1) -$00A8 -168 GetGBuffers (animationObj,rastPort,doubleBuffer) (A0,A1,00) Anhang 701 -$00AE -174 InitGMasks (anImationObjXAO) -$00B4 -180 GelsFuncE () -SOOBA -186 GelsFuncF () -SOOCO -192 LoadRGBA (viewPort,colors,count)(A0,A1,D0) -$00C6 -198 InitRastPort (rastPortXAl) -SOOCC -204 InitVPort (viewPort)(AO) -$0002 -210 MrgCop (view)(A1} -$0008 - 216 HakeVPort (view,viewPortXA0,A1) -SOOOE -222 Loadview (view)(A1) -$00E4 -228 UaitBlit () -SOOEA -234 SetRast (rastPort,colorXA1,D0) -SOOFO -240 Move (rastPort,x,yXA1,D0,01) -$00F6 - 246 Oraw (rastPort,x,yXA1,D0,D1) -SOOFC -252 AreaHove (rastPort.x.yXAl.DO.DI) -$0102 -258 AreaOraw (rastPort,x,y)(A1,D0,01) -$0108 -264 AreaEnd (rastPort)(A1) -SOIOE -270 UaitTOF () -$0114 -276 QBlit (blitXAl) -$011A -282 InitArea (areaInfo,vectorTable,vectorTableSizeXA0,A1,D0) -$0120 -288 SetRGB4 (viewPort,index,r,g,b)(A0,D0,D1,D2,D3) -$0126 -294 QBSBlit (blit)(A1) -$012C -300 BUClear (ineflK>ry,size,flags)(A1,D0,01) -$0132 - 306 RectFUl (rastPort,xl,yt,xu,yu)(A1,00,D1,D2,D3) -$0138 -312 BltPattern (rastPort,ros,xl,yl,niaxX,maxY,f{UBytes) (AI,AO,00,01,02,03,04) -$013E -318 ReadPixel 6 FreeCopList (coptistXAO) 2 ClipBlit (srcrp,srcX,srcY,destrp,destX,destY,sizeX, sizeY,minterinXA0,D0.D1,A1,D2,D3,D4,D5,D6) 18 XorRectRegion (rgn.rectXAO.AI) 4 FreeCprList (cprlist)(AO) '0 GetColorMap (entriesXDO) ’6 FreeColorMap (colonnap)(A0) 12 GetRGBA (colormap.entryXAO.DO) 8 ScroUVPort (vpXAO) '4 UCopperListlnit (copperlist,num)(A0,DO) lO FreeGBuffers (animationObj.rastPort, doubleBufferXA0,A1,D0) -606 BltBitHapRastPort {srcbm,srcx,srcy,destrp,destX,destY, sizeX,sizeY,niinterXA0,D0,D1,A1,D2,D3,D4,DS,D6) icon.library •S001E -30 GetUBObject (nameXAO) ■$0024 ■36 PutUBObject (nanie,objectXA0,A1) -$002A -42 Getlcon (nanie,icon,freelist)(A0,A1,A2) -$0030 -48 Puticon (naiiie,iconXA0,A1) -$0036 -54 FreeFreeList (freelistXAO) ■$003C -60 FreeWBObject (UBObjectXAO) -$0042 •66 AUocUBObject () -$0048 -72 AddFreeList (freel ist,inem,size)(A0,A1 ,A2) -$004E -78 GetOiskObject (nameXAO) ■$0054 -84 PutOiskObject (name,diskobj)(AO,AI) -$00SA -90 FreeOiskObj (diskobj)(A0) -$0060 -96 FindToolType (toolTypeArray,typeName)(A0,A1) -$0066 ■102 HatchToolValue (typeString,value)(A0,A1) -$006C -108 BunUtevision (newname,oldname)(A0,A1) Intuition. l ibrary -$001E -30 Openlntuition () -$0024 -36 Intuition (ievent)(A0) -$002A -42 AddGadget (AddPtr,Gadget,Position)(A0,A1,00) -$0030 ■48 ClearDMRequest (Uindou)(A0) -$0036 -54 ClearHenuStrip (UindoM)(A0) -$003C -60 ClearPointer (Uindow)(A0) -$0042 -66 CloseScreen (Screen)(A0) -$0048 -72 CloseUindou (Uindow)(A0) -$004E -78 CloseUorkBench () -$0054 -84 CurrentTime (Seconds,Hicros)(A0,A1) -$005A -90 DisplayAlert (AlertNumber,String,Height)(D0,A0.l -$0060 -96 DisplayBeep (Screen)(A0) -$0066 -102 DoubleClick (sseconds,smicros,cseconds,cmicros) (D0,D1,D2,D3) -$006C -108 DrawBorder (Rport,Border,Leftoffset,TopOffset) (AO,AI,DO,Dl) -$0072 -114 Drawimage (RPort,Image,Leftoffset,TopOffset)(AO Anhang 703 -$0078 -120 EndRequest (requester,uindow)(A0,A1) -$007E -126 GetDefPrefs (preferences,size)(A0,D0) -$0084 -132 GetPrefs (preferences,size)(A0.D0) -$008A -138 InitRequester (req)(A0) -$0090 -144 ItemAddress (MenuStrip,MenuNijnber)(AO,DO) -$0096 -150 ModifylDCMP (Window,Flags)(A0,D0) -$009C -156 ModifyProp (Gadget,Ptr,Reg,Flags,HPos,VPos,HBody,VBody) (A0,A1,A2,D0,D1,02,03,04} -$00A2 -162 MoveScreen (Screen,dx,dy}(A0,00,01) -$00A8 -168 MoveUindow (Window,dx,dy}(A0,00,01) -$00AE -174 OffGadget (Gadget,Ptr,Req)(A0,A1,A2) -$O0B4 -180 OffHenu (Window,MenuNunberXAO,00) -$O0BA -186 OnGadget (Gadget,Ptr,Req)(A0,A1,A2) -$OOCO -192 OnMenu (Window,MenuNutnber)(A0,00) -$O0C6 -198 OpenScreen (OSArgs)(AO) -$O0CC -204 OpenWindow (OWArgs)(A0) -$0002 -210 OpenWorkBench () -$0008 -216 PrintIText (rp,itext,left,top)(A0,AI,00,01) -$00OE -222 RefreshGadgets (Gadgets,Ptr,Req)(A0,A1,A2) -$OOE4 -228 RemveGadgets (ReinPtr,Gadget)(A0,A1) -$00EA -234 ReportMouse (Window,Boolean)(A0,00) -$O0F0 -240 Request (Requester,Window)(A0,Ai) -$O0F6 -246 ScreenToBack (Screen)(A0) -$00FC -252 SCreenToFront (Screen)(A0) -$0102 -258 SetOMRequest (Window,req)(A0,AI) -$0108 -264 SetMenuStrip (Window,Menu)(A0,AI) -$010E -270 SetPointer (Window,Pointer,Height,Width,X0ffset,Y0ffset) (AO,AI,00,01,02,03) -$0114 - 276 SetWin^wTitles (Window,windowTitle,screenTitle} (A0,A1,A2) -$011A -282 ShowTitle (Screen,ShowIt)(A0,00) -$0120 -288 SizeWindow (WirKiow,dx,dy)(A0,00,01) -$0126 -294 ViewAddress () -$012C -300 ViewPortAddress (WirxiowKAO) -$0132 - 306 WindowToBack (WirKiow)(A0) -$0138 -312 WindowToFront (WirKiow)(A0} -$013E -318 WindowLimits (WirKiou,ininwidth,ininheight,[naxwidth, maxheight)(A0,00,01,02,03) -$0144 -324 SetPrefs (prefererKes,size,flag)(A0,00,01) -$014A -330 IntuiTextLength (itext)(A0) -$0150 -336 WBenchToBack () -$0156 -342 WBenchToFront () -$015C -348 AutoRequest (WIrKiow,Body,PText,NText,PFlag,NFlag,W,H) (A0,A1,A2,A3,00,01,02,03) -$0162 -354 BeginRefresh (WirKiou)(A0) -$0168 -360 BuiIdSysRequest (Window,Body,PosText,NegText,Flags,W,H) (A0,A1,A2,A3,00,O1,02) -$016E -366 EndRefresh (Window,Coinplete)(A0,00) -$0174 -372 FreeSysRequest (Windou)(A0) -$017A -378 MakeScreen (Screen)(A0) -$0180 - 384 RefliakeOisplay () -$0186 -390 RethinkOisplay () -$018C -396 AllocRemenber (RenienfcerKey,Size,Flags)(A0,00,01) -$0192 -402 AlohaWorkbench (wbport)(A0) -$0198 - 408 FreeRemenber (RenieinberKey,ReallyForget)(A0,00) -$019E -414 LocklBase (dontknou)(00) -$01A4 -420 UnlocklBase (IBLock)(A0) 704 Amiga intern layers.library -S001E -30 InitLayers (liXAO) -$0024 -36 CreateUpfrontLayer (li,bm,x0,y0,x1,y1,flags,bm2)(A0,A1, D0,D1,D2,D3,D4,A2) -$002A -42 CreateBehindLayer (li,hni,x0,y0,x1,y1,fla9s,bm2)(A0,A1,D0, D1,D2,D3,D4,A2) -$0030 -48 UpfrontLayer (li,layer}(A0,A1} -$0036 -54 BehindLayer (li,[ayer)(A0,A1) -$003C -60 HoveLayer (li,layer,dx,dy)(A0,Al,D0,D1) -$0042 -66 SizeLayer (li,layer,dx,dy)(A0,A1,D0,D1) -$0048 -72 ScrollLayer (li,layer,dx,dy)(A0,A1,D0,D1) -$004E -78 BeginUpdate (layerXAO) -$0054 -84 EndUpdate (layer)(A0) -$005A -90 DeleteLayer (l i, layerXAO,AI) -$0060 -96 LockLayer (li,layer)(A0,A1) -$0066 -102 UnlockLayer (l i, layerXAO,AI) -$006C -108 LockLayers (li)(A0) -$0072 -114 UnlockLayers (li)(A0) -$0078 -120 LockLayerlnfo (liXAO) -$007E -126 SwapBitsRastPortClipRect (rp,cr)(A0,A1) -$0084 -132 UhichLayer (li,x,y)(A0,D0,D1) -$008A -138 UnlockLayerlnfo OiXAO) -$0090 -144 NeuLayerlnfo () -$0096 -150 DisposeLayerlnfo (liXAO) -$009C -156 FattenLayerlnfo (liXAO) -$OOA2 -162 ThinLayerlnfo (li)(A0) -$00A8 -168 HoveLayerInFrontOf (layer to_move,layer to be_in_front of)(A0,A1) mathffp.library -$001E -30 SPFix (float)(D0) $0024 -36 SPFlt (integer)(D0) -$002A -42 SPCmp (leftFloat,rightFloat)(D1,D0) -$0030 -48 SPTst (float)(D1) $0036 -54 SPAbs (float)(D0) -$003C -60 SPNeg (float)(D0) $0042 -66 SPAdd (leftFloat,rightFloat)(D1,D0) $0048 -72 SPSub (leftFloat,rightFloat)(D1,D0) -$004E -78 SPMul (leftFloat,rightFloat)(Dl,D0) -$0054 -84 SPDiv (leftFloat,rightFloat)(D1,D0) mathieeedoubbas.library -$001E -30 lEEEDPFix (integer,integer)(D0,Dl) -$0024 -36 lEEEDPFlt (integerXDO) -$002A -42 lEEEDPCmp (integer,integer,integer,integer)(D0,Dl,D2,D3) -$0030 -48 lEEEDPTst (integer,integer)(D0,Dl) -$0036 -54 lEEEDPAbs (integer,integer)(D0,Dl) -$003C -60 lEEEDPNeg (integer,integer)(D0,Dl) -$0042 -66 lEEEDPAdd (integer,integer,integer,integer)(D0,Dl,D2,D3) -$0048 -72 lEEEDPSub (integer,integer,integer,integer)(D0,Dl,D2,D3) -$004E -78 lEEEDPHul (integer,integer,integer,integer)(D0,Dl,D2,D3) -$0054 -84 lEEEDPDiv (integer,integer,integer,integer)(D0,Dl,D2,D3) Anhang 705 mathtrans. l ibrary -$001E -30 SPAtan (floatXDO) -S0024 -36 SPSin (floatXDO) •$002A -42 SPCos (floatXDO) -$0030 -48 SPTan (float)(D0) $0036 -54 SPSincos (leftFloat,rightFloat)(D1,D0) -$003C -60 SPSinh (float)(D0) -$0042 -66 SPCosh (float)(D0) -$0048 -72 SPTanh (float)(D0) -$004E -78 SPExp (float)(D0) -$0054 -84 SPLog (float)(D0) -$005A -90 SPPow (leftFloat,rightFloat)(01,D0) -$0060 -96 SPSqrt (float)(D0) $0066 -102 SPTieee (float)(00) -$006C -108 SPFieee (float)(00) -$0072 -114 SPAsin (float)(00) $0078 -120 SPAcos (float)(00) -$007E -126 SPLoglO (float)(D0) potgo.library -$0006 -6 AllocPotBits (bitsXDO) -$oooc -12 FreePotBits (bits)(00) $0012 -18 WritePotgo (word,inask)(00.D1) timer.library •$002A -42 AddTlnie (dest,src)(A0,Al) -$0030 -48 SubTime (dest,src)(A0,A1) $0036 -54 CmpTime (dest,src)(A0,A1) translator.library -SOOIE -30 Translate (inputstring,inputLength.outputBuffer, bufferSize)(A0,D0,A1,O1)ftg 706 Amiga intern Stichwortverzeichnis 707 Stichwortverzeichnis * .558 512-KByte-Erweiterung8karte . 104 68000 . 13 8361 . 35 8362 . 35 8364 . 35 8367 . 35 8520 . 21 A2000 . 55 A500 . 53 AbortIO . 392, 396, 399 AddHead .287 AddlntServer.437 AddLibrary.382 AddMemList .370 AddPort . 353, 637 AddSemaphore .453 AddTail . 288 AddTask . 33O, 331 Adreßbus . 15 Agnus . 35, 40. 41, 53 Aliasing Dietortion . 243 AlIocAbs .371 Allocate . 3$9 AIlocEntry . 362, 364 AllocMem .360 AllocSignal .337 AIlocTrap .354 Amiga 1000 . 112 AmigaDOS . 519 Analogeingänge . 262 Antworten auf eine Nachricht .345 APTR .302 AS . 15 Ascending Mode . 194 ASSEM .292 Assemblieren mit dem ASSEM . 295 AttemptSemaphore . 449 Audio . 61 Audio-Interrupts . 238 Aufbau eines Devices . 391 AustastlUcke . 117 Autokonfiguration .681 Autovektorielle Unterbrechung . 19 AUX . 551 AvailMem . 371 Ändern einer bestehenden Library .374 Baudrate . 265, 269 BCPL . 593 Beenden eines Tasks . 330 708 Amiga intern BeginlO .392 Betriebsparameter .272 Bewegen von Sprites. 180 Bibliotheken .697 Bibliotheksfunktionen .697 Bildschirmausgabe. 117 Bildschirmfenster. 153 Binddrivers.686 Bit-Map-Adressen . 153 Bit-Map-Biock.676 Bit-Plane . 120, 142 BITDEF .300 Bluter. 187 Blitter-Kontrollregister .200 Blitter-Operation . 188, 190, 216 Blitter-Steuerung . 200 Blocks.667 Boolesche Algebra . 195 Boot-Block .669 Boot-Vorgang .668 Bootbare RAM-Disk .512 BootPri.616 BrUckenkarte . 58, 690 Buster. 88 Bussuweisung . 19 Bussyklen . 121 C . 276 CALLSYS . 299 Cause.438 Centronics-Schnittstelle . 68 ChecklO . 396, 399 Checksumme .669 Chip-RAM . 104 Chip-Register. 113 CIA . 21, 104 CIA-Interrupts .428 CIA-Resource .429 CLI . 520, 642 CLI-Kommando . 562 Ciist.library .697 CloseLibrary .316 CMD .638 CoidCapture-Vektor .494 CON . 550 ConfigDev . 502, 687 Consol-Device .649 Console.library .697 Copper. 131 Copper-Interrupt . 136 Copper-List . 132 Coprozessor . 131 Coprozessor-Slot . 87 CreateDir .530 CreatePort .347 CreateProc . 536 CSI . 555 CurrentBinding .687 Stichwortverzeichnis 709 CurrentDir .... Cursor-Taste . Custom-Chips . 531 . 556 35, 105 Data-Block. Datenbus . D atenrichtungsregister Datenübertragung . DateStamp . Deallocate. Delay . DeleteFile . DeletePort . Denise. Descending Mode . Device . DeviceNode . Dezibel . DFn . DiagArea . Direct Memory Access , Directory . Disable . Disk-Controller . Disk-Objekt . DISKDOCTOR . Disketten . Disketten-Dateien . Diskfont.library . DiskState. DiskType . DMA . DMA-Controller . DMA-Kanäle. DMA-Kontrollregister . DMA-Vorgang . DoIO . DOS-Bibliothek . DOS-Fehlermeldungen DOS-Handler . DOS-Strukturen. DOS.LIBRARY . DOSBase . Doslnfo . DosLibrary . Drucker . DTACK . Dual-Playfield-Modus . DupLock . .676 . 15 . 23 . 97 .544 .370 . 544 . 531 . 348 . 35, 45, 46 . 194 . 636, 391 . 504, 606 . 227 . 549 . 503, 512 . 114 . 533 . 325, 334 . 269 . 571 .670 . 667 . 559 .697 . 535 . 535 . 114 . 114 . 116 . 125 . 115 395, 396, 637 . 520, 523 . 547 . 603, 616 . 593 . 524, 698 . 524 . 606 . 605 .661 . 15 . 147, 167 . 532 Early Read . lOß Echtzeituhr . 55 Eigenes Device. 400 Ein-/Ausgabe . 521, 548 Ein-/Ausgabe-Einheiten . Ein-/Ausgabe-Kanäle . 52 i Eingabegeräte . 263 Empfangen einer Nachricht .. 710 Amiga intern Enable . 325, 334 Enqueue . 289 Environment-Vektor .611 EQU . 293 Ereignisse . 557 Erlauben und Verbieten des Task-Switching .325 Erstellen einer eigenen Library .375 Erweiterung. 84 Erweiterungskarte .679 Erseugen neuer Tasks .328 Even Cycles . 122 Examine .533 Exec . 275 Exec-Library . 310, 698 ExecBase-Struktur .469 Execute .545 Exit .545 ExNext .533 Expansion-Port . 83 ExpansionBase . 500, 687 ExpansionRom .502 Expansionsarchitektur.679 Externe Diskettenlaufwerke . 73 Extra-Halfbright . 144 Farbtabelle . 143 Farbpalette.143 Farbwahl .172 FAST .552 Fast RAM . 39, 104 Fast-Filing-System .676 Fat-Agnus . 54 FCO, FCl, FC2 . 18 Fenster öffnen . 553 File-Header-Block.673 File-List-Block.674 File-Systeme .522 File-Tracer .645 FilelnfoBlock . 533 FileSysStartupMsg . 506 FindName. 289 FindPort. 347, 354 FindSemaphore .454 FindTask . 332, 637 Fis .605 Font . 562 FONT-Befehl . 563 Forbid . 325, 333 FORM .586 FreeEntry . 362, 364 FreeMem .361 FreeSignal .338 FreeTrap .355 Funktionen .276 Funktionstaste .556 Fullen von Flächen . 202 FUlloperation .205 Stichwortverzeichnis 711 Game-Port-Device . 603 Game-Ports. 80 , J61 Gary . 64 Gemultiplexte Adressen. 37 Genlock-SIot . 66 GetMsg. 345 , 358 , 666 GetPacket .. Global-Vector-Table. 606 GiobVec.eu Grafikauflösungen . 12 I Graphics.library . 7 OO Grundaufbau . 36 HAM . 145 Händler . 521 Hardware . H Herta . 226 Hierarchie . 522 Hold-And-Modify . I 45 Horiaontal Quadrature Pulse .267 Hunk_break .. Hunk_bss . 575 Hunk_code . 575 Hunk_data. 575 Hunk_debug . 577 Hunk_end . 577 Hunk_ext . 676 Hunk_header . 577 Hunk_name . 574 Hunk_overlay . 678 Hunk_reIocl6 . 576 Hunk_reloc32 . 575 Hunk_reloc8 . 676 Hunk_symbol . 577 Hunk_unit . 574 Hunks . 673 Hunks-Analysator . 581 Hüllkurve . 244, 246 I/O . 115 IBM'Steckplätse . 5 g Icon.library. 702 .298 IFF . 686 Include .. Into. 636, 668 InfoData . 535 INITBYTE .378 InitCode . 492 iNiTLONG.378 InitResident . 492 InitSemaphore . 445 InitStruct . 376 379 INITWORD .378 Input . 628 Insert . 286 INT2, INT3, INT 6 . 62 Interlace . 118 712 Amiga intern Intierlace-Modus . 169 Interne Speicherverwaltung.366 Interrupt ... 129, 418, 419 Interrupt-Kontrollregister (ICR). 30 Interrupt-Servers .438 Intuition.library .702 IntVector . 420 lO-Handhabung .389 IO_ERROR . 530, 643 lOExt .637 lORequest .389 lOStdExt .638 lOStdReq .390 IPLO, IPLl, IPL2 . 18 Januskonsept.689 Joystick . 256, 261, 663 Kickstart . 111 KickSumData .496 Klangeffekte . 239 Klangqualität . 246 Kollision . 183 Kommunikation swischen Tasks.334 Kontrollregister . 161 LABEL .303 Laufwerk . 79 Lautstärke .239 Lautstärkemodulation .244 Lautstärkeverlauf .244 Layers.library .704 LDS . 16 Library . 309, 372 Library-Aufbau .372 Library-Aufruf .310 Lightpen . 81, 127 List . 283 Listen .282 LoadSeg. 646 Lock .631 Lokale Label . 293 Long Frame . 118 Macros . 296 MakeDosNode . 509 MakeLibrary . 381 Maskierung. 198 Mathffp.library . 704 Mathieeedoubbas.library .704 Mathtrans.library .706 Maus . 256, 663 Mehrere Entries mit einer MemList-Struktur.364 Mehrstimmiger Klang. 260 MemChunk . 367 MemEntry . 363 MemHeader . 366 MemList . 363 Stichwortverzeichnis -— 713 Memory mapped. 22 Memory-List-Stniktur . 3^2 MeMag* .M 2 Meuage-System .. Miniternu .. MinNode.280 MountList . 604, 678 MountNode .. MsgNode .637 MsgPort . 341 _ 345 Multitaaking .. Musik .226 Musiknoten. 248 Narrator-Device . 654 NEWCON .551 NH-. 549 Node-Struktur.278 Noten . 248 Obertöne .231 ObtainSemaphore .. ObtainSemaphoreList .. Odd Cycles . 222 Oktanten .209 OldOpenLibrary .. Op«n.. OpenDeviee . 392 , 637 OpenLibrary . 312 , 314 , 534 Output . 629 OVL-Leitung. 112 Packet-Types . 623, 635 Paddies . 256, 262 PAL-Fernsehnorm . II 7 . 549 Parallel-Device . 662 Parallele Schnittstelle . 661 Parallel-Port . 23 Parameter.. ParentDir .. . 35, 49, 50 . 325. 334 PIPE .551 Playfields . 141 149 Potentiometer. 262 Potgo.Iibrary . 7 O 6 Printer-Device .661 Priorität . Igl Programmsegmente . 672 Prosessor-Traps .. Proseß .■ 636 Proseß-Struktur . 639 PET. 549 PutMsg. 343 , 366 714 Amiga intern Qusntisieningsfehler. 233, 246 Qusntisicningsrauschen.233 QueuePacket .646 R/W . 16 RAD .662 RAM .649 RAM-Disk.662 RAM-Library .465 Raatericile .121 Rauaehen .231 RAW .660 RAW-Key-Codes . 93 R«ad .627 Refresh-Zyklen . 128 Register . 105, 595 ReleaseSemaphoreList .462 ReleaseSemaphore .447 RemHead .288 RemlntServer . 437 RemLibrary .316 Remove .287 RemPort .366 RemSemaphore.454 RemTail . 288 RemTask .332 Rename .632 ReplyMsg . 346, 366 Reset . 17, 100 Reset-Routine .479 Resetfeste Progranune . 479, 493 Resident-Strukturen .487 Resource-Struktur.431 ReturilPacket . 630, 632 RGB-Buchse . 63 ROM . 111 Root-BIock.671 ROM-Boot-Library.499 RootNode .606 Routinen .276 RS232-Schnitt8telle. 71, 264, 668 Sample.233 Sampleperiod .237 Sampling-Rate .236 Saubere Programmierung .305 Schreib-/Lese-Status .636 Scrolling . 154 Seek . 528 Sektor.667 Sektoren lesen .640 Selber bauen . 79 Semaphoren .441 SemaphoreRequest .444 Senden einer Nachricht .343 SendIO . 395, 398, 637 SER .649 Serial-Device .668 Stichwortverzeichnis 715 Serielle Schnittstelle . 70, 264, 560 Serieller Port . 29 ServerList .423 SetComment . 536 SetExcept .367 SetFunktion .376 SetlntVector.436 SetProtection . 536 SetSignsls .338 SetTaskPri . 333 Short Frame . 118 Signal .339 SignalSemaphore .442 Slots . 57 Smooth Scrolling . 167 Soft-Interrupt .426 SoftlntList . 426 Sonder-Codes . 99 SPEAK .562 Speicherbedingungen .369 Speicherbelegung . 103 Speicherverwaltung .368 Speichersuweisung und Tasks.366 Sprachausgabe .654 Sprite . 141, 172 Sprite-Datenliste . 174 Sprite-DMA . 174 Sprite-Register.185 Spur .667 Standard-I/O .548 Startup-Struktur.567 Steuerseichen .555 Störfrequensen . 243 Strobe. 106 Stromversorgung . 14 STRUCT .304 STRUCTURE. 302, 305 Strukturen . 277 Synchronisation . 98 SYS . 549 SysBase .310 Takteingang . 15 Task .318 Task-Exceptions . 348 Task-Funktionen . 331 Task-Signale .336 Task-Stack .323 Task-States .317 Task-Struktur .318 Task-Switching .320 TaskWait . 630, 631 Tastatur . 93 Tastaturprosessor. 94 TiefpaSfilter.243 Timer.library .705 Tonausgabe . 226 Track .667 716 Amiga intern Trackdisk-Device .638 Translator.library . 705 Trap-Befehle . 353 Tremolo .227 TV-Mod-Buchse . 61 UART.266 UBYTE .303 UDS. 15 UnLoadSeg .546 UnLock .532 User-Directory-Block.675 Vertical Quadrature Pulse .257 Vibrato . 226 ' Video-Buchsen . 60 Wait .340 WaitForChar . 529 WaitIO .396 WaitPort ..... 344, 367 WBStartup .. 566 Wellenform . 229 WOM . 112 Workbench .565 Write . 527 XDEF .294 XLIB .299 XREF .294 Zylinder 667 KNALLHARTE INFORMATIONEN. ^'’^BecKER AMl Amigfl Intern Band 2 - das Buch für jeden aldiven Programmierer, der alle weiterführenden Informationen zu seiner Arbeit schnell und zuver¬ lässig finden will. Ein Buch, dos dies verspricht, muß notüHich einiges bieten; Ein- und Ausgobe über Devices, Standard-Austausch-Formate und Komprimierungsverfahren, olle Amigo-Libraries mit den dazugehö¬ rigen Strukturen, Basis- und Grundstrukturen, Preferences als Datenslruk- lur, Datenübermittlung von Workbench und CLI, Konventionen im Pro¬ grammierstil und, um aktuell zu sein;: alles zur Kickstart-Version 1.3! Doch da können Sie sicher sein, Amiga Intern Band 2 wird Sie nicht enttäu¬ schen. Was Sie bei Ihrer Programmentwicklung brauchen, werden Sie finden. Bleek/Jennrich/khulz Amiga Intern Band 2 Hardcover, ca. 700 Seiten, DM 69,- ISBN3-89011-268-4 erscheint ca. 9/88 DASTECKTMUSIKDRIN. Zaubern Se zarte Klänge oder heiße Rhythmen aus Ihrem Amiga. Mit dem Amiga Musikbuch. Hier werden Sie zu einem Komponisten ausge¬ bildet, der nicht nur die notwendigen Grundbegriffe der Musiktheorie beherrscht, sondern der auch modernste Technik einzusetzen weiß. Denn in diesem Buch erfahren Sie olles zu den Musikprogrammen Sonix, DeLuxe Constructkjn Set und Audio Master. Dabei lernen Sie auch, wie man Sound-Sompler und MIDI-Interface professionell einsetzt. Wenn Sie mit diesem Buch gearbeitet haben, sollten Sie gleich der GEMA beitreten. Sponlk/Tel Affligo Muslkbvch Hordcover, co. 300 Sellen, DM 49,- ISBN3-0901I-2I5-3 erscheint CO. 9/W Bücher zum Amiga Wer den Einstieg in C geschafft hat, wird sich so manches Mal fragen, warum das gut durchdachte Programm nicht funktioniert. Hier helfen fundierte Informationen von Profis weiter: Funktionsweise von Compiler Assembler und Linker (am Beispiel von Aztec 3.4.), komfortable Oberfläche für ei¬ gene Programme mit Intuition und ein großes Projekt pro¬ fessionell programmiert - mit diesem Buch bleibt keine Frage offen. Aus dem Inhalt: - Funktionsweise des Aztec-Compilers (Compiler, Assembler, Linker) - Wie funktionieren INCLUDE, DEFINE und CASTS? - Debugging und Optimierung des Assembler-Sources - Knifflige Programmierprobleme: Sprungtabellen und dynamische Arrays in C - Einbinden von Assembler-Source in den C-Source - Alles über Intuition-Programmierung (Windows, Screens, Pull-Down-Menüs, Requester, Gadgets) - Programmierung des Console-Device - Ein professionelles Editor zeigt alle Probleme bei der Erstellung größerer Programme - Richtiger Einsatz von MAKE - Debugging von C-Programmen mit verschiedenen Hilfsmitteln - Ein kompletter Text-Editor als Source in mehreren Entwicklungsstufen - Folding-Technik (Der Editor kann Textteile und Funktionen wegfalten, das erhöht die Übersichtlichkeit enorm.) Bleek, Jennrich, Schulz Das große C-Buch Hardcover, 682 Seiten, DM 69,- ISBN3-89011-191-2 Bücher zu Amiga Schreiben Sie Ihre Programme in. Maschinenesprache - und Sie werden sehen, wie schnell ein Amiga sein kann. Das nötige Know-how liefert Ihnen dieses Buch: Grundlagen des 68000, das Amiga-Betriebssystem, Druckeransteuerung, Diskettenoperationen, Sprachausgabe, Windows, Screens, Register, Pull-Down-Menüs . . . Aber es wird auch gleich gezeigt, wie man mit den wichtigsten Assemblern arbeitet. Dittrich Amiga Maschinensprache Hardcover, 288 Seiten, DM 49,- ISBN 3-89011-076-2 Bücher zum Amiga AmigaBASIC für alle. Im ersten Teil werden Sie Schritt für Schritt - und vor allem auf verständliche Art und Weise - in die Programmierung des AMIGA eingeführt: Grafik und Sound gehören genauso dazu wie Datenverwaltung und Statistik. Im zweiten Teil finden Sie alle gelernten Befehle mit Syntax- und Parameterangaben zum schnellen Nachschlagen. Dazu gibt es Programme und Utilities in Hülle und Fülle. AMIGA Aus dem Inhalt: - DasVideotitel-Programm zeigt die OBJECT-Animation - Das Balken- und Tortengrafik-Programm erklärt die Graflkbefehle - Das Malprogramm mit Windows, Pulldowns, Mausbefehlen, Füllmustern, Einlesen und Abspeichern von IFF-Bildern - Das Statlstikdaten-Programm hilft, sequentielle Dateien zu verstehen - Die Datenbank zeigt den Umgang mit reiativen Dateien - Das Sprachutility sorgt für mehr Verständnis bei der Sprachprogrammie- rung - Das Synthesizer-Programm führt Sie in die Welt der Töne, Wellenformen und Hüllkurven Rügheimer, Spanik AmigaBASIC Hardcover, 775 Seiten, DM 59,- ISBN 3-89011-209-9